Merge topic 'msys-pkgconf'

903ae3c71b MSYS Makefiles: Modernize search paths for common MSYS 'make' locations
2057066e05 FindMsys: Modernize search paths for common MSYS locations

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !7955
diff --git a/.clang-tidy b/.clang-tidy
index 9ce5f42..c790467 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -44,6 +44,7 @@
 -readability-uppercase-literal-suffix,\
 cmake-*,\
 -cmake-ostringstream-use-cmstrcat,\
+-cmake-string-concatenation-use-cmstrcat,\
 -cmake-use-bespoke-enum-class,\
 "
 HeaderFilterRegex: 'Source/cm[^/]*\.(h|hxx|cxx)$'
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 24e19c7..a2b3ec3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -49,7 +49,7 @@
     extends:
         - .linux_prep_source
         - .cmake_prep_source_linux
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .cmake_release_artifacts
         - .run_only_for_package
     variables:
@@ -59,7 +59,7 @@
     extends:
         - .fedora37_sphinx_package
         - .cmake_prep_doc_linux
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .cmake_doc_artifacts
         - .run_only_for_package
 
@@ -95,28 +95,28 @@
 l:codespell:
     extends:
         - .cmake_codespell_linux
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_automatically
 
 l:iwyu-debian10:
     extends:
         - .debian10_iwyu
         - .cmake_build_linux
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_automatically
 
 l:tidy-fedora37:
     extends:
         - .fedora37_tidy
         - .cmake_build_linux
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_automatically
 
 l:sphinx-fedora37:
     extends:
         - .fedora37_sphinx
         - .cmake_build_linux
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_automatically
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
@@ -126,7 +126,7 @@
     extends:
         - .fedora37_clang_analyzer
         - .cmake_build_linux
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_automatically
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -138,7 +138,7 @@
         - .linux_release_x86_64
         - .cmake_build_linux_release
         - .cmake_release_artifacts
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_ARTIFACTS_NAME: "artifacts-centos6-x86_64"
@@ -148,7 +148,7 @@
         - .linux_release_aarch64
         - .cmake_build_linux_release
         - .cmake_release_artifacts
-        - .linux_builder_tags_aarch64
+        - .linux_aarch64_tags
         - .run_manually
     variables:
         CMAKE_CI_ARTIFACTS_NAME: "artifacts-centos7-aarch64"
@@ -158,7 +158,7 @@
     extends:
         - .debian10_ninja
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos6_x86_64
@@ -169,7 +169,7 @@
     extends:
         - .debian10_aarch64_ninja
         - .cmake_test_linux_release
-        - .linux_builder_tags_aarch64_qt
+        - .linux_aarch64_tags
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos7_aarch64
@@ -180,7 +180,7 @@
     extends:
         - .debian10_ninja_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -190,7 +190,7 @@
     extends:
         - .debian10_makefiles_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -200,7 +200,7 @@
     extends:
         - .fedora37_ninja_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -210,7 +210,7 @@
     extends:
         - .fedora37_makefiles_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -220,7 +220,7 @@
     extends:
         - .fedora37_makefiles
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
 
@@ -228,7 +228,7 @@
     extends:
         - .fedora37_makefiles
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos6_x86_64
@@ -237,11 +237,21 @@
         CMAKE_CI_BUILD_NAME: fedora37_makefiles_nospace
         CMAKE_CI_JOB_NIGHTLY: "true"
 
+t:nvhpc22.9-ninja:
+    extends:
+        - .nvhpc_ninja
+        - .cmake_test_linux_release
+        - .linux_x86_64_v3_tags_cuda
+        - .run_dependent
+        - .needs_centos6_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 t:cuda9.2-nvidia:
     extends:
         - .cuda9.2_nvidia
         - .cmake_test_linux_release
-        - .linux_builder_tags_cuda
+        - .linux_x86_64_tags_cuda
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -251,7 +261,7 @@
     extends:
         - .cuda10.2_nvidia
         - .cmake_test_linux_release
-        - .linux_builder_tags_cuda
+        - .linux_x86_64_tags_cuda
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos6_x86_64
@@ -260,7 +270,7 @@
     extends:
         - .cuda10.2_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_cuda
+        - .linux_x86_64_tags_cuda
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -270,7 +280,7 @@
     extends:
         - .cuda11.6_nvidia
         - .cmake_test_linux_release
-        - .linux_builder_tags_cuda
+        - .linux_x86_64_tags_cuda
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos6_x86_64
@@ -279,7 +289,7 @@
     extends:
         - .cuda11.6_clang
         - .cmake_test_linux_release
-        - .linux_builder_tags_cuda
+        - .linux_x86_64_tags_cuda
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -289,7 +299,7 @@
     extends:
         - .hip4.2_radeon
         - .cmake_test_linux_release
-        - .linux_builder_tags_radeon
+        - .linux_x86_64_tags_radeon
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -299,7 +309,7 @@
     extends:
         - .gcc_cxx_modules_ninja
         - .cmake_test_linux_release
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -309,7 +319,7 @@
     extends:
         - .gcc_cxx_modules_ninja_multi
         - .cmake_test_linux_release
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_dependent
         - .needs_centos6_x86_64
     variables:
@@ -320,7 +330,7 @@
         - .fedora37_ninja
         - .cmake_build_linux
         - .cmake_build_artifacts
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
@@ -329,7 +339,7 @@
     extends:
         - .debian10_makefiles_inplace
         - .cmake_build_linux_standalone
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -338,7 +348,7 @@
     extends:
         - .debian10_extdeps
         - .cmake_build_linux_standalone
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -347,7 +357,7 @@
     extends:
         - .debian10_aarch64_extdeps
         - .cmake_build_linux_standalone
-        - .linux_builder_tags_aarch64
+        - .linux_aarch64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -356,7 +366,7 @@
     extends:
         - .fedora37_extdeps
         - .cmake_build_linux_standalone
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -365,7 +375,7 @@
     extends:
         - .fedora37_ninja
         - .cmake_test_linux
-        - .linux_builder_tags_x11
+        - .linux_x86_64_tags_x11
         - .cmake_test_artifacts
         - .run_dependent
     dependencies:
@@ -379,7 +389,7 @@
     extends:
         - .fedora37_ninja_multi
         - .cmake_test_linux_external
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -680,7 +690,7 @@
         - .linux_release_x86_64
         - .cmake_build_linux_release
         - .cmake_release_artifacts
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -704,7 +714,7 @@
         - .linux_release_aarch64
         - .cmake_build_linux_release
         - .cmake_release_artifacts
-        - .linux_builder_tags_aarch64
+        - .linux_aarch64_tags
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -729,7 +739,7 @@
         - .fedora37_asan
         - .cmake_build_linux
         - .cmake_build_artifacts
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -738,7 +748,7 @@
     extends:
         - .fedora37_asan
         - .cmake_memcheck_linux
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_dependent
     dependencies:
         - b:fedora37-asan
@@ -754,7 +764,7 @@
         - .macos_x86_64_ninja
         - .cmake_build_macos
         - .cmake_build_artifacts
-        - .macos_x86_64_builder_tags
+        - .macos_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
@@ -764,7 +774,7 @@
         - .macos_arm64_ninja
         - .cmake_build_macos
         - .cmake_build_artifacts
-        - .macos_arm64_builder_tags
+        - .macos_arm64_tags
         - .run_manually
     variables:
         CMAKE_CI_NO_MR: "true"
@@ -774,7 +784,7 @@
         - .macos_x86_64_ninja
         - .cmake_test_macos
         - .cmake_test_artifacts
-        - .macos_x86_64_builder_tags
+        - .macos_x86_64_tags
         - .run_dependent
     dependencies:
         - b:macos-x86_64-ninja
@@ -789,7 +799,7 @@
         - .macos_arm64_ninja
         - .cmake_test_macos
         - .cmake_test_artifacts
-        - .macos_arm64_builder_tags
+        - .macos_arm64_tags
         - .run_dependent
     dependencies:
         - b:macos-arm64-ninja
@@ -803,14 +813,14 @@
         - .macos_x86_64_makefiles
         - .cmake_build_macos
         - .cmake_build_artifacts
-        - .macos_x86_64_builder_tags
+        - .macos_x86_64_tags
         - .run_manually
 
 t:macos-x86_64-makefiles:
     extends:
         - .macos_x86_64_makefiles
         - .cmake_test_macos
-        - .macos_x86_64_builder_tags
+        - .macos_x86_64_tags
         - .run_dependent
     dependencies:
         - b:macos-x86_64-makefiles
@@ -821,7 +831,7 @@
     extends:
         - .macos_x86_64_ninja_multi
         - .cmake_test_macos_external
-        - .macos_x86_64_builder_ext_tags
+        - .macos_x86_64_tags_ext
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -835,7 +845,7 @@
     extends:
         - .macos_x86_64_xcode
         - .cmake_test_macos_external
-        - .macos_x86_64_builder_ext_tags
+        - .macos_x86_64_tags_ext
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -847,7 +857,7 @@
     extends:
         - .macos_arm64_xcode
         - .cmake_test_macos_external
-        - .macos_arm64_builder_ext_tags
+        - .macos_arm64_tags_ext
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -862,7 +872,7 @@
         - .macos_package
         - .cmake_build_macos_package
         - .cmake_release_artifacts
-        - .macos_x86_64_builder_tags_package
+        - .macos_x86_64_tags_package
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -885,7 +895,7 @@
         - .macos10.10_package
         - .cmake_build_macos_package
         - .cmake_release_artifacts
-        - .macos_x86_64_builder_tags_package
+        - .macos_x86_64_tags_package
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -910,14 +920,14 @@
         - .windows_vs2022_x64_ninja
         - .cmake_build_windows
         - .cmake_build_artifacts
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_manually
 
 t:windows-vs2022-x64-ninja:
     extends:
         - .windows_vs2022_x64_ninja
         - .cmake_test_windows
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .cmake_test_artifacts
         - .run_dependent
     dependencies:
@@ -931,7 +941,7 @@
     extends:
         - .windows_vs2022_x64_ninja_multi
         - .cmake_test_windows_external
-        - .windows_tags_concurrent_vs2022
+        - .windows_x86_64_tags_concurrent_vs2022
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -945,7 +955,7 @@
     extends:
         - .windows_vs2022_x64
         - .cmake_test_windows_external
-        - .windows_tags_concurrent_vs2022
+        - .windows_x86_64_tags_concurrent_vs2022
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -957,7 +967,7 @@
     extends:
         - .windows_vs2019_x64
         - .cmake_test_windows_external
-        - .windows_tags_concurrent_vs2019
+        - .windows_x86_64_tags_concurrent_vs2019
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -971,7 +981,7 @@
     extends:
         - .windows_vs2022_x64_nmake
         - .cmake_test_windows_nmake
-        - .windows_tags_concurrent_vs2022
+        - .windows_x86_64_tags_concurrent_vs2022
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -985,7 +995,7 @@
     extends:
         - .windows_vs2022_x64_jom
         - .cmake_test_windows_jom
-        - .windows_tags_concurrent_vs2022
+        - .windows_x86_64_tags_concurrent_vs2022
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -999,7 +1009,7 @@
     extends:
         - .windows_borland5.5
         - .cmake_test_windows_borland
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1013,7 +1023,7 @@
     extends:
         - .windows_borland5.8
         - .cmake_test_windows_borland
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1027,7 +1037,7 @@
     extends:
         - .windows_clang_ninja
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1042,7 +1052,7 @@
     extends:
         - .windows_clang_nmake
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1057,7 +1067,7 @@
     extends:
         - .windows_clang_ninja
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1072,7 +1082,7 @@
     extends:
         - .windows_clang_nmake
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1087,7 +1097,7 @@
     extends:
         - .mingw_osdn_io_mingw_makefiles
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1101,7 +1111,7 @@
     extends:
         - .mingw_osdn_io_msys_makefiles
         - .cmake_test_windows_external
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1115,7 +1125,7 @@
     extends:
         - .windows_msvc_v71_nmake
         - .cmake_test_windows_msvc
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1129,7 +1139,7 @@
     extends:
         - .windows_openwatcom1.9
         - .cmake_test_windows_openwatcom
-        - .windows_tags_concurrent
+        - .windows_x86_64_tags_concurrent
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
@@ -1186,7 +1196,7 @@
         - .windows_x86_64_package
         - .cmake_build_windows
         - .cmake_build_package_artifacts
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -1200,7 +1210,7 @@
         - .windows_x86_64_package
         - .cmake_package_windows
         - .cmake_release_artifacts
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_only_for_package
     dependencies:
         - b:windows-x86_64-package
@@ -1223,7 +1233,7 @@
         - .windows_i386_package
         - .cmake_build_windows
         - .cmake_build_package_artifacts
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -1237,7 +1247,7 @@
         - .windows_i386_package
         - .cmake_package_windows
         - .cmake_release_artifacts
-        - .windows_tags_nonconcurrent_vs2022
+        - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_only_for_package
     dependencies:
         - b:windows-i386-package
@@ -1260,7 +1270,7 @@
         - .windows_arm64_package
         - .cmake_build_windows
         - .cmake_build_package_artifacts
-        - .windows_tags_nonconcurrent_vs2022_arm64
+        - .windows_x86_64_tags_nonconcurrent_vs2022_arm64
         - .run_only_for_package
     dependencies:
         - p:doc-package
@@ -1274,7 +1284,7 @@
         - .windows_arm64_package
         - .cmake_package_windows
         - .cmake_release_artifacts
-        - .windows_tags_nonconcurrent_vs2022_arm64
+        - .windows_x86_64_tags_nonconcurrent_vs2022_arm64
         - .run_only_for_package
     dependencies:
         - b:windows-arm64-package
diff --git a/.gitlab/ci/configure_nvhpc_ninja.cmake b/.gitlab/ci/configure_nvhpc_ninja.cmake
new file mode 100644
index 0000000..ca8ba93
--- /dev/null
+++ b/.gitlab/ci/configure_nvhpc_ninja.cmake
@@ -0,0 +1,5 @@
+set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "")
+
+set(configure_no_sccache 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake
index 89a5ace..b29e785 100644
--- a/.gitlab/ci/ctest_exclusions.cmake
+++ b/.gitlab/ci/ctest_exclusions.cmake
@@ -27,6 +27,16 @@
     )
 endif()
 
+if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "nvhpc_")
+  list(APPEND test_exclusions
+    # FIXME(#24187): This test fails with NVHPC as the CUDA host compiler.
+    "^CudaOnly.SeparateCompilationPTX$"
+
+    # FIXME(#24188): FindCUDAToolkit breaks on some symlink layouts.
+    "^Cuda.Toolkit$"
+    )
+endif()
+
 string(REPLACE ";" "|" test_exclusions "${test_exclusions}")
 if (test_exclusions)
   set(test_exclusions "(${test_exclusions})")
diff --git a/.gitlab/ci/cxx_modules_rules_msvc.cmake b/.gitlab/ci/cxx_modules_rules_msvc.cmake
index 2b09b0e..cb327fd 100644
--- a/.gitlab/ci/cxx_modules_rules_msvc.cmake
+++ b/.gitlab/ci/cxx_modules_rules_msvc.cmake
@@ -1 +1,3 @@
 set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
diff --git a/.gitlab/ci/docker/nvhpc22.9/Dockerfile b/.gitlab/ci/docker/nvhpc22.9/Dockerfile
new file mode 100644
index 0000000..90e7d12
--- /dev/null
+++ b/.gitlab/ci/docker/nvhpc22.9/Dockerfile
@@ -0,0 +1,6 @@
+# https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nvhpc/tags
+FROM nvcr.io/nvidia/nvhpc:22.9-devel-cuda_multi-ubuntu22.04
+MAINTAINER Brad King <brad.king@kitware.com>
+
+COPY install_deps.sh /root/install_deps.sh
+RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/nvhpc22.9/install_deps.sh b/.gitlab/ci/docker/nvhpc22.9/install_deps.sh
new file mode 100755
index 0000000..51ee410
--- /dev/null
+++ b/.gitlab/ci/docker/nvhpc22.9/install_deps.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+apt-get update
+
+# Install development tools.
+apt-get install -y \
+    curl
+
+apt-get clean
diff --git a/.gitlab/ci/env_nvhpc_ninja.sh b/.gitlab/ci/env_nvhpc_ninja.sh
new file mode 100644
index 0000000..687403d
--- /dev/null
+++ b/.gitlab/ci/env_nvhpc_ninja.sh
@@ -0,0 +1,5 @@
+export CC=nvc
+export CXX=nvc++
+export FC=nvfortran
+export CUDACXX=nvcc
+export CUDAHOSTCXX=nvc++
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 8fc6ae1..decf1b1 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -255,6 +255,18 @@
         CMAKE_CONFIGURATION: inteloneapi_makefiles
         CMAKE_GENERATOR: "Unix Makefiles"
 
+### NVHPC Compiler
+
+.nvhpc:
+    image: "kitware/cmake:ci-nvhpc22.9-x86_64-2022-11-22"
+    variables:
+        CMAKE_ARCH: x86_64
+
+.nvhpc_ninja:
+    extends: .nvhpc
+    variables:
+        CMAKE_CONFIGURATION: nvhpc_ninja
+
 ### CUDA builds
 
 .cuda9.2:
@@ -362,58 +374,48 @@
 
 ## Tags
 
-.linux_builder_tags:
+.linux_x86_64_tags:
     tags:
         - cmake
         - build
         - docker
-        - linux
+        - linux-x86_64
 
-.linux_builder_tags_qt:
-    tags:
-        - cmake
-        - build
-        - docker
-        - linux
-        - linux-3.17 # Needed to be able to load Fedora's Qt libraries.
-
-.linux_builder_tags_x11:
+.linux_x86_64_tags_x11:
     tags:
         - cmake
         - docker
-        - linux
-        - linux-3.17 # Needed to be able to load Fedora's Qt libraries.
+        - linux-x86_64
         - x11
 
-.linux_builder_tags_cuda:
+.linux_x86_64_tags_cuda:
     tags:
         - cmake
         - cuda-rt
         - docker
-        - linux
+        - linux-x86_64
 
-.linux_builder_tags_radeon:
+.linux_x86_64_v3_tags_cuda:
+    tags:
+        - cmake
+        - cuda-rt
+        - docker
+        - linux-x86_64-v3
+
+.linux_x86_64_tags_radeon:
     tags:
         - cmake
         - radeon
         - docker
-        - linux
+        - linux-x86_64
 
-.linux_builder_tags_aarch64:
+.linux_aarch64_tags:
     tags:
         - cmake
         - build
         - docker
         - linux-aarch64
 
-.linux_builder_tags_aarch64_qt:
-    tags:
-        - cmake
-        - build
-        - docker
-        - linux-aarch64
-        - linux-3.17 # Needed to be able to load Fedora's Qt libraries.
-
 ## Linux-specific scripts
 
 .before_script_linux: &before_script_linux
@@ -572,7 +574,7 @@
     extends:
         - .intelclassic_makefiles
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_manually
         - .needs_centos6_x86_64
     variables:
@@ -582,7 +584,7 @@
     extends:
         - .inteloneapi_makefiles
         - .cmake_test_linux_release
-        - .linux_builder_tags_qt
+        - .linux_x86_64_tags
         - .run_manually
         - .needs_centos6_x86_64
     variables:
@@ -594,7 +596,7 @@
     stage: build
     extends:
         - .fedora37
-        - .linux_builder_tags
+        - .linux_x86_64_tags
         - .cmake_org_help_artifacts
     script:
         - *before_script_linux
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 4b71a1b..258fa31 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -90,32 +90,32 @@
 
 ## Tags
 
-.macos_x86_64_builder_tags:
+.macos_x86_64_tags:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - macos
+        - macos-x86_64
         - shell
         - xcode-14.1
         - nonconcurrent
 
-.macos_x86_64_builder_tags_package:
+.macos_x86_64_tags_package:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - macos
+        - macos-x86_64
         - shell
         - xcode-14.1
         - nonconcurrent
         - finder
 
-.macos_x86_64_builder_ext_tags:
+.macos_x86_64_tags_ext:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - macos
+        - macos-x86_64
         - shell
         - xcode-14.1
         - concurrent
 
-.macos_arm64_builder_tags:
+.macos_arm64_tags:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
         - macos-arm64
@@ -123,7 +123,7 @@
         - xcode-14.1
         - nonconcurrent
 
-.macos_arm64_builder_ext_tags:
+.macos_arm64_tags_ext:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
         - macos-arm64
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml
index 64b595e..22d731a 100644
--- a/.gitlab/os-windows.yml
+++ b/.gitlab/os-windows.yml
@@ -273,46 +273,46 @@
 
 ## Tags
 
-.windows_tags_nonconcurrent_vs2022:
+.windows_x86_64_tags_nonconcurrent_vs2022:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - windows
+        - windows-x86_64
         - shell
         - vs2022
         - msvc-19.34
         - nonconcurrent
 
-.windows_tags_nonconcurrent_vs2022_arm64:
+.windows_x86_64_tags_nonconcurrent_vs2022_arm64:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - windows
+        - windows-x86_64
         - shell
         - vs2022
         - msvc-19.34-arm64
         - nonconcurrent
 
-.windows_tags_concurrent_vs2022:
+.windows_x86_64_tags_concurrent_vs2022:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - windows
+        - windows-x86_64
         - shell
         - vs2022
         - msvc-19.34
         - concurrent
 
-.windows_tags_concurrent_vs2019:
+.windows_x86_64_tags_concurrent_vs2019:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - windows
+        - windows-x86_64
         - shell
         - vs2019
         - msvc-19.29-16.11
         - concurrent
 
-.windows_tags_concurrent:
+.windows_x86_64_tags_concurrent:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
-        - windows
+        - windows-x86_64
         - shell
         - concurrent
 
diff --git a/Help/command/add_compile_options.rst b/Help/command/add_compile_options.rst
index ad65357..0ccebc6 100644
--- a/Help/command/add_compile_options.rst
+++ b/Help/command/add_compile_options.rst
@@ -28,13 +28,16 @@
 .. code-block:: cmake
 
   if (MSVC)
-      # warning level 4 and all warnings as errors
-      add_compile_options(/W4 /WX)
+      # warning level 4
+      add_compile_options(/W4)
   else()
-      # lots of warnings and all warnings as errors
-      add_compile_options(-Wall -Wextra -pedantic -Werror)
+      # additional warnings
+      add_compile_options(-Wall -Wextra -Wpedantic)
   endif()
 
+To set per-language options, use the :genex:`$<COMPILE_LANGUAGE>`
+or :genex:`$<COMPILE_LANGUAGE:languages>` generator expressions.
+
 See Also
 ^^^^^^^^
 
diff --git a/Help/command/target_compile_options.rst b/Help/command/target_compile_options.rst
index 8f13f45..698f62d 100644
--- a/Help/command/target_compile_options.rst
+++ b/Help/command/target_compile_options.rst
@@ -19,7 +19,8 @@
 ^^^^^^^^^
 
 If ``BEFORE`` is specified, the content will be prepended to the property
-instead of being appended.
+instead of being appended.  See policy :policy:`CMP0101` which affects
+whether ``BEFORE`` will be ignored in certain cases.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
 specify the :ref:`scope <Target Usage Requirements>` of the following arguments.
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 78770f7..03eb076 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -41,7 +41,8 @@
 
 MSVC 19.34 (provided with Visual Studio 17.4) and above contains the support
 that CMake needs and has these variables already set up as required and only
-the UUID variable needs to be set.
+the UUID and the ``CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP`` variables need to be
+set.
 
 For example, add code like the following to a test project:
 
diff --git a/Help/policy/CMP0101.rst b/Help/policy/CMP0101.rst
index f02bccc..6781079 100644
--- a/Help/policy/CMP0101.rst
+++ b/Help/policy/CMP0101.rst
@@ -3,16 +3,24 @@
 
 .. versionadded:: 3.17
 
-:command:`target_compile_options` now honors ``BEFORE`` keyword in all scopes.
+:command:`target_compile_options` now always honors the ``BEFORE`` keyword.
 
-In CMake 3.16 and below the :command:`target_compile_options` ignores the
-``BEFORE`` keyword in private scope. CMake 3.17 and later honors
-``BEFORE`` keyword in all scopes. This policy provides compatibility for
-projects that have not been updated to expect the new behavior.
+In CMake 3.16 and below, the :command:`target_compile_options` command
+ignores the ``BEFORE`` keyword when inserting items into the
+:prop_tgt:`COMPILE_OPTIONS` target property (``PRIVATE`` and ``PUBLIC``
+items).  CMake 3.17 and later honors the ``BEFORE`` keyword in all cases.
+This policy provides compatibility for projects that have not been updated
+to expect the new behavior.
 
-The ``OLD`` behavior for this policy is to not honor ``BEFORE`` keyword in
-private scope. The ``NEW`` behavior of this policy is to honor
-``BEFORE`` keyword in all scopes.
+The behavior of inserting items into the :prop_tgt:`INTERFACE_COMPILE_OPTIONS`
+target property (``PUBLIC`` and ``INTERFACE`` items) is not affected by this
+policy.  The ``BEFORE`` keyword has always been honored when adding items to
+:prop_tgt:`INTERFACE_COMPILE_OPTIONS`.
+
+The ``OLD`` behavior for this policy is to not honor the ``BEFORE`` keyword
+when inserting into the :prop_tgt:`COMPILE_OPTIONS` property.
+The ``NEW`` behavior for this policy is to honor the ``BEFORE`` keyword in
+all cases.
 
 This policy was introduced in CMake version 3.17.  Use the
 :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
diff --git a/Help/release/3.25.rst b/Help/release/3.25.rst
index 7d0cf0a..fdf8a94 100644
--- a/Help/release/3.25.rst
+++ b/Help/release/3.25.rst
@@ -223,11 +223,6 @@
 Other Changes
 =============
 
-* On Windows, when targeting the MSVC ABI, the :command:`find_library` command
-  now accepts ``.a`` file names after first considering ``.lib``.  This is
-  symmetric with existing behavior when targeting the GNU ABI, in which the
-  command accepts ``.lib`` file names after first considering ``.a``.
-
 * The :envvar:`SSL_CERT_FILE` and :envvar:`SSL_CERT_DIR` environment
   variables can now be used to override where to find certificate
   authorities for TLS/SSL operations.
@@ -238,3 +233,16 @@
 * The :generator:`Xcode` generator no longer adds the per-config suffix
   ``$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)`` to library search paths.
   See policy :policy:`CMP0142`.
+
+Updates
+=======
+
+Changes made since CMake 3.25.0 include the following.
+
+3.25.1
+------
+
+* On Windows, when targeting the MSVC ABI, the :command:`find_library`
+  command no longer accepts ``.a`` file names.  This behavior was added
+  in CMake 3.25.0, but has been reverted due finding GNU-ABI libraries
+  in cases we did not previously.
diff --git a/Help/release/dev/FindPython-Stable-ABI.rst b/Help/release/dev/FindPython-Stable-ABI.rst
new file mode 100644
index 0000000..c6a06b9
--- /dev/null
+++ b/Help/release/dev/FindPython-Stable-ABI.rst
@@ -0,0 +1,6 @@
+FindPython-Stable-ABI
+---------------------
+
+* The :module:`FindPython3` and :module:`FindPython` modules gain the support
+  of the
+  `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_.
diff --git a/Modules/CMakeDetermineRCCompiler.cmake b/Modules/CMakeDetermineRCCompiler.cmake
index f8d55a5..d22741b 100644
--- a/Modules/CMakeDetermineRCCompiler.cmake
+++ b/Modules/CMakeDetermineRCCompiler.cmake
@@ -30,16 +30,19 @@
 
   # finally list compilers to try
   if(CMAKE_RC_COMPILER_INIT)
-    set(CMAKE_RC_COMPILER_LIST ${CMAKE_RC_COMPILER_INIT})
-  else()
-    set(CMAKE_RC_COMPILER_LIST rc)
+    set(_CMAKE_RC_COMPILER_LIST     ${CMAKE_RC_COMPILER_INIT})
+    set(_CMAKE_RC_COMPILER_FALLBACK ${CMAKE_RC_COMPILER_INIT})
+  elseif(NOT _CMAKE_RC_COMPILER_LIST)
+    set(_CMAKE_RC_COMPILER_LIST rc)
   endif()
 
   # Find the compiler.
-  find_program(CMAKE_RC_COMPILER NAMES ${CMAKE_RC_COMPILER_LIST} DOC "RC compiler")
-  if(CMAKE_RC_COMPILER_INIT AND NOT CMAKE_RC_COMPILER)
-    set(CMAKE_RC_COMPILER "${CMAKE_RC_COMPILER_INIT}" CACHE FILEPATH "RC compiler" FORCE)
+  find_program(CMAKE_RC_COMPILER NAMES ${_CMAKE_RC_COMPILER_LIST} DOC "RC compiler")
+  if(_CMAKE_RC_COMPILER_FALLBACK AND NOT CMAKE_RC_COMPILER)
+    set(CMAKE_RC_COMPILER "${_CMAKE_RC_COMPILER_FALLBACK}" CACHE FILEPATH "RC compiler" FORCE)
   endif()
+  unset(_CMAKE_RC_COMPILER_FALLBACK)
+  unset(_CMAKE_RC_COMPILER_LIST)
 endif()
 
 mark_as_advanced(CMAKE_RC_COMPILER)
diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake
index 8e82859..8fc6921 100644
--- a/Modules/CheckIncludeFiles.cmake
+++ b/Modules/CheckIncludeFiles.cmake
@@ -70,10 +70,11 @@
       message(FATAL_ERROR "Unknown arguments:\n  ${ARGN}\n")
     endif()
 
+    string(MAKE_C_IDENTIFIER ${VARIABLE} _variable_escaped)
     if(_lang STREQUAL "C")
-      set(src ${VARIABLE}.c)
+      set(src ${_variable_escaped}.c)
     elseif(_lang STREQUAL "CXX")
-      set(src ${VARIABLE}.cpp)
+      set(src ${_variable_escaped}.cpp)
     else()
       message(FATAL_ERROR "Unknown language:\n  ${_lang}\nSupported languages: C, CXX.\n")
     endif()
diff --git a/Modules/CheckTypeSize.cmake b/Modules/CheckTypeSize.cmake
index b14ab06..e09b7c8 100644
--- a/Modules/CheckTypeSize.cmake
+++ b/Modules/CheckTypeSize.cmake
@@ -103,10 +103,11 @@
   endif()
 
   # Perform language check
+  string(MAKE_C_IDENTIFIER ${var} _var_escaped)
   if(language STREQUAL "C")
-    set(src ${var}.c)
+    set(src ${_var_escaped}.c)
   elseif(language STREQUAL "CXX")
-    set(src ${var}.cpp)
+    set(src ${_var_escaped}.cpp)
   else()
     message(FATAL_ERROR "Unknown language:\n  ${language}\nSupported languages: C, CXX.\n")
   endif()
diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake
index 53456f5..7908f96 100644
--- a/Modules/Compiler/IAR.cmake
+++ b/Modules/Compiler/IAR.cmake
@@ -8,54 +8,48 @@
 #
 include_guard()
 
-macro(__compiler_iar_ilink lang)
-  set(CMAKE_EXECUTABLE_SUFFIX ".elf")
-  set(CMAKE_${lang}_OUTPUT_EXTENSION ".o")
-  if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
+macro(__compiler_iar_common lang)
+  if (${lang} MATCHES "^(C|CXX)$")
     set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
     set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
     set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE     "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy")
 
-    set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
     set(CMAKE_DEPFILE_FLAGS_${lang} "--dependencies=ns <DEP_FILE>")
 
     string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
     string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -r")
-    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Ohz -DNDEBUG")
     string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -Oh -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Ohz -DNDEBUG")
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
   endif()
 
+  set(CMAKE_${lang}_RESPONSE_FILE_FLAG "-f ")
+  set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
+
+  set(CMAKE_${lang}_ARCHIVE_FINISH "")
+endmacro()
+
+macro(__compiler_iar_ilink lang)
+  set(CMAKE_EXECUTABLE_SUFFIX ".elf")
+  set(CMAKE_${lang}_OUTPUT_EXTENSION ".o")
+
+  __compiler_iar_common(${lang})
+
   set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_LINKER> --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <TARGET> --create <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> <TARGET> --create <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> <TARGET> --replace <LINK_FLAGS> <OBJECTS>")
-  set(CMAKE_${lang}_ARCHIVE_FINISH "")
 endmacro()
 
 macro(__compiler_iar_xlink lang)
   set(CMAKE_EXECUTABLE_SUFFIX ".bin")
-  if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
 
-    set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-    set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
-    set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE     "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy")
-
-    set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
-    set(CMAKE_DEPFILE_FLAGS_${lang} "--dependencies=ns <DEP_FILE>")
-
-    string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
-    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -r")
-    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Ohz -DNDEBUG")
-    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -Oh -DNDEBUG")
-    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
-  endif()
+  __compiler_iar_common(${lang})
 
   set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_LINKER> -S <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_APPEND "")
-  set(CMAKE_${lang}_ARCHIVE_FINISH "")
 
   set(CMAKE_LIBRARY_PATH_FLAG "-I")
 endmacro()
diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake
index 212f6d1..10a9073 100644
--- a/Modules/Compiler/MSVC-CXX.cmake
+++ b/Modules/Compiler/MSVC-CXX.cmake
@@ -79,7 +79,6 @@
 endif()
 
 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.34")
-  set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
   string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
     "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE> -nologo -TP"
     " -showIncludes"
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index 3fb56e4..fd2eeaa 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -31,6 +31,13 @@
     * ``Development.Embed``: search for artifacts for Python embedding
       developments.
 
+  .. versionadded:: 3.26
+
+    * ``Development.SABIModule``: search for artifacts for Python module
+      developments using the
+      `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_.
+      This component is available only for version ``3.2`` and upper.
+
 * ``NumPy``: search for NumPy include directories.
 
 .. versionadded:: 3.14
@@ -80,6 +87,12 @@
   Python library for Python module. Target defined if component
   ``Development.Module`` is found.
 
+``Python::SABIModule``
+  .. versionadded:: 3.26
+
+  Python library for Python module using the Stable Application Binary
+  Interface. Target defined if component ``Development.SABIModule`` is found.
+
 ``Python::Python``
   Python library for Python embedding. Target defined if component
   ``Development.Embed`` is found.
