Merge branch 'release-3.25'
diff --git a/.clang-tidy b/.clang-tidy
index a86f39a..1e9b78a 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -33,6 +33,7 @@
 -readability-redundant-member-init,\
 -readability-suspicious-call-argument,\
 -readability-uppercase-literal-suffix,\
+cmake-*,\
 "
 HeaderFilterRegex: 'Source/cm[^/]*\.(h|hxx|cxx)$'
 CheckOptions:
diff --git a/.gitlab/ci/configure_fedora36_tidy.cmake b/.gitlab/ci/configure_fedora36_tidy.cmake
index 38414d3..2d0eeeb 100644
--- a/.gitlab/ci/configure_fedora36_tidy.cmake
+++ b/.gitlab/ci/configure_fedora36_tidy.cmake
@@ -1,3 +1,5 @@
 set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "")
+set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "")
+set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora36_common.cmake")
diff --git a/.gitlab/ci/env_fedora36_tidy.sh b/.gitlab/ci/env_fedora36_tidy.sh
new file mode 100644
index 0000000..d5b1989
--- /dev/null
+++ b/.gitlab/ci/env_fedora36_tidy.sh
@@ -0,0 +1,6 @@
+cmake \
+  -S Utilities/ClangTidyModule \
+  -B Utilities/ClangTidyModule/build \
+  -DRUN_TESTS=ON
+cmake --build Utilities/ClangTidyModule/build
+ctest --test-dir Utilities/ClangTidyModule/build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b9eb2d..73c2c2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,14 +1,10 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
 
-if(POLICY CMP0129)
-  cmake_policy(SET CMP0129 NEW) # CMake 3.23
-endif()
-
 project(CMake)
 unset(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX)
 unset(CMAKE_USER_MAKE_RULES_OVERRIDE_C)
@@ -277,6 +273,16 @@
   endif()
   set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
 
+  option(CMake_USE_CLANG_TIDY_MODULE "Use CMake's clang-tidy module." OFF)
+  if(CMake_USE_CLANG_TIDY_MODULE)
+    find_library(CMake_CLANG_TIDY_MODULE NAMES cmake-clang-tidy-module DOC "Location of the clang-tidy module")
+    if(NOT CMake_CLANG_TIDY_MODULE)
+      message(FATAL_ERROR "CMake_USE_CLANG_TIDY_MODULE is ON but cmake-clang-tidy-module is not found!")
+    endif()
+    list(APPEND CMAKE_CXX_CLANG_TIDY "--load=${CMake_CLANG_TIDY_MODULE}")
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMake_CLANG_TIDY_MODULE}")
+  endif()
+
   # Create a preprocessor definition that depends on .clang-tidy content so
   # the compile command will change when .clang-tidy changes.  This ensures
   # that a subsequent build re-runs clang-tidy on all sources even if they
@@ -286,6 +292,11 @@
   file(SHA1 ${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy clang_tidy_sha1)
   set(CLANG_TIDY_DEFINITIONS "CLANG_TIDY_SHA1=${clang_tidy_sha1}")
   unset(clang_tidy_sha1)
+  if(CMake_USE_CLANG_TIDY_MODULE)
+    file(SHA1 "${CMake_CLANG_TIDY_MODULE}" clang_tidy_module_sha1)
+    list(APPEND CLANG_TIDY_DEFINITIONS "CLANG_TIDY_MODULE_SHA1=${clang_tidy_module_sha1}")
+    unset(clang_tidy_module_sha1)
+  endif()
 
 endif()
 configure_file(.clang-tidy .clang-tidy COPYONLY)
diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000..e4cc01e
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+  Developers should add similar notes for each topic branch
+  making a noteworthy change.  Each document should be named
+  and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/UseSWIG-perl5.rst b/Help/release/dev/UseSWIG-perl5.rst
new file mode 100644
index 0000000..67d4161
--- /dev/null
+++ b/Help/release/dev/UseSWIG-perl5.rst
@@ -0,0 +1,4 @@
+UseSWIG-perl5
+-------------
+
+* The :module:`UseSWIG` module gained the support of ``perl5`` language.
diff --git a/Help/release/dev/trace-try_compile.rst b/Help/release/dev/trace-try_compile.rst
new file mode 100644
index 0000000..886aaad
--- /dev/null
+++ b/Help/release/dev/trace-try_compile.rst
@@ -0,0 +1,5 @@
+trace-try_compile
+-----------------
+
+* The :option:`cmake --trace` option now follows :command:`try_compile` and
+  :command:`try_run` invocations.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index b6ecf7b..50e06bb 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,6 +7,8 @@
   This file should include the adjacent "dev.txt" file
   in development versions but not in release versions.
 
+.. include:: dev.txt
+
 Releases
 ========
 
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 16726d2..a0cd9da 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -65,10 +65,22 @@
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -libc MTd)
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -libc MDd)
 