@@ -145,6 +158,16 @@
   not available, ``sysconfig.get_config_var('EXT_SUFFIX')`` or
   ``sysconfig.get_config_var('SOABI')`` are used.
 
+``Python_SOSABI``
+  .. versionadded:: 3.26
+
+  Extension suffix for modules using the Stable Application Binary Interface.
+
+  Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the
+  COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3``
+  except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty
+  string.
+
 ``Python_Compiler_FOUND``
   System has the Python compiler.
 ``Python_COMPILER``
@@ -166,6 +189,12 @@
 
   System has the Python development artifacts for Python module.
 
+``Python_Development.SABIModule_FOUND``
+  .. versionadded:: 3.26
+
+  System has the Python development artifacts for Python module using the
+  Stable Application Binary Interface.
+
 ``Python_Development.Embed_FOUND``
   .. versionadded:: 3.18
 
@@ -187,6 +216,18 @@
   The Python library directories.
 ``Python_RUNTIME_LIBRARY_DIRS``
   The Python runtime library directories.
+``Python_SABI_LIBRARIES``
+  .. versionadded:: 3.26
+
+  The Python libraries for the Stable Application Binary Interface.
+``Python_SABI_LIBRARY_DIRS``
+  .. versionadded:: 3.26
+
+  The Python ``SABI`` library directories.
+``Python_RUNTIME_SABI_LIBRARY_DIRS``
+  .. versionadded:: 3.26
+
+  The Python runtime ``SABI`` library directories.
 ``Python_VERSION``
   Python version.
 ``Python_VERSION_MAJOR``
@@ -425,6 +466,13 @@
   variables ``Python_LIBRARIES``, ``Python_LIBRARY_DIRS`` and
   ``Python_RUNTIME_LIBRARY_DIRS``.
 
+``Python_SABI_LIBRARY``
+  .. versionadded:: 3.26
+
+  The path to the library for Stable Application Binary Interface. It will be
+  used to compute the variables ``Python_SABI_LIBRARIES``,
+  ``Python_SABI_LIBRARY_DIRS`` and ``Python_RUNTIME_SABI_LIBRARY_DIRS``.
+
 ``Python_INCLUDE_DIR``
   The path to the directory of the ``Python`` headers. It will be used to
   compute the variable ``Python_INCLUDE_DIRS``.
@@ -470,10 +518,11 @@
 This module defines the command ``Python_add_library`` (when
 :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as
 :command:`add_library` and adds a dependency to target ``Python::Python`` or,
-when library type is ``MODULE``, to target ``Python::Module`` and takes care of
-Python module naming rules::
+when library type is ``MODULE``, to target ``Python::Module`` or
+``Python::SABIModule`` (when ``USE_SABI`` option is specified) and takes care
+of Python module naming rules::
 
-  Python_add_library (<name> [STATIC | SHARED | MODULE [WITH_SOABI]]
+  Python_add_library (<name> [STATIC | SHARED | MODULE [USE_SABI <version>] [WITH_SOABI]]
                       <source1> [<source2> ...])
 
 If the library type is not specified, ``MODULE`` is assumed.
@@ -481,6 +530,19 @@
 .. versionadded:: 3.17
   For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the
   module suffix will include the ``Python_SOABI`` value, if any.
+
+.. versionadded:: 3.26
+  For ``MODULE`` type, if the option ``USE_SABI`` is specified, the
+  preprocessor definition ``Py_LIMITED_API`` will be specified, as ``PRIVATE``,
+  for the target ``<name>`` with the value computed from ``<version>`` argument.
+  The expected format for ``<version>`` is ``major[.minor]``, where each
+  component is a numeric value. If ``minor`` component is specified, the
+  version should be, at least, ``3.2`` which is the version where the
+  `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_
+  was introduced. Specifying only major version ``3`` is equivalent to ``3.2``.
+
+  When option ``WITH_SOABI`` is also specified,  the module suffix will include
+  the ``Python3_SOSABI`` value, if any.
 #]=======================================================================]
 
 
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 9b78220..069bc31 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -14,6 +14,8 @@
 cmake_policy (SET CMP0012 NEW)
 # IN_LIST operator
 cmake_policy (SET CMP0057 NEW)
+# foreach loop variable scope
+cmake_policy (SET CMP0124 NEW)
 # registry view behavior
 cmake_policy (SET CMP0134 NEW)
 
@@ -53,6 +55,18 @@
 endmacro()
 
 
+function (_PYTHON_ADD_REASON_FAILURE module message)
+  if (_${_PYTHON_PREFIX}_${module}_REASON_FAILURE)
+    string (LENGTH "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}" length)
+    math (EXPR length "${length} + 10")
+    string (REPEAT " " ${length} shift)
+    set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}\n${shift}${message}")
+  else()
+    set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${message}")
+  endif()
+endfunction()
+
+
 function (_PYTHON_MARK_AS_INTERNAL)
   foreach (var IN LISTS ARGV)
     if (DEFINED CACHE{${var}})
@@ -443,11 +457,18 @@
 function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
   unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE)
 
-  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI)$")
+  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI)$")
     return()
   endif()
 
-  if (_${_PYTHON_PREFIX}_CONFIG)
+  if (NAME STREQUAL "SOSABI")
+    # assume some default
+    if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN")
+      set (_values "")
+    else()
+      set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}")
+    endif()
+  elseif (_${_PYTHON_PREFIX}_CONFIG)
     if (NAME STREQUAL "SOABI")
       set (config_flag "--extension-suffix")
     else()
@@ -555,6 +576,17 @@
           endif()
         endif()
       endif()
+    elseif (NAME STREQUAL "SOSABI")
+      execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\nimport re\nimport importlib\nsys.stdout.write(next(filter(lambda x: re.search('^\\.abi', x), importlib.machinery.EXTENSION_SUFFIXES)))"
+                       RESULT_VARIABLE _result
+                       OUTPUT_VARIABLE _values
+                       ERROR_QUIET
+                       OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (_result)
+        unset (_values)
+      else()
+        string (REGEX REPLACE "^\\.(.+)\\.[^.]+$" "\\1" _values "${_values}")
+      endif()
     else()
       set (config_flag "${NAME}")
       if (NAME STREQUAL "CONFIGDIR")
@@ -572,7 +604,7 @@
     endif()
   endif()
 
-  if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI")
+  if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI")
     set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE)
     return()
   endif()
@@ -597,7 +629,7 @@
 endfunction()
 
 function (_PYTHON_GET_VERSION)
-  cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;INCLUDE" "PREFIX" "")
+  cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;SABI_LIBRARY;INCLUDE" "PREFIX" "")
 
   unset (${_PGV_PREFIX}VERSION PARENT_SCOPE)
   unset (${_PGV_PREFIX}VERSION_MAJOR PARENT_SCOPE)
@@ -643,6 +675,29 @@
         set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
       endif()
     endif()
+  elseif (_PGV_SABI_LIBRARY)
+    # retrieve version and abi from library name
+    if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+      get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME)
+      # extract version from library name
+      if (library_name MATCHES "python([23])([dmu]*)")
+        set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_2}" PARENT_SCOPE)
+      elseif (library_name MATCHES "pypy([23])-c")
+        set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
+      elseif (library_name MATCHES "pypy-c")
+        # try to pick-up a more precise version from the path
+        get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
+        if (library_dir MATCHES "/pypy([23])\\.([0-9]+)/")
+          set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
+          set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        endif()
+        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
+      endif()
+    endif()
   else()
     if (_${_PYTHON_PREFIX}_INCLUDE_DIR)
       # retrieve version from header file
@@ -850,6 +905,7 @@
   endif()
 
   if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+        OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
         OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       AND NOT CMAKE_CROSSCOMPILING)
     # In this case, interpreter must have same architecture as environment
@@ -999,7 +1055,7 @@
 
   if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
     # library does not exist anymore
-    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
     if (WIN32)
       set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND")
@@ -1013,7 +1069,7 @@
 
   if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     # incompatible ABI
-    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
   else()
     if (_PVL_VERSION OR _PVL_IN_RANGE)
@@ -1022,7 +1078,7 @@
         string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}")
         if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version))
           # library has wrong version
-          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
         endif()
       endif()
@@ -1032,14 +1088,14 @@
         find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE)
         if (NOT in_range)
           # library has wrong version
-          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
         endif()
       endif()
     else()
       if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # library has wrong major version
-        set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+        set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
       endif()
     endif()
@@ -1056,6 +1112,51 @@
 endfunction()
 
 
+function (_PYTHON_VALIDATE_SABI_LIBRARY)
+  if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+    unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG)
+    return()
+  endif()
+
+  cmake_parse_arguments (PARSE_ARGV 0 _PVL "CHECK_EXISTS" "" "")
+
+  if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}")
+    # library does not exist anymore
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND")
+    if (WIN32)
+      set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG-NOTFOUND")
+    endif()
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+    return()
+  endif()
+
+  # retrieve version and abi from library name
+  _python_get_version (SABI_LIBRARY PREFIX lib_)
+
+  if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+    # incompatible ABI
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND")
+  else()
+    if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
+      # library has wrong major version
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND")
+    endif()
+  endif()
+
+  if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+    if (WIN32)
+      set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND")
+    endif()
+    unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE CACHE)
+    unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG CACHE)
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+  endif()
+endfunction()
+
+
 function (_PYTHON_VALIDATE_INCLUDE_DIR)
   if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
     return()
@@ -1065,7 +1166,7 @@
 
   if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
     # include file does not exist anymore
-    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
     return()
   endif()
@@ -1075,14 +1176,14 @@
 
   if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     # incompatible ABI
-    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
   else()
     if (_PVID_VERSION OR _PVID_IN_RANGE)
       if (_PVID_VERSION)
         if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version))
           # include dir has wrong version
-          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
         endif()
       endif()
@@ -1092,14 +1193,14 @@
         find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE)
         if (NOT in_range)
           # include dir has wrong version
-          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
         endif()
       endif()
     else()
       if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # include dir has wrong major version
-        set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+        set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
       endif()
     endif()
@@ -1139,6 +1240,14 @@
 
 function (_PYTHON_SET_DEVELOPMENT_MODULE_FOUND module)
   if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
+    if (module STREQUAL "SABIModule"
+        AND "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_LESS "3.2")
+      # Stable API was introduced in version 3.2
+      set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE PARENT_SCOPE)
+      _python_add_reason_failure ("Development" "SABIModule requires version 3.2 or upper.")
+      return()
+    endif()
+
     string(TOUPPER "${module}" id)
     set (module_found TRUE)
 
@@ -1146,6 +1255,10 @@
         AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
       set (module_found FALSE)
     endif()
+    if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS
+        AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+      set (module_found FALSE)
+    endif()
     if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS
         AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
       set (module_found FALSE)
@@ -1192,7 +1305,7 @@
   list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Development.Module" "Development.Embed")
 endif()
 list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS)
-foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development Development.Module Development.Embed NumPy)
+foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development Development.Module Development.SABIModule Development.Embed NumPy)
   set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE)
 endforeach()
 if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development)
@@ -1202,6 +1315,7 @@
 
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS)
+unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
 unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS)
 if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
   if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$")
@@ -1209,10 +1323,16 @@
   endif()
   list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "INCLUDE_DIR")
 endif()
+if ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
+  if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$")
+    list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "SABI_LIBRARY")
+  endif()
+  list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "INCLUDE_DIR")
+endif()
 if ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
   list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS "LIBRARY" "INCLUDE_DIR")
 endif()
-set (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS})
+set (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS})
 list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
 
 # Set versions to search
@@ -1271,6 +1391,7 @@
   endif()
 endif()
 unset (${_PYTHON_PREFIX}_SOABI)
+unset (${_PYTHON_PREFIX}_SOSABI)
 
 # Define lookup strategy
 cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY)
@@ -1293,6 +1414,7 @@
 if (CMAKE_SIZEOF_VOID_P)
   math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8")
   if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+      OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
       OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
     # In this case, search only for 64bit or 32bit
     set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH})
@@ -1483,6 +1605,9 @@
     if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
       list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:")
     endif()
+    if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
+      list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:")
+    endif()
     if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
       list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:")
     endif()
@@ -1499,6 +1624,9 @@
           _python_validate_library (CHECK_EXISTS)
         endif()
       endif()
+      if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
+          _python_validate_sabi_library (CHECK_EXISTS)
+      endif()
       if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
         if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT)
           _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS)
@@ -1515,12 +1643,18 @@
         unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE)
         unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
       endif()
+      if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
+        unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE CACHE)
+        unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE)
+      endif()
       if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
         unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
       endif()
     endif()
     if (("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS
           AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+        OR ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS
+          AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
         OR ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS
           AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR))
       unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
@@ -1536,6 +1670,9 @@
     if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
       list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:")
     endif()
+    if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
+      list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:")
+    endif()
     if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS)
       list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:")
     endif()
@@ -1546,13 +1683,16 @@
   endif()
 endfunction()
 
-
 unset (_${_PYTHON_PREFIX}_REQUIRED_VARS)
 unset (_${_PYTHON_PREFIX}_CACHED_VARS)
 unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE)
 set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE INTERNAL "Interpreter reason failure")
 unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE)
 set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE INTERNAL "Compiler reason failure")
+foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+  unset (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE)
+  set (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE CACHE INTERNAL "Development ${artifact} reason failure")
+endforeach()
 unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE)
 set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE INTERNAL "Development reason failure")
 unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE)
@@ -1889,6 +2029,13 @@
   endif()
 
   if (_${_PYTHON_PREFIX}_EXECUTABLE AND _${_PYTHON_PREFIX}_EXECUTABLE_USABLE)
+    list (LENGTH _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES _properties_length)
+    if (NOT _properties_length EQUAL "12")
+      # cache variable comes from some older Python module version: not usable
+      unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE)
+    endif()
+    unset (_properties_length)
+
     if (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES)
       set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE)
 
@@ -1903,11 +2050,12 @@
 
       list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 5 _${_PYTHON_PREFIX}_ABIFLAGS)
       list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 6 ${_PYTHON_PREFIX}_SOABI)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 7 ${_PYTHON_PREFIX}_SOSABI)
 
-      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 7 ${_PYTHON_PREFIX}_STDLIB)
-      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 8 ${_PYTHON_PREFIX}_STDARCH)
-      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 9 ${_PYTHON_PREFIX}_SITELIB)
-      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 10 ${_PYTHON_PREFIX}_SITEARCH)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 8 ${_PYTHON_PREFIX}_STDLIB)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 9 ${_PYTHON_PREFIX}_STDARCH)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 10 ${_PYTHON_PREFIX}_SITELIB)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 11 ${_PYTHON_PREFIX}_SITEARCH)
     else()
       string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}")
       list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR)
@@ -2006,10 +2154,11 @@
         endif()
 
         _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI)
+        _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI)
 
         # store properties in the cache to speed-up future searches
         set (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES
-          "${${_PYTHON_PREFIX}_INTERPRETER_ID};${${_PYTHON_PREFIX}_VERSION_MAJOR};${${_PYTHON_PREFIX}_VERSION_MINOR};${${_PYTHON_PREFIX}_VERSION_PATCH};${_${_PYTHON_PREFIX}_ARCH};${_${_PYTHON_PREFIX}_ABIFLAGS};${${_PYTHON_PREFIX}_SOABI};${${_PYTHON_PREFIX}_STDLIB};${${_PYTHON_PREFIX}_STDARCH};${${_PYTHON_PREFIX}_SITELIB};${${_PYTHON_PREFIX}_SITEARCH}" CACHE INTERNAL "${_PYTHON_PREFIX} Properties")
+          "${${_PYTHON_PREFIX}_INTERPRETER_ID};${${_PYTHON_PREFIX}_VERSION_MAJOR};${${_PYTHON_PREFIX}_VERSION_MINOR};${${_PYTHON_PREFIX}_VERSION_PATCH};${_${_PYTHON_PREFIX}_ARCH};${_${_PYTHON_PREFIX}_ABIFLAGS};${${_PYTHON_PREFIX}_SOABI};${${_PYTHON_PREFIX}_SOSABI};${${_PYTHON_PREFIX}_STDLIB};${${_PYTHON_PREFIX}_STDARCH};${${_PYTHON_PREFIX}_SITELIB};${${_PYTHON_PREFIX}_SITEARCH}" CACHE INTERNAL "${_PYTHON_PREFIX} Properties")
       else()
         unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE)
         unset (${_PYTHON_PREFIX}_INTERPRETER_ID)
@@ -2390,6 +2539,14 @@
     list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS)
   endif()
 endif()
+if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.SABIModule)
+  if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
+    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_SABI_LIBRARIES)
+  endif()
+  if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
+    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS)
+  endif()
+endif()
 if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed)
   if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS)
     list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES)
@@ -2401,6 +2558,7 @@
 list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_REQUIRED_VARS)
 ## Development environment is not compatible with IronPython interpreter
 if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+      OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
       OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
     AND ((${_PYTHON_PREFIX}_Interpreter_FOUND
         AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython")
@@ -2421,11 +2579,18 @@
                                               _${_PYTHON_PREFIX}_LIBRARY_DEBUG
                                               _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)
   endif()
+  if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                                              _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE
+                                              _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG
+                                              _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG)
+  endif()
   if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
     list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_INCLUDE_DIR)
   endif()
 
   _python_check_development_signature (Module)
+  _python_check_development_signature (SABIModule)
   _python_check_development_signature (Embed)
 
   if (DEFINED ${_PYTHON_PREFIX}_LIBRARY
@@ -2434,6 +2599,12 @@
     unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
     unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
   endif()
+  if (DEFINED ${_PYTHON_PREFIX}_SABI_LIBRARY
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_SABI_LIBRARY}")
+    set (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_SABI_LIBRARY}" CACHE INTERNAL "")
+    unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE)
+    unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
+  endif()
   if (DEFINED ${_PYTHON_PREFIX}_INCLUDE_DIR
       AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_INCLUDE_DIR}")
     set (_${_PYTHON_PREFIX}_INCLUDE_DIR "${${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE INTERNAL "")
@@ -2450,7 +2621,8 @@
     endif()
   endif()
 
-  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+      OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
     # if python interpreter is found, use it to look-up for artifacts
     # to ensure consistency between interpreter and development environments.
     # If not, try to locate a compatible config tool
@@ -2843,8 +3015,10 @@
     set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
 
     if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
-      set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+    else()
+      unset (_${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE CACHE)
     endif()
 
     set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
@@ -2892,18 +3066,280 @@
     endif()
   endif()
 
+  if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+    if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+      ## compute artifact names
+      _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 POSIX LIBRARY)
+      _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 DEBUG)
+
+      if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS
+          AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+        # SABI_LIBRARY_RELEASE search is based on LIBRARY_RELEASE
+        set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+        get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
+
+        find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+          HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
+          NO_DEFAULT_PATH)
+      else()
+        if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+          # retrieve root install directory
+          _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
+
+          # enforce current ABI
+          _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS)
+
+          set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
+
+          # retrieve SABI library
+          ## compute some paths
+          if (_${_PYTHON_PREFIX}_CONFIG)
+            string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}")
+          else()
+            set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}")
+          endif()
+          _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY)
+
+          _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR)
+          list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}")
+
+          list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+          find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                        NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+        endif()
+
+        # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts
+        if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+          set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+          unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
+          if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+            set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX)
+          endif()
+
+          if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
+            # Paths suffixes
+            _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY)
+
+            # Framework Paths
+            _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS})
+            # Registry Paths
+            _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} )
+
+            if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+              find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                            NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                            NAMES_PER_DIR
+                            HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                            PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                  ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                            PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                            NO_CMAKE_PATH
+                            NO_CMAKE_ENVIRONMENT_PATH
+                            NO_SYSTEM_ENVIRONMENT_PATH
+                            NO_CMAKE_SYSTEM_PATH)
+            endif()
+
+            if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+              find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                            NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                            NAMES_PER_DIR
+                            HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                            PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                  ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                            PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                            NO_SYSTEM_ENVIRONMENT_PATH
+                            NO_CMAKE_SYSTEM_PATH)
+            endif()
+
+            # search in HINTS locations
+            find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                          NAMES_PER_DIR
+                          HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                          PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                          PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                          NO_SYSTEM_ENVIRONMENT_PATH
+                          NO_CMAKE_SYSTEM_PATH)
+
+            if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+              set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
+            else()
+              unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+            endif()
+
+            if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+              set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
+            else()
+              unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
+            endif()
+
+            # search in all default paths
+            find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                          NAMES_PER_DIR
+                          PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                                ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                          PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
+          else()
+            foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+              _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION})
+              _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION})
+
+              _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY)
+
+              if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+                find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                              NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                              NAMES_PER_DIR
+                              HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                              PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                    ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                              PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                              NO_CMAKE_PATH
+                              NO_CMAKE_ENVIRONMENT_PATH
+                              NO_SYSTEM_ENVIRONMENT_PATH
+                              NO_CMAKE_SYSTEM_PATH)
+              endif()
+
+              if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+                find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                              NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                              NAMES_PER_DIR
+                              HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                              PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                    ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                              PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                              NO_SYSTEM_ENVIRONMENT_PATH
+                              NO_CMAKE_SYSTEM_PATH)
+              endif()
+
+              # search in HINTS locations
+              find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                            NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                            NAMES_PER_DIR
+                            HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                            PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                            PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                            NO_SYSTEM_ENVIRONMENT_PATH
+                            NO_CMAKE_SYSTEM_PATH)
+
+              if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+                set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
+              else()
+                unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+              endif()
+
+              if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+                set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
+              else()
+                unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
+              endif()
+
+              # search in all default paths
+              find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                            NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                            NAMES_PER_DIR
+                            PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                                  ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                            PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
+
+              if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+                break()
+              endif()
+            endforeach()
+          endif()
+        endif()
+      endif()
+    endif()
+
+    # finalize library version information
+    _python_get_version (SABI_LIBRARY PREFIX _${_PYTHON_PREFIX}_)
+    # ABI library does not have the full version information
+    if (${_PYTHON_PREFIX}_Interpreter_FOUND OR _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+      # update from interpreter or library
+      set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION})
+      set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR})
+      set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR})
+      set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH})
+    endif()
+
+    set (${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}")
+
+    if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}")
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND")
+    else()
+      unset (_${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE CACHE)
+    endif()
+
+    if (WIN32 AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+      # search for debug library
+      get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
+      find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG
+                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+                    NAMES_PER_DIR
+                    HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
+                    NO_DEFAULT_PATH)
+      # second try including CMAKE variables to catch-up non conventional layouts
+      find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG
+                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+                    NAMES_PER_DIR
+                    NO_SYSTEM_ENVIRONMENT_PATH
+                    NO_CMAKE_SYSTEM_PATH)
+    endif()
+
+    # retrieve runtime libraries
+    if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+      get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
+      get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
+      _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE
+                                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                                    NAMES_PER_DIR
+                                    HINTS "${_${_PYTHON_PREFIX}_PATH}"
+                                          "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
+                                    PATH_SUFFIXES bin)
+    endif()
+
+    if (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG)
+      get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}" DIRECTORY)
+      get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
+      _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG
+                                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+                                    NAMES_PER_DIR
+                                    HINTS "${_${_PYTHON_PREFIX}_PATH}"
+                                          "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
+                                    PATH_SUFFIXES bin)
+    endif()
+  endif()
+
   if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
     while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
-      set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE)
-      foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module Embed)
+      set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE)
+      set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED FALSE)
+      foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module SABIModule Embed)
         string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID)
         if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
-            AND NOT "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS)
-          set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE)
+            AND "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS)
+          set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE)
+        endif()
+        if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+            AND "SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS)
+          set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED TRUE)
         endif()
       endforeach()
-      if (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED
+      if ((_${_PYTHON_PREFIX}_LIBRARY_REQUIRED
           AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+        AND (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED
+          AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE))
         # Don't search for include dir if no library was founded
         break()
       endif()
@@ -2941,6 +3377,21 @@
             get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY)
             list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
           endif()
+        elseif ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS
+            AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
+          # Use the library's install prefix as a hint
+          if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)")
+            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+          elseif (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config")
+            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+          elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
+            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+          else()
+            # assume library is in a directory under root
+            get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
+            get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY)
+            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
+          endif()
         endif()
 
         _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION})
@@ -3004,8 +3455,10 @@
     set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
 
     if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
-      set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+    else()
+      unset (_${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE CACHE)
     endif()
 
     if (_${_PYTHON_PREFIX}_INCLUDE_DIR)
@@ -3071,36 +3524,59 @@
     endif()
   endif()
 
-  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR)
+  if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+    set (${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}")
+    _python_select_library_configurations (${_PYTHON_PREFIX}_SABI)
+
+    set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}")
+    set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}")
+
+    if (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE)
+      set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}")
+    elseif (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG)
+      set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}")
+    else()
+      set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY-NOTFOUND")
+    endif()
+
+    _python_set_library_dirs (${_PYTHON_PREFIX}_SABI_LIBRARY_DIRS
+                              _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                              _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG)
+    if (UNIX)
+      if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$")
+        set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS})
+      endif()
+    else()
+      _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS
+                                _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE
+                                _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG)
+    endif()
+  endif()
+
+  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR)
     if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND)
       # development environment must be compatible with interpreter/compiler
       if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}"
           AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}")
         _python_set_development_module_found (Module)
+        _python_set_development_module_found (SABIModule)
         _python_set_development_module_found (Embed)
       endif()
     elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR
         AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}")
       _python_set_development_module_found (Module)
+      _python_set_development_module_found (SABIModule)
       _python_set_development_module_found (Embed)
     endif()
     if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND
         (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS
           OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS))
       set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE)
+      set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE)
     endif()
   endif()
 
-  if (( ${_PYTHON_PREFIX}_Development.Module_FOUND
-        AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)
-      OR (NOT "Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
-        AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)
-      OR (NOT "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
-        AND ${_PYTHON_PREFIX}_Development.Module_FOUND))
-    unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE)
-  endif()
-
   if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
       AND ${_PYTHON_PREFIX}_Development.Module_FOUND
       AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)
@@ -3108,13 +3584,14 @@
   endif()
 
   if ((${_PYTHON_PREFIX}_Development.Module_FOUND
-      OR ${_PYTHON_PREFIX}_Development.Embed_FOUND)
-    AND EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/PyPy.h")
-  # retrieve PyPy version
-  file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" ${_PYTHON_PREFIX}_PyPy_VERSION
-                REGEX "^#define[ \t]+PYPY_VERSION[ \t]+\"[^\"]+\"")
-  string (REGEX REPLACE "^#define[ \t]+PYPY_VERSION[ \t]+\"([^\"]+)\".*" "\\1"
-                ${_PYTHON_PREFIX}_PyPy_VERSION "${${_PYTHON_PREFIX}_PyPy_VERSION}")
+        OR ${_PYTHON_PREFIX}_Development.SABIModule_FOUND
+        OR ${_PYTHON_PREFIX}_Development.Embed_FOUND)
+      AND EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/PyPy.h")
+    # retrieve PyPy version
+    file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" ${_PYTHON_PREFIX}_PyPy_VERSION
+          REGEX "^#define[ \t]+PYPY_VERSION[ \t]+\"[^\"]+\"")
+    string (REGEX REPLACE "^#define[ \t]+PYPY_VERSION[ \t]+\"([^\"]+)\".*" "\\1"
+            ${_PYTHON_PREFIX}_PyPy_VERSION "${${_PYTHON_PREFIX}_PyPy_VERSION}")
   endif()
 
   unset(${_PYTHON_PREFIX}_LINK_OPTIONS)
@@ -3144,7 +3621,12 @@
     _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI)
   endif()
 
+  if (NOT DEFINED ${_PYTHON_PREFIX}_SOSABI)
+    _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI)
+  endif()
+
   _python_compute_development_signature (Module)
+  _python_compute_development_signature (SABIModule)
   _python_compute_development_signature (Embed)
 
   # Restore the original find library ordering
@@ -3156,6 +3638,9 @@
     if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
       set (${_PYTHON_PREFIX}_LIBRARY "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} Library")
     endif()
+    if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+      set (${_PYTHON_PREFIX}_SABI_LIBRARY "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} ABI Library")
+    endif()
     if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
       set (${_PYTHON_PREFIX}_INCLUDE_DIR "${_${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} Include Directory")
     endif()
@@ -3165,6 +3650,10 @@
                             _${_PYTHON_PREFIX}_LIBRARY_DEBUG
                             _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
                             _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
+                            _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE
+                            _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG
+                            _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE
+                            _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG
                             _${_PYTHON_PREFIX}_INCLUDE_DIR
                             _${_PYTHON_PREFIX}_CONFIG
                             _${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE
@@ -3259,6 +3748,13 @@
 
 unset (_${_PYTHON_PREFIX}_REASON_FAILURE)
 foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy)
+  if (_${_PYTHON_PREFIX}_COMPONENT STREQUAL "Development")
+    foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
+      if (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE)
+        _python_add_reason_failure ("Development" "${_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE}")
+      endif()
+    endforeach()
+  endif()
   if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE)
     string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n        ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}")
     unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE CACHE)
@@ -3292,12 +3788,19 @@
 
   if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
         AND ${_PYTHON_PREFIX}_Development.Module_FOUND)
+      OR ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+        AND ${_PYTHON_PREFIX}_Development.SABIModule_FOUND)
       OR ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
         AND ${_PYTHON_PREFIX}_Development.Embed_FOUND))
 
     macro (__PYTHON_IMPORT_LIBRARY __name)
-      if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$"
-          OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
+      if (${ARGC} GREATER 1)
+        set (_PREFIX "${ARGV1}_")
+      else()
+        set (_PREFIX "")
+      endif()
+      if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$"
+          OR ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE)
         set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED)
       else()
         set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC)
@@ -3310,37 +3813,37 @@
       set_property (TARGET ${__name}
                     PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}")
 
-      if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
+      if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE)
         # System manage shared libraries in two parts: import and runtime
-        if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+        if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG)
           set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG)
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
-                                            IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}"
-                                            IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}")
+                                            IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}"
+                                            IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}RUNTIME_LIBRARY_RELEASE}")
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
-                                            IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}"
-                                            IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}")
+                                            IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}"
+                                            IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_DEBUG}")
         else()
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C"
-                                            IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARIES}"
-                                            IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}")
+                                            IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARIES}"
+                                            IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE}")
         endif()
       else()
-        if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+        if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG)
           set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG)
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
-                                            IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
+                                            IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}")
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
-                                            IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}")
+                                            IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}")
         else()
           set_target_properties (${__name}
                                  PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C"
-                                            IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
+                                            IMPORTED_LOCATION "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}")
         endif()
       endif()
 
@@ -3359,6 +3862,28 @@
       endif()
     endmacro()
 
+    macro (__PYTHON_IMPORT_MODULE __name)
+      if (NOT TARGET ${__name})
+        add_library (${__name} INTERFACE IMPORTED)
+      endif()
+      set_property (TARGET ${__name}
+                    PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}")
+
+      # When available, enforce shared library generation with undefined symbols
+      if (APPLE)
+        set_property (TARGET ${__name}
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
+      endif()
+      if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+        set_property (TARGET ${__name}
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs")
+      endif()
+      if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+        set_property (TARGET ${__name}
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok")
+      endif()
+    endmacro()
+
     if (${_PYTHON_PREFIX}_Development.Embed_FOUND)
       __python_import_library (${_PYTHON_PREFIX}::Python)
     endif()
@@ -3369,25 +3894,15 @@
         # but ALIAS cannot be used because the imported library is not GLOBAL.
         __python_import_library (${_PYTHON_PREFIX}::Module)
       else()
-        if (NOT TARGET ${_PYTHON_PREFIX}::Module)
-          add_library (${_PYTHON_PREFIX}::Module INTERFACE IMPORTED)
-        endif()
-        set_property (TARGET ${_PYTHON_PREFIX}::Module
-                      PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}")
+        __python_import_module (${_PYTHON_PREFIX}::Module)
+      endif()
+    endif()
 
-        # When available, enforce shared library generation with undefined symbols
-        if (APPLE)
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
-        endif()
-        if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs")
-        endif()
-        if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok")
-        endif()
+    if (${_PYTHON_PREFIX}_Development.SABIModule_FOUND)
+      if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS)
+        __python_import_library (${_PYTHON_PREFIX}::SABIModule SABI)
+      else()
+        __python_import_module (${_PYTHON_PREFIX}::SABIModule)
       endif()
     endif()
 
@@ -3396,7 +3911,7 @@
     # It is used to build modules for python.
     #
     function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name)
-      cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE;WITH_SOABI" "" "")
+      cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE;WITH_SOABI" "USE_SABI" "")
 
       if (PYTHON_ADD_LIBRARY_STATIC)
         set (type STATIC)
@@ -3406,9 +3921,51 @@
         set (type MODULE)
       endif()
 
-      if (type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Module)
-        message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Module' is not defined.\n   Did you miss to request COMPONENT 'Development.Module'?")
-        return()
+      if (PYTHON_ADD_LIBRARY_USE_SABI)
+        if (NOT type STREQUAL MODULE)
+          message (SEND_ERROR "${prefix}_ADD_LIBRARY: 'USE_SABI' option is only valid for 'MODULE' type.")
+          return()
+        endif()
+        if (NOT PYTHON_ADD_LIBRARY_USE_SABI MATCHES "^(3)(\\.([0-9]+))?$")
+          message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: wrong version specified for 'USE_SABI'.")
+          return()
+        endif()
+        # compute value for Py_LIMITED_API macro
+        set (major_version "${CMAKE_MATCH_1}")
+        unset (minor_version)
+        if (CMAKE_MATCH_3)
+          set (minor_version "${CMAKE_MATCH_3}")
+        endif()
+        if (major_version EQUAL "3" AND NOT minor_version)
+          set (Py_LIMITED_API "3")
+        elseif ("${major_version}.${minor_version}" VERSION_LESS "3.2")
+          message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: invalid version. Version must be '3.2' or upper.")
+          return()
+        else()
+          set (Py_LIMITED_API "0x0${major_version}")
+          if (NOT minor_version)
+            string (APPEND Py_LIMITED_API "00")
+          else()
+            if (minor_version LESS 16)
+              string (APPEND Py_LIMITED_API "0")
+            endif()
+            math (EXPR minor_version "${minor_version}" OUTPUT_FORMAT HEXADECIMAL)
+            string (REGEX REPLACE "^0x(.+)$" "\\1" minor_version "${minor_version}")
+            string (APPEND Py_LIMITED_API "${minor_version}")
+          endif()
+          string (APPEND Py_LIMITED_API "0000")
+        endif()
+      endif()
+
+      if (type STREQUAL "MODULE")
+        if (PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::SABIModule)
+          message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::SABIModule' is not defined.\n   Did you miss to request COMPONENT 'Development.SABIModule'?")
+          return()
+        endif()
+        if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::Module)
+          message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Module' is not defined.\n   Did you miss to request COMPONENT 'Development.Module'?")
+          return()
+        endif()
       endif()
       if (NOT type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Python)
         message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Python' is not defined.\n   Did you miss to request COMPONENT 'Development.Embed'?")
@@ -3420,23 +3977,37 @@
       get_property (type TARGET ${name} PROPERTY TYPE)
 
       if (type STREQUAL "MODULE_LIBRARY")
-        target_link_libraries (${name} PRIVATE ${prefix}::Module)
+        if (PYTHON_ADD_LIBRARY_USE_SABI)
+          target_compile_definitions (${name} PRIVATE Py_LIMITED_API=${Py_LIMITED_API})
+          target_link_libraries (${name} PRIVATE ${prefix}::SABIModule)
+        else()
+          target_link_libraries (${name} PRIVATE ${prefix}::Module)
+        endif()
         # customize library name to follow module name rules
         set_property (TARGET ${name} PROPERTY PREFIX "")
         if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
           set_property (TARGET ${name} PROPERTY SUFFIX ".pyd")
         endif()
 
-        if (PYTHON_ADD_LIBRARY_WITH_SOABI AND ${prefix}_SOABI)
-          get_property (suffix TARGET ${name} PROPERTY SUFFIX)
-          if (NOT suffix)
-            set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}")
+        if (PYTHON_ADD_LIBRARY_WITH_SOABI)
+          if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOABI)
+            get_property (suffix TARGET ${name} PROPERTY SUFFIX)
+            if (NOT suffix)
+              set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}")
+            endif()
+            set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOABI}${suffix}")
           endif()
-          set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOABI}${suffix}")
+          if (PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOSABI)
+            get_property (suffix TARGET ${name} PROPERTY SUFFIX)
+            if (NOT suffix)
+              set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}")
+            endif()
+            set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOSABI}${suffix}")
+          endif()
         endif()
       else()
-        if (PYTHON_ADD_LIBRARY_WITH_SOABI)
-          message (AUTHOR_WARNING "Find${prefix}: Option `WITH_SOABI` is only supported for `MODULE` library type.")
+        if (PYTHON_ADD_LIBRARY_WITH_SOABI OR PYTHON_ADD_LIBRARY_USE_SABI)
+          message (AUTHOR_WARNING "Find${prefix}: Options 'WITH_SOABI' and 'USE_SABI' are only supported for `MODULE` library type.")
         endif()
         target_link_libraries (${name} PRIVATE ${prefix}::Python)
       endif()
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index 837300e..ae086e8 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -31,6 +31,13 @@
     * ``Development.Embed``: search for artifacts for Python 3 embedding
       developments.
 
+  .. versionadded:: 3.26
+
+    * ``Development.SABIModule``: search for artifacts for Python 3 module
+      developments using the
+      `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_.
+      This component is available only for version ``3.2`` and upper.
+
 * ``NumPy``: search for NumPy include directories.
 
 .. versionadded:: 3.14
@@ -81,6 +88,12 @@
   Python 3 library for Python module. Target defined if component
   ``Development.Module`` is found.
 
+``Python3::SABIModule``
+  .. versionadded:: 3.26
+
+  Python 3 library for Python module using the Stable Application Binary
+  Interface. Target defined if component ``Development.SABIModule`` is found.
+
 ``Python3::Python``
   Python 3 library for Python embedding. Target defined if component
   ``Development.Embed`` is found.
@@ -146,6 +159,16 @@
   not available, ``sysconfig.get_config_var('EXT_SUFFIX')`` or
   ``sysconfig.get_config_var('SOABI')`` are used.
 
+``Python3_SOSABI``
+  .. versionadded:: 3.26
+
+  Extension suffix for modules using the Stable Application Binary Interface.
+
+  Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the
+  COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3``
+  except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty
+  string.
+
 ``Python3_Compiler_FOUND``
   System has the Python 3 compiler.
 ``Python3_COMPILER``
@@ -168,6 +191,12 @@
 
   System has the Python 3 development artifacts for Python module.
 
+``Python3_Development.SABIModule_FOUND``
+  .. versionadded:: 3.26
+
+  System has the Python 3 development artifacts for Python module using the
+  Stable Application Binary Interface.
+
 ``Python3_Development.Embed_FOUND``
   .. versionadded:: 3.18
 
@@ -189,6 +218,18 @@
   The Python 3 library directories.
 ``Python3_RUNTIME_LIBRARY_DIRS``
   The Python 3 runtime library directories.
+``Python3_SABI_LIBRARIES``
+  .. versionadded:: 3.26
+
+  The Python 3 libraries for the Stable Application Binary Interface.
+``Python3_SABI_LIBRARY_DIRS``
+  .. versionadded:: 3.26
+
+  The Python 3 ``SABI`` library directories.
+``Python3_RUNTIME_SABI_LIBRARY_DIRS``
+  .. versionadded:: 3.26
+
+  The Python 3 runtime ``SABI`` library directories.
 ``Python3_VERSION``
   Python 3 version.
 ``Python3_VERSION_MAJOR``
@@ -423,6 +464,13 @@
   variables ``Python3_LIBRARIES``, ``Python3_LIBRARY_DIRS`` and
   ``Python3_RUNTIME_LIBRARY_DIRS``.
 
+``Python3_SABI_LIBRARY``
+  .. versionadded:: 3.26
+
+  The path to the library for Stable Application Binary Interface. It will be
+  used to compute the variables ``Python3_SABI_LIBRARIES``,
+  ``Python3_SABI_LIBRARY_DIRS`` and ``Python3_RUNTIME_SABI_LIBRARY_DIRS``.
+
 ``Python3_INCLUDE_DIR``
   The path to the directory of the ``Python`` headers. It will be used to
   compute the variable ``Python3_INCLUDE_DIRS``.
@@ -468,10 +516,11 @@
 This module defines the command ``Python3_add_library`` (when
 :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as
 :command:`add_library` and adds a dependency to target ``Python3::Python`` or,
-when library type is ``MODULE``, to target ``Python3::Module`` and takes care
+when library type is ``MODULE``, to target ``Python3::Module`` or
+``Python3::SABIModule`` (when ``USE_SABI`` option is specified) and takes care
 of Python module naming rules::
 
-  Python3_add_library (<name> [STATIC | SHARED | MODULE [WITH_SOABI]]
+  Python3_add_library (<name> [STATIC | SHARED | MODULE [USE_SABI <version>] [WITH_SOABI]]
                        <source1> [<source2> ...])
 
 If the library type is not specified, ``MODULE`` is assumed.
@@ -479,6 +528,19 @@
 .. versionadded:: 3.17
   For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the
   module suffix will include the ``Python3_SOABI`` value, if any.
+
+.. versionadded:: 3.26
+  For ``MODULE`` type, if the option ``USE_SABI`` is specified, the
+  preprocessor definition ``Py_LIMITED_API`` will be specified, as ``PRIVATE``,
+  for the target ``<name>`` with the value computed from ``<version>`` argument.
+  The expected format for ``<version>`` is ``major[.minor]``, where each
+  component is a numeric value. If ``minor`` component is specified, the
+  version should be, at least, ``3.2`` which is the version where the
+  `Stable Application Binary Interface <https://docs.python.org/3/c-api/stable.html>`_
+  was introduced. Specifying only major version ``3`` is equivalent to ``3.2``.
+
+  When option ``WITH_SOABI`` is also specified,  the module suffix will include
+  the ``Python3_SOSABI`` value, if any.
 #]=======================================================================]
 
 
diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index b14349f..d82f41d 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -71,9 +71,6 @@
 
 .. versionadded:: 3.18
 
-``Ruby_ROOT_DIR``
-  Define the root directory of a Ruby installation.
-
 ``Ruby_FIND_VIRTUALENV``
   This variable defines the handling of virtual environments managed by
   ``rvm``. It is meaningful only when a virtual environment
diff --git a/Modules/Platform/Android.cmake b/Modules/Platform/Android.cmake
index 09a12cc..d0f686c 100644
--- a/Modules/Platform/Android.cmake
+++ b/Modules/Platform/Android.cmake
@@ -5,6 +5,7 @@
 endif()
 
 include(Platform/Linux)
+unset(LINUX)
 
 set(ANDROID 1)
 
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index bf96e63..088b238 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -157,7 +157,8 @@
   endif()
 
   if(NOT CMAKE_RC_COMPILER_INIT AND NOT CMAKE_GENERATOR_RC)
-    set(CMAKE_RC_COMPILER_INIT ${_CMAKE_TOOLCHAIN_PREFIX}windres windres)
+    set(_CMAKE_RC_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}windres windres)
+    set(_CMAKE_RC_COMPILER_FALLBACK windres)
   endif()
 
   enable_language(RC)
diff --git a/Modules/Platform/Windows.cmake b/Modules/Platform/Windows.cmake
index 5263161..d8b3957 100644
--- a/Modules/Platform/Windows.cmake
+++ b/Modules/Platform/Windows.cmake
@@ -19,8 +19,8 @@
 set(CMAKE_DL_LIBS "")
 set(CMAKE_EXTRA_LINK_EXTENSIONS ".targets")
 
-set(CMAKE_FIND_LIBRARY_PREFIXES "" "lib")
-set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a")
+set(CMAKE_FIND_LIBRARY_PREFIXES "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
 
 # for borland make long command lines are redirected to a file
 # with the following syntax, see Windows-bcc32.cmake for use
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index c8a21dc..d2236b1 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 25)
-set(CMake_VERSION_PATCH 20221127)
+set(CMake_VERSION_PATCH 20221201)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx
index 5e5cc0b..f25856a 100644
--- a/Source/LexerParser/cmFortranParser.cxx
+++ b/Source/LexerParser/cmFortranParser.cxx
@@ -548,16 +548,16 @@
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  2
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   432
+#define YYLAST   433
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  41
 /* YYNNTS -- Number of nonterminals.  */
 #define YYNNTS  14
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  64
+#define YYNRULES  65
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  121
+#define YYNSTATES  123
 
 /* YYMAXUTOK -- Last valid token kind.  */
 #define YYMAXUTOK   295
@@ -608,15 +608,15 @@
 
 #if YYDEBUG
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_uint8 yyrline[] =
+static const yytype_int16 yyrline[] =
 {
        0,   106,   106,   106,   109,   113,   118,   123,   129,   136,
-     141,   145,   150,   162,   167,   172,   177,   182,   187,   192,
-     197,   202,   206,   210,   214,   218,   219,   224,   224,   224,
-     225,   225,   226,   226,   227,   227,   228,   228,   229,   229,
-     230,   230,   231,   231,   232,   232,   233,   233,   236,   237,
-     238,   239,   240,   241,   242,   243,   244,   245,   246,   247,
-     248,   249,   250,   251,   252
+     141,   146,   150,   155,   167,   172,   177,   182,   187,   192,
+     197,   202,   207,   211,   215,   219,   223,   224,   229,   229,
+     229,   230,   230,   231,   231,   232,   232,   233,   233,   234,
+     234,   235,   235,   236,   236,   237,   237,   238,   238,   241,
+     242,   243,   244,   245,   246,   247,   248,   249,   250,   251,
+     252,   253,   254,   255,   256,   257
 };
 #endif
 
@@ -670,15 +670,15 @@
      -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,
      -39,   -39,   -39,   -39,   -39,   -39,   -25,   -19,    20,    -8,
      -15,   -22,   -39,    -6,    14,    15,    16,    17,   -39,   -39,
-     -39,   -39,   -39,   -39,    59,    49,    51,   -39,    63,    64,
-      35,    36,    37,   -39,   -39,   -39,   -39,   -39,   -39,    74,
-     112,   127,   165,   180,   -39,   -39,   -39,   -39,   -39,   -39,
+     -39,   -39,   -39,   -39,    59,    51,    48,   -39,    63,    64,
+      35,    36,    37,   -39,   -39,   -39,   -39,   -39,   -39,    75,
+     113,   128,   166,   181,   -39,   -39,   -39,   -39,   -39,   -39,
      -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,
-     -39,   -39,   -39,   -39,   -39,   -39,   -39,   -20,    43,   -39,
-     218,   233,   271,   286,   324,   339,   -39,   -39,   -39,   -39,
-     -39,    39,    40,    41,   377,   -39,   -39,   -39,   -39,   -39,
-     -39,    46,    78,   -39,   -39,    50,   -39,   392,    79,   -39,
-     -39
+     -39,   -39,   -39,   -39,    68,   -39,   -39,   -39,   -20,    44,
+     -39,   219,   234,   272,   287,   325,   340,   -39,   -39,   -39,
+     -39,   -39,   -39,    40,    41,    42,   378,   -39,   -39,   -39,
+     -39,   -39,   -39,    46,    79,   -39,   -39,    50,   -39,   393,
+      90,   -39,   -39
 };
 
 /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -686,19 +686,19 @@
    means the default is an error.  */
 static const yytype_int8 yydefact[] =
 {
-       2,     0,     1,     0,    25,     0,    27,    28,    29,    31,
-      30,    33,    32,    34,    36,    38,    42,    40,    44,    35,
-      37,    39,    43,    41,    45,    46,     0,     0,     0,     0,
-       0,     0,     3,     0,     0,     0,     0,     0,    46,    46,
-      46,    46,    26,    46,     0,     0,     0,     4,     0,     0,
-       0,     0,     0,    46,    46,    46,    46,    46,    46,     0,
-       0,     0,     0,     0,    15,    57,    56,    64,    62,    58,
-      59,    60,    61,    63,    55,    48,    49,    50,    51,    52,
-      53,    54,    47,    10,    13,     9,     6,     0,     0,    46,
-       0,     0,     0,     0,     0,     0,    21,    22,    23,    24,
-      14,     0,     0,     0,     0,     5,    16,    17,    18,    19,
-      20,     0,     0,    46,    11,     0,     7,     0,     0,    12,
-       8
+       2,     0,     1,     0,    26,     0,    28,    29,    30,    32,
+      31,    34,    33,    35,    37,    39,    43,    41,    45,    36,
+      38,    40,    44,    42,    46,    47,     0,     0,     0,     0,
+       0,     0,     3,     0,     0,     0,     0,     0,    47,    47,
+      47,    47,    27,    47,     0,     0,     0,     4,     0,     0,
+       0,     0,     0,    47,    47,    47,    47,    47,    47,     0,
+       0,     0,     0,     0,    16,    58,    57,    65,    63,    59,
+      60,    61,    62,    64,    56,    49,    50,    51,    52,    53,
+      54,    55,    48,    11,     0,    14,     9,     6,     0,     0,
+      47,     0,     0,     0,     0,     0,     0,    22,    23,    24,
+      25,    15,    10,     0,     0,     0,     0,     5,    17,    18,
+      19,    20,    21,     0,     0,    47,    12,     0,     7,     0,
+       0,    13,     8
 };
 
 /* YYPGOTO[NTERM-NUM].  */
@@ -720,50 +720,50 @@
    number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int8 yytable[] =
 {
-      59,    60,    61,    62,    51,    63,    52,   101,    42,    43,
-     102,    53,    45,    46,    50,    90,    91,    92,    93,    94,
-      95,     2,     3,    47,     4,    49,    54,     5,     6,     7,
+      59,    60,    61,    62,    51,    63,    52,   103,    42,    43,
+     104,    53,    45,    46,    50,    91,    92,    93,    94,    95,
+      96,     2,     3,    47,     4,    49,    54,     5,     6,     7,
        8,     9,    10,    11,    12,    13,    14,    15,    16,    17,
       18,    19,    20,    21,    22,    23,    24,    55,    56,    57,
-      58,   104,    83,    48,    84,    25,    26,    27,    28,    29,
-      30,    31,    64,    65,    66,    67,    85,    86,    87,    88,
-      89,   103,   111,   112,   113,   117,   115,    96,    65,    66,
-      67,   116,   120,   118,     0,    68,    69,    70,    71,    72,
+      58,    85,   106,    48,    83,    25,    26,    27,    28,    29,
+      30,    31,    64,    65,    66,    67,    86,    87,    88,    89,
+      90,   102,   105,   113,   114,   115,   117,   119,    97,    65,
+      66,    67,   118,   120,    84,    68,    69,    70,    71,    72,
+      73,    74,    75,   122,    76,    77,    78,    79,    80,    81,
+       0,    68,    69,    70,    71,    72,    73,    74,    75,     0,
+      76,    77,    78,    79,    80,    81,    98,    65,    66,    67,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    99,    65,    66,    67,     0,     0,     0,     0,    68,
+      69,    70,    71,    72,    73,    74,    75,     0,    76,    77,
+      78,    79,    80,    81,    68,    69,    70,    71,    72,    73,
+      74,    75,     0,    76,    77,    78,    79,    80,    81,   100,
+      65,    66,    67,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   101,    65,    66,    67,     0,     0,
+       0,     0,    68,    69,    70,    71,    72,    73,    74,    75,
+       0,    76,    77,    78,    79,    80,    81,    68,    69,    70,
+      71,    72,    73,    74,    75,     0,    76,    77,    78,    79,
+      80,    81,   107,    65,    66,    67,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   108,    65,    66,
+      67,     0,     0,     0,     0,    68,    69,    70,    71,    72,
       73,    74,    75,     0,    76,    77,    78,    79,    80,    81,
       68,    69,    70,    71,    72,    73,    74,    75,     0,    76,
-      77,    78,    79,    80,    81,    97,    65,    66,    67,     0,
+      77,    78,    79,    80,    81,   109,    65,    66,    67,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-      98,    65,    66,    67,     0,     0,     0,     0,    68,    69,
+     110,    65,    66,    67,     0,     0,     0,     0,    68,    69,
       70,    71,    72,    73,    74,    75,     0,    76,    77,    78,
       79,    80,    81,    68,    69,    70,    71,    72,    73,    74,
-      75,     0,    76,    77,    78,    79,    80,    81,    99,    65,
+      75,     0,    76,    77,    78,    79,    80,    81,   111,    65,
       66,    67,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,   100,    65,    66,    67,     0,     0,     0,
+       0,     0,     0,   112,    65,    66,    67,     0,     0,     0,
        0,    68,    69,    70,    71,    72,    73,    74,    75,     0,
       76,    77,    78,    79,    80,    81,    68,    69,    70,    71,
       72,    73,    74,    75,     0,    76,    77,    78,    79,    80,
-      81,   105,    65,    66,    67,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,   106,    65,    66,    67,
+      81,   116,    65,    66,    67,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   121,    65,    66,    67,
        0,     0,     0,     0,    68,    69,    70,    71,    72,    73,
       74,    75,     0,    76,    77,    78,    79,    80,    81,    68,
       69,    70,    71,    72,    73,    74,    75,     0,    76,    77,
-      78,    79,    80,    81,   107,    65,    66,    67,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,   108,
-      65,    66,    67,     0,     0,     0,     0,    68,    69,    70,
-      71,    72,    73,    74,    75,     0,    76,    77,    78,    79,
-      80,    81,    68,    69,    70,    71,    72,    73,    74,    75,
-       0,    76,    77,    78,    79,    80,    81,   109,    65,    66,
-      67,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,   110,    65,    66,    67,     0,     0,     0,     0,
-      68,    69,    70,    71,    72,    73,    74,    75,     0,    76,
-      77,    78,    79,    80,    81,    68,    69,    70,    71,    72,
-      73,    74,    75,     0,    76,    77,    78,    79,    80,    81,
-     114,    65,    66,    67,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,   119,    65,    66,    67,     0,
-       0,     0,     0,    68,    69,    70,    71,    72,    73,    74,
-      75,     0,    76,    77,    78,    79,    80,    81,    68,    69,
-      70,    71,    72,    73,    74,    75,     0,    76,    77,    78,
-      79,    80,    81
+      78,    79,    80,    81
 };
 
 static const yytype_int8 yycheck[] =
@@ -773,10 +773,26 @@
       58,     0,     1,     3,     3,    33,    32,     6,     7,     8,
        9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
       19,    20,    21,    22,    23,    24,    25,    33,    33,    33,
-      33,    89,     3,    33,     3,    34,    35,    36,    37,    38,
+      33,     3,    90,    33,     3,    34,    35,    36,    37,    38,
       39,    40,     3,     4,     5,     6,     3,     3,    33,    33,
-      33,    28,    33,    33,    33,   113,    30,     3,     4,     5,
-       6,     3,     3,    33,    -1,    26,    27,    28,    29,    30,
+      33,     3,    28,    33,    33,    33,    30,   115,     3,     4,
+       5,     6,     3,    33,    33,    26,    27,    28,    29,    30,
+      31,    32,    33,     3,    35,    36,    37,    38,    39,    40,
+      -1,    26,    27,    28,    29,    30,    31,    32,    33,    -1,
+      35,    36,    37,    38,    39,    40,     3,     4,     5,     6,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,     3,     4,     5,     6,    -1,    -1,    -1,    -1,    26,
+      27,    28,    29,    30,    31,    32,    33,    -1,    35,    36,
+      37,    38,    39,    40,    26,    27,    28,    29,    30,    31,
+      32,    33,    -1,    35,    36,    37,    38,    39,    40,     3,
+       4,     5,     6,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,     3,     4,     5,     6,    -1,    -1,
+      -1,    -1,    26,    27,    28,    29,    30,    31,    32,    33,
+      -1,    35,    36,    37,    38,    39,    40,    26,    27,    28,
+      29,    30,    31,    32,    33,    -1,    35,    36,    37,    38,
+      39,    40,     3,     4,     5,     6,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,     3,     4,     5,
+       6,    -1,    -1,    -1,    -1,    26,    27,    28,    29,    30,
       31,    32,    33,    -1,    35,    36,    37,    38,    39,    40,
       26,    27,    28,    29,    30,    31,    32,    33,    -1,    35,
       36,    37,    38,    39,    40,     3,     4,     5,     6,    -1,
@@ -795,23 +811,7 @@
       -1,    -1,    -1,    -1,    26,    27,    28,    29,    30,    31,
       32,    33,    -1,    35,    36,    37,    38,    39,    40,    26,
       27,    28,    29,    30,    31,    32,    33,    -1,    35,    36,
-      37,    38,    39,    40,     3,     4,     5,     6,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,     3,
-       4,     5,     6,    -1,    -1,    -1,    -1,    26,    27,    28,
-      29,    30,    31,    32,    33,    -1,    35,    36,    37,    38,
-      39,    40,    26,    27,    28,    29,    30,    31,    32,    33,
-      -1,    35,    36,    37,    38,    39,    40,     3,     4,     5,
-       6,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,     3,     4,     5,     6,    -1,    -1,    -1,    -1,
-      26,    27,    28,    29,    30,    31,    32,    33,    -1,    35,
-      36,    37,    38,    39,    40,    26,    27,    28,    29,    30,
-      31,    32,    33,    -1,    35,    36,    37,    38,    39,    40,
-       3,     4,     5,     6,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,     3,     4,     5,     6,    -1,
-      -1,    -1,    -1,    26,    27,    28,    29,    30,    31,    32,
-      33,    -1,    35,    36,    37,    38,    39,    40,    26,    27,
-      28,    29,    30,    31,    32,    33,    -1,    35,    36,    37,
-      38,    39,    40
+      37,    38,    39,    40
 };
 
 /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
@@ -826,11 +826,11 @@
       29,    26,    28,    33,    32,    33,    33,    33,    33,    53,
       53,    53,    53,    53,     3,     4,     5,     6,    26,    27,
       28,    29,    30,    31,    32,    33,    35,    36,    37,    38,
-      39,    40,    54,     3,     3,     3,     3,    33,    33,    33,
-      53,    53,    53,    53,    53,    53,     3,     3,     3,     3,
-       3,    27,    30,    28,    53,     3,     3,     3,     3,     3,
-       3,    33,    33,    33,     3,    30,     3,    53,    33,     3,
-       3
+      39,    40,    54,     3,    33,     3,     3,     3,    33,    33,
+      33,    53,    53,    53,    53,    53,    53,     3,     3,     3,
+       3,     3,     3,    27,    30,    28,    53,     3,     3,     3,
+       3,     3,     3,    33,    33,    33,     3,    30,     3,    53,
+      33,     3,     3
 };
 
 /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
@@ -838,23 +838,23 @@
 {
        0,    41,    42,    42,    43,    43,    43,    43,    43,    43,
       43,    43,    43,    43,    43,    43,    43,    43,    43,    43,
-      43,    43,    43,    43,    43,    43,    43,    44,    44,    44,
-      45,    45,    46,    46,    47,    47,    48,    48,    49,    49,
-      50,    50,    51,    51,    52,    52,    53,    53,    54,    54,
+      43,    43,    43,    43,    43,    43,    43,    43,    44,    44,
+      44,    45,    45,    46,    46,    47,    47,    48,    48,    49,
+      49,    50,    50,    51,    51,    52,    52,    53,    53,    54,
       54,    54,    54,    54,    54,    54,    54,    54,    54,    54,
-      54,    54,    54,    54,    54
+      54,    54,    54,    54,    54,    54
 };
 
 /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
 static const yytype_int8 yyr2[] =
 {
        0,     2,     0,     2,     2,     4,     3,     6,     8,     3,
-       3,     5,     7,     3,     4,     3,     4,     4,     4,     4,
-       4,     3,     3,     3,     3,     1,     2,     1,     1,     1,
+       4,     3,     5,     7,     3,     4,     3,     4,     4,     4,
+       4,     4,     3,     3,     3,     3,     1,     2,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     1,     1,     1,     1,     1,     0,     2,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     0,     2,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     1,     1,     1,     1
+       1,     1,     1,     1,     1,     1
 };
 
 
@@ -1679,27 +1679,37 @@
 #line 1680 "cmFortranParser.cxx"
     break;
 
-  case 10: /* stmt: END INTERFACE EOSTMT  */
+  case 10: /* stmt: END INTERFACE WORD EOSTMT  */
 #line 141 "cmFortranParser.y"
+                            {
+    cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
+    cmFortranParser_SetInInterface(parser, false);
+    free((yyvsp[-1].string));
+  }
+#line 1690 "cmFortranParser.cxx"
+    break;
+
+  case 11: /* stmt: END INTERFACE EOSTMT  */
+#line 146 "cmFortranParser.y"
                        {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
   }
-#line 1689 "cmFortranParser.cxx"
+#line 1699 "cmFortranParser.cxx"
     break;
 
-  case 11: /* stmt: USE DCOLON WORD other EOSTMT  */
-#line 145 "cmFortranParser.y"
+  case 12: /* stmt: USE DCOLON WORD other EOSTMT  */
+#line 150 "cmFortranParser.y"
                                {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1699 "cmFortranParser.cxx"
+#line 1709 "cmFortranParser.cxx"
     break;
 
-  case 12: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT  */
-#line 150 "cmFortranParser.y"
+  case 13: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT  */
+#line 155 "cmFortranParser.y"
                                           {
     if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) {
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
@@ -1712,42 +1722,32 @@
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1716 "cmFortranParser.cxx"
+#line 1726 "cmFortranParser.cxx"
     break;
 
-  case 13: /* stmt: INCLUDE STRING EOSTMT  */
-#line 162 "cmFortranParser.y"
+  case 14: /* stmt: INCLUDE STRING EOSTMT  */
+#line 167 "cmFortranParser.y"
                         {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-1].string));
     free((yyvsp[-1].string));
   }
-#line 1726 "cmFortranParser.cxx"
+#line 1736 "cmFortranParser.cxx"
     break;
 
-  case 14: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT  */
-#line 167 "cmFortranParser.y"
+  case 15: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT  */
+#line 172 "cmFortranParser.y"
                                          {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1736 "cmFortranParser.cxx"
-    break;
-
-  case 15: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT  */
-#line 172 "cmFortranParser.y"
-                                 {
-    cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
-    free((yyvsp[-2].string));
-  }
 #line 1746 "cmFortranParser.cxx"
     break;
 
-  case 16: /* stmt: include STRING other EOSTMT  */
+  case 16: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT  */
 #line 177 "cmFortranParser.y"
-                              {
+                                 {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
@@ -1755,96 +1755,106 @@
 #line 1756 "cmFortranParser.cxx"
     break;
 
-  case 17: /* stmt: define WORD other EOSTMT  */
+  case 17: /* stmt: include STRING other EOSTMT  */
 #line 182 "cmFortranParser.y"
-                           {
+                              {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
+    cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
 #line 1766 "cmFortranParser.cxx"
     break;
 
-  case 18: /* stmt: undef WORD other EOSTMT  */
+  case 18: /* stmt: define WORD other EOSTMT  */
 #line 187 "cmFortranParser.y"
-                          {
+                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
+    cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
 #line 1776 "cmFortranParser.cxx"
     break;
 
-  case 19: /* stmt: ifdef WORD other EOSTMT  */
+  case 19: /* stmt: undef WORD other EOSTMT  */
 #line 192 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
+    cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
 #line 1786 "cmFortranParser.cxx"
     break;
 
-  case 20: /* stmt: ifndef WORD other EOSTMT  */
+  case 20: /* stmt: ifdef WORD other EOSTMT  */
 #line 197 "cmFortranParser.y"
-                           {
+                          {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
+    cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
 #line 1796 "cmFortranParser.cxx"
     break;
 
-  case 21: /* stmt: if other EOSTMT  */
+  case 21: /* stmt: ifndef WORD other EOSTMT  */
 #line 202 "cmFortranParser.y"
+                           {
+    cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
+    cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
+    free((yyvsp[-2].string));
+  }
+#line 1806 "cmFortranParser.cxx"
+    break;
+
+  case 22: /* stmt: if other EOSTMT  */
+#line 207 "cmFortranParser.y"
                   {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIf(parser);
   }
-#line 1805 "cmFortranParser.cxx"
+#line 1815 "cmFortranParser.cxx"
     break;
 
-  case 22: /* stmt: elif other EOSTMT  */
-#line 206 "cmFortranParser.y"
+  case 23: /* stmt: elif other EOSTMT  */
+#line 211 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElif(parser);
   }
-#line 1814 "cmFortranParser.cxx"
+#line 1824 "cmFortranParser.cxx"
     break;
 
-  case 23: /* stmt: else other EOSTMT  */
-#line 210 "cmFortranParser.y"
+  case 24: /* stmt: else other EOSTMT  */
+#line 215 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElse(parser);
   }
-#line 1823 "cmFortranParser.cxx"
+#line 1833 "cmFortranParser.cxx"
     break;
 
-  case 24: /* stmt: endif other EOSTMT  */
-#line 214 "cmFortranParser.y"
+  case 25: /* stmt: endif other EOSTMT  */
+#line 219 "cmFortranParser.y"
                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleEndif(parser);
   }
-#line 1832 "cmFortranParser.cxx"
+#line 1842 "cmFortranParser.cxx"
     break;
 
-  case 48: /* misc_code: WORD  */
-#line 236 "cmFortranParser.y"
+  case 49: /* misc_code: WORD  */
+#line 241 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1838 "cmFortranParser.cxx"
-    break;
-
-  case 55: /* misc_code: STRING  */
-#line 243 "cmFortranParser.y"
-                      { free ((yyvsp[0].string)); }
-#line 1844 "cmFortranParser.cxx"
-    break;
-
-
 #line 1848 "cmFortranParser.cxx"
+    break;
+
+  case 56: /* misc_code: STRING  */
+#line 248 "cmFortranParser.y"
+                      { free ((yyvsp[0].string)); }
+#line 1854 "cmFortranParser.cxx"
+    break;
+
+
+#line 1858 "cmFortranParser.cxx"
 
       default: break;
     }
@@ -2068,6 +2078,6 @@
   return yyresult;
 }
 
-#line 255 "cmFortranParser.y"
+#line 260 "cmFortranParser.y"
 
 /* End of grammar */
diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y
index a5c8976..0b27060 100644
--- a/Source/LexerParser/cmFortranParser.y
+++ b/Source/LexerParser/cmFortranParser.y
@@ -138,6 +138,11 @@
     cmFortranParser_SetInInterface(parser, true);
     free($2);
   }
+| END INTERFACE WORD EOSTMT {
+    cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
+    cmFortranParser_SetInInterface(parser, false);
+    free($3);
+  }
 | END INTERFACE EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
diff --git a/Source/cmCallVisualStudioMacro.h b/Source/cmCallVisualStudioMacro.h
index 795b863..78f22ae 100644
--- a/Source/cmCallVisualStudioMacro.h
+++ b/Source/cmCallVisualStudioMacro.h
@@ -19,8 +19,7 @@
   //! given solution file open. Pass "ALL" for slnFile to call the
   //! macro in each Visual Studio instance.
   static int CallMacro(const std::string& slnFile, const std::string& macro,
-                       const std::string& args,
-                       const bool logErrorsAsMessages);
+                       const std::string& args, bool logErrorsAsMessages);
 
   //! Count the number of running instances of Visual Studio with the
   //! given solution file open. Pass "ALL" for slnFile to count all
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index fae6d54..c80cdb9 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -749,6 +749,12 @@
   this->VisitedConfigsForObjects.clear();
   this->LinkImplMap.clear();
   this->LinkImplUsageRequirementsOnlyMap.clear();
+  this->IncludeDirectoriesCache.clear();
+  this->CompileOptionsCache.clear();
+  this->CompileDefinitionsCache.clear();
+  this->PrecompileHeadersCache.clear();
+  this->LinkOptionsCache.clear();
+  this->LinkDirectoriesCache.clear();
 }
 
 void cmGeneratorTarget::ClearLinkInterfaceCache()
@@ -3829,6 +3835,13 @@
 std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
   const std::string& config, const std::string& lang) const
 {
+  ConfigAndLanguage cacheKey(config, lang);
+  {
+    auto it = this->IncludeDirectoriesCache.find(cacheKey);
+    if (it != this->IncludeDirectoriesCache.end()) {
+      return it->second;
+    }
+  }
   std::vector<BT<std::string>> includes;
   std::unordered_set<std::string> uniqueIncludes;
 
@@ -3903,6 +3916,7 @@
   processIncludeDirectories(this, entries, includes, uniqueIncludes,
                             debugIncludes);
 
+  this->IncludeDirectoriesCache.emplace(cacheKey, includes);
   return includes;
 }
 