-set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g")
-set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
-set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
-set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
+if(CMAKE_GENERATOR STREQUAL "Xcode")
+  # Xcode has a separate Xcode project option (SWIFT_COMPILATION_MODE) used to set
+  # whether compiling with whole-module optimizations or incrementally. Setting
+  # these options here will have no effect when compiling with the built-in driver,
+  # and will explode violently, leaving build products in the source directory, when
+  # using the old swift driver.
+  set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g")
+  set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
+  set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
+  set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
+else()
+  set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g -incremental")
+  set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O -wmo")
+  set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g -wmo")
+  set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize -wmo")
+endif()
 
 if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
   if(NOT DEFINED CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG)
@@ -91,7 +103,7 @@
 endif()
 
 if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY)
-  set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -j ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
+  set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
 endif()
 
 if(NOT CMAKE_Swift_CREATE_SHARED_MODULE)
@@ -99,11 +111,11 @@
 endif()
 
 if(NOT CMAKE_Swift_LINK_EXECUTABLE)
-  set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -j ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
+  set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
 endif()
 
 if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY)
-  set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -j ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
+  set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
 
   set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>")
   set(CMAKE_Swift_ARCHIVE_FINISH "")
diff --git a/Modules/FindZLIB.cmake b/Modules/FindZLIB.cmake
index be5c775..cfe6715 100644
--- a/Modules/FindZLIB.cmake
+++ b/Modules/FindZLIB.cmake
@@ -60,6 +60,14 @@
 
 #]=======================================================================]
 
+if(ZLIB_FIND_COMPONENTS AND NOT ZLIB_FIND_QUIETLY)
+  message(AUTHOR_WARNING
+    "ZLIB does not provide any COMPONENTS.  Calling\n"
+    "  find_package(ZLIB COMPONENTS ...)\n"
+    "will always fail."
+    )
+endif()
+
 set(_ZLIB_SEARCHES)
 
 # Search ZLIB_ROOT first if it is set.
@@ -164,7 +172,8 @@
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZLIB REQUIRED_VARS ZLIB_LIBRARY ZLIB_INCLUDE_DIR
-                                       VERSION_VAR ZLIB_VERSION_STRING)
+                                       VERSION_VAR ZLIB_VERSION_STRING
+                                       HANDLE_COMPONENTS)
 
 if(ZLIB_FOUND)
     set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake
index fd6596b..e0e01f5 100644
--- a/Modules/UseSWIG.cmake
+++ b/Modules/UseSWIG.cmake
@@ -378,6 +378,7 @@
 set(SWIG_JAVA_EXTRA_FILE_EXTENSIONS ".java" "JNI.java")
 set(SWIG_CSHARP_EXTRA_FILE_EXTENSIONS ".cs" "PINVOKE.cs")
 set(SWIG_PERL_EXTRA_FILE_EXTENSIONS ".pm")
+set(SWIG_PERL5_EXTRA_FILE_EXTENSIONS ".pm")
 
 set(SWIG_MANAGE_SUPPORT_FILES_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/UseSWIG/ManageSupportFiles.cmake")
 
@@ -414,8 +415,8 @@
   endif()
   if(SWIG_MODULE_${name}_LANGUAGE STREQUAL "UNKNOWN")
     message(FATAL_ERROR "SWIG Error: Language \"${language}\" not found")
-  elseif(SWIG_MODULE_${name}_LANGUAGE STREQUAL "PERL" AND
-         NOT "-shadow" IN_LIST SWIG_MODULE_${name}_EXTRA_FLAGS)
+  elseif((SWIG_MODULE_${name}_LANGUAGE STREQUAL "PERL" OR SWIG_MODULE_${name}_LANGUAGE STREQUAL "PERL5")
+      AND NOT "-shadow" IN_LIST SWIG_MODULE_${name}_EXTRA_FLAGS)
     list(APPEND SWIG_MODULE_${name}_EXTRA_FLAGS "-shadow")
   endif()
 endmacro()
@@ -971,7 +972,7 @@
     if (APPLE)
       set_target_properties (${target_name} PROPERTIES SUFFIX ".bundle")
     endif ()
-  elseif (swig_lowercase_language STREQUAL "perl")
+  elseif (swig_lowercase_language STREQUAL "perl" OR swig_lowercase_language STREQUAL "perl5")
     # assume empty prefix because we expect the module to be dynamically loaded
     set_target_properties (${target_name} PROPERTIES PREFIX "")
     if (APPLE)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index c51f421..d4b0490 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,8 +1,8 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 25)