@@ -4080,6 +4094,13 @@
 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
   std::string const& config, std::string const& language) const
 {
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->CompileOptionsCache.find(cacheKey);
+    if (it != this->CompileOptionsCache.end()) {
+      return it->second;
+    }
+  }
   std::vector<BT<std::string>> result;
   std::unordered_set<std::string> uniqueOptions;
 
@@ -4106,6 +4127,7 @@
   processOptions(this, entries, result, uniqueOptions, debugOptions,
                  "compile options", OptionsParse::Shell);
 
+  CompileOptionsCache.emplace(cacheKey, result);
   return result;
 }
 
@@ -4167,6 +4189,13 @@
 std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
   std::string const& config, std::string const& language) const
 {
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->CompileDefinitionsCache.find(cacheKey);
+    if (it != this->CompileDefinitionsCache.end()) {
+      return it->second;
+    }
+  }
   std::vector<BT<std::string>> list;
   std::unordered_set<std::string> uniqueOptions;
 
@@ -4220,12 +4249,20 @@
   processOptions(this, entries, list, uniqueOptions, debugDefines,
                  "compile definitions", OptionsParse::None);
 
+  this->CompileDefinitionsCache.emplace(cacheKey, list);
   return list;
 }
 
 std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
   const std::string& config, const std::string& language) const
 {
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->PrecompileHeadersCache.find(cacheKey);
+    if (it != this->PrecompileHeadersCache.end()) {
+      return it->second;
+    }
+  }
   std::unordered_set<std::string> uniqueOptions;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
@@ -4253,6 +4290,7 @@
   processOptions(this, entries, list, uniqueOptions, debugDefines,
                  "precompile headers", OptionsParse::None);
 
+  this->PrecompileHeadersCache.emplace(cacheKey, list);
   return list;
 }
 
@@ -4613,6 +4651,13 @@
 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
   std::string const& config, std::string const& language) const
 {
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->LinkOptionsCache.find(cacheKey);
+    if (it != this->LinkOptionsCache.end()) {
+      return it->second;
+    }
+  }
   std::vector<BT<std::string>> result;
   std::unordered_set<std::string> uniqueOptions;
 
@@ -4690,7 +4735,10 @@
 
   // Last step: replace "LINKER:" prefixed elements by
   // actual linker wrapper
-  return this->ResolveLinkerWrapper(result, language);
+  result = this->ResolveLinkerWrapper(result, language);
+
+  this->LinkOptionsCache.emplace(cacheKey, result);
+  return result;
 }
 
 std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
@@ -4889,6 +4937,13 @@
 std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
   std::string const& config, std::string const& language) const
 {
+  ConfigAndLanguage cacheKey(config, language);
+  {
+    auto it = this->LinkDirectoriesCache.find(cacheKey);
+    if (it != this->LinkDirectoriesCache.end()) {
+      return it->second;
+    }
+  }
   std::vector<BT<std::string>> result;
   std::unordered_set<std::string> uniqueDirectories;
 
@@ -4918,6 +4973,7 @@
   processLinkDirectories(this, entries, result, uniqueDirectories,
                          debugDirectories);
 
+  this->LinkDirectoriesCache.emplace(cacheKey, result);
   return result;
 }
 
@@ -8869,3 +8925,101 @@
     }
   }
 }
+
+bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang,
+                                             std::string const& config) const
+{
+  if (lang != "CXX"_s) {
+    return false;
+  }
+  return this->HaveCxxModuleSupport(config) == Cxx20SupportLevel::Supported &&
+    this->GetGlobalGenerator()->CheckCxxModuleSupport();
+}
+
+bool cmGeneratorTarget::NeedDyndep(std::string const& lang,
+                                   std::string const& config) const
+{
+  return lang == "Fortran"_s || this->NeedCxxModuleSupport(lang, config);
+}
+
+cmFileSet const* cmGeneratorTarget::GetFileSetForSource(
+  std::string const& config, cmSourceFile const* sf) const
+{
+  this->BuildFileSetInfoCache(config);
+
+  auto const& path = sf->GetFullPath();
+  auto const& per_config = this->Configs[config];
+
+  auto const fsit = per_config.FileSetCache.find(path);
+  if (fsit == per_config.FileSetCache.end()) {
+    return nullptr;
+  }
+  return fsit->second;
+}
+
+bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang,
+                                            std::string const& config,
+                                            cmSourceFile const* sf) const
+{
+  bool const needDyndep = this->NeedDyndep(lang, config);
+  if (!needDyndep) {
+    return false;
+  }
+  auto const* fs = this->GetFileSetForSource(config, sf);
+  if (fs &&
+      (fs->GetType() == "CXX_MODULES"_s ||
+       fs->GetType() == "CXX_MODULE_HEADER_UNITS"_s)) {
+    return true;
+  }
+  auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
+  if (sfProp.IsSet()) {
+    return sfProp.IsOn();
+  }
+  auto const tgtProp = this->GetProperty("CXX_SCAN_FOR_MODULES");
+  if (tgtProp.IsSet()) {
+    return tgtProp.IsOn();
+  }
+  return true;
+}
+
+void cmGeneratorTarget::BuildFileSetInfoCache(std::string const& config) const
+{
+  auto& per_config = this->Configs[config];
+
+  if (per_config.BuiltFileSetCache) {
+    return;
+  }
+
+  auto const* tgt = this->Target;
+
+  for (auto const& name : tgt->GetAllFileSetNames()) {
+    auto const* file_set = tgt->GetFileSet(name);
+    if (!file_set) {
+      tgt->GetMakefile()->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", name,
+                 "\", but it was not found."));
+      continue;
+    }
+
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->LocalGenerator, config, this);
+
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files, entry,
+                                  this->LocalGenerator, config, this);
+    }
+
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        per_config.FileSetCache[filename] = file_set;
+      }
+    }
+  }
+
+  per_config.BuiltFileSetCache = true;
+}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 25e6a81..3cd5e34 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -26,6 +26,7 @@
 enum class cmBuildStep;
 class cmComputeLinkInformation;
 class cmCustomCommand;
+class cmFileSet;
 class cmGlobalGenerator;
 class cmLocalGenerator;
 class cmMakefile;
@@ -489,6 +490,18 @@
   std::string GetCreateRuleVariable(std::string const& lang,
                                     std::string const& config) const;
 
+private:
+  using ConfigAndLanguage = std::pair<std::string, std::string>;
+  using ConfigAndLanguageToBTStrings =
+    std::map<ConfigAndLanguage, std::vector<BT<std::string>>>;
+  mutable ConfigAndLanguageToBTStrings IncludeDirectoriesCache;
+  mutable ConfigAndLanguageToBTStrings CompileOptionsCache;
+  mutable ConfigAndLanguageToBTStrings CompileDefinitionsCache;
+  mutable ConfigAndLanguageToBTStrings PrecompileHeadersCache;
+  mutable ConfigAndLanguageToBTStrings LinkOptionsCache;
+  mutable ConfigAndLanguageToBTStrings LinkDirectoriesCache;
+
+public:
   /** Get the include directories for this target.  */
   std::vector<BT<std::string>> GetIncludeDirectories(
     const std::string& config, const std::string& lang) const;
@@ -1229,4 +1242,21 @@
 
   // Check C++ module status for the target.
   void CheckCxxModuleStatus(std::string const& config) const;
+
+  bool NeedCxxModuleSupport(std::string const& lang,
+                            std::string const& config) const;
+  bool NeedDyndep(std::string const& lang, std::string const& config) const;
+  cmFileSet const* GetFileSetForSource(std::string const& config,
+                                       cmSourceFile const* sf) const;
+  bool NeedDyndepForSource(std::string const& lang, std::string const& config,
+                           cmSourceFile const* sf) const;
+
+private:
+  void BuildFileSetInfoCache(std::string const& config) const;
+  struct InfoByConfig
+  {
+    bool BuiltFileSetCache = false;
+    std::map<std::string, cmFileSet const*> FileSetCache;
+  };
+  mutable std::map<std::string, InfoByConfig> Configs;
 };
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index a9485b5..8ca6ee6 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1439,6 +1439,19 @@
   return false;
 }
 
+void cmGlobalGenerator::CxxModuleSupportCheck() const
+{
+  bool const diagnose = !this->DiagnosedCxxModuleSupport &&
+    !this->CMakeInstance->GetIsInTryCompile();
+  if (diagnose) {
+    this->DiagnosedCxxModuleSupport = true;
+    this->GetCMakeInstance()->IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP "
+      "is experimental.  It is meant only for compiler developers to try.");
+  }
+}
+
 void cmGlobalGenerator::ComputeBuildFileGenerators()
 {
   for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
@@ -1597,6 +1610,8 @@
   // it builds by default.
   this->InitializeProgressMarks();
 
+  this->DiagnosedCxxModuleSupport = false;
+
   this->ProcessEvaluationFiles();
 
   this->CMakeInstance->UpdateProgress("Generating", 0.1f);
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 89b2ea8..4ebf9f6 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -157,6 +157,8 @@
 
   virtual bool InspectConfigTypeVariables() { return true; }
 
+  virtual bool CheckCxxModuleSupport() { return false; }
+
   bool Compute();
   virtual void AddExtraIDETargets() {}
 
@@ -621,6 +623,8 @@
 
   virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const;
 
+  void CxxModuleSupportCheck() const;
+
   /// @brief Qt AUTOMOC/UIC/RCC target generation
   /// @return true on success
   bool QtAutoGen();
@@ -728,6 +732,8 @@
   std::map<std::string, int> LanguageToLinkerPreference;
   std::map<std::string, std::string> LanguageToOriginalSharedLibFlags;
 
+  mutable bool DiagnosedCxxModuleSupport = false;
+
   // Deferral id generation.
   size_t NextDeferId = 0;
 
diff --git a/Source/cmGlobalMSYSMakefileGenerator.h b/Source/cmGlobalMSYSMakefileGenerator.h
index a8516a4..ee9a4ee 100644
--- a/Source/cmGlobalMSYSMakefileGenerator.h
+++ b/Source/cmGlobalMSYSMakefileGenerator.h
@@ -28,7 +28,7 @@
   }
 
   //! Get the name for the generator.
-  virtual std::string GetName() const
+  std::string GetName() const override
   {
     return cmGlobalMSYSMakefileGenerator::GetActualName();
   }
@@ -41,8 +41,8 @@
    * Try to determine system information such as shared library
    * extension, pthreads, byte order etc.
    */
-  virtual void EnableLanguage(std::vector<std::string> const& languages,
-                              cmMakefile*, bool optional);
+  void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*,
+                      bool optional) override;
 
 private:
   std::string FindMinGW(std::string const& makeloc);
diff --git a/Source/cmGlobalMinGWMakefileGenerator.h b/Source/cmGlobalMinGWMakefileGenerator.h
index b39db03..7dd968b 100644
--- a/Source/cmGlobalMinGWMakefileGenerator.h
+++ b/Source/cmGlobalMinGWMakefileGenerator.h
@@ -27,7 +27,7 @@
       new cmGlobalGeneratorSimpleFactory<cmGlobalMinGWMakefileGenerator>());
   }
   //! Get the name for the generator.
-  virtual std::string GetName() const
+  std::string GetName() const override
   {
     return cmGlobalMinGWMakefileGenerator::GetActualName();
   }
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index e334666..618dfb7 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -591,7 +591,7 @@
   this->TargetAll = this->NinjaOutputPath("all");
   this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
   this->DisableCleandead = false;
-  this->DiagnosedCxxModuleSupport = false;
+  this->DiagnosedCxxModuleNinjaSupport = false;
 
   this->PolicyCMP0058 =
     this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