-set(CMake_VERSION_PATCH 0)
-set(CMake_VERSION_RC 2)
+set(CMake_VERSION_PATCH 20221021)
+#set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
 # Start with the full version number used in tags.  It has no dev info.
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 50bc78c..2c61163 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -939,13 +939,13 @@
 
   // Isolate the file policy level.
   // Support CMake versions as far back as 2.6 but also support using NEW
-  // policy settings for up to CMake 3.23 (this upper limit may be reviewed
+  // policy settings for up to CMake 3.24 (this upper limit may be reviewed
   // and increased from time to time). This reduces the opportunity for CMake
   // warnings when an older export file is later used with newer CMake
   // versions.
   /* clang-format off */
   os << "cmake_policy(PUSH)\n"
-     << "cmake_policy(VERSION 2.8.3...3.23)\n";
+     << "cmake_policy(VERSION 2.8.3...3.24)\n";
   /* clang-format on */
 }
 
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 3f8378b..60b0a7b 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -377,6 +377,8 @@
 #  pragma diag_suppress 1222 // invalid error number (3288, but works anyway)
 #  define CM_LCC_DIAG_SUPPRESS_3288
 #  pragma diag_suppress 3288 // parameter was declared but never referenced
+#  define CM_LCC_DIAG_SUPPRESS_3301
+#  pragma diag_suppress 3301 // parameter was declared but never referenced
 #endif
 
 void ResetGenerator()
@@ -421,6 +423,11 @@
   return false;
 }
 
+#ifdef CM_LCC_DIAG_SUPPRESS_3301
+#  undef CM_LCC_DIAG_SUPPRESS_3301
+#  pragma diag_default 3301
+#endif
+
 #ifdef CM_LCC_DIAG_SUPPRESS_3288
 #  undef CM_LCC_DIAG_SUPPRESS_3288
 #  pragma diag_default 3288
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 6195d1f..321122a 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -5532,7 +5532,7 @@
       } else if (cmHasLiteralPrefix(*location, "Resources/")) {
         flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource;
         if (stripResources) {
-          flags.MacFolder += strlen("Resources/");
+          flags.MacFolder += cmStrLen("Resources/");
         }
       } else {
         flags.Type = cmGeneratorTarget::SourceFileTypeMacContent;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index a1af268..bc703e5 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -4386,12 +4386,20 @@
     buildSettings->AddAttribute("CODE_SIGNING_ALLOWED",
                                 this->CreateString("NO"));
   }
+  auto debugConfigs = this->GetCMakeInstance()->GetDebugConfigs();
+  std::set<std::string> debugConfigSet(debugConfigs.begin(),
+                                       debugConfigs.end());
 
   for (auto& config : configs) {
     CreateGlobalXCConfigSettings(root, config.second, config.first);
 
     cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
 
+    if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) {
+      buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE",
+                                        this->CreateString("wholemodule"));
+    }
+
     // Put this last so it can override existing settings
     // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
     for (const auto& var : this->CurrentMakefile->GetDefinitions()) {
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 6e0d704..90a38be 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3584,6 +3584,9 @@
   gg->RecursionDepth = this->RecursionDepth;
   cm.SetGlobalGenerator(std::move(gg));
 
+  // copy trace state
+  cm.SetTraceRedirect(this->GetCMakeInstance());
+
   // do a configure
   cm.SetHomeDirectory(srcdir);
   cm.SetHomeOutputDirectory(bindir);
@@ -4470,12 +4473,12 @@
   }
 
   // Deprecate old policies.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0102 &&
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0108 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
           id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 ||
-          id == cmPolicies::CMP0091)) &&
+          id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104)) &&
       (!this->IsSet("CMAKE_WARN_DEPRECATED") ||
        this->IsOn("CMAKE_WARN_DEPRECATED"))) {
     this->IssueMessage(MessageType::DEPRECATION_WARNING,
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 8800792..c4c638c 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1537,6 +1537,16 @@
   }
 }
 
+void cmake::SetTraceRedirect(cmake* other)
+{
+  this->Trace = other->Trace;
+  this->TraceExpand = other->TraceExpand;
+  this->TraceFormatVar = other->TraceFormatVar;
+  this->TraceOnlyThisSources = other->TraceOnlyThisSources;
+
+  this->TraceRedirect = other;
+}
+
 bool cmake::SetDirectoriesFromFile(const std::string& arg)
 {
   // Check if the argument refers to a CMakeCache.txt or
diff --git a/Source/cmake.h b/Source/cmake.h
index 54d0bb5..5b5425c 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -513,10 +513,19 @@
   {
     return this->TraceOnlyThisSources;
   }
-  cmGeneratedFileStream& GetTraceFile() { return this->TraceFile; }
+  cmGeneratedFileStream& GetTraceFile()
+  {
+    if (this->TraceRedirect) {
+      return this->TraceRedirect->GetTraceFile();
+    }
+    return this->TraceFile;
+  }
   void SetTraceFile(std::string const& file);
   void PrintTraceFormatVersion();
 
+  //! Use trace from another ::cmake instance.
+  void SetTraceRedirect(cmake* other);
+
   bool GetWarnUninitialized() const { return this->WarnUninitialized; }
   void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; }
   bool GetWarnUnusedCli() const { return this->WarnUnusedCli; }
@@ -677,6 +686,7 @@
   bool TraceExpand = false;
   TraceFormat TraceFormatVar = TRACE_HUMAN;
   cmGeneratedFileStream TraceFile;
+  cmake* TraceRedirect = nullptr;
   bool WarnUninitialized = false;
   bool WarnUnusedCli = true;
   bool CheckSystemVars = false;
diff --git a/Tests/CheckSourceTree/check.cmake b/Tests/CheckSourceTree/check.cmake
index c2e3529..a59ffb5 100644
--- a/Tests/CheckSourceTree/check.cmake
+++ b/Tests/CheckSourceTree/check.cmake
@@ -3,6 +3,13 @@
   set(ENV{HOME} "$ENV{CTEST_REAL_HOME}")
 endif()
 
+file(GLOB known_files
+  "${CMake_SOURCE_DIR}/Tests/JavaExportImport/InstallExport/hs_err_pid*.log"
+  )
+if(known_files)
+  file(REMOVE ${known_files})
+endif()
+
 execute_process(
   COMMAND "${GIT_EXECUTABLE}" status
   WORKING_DIRECTORY "${CMake_SOURCE_DIR}"
diff --git a/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt b/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt
new file mode 100644
index 0000000..66d3016
--- /dev/null
+++ b/Tests/RunCMake/CMP0104/CMP0104-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0104-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0104 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt b/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt
new file mode 100644
index 0000000..ef48d5c
--- /dev/null
+++ b/Tests/RunCMake/CMP0106/CMP0106-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0106-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0106 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
index 3f17c1f..921fabd 100644
--- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -120,7 +120,9 @@
   if (RunCMake_CXXModules_INSTALL)
     run_cmake_command("examples/${test_name}-install" "${CMAKE_COMMAND}" --build . --target install --config Debug)
   endif ()
-  run_cmake_command("examples/${test_name}-test" "${CMAKE_CTEST_COMMAND}" -C Debug --output-on-failure)
+  if (NOT RunCMake_CXXModules_NO_TEST)
+    run_cmake_command("examples/${test_name}-test" "${CMAKE_CTEST_COMMAND}" -C Debug --output-on-failure)
+  endif ()
 endfunction ()
 
 string(REPLACE "," ";" CMake_TEST_MODULE_COMPILATION "${CMake_TEST_MODULE_COMPILATION}")
@@ -132,6 +134,10 @@
   run_cxx_module_test(generated)
   run_cxx_module_test(public-req-private)
   run_cxx_module_test(deep-chain)
+  run_cxx_module_test(duplicate)
+  set(RunCMake_CXXModules_NO_TEST 1)
+  run_cxx_module_test(circular)
+  unset(RunCMake_CXXModules_NO_TEST)
 endif ()
 
 # Tests which use named modules in shared libraries.
diff --git a/Tests/RunCMake/CXXModules/examples/circular-build-result.txt b/Tests/RunCMake/CXXModules/examples/circular-build-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular-build-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/examples/circular-build-stdout.txt b/Tests/RunCMake/CXXModules/examples/circular-build-stdout.txt
new file mode 100644
index 0000000..433b461
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular-build-stdout.txt
@@ -0,0 +1 @@
+(Ninja generators)?(build stopped: dependency cycle:)
diff --git a/Tests/RunCMake/CXXModules/examples/circular-stderr.txt b/Tests/RunCMake/CXXModules/examples/circular-stderr.txt
new file mode 100644
index 0000000..5e4392a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt
new file mode 100644
index 0000000..4d1997c
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_circular CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(circular STATIC)
+target_sources(circular
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        circular-a.cppm
+        circular-b.cppm)
+target_compile_features(circular PUBLIC cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/examples/circular/circular-a.cppm b/Tests/RunCMake/CXXModules/examples/circular/circular-a.cppm
new file mode 100644
index 0000000..eea842b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular/circular-a.cppm
@@ -0,0 +1,6 @@
+export module a;
+import b;
+
+export int a() {
+    return b();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/circular/circular-b.cppm b/Tests/RunCMake/CXXModules/examples/circular/circular-b.cppm
new file mode 100644
index 0000000..fc6dc42
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/circular/circular-b.cppm
@@ -0,0 +1,6 @@
+export module b;
+import a;
+
+export int b() {
+    return a();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt b/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt
new file mode 100644
index 0000000..5e4392a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt
new file mode 100644
index 0000000..27be7a8
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_duplicate CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_executable(duplicate)
+target_sources(duplicate
+  PRIVATE
+    main.cxx
+  PRIVATE
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        duplicate.cxx)
+target_compile_features(duplicate PRIVATE cxx_std_20)
+target_compile_definitions(duplicate PRIVATE NDUPLICATE=1)
+
+add_executable(duplicate2)
+target_sources(duplicate2
+  PRIVATE
+    main.cxx
+  PRIVATE
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        duplicate.cxx)
+target_compile_features(duplicate2 PRIVATE cxx_std_20)
+target_compile_definitions(duplicate2 PRIVATE NDUPLICATE=2)
+
+add_test(NAME duplicate COMMAND duplicate)
+set_property(TEST duplicate
+  PROPERTY
+    PASS_REGULAR_EXPRESSION "From duplicate #1")
+add_test(NAME duplicate2 COMMAND duplicate2)
+set_property(TEST duplicate2
+  PROPERTY
+    PASS_REGULAR_EXPRESSION "From duplicate #2")
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate/duplicate.cxx b/Tests/RunCMake/CXXModules/examples/duplicate/duplicate.cxx
new file mode 100644
index 0000000..c0c820b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/duplicate/duplicate.cxx
@@ -0,0 +1,11 @@
+module;
+
+#include <iostream>
+
+export module duplicate;
+
+export int from_import()
+{
+  std::cerr << "From duplicate #" << NDUPLICATE << std::endl;
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate/main.cxx b/Tests/RunCMake/CXXModules/examples/duplicate/main.cxx
new file mode 100644
index 0000000..c2c0636
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/duplicate/main.cxx
@@ -0,0 +1,6 @@
+import duplicate;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index a2eeddf..50b1f90 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -940,6 +940,7 @@
 
 set(RunCMake_TEST_OPTIONS --trace)
 run_cmake(trace)
+run_cmake(trace-try_compile)
 unset(RunCMake_TEST_OPTIONS)
 
 set(RunCMake_TEST_OPTIONS --trace-expand)
@@ -952,6 +953,7 @@
 
 set(RunCMake_TEST_OPTIONS --trace-redirect=${RunCMake_BINARY_DIR}/redirected.trace)
 run_cmake(trace-redirect)
+run_cmake(trace-try_compile-redirect)
 unset(RunCMake_TEST_OPTIONS)
 
 set(RunCMake_TEST_OPTIONS --trace-redirect=/no/such/file.txt)
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-redirect-check.cmake b/Tests/RunCMake/CommandLine/trace-try_compile-redirect-check.cmake
new file mode 100644
index 0000000..94a7c95
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-try_compile-redirect-check.cmake
@@ -0,0 +1,13 @@
+file(READ ${RunCMake_SOURCE_DIR}/trace-try_compile-stderr.txt expected_content)
+string(REGEX REPLACE "\n+$" "" expected_content "${expected_content}")
+
+file(READ ${RunCMake_BINARY_DIR}/redirected.trace actual_content)
+string(REGEX REPLACE "\r\n" "\n" actual_content "${actual_content}")
+string(REGEX REPLACE "\n+$" "" actual_content "${actual_content}")
+if(NOT "${actual_content}" MATCHES "${expected_content}")
+    set(RunCMake_TEST_FAILED
+        "Trace file content does not match that expected."
+        "Expected to match:\n${expected_content}\n"
+        "Actual content:\n${actual_content}\n"
+        )
+endif()
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
new file mode 100644
index 0000000..982cb89
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.24)
+project(test C)
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-stderr.txt b/Tests/RunCMake/CommandLine/trace-try_compile-stderr.txt
new file mode 100644
index 0000000..1674b8f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-try_compile-stderr.txt
@@ -0,0 +1,4 @@
+.*Modules/CMakeDetermineCompilerABI.cmake\([0-9]+\):  try_compile\([^)]+\)
+.*Tests/RunCMake/CommandLine/trace-try_compile(-redirect)?-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+/CMakeLists.txt\([0-9]+\):  cmake_minimum_required.*
+.*Tests/RunCMake/CommandLine/trace-try_compile(-redirect)?-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+/CMakeLists.txt\([0-9]+\):  project\(CMAKE_TRY_COMPILE.*
+.*Tests/RunCMake/CommandLine/trace-try_compile(-redirect)?-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+/CMakeLists.txt\([0-9]+\):  add_executable\(cmTC_.*
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile.cmake b/Tests/RunCMake/CommandLine/trace-try_compile.cmake
new file mode 100644
index 0000000..982cb89
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-try_compile.cmake
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.24)
+project(test C)
diff --git a/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt b/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt
new file mode 100644
index 0000000..f5247ca
--- /dev/null
+++ b/Tests/RunCMake/alias_targets/duplicate-target-CMP0107-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at duplicate-target-CMP0107-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0107 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt b/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt
new file mode 100644
index 0000000..1183f86
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at Repeat-CMP0103-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0103 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt b/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt
new file mode 100644
index 0000000..07e9a9f
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries/CMP0108-OLD-self-link-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0108-OLD-self-link.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0108 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/UseSWIG/BasicPerl/CMakeLists.txt b/Tests/UseSWIG/BasicPerl/CMakeLists.txt
index 671d529..1151739 100644
--- a/Tests/UseSWIG/BasicPerl/CMakeLists.txt
+++ b/Tests/UseSWIG/BasicPerl/CMakeLists.txt
@@ -4,7 +4,9 @@
 
 include(CTest)
 
-set(language "perl")
+if(NOT DEFINED language)
+  set(language "perl")
+endif()
 
 include (../BasicConfiguration.cmake)
 
diff --git a/Tests/UseSWIG/CMakeLists.txt b/Tests/UseSWIG/CMakeLists.txt
index c76e8a0..7c4925e 100644
--- a/Tests/UseSWIG/CMakeLists.txt
+++ b/Tests/UseSWIG/CMakeLists.txt
@@ -20,6 +20,16 @@
   --build-options ${build_options}
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
+add_test(NAME UseSWIG.LegacyPerl5 COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/UseSWIG/LegacyPerl"
+  "${CMake_BINARY_DIR}/Tests/UseSWIG/LegacyPerl5"
+  ${build_generator_args}
+  --build-project TestLegacyPerl
+  --build-options ${build_options} -Dlanguage=perl5
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
 
 include(CheckLanguage)
 check_language(CSharp)
@@ -66,6 +76,16 @@
   --build-options ${build_options}
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
+add_test(NAME UseSWIG.BasicPerl5 COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/UseSWIG/BasicPerl"
+  "${CMake_BINARY_DIR}/Tests/UseSWIG/BasicPerl5"
+  ${build_generator_args}
+  --build-project TestBasicPerl
+  --build-options ${build_options} -Dlanguage=perl5
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
 if(SWIG_FOUND AND NOT SWIG_VERSION VERSION_LESS "4.0.2"
     AND CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])")
   add_test(NAME UseSWIG.Depfile.BasicPython COMMAND
@@ -89,6 +109,16 @@
     --build-options ${build_options} -DSWIG_USE_SWIG_DEPENDENCIES=ON
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
+  add_test(NAME UseSWIG.Depfile.BasicPerl5 COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/UseSWIG/BasicPerl"
+    "${CMake_BINARY_DIR}/Tests/UseSWIG/BasicPerl5.Depfile"
+    ${build_generator_args}
+    --build-project TestBasicPerl
+    --build-options ${build_options} -DSWIG_USE_SWIG_DEPENDENCIES=ON -Dlanguage=perl5
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
 endif()
 
 if (CMake_TEST_UseSWIG_Fortran)
diff --git a/Tests/UseSWIG/LegacyPerl/CMakeLists.txt b/Tests/UseSWIG/LegacyPerl/CMakeLists.txt
index 90d92f4..be0b465 100644
--- a/Tests/UseSWIG/LegacyPerl/CMakeLists.txt
+++ b/Tests/UseSWIG/LegacyPerl/CMakeLists.txt
@@ -4,7 +4,9 @@
 
 include(CTest)
 
-set(language "perl")
+if(NOT DEFINED language)
+  set(language "perl")
+endif()
 
 include (../LegacyConfiguration.cmake)
 
diff --git a/Utilities/ClangTidyModule/CMakeLists.txt b/Utilities/ClangTidyModule/CMakeLists.txt
new file mode 100644
index 0000000..6be13d6
--- /dev/null
+++ b/Utilities/ClangTidyModule/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+cmake_minimum_required(VERSION 3.13)
+project(CMakeClangTidyModule C CXX)
+
+get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
+get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Clang REQUIRED)
+
+add_library(cmake-clang-tidy-module MODULE
+  Module.cxx
+
+  UseCmstrlenCheck.cxx
+  UseCmstrlenCheck.h
+  )
+target_include_directories(cmake-clang-tidy-module PRIVATE ${CLANG_INCLUDE_DIRS})
+target_link_libraries(cmake-clang-tidy-module PRIVATE clang-tidy)
+
+option(RUN_TESTS "Run the tests for the clang-tidy module" OFF)
+if(RUN_TESTS)
+  enable_testing()
+  add_subdirectory(Tests)
+endif()
diff --git a/Utilities/ClangTidyModule/Module.cxx b/Utilities/ClangTidyModule/Module.cxx
new file mode 100644
index 0000000..a35c336
--- /dev/null
+++ b/Utilities/ClangTidyModule/Module.cxx
@@ -0,0 +1,24 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <clang-tidy/ClangTidyModule.h>
+#include <clang-tidy/ClangTidyModuleRegistry.h>
+
+#include "UseCmstrlenCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class CMakeClangTidyModule : public ClangTidyModule
+{
+public:
+  void addCheckFactories(ClangTidyCheckFactories& CheckFactories) override
+  {
+    CheckFactories.registerCheck<UseCmstrlenCheck>("cmake-use-cmstrlen");
+  }
+};
+
+static ClangTidyModuleRegistry::Add<CMakeClangTidyModule> X(
+  "cmake-clang-tidy", "Adds lint checks for the CMake code base.");
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/Tests/CMakeLists.txt b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
new file mode 100644
index 0000000..42027ed
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
@@ -0,0 +1,13 @@
+configure_file("${CMake_SOURCE_DIR}/.clang-format" ".clang-format" COPYONLY)
+
+function(add_run_clang_tidy_test check_name)
+  add_test(NAME "RunClangTidy.${check_name}" COMMAND ${CMAKE_COMMAND}
+    "-DCLANG_TIDY_COMMAND=$<TARGET_FILE:clang-tidy>"
+    "-DCLANG_TIDY_MODULE=$<TARGET_FILE:cmake-clang-tidy-module>"
+    "-DCHECK_NAME=${check_name}"
+    "-DRunClangTidy_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/RunClangTidy.cmake"
+    )
+endfunction()
+
+add_run_clang_tidy_test(cmake-use-cmstrlen)
diff --git a/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake b/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake
new file mode 100644
index 0000000..486d592
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake
@@ -0,0 +1,67 @@
+set(config_arg)
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
+  set(config_arg "--config-file=${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
+endif()
+
+foreach(o out err)
+  if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-std${o}.txt")
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-std${o}.txt" expect_std${o})
+    string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}")
+  else()
+    set(expect_std${o} "")
+  endif()
+endforeach()
+
+set(source_file "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}.cxx")
+configure_file("${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cxx" "${source_file}" COPYONLY)
+
+set(command
+  "${CLANG_TIDY_COMMAND}"
+  "--load=${CLANG_TIDY_MODULE}"
+  "--checks=-*,${CHECK_NAME}"
+  "--fix"
+  "--format-style=file"
+  ${config_arg}
+  "${source_file}"
+  --
+  )
+execute_process(
+  COMMAND ${command}
+  RESULT_VARIABLE result
+  OUTPUT_VARIABLE actual_stdout
+  ERROR_VARIABLE actual_stderr
+  )
+string(REPLACE "${RunClangTidy_BINARY_DIR}/" "" actual_stdout "${actual_stdout}")
+
+set(RunClangTidy_TEST_FAILED)
+
+if(NOT result EQUAL 0)
+  string(APPEND RunClangTidy_TEST_FAILED "Expected result: 0, actual result: ${result}\n")
+endif()
+
+foreach(o out err)
+  string(REGEX REPLACE "\n+$" "" actual_std${o} "${actual_std${o}}")
+  if(NOT actual_std${o} STREQUAL expect_std${o})
+    string(REPLACE "\n" "\n  " expect_std${o}_formatted "  ${expect_std${o}}")
+    string(REPLACE "\n" "\n  " actual_std${o}_formatted "  ${actual_std${o}}")
+    string(APPEND RunClangTidy_TEST_FAILED "Expected std${o}:\n${expect_std${o}_formatted}\nActual std${o}:\n${actual_std${o}_formatted}\n")
+  endif()
+endforeach()
+
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-fixit.cxx")
+  set(expect_fixit_file "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-fixit.cxx")
+else()
+  set(expect_fixit_file "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cxx")
+endif()
+file(READ "${expect_fixit_file}" expect_fixit)
+file(READ "${source_file}" actual_fixit)
+if(NOT expect_fixit STREQUAL actual_fixit)
+  string(REPLACE "\n" "\n  " expect_fixit_formatted "  ${expect_fixit}")
+  string(REPLACE "\n" "\n  " actual_fixit_formatted "  ${actual_fixit}")
+  string(APPEND RunClangTidy_TEST_FAILED "Expected fixit:\n${expect_fixit_formatted}\nActual fixit:\n${actual_fixit_formatted}\n")
+endif()
+
+if(RunClangTidy_TEST_FAILED)
+  string(REPLACE ";" " " command_formatted "${command}")
+  message(FATAL_ERROR "Command:\n  ${command_formatted}\n${RunClangTidy_TEST_FAILED}")
+endif()
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx
new file mode 100644
index 0000000..c93d557
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx
@@ -0,0 +1,37 @@
+#include <cstring>
+
+template <size_t N>
+constexpr size_t cmStrLen(const char (&/*str*/)[N])
+{
+  return N - 1;
+}
+
+namespace ns1 {
+using std::strlen;
+}
+
+namespace ns2 {
+std::size_t strlen(const char* str)
+{
+  return std::strlen(str);
+}
+}
+
+int main()
+{
+  // String variable used for calling strlen() on a variable
+  auto s0 = "howdy";
+
+  // Correction needed
+  (void)cmStrLen("Hello");
+  (void)cmStrLen("Goodbye");
+  (void)cmStrLen("Hola");
+  (void)cmStrLen("Bonjour");
+
+  // No correction needed
+  (void)ns2::strlen("Salve");
+  (void)cmStrLen("Konnichiwa");
+  (void)strlen(s0);
+
+  return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stderr.txt b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stderr.txt
new file mode 100644
index 0000000..9d9d2ed
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stderr.txt
@@ -0,0 +1,2 @@
+4 warnings generated.
+clang-tidy applied 4 of 4 suggested fixes.
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt
new file mode 100644
index 0000000..6c52ad5
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt
@@ -0,0 +1,20 @@
+cmake-use-cmstrlen.cxx:26:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+  (void)strlen("Hello");
+        ^~~~~~
+        cmStrLen
+cmake-use-cmstrlen.cxx:26:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:27:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+  (void)::strlen("Goodbye");
+        ^~~~~~~~
+        cmStrLen
+cmake-use-cmstrlen.cxx:27:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:28:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+  (void)std::strlen("Hola");
+        ^~~~~~~~~~~
+        cmStrLen
+cmake-use-cmstrlen.cxx:28:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:29:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+  (void)ns1::strlen("Bonjour");
+        ^~~~~~~~~~~
+        cmStrLen
+cmake-use-cmstrlen.cxx:29:9: note: FIX-IT applied suggested code changes
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx
new file mode 100644
index 0000000..f36262b
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx
@@ -0,0 +1,37 @@
+#include <cstring>
+
+template <size_t N>
+constexpr size_t cmStrLen(const char (&/*str*/)[N])
+{
+  return N - 1;
+}
+
+namespace ns1 {
+using std::strlen;
+}
+
+namespace ns2 {
+std::size_t strlen(const char* str)
+{
+  return std::strlen(str);
+}
+}
+
+int main()
+{
+  // String variable used for calling strlen() on a variable
+  auto s0 = "howdy";
+
+  // Correction needed
+  (void)strlen("Hello");
+  (void)::strlen("Goodbye");
+  (void)std::strlen("Hola");
+  (void)ns1::strlen("Bonjour");
+
+  // No correction needed
+  (void)ns2::strlen("Salve");
+  (void)cmStrLen("Konnichiwa");
+  (void)strlen(s0);
+
+  return 0;
+}
diff --git a/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx b/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx
new file mode 100644
index 0000000..590d260
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx
@@ -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.  */
+#include "UseCmstrlenCheck.h"
+
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+UseCmstrlenCheck::UseCmstrlenCheck(StringRef Name, ClangTidyContext* Context)
+  : ClangTidyCheck(Name, Context)
+{
+}
+
+void UseCmstrlenCheck::registerMatchers(MatchFinder* Finder)
+{
+  Finder->addMatcher(callExpr(callee(functionDecl(hasName("::strlen"))),
+                              callee(expr().bind("callee")),
+                              hasArgument(0, stringLiteral())),
+                     this);
+}
+
+void UseCmstrlenCheck::check(const MatchFinder::MatchResult& Result)
+{
+  const Expr* Node = Result.Nodes.getNodeAs<Expr>("callee");
+
+  this->diag(Node->getBeginLoc(), "use cmStrLen() for string literals")
+    << FixItHint::CreateReplacement(Node->getSourceRange(), "cmStrLen");
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseCmstrlenCheck.h b/Utilities/ClangTidyModule/UseCmstrlenCheck.h
new file mode 100644
index 0000000..08f77c2
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmstrlenCheck.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class UseCmstrlenCheck : public ClangTidyCheck
+{
+public:
+  UseCmstrlenCheck(StringRef Name, ClangTidyContext* Context);
+  void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+
+  void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+};
+}
+}
+}
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index bc16350..b084dd5 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index 886f4e0..bd2f305 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.23 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)