@@ -850,18 +850,12 @@
 
 bool cmGlobalNinjaGenerator::CheckCxxModuleSupport()
 {
-  bool const diagnose = !this->DiagnosedCxxModuleSupport &&
-    !this->CMakeInstance->GetIsInTryCompile();
-  if (diagnose) {
-    this->DiagnosedCxxModuleSupport = true;
-    this->GetCMakeInstance()->IssueMessage(
-      MessageType::AUTHOR_WARNING,
-      "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP "
-      "is experimental.  It is meant only for compiler developers to try.");
-  }
+  this->CxxModuleSupportCheck();
   if (this->NinjaSupportsDyndeps) {
     return true;
   }
+  bool const diagnose = !this->DiagnosedCxxModuleNinjaSupport &&
+    !this->CMakeInstance->GetIsInTryCompile();
   if (diagnose) {
     std::ostringstream e;
     /* clang-format off */
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 1628349..6f654f6 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -469,7 +469,7 @@
 
   bool IsSingleConfigUtility(cmGeneratorTarget const* target) const;
 
-  bool CheckCxxModuleSupport();
+  bool CheckCxxModuleSupport() override;
 
 protected:
   void Generate() override;
@@ -592,7 +592,7 @@
 
   codecvt::Encoding NinjaExpectedEncoding = codecvt::None;
 
-  bool DiagnosedCxxModuleSupport = false;
+  bool DiagnosedCxxModuleNinjaSupport = false;
 
   void InitOutputPathPrefix();
 
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 266e08d..192663d 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -302,7 +302,7 @@
   std::string const& ts, cmMakefile* mf)
 {
   std::vector<std::string> const fields = cmTokenize(ts, ",");
-  std::vector<std::string>::const_iterator fi = fields.begin();
+  auto fi = fields.begin();
   if (fi == fields.end()) {
     return true;
   }
@@ -501,7 +501,8 @@
 
 bool cmGlobalVisualStudio10Generator::InitializeTegraAndroid(cmMakefile* mf)
 {
-  std::string v = this->GetInstalledNsightTegraVersion();
+  std::string v =
+    cmGlobalVisualStudio10Generator::GetInstalledNsightTegraVersion();
   if (v.empty()) {
     mf->IssueMessage(MessageType::FATAL_ERROR,
                      "CMAKE_SYSTEM_NAME is 'Android' but "
@@ -773,9 +774,9 @@
 
   // Search in standard location.
   mskey = cmStrCat(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\",
+    R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\)",
     this->GetToolsVersion(), ";MSBuildToolsPath");
-  if (cmSystemTools::ReadRegistryValue(mskey.c_str(), msbuild,
+  if (cmSystemTools::ReadRegistryValue(mskey, msbuild,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(msbuild);
     msbuild += "/MSBuild.exe";
@@ -798,6 +799,7 @@
   }
   // Skip over the cmGlobalVisualStudio8Generator implementation because
   // we expect a real devenv and do not want to look for VCExpress.
+  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
   return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
 }
 
@@ -1108,8 +1110,9 @@
             // Decide if a restore is performed, based on a cache variable.
             if (cmValue cached =
                   this->CMakeInstance->GetState()->GetCacheEntryValue(
-                    "CMAKE_VS_NUGET_PACKAGE_RESTORE"))
+                    "CMAKE_VS_NUGET_PACKAGE_RESTORE")) {
               restorePackages = cached.IsOn();
+            }
           }
 
           if (restorePackages) {
@@ -1137,7 +1140,7 @@
       std::string extension =
         cmSystemTools::GetFilenameLastExtension(proj->GetRelativePath());
       extension = cmSystemTools::LowerCase(extension);
-      if (extension.compare(".csproj") == 0) {
+      if (extension == ".csproj") {
         // Use correct platform name
         platform =
           slnData.GetConfigurationTarget(tname, plainConfig, platform);
@@ -1502,19 +1505,23 @@
 
   if (toolset == "v142") {
     return "v142";
-  } else if (toolset == "v141") {
-    return "v141";
-  } else if (useToolset == "v140") {
-    return "v140";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (toolset == "v141") {
+    return "v141";
+  }
+  if (useToolset == "v140") {
+    return "v140";
+  }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::GetCSharpFlagTableName() const
@@ -1524,19 +1531,23 @@
 
   if (useToolset == "v142") {
     return "v142";
-  } else if (useToolset == "v141") {
-    return "v141";
-  } else if (useToolset == "v140") {
-    return "v140";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (useToolset == "v141") {
+    return "v141";
+  }
+  if (useToolset == "v140") {
+    return "v140";
+  }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::GetRcFlagTableName() const
@@ -1547,15 +1558,17 @@
   if ((useToolset == "v140") || (useToolset == "v141") ||
       (useToolset == "v142")) {
     return "v14";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::GetLibFlagTableName() const
@@ -1566,15 +1579,17 @@
   if ((useToolset == "v140") || (useToolset == "v141") ||
       (useToolset == "v142")) {
     return "v14";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::GetLinkFlagTableName() const
@@ -1584,19 +1599,23 @@
 
   if (useToolset == "v142") {
     return "v142";
-  } else if (useToolset == "v141") {
-    return "v141";
-  } else if (useToolset == "v140") {
-    return "v140";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (useToolset == "v141") {
+    return "v141";
+  }
+  if (useToolset == "v140") {
+    return "v140";
+  }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::GetMasmFlagTableName() const
@@ -1607,15 +1626,17 @@
   if ((useToolset == "v140") || (useToolset == "v141") ||
       (useToolset == "v142")) {
     return "v14";
-  } else if (useToolset == "v120") {
-    return "v12";
-  } else if (useToolset == "v110") {
-    return "v11";
-  } else if (useToolset == "v100") {
-    return "v10";
-  } else {
-    return "";
   }
+  if (useToolset == "v120") {
+    return "v12";
+  }
+  if (useToolset == "v110") {
+    return "v11";
+  }
+  if (useToolset == "v100") {
+    return "v10";
+  }
+  return "";
 }
 
 std::string cmGlobalVisualStudio10Generator::CanonicalToolsetName(
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index b3d9552..63c21c5 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -241,15 +241,10 @@
 private:
   struct LongestSourcePath
   {
-    LongestSourcePath()
-      : Length(0)
-      , Target(0)
-      , SourceFile(0)
-    {
-    }
-    size_t Length;
-    cmGeneratorTarget* Target;
-    cmSourceFile const* SourceFile;
+    LongestSourcePath() = default;
+    size_t Length = 0;
+    cmGeneratorTarget* Target = nullptr;
+    cmSourceFile const* SourceFile = nullptr;
     std::string SourceRel;
   };
   LongestSourcePath LongestSource;
diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx
index 75e3df8..3ad10eb 100644
--- a/Source/cmGlobalVisualStudio11Generator.cxx
+++ b/Source/cmGlobalVisualStudio11Generator.cxx
@@ -22,7 +22,7 @@
 {
   if (strncmp(name.c_str(), vs11generatorName,
               sizeof(vs11generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs11generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2012")) {
@@ -216,9 +216,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v110_wp80";
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
     toolset);
@@ -232,9 +231,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v110";
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio10Generator::SelectWindowsStoreToolset(
     toolset);
@@ -245,6 +243,7 @@
   // Intentionally skip up to the top-level class implementation.
   // Folders are not supported by the Express editions in VS10 and earlier,
   // but they are in VS11 Express and above.
+  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
   return cmGlobalGenerator::UseFolderProperty();
 }
 
diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx
index 7e56a78..d417f9e 100644
--- a/Source/cmGlobalVisualStudio12Generator.cxx
+++ b/Source/cmGlobalVisualStudio12Generator.cxx
@@ -21,7 +21,7 @@
 {
   if (strncmp(name.c_str(), vs12generatorName,
               sizeof(vs12generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs12generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2013")) {
@@ -194,9 +194,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v120_wp81";
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio11Generator::SelectWindowsPhoneToolset(
     toolset);
@@ -210,9 +209,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v120";
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio11Generator::SelectWindowsStoreToolset(
     toolset);
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index 0e05083..7424ca3 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -23,7 +23,7 @@
 {
   if (strncmp(name.c_str(), vs14generatorName,
               sizeof(vs14generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs14generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2015")) {
@@ -213,9 +213,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v140";
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio12Generator::SelectWindowsStoreToolset(
     toolset);
@@ -255,7 +254,7 @@
       return std::string();
     }
     // If the value is something else, trust that it is a valid SDK value.
-    else if (value) {
+    if (value) {
       return *value;
     }
     // If value is an invalid pointer, leave result unchanged.
@@ -373,6 +372,7 @@
     return sdks.at(0);
   }
 #endif
+  (void)mf;
   // Return an empty string
   return std::string();
 }
diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx
index 758ce83..06fd61c 100644
--- a/Source/cmGlobalVisualStudio71Generator.cxx
+++ b/Source/cmGlobalVisualStudio71Generator.cxx
@@ -101,14 +101,14 @@
   // check to see if this is a fortran build
   std::string ext = ".vcproj";
   const char* project =
-    "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"";
+    R"(Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = ")";
   if (this->TargetIsFortranOnly(t)) {
     ext = ".vfproj";
-    project = "Project(\"{6989167D-11E4-40FE-8C1A-2192A86A7E90}\") = \"";
+    project = R"(Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = ")";
   }
   if (t->IsCSharpOnly()) {
     ext = ".csproj";
-    project = "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"";
+    project = R"(Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = ")";
   }
   cmValue targetExt = t->GetProperty("GENERATOR_FILE_NAME_EXT");
   if (targetExt) {
@@ -125,11 +125,11 @@
 
   fout << "EndProject\n";
 
-  UtilityDependsMap::iterator ui = this->UtilityDepends.find(t);
+  auto ui = this->UtilityDepends.find(t);
   if (ui != this->UtilityDepends.end()) {
     const char* uname = ui->second.c_str();
     /* clang-format off */
-    fout << "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \""
+    fout << R"(Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = ")"
          << uname << "\", \""
          << this->ConvertToSolutionPath(dir) << (dir[0]? "\\":"")
          << uname << ".vcproj" << "\", \"{"
@@ -169,7 +169,9 @@
 {
   fout << "Project(\"{"
        << (typeGuid ? typeGuid
-                    : std::string(this->ExternalProjectType(location)))
+                    : std::string(
+                        cmGlobalVisualStudio71Generator::ExternalProjectType(
+                          location)))
        << "}\") = \"" << name << "\", \""
        << this->ConvertToSolutionPath(location) << "\", \"{"
        << this->GetGUID(name) << "}\"\n";
@@ -216,8 +218,7 @@
     }
     fout << "\t\t{" << guid << "}." << i << ".ActiveCfg = " << dstConfig << "|"
          << platformName << std::endl;
-    std::set<std::string>::const_iterator ci =
-      configsPartOfDefaultBuild.find(i);
+    auto ci = configsPartOfDefaultBuild.find(i);
     if (!(ci == configsPartOfDefaultBuild.end())) {
       fout << "\t\t{" << guid << "}." << i << ".Build.0 = " << dstConfig << "|"
            << platformName << std::endl;
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 7431176..c375d60 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -76,9 +76,7 @@
   this->ExtraFlagTable = cmVS7ExtraFlagTable;
 }
 
-cmGlobalVisualStudio7Generator::~cmGlobalVisualStudio7Generator()
-{
-}
+cmGlobalVisualStudio7Generator::~cmGlobalVisualStudio7Generator() = default;
 
 // Package GUID of Intel Visual Fortran plugin to VS IDE
 #define CM_INTEL_PLUGIN_GUID "{B68A201D-CB9B-47AF-A52F-7EEC72E217E4}"
@@ -160,7 +158,7 @@
 
   // Search in standard location.
   vskey = this->GetRegistryBase() + ";InstallDir";
-  if (cmSystemTools::ReadRegistryValue(vskey.c_str(), vscmd,
+  if (cmSystemTools::ReadRegistryValue(vskey, vscmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vscmd);
     vscmd += "/devenv.com";
@@ -171,9 +169,9 @@
 
   // Search where VS15Preview places it.
   vskey = cmStrCat(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7;",
+    R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7;)",
     this->GetIDEVersion());
-  if (cmSystemTools::ReadRegistryValue(vskey.c_str(), vscmd,
+  if (cmSystemTools::ReadRegistryValue(vskey, vscmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vscmd);
     vscmd += "/Common7/IDE/devenv.com";
@@ -192,17 +190,23 @@
   std::string extension = cmSystemTools::GetFilenameLastExtension(location);
   if (extension == ".vbproj") {
     return "F184B08F-C81C-45F6-A57F-5ABD9991F28F";
-  } else if (extension == ".csproj") {
+  }
+  if (extension == ".csproj") {
     return "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC";
-  } else if (extension == ".fsproj") {
+  }
+  if (extension == ".fsproj") {
     return "F2A71F9B-5D33-465A-A702-920D77279786";
-  } else if (extension == ".vdproj") {
+  }
+  if (extension == ".vdproj") {
     return "54435603-DBB4-11D2-8724-00A0C9A8B90C";
-  } else if (extension == ".dbproj") {
+  }
+  if (extension == ".dbproj") {
     return "C8D11400-126E-41CD-887F-60BD40844F9E";
-  } else if (extension == ".wixproj") {
+  }
+  if (extension == ".wixproj") {
     return "930C7802-8A8C-48F9-8165-68863BCCD9DD";
-  } else if (extension == ".pyproj") {
+  }
+  if (extension == ".pyproj") {
     return "888888A0-9F3D-457C-B088-3A5042F75D52";
   }
   return "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942";
@@ -334,7 +338,7 @@
   }
   this->CurrentProject = root->GetProjectName();
   std::string fname = GetSLNFile(root);
-  cmGeneratedFileStream fout(fname.c_str());
+  cmGeneratedFileStream fout(fname);
   fout.SetCopyIfDifferent(true);
   if (!fout) {
     return;
@@ -380,7 +384,8 @@
         // On VS 19 and above, always map .NET SDK projects to "Any CPU".
         if (target->IsDotNetSdkTarget() &&
             this->GetVersion() >= VSVersion::VS16 &&
-            !this->IsReservedTarget(target->GetName())) {
+            !cmGlobalVisualStudio7Generator::IsReservedTarget(
+              target->GetName())) {
           mapping = "Any CPU";
         }
         this->WriteProjectConfigurations(fout, *vcprojName, *target, configs,
@@ -421,7 +426,7 @@
     cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
       std::string project = target->GetName();
-      std::string location = *expath;
+      std::string const& location = *expath;
 
       this->WriteExternalProject(fout, project, location,
                                  target->GetProperty("VS_PROJECT_TYPE"),
@@ -452,7 +457,7 @@
         std::string cumulativePath;
 
         for (std::string const& iter : tokens) {
-          if (!iter.size()) {
+          if (iter.empty()) {
             continue;
           }
 
@@ -501,7 +506,7 @@
     std::string guidParent(this->GetGUID(key));
 
     for (std::string const& it : iter.second) {
-      std::string value(it);
+      std::string const& value(it);
       std::string guid(this->GetGUID(value));
 
       fout << "\t\t{" << guid << "} = {" << guidParent << "}\n";
@@ -517,7 +522,7 @@
   // use windows slashes.
   std::string d = path;
   std::string::size_type pos = 0;
-  while ((pos = d.find('/', pos)) != d.npos) {
+  while ((pos = d.find('/', pos)) != std::string::npos) {
     d[pos++] = '\\';
   }
   return d;
@@ -541,8 +546,9 @@
       } else if (cmHasLiteralPrefix(name, "POST_")) {
         name = name.substr(5);
         sectionType = "postSolution";
-      } else
+      } else {
         continue;
+      }
       if (!name.empty()) {
         bool addGuid = false;
         if (name == "ExtensibilityGlobals" && sectionType == "postSolution") {
@@ -580,9 +586,10 @@
          << "\t\tSolutionGuid = {" << guid << "}\n"
          << "\tEndGlobalSection\n";
   }
-  if (!extensibilityAddInsOverridden)
+  if (!extensibilityAddInsOverridden) {
     fout << "\tGlobalSection(ExtensibilityAddIns) = postSolution\n"
          << "\tEndGlobalSection\n";
+  }
 }
 
 // Standard end of dsw file
@@ -601,13 +608,13 @@
   std::string fname =
     cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
              pname, ".vcproj");
-  cmGeneratedFileStream fout(fname.c_str());
+  cmGeneratedFileStream fout(fname);
   fout.SetCopyIfDifferent(true);
-  std::string guid = this->GetGUID(pname.c_str());
+  std::string guid = this->GetGUID(pname);
 
   /* clang-format off */
   fout <<
-    "<?xml version=\"1.0\" encoding = \""
+    R"(<?xml version="1.0" encoding = ")"
     << this->Encoding() << "\"?>\n"
     "<VisualStudioProject\n"
     "\tProjectType=\"Visual C++\"\n"
@@ -730,13 +737,12 @@
 bool cmGlobalVisualStudio7Generator::IsDependedOn(
   OrderedTargetDependSet const& projectTargets, cmGeneratorTarget const* gtIn)
 {
-  for (cmTargetDepend const& l : projectTargets) {
-    TargetDependSet const& tgtdeps = this->GetTargetDirectDepends(l);
-    if (tgtdeps.count(gtIn)) {
-      return true;
-    }
-  }
-  return false;
+  return std::any_of(projectTargets.begin(), projectTargets.end(),
+                     [this, gtIn](cmTargetDepend const& l) {
+                       TargetDependSet const& tgtdeps =
+                         this->GetTargetDirectDepends(l);
+                       return tgtdeps.count(gtIn);
+                     });
 }
 
 std::string cmGlobalVisualStudio7Generator::Encoding()
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index e901ecd..1594457 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -31,7 +31,7 @@
 class cmGlobalVisualStudio7Generator : public cmGlobalVisualStudioGenerator
 {
 public:
-  ~cmGlobalVisualStudio7Generator();
+  ~cmGlobalVisualStudio7Generator() override;
 
   //! Create a local generator appropriate to this Global Generator
   std::unique_ptr<cmLocalGenerator> CreateLocalGenerator(
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 323ee67..647fc2d 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -44,7 +44,8 @@
 {
   this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms";
   this->Name = name;
-  this->ExtraFlagTable = this->GetExtraFlagTableVS8();
+  this->ExtraFlagTable =
+    cmGlobalVisualStudio8Generator::GetExtraFlagTableVS8();
 }
 
 std::string cmGlobalVisualStudio8Generator::FindDevEnvCommand()
@@ -52,9 +53,9 @@
   // First look for VCExpress.
   std::string vsxcmd;
   std::string vsxkey =
-    cmStrCat("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\",
+    cmStrCat(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VCExpress\)",
              this->GetIDEVersion(), ";InstallDir");
-  if (cmSystemTools::ReadRegistryValue(vsxkey.c_str(), vsxcmd,
+  if (cmSystemTools::ReadRegistryValue(vsxkey, vsxcmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vsxcmd);
     vsxcmd += "/VCExpress.exe";
@@ -149,6 +150,7 @@
 
 bool cmGlobalVisualStudio8Generator::UseFolderProperty() const
 {
+  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
   return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty();
 }
 
@@ -172,7 +174,7 @@
                                        std::move(cc));
 
   auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
-  auto gt = ptr.get();
+  auto* gt = ptr.get();
   lg.AddGeneratorTarget(std::move(ptr));
 
   // Organize in the "predefined targets" folder:
@@ -190,7 +192,7 @@
       cmStrCat(generators[0]->GetMakefile()->GetCurrentBinaryDirectory(), '/',
                stampList);
     std::string stampFile;
-    cmGeneratedFileStream fout(stampListFile.c_str());
+    cmGeneratedFileStream fout(stampListFile);
     for (const auto& gi : generators) {
       stampFile = cmStrCat(gi->GetMakefile()->GetCurrentBinaryDirectory(),
                            "/CMakeFiles/generate.stamp");
@@ -237,8 +239,7 @@
 
     // Sort the list of input files and remove duplicates.
     std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
-    std::vector<std::string>::iterator new_end =
-      std::unique(listFiles.begin(), listFiles.end());
+    auto new_end = std::unique(listFiles.begin(), listFiles.end());
     listFiles.erase(new_end, listFiles.end());
 
     // Create a rule to re-run CMake.
@@ -277,8 +278,8 @@
 {
   cmGlobalVisualStudio7Generator::AddExtraIDETargets();
   if (this->AddCheckTarget()) {
-    for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
-      const auto& tgts = this->LocalGenerators[i]->GetGeneratorTargets();
+    for (auto& LocalGenerator : this->LocalGenerators) {
+      const auto& tgts = LocalGenerator->GetGeneratorTargets();
       // All targets depend on the build-system check target.
       for (const auto& ti : tgts) {
         if (ti->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
@@ -324,8 +325,7 @@
          << (!platformMapping.empty() ? platformMapping
                                       : this->GetPlatformName())
          << "\n";
-    std::set<std::string>::const_iterator ci =
-      configsPartOfDefaultBuild.find(i);
+    auto ci = configsPartOfDefaultBuild.find(i);
     if (!(ci == configsPartOfDefaultBuild.end())) {
       fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
            << ".Build.0 = " << dstConfig << "|"
@@ -382,6 +382,7 @@
 {
   // Skip over the cmGlobalVisualStudioGenerator implementation!
   // We do not need the support that VS <= 7.1 needs.
+  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
   return this->cmGlobalGenerator::ComputeTargetDepends();
 }
 
@@ -404,20 +405,23 @@
   cmGeneratorTarget* target)
 {
   // Look for utility dependencies that magically link.
-  for (BT<std::pair<std::string, bool>> const& ui : target->GetUtilities()) {
-    if (cmGeneratorTarget* depTarget =
-          target->GetLocalGenerator()->FindGeneratorTargetToUse(
-            ui.Value.first)) {
-      if (depTarget->IsInBuildSystem() &&
-          depTarget->GetProperty("EXTERNAL_MSPROJECT")) {
-        // This utility dependency names an external .vcproj target.
-        // We use LinkLibraryDependencies="true" to link to it without
-        // predicting the .lib file location or name.
-        return true;
+  auto const& utilities = target->GetUtilities();
+  return std::any_of(
+    utilities.begin(), utilities.end(),
+    [target](BT<std::pair<std::string, bool>> const& ui) {
+      if (cmGeneratorTarget* depTarget =
+            target->GetLocalGenerator()->FindGeneratorTargetToUse(
+              ui.Value.first)) {
+        if (depTarget->IsInBuildSystem() &&
+            depTarget->GetProperty("EXTERNAL_MSPROJECT")) {
+          // This utility dependency names an external .vcproj target.
+          // We use LinkLibraryDependencies="true" to link to it without
+          // predicting the .lib file location or name.
+          return true;
+        }
       }
-    }
-  }
-  return false;
+      return false;
+    });
 }
 
 static cmVS7FlagTable cmVS8ExtraFlagTable[] = {
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
index cd0b114..9f6550b 100644
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ b/Source/cmGlobalVisualStudio9Generator.cxx
@@ -158,5 +158,5 @@
 
 std::string cmGlobalVisualStudio9Generator::GetUserMacrosRegKeyBase()
 {
-  return "Software\\Microsoft\\VisualStudio\\9.0\\vsmacros";
+  return R"(Software\Microsoft\VisualStudio\9.0\vsmacros)";
 }
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 9d168d0..31f6f77 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -51,9 +51,7 @@
   }
 }
 
-cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
-{
-}
+cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator() = default;
 
 cmGlobalVisualStudioGenerator::VSVersion
 cmGlobalVisualStudioGenerator::GetVersion() const
@@ -188,7 +186,7 @@
 
 std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
 {
-  std::string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\";
+  std::string key = R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\)";
   return key + version;
 }
 
@@ -520,13 +518,12 @@
     std::string startup = *n;
     if (this->FindTarget(startup)) {
       return startup;
-    } else {
-      root->GetMakefile()->IssueMessage(
-        MessageType::AUTHOR_WARNING,
-        "Directory property VS_STARTUP_PROJECT specifies target "
-        "'" +
-          startup + "' that does not exist.  Ignoring.");
     }
+    root->GetMakefile()->IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      "Directory property VS_STARTUP_PROJECT specifies target "
+      "'" +
+        startup + "' that does not exist.  Ignoring.");
   }
 
   // default, if not specified
@@ -961,7 +958,7 @@
   commands.push_back(std::move(command));
 }
 
-static bool OpenSolution(std::string sln)
+static bool OpenSolution(std::string const& sln)
 {
   HRESULT comInitialized =
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 576e4f2..3e20ada 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -44,7 +44,7 @@
     VS17 = 170
   };
 
-  virtual ~cmGlobalVisualStudioGenerator();
+  ~cmGlobalVisualStudioGenerator() override;
 
   VSVersion GetVersion() const;
   void SetVersion(VSVersion v);
@@ -133,8 +133,8 @@
     std::string First;
 
   public:
-    TargetCompare(std::string const& first)
-      : First(first)
+    TargetCompare(std::string first)
+      : First(std::move(first))
     {
     }
     bool operator()(cmGeneratorTarget const* l,
@@ -193,7 +193,6 @@
   using UtilityDependsMap = std::map<cmGeneratorTarget const*, std::string>;
   UtilityDependsMap UtilityDepends;
 
-protected:
   VSVersion Version;
   bool ExpressEdition;
 
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index ee24f05..59f1dc2 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -53,7 +53,8 @@
 #  undef CM_VS_GCC_DIAGNOSTIC_PUSHED
 #endif
 
-  USHORT processMachine, nativeMachine;
+  USHORT processMachine;
+  USHORT nativeMachine;
 
   return s_IsWow64Process2Impl != nullptr &&
     s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine,
@@ -65,7 +66,7 @@
 {
   std::string dotNetArm64;
   return cmSystemTools::ReadRegistryValue(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64",
+    R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework;InstallRootArm64)",
     dotNetArm64, cmSystemTools::KeyWOW64_64);
 }
 
@@ -85,19 +86,19 @@
 {
   if (VSIsArm64Host()) {
     return "ARM64";
-  } else if (VSIsWow64()) {
-    return "x64";
-  } else {
-#if defined(_M_ARM)
-    return "ARM";
-#elif defined(_M_IA64)
-    return "Itanium";
-#elif defined(_WIN64)
-    return "x64";
-#else
-    return "Win32";
-#endif
   }
+  if (VSIsWow64()) {
+    return "x64";
+  }
+#if defined(_M_ARM)
+  return "ARM";
+#elif defined(_M_IA64)
+  return "Itanium";
+#elif defined(_WIN64)
+  return "x64";
+#else
+  return "Win32";
+#endif
 }
 
 static std::string VSHostArchitecture(
@@ -105,19 +106,19 @@
 {
   if (VSIsArm64Host()) {
     return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : "";
-  } else if (VSIsWow64()) {
-    return "x64";
-  } else {
-#if defined(_M_ARM)
-    return "";
-#elif defined(_M_IA64)
-    return "";
-#elif defined(_WIN64)
-    return "x64";
-#else
-    return "x86";
-#endif
   }
+  if (VSIsWow64()) {
+    return "x64";
+  }
+#if defined(_M_ARM)
+  return "";
+#elif defined(_M_IA64)
+  return "";
+#elif defined(_WIN64)
+  return "x64";
+#else
+  return "x86";
+#endif
 }
 
 static unsigned int VSVersionToMajor(
@@ -211,7 +212,7 @@
 {
   if (strncmp(name.c_str(), vs15generatorName,
               sizeof(vs15generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2017")) {
@@ -306,7 +307,7 @@
 {
   if (strncmp(name.c_str(), vs16generatorName,
               sizeof(vs16generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2019")) {
@@ -320,7 +321,7 @@
 {
   if (strncmp(name.c_str(), vs17generatorName,
               sizeof(vs17generatorName) - 6) != 0) {
-    return 0;
+    return nullptr;
   }
   const char* p = name.c_str() + sizeof(vs17generatorName) - 6;
   if (cmHasLiteralPrefix(p, " 2022")) {
@@ -530,7 +531,7 @@
   if (!this->GeneratorInstanceVersion.empty()) {
     std::string const majorStr = VSVersionToMajorString(this->Version);
     cmsys::RegularExpression versionRegex(
-      cmStrCat("^", majorStr, "\\.[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 */
@@ -606,7 +607,7 @@
   this->GeneratorInstanceVersion.clear();
 
   std::vector<std::string> const fields = cmTokenize(is, ",");
-  std::vector<std::string>::const_iterator fi = fields.begin();
+  auto fi = fields.begin();
   if (fi == fields.end()) {
     return true;
   }
@@ -900,9 +901,8 @@
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = VSVersionToToolset(this->Version);
       return true;
-    } else {
-      return false;
     }
+    return false;
   }
   return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
     toolset);
diff --git a/Source/cmIDEOptions.cxx b/Source/cmIDEOptions.cxx
index c09aa46..9468d4a 100644
--- a/Source/cmIDEOptions.cxx
+++ b/Source/cmIDEOptions.cxx
@@ -20,15 +20,13 @@
   this->AllowDefine = true;
   this->DoingInclude = false;
   this->AllowSlash = false;
-  this->DoingFollowing = 0;
-  for (int i = 0; i < FlagTableCount; ++i) {
-    this->FlagTable[i] = 0;
+  this->DoingFollowing = nullptr;
+  for (auto& flag : this->FlagTable) {
+    flag = nullptr;
   }
 }
 
-cmIDEOptions::~cmIDEOptions()
-{
-}
+cmIDEOptions::~cmIDEOptions() = default;
 
 void cmIDEOptions::HandleFlag(std::string const& flag)
 {
@@ -49,7 +47,7 @@
   // If the last option expected a following value, this is it.
   if (this->DoingFollowing) {
     this->FlagMapUpdate(this->DoingFollowing, flag);
-    this->DoingFollowing = 0;
+    this->DoingFollowing = nullptr;
     return;
   }
 
@@ -248,8 +246,7 @@
 const char* cmIDEOptions::GetFlag(std::string const& flag) const
 {
   // This method works only for single-valued flags!
-  std::map<std::string, FlagValue>::const_iterator i =
-    this->FlagMap.find(flag);
+  auto i = this->FlagMap.find(flag);
   if (i != this->FlagMap.cend() && i->second.size() == 1) {
     return i->second[0].c_str();
   }
diff --git a/Source/cmLocalVisualStudio10Generator.cxx b/Source/cmLocalVisualStudio10Generator.cxx
index 4c0d2eea..8fe6677 100644
--- a/Source/cmLocalVisualStudio10Generator.cxx
+++ b/Source/cmLocalVisualStudio10Generator.cxx
@@ -18,8 +18,8 @@
 class cmVS10XMLParser : public cmXMLParser
 {
 public:
-  virtual void EndElement(const std::string& /* name */) {}
-  virtual void CharacterDataHandler(const char* data, int length)
+  void EndElement(const std::string& /* name */) override {}
+  void CharacterDataHandler(const char* data, int length) override
   {
     if (this->DoGUID) {
       if (data[0] == '{') {
@@ -31,7 +31,7 @@
       this->DoGUID = false;
     }
   }
-  virtual void StartElement(const std::string& name, const char**)
+  void StartElement(const std::string& name, const char**) override
   {
     // once the GUID is found do nothing
     if (!this->GUID.empty()) {
@@ -41,7 +41,7 @@
       this->DoGUID = true;
     }
   }
-  int InitializeParser()
+  int InitializeParser() override
   {
     this->DoGUID = false;
     int ret = cmXMLParser::InitializeParser();
@@ -63,9 +63,7 @@
 {
 }
 
-cmLocalVisualStudio10Generator::~cmLocalVisualStudio10Generator()
-{
-}
+cmLocalVisualStudio10Generator::~cmLocalVisualStudio10Generator() = default;
 
 void cmLocalVisualStudio10Generator::GenerateTarget(cmGeneratorTarget* target)
 {
diff --git a/Source/cmLocalVisualStudio10Generator.h b/Source/cmLocalVisualStudio10Generator.h
index 7bfe3b7..fe44bb5 100644
--- a/Source/cmLocalVisualStudio10Generator.h
+++ b/Source/cmLocalVisualStudio10Generator.h
@@ -24,7 +24,7 @@
   //! Set cache only and recurse to false by default.
   cmLocalVisualStudio10Generator(cmGlobalGenerator* gg, cmMakefile* mf);
 
-  virtual ~cmLocalVisualStudio10Generator();
+  ~cmLocalVisualStudio10Generator() override;
 
   void ReadAndStoreExternalGUID(const std::string& name,
                                 const char* path) override;
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 538c036..ded1647 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -57,7 +57,7 @@
   using ItemVector = cmComputeLinkInformation::ItemVector;
   void OutputLibraries(std::ostream& fout, ItemVector const& libs);
   void OutputObjects(std::ostream& fout, cmGeneratorTarget* t,
-                     std::string const& config, const char* isep = 0);
+                     std::string const& config, const char* isep = nullptr);
 
 private:
   cmLocalVisualStudio7Generator* LocalGenerator;
@@ -108,8 +108,8 @@
     }
 
     auto& gtVisited = this->GetSourcesVisited(gt);
-    auto& deps = this->GlobalGenerator->GetTargetDirectDepends(gt);
-    for (auto& d : deps) {
+    auto const& deps = this->GlobalGenerator->GetTargetDirectDepends(gt);
+    for (auto const& d : deps) {
       // Take the union of visited source files of custom commands
       auto depVisited = this->GetSourcesVisited(d);
       gtVisited.insert(depVisited.begin(), depVisited.end());
@@ -127,7 +127,7 @@
   // commands for targets in which no sources are built.  Add dummy
   // rules to force these targets to build.
   const auto& tgts = this->GetGeneratorTargets();
-  for (auto& l : tgts) {
+  for (auto const& l : tgts) {
     if (l->GetType() == cmStateEnums::GLOBAL_TARGET) {
       cmCustomCommandLines force_commands =
         cmMakeSingleCommandLine({ "cd", "." });
@@ -179,8 +179,7 @@
 
   // Sort the list of input files and remove duplicates.
   std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
-  std::vector<std::string>::iterator new_end =
-    std::unique(listFiles.begin(), listFiles.end());
+  auto new_end = std::unique(listFiles.begin(), listFiles.end());
   listFiles.erase(new_end, listFiles.end());
 
   for (const std::string& lf : listFiles) {
@@ -217,7 +216,7 @@
   // Generate the project file and replace it atomically with
   // copy-if-different.  We use a separate timestamp so that the IDE
   // does not reload project files unnecessarily.
-  cmGeneratedFileStream fout(fname.c_str());
+  cmGeneratedFileStream fout(fname);
   fout.SetCopyIfDifferent(true);
   this->WriteVCProjFile(fout, lname, target);
   if (fout.Close()) {
@@ -253,8 +252,7 @@
 
   // Sort the list of input files and remove duplicates.
   std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
-  std::vector<std::string>::iterator new_end =
-    std::unique(listFiles.begin(), listFiles.end());
+  auto new_end = std::unique(listFiles.begin(), listFiles.end());
   listFiles.erase(new_end, listFiles.end());
 
   std::string argS = cmStrCat("-S", this->GetSourceDirectory());
@@ -280,10 +278,9 @@
     // the generator validated all project-named sources.
     file->ResolveFullPath();
     return file;
-  } else {
-    cmSystemTools::Error("Error adding rule for " + makefileIn);
-    return nullptr;
   }
+  cmSystemTools::Error("Error adding rule for " + makefileIn);
+  return nullptr;
 }
 
 void cmLocalVisualStudio7Generator::WriteConfigurations(
@@ -559,12 +556,11 @@
 class cmLocalVisualStudio7Generator::EventWriter
 {
 public:
-  EventWriter(cmLocalVisualStudio7Generator* lg, const std::string& config,
+  EventWriter(cmLocalVisualStudio7Generator* lg, std::string config,
               std::ostream& os)
     : LG(lg)
-    , Config(config)
+    , Config(std::move(config))
     , Stream(os)
-    , First(true)
   {
   }
   void Start(const char* tool)
@@ -610,7 +606,7 @@
   cmLocalVisualStudio7Generator* LG;
   std::string Config;
   std::ostream& Stream;
-  bool First;
+  bool First = true;
 };
 
 void cmLocalVisualStudio7Generator::WriteConfiguration(
@@ -634,7 +630,7 @@
   // 1 == executable
   // 10 == utility
   const char* configType = "10";
-  const char* projectType = 0;
+  const char* projectType = nullptr;
   bool targetBuilds = true;
 
   switch (target->GetType()) {
@@ -843,7 +839,7 @@
   }
   fout << "/>\n"; // end of <Tool Name=VCCLCompilerTool
   if (gg->IsMarmasmEnabled() && !this->FortranProject) {
-    Options marmasmOptions(this, Options::MarmasmCompiler, 0, 0);
+    Options marmasmOptions(this, Options::MarmasmCompiler, nullptr, nullptr);
     /* clang-format off */
     fout <<
       "\t\t\t<Tool\n"
@@ -861,7 +857,7 @@
     /* clang-format on */
   }
   if (gg->IsMasmEnabled() && !this->FortranProject) {
-    Options masmOptions(this, Options::MasmCompiler, 0, 0);
+    Options masmOptions(this, Options::MasmCompiler, nullptr, nullptr);
     /* clang-format off */
     fout <<
       "\t\t\t<Tool\n"
@@ -957,7 +953,7 @@
 }
 
 std::string cmLocalVisualStudio7Generator::GetBuildTypeLinkerFlags(
-  std::string rootLinkerFlags, const std::string& configName)
+  std::string const& rootLinkerFlags, const std::string& configName)
 {
   std::string configTypeUpper = cmSystemTools::UpperCase(configName);
   std::string extraLinkOptionsBuildTypeDef =
@@ -1439,8 +1435,7 @@
   fout << "\t<Files>\n";
 
   // Loop through every source group.
-  for (unsigned int i = 0; i < sourceGroups.size(); ++i) {
-    cmSourceGroup sg = sourceGroups[i];
+  for (auto const& sg : sourceGroups) {
     this->WriteGroup(&sg, target, fout, libName, configs, sources);
   }
 
@@ -1634,10 +1629,9 @@
 
   // Compute the maximum length configuration name.
   std::string config_max;
-  for (std::vector<std::string>::iterator i = configs.begin();
-       i != configs.end(); ++i) {
-    if (i->size() > config_max.size()) {
-      config_max = *i;
+  for (auto& config : configs) {
+    if (config.size() > config_max.size()) {
+      config_max = config;
     }
   }
 
@@ -1663,9 +1657,8 @@
   // Write the children to temporary output.
   bool hasChildrenWithSources = false;
   std::ostringstream tmpOut;
-  for (unsigned int i = 0; i < children.size(); ++i) {
-    if (this->WriteGroup(&children[i], target, tmpOut, libName, configs,
-                         sources)) {
+  for (const auto& child : children) {
+    if (this->WriteGroup(&child, target, tmpOut, libName, configs, sources)) {
       hasChildrenWithSources = true;
     }
   }
@@ -1691,8 +1684,7 @@
         target->GetType() == cmStateEnums::GLOBAL_TARGET ||
         target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       // Look up the source kind and configs.
-      std::map<cmSourceFile const*, size_t>::const_iterator map_it =
-        sources.Index.find(sf);
+      auto map_it = sources.Index.find(sf);
       // The map entry must exist because we populated it earlier.
       assert(map_it != sources.Index.end());
       cmGeneratorTarget::AllConfigSource const& acs =
@@ -1943,7 +1935,7 @@
   }
   std::unique_ptr<cmCustomCommand> pcc(
     this->MaybeCreateImplibDir(target, configName, this->FortranProject));
-  if (pcc.get()) {
+  if (pcc) {
     event.Write(*pcc);
   }
   event.Finish();
@@ -1986,7 +1978,7 @@
   cmGlobalVisualStudio7Generator* gg =
     static_cast<cmGlobalVisualStudio7Generator*>(this->GlobalGenerator);
   /* clang-format off */
-  fout << "<?xml version=\"1.0\" encoding = \""
+  fout << R"(<?xml version="1.0" encoding = ")"
        << gg->Encoding() << "\"?>\n"
        << "<VisualStudioProject\n"
        << "\tProjectCreator=\"Intel Fortran\"\n"
@@ -1994,7 +1986,7 @@
   /* clang-format on */
   cmValue p = target->GetProperty("VS_KEYWORD");
   const char* keyword = p ? p->c_str() : "Console Application";
-  const char* projectType = 0;
+  const char* projectType = nullptr;
   switch (target->GetType()) {
     case cmStateEnums::OBJECT_LIBRARY:
     case cmStateEnums::STATIC_LIBRARY:
@@ -2014,7 +2006,7 @@
       if (!keyword) {
         keyword = "Console Application";
       }
-      projectType = 0;
+      projectType = nullptr;
       break;
     case cmStateEnums::UTILITY:
     case cmStateEnums::GLOBAL_TARGET:
@@ -2048,7 +2040,7 @@
     static_cast<cmGlobalVisualStudio7Generator*>(this->GlobalGenerator);
 
   /* clang-format off */
-  fout << "<?xml version=\"1.0\" encoding = \""
+  fout << R"(<?xml version="1.0" encoding = ")"
        << gg->Encoding() << "\"?>\n"
        << "<VisualStudioProject\n"
        << "\tProjectType=\"Visual C++\"\n";
@@ -2166,8 +2158,8 @@
 class cmVS7XMLParser : public cmXMLParser
 {
 public:
-  virtual void EndElement(const std::string& /* name */) {}
-  virtual void StartElement(const std::string& name, const char** atts)
+  void EndElement(const std::string& /* name */) override {}
+  void StartElement(const std::string& name, const char** atts) override
   {
     // once the GUID is found do nothing
     if (!this->GUID.empty()) {
@@ -2192,7 +2184,7 @@
       }
     }
   }
-  int InitializeParser()
+  int InitializeParser() override
   {
     int ret = cmXMLParser::InitializeParser();
     if (ret == 0) {
@@ -2235,7 +2227,7 @@
     volRoot[0] = dir[0];
     char fsName[16];
     if (GetVolumeInformationA(volRoot, 0, 0, 0, 0, 0, fsName, 16) &&
-        strstr(fsName, "FAT") != 0) {
+        strstr(fsName, "FAT") != nullptr) {
       return true;
     }
   }
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index d95ebc2..ce1156f 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/Source/cmLocalVisualStudio7Generator.h
@@ -48,7 +48,7 @@
   //! Set cache only and recurse to false by default.
   cmLocalVisualStudio7Generator(cmGlobalGenerator* gg, cmMakefile* mf);
 
-  virtual ~cmLocalVisualStudio7Generator();
+  ~cmLocalVisualStudio7Generator() override;
 
   cmLocalVisualStudio7Generator(const cmLocalVisualStudio7Generator&) = delete;
   const cmLocalVisualStudio7Generator& operator=(
@@ -97,7 +97,7 @@
 private:
   using Options = cmVS7GeneratorOptions;
   using FCInfo = cmLocalVisualStudio7GeneratorFCInfo;
-  std::string GetBuildTypeLinkerFlags(std::string rootLinkerFlags,
+  std::string GetBuildTypeLinkerFlags(std::string const& rootLinkerFlags,
                                       const std::string& configName);
   void FixGlobalTargets();
   void WriteVCProjHeader(std::ostream& fout, const std::string& libName,
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index 2703c7b..58d46f1 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -4,6 +4,8 @@
 
 #include <utility>
 
+#include <cm/memory>
+
 #include "windows.h"
 
 #include "cmCustomCommand.h"
@@ -24,9 +26,7 @@
 {
 }
 
-cmLocalVisualStudioGenerator::~cmLocalVisualStudioGenerator()
-{
-}
+cmLocalVisualStudioGenerator::~cmLocalVisualStudioGenerator() = default;
 
 cmGlobalVisualStudioGenerator::VSVersion
 cmLocalVisualStudioGenerator::GetVersion() const
@@ -107,7 +107,7 @@
   // Add a pre-build event to create the directory.
   cmCustomCommandLines commands = cmMakeSingleCommandLine(
     { cmSystemTools::GetCMakeCommand(), "-E", "make_directory", impDir });
-  pcc.reset(new cmCustomCommand());
+  pcc = cm::make_unique<cmCustomCommand>();
   pcc->SetCommandLines(commands);
   pcc->SetStdPipesUTF8(true);
   pcc->SetEscapeOldStyle(false);
diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h
index cf4f4d9..8fed1bd 100644
--- a/Source/cmLocalVisualStudioGenerator.h
+++ b/Source/cmLocalVisualStudioGenerator.h
@@ -29,7 +29,7 @@
 {
 public:
   cmLocalVisualStudioGenerator(cmGlobalGenerator* gg, cmMakefile* mf);
-  virtual ~cmLocalVisualStudioGenerator();
+  ~cmLocalVisualStudioGenerator() override;
 
   std::string ConstructScript(cmCustomCommandGenerator const& ccg,
                               const std::string& newline = "\n");
@@ -47,7 +47,7 @@
 
   void ComputeObjectFilenames(
     std::map<cmSourceFile const*, std::string>& mapping,
-    cmGeneratorTarget const* = 0) override;
+    cmGeneratorTarget const* = nullptr) override;
 
 protected:
   virtual const char* ReportErrorLabel() const;
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index d39e155..095bf40 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -157,107 +157,6 @@
     '_', config);
 }
 
-bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
-  std::string const& lang, std::string const& config) const
-{
-  if (lang != "CXX"_s) {
-    return false;
-  }
-  return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) ==
-    cmGeneratorTarget::Cxx20SupportLevel::Supported &&
-    this->GetGlobalGenerator()->CheckCxxModuleSupport();
-}
-
-bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
-                                        std::string const& config) const
-{
-  return lang == "Fortran" || this->NeedCxxModuleSupport(lang, config);
-}
-
-void cmNinjaTargetGenerator::BuildFileSetInfoCache(std::string const& config)
-{
-  auto& per_config = this->Configs[config];
-
-  if (per_config.BuiltFileSetCache) {
-    return;
-  }
-
-  auto const* tgt = this->GeneratorTarget->Target;
-
-  for (auto const& name : tgt->GetAllFileSetNames()) {
-    auto const* file_set = tgt->GetFileSet(name);
-    if (!file_set) {
-      this->GetMakefile()->IssueMessage(
-        MessageType::INTERNAL_ERROR,
-        cmStrCat("Target \"", tgt->GetName(),
-                 "\" is tracked to have file set \"", name,
-                 "\", but it was not found."));
-      continue;
-    }
-
-    auto fileEntries = file_set->CompileFileEntries();
-    auto directoryEntries = file_set->CompileDirectoryEntries();
-    auto directories = file_set->EvaluateDirectoryEntries(
-      directoryEntries, this->LocalGenerator, config, this->GeneratorTarget);
-
-    std::map<std::string, std::vector<std::string>> files;
-    for (auto const& entry : fileEntries) {
-      file_set->EvaluateFileEntry(directories, files, entry,
-                                  this->LocalGenerator, config,
-                                  this->GeneratorTarget);
-    }
-
-    for (auto const& it : files) {
-      for (auto const& filename : it.second) {
-        per_config.FileSetCache[filename] = file_set;
-      }
-    }
-  }
-
-  per_config.BuiltFileSetCache = true;
-}
-
-cmFileSet const* cmNinjaTargetGenerator::GetFileSetForSource(
-  std::string const& config, cmSourceFile const* sf)
-{
-  this->BuildFileSetInfoCache(config);
-
-  auto const& path = sf->GetFullPath();
-  auto const& per_config = this->Configs[config];
-
-  auto const fsit = per_config.FileSetCache.find(path);
-  if (fsit == per_config.FileSetCache.end()) {
-    return nullptr;
-  }
-  return fsit->second;
-}
-
-bool cmNinjaTargetGenerator::NeedDyndepForSource(std::string const& lang,
-                                                 std::string const& config,
-                                                 cmSourceFile const* sf)
-{
-  bool const needDyndep = this->NeedDyndep(lang, config);
-  if (!needDyndep) {
-    return false;
-  }
-  auto const* fs = this->GetFileSetForSource(config, sf);
-  if (fs &&
-      (fs->GetType() == "CXX_MODULES"_s ||
-       fs->GetType() == "CXX_MODULE_HEADER_UNITS"_s)) {
-    return true;
-  }
-  auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
-  if (sfProp.IsSet()) {
-    return sfProp.IsOn();
-  }
-  auto const tgtProp =
-    this->GeneratorTarget->GetProperty("CXX_SCAN_FOR_MODULES");
-  if (tgtProp.IsSet()) {
-    return tgtProp.IsOn();
-  }
-  return true;
-}
-
 std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
   const std::string& config)
 {
@@ -341,7 +240,7 @@
       flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
   }
 
-  auto const* fs = this->GetFileSetForSource(config, source);
+  auto const* fs = this->GeneratorTarget->GetFileSetForSource(config, source);
   if (fs &&
       (fs->GetType() == "CXX_MODULES"_s ||
        fs->GetType() == "CXX_MODULE_HEADER_UNITS"_s)) {
@@ -699,7 +598,7 @@
                                               const std::string& config)
 {
   // For some cases we scan to dynamically discover dependencies.
-  bool const needDyndep = this->NeedDyndep(lang, config);
+  bool const needDyndep = this->GetGeneratorTarget()->NeedDyndep(lang, config);
 
   if (needDyndep) {
     this->WriteCompileRule(lang, config, WithScanning::Yes);
@@ -1387,7 +1286,8 @@
     !(language == "RC" || (language == "CUDA" && !flag));
   int const commandLineLengthLimit =
     ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0;
-  bool const needDyndep = this->NeedDyndepForSource(language, config, source);
+  bool const needDyndep =
+    this->GeneratorTarget->NeedDyndepForSource(language, config, source);
 
   cmNinjaBuild objBuild(this->LanguageCompilerRule(
     language, config, needDyndep ? WithScanning::Yes : WithScanning::No));
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index d09f04b..b5abb36 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -19,7 +19,6 @@
 #include "cmOSXBundleGenerator.h"
 
 class cmCustomCommand;
-class cmFileSet;
 class cmGeneratedFileStream;
 class cmGeneratorTarget;
 class cmLocalNinjaGenerator;
@@ -64,10 +63,6 @@
 
   cmMakefile* GetMakefile() const { return this->Makefile; }
 
-  void BuildFileSetInfoCache(std::string const& config);
-  cmFileSet const* GetFileSetForSource(std::string const& config,
-                                       cmSourceFile const* sf);
-
   enum class WithScanning
   {
     No,
@@ -82,13 +77,8 @@
                                const std::string& config) const;
   std::string LanguageDyndepRule(std::string const& lang,
                                  const std::string& config) const;
-  bool NeedDyndep(std::string const& lang, std::string const& config) const;
-  bool NeedDyndepForSource(std::string const& lang, std::string const& config,
-                           cmSourceFile const* sf);
   bool NeedExplicitPreprocessing(std::string const& lang) const;
   bool CompileWithDefines(std::string const& lang) const;
-  bool NeedCxxModuleSupport(std::string const& lang,
-                            std::string const& config) const;
 
   std::string OrderDependsTargetForTarget(const std::string& config);
 
@@ -238,8 +228,6 @@
     std::vector<cmCustomCommand const*> CustomCommands;
     cmNinjaDeps ExtraFiles;
     std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator;
-    bool BuiltFileSetCache = false;
-    std::map<std::string, cmFileSet const*> FileSetCache;
   };
 
   std::map<std::string, ByConfig> Configs;
diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx
index f73c854..e559cfa 100644
--- a/Source/cmStringAlgorithms.cxx
+++ b/Source/cmStringAlgorithms.cxx
@@ -203,15 +203,23 @@
   MakeDigits(this->View_, this->Digits_, "%g", val);
 }
 
-std::string cmCatViews(std::initializer_list<cm::string_view> views)
+std::string cmCatViews(cm::optional<std::string>&& first,
+                       std::initializer_list<cm::string_view> views)
 {
-  std::size_t total_size = 0;
+  std::size_t totalSize = 0;
   for (cm::string_view const& view : views) {
-    total_size += view.size();
+    totalSize += view.size();
   }
 
-  std::string result(total_size, '\0');
-  std::string::iterator sit = result.begin();
+  std::string result;
+  std::string::size_type initialLen = 0;
+  if (first) {
+    totalSize += first->length();
+    initialLen = first->length();
+    result = std::move(*first);
+  }
+  result.resize(totalSize);
+  std::string::iterator sit = result.begin() + initialLen;
   for (cm::string_view const& view : views) {
     sit = std::copy_n(view.data(), view.size(), sit);
   }
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 83938bc..bff2eda 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -12,6 +12,7 @@
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cm/string_view>
 
 #include "cmRange.h"
@@ -146,7 +147,8 @@
 }
 
 /** Concatenate string pieces into a single string.  */
-std::string cmCatViews(std::initializer_list<cm::string_view> views);
+std::string cmCatViews(cm::optional<std::string>&& first,
+                       std::initializer_list<cm::string_view> views);
 
 /** Utility class for cmStrCat.  */
 class cmAlphaNum
@@ -189,13 +191,38 @@
   char Digits_[32];
 };
 
-/** Concatenate string pieces and numbers into a single string.  */
-template <typename... AV>
-inline std::string cmStrCat(cmAlphaNum const& a, cmAlphaNum const& b,
-                            AV const&... args)
+template <typename A, typename B, typename... AV>
+class cmStrCatHelper
 {
-  return cmCatViews(
-    { a.View(), b.View(), static_cast<cmAlphaNum const&>(args).View()... });
+public:
+  static std::string Compute(cmAlphaNum const& a, cmAlphaNum const& b,
+                             AV const&... args)
+  {
+    return cmCatViews(
+      cm::nullopt,
+      { a.View(), b.View(), static_cast<cmAlphaNum const&>(args).View()... });
+  }
+};
+
+template <typename B, typename... AV>
+class cmStrCatHelper<std::string, B, AV...>
+{
+public:
+  static std::string Compute(std::string&& a, cmAlphaNum const& b,
+                             AV const&... args)
+  {
+    return cmCatViews(
+      std::move(a),
+      { b.View(), static_cast<cmAlphaNum const&>(args).View()... });
+  }
+};
+
+/** Concatenate string pieces and numbers into a single string.  */
+template <typename A, typename B, typename... AV>
+inline std::string cmStrCat(A&& a, B&& b, AV&&... args)
+{
+  return cmStrCatHelper<A, B, AV...>::Compute(
+    std::forward<A>(a), std::forward<B>(b), std::forward<AV>(args)...);
 }
 
 /** Joins wrapped elements of a range with separator into a single string.  */
@@ -207,7 +234,9 @@
     return std::string();
   }
   return cmCatViews(
-    { prefix, cmJoin(rng, cmCatViews({ suffix, sep, prefix })), suffix });
+    cm::nullopt,
+    { prefix, cmJoin(rng, cmCatViews(cm::nullopt, { suffix, sep, prefix })),
+      suffix });
 }
 
 /** Joins wrapped elements of a range with separator into a single string.  */
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index c12d1fe..5a64588 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -9,7 +9,6 @@
 #include <cctype>
 #include <cstdio>
 #include <cstdlib>
-#include <initializer_list>
 #include <limits>
 #include <memory>
 #include <stdexcept>
@@ -950,9 +949,9 @@
 class json_error : public std::runtime_error
 {
 public:
-  json_error(std::initializer_list<cm::string_view> message,
+  json_error(const std::string& message,
              cm::optional<Args> errorPath = cm::nullopt)
-    : std::runtime_error(cmCatViews(message))
+    : std::runtime_error(message)
     , ErrorPath{
       std::move(errorPath) // NOLINT(performance-move-const-arg)
     }
@@ -964,7 +963,7 @@
 const std::string& Args::PopFront(cm::string_view error)
 {
   if (this->empty()) {
-    throw json_error({ error });
+    throw json_error(std::string(error));
   }
   const std::string& res = *this->begin();
   this->advance(1);
@@ -974,7 +973,7 @@
 const std::string& Args::PopBack(cm::string_view error)
 {
   if (this->empty()) {
-    throw json_error({ error });
+    throw json_error(std::string(error));
   }
   const std::string& res = *(this->end() - 1);
   this->retreat(1);
@@ -999,7 +998,7 @@
     case Json::ValueType::objectValue:
       return "OBJECT"_s;
   }
-  throw json_error({ "invalid JSON type found"_s });
+  throw json_error("invalid JSON type found");
 }
 
 int ParseIndex(
@@ -1008,14 +1007,14 @@
 {
   unsigned long lindex;
   if (!cmStrToULong(str, &lindex)) {
-    throw json_error({ "expected an array index, got: '"_s, str, "'"_s },
+    throw json_error(cmStrCat("expected an array index, got: '"_s, str, "'"_s),
                      progress);
   }
   Json::ArrayIndex index = static_cast<Json::ArrayIndex>(lindex);
   if (index >= max) {
     cmAlphaNum sizeStr{ max };
-    throw json_error({ "expected an index less than "_s, sizeStr.View(),
-                       " got '"_s, str, "'"_s },
+    throw json_error(cmStrCat("expected an index less than "_s, sizeStr.View(),
+                              " got '"_s, str, "'"_s),
                      progress);
   }
   return index;
@@ -1036,16 +1035,16 @@
     } else if (search->isObject()) {
       if (!search->isMember(field)) {
         const auto progressStr = cmJoin(progress, " "_s);
-        throw json_error({ "member '"_s, progressStr, "' not found"_s },
+        throw json_error(cmStrCat("member '"_s, progressStr, "' not found"_s),
                          progress);
       }
       search = &(*search)[field];
     } else {
       const auto progressStr = cmJoin(progress, " "_s);
       throw json_error(
-        { "invalid path '"_s, progressStr,
-          "', need element of OBJECT or ARRAY type to lookup '"_s, field,
-          "' got "_s, JsonTypeToString(search->type()) },
+        cmStrCat("invalid path '"_s, progressStr,
+                 "', need element of OBJECT or ARRAY type to lookup '"_s,
+                 field, "' got "_s, JsonTypeToString(search->type())),
         progress);
     }
   }
@@ -1061,7 +1060,7 @@
   std::string error;
   if (!jsonReader->parse(jsonstr.data(), jsonstr.data() + jsonstr.size(),
                          &json, &error)) {
-    throw json_error({ "failed parsing json string: "_s, error });
+    throw json_error(cmStrCat("failed parsing json string: "_s, error));
   }
   return json;
 }
@@ -1101,9 +1100,9 @@
         mode != "LENGTH"_s && mode != "REMOVE"_s && mode != "SET"_s &&
         mode != "EQUAL"_s) {
       throw json_error(
-        { "got an invalid mode '"_s, mode,
-          "', expected one of GET, TYPE, MEMBER, LENGTH, REMOVE, SET, "
-          " EQUAL"_s });
+        cmStrCat("got an invalid mode '"_s, mode,
+                 "', expected one of GET, TYPE, MEMBER, LENGTH, REMOVE, SET, "
+                 " EQUAL"_s));
     }
 
     const auto& jsonstr = args.PopFront("missing json string argument"_s);
@@ -1127,10 +1126,11 @@
       const auto& indexStr = args.PopBack("missing member index"_s);
       const auto& value = ResolvePath(json, args);
       if (!value.isObject()) {
-        throw json_error({ "MEMBER needs to be called with an element of "
-                           "type OBJECT, got "_s,
-                           JsonTypeToString(value.type()) },
-                         args);
+        throw json_error(
+          cmStrCat("MEMBER needs to be called with an element of "
+                   "type OBJECT, got "_s,
+                   JsonTypeToString(value.type())),
+          args);
       }
       const auto index = ParseIndex(
         indexStr, Args{ args.begin(), args.end() + 1 }, value.size());
@@ -1140,9 +1140,9 @@
     } else if (mode == "LENGTH"_s) {
       const auto& value = ResolvePath(json, args);
       if (!value.isArray() && !value.isObject()) {
-        throw json_error({ "LENGTH needs to be called with an "
-                           "element of type ARRAY or OBJECT, got "_s,
-                           JsonTypeToString(value.type()) },
+        throw json_error(cmStrCat("LENGTH needs to be called with an "
+                                  "element of type ARRAY or OBJECT, got "_s,
+                                  JsonTypeToString(value.type())),
                          args);
       }
 
@@ -1165,9 +1165,9 @@
         value.removeMember(toRemove, &removed);
 
       } else {
-        throw json_error({ "REMOVE needs to be called with an "
-                           "element of type ARRAY or OBJECT, got "_s,
-                           JsonTypeToString(value.type()) },
+        throw json_error(cmStrCat("REMOVE needs to be called with an "
+                                  "element of type ARRAY or OBJECT, got "_s,
+                                  JsonTypeToString(value.type())),
                          args);
       }
       makefile.AddDefinition(*outputVariable, WriteJson(json));
@@ -1189,9 +1189,9 @@
           value.append(newValue);
         }
       } else {
-        throw json_error({ "SET needs to be called with an "
-                           "element of type OBJECT or ARRAY, got "_s,
-                           JsonTypeToString(value.type()) });
+        throw json_error(cmStrCat("SET needs to be called with an "
+                                  "element of type OBJECT or ARRAY, got "_s,
+                                  JsonTypeToString(value.type())));
       }
 
       makefile.AddDefinition(*outputVariable, WriteJson(json));
@@ -1207,7 +1207,7 @@
     if (outputVariable && e.ErrorPath) {
       const auto errorPath = cmJoin(*e.ErrorPath, "-");
       makefile.AddDefinition(*outputVariable,
-                             cmCatViews({ errorPath, "-NOTFOUND"_s }));
+                             cmStrCat(errorPath, "-NOTFOUND"_s));
     } else if (outputVariable) {
       makefile.AddDefinition(*outputVariable, "NOTFOUND"_s);
     }
@@ -1215,7 +1215,7 @@
     if (errorVariable) {
       makefile.AddDefinition(*errorVariable, e.what());
     } else {
-      status.SetError(cmCatViews({ "sub-command JSON "_s, e.what(), "."_s }));
+      status.SetError(cmStrCat("sub-command JSON "_s, e.what(), "."_s));
       success = false;
     }
   }
diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h
index 6bf7c3c..487beb4 100644
--- a/Source/cmTargetPropCommandBase.h
+++ b/Source/cmTargetPropCommandBase.h
@@ -24,8 +24,8 @@
     NO_FLAGS = 0x0,
     PROCESS_BEFORE = 0x1,
     PROCESS_AFTER = 0x2,
-    PROCESS_SYSTEM = 0x3,
-    PROCESS_REUSE_FROM = 0x4
+    PROCESS_SYSTEM = 0x4,
+    PROCESS_REUSE_FROM = 0x8
   };
 
   bool HandleArguments(std::vector<std::string> const& args,
diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx
index 8764f21..6702b7b 100644
--- a/Source/cmVSSetupHelper.cxx
+++ b/Source/cmVSSetupHelper.cxx
@@ -87,12 +87,11 @@
 
 cmVSSetupAPIHelper::cmVSSetupAPIHelper(unsigned int version)
   : Version(version)
-  , setupConfig(NULL)
-  , setupConfig2(NULL)
-  , setupHelper(NULL)
-  , initializationFailure(false)
+  , setupConfig(nullptr)
+  , setupConfig2(nullptr)
+  , setupHelper(nullptr)
 {
-  comInitialized = CoInitializeEx(NULL, 0);
+  comInitialized = CoInitializeEx(nullptr, 0);
   if (SUCCEEDED(comInitialized)) {
     Initialize();
   } else {
@@ -102,11 +101,12 @@
 
 cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
 {
-  setupHelper = NULL;
-  setupConfig2 = NULL;
-  setupConfig = NULL;
-  if (SUCCEEDED(comInitialized))
+  setupHelper = nullptr;
+  setupConfig2 = nullptr;
+  setupConfig = nullptr;
+  if (SUCCEEDED(comInitialized)) {
     CoUninitialize();
+  }
 }
 
 bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation,
@@ -159,12 +159,12 @@
   // the
   // component name ex: Microsoft.VisualStudio.Component.Windows10SDK.10240
   if (id.find(Win10SDKComponent) != std::wstring::npos &&
-      type.compare(ComponentType) == 0) {
+      type == ComponentType) {
     bWin10SDK = true;
     ret = true;
   }
 
-  if (id.compare(Win81SDKComponent) == 0 && type.compare(ComponentType) == 0) {
+  if (id == Win81SDKComponent && type == ComponentType) {
     bWin81SDK = true;
     ret = true;
   }
@@ -177,8 +177,9 @@
 bool cmVSSetupAPIHelper::GetVSInstanceInfo(
   SmartCOMPtr<ISetupInstance2> pInstance, VSInstanceInfo& vsInstanceInfo)
 {
-  if (pInstance == NULL)
+  if (pInstance == nullptr) {
     return false;
+  }
 
   InstanceState state;
   if (FAILED(pInstance->GetState(&state))) {
@@ -188,21 +189,19 @@
   SmartBSTR bstrVersion;
   if (FAILED(pInstance->GetInstallationVersion(&bstrVersion))) {
     return false;
-  } else {
-    vsInstanceInfo.Version =
-      cmsys::Encoding::ToNarrow(std::wstring(bstrVersion));
   }
+  vsInstanceInfo.Version =
+    cmsys::Encoding::ToNarrow(std::wstring(bstrVersion));
 
   // Reboot may have been required before the installation path was created.
   SmartBSTR bstrInstallationPath;
   if ((eLocal & state) == eLocal) {
     if (FAILED(pInstance->GetInstallationPath(&bstrInstallationPath))) {
       return false;
-    } else {
-      vsInstanceInfo.VSInstallLocation =
-        cmsys::Encoding::ToNarrow(std::wstring(bstrInstallationPath));
-      cmSystemTools::ConvertToUnixSlashes(vsInstanceInfo.VSInstallLocation);
     }
+    vsInstanceInfo.VSInstallLocation =
+      cmsys::Encoding::ToNarrow(std::wstring(bstrInstallationPath));
+    cmSystemTools::ConvertToUnixSlashes(vsInstanceInfo.VSInstallLocation);
   }
 
   // Check if a compiler is installed with this instance.
@@ -220,7 +219,7 @@
 
     LPSAFEARRAY lpsaPackages;
     if (FAILED(pInstance->GetPackages(&lpsaPackages)) ||
-        lpsaPackages == NULL) {
+        lpsaPackages == nullptr) {
       return false;
     }
 
@@ -229,11 +228,12 @@
 
     IUnknown** ppData = (IUnknown**)lpsaPackages->pvData;
     for (int i = lower; i < upper; i++) {
-      SmartCOMPtr<ISetupPackageReference> package = NULL;
+      SmartCOMPtr<ISetupPackageReference> package = nullptr;
       if (FAILED(ppData[i]->QueryInterface(IID_ISetupPackageReference,
                                            (void**)&package)) ||
-          package == NULL)
+          package == nullptr) {
         continue;
+      }
 
       bool win10SDKInstalled = false;
       bool win81SDkInstalled = false;
@@ -289,7 +289,8 @@
 
 bool cmVSSetupAPIHelper::IsEWDKEnabled()
 {
-  std::string envEnterpriseWDK, envDisableRegistryUse;
+  std::string envEnterpriseWDK;
+  std::string envDisableRegistryUse;
   cmSystemTools::GetEnv("EnterpriseWDK", envEnterpriseWDK);
   cmSystemTools::GetEnv("DisableRegistryUse", envDisableRegistryUse);
   if (!cmSystemTools::Strucmp(envEnterpriseWDK.c_str(), "True") &&
@@ -370,11 +371,12 @@
 bool cmVSSetupAPIHelper::EnumerateVSInstancesWithCOM(
   std::vector<VSInstanceInfo>& VSInstances)
 {
-  if (initializationFailure || setupConfig == NULL || setupConfig2 == NULL ||
-      setupHelper == NULL)
+  if (initializationFailure || setupConfig == nullptr ||
+      setupConfig2 == nullptr || setupHelper == nullptr) {
     return false;
+  }
 
-  SmartCOMPtr<IEnumSetupInstances> enumInstances = NULL;
+  SmartCOMPtr<IEnumSetupInstances> enumInstances = nullptr;
   if (FAILED(
         setupConfig2->EnumInstances((IEnumSetupInstances**)&enumInstances)) ||
       !enumInstances) {
@@ -382,20 +384,21 @@
   }
 
   SmartCOMPtr<ISetupInstance> instance;
-  while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) {
-    SmartCOMPtr<ISetupInstance2> instance2 = NULL;
+  while (SUCCEEDED(enumInstances->Next(1, &instance, nullptr)) && instance) {
+    SmartCOMPtr<ISetupInstance2> instance2 = nullptr;
     if (FAILED(
           instance->QueryInterface(IID_ISetupInstance2, (void**)&instance2)) ||
         !instance2) {
-      instance = NULL;
+      instance = nullptr;
       continue;
     }
 
     VSInstanceInfo instanceInfo;
     bool isInstalled = GetVSInstanceInfo(instance2, instanceInfo);
-    instance = instance2 = NULL;
-    if (isInstalled)
+    instance = instance2 = nullptr;
+    if (isInstalled) {
       VSInstances.push_back(instanceInfo);
+    }
   }
   return true;
 }
@@ -403,18 +406,21 @@
 bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
 {
   bool isVSInstanceExists = false;
-  if (chosenInstanceInfo.VSInstallLocation.compare("") != 0) {
+  if (!chosenInstanceInfo.VSInstallLocation.empty()) {
     return true;
   }
 
   if (this->IsEWDKEnabled()) {
-    std::string envWindowsSdkDir81, envVSVersion, envVsInstallDir;
+    std::string envWindowsSdkDir81;
+    std::string envVSVersion;
+    std::string envVsInstallDir;
 
     cmSystemTools::GetEnv("WindowsSdkDir_81", envWindowsSdkDir81);
     cmSystemTools::GetEnv("VisualStudioVersion", envVSVersion);
     cmSystemTools::GetEnv("VSINSTALLDIR", envVsInstallDir);
-    if (envVSVersion.empty() || envVsInstallDir.empty())
+    if (envVSVersion.empty() || envVsInstallDir.empty()) {
       return false;
+    }
 
     chosenInstanceInfo.VSInstallLocation = envVsInstallDir;
     chosenInstanceInfo.Version = envVSVersion;
@@ -499,7 +505,7 @@
     return this->LoadSpecifiedVSInstanceFromDisk();
   }
 
-  if (vecVSInstances.size() > 0) {
+  if (!vecVSInstances.empty()) {
     isVSInstanceExists = true;
     int index = ChooseVSInstance(vecVSInstances);
     chosenInstanceInfo = vecVSInstances[index];
@@ -511,11 +517,13 @@
 int cmVSSetupAPIHelper::ChooseVSInstance(
   const std::vector<VSInstanceInfo>& vecVSInstances)
 {
-  if (vecVSInstances.size() == 0)
+  if (vecVSInstances.empty()) {
     return -1;
+  }
 
-  if (vecVSInstances.size() == 1)
+  if (vecVSInstances.size() == 1) {
     return 0;
+  }
 
   unsigned int chosenIndex = 0;
   for (unsigned int i = 1; i < vecVSInstances.size(); i++) {
@@ -589,32 +597,33 @@
 
 bool cmVSSetupAPIHelper::Initialize()
 {
-  if (initializationFailure)
+  if (initializationFailure) {
     return false;
+  }
 
   if (FAILED(comInitialized)) {
     initializationFailure = true;
     return false;
   }
 
-  if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, NULL,
+  if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, nullptr,
                                           IID_ISetupConfiguration,
                                           CLSCTX_INPROC_SERVER)) ||
-      setupConfig == NULL) {
+      setupConfig == nullptr) {
     initializationFailure = true;
     return false;
   }
 
   if (FAILED(setupConfig.QueryInterface(IID_ISetupConfiguration2,
                                         (void**)&setupConfig2)) ||
-      setupConfig2 == NULL) {
+      setupConfig2 == nullptr) {
     initializationFailure = true;
     return false;
   }
 
   if (FAILED(
         setupConfig.QueryInterface(IID_ISetupHelper, (void**)&setupHelper)) ||
-      setupHelper == NULL) {
+      setupHelper == nullptr) {
     initializationFailure = true;
     return false;
   }
diff --git a/Source/cmVSSetupHelper.h b/Source/cmVSSetupHelper.h
index a16f00b..b8be9b9 100644
--- a/Source/cmVSSetupHelper.h
+++ b/Source/cmVSSetupHelper.h
@@ -17,18 +17,20 @@
 class SmartCOMPtr
 {
 public:
-  SmartCOMPtr() { ptr = NULL; }
+  SmartCOMPtr() = default;
   SmartCOMPtr(T* p)
   {
     ptr = p;
-    if (ptr != NULL)
+    if (ptr != nullptr) {
       ptr->AddRef();
+    }
   }
   SmartCOMPtr(const SmartCOMPtr<T>& sptr)
   {
     ptr = sptr.ptr;
-    if (ptr != NULL)
+    if (ptr != nullptr) {
       ptr->AddRef();
+    }
   }
   T** operator&() { return &ptr; }
   T* operator->() { return ptr; }
@@ -36,8 +38,9 @@
   {
     if (*this != p) {
       ptr = p;
-      if (ptr != NULL)
+      if (ptr != nullptr) {
         ptr->AddRef();
+      }
     }
     return *this;
   }
@@ -45,11 +48,10 @@
   template <class I>
   HRESULT QueryInterface(REFCLSID rclsid, I** pp)
   {
-    if (pp != NULL) {
+    if (pp != nullptr) {
       return ptr->QueryInterface(rclsid, (void**)pp);
-    } else {
-      return E_FAIL;
     }
+    return E_FAIL;
   }
   HRESULT CoCreateInstance(REFCLSID clsid, IUnknown* pUnknown,
                            REFIID interfaceId, DWORD dwClsContext = CLSCTX_ALL)
@@ -60,18 +62,19 @@
   }
   ~SmartCOMPtr()
   {
-    if (ptr != NULL)
+    if (ptr != nullptr) {
       ptr->Release();
+    }
   }
 
 private:
-  T* ptr;
+  T* ptr = nullptr;
 };
 
 class SmartBSTR
 {
 public:
-  SmartBSTR() { str = NULL; }
+  SmartBSTR() = default;
   SmartBSTR(const SmartBSTR& src) = delete;
   SmartBSTR& operator=(const SmartBSTR& src) = delete;
   operator BSTR() const { return str; }
@@ -79,7 +82,7 @@
   ~SmartBSTR() throw() { ::SysFreeString(str); }
 
 private:
-  BSTR str;
+  BSTR str = nullptr;
 };
 
 struct VSInstanceInfo
@@ -129,7 +132,7 @@
   SmartCOMPtr<ISetupConfiguration2> setupConfig2;
   SmartCOMPtr<ISetupHelper> setupHelper;
   // used to indicate failure in Initialize(), so we don't have to call again
-  bool initializationFailure;
+  bool initializationFailure = false;
   // indicated if COM initialization is successful
   HRESULT comInitialized;
   // current best instance of VS selected
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index cd73551..e06dc9b 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -80,10 +80,10 @@
   bool HasContent = false;
   std::string Tag;
 
-  Elem(std::ostream& s, const std::string& tag)
+  Elem(std::ostream& s, std::string tag)
     : S(s)
     , Indent(0)
-    , Tag(tag)
+    , Tag(std::move(tag))
   {
     this->StartElement();
   }
@@ -200,7 +200,7 @@
   }
 };
 
-static std::string cmVS10EscapeComment(std::string comment)
+static std::string cmVS10EscapeComment(std::string const& comment)
 {
   // MSBuild takes the CDATA of a <Message></Message> element and just
   // does "echo $CDATA" with no escapes.  We must encode the string.
@@ -275,8 +275,8 @@
     this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
   this->NsightTegra = gg->IsNsightTegra();
   this->Android = gg->TargetsAndroid();
-  for (int i = 0; i < 4; ++i) {
-    this->NsightTegraVersion[i] = 0;
+  for (unsigned int& version : this->NsightTegraVersion) {
+    version = 0;
   }
   sscanf(gg->GetNsightTegraVersion().c_str(), "%u.%u.%u.%u",
          &this->NsightTegraVersion[0], &this->NsightTegraVersion[1],
@@ -293,9 +293,7 @@
   this->ClassifyAllConfigSources();
 }
 
-cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
-{
-}
+cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator() = default;
 
 std::string cmVisualStudio10TargetGenerator::CalcCondition(
   const std::string& config) const
@@ -426,7 +424,7 @@
     cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
              this->Name, ProjectFileExtension);
   cmGeneratedFileStream BuildFileStream(path);
-  const std::string PathToProjectFile = path;
+  const std::string& PathToProjectFile = path;
   BuildFileStream.SetCopyIfDifferent(true);
 
   // Write the encoding header into the file
@@ -456,7 +454,7 @@
 void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
   cmGeneratedFileStream& BuildFileStream)
 {
-  BuildFileStream << "<?xml version=\"1.0\" encoding=\""
+  BuildFileStream << R"(<?xml version="1.0" encoding=")"
                   << this->GlobalGenerator->Encoding() << "\"?>";
   {
     Elem e0(BuildFileStream, "Project");
@@ -728,7 +726,7 @@
           : customDir +
             this->GlobalGenerator
               ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
-            "extras\\visual_studio_integration\\MSBuildExtensions\\";
+            R"(extras\visual_studio_integration\MSBuildExtensions\)";
         Elem(e1, "Import")
           .Attribute("Project",
                      std::move(cudaPath) + "CUDA " +
@@ -831,7 +829,7 @@
           : customDir +
             this->GlobalGenerator
               ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
-            "extras\\visual_studio_integration\\MSBuildExtensions\\";
+            R"(extras\visual_studio_integration\MSBuildExtensions\)";
         Elem(e1, "Import")
           .Attribute("Project",
                      std::move(cudaPath) + "CUDA " +
@@ -1033,8 +1031,9 @@
   std::vector<std::string> keys = this->GeneratorTarget->GetPropertyKeys();
   for (std::string const& keyIt : keys) {
     static const cm::string_view prefix = "VS_GLOBAL_";
-    if (!cmHasPrefix(keyIt, prefix))
+    if (!cmHasPrefix(keyIt, prefix)) {
       continue;
+    }
     cm::string_view globalKey = cm::string_view(keyIt).substr(prefix.length());
     // Skip invalid or separately-handled properties.
     if (globalKey.empty() || globalKey == "PROJECT_TYPES" ||
@@ -1042,8 +1041,9 @@
       continue;
     }
     cmValue value = this->GeneratorTarget->GetProperty(keyIt);
-    if (!value)
+    if (!value) {
       continue;
+    }
     e1.Element(globalKey, *value);
   }
 }
@@ -1056,14 +1056,11 @@
     return true;
   }
 
-  for (cmGeneratorTarget::AllConfigSource const& si :
-       this->GeneratorTarget->GetAllConfigSources()) {
-    if (si.Source->GetCustomCommand()) {
-      return true;
-    }
-  }
-
-  return false;
+  auto const& config_sources = this->GeneratorTarget->GetAllConfigSources();
+  return std::any_of(config_sources.begin(), config_sources.end(),
+                     [](cmGeneratorTarget::AllConfigSource const& si) {
+                       return si.Source->GetCustomCommand();
+                     });
 }
 
 void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0)
@@ -1233,7 +1230,7 @@
       e2.Attribute("Include", obj);
 
       if (this->ProjectType != VsProjectType::csproj) {
-        std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h";
+        std::string hFileName = obj.substr(0, obj.find_last_of('.')) + ".h";
         e2.Element("DependentUpon", hFileName);
 
         for (std::string const& c : this->Configurations) {
@@ -1925,7 +1922,7 @@
   char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
   fout.write(magic, 3);
 
-  fout << "<?xml version=\"1.0\" encoding=\""
+  fout << R"(<?xml version="1.0" encoding=")"
        << this->GlobalGenerator->Encoding() << "\"?>";
   {
     Elem e0(fout, "Project");
@@ -2081,7 +2078,7 @@
     e2.Element("FileType", "CppForm");
   } else if (this->IsXamlHeader(fileName)) {
     e2.Element("DependentUpon",
-               fileName.substr(0, fileName.find_last_of(".")));
+               fileName.substr(0, fileName.find_last_of('.')));
   }
   this->FinishWritingSource(e2, toolSettings);
 }
@@ -2117,7 +2114,7 @@
 bool cmVisualStudio10TargetGenerator::PropertyIsSameInAllConfigs(
   const ConfigToSettings& toolSettings, const std::string& propName)
 {
-  std::string firstPropValue = "";
+  std::string firstPropValue;
   for (const auto& configToSettings : toolSettings) {
     const std::unordered_map<std::string, std::string>& settings =
       configToSettings.second;
@@ -2338,19 +2335,16 @@
       if (!deployLocation.empty()) {
         e2.Element("Link", deployLocation + "\\%(FileName)%(Extension)");
       }
-      for (size_t i = 0; i != this->Configurations.size(); ++i) {
-        if (cge->Evaluate(this->LocalGenerator, this->Configurations[i]) ==
-            "1") {
+      for (auto& config : this->Configurations) {
+        if (cge->Evaluate(this->LocalGenerator, config) == "1") {
           e2.WritePlatformConfigTag("DeploymentContent",
                                     "'$(Configuration)|$(Platform)'=='" +
-                                      this->Configurations[i] + "|" +
-                                      this->Platform + "'",
+                                      config + "|" + this->Platform + "'",
                                     "true");
         } else {
           e2.WritePlatformConfigTag("ExcludedFromBuild",
                                     "'$(Configuration)|$(Platform)'=='" +
-                                      this->Configurations[i] + "|" +
-                                      this->Platform + "'",
+                                      config + "|" + this->Platform + "'",
                                     "true");
         }
       }
@@ -2401,8 +2395,9 @@
     // added with the plain filename without any path. This means the file will
     // show up at root-level of the csproj (where CMakeLists.txt etc. are).
     std::string link = this->GetCSharpSourceLink(sf);
-    if (link.empty())
+    if (link.empty()) {
       link = cmsys::SystemTools::GetFilenameName(sf->GetFullPath());
+    }
     e2.Element("Link", link);
   }
 
@@ -2535,7 +2530,7 @@
 
       Elem e2(e1, tool);
       bool isCSharp = (si.Source->GetLanguage() == "CSharp");
-      if (isCSharp && exclude_configs.size() > 0) {
+      if (isCSharp && !exclude_configs.empty()) {
         std::stringstream conditions;
         bool firstConditionSet{ false };
         for (const auto& ci : include_configs) {
@@ -2677,7 +2672,7 @@
     ? "C"
     : this->GlobalGenerator->GetLanguageFromExtension(ext.c_str());
   std::string lang = this->LocalGenerator->GetSourceFileLanguage(sf);
-  const char* compileAs = 0;
+  const char* compileAs = nullptr;
   if (lang != extLang) {
     if (lang == "CXX") {
       // force a C++ file type
@@ -2861,10 +2856,9 @@
   if (this->IsXamlSource(source->GetFullPath())) {
     const std::string& fileName = source->GetFullPath();
     e2.Element("DependentUpon",
-               fileName.substr(0, fileName.find_last_of(".")));
+               fileName.substr(0, fileName.find_last_of('.')));
   }
   if (this->ProjectType == VsProjectType::csproj) {
-    std::string f = source->GetFullPath();
     using CsPropMap = std::map<std::string, std::string>;
     CsPropMap sourceFileTags;
     this->GetCSharpSourceProperties(&sf, sourceFileTags);
@@ -2905,7 +2899,7 @@
 
     if (ttype >= cmStateEnums::UTILITY) {
       e1.WritePlatformConfigTag(
-        "IntDir", cond, "$(Platform)\\$(Configuration)\\$(ProjectName)\\");
+        "IntDir", cond, R"($(Platform)\$(Configuration)\$(ProjectName)\)");
     } else {
       std::string intermediateDir = cmStrCat(
         this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
@@ -3072,12 +3066,9 @@
 
 bool cmVisualStudio10TargetGenerator::ComputeClOptions()
 {
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeClOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeClOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeClOptions(
@@ -3435,7 +3426,7 @@
     // add AdditionalUsingDirectories
     if (this->AdditionalUsingDirectories.count(configName) > 0) {
       std::string dirs;
-      for (auto u : this->AdditionalUsingDirectories[configName]) {
+      for (auto const& u : this->AdditionalUsingDirectories[configName]) {
         if (!dirs.empty()) {
           dirs.append(";");
         }
@@ -3448,12 +3439,9 @@
 
 bool cmVisualStudio10TargetGenerator::ComputeRcOptions()
 {
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeRcOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeRcOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeRcOptions(
@@ -3502,13 +3490,12 @@
   if (!this->GlobalGenerator->IsCudaEnabled()) {
     return true;
   }
-  for (std::string const& c : this->Configurations) {
-    if (this->GeneratorTarget->IsLanguageUsed("CUDA", c) &&
-        !this->ComputeCudaOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(this->Configurations.begin(), this->Configurations.end(),
+                     [this](std::string const& c) {
+                       return !this->GeneratorTarget->IsLanguageUsed("CUDA",
+                                                                     c) ||
+                         this->ComputeCudaOptions(c);
+                     });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeCudaOptions(
@@ -3594,7 +3581,7 @@
       // limitation by creating the directory and passing the flag ourselves.
       pdb = this->ConvertPath(pdb, true);
       ConvertToWindowsSlash(pdb);
-      std::string const clFd = "-Xcompiler=\"-Fd\\\"" + pdb + "\\\"\"";
+      std::string const clFd = R"(-Xcompiler="-Fd\")" + pdb + R"(\"")";
       cudaOptions.AppendFlagString("AdditionalOptions", clFd);
     }
   }
@@ -3678,12 +3665,9 @@
   if (!this->GlobalGenerator->IsCudaEnabled()) {
     return true;
   }
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeCudaLinkOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeCudaLinkOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
@@ -3777,12 +3761,9 @@
   if (!this->GlobalGenerator->IsMarmasmEnabled()) {
     return true;
   }
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeMarmasmOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeMarmasmOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeMarmasmOptions(
@@ -3830,12 +3811,9 @@
   if (!this->GlobalGenerator->IsMasmEnabled()) {
     return true;
   }
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeMasmOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeMasmOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeMasmOptions(
@@ -3883,12 +3861,9 @@
   if (!this->GlobalGenerator->IsNasmEnabled()) {
     return true;
   }
-  for (std::string const& c : this->Configurations) {
-    if (!this->ComputeNasmOptions(c)) {
-      return false;
-    }
-  }
-  return true;
+  return std::all_of(
+    this->Configurations.begin(), this->Configurations.end(),
+    [this](std::string const& c) { return this->ComputeNasmOptions(c); });
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeNasmOptions(
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 60e9736..166cdf7 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -203,7 +203,6 @@
   void WriteStdOutEncodingUtf8(Elem& e1);
   void UpdateCache();
 
-private:
   friend class cmVS10GeneratorOptions;
   using Options = cmVS10GeneratorOptions;
   using OptionsMap = std::map<std::string, std::unique_ptr<Options>>;
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index e6f5ece..6e98874 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -51,9 +51,9 @@
 void cmVisualStudioGeneratorOptions::AddTable(cmVS7FlagTable const* table)
 {
   if (table) {
-    for (int i = 0; i < FlagTableCount; ++i) {
-      if (!this->FlagTable[i]) {
-        this->FlagTable[i] = table;
+    for (auto& flag : this->FlagTable) {
+      if (!flag) {
+        flag = table;
         break;
       }
     }
@@ -62,8 +62,8 @@
 
 void cmVisualStudioGeneratorOptions::ClearTables()
 {
-  for (int i = 0; i < FlagTableCount; ++i) {
-    this->FlagTable[i] = nullptr;
+  for (auto& flag : this->FlagTable) {
+    flag = nullptr;
   }
 }
 
@@ -115,8 +115,7 @@
   if (this->CurrentTool != CSharpCompiler) {
     return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
   }
-  std::map<std::string, FlagValue>::const_iterator i =
-    this->FlagMap.find("DebugType");
+  auto i = this->FlagMap.find("DebugType");
   if (i != this->FlagMap.end()) {
     if (i->second.size() == 1) {
       return i->second[0] != "none";
@@ -138,22 +137,14 @@
 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
 {
   // Look for a _UNICODE definition.
-  for (std::string const& di : this->Defines) {
-    if (di == "_UNICODE") {
-      return true;
-    }
-  }
-  return false;
+  return std::any_of(this->Defines.begin(), this->Defines.end(),
+                     [](std::string const& di) { return di == "_UNICODE"; });
 }
 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
 {
   // Look for a _SBCS definition.
-  for (std::string const& di : this->Defines) {
-    if (di == "_SBCS") {
-      return true;
-    }
-  }
-  return false;
+  return std::any_of(this->Defines.begin(), this->Defines.end(),
+                     [](std::string const& di) { return di == "_SBCS"; });
 }
 
 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
@@ -267,8 +258,7 @@
   }
 
   if (this->CurrentTool == CudaCompiler) {
-    std::map<std::string, FlagValue>::iterator i =
-      this->FlagMap.find("CudaRuntime");
+    auto i = this->FlagMap.find("CudaRuntime");
     if (i != this->FlagMap.end() && i->second.size() == 1) {
       std::string& cudaRuntime = i->second[0];
       if (cudaRuntime == "static") {
@@ -285,7 +275,7 @@
 void cmVisualStudioGeneratorOptions::PrependInheritedString(
   std::string const& key)
 {
-  std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
+  auto i = this->FlagMap.find(key);
   if (i == this->FlagMap.end() || i->second.size() != 1) {
     return;
   }
@@ -295,7 +285,7 @@
 
 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
 {
-  std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
+  auto i = this->FlagMap.find(key);
   if (i == this->FlagMap.end() || i->second.size() != 1) {
     return;
   }
@@ -339,7 +329,7 @@
   std::string const& key)
 {
   FlagValue value;
-  std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
+  auto i = this->FlagMap.find(key);
   if (i != this->FlagMap.end()) {
     value = i->second;
     this->FlagMap.erase(i);
@@ -373,8 +363,7 @@
   if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
     oss << "%(" << tag << ")";
   }
-  std::vector<std::string>::const_iterator de =
-    cmRemoveDuplicates(this->Defines);
+  auto de = cmRemoveDuplicates(this->Defines);
   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
     // Escape the definition for the compiler.
     std::string define;
diff --git a/Source/cmVisualStudioSlnData.cxx b/Source/cmVisualStudioSlnData.cxx
index 2a6dfc4..4b6754e 100644
--- a/Source/cmVisualStudioSlnData.cxx
+++ b/Source/cmVisualStudioSlnData.cxx
@@ -20,33 +20,34 @@
   return projectConfigurationMap[solutionConfiguration];
 }
 
-const cm::optional<cmSlnProjectEntry> cmSlnData::GetProjectByGUID(
+cm::optional<cmSlnProjectEntry> cmSlnData::GetProjectByGUID(
   const std::string& projectGUID) const
 {
-  ProjectStorage::const_iterator it(ProjectsByGUID.find(projectGUID));
-  if (it != ProjectsByGUID.end())
+  auto it(ProjectsByGUID.find(projectGUID));
+  if (it != ProjectsByGUID.end()) {
     return it->second;
-  else
-    return cm::nullopt;
+  }
+  return cm::nullopt;
 }
 
-const cm::optional<cmSlnProjectEntry> cmSlnData::GetProjectByName(
+cm::optional<cmSlnProjectEntry> cmSlnData::GetProjectByName(
   const std::string& projectName) const
 {
-  ProjectStringIndex::const_iterator it(ProjectNameIndex.find(projectName));
-  if (it != ProjectNameIndex.end())
+  auto it(ProjectNameIndex.find(projectName));
+  if (it != ProjectNameIndex.end()) {
     return it->second->second;
-  else
-    return cm::nullopt;
+  }
+  return cm::nullopt;
 }
 
 std::vector<cmSlnProjectEntry> cmSlnData::GetProjects() const
 {
-  ProjectStringIndex::const_iterator it(this->ProjectNameIndex.begin()),
-    itEnd(this->ProjectNameIndex.end());
+  auto it(this->ProjectNameIndex.begin());
+  auto itEnd(this->ProjectNameIndex.end());
   std::vector<cmSlnProjectEntry> result;
-  for (; it != itEnd; ++it)
+  for (; it != itEnd; ++it) {
     result.push_back(it->second->second);
+  }
   return result;
 }
 
@@ -54,9 +55,10 @@
   const std::string& projectGUID, const std::string& projectName,
   const std::string& projectRelativePath)
 {
-  ProjectStorage::iterator it(ProjectsByGUID.find(projectGUID));
-  if (it != ProjectsByGUID.end())
-    return NULL;
+  auto it(ProjectsByGUID.find(projectGUID));
+  if (it != ProjectsByGUID.end()) {
+    return nullptr;
+  }
   it = ProjectsByGUID
          .insert(ProjectStorage::value_type(
            projectGUID,
@@ -72,17 +74,20 @@
 {
   std::string solutionTarget = solutionConfiguration + "|" + platformName;
   cm::optional<cmSlnProjectEntry> project = GetProjectByName(projectName);
-  if (!project)
+  if (!project) {
     return platformName;
+  }
 
   std::string projectTarget = project->GetProjectConfiguration(solutionTarget);
-  if (projectTarget.empty())
+  if (projectTarget.empty()) {
     return platformName;
+  }
 
   std::vector<std::string> targetElements =
     cmSystemTools::SplitString(projectTarget, '|');
-  if (targetElements.size() != 2)
+  if (targetElements.size() != 2) {
     return platformName;
+  }
 
   return targetElements[1];
 }
diff --git a/Source/cmVisualStudioSlnData.h b/Source/cmVisualStudioSlnData.h
index 100dd9b..5f03895 100644
--- a/Source/cmVisualStudioSlnData.h
+++ b/Source/cmVisualStudioSlnData.h
@@ -13,12 +13,12 @@
 class cmSlnProjectEntry
 {
 public:
-  cmSlnProjectEntry() {}
-  cmSlnProjectEntry(const std::string& guid, const std::string& name,
-                    const std::string& relativePath)
-    : Guid(guid)
-    , Name(name)
-    , RelativePath(relativePath)
+  cmSlnProjectEntry() = default;
+  cmSlnProjectEntry(std::string guid, std::string name,
+                    std::string relativePath)
+    : Guid(std::move(guid))
+    , Name(std::move(name))
+    , RelativePath(std::move(relativePath))
   {
   }
 
@@ -56,10 +56,10 @@
     minimumVisualStudioVersion = version;
   }
 
-  const cm::optional<cmSlnProjectEntry> GetProjectByGUID(
+  cm::optional<cmSlnProjectEntry> GetProjectByGUID(
     const std::string& projectGUID) const;
 
-  const cm::optional<cmSlnProjectEntry> GetProjectByName(
+  cm::optional<cmSlnProjectEntry> GetProjectByName(
     const std::string& projectName) const;
 
   std::vector<cmSlnProjectEntry> GetProjects() const;
diff --git a/Source/cmVisualStudioSlnParser.cxx b/Source/cmVisualStudioSlnParser.cxx
index feab895..71c758e 100644
--- a/Source/cmVisualStudioSlnParser.cxx
+++ b/Source/cmVisualStudioSlnParser.cxx
@@ -80,19 +80,19 @@
 
 std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const
 {
-  if (this->Arg.second)
+  if (this->Arg.second) {
     return Quote + this->Arg.first + Quote;
-  else
-    return this->Arg.first;
+  }
+  return this->Arg.first;
 }
 
 const std::string& cmVisualStudioSlnParser::ParsedLine::GetValue(
   size_t idxValue) const
 {
-  if (idxValue < this->Values.size())
+  if (idxValue < this->Values.size()) {
     return this->Values[idxValue].first;
-  else
-    return BadString;
+  }
+  return BadString;
 }
 
 std::string cmVisualStudioSlnParser::ParsedLine::GetValueVerbatim(
@@ -100,12 +100,12 @@
 {
   if (idxValue < this->Values.size()) {
     const StringData& data = this->Values[idxValue];
-    if (data.second)
+    if (data.second) {
       return Quote + data.first + Quote;
-    else
-      return data.first;
-  } else
-    return BadString;
+    }
+    return data.first;
+  }
+  return BadString;
 }
 
 class cmVisualStudioSlnParser::State
@@ -140,17 +140,17 @@
   std::stack<FileState> Stack;
   std::string EndIgnoreTag;
   DataGroupSet RequestedData;
-  size_t CurrentLine;
+  size_t CurrentLine = 0;
 
   void IgnoreUntilTag(const std::string& endTag);
 };
 
 cmVisualStudioSlnParser::State::State(DataGroupSet requestedData)
   : RequestedData(requestedData)
-  , CurrentLine(0)
 {
-  if (this->RequestedData.test(DataGroupProjectDependenciesBit))
+  if (this->RequestedData.test(DataGroupProjectDependenciesBit)) {
     this->RequestedData.set(DataGroupProjectsBit);
+  }
   this->Stack.push(FileStateStart);
 }
 
@@ -206,7 +206,7 @@
       this->Stack.push(FileStateTopLevel);
       break;
     case FileStateTopLevel:
-      if (line.GetTag().compare("Project") == 0) {
+      if (line.GetTag() == "Project") {
         if (line.GetValueCount() != 3) {
           result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
           return false;
@@ -218,14 +218,15 @@
             return false;
           }
           this->Stack.push(FileStateProject);
-        } else
+        } else {
           this->IgnoreUntilTag("EndProject");
-      } else if (line.GetTag().compare("Global") == 0) {
+        }
+      } else if (line.GetTag() == "Global") {
 
         this->Stack.push(FileStateGlobal);
-      } else if (line.GetTag().compare("VisualStudioVersion") == 0) {
+      } else if (line.GetTag() == "VisualStudioVersion") {
         output.SetVisualStudioVersion(line.GetValue(0));
-      } else if (line.GetTag().compare("MinimumVisualStudioVersion") == 0) {
+      } else if (line.GetTag() == "MinimumVisualStudioVersion") {
         output.SetMinimumVisualStudioVersion(line.GetValue(0));
       } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
@@ -233,69 +234,75 @@
       }
       break;
     case FileStateProject:
-      if (line.GetTag().compare("EndProject") == 0)
+      if (line.GetTag() == "EndProject") {
         this->Stack.pop();
-      else if (line.GetTag().compare("ProjectSection") == 0) {
-        if (line.GetArg().compare("ProjectDependencies") == 0 &&
-            line.GetValue(0).compare("postProject") == 0) {
-          if (this->RequestedData.test(DataGroupProjectDependenciesBit))
+      } else if (line.GetTag() == "ProjectSection") {
+        if (line.GetArg() == "ProjectDependencies" &&
+            line.GetValue(0) == "postProject") {
+          if (this->RequestedData.test(DataGroupProjectDependenciesBit)) {
             this->Stack.push(FileStateProjectDependencies);
-          else
+          } else {
             this->IgnoreUntilTag("EndProjectSection");
-        } else
+          }
+        } else {
           this->IgnoreUntilTag("EndProjectSection");
+        }
       } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
       break;
     case FileStateProjectDependencies:
-      if (line.GetTag().compare("EndProjectSection") == 0)
+      if (line.GetTag() == "EndProjectSection") {
         this->Stack.pop();
-      else if (line.IsKeyValuePair())
+      } else if (line.IsKeyValuePair()) {
         // implement dependency storing here, once needed
         ;
-      else {
+      } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
       break;
     case FileStateGlobal:
-      if (line.GetTag().compare("EndGlobal") == 0)
+      if (line.GetTag() == "EndGlobal") {
         this->Stack.pop();
-      else if (line.GetTag().compare("GlobalSection") == 0) {
-        if (line.GetArg().compare("SolutionConfigurationPlatforms") == 0 &&
-            line.GetValue(0).compare("preSolution") == 0) {
-          if (this->RequestedData.test(DataGroupSolutionConfigurationsBit))
+      } else if (line.GetTag() == "GlobalSection") {
+        if (line.GetArg() == "SolutionConfigurationPlatforms" &&
+            line.GetValue(0) == "preSolution") {
+          if (this->RequestedData.test(DataGroupSolutionConfigurationsBit)) {
             this->Stack.push(FileStateSolutionConfigurations);
-          else
+          } else {
             this->IgnoreUntilTag("EndGlobalSection");
-        } else if (line.GetArg().compare("ProjectConfigurationPlatforms") ==
-                     0 &&
-                   line.GetValue(0).compare("postSolution") == 0) {
-          if (this->RequestedData.test(DataGroupProjectConfigurationsBit))
+          }
+        } else if (line.GetArg() == "ProjectConfigurationPlatforms" &&
+                   line.GetValue(0) == "postSolution") {
+          if (this->RequestedData.test(DataGroupProjectConfigurationsBit)) {
             this->Stack.push(FileStateProjectConfigurations);
-          else
+          } else {
             this->IgnoreUntilTag("EndGlobalSection");
-        } else if (line.GetArg().compare("NestedProjects") == 0 &&
-                   line.GetValue(0).compare("preSolution") == 0) {
-          if (this->RequestedData.test(DataGroupSolutionFiltersBit))
+          }
+        } else if (line.GetArg() == "NestedProjects" &&
+                   line.GetValue(0) == "preSolution") {
+          if (this->RequestedData.test(DataGroupSolutionFiltersBit)) {
             this->Stack.push(FileStateSolutionFilters);
-          else
+          } else {
             this->IgnoreUntilTag("EndGlobalSection");
-        } else if (this->RequestedData.test(DataGroupGenericGlobalSectionsBit))
+          }
+        } else if (this->RequestedData.test(
+                     DataGroupGenericGlobalSectionsBit)) {
           this->Stack.push(FileStateGlobalSection);
-        else
+        } else {
           this->IgnoreUntilTag("EndGlobalSection");
+        }
       } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
       break;
     case FileStateSolutionConfigurations:
-      if (line.GetTag().compare("EndGlobalSection") == 0)
+      if (line.GetTag() == "EndGlobalSection") {
         this->Stack.pop();
-      else if (line.IsKeyValuePair()) {
+      } else if (line.IsKeyValuePair()) {
         output.AddConfiguration(line.GetValue(0));
       } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
@@ -303,9 +310,9 @@
       }
       break;
     case FileStateProjectConfigurations:
-      if (line.GetTag().compare("EndGlobalSection") == 0)
+      if (line.GetTag() == "EndGlobalSection") {
         this->Stack.pop();
-      else if (line.IsKeyValuePair()) {
+      } else if (line.IsKeyValuePair()) {
         std::vector<std::string> tagElements =
           cmSystemTools::SplitString(line.GetTag(), '.');
         if (tagElements.size() != 3 && tagElements.size() != 4) {
@@ -324,7 +331,7 @@
           return false;
         }
 
-        if (activeBuild.compare("ActiveCfg") == 0) {
+        if (activeBuild == "ActiveCfg") {
           projectEntry->AddProjectConfiguration(solutionConfiguration,
                                                 line.GetValue(0));
         }
@@ -334,23 +341,23 @@
       }
       break;
     case FileStateSolutionFilters:
-      if (line.GetTag().compare("EndGlobalSection") == 0)
+      if (line.GetTag() == "EndGlobalSection") {
         this->Stack.pop();
-      else if (line.IsKeyValuePair())
+      } else if (line.IsKeyValuePair()) {
         // implement filter storing here, once needed
         ;
-      else {
+      } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
       break;
     case FileStateGlobalSection:
-      if (line.GetTag().compare("EndGlobalSection") == 0)
+      if (line.GetTag() == "EndGlobalSection") {
         this->Stack.pop();
-      else if (line.IsKeyValuePair())
+      } else if (line.IsKeyValuePair()) {
         // implement section storing here, once needed
         ;
-      else {
+      } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
@@ -385,11 +392,7 @@
   this->EndIgnoreTag = endTag;
 }
 
-cmVisualStudioSlnParser::ResultData::ResultData()
-  : Result(ResultOK)
-  , ResultLine(0)
-{
-}
+cmVisualStudioSlnParser::ResultData::ResultData() = default;
 
 void cmVisualStudioSlnParser::ResultData::Clear()
 {
@@ -487,34 +490,41 @@
 {
   std::string line;
   // Does the .sln start with a Byte Order Mark?
-  if (!this->ParseBOM(input, line, state))
+  if (!this->ParseBOM(input, line, state)) {
     return false;
+  }
   do {
     line = cmTrimWhitespace(line);
-    if (line.empty())
+    if (line.empty()) {
       continue;
+    }
     ParsedLine parsedLine;
     switch (state.NextLineFormat()) {
       case LineMultiValueTag:
-        if (!this->ParseMultiValueTag(line, parsedLine, state))
+        if (!this->ParseMultiValueTag(line, parsedLine, state)) {
           return false;
+        }
         break;
       case LineSingleValueTag:
-        if (!this->ParseSingleValueTag(line, parsedLine, state))
+        if (!this->ParseSingleValueTag(line, parsedLine, state)) {
           return false;
+        }
         break;
       case LineKeyValuePair:
-        if (!this->ParseKeyValuePair(line, parsedLine, state))
+        if (!this->ParseKeyValuePair(line, parsedLine, state)) {
           return false;
+        }
         break;
       case LineVerbatim:
         parsedLine.CopyVerbatim(line);
         break;
     }
-    if (parsedLine.IsComment())
+    if (parsedLine.IsComment()) {
       continue;
-    if (!state.Process(parsedLine, output, this->LastResult))
+    }
+    if (!state.Process(parsedLine, output, this->LastResult)) {
       return false;
+    }
   } while (state.ReadLine(input, line));
   return state.Finished(this->LastResult);
 }
@@ -533,8 +543,9 @@
     this->LastResult.SetError(ResultErrorReadingInput, 1);
     return false;
   }
-  if (!this->LastResult.HadBOM)
+  if (!this->LastResult.HadBOM) {
     line = bom + line; // it wasn't a BOM, prepend it to first line
+  }
   return true;
 }
 
@@ -544,9 +555,10 @@
 {
   size_t idxEqualSign = line.find('=');
   auto fullTag = cm::string_view(line).substr(0, idxEqualSign);
-  if (!this->ParseTag(fullTag, parsedLine, state))
+  if (!this->ParseTag(fullTag, parsedLine, state)) {
     return false;
-  if (idxEqualSign != line.npos) {
+  }
+  if (idxEqualSign != std::string::npos) {
     size_t idxFieldStart = idxEqualSign + 1;
     if (idxFieldStart < line.size()) {
       size_t idxParsing = idxFieldStart;
@@ -554,24 +566,27 @@
       for (;;) {
         idxParsing = line.find_first_of(",\"", idxParsing);
         bool fieldOver = false;
-        if (idxParsing == line.npos) {
+        if (idxParsing == std::string::npos) {
           fieldOver = true;
           if (inQuotes) {
             this->LastResult.SetError(ResultErrorInputStructure,
                                       state.GetCurrentLine());
             return false;
           }
-        } else if (line[idxParsing] == ',' && !inQuotes)
+        } else if (line[idxParsing] == ',' && !inQuotes) {
           fieldOver = true;
-        else if (line[idxParsing] == '"')
+        } else if (line[idxParsing] == '"') {
           inQuotes = !inQuotes;
+        }
         if (fieldOver) {
           if (!this->ParseValue(
                 line.substr(idxFieldStart, idxParsing - idxFieldStart),
-                parsedLine))
+                parsedLine)) {
             return false;
-          if (idxParsing == line.npos)
+          }
+          if (idxParsing == std::string::npos) {
             break; // end of last field
+          }
           idxFieldStart = idxParsing + 1;
         }
         ++idxParsing;
@@ -587,11 +602,13 @@
 {
   size_t idxEqualSign = line.find('=');
   auto fullTag = cm::string_view(line).substr(0, idxEqualSign);
-  if (!this->ParseTag(fullTag, parsedLine, state))
+  if (!this->ParseTag(fullTag, parsedLine, state)) {
     return false;
-  if (idxEqualSign != line.npos) {
-    if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine))
+  }
+  if (idxEqualSign != std::string::npos) {
+    if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine)) {
       return false;
+    }
   }
   return true;
 }
@@ -601,7 +618,7 @@
                                                 State& /*state*/)
 {
   size_t idxEqualSign = line.find('=');
-  if (idxEqualSign == line.npos) {
+  if (idxEqualSign == std::string::npos) {
     parsedLine.CopyVerbatim(line);
     return true;
   }
@@ -636,8 +653,9 @@
       return false;
     }
     parsedLine.SetQuotedArg(arg.substr(1, arg.size() - 2));
-  } else
+  } else {
     parsedLine.SetArg(arg);
+  }
   return true;
 }
 
@@ -645,11 +663,12 @@
                                          ParsedLine& parsedLine)
 {
   const std::string& trimmed = cmTrimWhitespace(value);
-  if (trimmed.empty())
+  if (trimmed.empty()) {
     parsedLine.AddValue(trimmed);
-  else if (trimmed.front() == '"' && trimmed.back() == '"')
+  } else if (trimmed.front() == '"' && trimmed.back() == '"') {
     parsedLine.AddQuotedValue(trimmed.substr(1, trimmed.size() - 2));
-  else
+  } else {
     parsedLine.AddValue(trimmed);
+  }
   return true;
 }
diff --git a/Source/cmVisualStudioSlnParser.h b/Source/cmVisualStudioSlnParser.h
index 60be598..0cac140 100644
--- a/Source/cmVisualStudioSlnParser.h
+++ b/Source/cmVisualStudioSlnParser.h
@@ -73,8 +73,8 @@
 
   struct ResultData
   {
-    ParseResult Result;
-    size_t ResultLine;
+    ParseResult Result = ResultOK;
+    size_t ResultLine = 0;
     bool HadBOM;
 
     ResultData();
diff --git a/Source/cmVisualStudioWCEPlatformParser.cxx b/Source/cmVisualStudioWCEPlatformParser.cxx
index 2f71cf5..d8d0da9 100644
--- a/Source/cmVisualStudioWCEPlatformParser.cxx
+++ b/Source/cmVisualStudioWCEPlatformParser.cxx
@@ -16,9 +16,9 @@
   const std::string vckey = registryBase + "\\Setup\\VC;ProductDir";
   const std::string vskey = registryBase + "\\Setup\\VS;ProductDir";
 
-  if (!cmSystemTools::ReadRegistryValue(vckey.c_str(), this->VcInstallDir,
+  if (!cmSystemTools::ReadRegistryValue(vckey, this->VcInstallDir,
                                         cmSystemTools::KeyWOW64_32) ||
-      !cmSystemTools::ReadRegistryValue(vskey.c_str(), this->VsInstallDir,
+      !cmSystemTools::ReadRegistryValue(vskey, this->VsInstallDir,
                                         cmSystemTools::KeyWOW64_32)) {
     return 0;
   }
@@ -44,13 +44,12 @@
 
 const char* cmVisualStudioWCEPlatformParser::GetArchitectureFamily() const
 {
-  std::map<std::string, std::string>::const_iterator it =
-    this->Macros.find("ARCHFAM");
+  auto it = this->Macros.find("ARCHFAM");
   if (it != this->Macros.end()) {
     return it->second.c_str();
   }
 
-  return 0;
+  return nullptr;
 }
 
 void cmVisualStudioWCEPlatformParser::StartElement(const std::string& name,
diff --git a/Source/cmVisualStudioWCEPlatformParser.h b/Source/cmVisualStudioWCEPlatformParser.h
index 2fff91c..895a90d 100644
--- a/Source/cmVisualStudioWCEPlatformParser.h
+++ b/Source/cmVisualStudioWCEPlatformParser.h
@@ -16,9 +16,8 @@
 class cmVisualStudioWCEPlatformParser : public cmXMLParser
 {
 public:
-  cmVisualStudioWCEPlatformParser(const char* name = NULL)
+  cmVisualStudioWCEPlatformParser(const char* name = nullptr)
     : RequiredName(name)
-    , FoundRequiredName(false)
   {
   }
 
@@ -42,9 +41,9 @@
   }
 
 protected:
-  virtual void StartElement(const std::string& name, const char** attributes);
-  void EndElement(const std::string& name);
-  void CharacterDataHandler(const char* data, int length);
+  void StartElement(const std::string& name, const char** attributes) override;
+  void EndElement(const std::string& name) override;
+  void CharacterDataHandler(const char* data, int length) override;
 
 private:
   std::string FixPaths(const std::string& paths) const;
@@ -61,7 +60,7 @@
   std::vector<std::string> AvailablePlatforms;
 
   const char* RequiredName;
-  bool FoundRequiredName;
+  bool FoundRequiredName = false;
   std::string VcInstallDir;
   std::string VsInstallDir;
 };
diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx
index 1e6b611..f73e62a 100644
--- a/Tests/CMakeLib/testStringAlgorithms.cxx
+++ b/Tests/CMakeLib/testStringAlgorithms.cxx
@@ -6,6 +6,8 @@
 #include <iostream>
 #include <sstream>
 #include <string>
+#include <type_traits>
+#include <utility>
 #include <vector>
 
 #include <cm/string_view>
@@ -144,6 +146,18 @@
     d -= val;
     assert_ok((d < div) && (d > -div), "cmStrCat double");
   }
+  {
+    std::string val;
+    std::string expect;
+    val.reserve(120 * cmStrLen("cmStrCat move"));
+    auto data = val.data();
+    for (int i = 0; i < 100; i++) {
+      val = cmStrCat(std::move(val), "cmStrCat move");
+      expect += "cmStrCat move";
+    }
+    assert_ok((val.data() == data), "cmStrCat move");
+    assert_string(val, expect, "cmStrCat move");
+  }
 
   // ----------------------------------------------------------------------
   // Test cmWrap
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index d4ef7ee..e5e592b 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1538,8 +1538,11 @@
     add_subdirectory(GoogleTest)
   endif()
 
-  if(CMake_TEST_FindPython OR CMake_TEST_FindPython_NumPy
+  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)
+    endif()
     add_subdirectory(FindPython)
   endif()
 
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt
index 669c412..0041b07 100644
--- a/Tests/Cuda/CMakeLists.txt
+++ b/Tests/Cuda/CMakeLists.txt
@@ -16,6 +16,7 @@
 add_cuda_test_macro(Cuda.Toolkit Toolkit)
 add_cuda_test_macro(Cuda.IncludePathNoToolkit IncludePathNoToolkit)
 add_cuda_test_macro(Cuda.SharedRuntimePlusToolkit SharedRuntimePlusToolkit)
+add_cuda_test_macro(Cuda.StaticRuntimePlusToolkit StaticRuntimePlusToolkit)
 add_cuda_test_macro(Cuda.Complex CudaComplex)
 add_cuda_test_macro(Cuda.ProperLinkFlags ProperLinkFlags)
 
@@ -24,10 +25,4 @@
   add_cuda_test_macro(Cuda.ProperDeviceLibraries ProperDeviceLibraries)
 endif()
 
-# The CUDA only ships the shared version of the toolkit libraries
-# on windows
-if(NOT WIN32)
-  add_cuda_test_macro(Cuda.StaticRuntimePlusToolkit StaticRuntimePlusToolkit)
-endif()
-
 add_cuda_test_macro(Cuda.WithC CudaWithC)
diff --git a/Tests/Cuda/SharedRuntimePlusToolkit/CMakeLists.txt b/Tests/Cuda/SharedRuntimePlusToolkit/CMakeLists.txt
index 61a3190..088be3b 100644
--- a/Tests/Cuda/SharedRuntimePlusToolkit/CMakeLists.txt
+++ b/Tests/Cuda/SharedRuntimePlusToolkit/CMakeLists.txt
@@ -15,16 +15,19 @@
 target_link_libraries(SharedToolkit PRIVATE Common PUBLIC CUDA::curand CUDA::nppif)
 target_link_libraries(SharedToolkit PUBLIC CUDA::cudart)
 
-# The CUDA only ships the shared version of the toolkit libraries
-# on windows
-if(NOT WIN32)
+# Verify the CUDA Toolkit has static libraries
+if(TARGET CUDA::curand_static AND
+   TARGET CUDA::nppif_static)
   #shared runtime with static toolkit libraries
   add_library(StaticToolkit SHARED static.cpp)
+  target_compile_definitions(StaticToolkit INTERFACE "HAS_STATIC_VERSION")
   target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
   target_link_libraries(StaticToolkit PUBLIC CUDA::cudart)
 
-  #static runtime with mixed toolkit libraries
+
+  #shared runtime with mixed toolkit libraries
   add_library(MixedToolkit SHARED mixed.cpp)
+  target_compile_definitions(MixedToolkit INTERFACE "HAS_MIXED_VERSION")
   target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand_static CUDA::nppif)
   target_link_libraries(MixedToolkit PUBLIC CUDA::cudart)
 endif()
diff --git a/Tests/Cuda/SharedRuntimePlusToolkit/main.cpp b/Tests/Cuda/SharedRuntimePlusToolkit/main.cpp
index 2a4da22..d958c3a 100644
--- a/Tests/Cuda/SharedRuntimePlusToolkit/main.cpp
+++ b/Tests/Cuda/SharedRuntimePlusToolkit/main.cpp
@@ -1,19 +1,28 @@
 
 #ifdef _WIN32
 #  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
 IMPORT int shared_version();
+
+#ifdef HAS_STATIC_VERSION
+IMPORT int static_version();
+#else
 int static_version()
 {
   return 0;
 }
+#endif
+
+#ifdef HAS_MIXED_VERSION
+IMPORT int mixed_version();
+#else
 int mixed_version()
 {
   return 0;
 }
-#else
-int shared_version();
-int static_version();
-int mixed_version();
 #endif
 
 int main()
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/CMakeLists.txt b/Tests/Cuda/StaticRuntimePlusToolkit/CMakeLists.txt
index df6c392..bb3dadf 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/CMakeLists.txt
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/CMakeLists.txt
@@ -15,15 +15,23 @@
 target_link_libraries(SharedToolkit PRIVATE Common PUBLIC CUDA::curand CUDA::nppif)
 target_link_libraries(SharedToolkit PUBLIC CUDA::cudart_static)
 
-#static runtime with static toolkit libraries
-add_library(StaticToolkit SHARED static.cpp)
-target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
-target_link_libraries(StaticToolkit PUBLIC CUDA::cudart_static)
+# Verify the CUDA Toolkit has static libraries
+if(TARGET CUDA::curand_static AND
+   TARGET CUDA::nppif_static)
+  #static runtime with static toolkit libraries
+  add_library(StaticToolkit SHARED static.cpp)
+  target_compile_definitions(StaticToolkit INTERFACE "HAS_STATIC_VERSION")
+  target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
+  target_link_libraries(StaticToolkit PUBLIC CUDA::cudart_static)
 
-#static runtime with mixed toolkit libraries
-add_library(MixedToolkit SHARED mixed.cpp)
-target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand CUDA::nppif_static)
-target_link_libraries(MixedToolkit PUBLIC CUDA::cudart_static)
+  #static runtime with mixed toolkit libraries
+  add_library(MixedToolkit SHARED mixed.cpp)
+  target_compile_definitions(MixedToolkit INTERFACE "HAS_MIXED_VERSION")
+  target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand CUDA::nppif_static)
+  target_link_libraries(MixedToolkit PUBLIC CUDA::cudart_static)
+endif()
 
 add_executable(StaticRuntimePlusToolkit main.cpp)
-target_link_libraries(StaticRuntimePlusToolkit PRIVATE SharedToolkit StaticToolkit MixedToolkit)
+target_link_libraries(StaticRuntimePlusToolkit PRIVATE SharedToolkit
+  $<TARGET_NAME_IF_EXISTS:StaticToolkit>
+  $<TARGET_NAME_IF_EXISTS:MixedToolkit>)
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/curand.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/curand.cpp
index 95872f0..fdd7b53 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/curand.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/curand.cpp
@@ -1,6 +1,12 @@
 // Comes from:
 // https://docs.nvidia.com/cuda/curand/host-api-overview.html#host-api-example
 
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
 /*
  * This program uses the host CURAND API to generate 100
  * pseudorandom floats.
@@ -25,7 +31,7 @@
     }                                                                         \
   } while (0)
 
-int curand_main()
+EXPORT int curand_main()
 {
   size_t n = 100;
   size_t i;
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/main.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/main.cpp
index 5a09f8e..d958c3a 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/main.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/main.cpp
@@ -1,8 +1,29 @@
 
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
 
-int shared_version();
-int static_version();
-int mixed_version();
+IMPORT int shared_version();
+
+#ifdef HAS_STATIC_VERSION
+IMPORT int static_version();
+#else
+int static_version()
+{
+  return 0;
+}
+#endif
+
+#ifdef HAS_MIXED_VERSION
+IMPORT int mixed_version();
+#else
+int mixed_version()
+{
+  return 0;
+}
+#endif
 
 int main()
 {
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/mixed.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/mixed.cpp
index a05140d..6de6886 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/mixed.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/mixed.cpp
@@ -1,8 +1,16 @@
 
-int curand_main();
-int nppif_main();
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
 
-int mixed_version()
+IMPORT int curand_main();
+IMPORT int nppif_main();
+
+EXPORT int mixed_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
index 2871090..ac5341c 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
@@ -1,6 +1,12 @@
 // Comes from
 // https://devtalk.nvidia.com/default/topic/1037482/gpu-accelerated-libraries/help-me-help-you-with-modern-cmake-and-cuda-mwe-for-npp/post/5271066/#5271066
 
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
 #include <cstdio>
 #include <iostream>
 
@@ -8,7 +14,7 @@
 #include <cuda_runtime_api.h>
 #include <nppi_filtering_functions.h>
 
-int nppif_main()
+EXPORT int nppif_main()
 {
   /**
    * 8-bit unsigned single-channel 1D row convolution.
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/shared.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/shared.cpp
index 9967b66..f3c3dbc 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/shared.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/shared.cpp
@@ -1,8 +1,16 @@
 
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
+
 int curand_main();
 int nppif_main();
 
-int shared_version()
+EXPORT int shared_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/static.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/static.cpp
index ca7eb4c..6932fa3 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/static.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/static.cpp
@@ -1,8 +1,16 @@
 
-int curand_main();
-int nppif_main();
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
 
-int static_version()
+IMPORT int curand_main();
+IMPORT int nppif_main();
+
+EXPORT int static_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index 39634ac..db08076 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -10,6 +10,7 @@
 add_cuda_test_macro(CudaOnly.EnableStandard CudaOnlyEnableStandard)
 add_cuda_test_macro(CudaOnly.ExportPTX CudaOnlyExportPTX)
 add_cuda_test_macro(CudaOnly.SharedRuntimePlusToolkit CudaOnlySharedRuntimePlusToolkit)
+add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
 add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98)
 add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit)
 add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang)
@@ -28,12 +29,6 @@
   add_cuda_test_macro(CudaOnly.GPUDebugFlag CudaOnlyGPUDebugFlag)
 endif()
 
-# The CUDA only ships the shared version of the toolkit libraries
-# on windows
-if(NOT WIN32)
-  add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
-endif()
-
 add_cuda_test_macro(CudaOnly.DeviceLTO CudaOnlyDeviceLTO)
 
 if(MSVC)
diff --git a/Tests/CudaOnly/SeparateCompilationPTX/main.cu b/Tests/CudaOnly/SeparateCompilationPTX/main.cu
index 164cde5..f94beff 100644
--- a/Tests/CudaOnly/SeparateCompilationPTX/main.cu
+++ b/Tests/CudaOnly/SeparateCompilationPTX/main.cu
@@ -21,10 +21,11 @@
   cuCtxCreate(&context, 0, device);
 
   CUmodule module;
-  cuModuleLoadData(&module, kernels);
-  if (module == nullptr) {
-    std::cerr << "Failed to load the embedded ptx" << std::endl;
+  CUresult result = cuModuleLoadData(&module, kernels);
+  std::cout << "module pointer: " << module << '\n';
+  if (result != CUDA_SUCCESS || module == nullptr) {
+    std::cerr << "Failed to load the embedded ptx with error: "
+              << static_cast<unsigned int>(result) << '\n';
     return 1;
   }
-  std::cout << module << std::endl;
 }
diff --git a/Tests/CudaOnly/SharedRuntimePlusToolkit/CMakeLists.txt b/Tests/CudaOnly/SharedRuntimePlusToolkit/CMakeLists.txt
index 03fba22..0b01085 100644
--- a/Tests/CudaOnly/SharedRuntimePlusToolkit/CMakeLists.txt
+++ b/Tests/CudaOnly/SharedRuntimePlusToolkit/CMakeLists.txt
@@ -16,16 +16,18 @@
 set_target_properties(SharedToolkit PROPERTIES CUDA_RUNTIME_LIBRARY none)
 target_link_libraries(SharedToolkit PUBLIC CUDA::cudart)
 
-# The CUDA only ships the shared version of the toolkit libraries
-# on windows
-if(NOT WIN32)
+# Verify the CUDA Toolkit has static libraries
+if(TARGET CUDA::curand_static AND
+   TARGET CUDA::nppif_static)
   #shared runtime with static toolkit libraries
   add_library(StaticToolkit SHARED static.cu)
+  target_compile_definitions(StaticToolkit INTERFACE "HAS_STATIC_VERSION")
   target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
   set_target_properties(StaticToolkit PROPERTIES CUDA_RUNTIME_LIBRARY Shared)
 
-  #static runtime with mixed toolkit libraries
+  #shared runtime with mixed toolkit libraries
   add_library(MixedToolkit SHARED mixed.cu)
+  target_compile_definitions(MixedToolkit INTERFACE "HAS_MIXED_VERSION")
   target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand_static CUDA::nppif)
   set_target_properties(MixedToolkit PROPERTIES CUDA_RUNTIME_LIBRARY Shared)
 endif()
diff --git a/Tests/CudaOnly/SharedRuntimePlusToolkit/main.cu b/Tests/CudaOnly/SharedRuntimePlusToolkit/main.cu
index 2a4da22..d958c3a 100644
--- a/Tests/CudaOnly/SharedRuntimePlusToolkit/main.cu
+++ b/Tests/CudaOnly/SharedRuntimePlusToolkit/main.cu
@@ -1,19 +1,28 @@
 
 #ifdef _WIN32
 #  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
 IMPORT int shared_version();
+
+#ifdef HAS_STATIC_VERSION
+IMPORT int static_version();
+#else
 int static_version()
 {
   return 0;
 }
+#endif
+
+#ifdef HAS_MIXED_VERSION
+IMPORT int mixed_version();
+#else
 int mixed_version()
 {
   return 0;
 }
-#else
-int shared_version();
-int static_version();
-int mixed_version();
 #endif
 
 int main()
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/CMakeLists.txt b/Tests/CudaOnly/StaticRuntimePlusToolkit/CMakeLists.txt
index 534bab2..ae03b66 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/CMakeLists.txt
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/CMakeLists.txt
@@ -16,17 +16,25 @@
 set_target_properties(SharedToolkit PROPERTIES CUDA_RUNTIME_LIBRARY none)
 target_link_libraries(SharedToolkit PUBLIC CUDA::cudart_static)
 
-#static runtime with static toolkit libraries
-add_library(StaticToolkit SHARED static.cu)
-target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
+# Verify the CUDA Toolkit has static libraries
+if(TARGET CUDA::curand_static AND
+   TARGET CUDA::nppif_static)
+  #static runtime with static toolkit libraries
+  add_library(StaticToolkit SHARED static.cu)
+  target_compile_definitions(StaticToolkit INTERFACE "HAS_STATIC_VERSION")
+  target_link_libraries(StaticToolkit PRIVATE Common CUDA::curand_static CUDA::nppif_static)
 
-#static runtime with mixed toolkit libraries
-add_library(MixedToolkit SHARED mixed.cu)
-target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand CUDA::nppif_static)
-set_target_properties(MixedToolkit PROPERTIES CUDA_RUNTIME_LIBRARY Static)
+  #static runtime with mixed toolkit libraries
+  add_library(MixedToolkit SHARED mixed.cu)
+  target_compile_definitions(MixedToolkit INTERFACE "HAS_MIXED_VERSION")
+  target_link_libraries(MixedToolkit PRIVATE Common CUDA::curand CUDA::nppif_static)
+  set_target_properties(MixedToolkit PROPERTIES CUDA_RUNTIME_LIBRARY Static)
+endif()
 
 add_executable(CudaOnlyStaticRuntimePlusToolkit main.cu)
-target_link_libraries(CudaOnlyStaticRuntimePlusToolkit PRIVATE SharedToolkit StaticToolkit MixedToolkit)
+target_link_libraries(CudaOnlyStaticRuntimePlusToolkit PRIVATE SharedToolkit
+                      $<TARGET_NAME_IF_EXISTS:StaticToolkit>
+                      $<TARGET_NAME_IF_EXISTS:MixedToolkit>)
 
 if(UNIX)
   # Help the shared cuda runtime find libcurand and libnppif when they are not located
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/curand.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/curand.cu
index 95872f0..fdd7b53 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/curand.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/curand.cu
@@ -1,6 +1,12 @@
 // Comes from:
 // https://docs.nvidia.com/cuda/curand/host-api-overview.html#host-api-example
 
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
 /*
  * This program uses the host CURAND API to generate 100
  * pseudorandom floats.
@@ -25,7 +31,7 @@
     }                                                                         \
   } while (0)
 
-int curand_main()
+EXPORT int curand_main()
 {
   size_t n = 100;
   size_t i;
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/main.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/main.cu
index 5a09f8e..d958c3a 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/main.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/main.cu
@@ -1,8 +1,29 @@
 
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
 
-int shared_version();
-int static_version();
-int mixed_version();
+IMPORT int shared_version();
+
+#ifdef HAS_STATIC_VERSION
+IMPORT int static_version();
+#else
+int static_version()
+{
+  return 0;
+}
+#endif
+
+#ifdef HAS_MIXED_VERSION
+IMPORT int mixed_version();
+#else
+int mixed_version()
+{
+  return 0;
+}
+#endif
 
 int main()
 {
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/mixed.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/mixed.cu
index a05140d..6de6886 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/mixed.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/mixed.cu
@@ -1,8 +1,16 @@
 
-int curand_main();
-int nppif_main();
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
 
-int mixed_version()
+IMPORT int curand_main();
+IMPORT int nppif_main();
+
+EXPORT int mixed_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
index 2871090..ac5341c 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
@@ -1,6 +1,12 @@
 // Comes from
 // https://devtalk.nvidia.com/default/topic/1037482/gpu-accelerated-libraries/help-me-help-you-with-modern-cmake-and-cuda-mwe-for-npp/post/5271066/#5271066
 
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
 #include <cstdio>
 #include <iostream>
 
@@ -8,7 +14,7 @@
 #include <cuda_runtime_api.h>
 #include <nppi_filtering_functions.h>
 
-int nppif_main()
+EXPORT int nppif_main()
 {
   /**
    * 8-bit unsigned single-channel 1D row convolution.
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/shared.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/shared.cu
index 9967b66..f3c3dbc 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/shared.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/shared.cu
@@ -1,8 +1,16 @@
 
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
+
 int curand_main();
 int nppif_main();
 
-int shared_version()
+EXPORT int shared_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/static.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/static.cu
index ca7eb4c..6932fa3 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/static.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/static.cu
@@ -1,8 +1,16 @@
 
-int curand_main();
-int nppif_main();
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define IMPORT
+#  define EXPORT
+#endif
 
-int static_version()
+IMPORT int curand_main();
+IMPORT int nppif_main();
+
+EXPORT int static_version()
 {
   return curand_main() == 0 && nppif_main() == 0;
 }
diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt
index d4cf36b..b6942c9 100644
--- a/Tests/FindPython/CMakeLists.txt
+++ b/Tests/FindPython/CMakeLists.txt
@@ -377,6 +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_SABIModule=${CMake_TEST_FindPython_SABIModule}"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
@@ -478,6 +479,35 @@
   endif()
 endif()
 
+if(CMake_TEST_FindPython_SABIModule)
+  add_test(NAME FindPython.Python2.Development.SABIModule COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python2SABIModule"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2SABIModule"
+    ${build_generator_args}
+    --build-project TestPython2SABIModule
+    --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")
+
+  # 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.
+  add_test(NAME FindPython.Python3.Development.SABIModule COMMAND
+    ${CMAKE_CTEST_COMMAND} -C Release
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python3SABIModule"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python3SABIModule"
+    ${build_generator_args}
+    --build-project TestPython3SABIModule
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C Release
+    )
+endif()
+
 if(CMake_TEST_FindPython_NumPy)
   add_test(NAME FindPython.NumPy COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
diff --git a/Tests/FindPython/Python2SABIModule/CMakeLists.txt b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
new file mode 100644
index 0000000..4f01197
--- /dev/null
+++ b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(TestPython2SABIModule LANGUAGES C)
+
+find_package(Python2 REQUIRED COMPONENTS Interpreter Development.Module Development.SABIModule)
diff --git a/Tests/FindPython/Python3Module/CMakeLists.txt b/Tests/FindPython/Python3Module/CMakeLists.txt
index 5945962..ccc1fdb 100644
--- a/Tests/FindPython/Python3Module/CMakeLists.txt
+++ b/Tests/FindPython/Python3Module/CMakeLists.txt
@@ -11,6 +11,9 @@
 if (Python3_Development_FOUND)
   message (FATAL_ERROR "Python 3, COMPONENT 'Development' unexpectedly found")
 endif()
+if (Python3_Development.SABIModule_FOUND)
+  message (FATAL_ERROR "Python 3, COMPONENT 'Development.SABIModule' unexpectedly found")
+endif()
 if (Python3_Development.Embed_FOUND)
   message (FATAL_ERROR "Python 3, COMPONENT 'Development.Embed' unexpectedly found")
 endif()
@@ -25,6 +28,12 @@
 if(TARGET Python3::Python)
   message(SEND_ERROR "Python3::Python unexpectedly found")
 endif()
+if(TARGET Python3::SABIMOdule)
+  message(SEND_ERROR "Python3::SABIModule unexpectedly found")
+endif()
+if(TARGET Python3::Embed)
+  message(SEND_ERROR "Python3::Embed unexpectedly found")
+endif()
 if(NOT TARGET Python3::Module)
   message(SEND_ERROR "Python3::Module not found")
 endif()
diff --git a/Tests/FindPython/Python3SABIModule/CMakeLists.txt b/Tests/FindPython/Python3SABIModule/CMakeLists.txt
new file mode 100644
index 0000000..2a067d0
--- /dev/null
+++ b/Tests/FindPython/Python3SABIModule/CMakeLists.txt
@@ -0,0 +1,51 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(TestPython3SABIModule LANGUAGES C)
+
+include(CTest)
+
+find_package(Python3 REQUIRED COMPONENTS Interpreter Development.SABIModule)
+if (NOT Python3_FOUND)
+  message (FATAL_ERROR "Failed to find Python 3")
+endif()
+if (Python3_Development_FOUND)
+  message (FATAL_ERROR "Python 3, COMPONENT 'Development' unexpectedly found")
+endif()
+if (Python3_Development.Embed_FOUND)
+  message (FATAL_ERROR "Python 3, COMPONENT 'Development.Embed' unexpectedly found")
+endif()
+if (Python3_Development.Module_FOUND)
+  message (FATAL_ERROR "Python 3, COMPONENT 'Development.Module' unexpectedly found")
+endif()
+if (NOT Python3_Development.SABIModule_FOUND)
+  message (FATAL_ERROR "Python 3, COMPONENT 'Development.SABIModule' not found")
+endif()
+
+if(NOT TARGET Python3::Interpreter)
+  message(SEND_ERROR "Python3::Interpreter not found")
+endif()
+
+if(TARGET Python3::Python)
+  message(SEND_ERROR "Python3::Python unexpectedly found")
+endif()
+if(TARGET Python3::Module)
+  message(SEND_ERROR "Python3::Module unexpectedly found")
+endif()
+if(NOT TARGET Python3::SABIModule)
+  message(SEND_ERROR "Python3::SABIModule not found")
+endif()
+
+Python3_add_library (spam3 MODULE USE_SABI 3 WITH_SOABI ../spam.c)
+target_compile_definitions (spam3 PRIVATE PYTHON3)
+
+if (Python3_SOSABI)
+  get_property (suffix TARGET spam3 PROPERTY SUFFIX)
+  if (NOT suffix MATCHES "^.${Python3_SOSABI}")
+    message(FATAL_ERROR "Module suffix do not include Python3_SOSABI")
+  endif()
+endif()
+
+
+add_test (NAME python3_spam3
+          COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
+          "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
index ae50f32..42d282d 100644
--- a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -8,7 +8,12 @@
 if (NOT Python2_FOUND)
   message (FATAL_ERROR "Failed to find Python 2")
 endif()
-find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
+
+set(components Interpreter Development)
+if (CMake_TEST_FindPython_SABIModule AND WIN32)
+  list (APPEND components Development.SABIModule)
+endif()
+find_package(Python3 REQUIRED COMPONENTS ${components})
 if (NOT Python3_FOUND)
   message (FATAL_ERROR "Failed to find Python 3")
 endif()
@@ -108,3 +113,28 @@
                   "-DPython3_INCLUDE_DIR=${Python2_INCLUDE_DIRS}"
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
+
+if (CMake_TEST_FindPython_SABIModule AND WIN32)
+  add_test(NAME FindPython.RequiredArtifacts.SABILibrary.VALID COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+    "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/SABILibrary.VALID"
+    ${build_generator_args}
+    --build-project TestRequiredArtifacts.Check
+    --build-options -DPYTHON_IS_FOUND=TRUE -DCHECK_SABI_LIBRARY=ON
+    "-DPython3_SABI_LIBRARY=${Python3_SABI_LIBRARY_RELEASE}"
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+  add_test(NAME FindPython.RequiredArtifacts.SABILibrary.INVALID COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+    "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/SABILibrary.INVALID"
+    ${build_generator_args}
+    --build-project TestRequiredArtifacts.Check
+    --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_SABI_LIBRARY=ON
+    "-DPython3_SABI_LIBRARY=${Python2_LIBRARY_RELEASE}"
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+endif()
diff --git a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
index 287cfdb..bb4f67c 100644
--- a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
@@ -16,6 +16,10 @@
     set (required_include "${Python3_INCLUDE_DIR}")
   endif()
 endif()
+if (CHECK_SABI_LIBRARY)
+  list (APPEND components Development.SABIModule)
+  set (required_sabi_library "${Python3_SABI_LIBRARY}")
+endif()
 
 find_package (Python3 COMPONENTS ${components})
 
@@ -39,3 +43,7 @@
 if (CHECK_INCLUDE AND NOT Python3_INCLUDE_DIRS STREQUAL required_include)
   message (FATAL_ERROR "Failed to use input variable Python3_INCLUDE_DIR")
 endif()
+
+if (CHECK_SABI_LIBRARY AND NOT Python3_SABI_LIBRARY_RELEASE STREQUAL required_sabi_library)
+  message (FATAL_ERROR "Failed to use input variable Python3_SABI_LIBRARY")
+endif()
diff --git a/Tests/FortranModules/CMakeLists.txt b/Tests/FortranModules/CMakeLists.txt
index 94f5939..5c76132 100644
--- a/Tests/FortranModules/CMakeLists.txt
+++ b/Tests/FortranModules/CMakeLists.txt
@@ -42,6 +42,16 @@
   test_module_implementation.f90
   test_module_interface.f90)
 
+add_executable(test_multi_module
+  # Place this first so that we do not get "lucky" and find the module provided
+  # by compiling `test_multi_module.f90` first.
+  test_multi_module_main.f90
+  test_multi_module.f90)
+set_property(TARGET test_multi_module PROPERTY
+  JOB_POOL_COMPILE multi_module_serial)
+set_property(GLOBAL APPEND PROPERTY
+  JOB_POOLS multi_module_serial=1)
+
 add_executable(test_use_in_comment_fixedform
   test_use_in_comment_fixedform.f)
 set_property(SOURCE test_use_in_comment_fixedform.f PROPERTY Fortran_FORMAT FIXED)
diff --git a/Tests/FortranModules/test_multi_module.f90 b/Tests/FortranModules/test_multi_module.f90
new file mode 100644
index 0000000..5b28f74
--- /dev/null
+++ b/Tests/FortranModules/test_multi_module.f90
@@ -0,0 +1,8 @@
+module first
+  interface inner
+  end interface inner
+end module first
+
+module second
+  REAL :: C = 1
+end module second
diff --git a/Tests/FortranModules/test_multi_module_main.f90 b/Tests/FortranModules/test_multi_module_main.f90
new file mode 100644
index 0000000..afd6dff
--- /dev/null
+++ b/Tests/FortranModules/test_multi_module_main.f90
@@ -0,0 +1,4 @@
+PROGRAM MAINF90
+  use second
+  PRINT *,'Constant is',C
+END PROGRAM MAINF90
diff --git a/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake b/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake
index f726759..d703839 100644
--- a/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_compile_options/RunCMakeTest.cmake
@@ -1,6 +1,7 @@
 include(RunCMake)
 
 run_cmake(empty_keyword_args)
+run_cmake(bad_keyword)
 
 if (CMAKE_C_COMPILER_ID MATCHES "GNU|LCC|Clang")
   macro(run_cmake_target test subtest target)
diff --git a/Tests/RunCMake/target_compile_options/bad_keyword-result.txt b/Tests/RunCMake/target_compile_options/bad_keyword-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/target_compile_options/bad_keyword-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_compile_options/bad_keyword-stderr.txt b/Tests/RunCMake/target_compile_options/bad_keyword-stderr.txt
new file mode 100644
index 0000000..e22013e
--- /dev/null
+++ b/Tests/RunCMake/target_compile_options/bad_keyword-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at bad_keyword\.cmake:[0-9]+ \(target_compile_options\):
+  target_compile_options called with invalid arguments
diff --git a/Tests/RunCMake/target_compile_options/bad_keyword.cmake b/Tests/RunCMake/target_compile_options/bad_keyword.cmake
new file mode 100644
index 0000000..b7e6fca
--- /dev/null
+++ b/Tests/RunCMake/target_compile_options/bad_keyword.cmake
@@ -0,0 +1,5 @@
+add_library(iface INTERFACE)
+
+# SYSTEM is a recognized keyword for the base class used to implement the
+# command. Verify that we don't allow it.
+target_compile_options(iface SYSTEM PRIVATE)
diff --git a/Utilities/ClangTidyModule/CMakeLists.txt b/Utilities/ClangTidyModule/CMakeLists.txt
index 6fc54b1..97c176f 100644
--- a/Utilities/ClangTidyModule/CMakeLists.txt
+++ b/Utilities/ClangTidyModule/CMakeLists.txt
@@ -16,6 +16,8 @@
 
   OstringstreamUseCmstrcatCheck.cxx
   OstringstreamUseCmstrcatCheck.h
+  StringConcatenationUseCmstrcatCheck.cxx
+  StringConcatenationUseCmstrcatCheck.h
   UseBespokeEnumClassCheck.cxx
   UseBespokeEnumClassCheck.h
   UseCmstrlenCheck.cxx
diff --git a/Utilities/ClangTidyModule/Module.cxx b/Utilities/ClangTidyModule/Module.cxx
index c747cee..4dd7dcd 100644
--- a/Utilities/ClangTidyModule/Module.cxx
+++ b/Utilities/ClangTidyModule/Module.cxx
@@ -4,6 +4,7 @@
 #include <clang-tidy/ClangTidyModuleRegistry.h>
 
 #include "OstringstreamUseCmstrcatCheck.h"
+#include "StringConcatenationUseCmstrcatCheck.h"
 #include "UseBespokeEnumClassCheck.h"
 #include "UseCmstrlenCheck.h"
 #include "UseCmsysFstreamCheck.h"
@@ -25,6 +26,8 @@
     CheckFactories.registerCheck<OstringstreamUseCmstrcatCheck>(
       "cmake-ostringstream-use-cmstrcat");
     CheckFactories.registerCheck<UsePragmaOnceCheck>("cmake-use-pragma-once");
+    CheckFactories.registerCheck<StringConcatenationUseCmstrcatCheck>(
+      "cmake-string-concatenation-use-cmstrcat");
   }
 };
 
diff --git a/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx
new file mode 100644
index 0000000..df14c83
--- /dev/null
+++ b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx
@@ -0,0 +1,179 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "StringConcatenationUseCmstrcatCheck.h"
+
+#include <cassert>
+
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+#include <clang/Lex/Lexer.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+StringConcatenationUseCmstrcatCheck::StringConcatenationUseCmstrcatCheck(
+  StringRef Name, ClangTidyContext* Context)
+  : ClangTidyCheck(Name, Context)
+{
+}
+
+void StringConcatenationUseCmstrcatCheck::registerMatchers(MatchFinder* Finder)
+{
+  auto IsString = expr(hasType(qualType(hasUnqualifiedDesugaredType(
+    recordType(hasDeclaration(classTemplateSpecializationDecl(
+      hasName("::std::basic_string"),
+      hasTemplateArgument(
+        0, templateArgument(refersToType(asString("char")))))))))));
+
+  auto IsChar = expr(hasType(asString("char")));
+
+  auto IsCharPtr = expr(hasType(pointerType(pointee(asString("const char")))));
+
+  auto IsStringConcat =
+    cxxOperatorCallExpr(hasOperatorName("+"),
+                        anyOf(allOf(hasLHS(IsString), hasRHS(IsString)),
+                              allOf(hasLHS(IsString), hasRHS(IsChar)),
+                              allOf(hasLHS(IsString), hasRHS(IsCharPtr)),
+                              allOf(hasLHS(IsChar), hasRHS(IsString)),
+                              allOf(hasLHS(IsCharPtr), hasRHS(IsString))));
+
+  auto IsStringAppend = cxxOperatorCallExpr(
+    hasOperatorName("+="), hasLHS(IsString),
+    anyOf(hasRHS(IsString), hasRHS(IsChar), hasRHS(IsCharPtr)));
+
+  auto IsStringConcatWithLHS =
+    cxxOperatorCallExpr(
+      IsStringConcat,
+      optionally(hasLHS(materializeTemporaryExpr(
+        has(cxxBindTemporaryExpr(has(IsStringConcat.bind("lhs"))))))))
+      .bind("concat");
+
+  auto IsStringAppendWithRHS =
+    cxxOperatorCallExpr(
+      IsStringAppend,
+      optionally(hasRHS(materializeTemporaryExpr(has(implicitCastExpr(
+        has(cxxBindTemporaryExpr(has(IsStringConcat.bind("rhs"))))))))))
+      .bind("append");
+
+  Finder->addMatcher(IsStringConcatWithLHS, this);
+  Finder->addMatcher(IsStringAppendWithRHS, this);
+}
+
+void StringConcatenationUseCmstrcatCheck::check(
+  const MatchFinder::MatchResult& Result)
+{
+  const CXXOperatorCallExpr* AppendNode =
+    Result.Nodes.getNodeAs<CXXOperatorCallExpr>("append");
+  const CXXOperatorCallExpr* ConcatNode =
+    Result.Nodes.getNodeAs<CXXOperatorCallExpr>("concat");
+
+  if (AppendNode != nullptr) {
+    if (AppendNode->getBeginLoc().isValid()) {
+      assert(InProgressExprChains.find(AppendNode) ==
+             InProgressExprChains.end());
+
+      ExprChain TmpExprChain =
+        std::make_pair(OperatorType::PlusEquals,
+                       std::vector<const CXXOperatorCallExpr*>{ AppendNode });
+      const CXXOperatorCallExpr* RHSNode =
+        Result.Nodes.getNodeAs<CXXOperatorCallExpr>("rhs");
+
+      if (RHSNode != nullptr) {
+        if (RHSNode->getBeginLoc().isValid()) {
+          InProgressExprChains[RHSNode] = std::move(TmpExprChain);
+        }
+      } else {
+        issueCorrection(TmpExprChain, Result);
+      }
+    }
+  }
+
+  if (ConcatNode != nullptr) {
+    if (ConcatNode->getBeginLoc().isValid()) {
+      ExprChain TmpExprChain;
+
+      if (!(InProgressExprChains.find(ConcatNode) ==
+            InProgressExprChains.end())) {
+        TmpExprChain = std::move(InProgressExprChains[ConcatNode]);
+        InProgressExprChains.erase(ConcatNode);
+        if (TmpExprChain.first == OperatorType::PlusEquals) {
+          TmpExprChain.second.insert(TmpExprChain.second.begin() + 1,
+                                     ConcatNode);
+        } else {
+          TmpExprChain.second.insert(TmpExprChain.second.begin(), ConcatNode);
+        }
+      } else {
+        TmpExprChain = std::make_pair(
+          OperatorType::Plus,
+          std::vector<const CXXOperatorCallExpr*>{ ConcatNode });
+      }
+
+      const CXXOperatorCallExpr* LHSNode =
+        Result.Nodes.getNodeAs<CXXOperatorCallExpr>("lhs");
+
+      if (LHSNode != nullptr) {
+        if (LHSNode->getBeginLoc().isValid()) {
+          InProgressExprChains[LHSNode] = std::move(TmpExprChain);
+        }
+      } else {
+        issueCorrection(TmpExprChain, Result);
+      }
+    }
+  }
+}
+
+void StringConcatenationUseCmstrcatCheck::issueCorrection(
+  const ExprChain& Chain, const MatchFinder::MatchResult& Result)
+{
+  std::vector<FixItHint> FixIts;
+  const CXXOperatorCallExpr* ExprNode;
+  std::vector<const clang::CXXOperatorCallExpr*>::const_iterator It =
+    Chain.second.begin();
+
+  if (Chain.first == OperatorType::PlusEquals) {
+    ExprNode = *It;
+    StringRef LHS = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(ExprNode->getArg(0)->getSourceRange()),
+      Result.Context->getSourceManager(), Result.Context->getLangOpts());
+
+    FixIts.push_back(FixItHint::CreateReplacement(
+      ExprNode->getExprLoc(), "= cmStrCat(" + LHS.str() + ","));
+    It++;
+  } else {
+    ExprNode = *It;
+    FixIts.push_back(
+      FixItHint::CreateInsertion(ExprNode->getBeginLoc(), "cmStrCat("));
+  }
+
+  while (It != std::end(Chain.second)) {
+    ExprNode = *It;
+    FixIts.push_back(
+      FixItHint::CreateReplacement(ExprNode->getOperatorLoc(), ","));
+    It++;
+  }
+  It--;
+  ExprNode = *It;
+
+  StringRef LastToken = Lexer::getSourceText(
+    CharSourceRange::getTokenRange(ExprNode->getArg(1)->getSourceRange()),
+    Result.Context->getSourceManager(), Result.Context->getLangOpts());
+  FixIts.push_back(FixItHint::CreateInsertion(
+    ExprNode->getEndLoc().getLocWithOffset(LastToken.str().size()), ")"));
+
+  It = Chain.second.begin();
+  ExprNode = *It;
+
+  if (Chain.first == OperatorType::PlusEquals) {
+    this->diag(ExprNode->getOperatorLoc(),
+               "use cmStrCat() instead of string append")
+      << FixIts;
+  } else {
+    this->diag(ExprNode->getBeginLoc(),
+               "use cmStrCat() instead of string concatenation")
+      << FixIts;
+  }
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h
new file mode 100644
index 0000000..43ff539
--- /dev/null
+++ b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h
@@ -0,0 +1,34 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class StringConcatenationUseCmstrcatCheck : public ClangTidyCheck
+{
+public:
+  StringConcatenationUseCmstrcatCheck(StringRef Name,
+                                      ClangTidyContext* Context);
+  void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+
+private:
+  enum class OperatorType
+  {
+    Plus,
+    PlusEquals
+  };
+  typedef std::pair<OperatorType, std::vector<const CXXOperatorCallExpr*>>
+    ExprChain;
+  std::map<const CXXOperatorCallExpr*, ExprChain> InProgressExprChains;
+
+  void issueCorrection(const ExprChain& ExprChain,
+                       const ast_matchers::MatchFinder::MatchResult& Result);
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/Tests/CMakeLists.txt b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
index b53d5d2..8220f39 100644
--- a/Utilities/ClangTidyModule/Tests/CMakeLists.txt
+++ b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
@@ -15,3 +15,4 @@
 add_run_clang_tidy_test(cmake-use-bespoke-enum-class)
 add_run_clang_tidy_test(cmake-ostringstream-use-cmstrcat)
 add_run_clang_tidy_test(cmake-use-pragma-once)
+add_run_clang_tidy_test(cmake-string-concatenation-use-cmstrcat)
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx
new file mode 100644
index 0000000..79aecd4
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx
@@ -0,0 +1,36 @@
+#include <string>
+
+template <typename... Args>
+std::string cmStrCat(Args&&... args)
+{
+  return "";
+}
+
+std::string a = "This is a string variable";
+std::string b = " and this is a string variable";
+std::string concat;
+
+// Correction needed
+void test1()
+{
+  concat = cmStrCat(a, b);
+  concat = cmStrCat(a, " and this is a string literal");
+  concat = cmStrCat(a, 'O');
+  concat = cmStrCat("This is a string literal", b);
+  concat = cmStrCat('O', a);
+  concat = cmStrCat(a, " and this is a string literal", 'O', b);
+
+  concat = cmStrCat(concat, b);
+  concat = cmStrCat(concat, " and this is a string literal");
+  concat = cmStrCat(concat, 'o');
+  concat = cmStrCat(concat, b, " and this is a string literal ", 'o', b);
+}
+
+// No correction needed
+void test2()
+{
+  a = b;
+  a = "This is a string literal";
+  a = 'X';
+  cmStrCat(a, b);
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt
new file mode 100644
index 0000000..3cfdef8
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt
@@ -0,0 +1,113 @@
+cmake-string-concatenation-use-cmstrcat.cxx:16:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = a + b;
+           ^ ~
+           cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:16:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:16:14: note: FIX-IT applied suggested code changes
+  concat = a + b;
+             ^
+cmake-string-concatenation-use-cmstrcat.cxx:16:17: note: FIX-IT applied suggested code changes
+  concat = a + b;
+                ^
+cmake-string-concatenation-use-cmstrcat.cxx:17:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = a + " and this is a string literal";
+           ^ ~
+           cmStrCat( ,                        )
+cmake-string-concatenation-use-cmstrcat.cxx:17:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:17:14: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal";
+             ^
+cmake-string-concatenation-use-cmstrcat.cxx:17:47: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal";
+                                              ^
+cmake-string-concatenation-use-cmstrcat.cxx:18:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = a + 'O';
+           ^ ~
+           cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:18:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:18:14: note: FIX-IT applied suggested code changes
+  concat = a + 'O';
+             ^
+cmake-string-concatenation-use-cmstrcat.cxx:18:19: note: FIX-IT applied suggested code changes
+  concat = a + 'O';
+                  ^
+cmake-string-concatenation-use-cmstrcat.cxx:19:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = "This is a string literal" + b;
+           ^                          ~
+           cmStrCat(                  ,  )
+cmake-string-concatenation-use-cmstrcat.cxx:19:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:19:39: note: FIX-IT applied suggested code changes
+  concat = "This is a string literal" + b;
+                                      ^
+cmake-string-concatenation-use-cmstrcat.cxx:19:42: note: FIX-IT applied suggested code changes
+  concat = "This is a string literal" + b;
+                                         ^
+cmake-string-concatenation-use-cmstrcat.cxx:20:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = 'O' + a;
+           ^   ~
+           cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:20:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:20:16: note: FIX-IT applied suggested code changes
+  concat = 'O' + a;
+               ^
+cmake-string-concatenation-use-cmstrcat.cxx:20:19: note: FIX-IT applied suggested code changes
+  concat = 'O' + a;
+                  ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+  concat = a + " and this is a string literal" + 'O' + b;
+           ^ ~                                 ~     ~
+           cmStrCat( ,                         ,     ,  )
+cmake-string-concatenation-use-cmstrcat.cxx:21:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:21:14: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal" + 'O' + b;
+             ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:48: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal" + 'O' + b;
+                                               ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:54: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal" + 'O' + b;
+                                                     ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:57: note: FIX-IT applied suggested code changes
+  concat = a + " and this is a string literal" + 'O' + b;
+                                                        ^
+cmake-string-concatenation-use-cmstrcat.cxx:23:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+  concat += b;
+         ^~
+         = cmStrCat(concat, )
+cmake-string-concatenation-use-cmstrcat.cxx:23:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:23:14: note: FIX-IT applied suggested code changes
+  concat += b;
+             ^
+cmake-string-concatenation-use-cmstrcat.cxx:24:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+  concat += " and this is a string literal";
+         ^~
+         = cmStrCat(concat,                )
+cmake-string-concatenation-use-cmstrcat.cxx:24:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:24:44: note: FIX-IT applied suggested code changes
+  concat += " and this is a string literal";
+                                           ^
+cmake-string-concatenation-use-cmstrcat.cxx:25:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+  concat += 'o';
+         ^~
+         = cmStrCat(concat, )
+cmake-string-concatenation-use-cmstrcat.cxx:25:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:25:16: note: FIX-IT applied suggested code changes
+  concat += 'o';
+               ^
+cmake-string-concatenation-use-cmstrcat.cxx:26:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+  concat += b + " and this is a string literal " + 'o' + b;
+         ^~   ~                                  ~     ~
+         = cmStrCat(concat, ,                    ,     ,  )
+cmake-string-concatenation-use-cmstrcat.cxx:26:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:26:15: note: FIX-IT applied suggested code changes
+  concat += b + " and this is a string literal " + 'o' + b;
+              ^
+cmake-string-concatenation-use-cmstrcat.cxx:26:50: note: FIX-IT applied suggested code changes
+  concat += b + " and this is a string literal " + 'o' + b;
+                                                 ^
+cmake-string-concatenation-use-cmstrcat.cxx:26:56: note: FIX-IT applied suggested code changes
+  concat += b + " and this is a string literal " + 'o' + b;
+                                                       ^
+cmake-string-concatenation-use-cmstrcat.cxx:26:59: note: FIX-IT applied suggested code changes
+  concat += b + " and this is a string literal " + 'o' + b;
+                                                          ^
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx
new file mode 100644
index 0000000..13a20ac
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx
@@ -0,0 +1,36 @@
+#include <string>
+
+template <typename... Args>
+std::string cmStrCat(Args&&... args)
+{
+  return "";
+}
+
+std::string a = "This is a string variable";
+std::string b = " and this is a string variable";
+std::string concat;
+
+// Correction needed
+void test1()
+{
+  concat = a + b;
+  concat = a + " and this is a string literal";
+  concat = a + 'O';
+  concat = "This is a string literal" + b;
+  concat = 'O' + a;
+  concat = a + " and this is a string literal" + 'O' + b;
+
+  concat += b;
+  concat += " and this is a string literal";
+  concat += 'o';
+  concat += b + " and this is a string literal " + 'o' + b;
+}
+
+// No correction needed
+void test2()
+{
+  a = b;
+  a = "This is a string literal";
+  a = 'X';
+  cmStrCat(a, b);
+}
diff --git a/Utilities/Release/win/sign-package.ps1 b/Utilities/Release/win/sign-package.ps1
index 0dbefd2..fdaff14 100755
--- a/Utilities/Release/win/sign-package.ps1
+++ b/Utilities/Release/win/sign-package.ps1
@@ -16,7 +16,7 @@
 $ErrorActionPreference = 'Stop'
 
 # Sign binaries with SHA-1 for Windows 7 and below.
-& $signtool sign -v -a -t http://timestamp.digicert.com bin\*.exe
+& $signtool sign -v -a -t http://timestamp.digicert.com -fd sha1 bin\*.exe
 
 # Sign binaries with SHA-256 for Windows 8 and above.
 & $signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -as bin\*.exe