Merge topic 'clang-tidy-8'

4af094c8df clang-tidy: Blacklist violations for version 8

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3627
diff --git a/.gitattributes b/.gitattributes
index 24fd9c2..3854b73 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,4 @@
-.gitattributes   export-ignore
+.git*            export-ignore
 .hooks*          export-ignore
 
 # Custom attribute to mark sources as using our C code style.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5efa077..75ac8bf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -626,8 +626,7 @@
 # The main section of the CMakeLists file
 #
 #-----------------------------------------------------------------------
-# Compute CMake_VERSION, etc.
-include(Source/CMakeVersionCompute.cmake)
+include(Source/CMakeVersion.cmake)
 
 # Include the standard Dart testing module
 enable_testing()
diff --git a/Help/guide/tutorial/MultiPackage/CMakeLists.txt b/Help/guide/tutorial/MultiPackage/CMakeLists.txt
index 067e807..bea611c 100644
--- a/Help/guide/tutorial/MultiPackage/CMakeLists.txt
+++ b/Help/guide/tutorial/MultiPackage/CMakeLists.txt
@@ -1,19 +1,26 @@
 cmake_minimum_required(VERSION 3.3)
 project(Tutorial)
 
-# control how we mark up Debug libraries compared to Release libraries
-set(CMAKE_DEBUG_POSTFIX "-d")
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# set the version number
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
 
 # control where the static and shared libraries are built so that on windows
 # we don't need to tinker with the path to run the executable
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
 
 option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
 
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
+if(APPLE)
+  set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
+elseif(UNIX)
+  set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
+endif()
 
 # configure a header file to pass the version number only
 configure_file(
diff --git a/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt
index 161ad64..63c0f5f 100644
--- a/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt
@@ -1,4 +1,3 @@
-
 # add the library that runs
 add_library(MathFunctions MathFunctions.cxx)
 
@@ -62,6 +61,7 @@
 set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
 set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
 
+# install rules
 install(TARGETS MathFunctions
         DESTINATION lib
         EXPORT MathFunctionsTargets)
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index 0e84116..773ee53 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -72,7 +72,7 @@
   find_program(CMAKE_LINKER NAMES link HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
   find_program(CMAKE_MT     NAMES mt   HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
 
-  list(APPEND _CMAKE_TOOL_VARS CMAKE_LINKER CMAKE_MT)
+  list(APPEND _CMAKE_TOOL_VARS LINKER MT)
 
 # in all other cases search for ar, ranlib, etc.
 else()
@@ -84,27 +84,36 @@
   endif()
 
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
-    set(LLVM_OBJDUMP "llvm-objdump")
-    set(LLVM_LLD "ld.lld")
-    set(LLVM_RANLIB "llvm-ranlib")
-    set(LLVM_AR "llvm-ar")
+    set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-ar")
+    set(_CMAKE_ADDITIONAL_RANLIB_NAMES "llvm-ranlib")
+    set(_CMAKE_ADDITIONAL_STRIP_NAMES "llvm-strip")
+    set(_CMAKE_ADDITIONAL_LINKER_NAMES "ld.lld")
+    set(_CMAKE_ADDITIONAL_NM_NAMES "llvm-nm")
+    set(_CMAKE_ADDITIONAL_OBJDUMP_NAMES "llvm-objdump")
+    set(_CMAKE_ADDITIONAL_OBJCOPY_NAMES "llvm-objcopy")
+    set(_CMAKE_ADDITIONAL_READELF_NAMES "llvm-readelf")
+    set(_CMAKE_ADDITIONAL_DLLTOOL_NAMES "llvm-dlltool")
+    set(_CMAKE_ADDITIONAL_ADDR2LINE_NAMES "llvm-addr2line")
   endif()
 
-  find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${LLVM_AR} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
 
-  find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${LLVM_RANLIB} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_CMAKE_ADDITIONAL_RANLIB_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
   if(NOT CMAKE_RANLIB)
     set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
   endif()
 
 
-  find_program(CMAKE_STRIP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${LLVM_LLD} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${LLVM_OBJDUMP} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_STRIP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_READELF NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_DLLTOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  find_program(CMAKE_ADDR2LINE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
 
-  list(APPEND _CMAKE_TOOL_VARS CMAKE_AR CMAKE_RANLIB CMAKE_STRIP CMAKE_LINKER CMAKE_NM CMAKE_OBJDUMP CMAKE_OBJCOPY)
+  list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE)
 
 endif()
 
@@ -115,15 +124,16 @@
     message(FATAL_ERROR "Could not find install_name_tool, please check your installation.")
   endif()
 
-  list(APPEND _CMAKE_TOOL_VARS CMAKE_INSTALL_NAME_TOOL)
+  list(APPEND _CMAKE_TOOL_VARS INSTALL_NAME_TOOL)
 endif()
 
 # Mark any tool cache entries as advanced.
 foreach(var IN LISTS _CMAKE_TOOL_VARS)
-  get_property(_CMAKE_TOOL_CACHED CACHE ${var} PROPERTY TYPE)
+  get_property(_CMAKE_TOOL_CACHED CACHE CMAKE_${var} PROPERTY TYPE)
   if(_CMAKE_TOOL_CACHED)
-    mark_as_advanced(${var})
+    mark_as_advanced(CMAKE_${var})
   endif()
+  unset(_CMAKE_ADDITIONAL_${var}_NAMES)
 endforeach()
 unset(_CMAKE_TOOL_VARS)
 unset(_CMAKE_TOOL_CACHED)
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 14fc231..e55ed46 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -1079,7 +1079,7 @@
     message(FATAL_ERROR "Tag for git checkout should not be empty.")
   endif()
 
-  set(git_clone_options)
+  set(git_clone_options "--no-checkout")
   if(git_shallow)
     if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10)
       list(APPEND git_clone_options "--depth 1 --no-single-branch")
diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake
index da33301..ccc7d5b 100644
--- a/Modules/FindPythonInterp.cmake
+++ b/Modules/FindPythonInterp.cmake
@@ -39,6 +39,15 @@
 ``find_package(PythonLibs)``, call ``find_package(PythonInterp)`` first to
 get the currently active Python version by default with a consistent version
 of PYTHON_LIBRARIES.
+
+.. note::
+
+  A call to ``find_package(PythonInterp ${V})`` for python version ``V``
+  may find a ``python`` executable with no version suffix.  In this case
+  no attempt is made to avoid python executables from other versions.
+  Use :module:`FindPython3`, :module:`FindPython2` or :module:`FindPython`
+  instead.
+
 #]=======================================================================]
 
 unset(_Python_NAMES)
diff --git a/Source/.gitattributes b/Source/.gitattributes
index 7c160cc..d0aedc2 100644
--- a/Source/.gitattributes
+++ b/Source/.gitattributes
@@ -1,2 +1,4 @@
+CMakeVersion.cmake    export-subst
+
 # Do not format third-party sources.
 /kwsys/**                                  -format.clang-format-6.0
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 8117916..041c606 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -142,6 +142,7 @@
 
   cmAffinity.cxx
   cmAffinity.h
+  cmAlgorithms.h
   cmArchiveWrite.cxx
   cmArgumentParser.cxx
   cmArgumentParser.h
@@ -403,6 +404,7 @@
   cmStateSnapshot.cxx
   cmStateSnapshot.h
   cmStateTypes.h
+  cmStringAlgorithms.h
   cmSystemTools.cxx
   cmSystemTools.h
   cmTarget.cxx
@@ -1159,6 +1161,21 @@
 include (${CMake_SOURCE_DIR}/Source/LocalUserOptions.cmake OPTIONAL)
 
 if(WIN32)
+  # Compute the binary version that appears in the RC file. Version
+  # components in the RC file are 16-bit integers so we may have to
+  # split the patch component.
+  if(CMake_VERSION_PATCH MATCHES "^([0-9]+)([0-9][0-9][0-9][0-9])$")
+    set(CMake_RCVERSION_YEAR "${CMAKE_MATCH_1}")
+    set(CMake_RCVERSION_MONTH_DAY "${CMAKE_MATCH_2}")
+    string(REGEX REPLACE "^0+" "" CMake_RCVERSION_MONTH_DAY "${CMake_RCVERSION_MONTH_DAY}")
+    set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_RCVERSION_YEAR},${CMake_RCVERSION_MONTH_DAY})
+    unset(CMake_RCVERSION_MONTH_DAY)
+    unset(CMake_RCVERSION_YEAR)
+  else()
+    set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_VERSION_PATCH})
+  endif()
+  set(CMake_RCVERSION_STR ${CMake_VERSION})
+
   # Add Windows executable version information.
   configure_file("CMakeVersion.rc.in" "CMakeVersion.rc" @ONLY)
 
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 0bde5ff..f02ef52 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,82 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 15)
-set(CMake_VERSION_PATCH 20190730)
+set(CMake_VERSION_PATCH 20190731)
 #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.
+set(CMake_VERSION
+  "${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH}")
+if(DEFINED CMake_VERSION_RC)
+  set(CMake_VERSION "${CMake_VERSION}-rc${CMake_VERSION_RC}")
+endif()
+
+# Releases define a small patch level.
+if("${CMake_VERSION_PATCH}" VERSION_LESS 20000000)
+  set(CMake_VERSION_IS_RELEASE 1)
+else()
+  set(CMake_VERSION_IS_RELEASE 0)
+endif()
+
+if(EXISTS ${CMake_SOURCE_DIR}/.git)
+  find_package(Git QUIET)
+  if(GIT_FOUND)
+    macro(_git)
+      execute_process(
+        COMMAND ${GIT_EXECUTABLE} ${ARGN}
+        WORKING_DIRECTORY ${CMake_SOURCE_DIR}
+        RESULT_VARIABLE _git_res
+        OUTPUT_VARIABLE _git_out OUTPUT_STRIP_TRAILING_WHITESPACE
+        ERROR_VARIABLE _git_err ERROR_STRIP_TRAILING_WHITESPACE
+        )
+    endmacro()
+  endif()
+endif()
+
+# Try to identify the current development source version.
+if(COMMAND _git)
+  # Get the commit checked out in this work tree.
+  _git(log -n 1 HEAD "--pretty=format:%h %s" --)
+  set(git_info "${_git_out}")
+else()
+  # Get the commit exported by 'git archive'.
+  set(git_info [==[$Format:%h %s$]==])
+endif()
+
+# Extract commit information if available.
+if(git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* (.*)$")
+  # Have commit information.
+  set(git_hash "${CMAKE_MATCH_1}")
+  set(git_subject "${CMAKE_MATCH_2}")
+
+  # If this is not the exact commit of a release, add dev info.
+  if(NOT "${git_subject}" MATCHES "^[Cc][Mm]ake ${CMake_VERSION}$")
+    set(CMake_VERSION "${CMake_VERSION}-g${git_hash}")
+  endif()
+
+  # If this is a work tree, check whether it is dirty.
+  if(COMMAND _git)
+    _git(update-index -q --refresh)
+    _git(diff-index --name-only HEAD --)
+    if(_git_out)
+      set(CMake_VERSION_IS_DIRTY 1)
+    endif()
+  endif()
+else()
+  # No commit information.
+  if(NOT CMake_VERSION_IS_RELEASE)
+    # Generic development version.
+    set(CMake_VERSION "${CMake_VERSION}-git")
+  endif()
+endif()
+
+# Extract the version suffix component.
+if(CMake_VERSION MATCHES "-(.*)$")
+  set(CMake_VERSION_SUFFIX "${CMAKE_MATCH_1}")
+else()
+  set(CMake_VERSION_SUFFIX "")
+endif()
+if(CMake_VERSION_IS_DIRTY)
+  set(CMake_VERSION ${CMake_VERSION}-dirty)
+endif()
diff --git a/Source/CMakeVersionCompute.cmake b/Source/CMakeVersionCompute.cmake
deleted file mode 100644
index 160f470..0000000
--- a/Source/CMakeVersionCompute.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
-# Load version number components.
-include(${CMake_SOURCE_DIR}/Source/CMakeVersion.cmake)
-
-# Releases define a small patch level.
-if("${CMake_VERSION_PATCH}" VERSION_LESS 20000000)
-  set(CMake_VERSION_IS_DIRTY 0)
-  set(CMake_VERSION_IS_RELEASE 1)
-  set(CMake_VERSION_SOURCE "")
-else()
-  set(CMake_VERSION_IS_DIRTY 0) # may be set to 1 by CMakeVersionSource
-  set(CMake_VERSION_IS_RELEASE 0)
-  include(${CMake_SOURCE_DIR}/Source/CMakeVersionSource.cmake)
-endif()
-
-# Compute the full version string.
-set(CMake_VERSION ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH})
-if(CMake_VERSION_SOURCE)
-  set(CMake_VERSION_SUFFIX "${CMake_VERSION_SOURCE}")
-elseif(DEFINED CMake_VERSION_RC)
-  set(CMake_VERSION_SUFFIX "rc${CMake_VERSION_RC}")
-else()
-  set(CMake_VERSION_SUFFIX "")
-endif()
-if(CMake_VERSION_SUFFIX)
-  set(CMake_VERSION ${CMake_VERSION}-${CMake_VERSION_SUFFIX})
-endif()
-if(CMake_VERSION_IS_DIRTY)
-  set(CMake_VERSION ${CMake_VERSION}-dirty)
-endif()
-
-# Compute the binary version that appears in the RC file. Version
-# components in the RC file are 16-bit integers so we may have to
-# split the patch component.
-if(CMake_VERSION_PATCH MATCHES "^([0-9]+)([0-9][0-9][0-9][0-9])$")
-  set(CMake_RCVERSION_YEAR "${CMAKE_MATCH_1}")
-  set(CMake_RCVERSION_MONTH_DAY "${CMAKE_MATCH_2}")
-  string(REGEX REPLACE "^0+" "" CMake_RCVERSION_MONTH_DAY "${CMake_RCVERSION_MONTH_DAY}")
-  set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_RCVERSION_YEAR},${CMake_RCVERSION_MONTH_DAY})
-  unset(CMake_RCVERSION_MONTH_DAY)
-  unset(CMake_RCVERSION_YEAR)
-else()
-  set(CMake_RCVERSION ${CMake_VERSION_MAJOR},${CMake_VERSION_MINOR},${CMake_VERSION_PATCH})
-endif()
-set(CMake_RCVERSION_STR ${CMake_VERSION})
diff --git a/Source/CMakeVersionSource.cmake b/Source/CMakeVersionSource.cmake
deleted file mode 100644
index 5ea1de3..0000000
--- a/Source/CMakeVersionSource.cmake
+++ /dev/null
@@ -1,30 +0,0 @@
-# Try to identify the current development source version.
-set(CMake_VERSION_SOURCE "")
-if(EXISTS ${CMake_SOURCE_DIR}/.git/HEAD)
-  find_program(GIT_EXECUTABLE NAMES git git.cmd)
-  mark_as_advanced(GIT_EXECUTABLE)
-  if(GIT_EXECUTABLE)
-    execute_process(
-      COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=4 HEAD
-      OUTPUT_VARIABLE head
-      OUTPUT_STRIP_TRAILING_WHITESPACE
-      WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-      )
-    if(head)
-      set(CMake_VERSION_SOURCE "g${head}")
-      execute_process(
-        COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
-        WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-        )
-      execute_process(
-        COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD --
-        OUTPUT_VARIABLE dirty
-        OUTPUT_STRIP_TRAILING_WHITESPACE
-        WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-        )
-      if(dirty)
-        set(CMake_VERSION_IS_DIRTY 1)
-      endif()
-    endif()
-  endif()
-endif()
diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx
index 76f0699..19a3a0a 100644
--- a/Source/CPack/cmCPackNuGetGenerator.cxx
+++ b/Source/CPack/cmCPackNuGetGenerator.cxx
@@ -2,9 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackNuGetGenerator.h"
 
-#include "cmAlgorithms.h"
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #include <algorithm>
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index c8e4fa1..407e9f8 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -9,6 +9,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index 9d9761c..093017c 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -10,11 +10,11 @@
 #include <time.h>
 #include <vector>
 
-#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmProcessOutput.h"
 #include "cmProcessTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 54c4bae..2c6ff83 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -19,6 +19,7 @@
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmake.h"
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 5134407..67c24ca 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -20,7 +20,6 @@
 
 #include "cm_memory.hxx"
 
-#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestMultiProcessHandler.h"
 #include "cmCommand.h"
@@ -30,6 +29,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
diff --git a/Source/CTest/cmParseGTMCoverage.cxx b/Source/CTest/cmParseGTMCoverage.cxx
index 0722753..881bf2d 100644
--- a/Source/CTest/cmParseGTMCoverage.cxx
+++ b/Source/CTest/cmParseGTMCoverage.cxx
@@ -1,8 +1,8 @@
 #include "cmParseGTMCoverage.h"
 
-#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #include "cmsys/Directory.hxx"
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index c9ebba8..287a482 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -6,6 +6,7 @@
 #include "cmAlgorithms.h"
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
+#include "cmStringAlgorithms.h"
 #include "cmVersion.h"
 #include "cmake.h"
 #include "cmsys/CommandLineArguments.hxx"
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index cf71052..d7ea483 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -6,41 +6,14 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include "cmRange.h"
-
 #include "cm_kwiml.h"
-#include "cm_string_view.hxx"
 #include <algorithm>
 #include <functional>
 #include <iterator>
-#include <sstream>
-#include <string.h>
-#include <string>
 #include <unordered_set>
 #include <utility>
 #include <vector>
 
-struct cmStrCmp
-{
-  cmStrCmp(const char* test)
-    : m_test(test)
-  {
-  }
-  cmStrCmp(std::string test)
-    : m_test(std::move(test))
-  {
-  }
-
-  bool operator()(const std::string& input) const { return m_test == input; }
-
-  bool operator()(const char* input) const
-  {
-    return strcmp(input, m_test.c_str()) == 0;
-  }
-
-private:
-  const std::string m_test;
-};
-
 template <typename FwdIt>
 FwdIt cmRotate(FwdIt first, FwdIt middle, FwdIt last)
 {
@@ -120,8 +93,6 @@
 };
 }
 
-typedef cmRange<std::vector<std::string>::const_iterator> cmStringRange;
-
 class cmListFileBacktrace;
 typedef cmRange<std::vector<cmListFileBacktrace>::const_iterator>
   cmBacktraceRange;
@@ -146,31 +117,6 @@
 }
 
 template <typename Range>
-std::string cmJoin(Range const& r, const char* delimiter)
-{
-  if (r.empty()) {
-    return std::string();
-  }
-  std::ostringstream os;
-  typedef typename Range::value_type ValueType;
-  typedef typename Range::const_iterator InputIt;
-  const InputIt first = r.begin();
-  InputIt last = r.end();
-  --last;
-  std::copy(first, last, std::ostream_iterator<ValueType>(os, delimiter));
-
-  os << *last;
-
-  return os.str();
-}
-
-template <typename Range>
-std::string cmJoin(Range const& r, std::string const& delimiter)
-{
-  return cmJoin(r, delimiter.c_str());
-}
-
-template <typename Range>
 typename Range::const_iterator cmRemoveN(Range& r, size_t n)
 {
   return ContainerAlgorithms::RemoveN(r.begin(), r.end(), n);
@@ -248,23 +194,6 @@
   return cmRemoveDuplicates(r.begin(), r.end());
 }
 
-template <typename Range>
-std::string cmWrap(std::string const& prefix, Range const& r,
-                   std::string const& suffix, std::string const& sep)
-{
-  if (r.empty()) {
-    return std::string();
-  }
-  return prefix + cmJoin(r, suffix + sep + prefix) + suffix;
-}
-
-template <typename Range>
-std::string cmWrap(char prefix, Range const& r, char suffix,
-                   std::string const& sep)
-{
-  return cmWrap(std::string(1, prefix), r, std::string(1, suffix), sep);
-}
-
 template <typename Range, typename T>
 typename Range::const_iterator cmFindNot(Range const& r, T const& t)
 {
@@ -277,61 +206,6 @@
   return std::reverse_iterator<Iter>(it);
 }
 
-/** Returns true if string @a str starts with the character @a prefix.  **/
-inline bool cmHasPrefix(cm::string_view str, char prefix)
-{
-  return !str.empty() && (str.front() == prefix);
-}
-
-/** Returns true if string @a str starts with string @a prefix.  **/
-inline bool cmHasPrefix(cm::string_view str, cm::string_view prefix)
-{
-  return str.compare(0, prefix.size(), prefix) == 0;
-}
-
-/** Returns true if string @a str starts with string @a prefix.  **/
-template <size_t N>
-inline bool cmHasLiteralPrefix(cm::string_view str, const char (&prefix)[N])
-{
-  return cmHasPrefix(str, cm::string_view(prefix, N - 1));
-}
-
-/** Returns true if string @a str ends with the character @a suffix.  **/
-inline bool cmHasSuffix(cm::string_view str, char suffix)
-{
-  return !str.empty() && (str.back() == suffix);
-}
-
-/** Returns true if string @a str ends with string @a suffix.  **/
-inline bool cmHasSuffix(cm::string_view str, cm::string_view suffix)
-{
-  return str.size() >= suffix.size() &&
-    str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
-/** Returns true if string @a str ends with string @a suffix.  **/
-template <size_t N>
-inline bool cmHasLiteralSuffix(cm::string_view str, const char (&suffix)[N])
-{
-  return cmHasSuffix(str, cm::string_view(suffix, N - 1));
-}
-
-/** Removes an existing suffix character of from the string @a str.  **/
-inline void cmStripSuffixIfExists(std::string& str, char suffix)
-{
-  if (cmHasSuffix(str, suffix)) {
-    str.pop_back();
-  }
-}
-
-/** Removes an existing suffix string of from the string @a str.  **/
-inline void cmStripSuffixIfExists(std::string& str, cm::string_view suffix)
-{
-  if (cmHasSuffix(str, suffix)) {
-    str.resize(str.size() - suffix.size());
-  }
-}
-
 namespace cm {
 
 #if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
diff --git a/Source/cmAuxSourceDirectoryCommand.cxx b/Source/cmAuxSourceDirectoryCommand.cxx
index c1a6e7f..45c6411 100644
--- a/Source/cmAuxSourceDirectoryCommand.cxx
+++ b/Source/cmAuxSourceDirectoryCommand.cxx
@@ -7,9 +7,9 @@
 #include <stddef.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
index ac93155..0c73ac8 100644
--- a/Source/cmBinUtilsMacOSMachOLinker.cxx
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -3,9 +3,9 @@
 
 #include "cmBinUtilsMacOSMachOLinker.h"
 
-#include "cmAlgorithms.h"
 #include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
 #include "cmRuntimeDependencyArchive.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #include <sstream>
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 83e3eff..610e9f9 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -52,6 +52,7 @@
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 66250f3..61880c2 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -6,7 +6,6 @@
 #include <sstream>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalCommonGenerator.h"
@@ -17,6 +16,7 @@
 #include "cmOutputConverter.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
   : GeneratorTarget(gt)
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 54fc54c..f8b78b4 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,6 +11,7 @@
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index f696f28..78cddf0 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmComputeLinkInformation.h"
 
-#include "cmAlgorithms.h"
 #include "cmComputeLinkDepends.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -14,6 +13,7 @@
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 2907f4a..5b88807 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -15,6 +15,7 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index d780af6..d2a4148 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -9,7 +9,6 @@
 #include <string.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmExportTryCompileFileGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -17,6 +16,7 @@
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmVersion.h"
diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx
index 178e18b..764ba30 100644
--- a/Source/cmDependsFortran.cxx
+++ b/Source/cmDependsFortran.cxx
@@ -9,7 +9,6 @@
 #include <stdlib.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmFortranParser.h" /* Interface to parser object.  */
 #include "cmGeneratedFileStream.h"
 #include "cmLocalGenerator.h"
@@ -17,6 +16,7 @@
 #include "cmOutputConverter.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 // TODO: Test compiler for the case of the mod file.  Some always
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 8d91f43..465f4b3 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -16,6 +16,7 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx
index e693155..5edf7ca 100644
--- a/Source/cmExportBuildAndroidMKGenerator.cxx
+++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -6,13 +6,13 @@
 #include <sstream>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmGeneratorTarget.h"
 #include "cmLinkItem.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index de3e0e2..33806f2 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportBuildFileGenerator.h"
 
-#include "cmAlgorithms.h"
 #include "cmExportSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -12,6 +11,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 #include "cmake.h"
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 29afc9f..8fd2947 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportFileGenerator.h"
 
-#include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
@@ -14,6 +13,7 @@
 #include "cmPolicies.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index f8bc0ab..ab4a62b 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportInstallFileGenerator.h"
 
-#include "cmAlgorithms.h"
 #include "cmExportSet.h"
 #include "cmExportSetMap.h"
 #include "cmGeneratedFileStream.h"
@@ -15,6 +14,7 @@
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index fa318bc..2594287 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -2,12 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPI.h"
 
-#include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmFileAPICMakeFiles.h"
 #include "cmFileAPICache.h"
 #include "cmFileAPICodemodel.h"
 #include "cmGlobalGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmake.h"
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 0c4f5c4..e9ee7f5 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -30,6 +30,9 @@
 
 #include <algorithm>
 #include <cassert>
+#include <cstddef>
+#include <functional>
+#include <limits>
 #include <map>
 #include <memory>
 #include <set>
@@ -135,6 +138,40 @@
   return gt->GetName() + CMAKE_DIRECTORY_ID_SEP + hash;
 }
 
+class JBTIndex
+{
+public:
+  JBTIndex() = default;
+  explicit operator bool() const { return Index != None; }
+  Json::ArrayIndex Index = None;
+  static Json::ArrayIndex const None = static_cast<Json::ArrayIndex>(-1);
+};
+
+template <typename T>
+class JBT
+{
+public:
+  JBT(T v = T(), JBTIndex bt = JBTIndex())
+    : Value(std::move(v))
+    , Backtrace(bt)
+  {
+  }
+  T Value;
+  JBTIndex Backtrace;
+  friend bool operator==(JBT<T> const& l, JBT<T> const& r)
+  {
+    return l.Value == r.Value && l.Backtrace.Index == r.Backtrace.Index;
+  }
+  static bool ValueEq(JBT<T> const& l, JBT<T> const& r)
+  {
+    return l.Value == r.Value;
+  }
+  static bool ValueLess(JBT<T> const& l, JBT<T> const& r)
+  {
+    return l.Value < r.Value;
+  }
+};
+
 class BacktraceData
 {
   std::string TopSource;
@@ -169,7 +206,7 @@
 
 public:
   BacktraceData(std::string topSource);
-  bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index);
+  JBTIndex Add(cmListFileBacktrace const& bt);
   Json::Value Dump();
 };
 
@@ -178,16 +215,17 @@
 {
 }
 
-bool BacktraceData::Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index)
+JBTIndex BacktraceData::Add(cmListFileBacktrace const& bt)
 {
+  JBTIndex index;
   if (bt.Empty()) {
-    return false;
+    return index;
   }
   cmListFileContext const* top = &bt.Top();
   auto found = this->NodeMap.find(top);
   if (found != this->NodeMap.end()) {
-    index = found->second;
-    return true;
+    index.Index = found->second;
+    return index;
   }
   Json::Value entry = Json::objectValue;
   entry["file"] = this->AddFile(top->FilePath);
@@ -197,13 +235,12 @@
   if (!top->Name.empty()) {
     entry["command"] = this->AddCommand(top->Name);
   }
-  Json::ArrayIndex parent;
-  if (this->Add(bt.Pop(), parent)) {
-    entry["parent"] = parent;
+  if (JBTIndex parent = this->Add(bt.Pop())) {
+    entry["parent"] = parent.Index;
   }
-  index = this->NodeMap[top] = this->Nodes.size();
+  index.Index = this->NodeMap[top] = this->Nodes.size();
   this->Nodes.append(std::move(entry)); // NOLINT(*)
-  return true;
+  return index;
 }
 
 Json::Value BacktraceData::Dump()
@@ -222,32 +259,65 @@
 {
   struct IncludeEntry
   {
-    BT<std::string> Path;
+    JBT<std::string> Path;
     bool IsSystem = false;
-    IncludeEntry(BT<std::string> path, bool isSystem)
+    IncludeEntry(JBT<std::string> path, bool isSystem)
       : Path(std::move(path))
       , IsSystem(isSystem)
     {
     }
+    friend bool operator==(IncludeEntry const& l, IncludeEntry const& r)
+    {
+      return l.Path == r.Path && l.IsSystem == r.IsSystem;
+    }
   };
 
-  void SetDefines(std::set<BT<std::string>> const& defines);
-
   std::string Language;
   std::string Sysroot;
-  std::vector<BT<std::string>> Flags;
-  std::vector<BT<std::string>> Defines;
+  std::vector<JBT<std::string>> Flags;
+  std::vector<JBT<std::string>> Defines;
   std::vector<IncludeEntry> Includes;
-};
 
-void CompileData::SetDefines(std::set<BT<std::string>> const& defines)
-{
-  this->Defines.reserve(defines.size());
-  for (BT<std::string> const& d : defines) {
-    this->Defines.push_back(d);
+  friend bool operator==(CompileData const& l, CompileData const& r)
+  {
+    return (l.Language == r.Language && l.Sysroot == r.Sysroot &&
+            l.Flags == r.Flags && l.Defines == r.Defines &&
+            l.Includes == r.Includes);
   }
+};
 }
 
+namespace std {
+
+template <>
+struct hash<CompileData>
+{
+  std::size_t operator()(CompileData const& in) const
+  {
+    using std::hash;
+    size_t result =
+      hash<std::string>()(in.Language) ^ hash<std::string>()(in.Sysroot);
+    for (auto const& i : in.Includes) {
+      result = result ^
+        (hash<std::string>()(i.Path.Value) ^
+         hash<Json::ArrayIndex>()(i.Path.Backtrace.Index) ^
+         (i.IsSystem ? std::numeric_limits<size_t>::max() : 0));
+    }
+    for (auto const& i : in.Flags) {
+      result = result ^ hash<std::string>()(i.Value) ^
+        hash<Json::ArrayIndex>()(i.Backtrace.Index);
+    }
+    for (auto const& i : in.Defines) {
+      result = result ^ hash<std::string>()(i.Value) ^
+        hash<Json::ArrayIndex>()(i.Backtrace.Index);
+    }
+    return result;
+  }
+};
+
+} // namespace std
+
+namespace {
 class Target
 {
   cmGeneratorTarget* GT;
@@ -272,24 +342,32 @@
 
   struct CompileGroup
   {
-    std::map<Json::Value, Json::ArrayIndex>::iterator Entry;
+    std::unordered_map<CompileData, Json::ArrayIndex>::iterator Entry;
     Json::Value SourceIndexes = Json::arrayValue;
   };
-  std::map<Json::Value, Json::ArrayIndex> CompileGroupMap;
+  std::unordered_map<CompileData, Json::ArrayIndex> CompileGroupMap;
   std::vector<CompileGroup> CompileGroups;
 
+  template <typename T>
+  JBT<T> ToJBT(BT<T> const& bt)
+  {
+    return JBT<T>(bt.Value, this->Backtraces.Add(bt.Backtrace));
+  }
+
   void ProcessLanguages();
   void ProcessLanguage(std::string const& lang);
 
   Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si);
   CompileData BuildCompileData(cmSourceFile* sf);
+  CompileData MergeCompileData(CompileData const& fd);
   Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf,
                                          Json::ArrayIndex si);
   void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt);
+  void AddBacktrace(Json::Value& object, JBTIndex bt);
   Json::Value DumpPaths();
   Json::Value DumpCompileData(CompileData const& cd);
   Json::Value DumpInclude(CompileData::IncludeEntry const& inc);
-  Json::Value DumpDefine(BT<std::string> const& def);
+  Json::Value DumpDefine(JBT<std::string> const& def);
   Json::Value DumpSources();
   Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
                          Json::ArrayIndex si);
@@ -306,8 +384,8 @@
   Json::Value DumpLink();
   Json::Value DumpArchive();
   Json::Value DumpLinkCommandFragments();
-  Json::Value DumpCommandFragments(std::vector<BT<std::string>> const& frags);
-  Json::Value DumpCommandFragment(BT<std::string> const& frag,
+  Json::Value DumpCommandFragments(std::vector<JBT<std::string>> const& frags);
+  Json::Value DumpCommandFragment(JBT<std::string> const& frag,
                                   std::string const& role = std::string());
   Json::Value DumpDependencies();
   Json::Value DumpDependency(cmTargetDepend const& td);
@@ -722,16 +800,20 @@
     // which may need to be factored out.
     std::string flags;
     lg->GetTargetCompileFlags(this->GT, this->Config, lang, flags);
-    cd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+    cd.Flags.emplace_back(std::move(flags), JBTIndex());
   }
   std::set<BT<std::string>> defines =
     lg->GetTargetDefines(this->GT, this->Config, lang);
-  cd.SetDefines(defines);
+  cd.Defines.reserve(defines.size());
+  for (BT<std::string> const& d : defines) {
+    cd.Defines.emplace_back(this->ToJBT(d));
+  }
   std::vector<BT<std::string>> includePathList =
     lg->GetIncludeDirectories(this->GT, lang, this->Config);
   for (BT<std::string> const& i : includePathList) {
     cd.Includes.emplace_back(
-      i, this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang));
+      this->ToJBT(i),
+      this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang));
   }
 }
 
@@ -758,26 +840,22 @@
   if (fd.Language.empty()) {
     return fd;
   }
-  CompileData const& cd = this->CompileDataMap.at(fd.Language);
-
-  fd.Sysroot = cd.Sysroot;
 
   cmLocalGenerator* lg = this->GT->GetLocalGenerator();
   cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT,
                                                     fd.Language);
 
-  fd.Flags = cd.Flags;
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
   if (const char* cflags = sf->GetProperty(COMPILE_FLAGS)) {
     std::string flags = genexInterpreter.Evaluate(cflags, COMPILE_FLAGS);
-    fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+    fd.Flags.emplace_back(std::move(flags), JBTIndex());
   }
   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
   if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) {
     std::string flags;
     lg->AppendCompileOptions(
       flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
-    fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace());
+    fd.Flags.emplace_back(std::move(flags), JBTIndex());
   }
 
   // Add include directories from source file properties.
@@ -796,8 +874,6 @@
       }
     }
   }
-  fd.Includes.insert(fd.Includes.end(), cd.Includes.begin(),
-                     cd.Includes.end());
 
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
   std::set<std::string> fileDefines;
@@ -814,27 +890,62 @@
       genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS));
   }
 
-  std::set<BT<std::string>> defines;
-  defines.insert(fileDefines.begin(), fileDefines.end());
-  defines.insert(cd.Defines.begin(), cd.Defines.end());
-
-  fd.SetDefines(defines);
+  fd.Defines.reserve(fileDefines.size());
+  for (std::string const& d : fileDefines) {
+    fd.Defines.emplace_back(d, JBTIndex());
+  }
 
   return fd;
 }
 
+CompileData Target::MergeCompileData(CompileData const& fd)
+{
+  CompileData cd;
+  cd.Language = fd.Language;
+  if (cd.Language.empty()) {
+    return cd;
+  }
+  CompileData const& td = this->CompileDataMap.at(cd.Language);
+
+  // All compile groups share the sysroot of the target.
+  cd.Sysroot = td.Sysroot;
+
+  // Use target-wide flags followed by source-specific flags.
+  cd.Flags.reserve(td.Flags.size() + fd.Flags.size());
+  cd.Flags.insert(cd.Flags.end(), td.Flags.begin(), td.Flags.end());
+  cd.Flags.insert(cd.Flags.end(), fd.Flags.begin(), fd.Flags.end());
+
+  // Use source-specific includes followed by target-wide includes.
+  cd.Includes.reserve(fd.Includes.size() + td.Includes.size());
+  cd.Includes.insert(cd.Includes.end(), fd.Includes.begin(),
+                     fd.Includes.end());
+  cd.Includes.insert(cd.Includes.end(), td.Includes.begin(),
+                     td.Includes.end());
+
+  // Use target-wide defines followed by source-specific defines.
+  cd.Defines.reserve(td.Defines.size() + fd.Defines.size());
+  cd.Defines.insert(cd.Defines.end(), td.Defines.begin(), td.Defines.end());
+  cd.Defines.insert(cd.Defines.end(), fd.Defines.begin(), fd.Defines.end());
+
+  // De-duplicate defines.
+  std::stable_sort(cd.Defines.begin(), cd.Defines.end(),
+                   JBT<std::string>::ValueLess);
+  auto end = std::unique(cd.Defines.begin(), cd.Defines.end(),
+                         JBT<std::string>::ValueEq);
+  cd.Defines.erase(end, cd.Defines.end());
+
+  return cd;
+}
+
 Json::ArrayIndex Target::AddSourceCompileGroup(cmSourceFile* sf,
                                                Json::ArrayIndex si)
 {
-  Json::Value compileDataJson =
-    this->DumpCompileData(this->BuildCompileData(sf));
-  std::map<Json::Value, Json::ArrayIndex>::iterator i =
-    this->CompileGroupMap.find(compileDataJson);
+  CompileData compileData = this->BuildCompileData(sf);
+  auto i = this->CompileGroupMap.find(compileData);
   if (i == this->CompileGroupMap.end()) {
     Json::ArrayIndex cgIndex =
       static_cast<Json::ArrayIndex>(this->CompileGroups.size());
-    i =
-      this->CompileGroupMap.emplace(std::move(compileDataJson), cgIndex).first;
+    i = this->CompileGroupMap.emplace(std::move(compileData), cgIndex).first;
     CompileGroup g;
     g.Entry = i;
     this->CompileGroups.push_back(std::move(g));
@@ -845,9 +956,15 @@
 
 void Target::AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt)
 {
-  Json::ArrayIndex backtrace;
-  if (this->Backtraces.Add(bt, backtrace)) {
-    object["backtrace"] = backtrace;
+  if (JBTIndex backtrace = this->Backtraces.Add(bt)) {
+    object["backtrace"] = backtrace.Index;
+  }
+}
+
+void Target::AddBacktrace(Json::Value& object, JBTIndex bt)
+{
+  if (bt) {
+    object["backtrace"] = bt.Index;
   }
 }
 
@@ -937,7 +1054,7 @@
   }
   if (!cd.Defines.empty()) {
     Json::Value defines = Json::arrayValue;
-    for (BT<std::string> const& d : cd.Defines) {
+    for (JBT<std::string> const& d : cd.Defines) {
       defines.append(this->DumpDefine(d));
     }
     result["defines"] = std::move(defines);
@@ -957,7 +1074,7 @@
   return include;
 }
 
-Json::Value Target::DumpDefine(BT<std::string> const& def)
+Json::Value Target::DumpDefine(JBT<std::string> const& def)
 {
   Json::Value define = Json::objectValue;
   define["define"] = def.Value;
@@ -993,7 +1110,8 @@
 
 Json::Value Target::DumpCompileGroup(CompileGroup& cg)
 {
-  Json::Value group = cg.Entry->first;
+  Json::Value group =
+    this->DumpCompileData(this->MergeCompileData(cg.Entry->first));
   group["sourceIndexes"] = std::move(cg.SourceIndexes);
   return group;
 }
@@ -1190,16 +1308,16 @@
 }
 
 Json::Value Target::DumpCommandFragments(
-  std::vector<BT<std::string>> const& frags)
+  std::vector<JBT<std::string>> const& frags)
 {
   Json::Value commandFragments = Json::arrayValue;
-  for (BT<std::string> const& f : frags) {
+  for (JBT<std::string> const& f : frags) {
     commandFragments.append(this->DumpCommandFragment(f));
   }
   return commandFragments;
 }
 
-Json::Value Target::DumpCommandFragment(BT<std::string> const& frag,
+Json::Value Target::DumpCommandFragment(JBT<std::string> const& frag,
                                         std::string const& role)
 {
   Json::Value fragment = Json::objectValue;
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index a8ee6a8..22f0d1f 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -39,6 +39,7 @@
 #include "cmRange.h"
 #include "cmRuntimeDependencyArchive.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cm_sys_stat.h"
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index be7964a..b1ccc83 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -13,6 +13,7 @@
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmFindBase::cmFindBase()
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index db3f4ef..04fbbad 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -28,6 +28,7 @@
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmVersion.h"
 
 #if defined(__HAIKU__)
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 6afd31a..2809cf7 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -11,6 +11,7 @@
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 
 // define the class for function commands
 class cmFunctionHelperCommand
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 817f41e..df2227f 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -2,12 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionDAGChecker.h"
 
-#include "cmAlgorithms.h"
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionEvaluator.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmake.h"
 
 #include <sstream>
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 49d0c47..14dc7b8 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -19,6 +19,7 @@
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cm_static_string_view.hxx"
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 7c41045..38f34ac 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -34,6 +34,7 @@
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index 8538944..38fee28 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -4,10 +4,10 @@
 
 #include <set>
 
-#include "cmAlgorithms.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 
 class cmExecutionStatus;
 
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 7b8ffc5..88da5eb 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -337,15 +337,16 @@
   bool failed = false;
   for (cmLocalGenerator* generator : this->LocalGenerators) {
     for (cmGeneratorTarget* target : generator->GetGeneratorTargets()) {
-      std::vector<std::string> configs;
-      target->Makefile->GetConfigurations(configs);
-      if (configs.empty()) {
-        configs.emplace_back();
-      }
+      if (target->GetType() == cmStateEnums::EXECUTABLE &&
+          target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
+        std::vector<std::string> configs;
+        target->Makefile->GetConfigurations(configs);
+        if (configs.empty()) {
+          configs.emplace_back();
+        }
 
-      for (std::string const& config : configs) {
-        if (target->GetLinkerLanguage(config) == "Swift") {
-          if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
+        for (std::string const& config : configs) {
+          if (target->GetLinkerLanguage(config) == "Swift") {
             this->GetCMakeInstance()->IssueMessage(
               MessageType::FATAL_ERROR,
               "WIN32_EXECUTABLE property is not supported on Swift "
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index e36825c..ea40ebc 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -19,6 +19,7 @@
 #include "cmDuration.h"
 #include "cmExportSetMap.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 7e81a54..0b68966 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -32,6 +32,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
@@ -414,14 +415,6 @@
 
 cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm)
   : cmGlobalCommonGenerator(cm)
-  , UsingGCCOnWindows(false)
-  , ComputingUnknownDependencies(false)
-  , PolicyCMP0058(cmPolicies::WARN)
-  , NinjaSupportsConsolePool(false)
-  , NinjaSupportsImplicitOuts(false)
-  , NinjaSupportsManifestRestat(false)
-  , NinjaSupportsMultilineDepfile(false)
-  , NinjaSupportsDyndeps(0)
 {
 #ifdef _WIN32
   cm->GetState()->SetWindowsShell(true);
@@ -555,14 +548,22 @@
   this->NinjaSupportsMultilineDepfile = !cmSystemTools::VersionCompare(
     cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
     RequiredNinjaVersionForMultilineDepfile().c_str());
-  {
+  this->NinjaSupportsDyndeps = !cmSystemTools::VersionCompare(
+    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
+    RequiredNinjaVersionForDyndeps().c_str());
+  if (!this->NinjaSupportsDyndeps) {
+    // The ninja version number is not new enough to have upstream support.
     // Our ninja branch adds ".dyndep-#" to its version number,
     // where '#' is a feature-specific version number.  Extract it.
     static std::string const k_DYNDEP_ = ".dyndep-";
     std::string::size_type pos = this->NinjaVersion.find(k_DYNDEP_);
     if (pos != std::string::npos) {
       const char* fv = &this->NinjaVersion[pos + k_DYNDEP_.size()];
-      cmSystemTools::StringToULong(fv, &this->NinjaSupportsDyndeps);
+      unsigned long dyndep = 0;
+      cmSystemTools::StringToULong(fv, &dyndep);
+      if (dyndep == 1) {
+        this->NinjaSupportsDyndeps = true;
+      }
     }
   }
 }
@@ -579,37 +580,25 @@
 
 bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const
 {
-  if (this->NinjaSupportsDyndeps == 1) {
+  if (this->NinjaSupportsDyndeps) {
     return true;
   }
 
   std::ostringstream e;
-  if (this->NinjaSupportsDyndeps == 0) {
-    /* clang-format off */
-    e <<
-      "The Ninja generator does not support Fortran using Ninja version\n"
-      "  " + this->NinjaVersion + "\n"
-      "due to lack of required features.  "
-      "Kitware has implemented the required features but as of this version "
-      "of CMake they have not been integrated to upstream ninja.  "
-      "Pending integration, Kitware maintains a branch at:\n"
-      "  https://github.com/Kitware/ninja/tree/features-for-fortran#readme\n"
-      "with the required features.  "
-      "One may build ninja from that branch to get support for Fortran."
-      ;
-    /* clang-format on */
-  } else {
-    /* clang-format off */
-    e <<
-      "The Ninja generator in this version of CMake does not support Fortran "
-      "using Ninja version\n"
-      "  " + this->NinjaVersion + "\n"
-      "because its 'dyndep' feature version is " <<
-      this->NinjaSupportsDyndeps << ".  "
-      "This version of CMake is aware only of 'dyndep' feature version 1."
-      ;
-    /* clang-format on */
-  }
+  /* clang-format off */
+  e <<
+    "The Ninja generator does not support Fortran using Ninja version\n"
+    "  " + this->NinjaVersion + "\n"
+    "due to lack of required features.  "
+    "Kitware has implemented the required features and they have been "
+    "merged to upstream ninja for inclusion in Ninja 1.10 and higher.  "
+    "As of this version of CMake, Ninja 1.10 has not been released.  "
+    "Meanwhile, Kitware maintains a branch of Ninja at:\n"
+    "  https://github.com/Kitware/ninja/tree/features-for-fortran#readme\n"
+    "with the required features.  "
+    "One may build ninja from that branch to get support for Fortran."
+    ;
+  /* clang-format on */
   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
   cmSystemTools::SetFatalErrorOccured();
   return false;
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 99afc1d..db64031 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -322,6 +322,7 @@
   {
     return "1.9";
   }
+  static std::string RequiredNinjaVersionForDyndeps() { return "1.10"; }
   bool SupportsConsolePool() const;
   bool SupportsImplicitOuts() const;
   bool SupportsManifestRestat() const;
@@ -402,7 +403,7 @@
   /// The set of dependencies to add to the "all" target.
   cmNinjaDeps AllDependencies;
 
-  bool UsingGCCOnWindows;
+  bool UsingGCCOnWindows = false;
 
   /// The set of custom commands we have seen.
   std::set<cmCustomCommand const*> CustomCommands;
@@ -412,8 +413,8 @@
 
   /// Whether we are collecting known build outputs and needed
   /// dependencies to determine unknown dependencies.
-  bool ComputingUnknownDependencies;
-  cmPolicies::PolicyStatus PolicyCMP0058;
+  bool ComputingUnknownDependencies = false;
+  cmPolicies::PolicyStatus PolicyCMP0058 = cmPolicies::WARN;
 
   /// The combined explicit dependencies of custom build commands
   std::set<std::string> CombinedCustomCommandExplicitDependencies;
@@ -435,11 +436,11 @@
 
   std::string NinjaCommand;
   std::string NinjaVersion;
-  bool NinjaSupportsConsolePool;
-  bool NinjaSupportsImplicitOuts;
-  bool NinjaSupportsManifestRestat;
-  bool NinjaSupportsMultilineDepfile;
-  unsigned long NinjaSupportsDyndeps;
+  bool NinjaSupportsConsolePool = false;
+  bool NinjaSupportsImplicitOuts = false;
+  bool NinjaSupportsManifestRestat = false;
+  bool NinjaSupportsMultilineDepfile = false;
+  bool NinjaSupportsDyndeps = false;
 
 private:
   void InitOutputPathPrefix();
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 3b0659c..aca7268 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -9,7 +9,6 @@
 #include <stddef.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
 #include "cmExportSet.h"
 #include "cmExportSetMap.h"
@@ -27,6 +26,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 7850977..a278925 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -4,11 +4,11 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 8d2add6..656907a 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -17,6 +16,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmOutputConverter;
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index d024256..8c14596 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -23,6 +23,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 242ffe3..28ae82e 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -21,6 +21,7 @@
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTestGenerator.h"
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 1ec1fd9..713c985 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -32,6 +32,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmake.h"
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 22748b4..1f2b5b2 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -14,6 +14,7 @@
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 // define the class for macro commands
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 0fb3237..8188ffa 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3061,15 +3061,7 @@
     return false;
   }
 
-  // loop over all function blockers to see if any block this command
-  // evaluate in reverse, this is critical for balanced IF statements etc
-  for (auto const& pos : cmReverseRange(this->FunctionBlockers)) {
-    if (pos->IsFunctionBlocked(lff, *this, status)) {
-      return true;
-    }
-  }
-
-  return false;
+  return this->FunctionBlockers.top()->IsFunctionBlocked(lff, *this, status);
 }
 
 void cmMakefile::PushFunctionBlockerBarrier()
@@ -3084,8 +3076,8 @@
     this->FunctionBlockerBarriers.back();
   while (this->FunctionBlockers.size() > barrier) {
     std::unique_ptr<cmFunctionBlocker> fb(
-      std::move(this->FunctionBlockers.back()));
-    this->FunctionBlockers.pop_back();
+      std::move(this->FunctionBlockers.top()));
+    this->FunctionBlockers.pop();
     if (reportError) {
       // Report the context in which the unclosed block was opened.
       cmListFileContext const& lfc = fb->GetStartingContext();
@@ -3216,46 +3208,36 @@
     fb->SetStartingContext(this->GetExecutionContext());
   }
 
-  this->FunctionBlockers.push_back(std::move(fb));
+  this->FunctionBlockers.push(std::move(fb));
 }
 
 std::unique_ptr<cmFunctionBlocker> cmMakefile::RemoveFunctionBlocker(
   cmFunctionBlocker* fb, const cmListFileFunction& lff)
 {
-  // Find the function blocker stack barrier for the current scope.
-  // We only remove a blocker whose index is not less than the barrier.
-  FunctionBlockersType::size_type barrier = 0;
-  if (!this->FunctionBlockerBarriers.empty()) {
-    barrier = this->FunctionBlockerBarriers.back();
+  assert(!this->FunctionBlockers.empty());
+  assert(this->FunctionBlockers.top().get() == fb);
+  assert(this->FunctionBlockerBarriers.empty() ||
+         this->FunctionBlockers.size() > this->FunctionBlockerBarriers.back());
+
+  // Warn if the arguments do not match, but always remove.
+  if (!fb->ShouldRemove(lff, *this)) {
+    cmListFileContext const& lfc = fb->GetStartingContext();
+    cmListFileContext closingContext =
+      cmListFileContext::FromCommandContext(lff, lfc.FilePath);
+    std::ostringstream e;
+    /* clang-format off */
+    e << "A logical block opening on the line\n"
+      << "  " << lfc << "\n"
+      << "closes on the line\n"
+      << "  " << closingContext << "\n"
+      << "with mis-matching arguments.";
+    /* clang-format on */
+    this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
   }
 
-  // Search for the function blocker whose scope this command ends.
-  for (FunctionBlockersType::size_type i = this->FunctionBlockers.size();
-       i > barrier; --i) {
-    auto pos = this->FunctionBlockers.begin() + (i - 1);
-    if (pos->get() == fb) {
-      // Warn if the arguments do not match, but always remove.
-      if (!(*pos)->ShouldRemove(lff, *this)) {
-        cmListFileContext const& lfc = fb->GetStartingContext();
-        cmListFileContext closingContext =
-          cmListFileContext::FromCommandContext(lff, lfc.FilePath);
-        std::ostringstream e;
-        /* clang-format off */
-        e << "A logical block opening on the line\n"
-          << "  " << lfc << "\n"
-          << "closes on the line\n"
-          << "  " << closingContext << "\n"
-          << "with mis-matching arguments.";
-        /* clang-format on */
-        this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
-      }
-      std::unique_ptr<cmFunctionBlocker> b = std::move(*pos);
-      this->FunctionBlockers.erase(pos);
-      return b;
-    }
-  }
-
-  return std::unique_ptr<cmFunctionBlocker>();
+  auto b = std::move(this->FunctionBlockers.top());
+  this->FunctionBlockers.pop();
+  return b;
 }
 
 std::string const& cmMakefile::GetHomeDirectory() const
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 18b81d4..dc196ac 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -20,7 +20,6 @@
 #include "cm_string_view.hxx"
 
 #include "cmAlgorithms.h"
-#include "cmFunctionBlocker.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 #include "cmNewLineStyle.h"
@@ -28,6 +27,7 @@
 #include "cmSourceFileLocationKind.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
@@ -39,6 +39,7 @@
 class cmExecutionStatus;
 class cmExpandedCommandArgument;
 class cmExportBuildFileGenerator;
+class cmFunctionBlocker;
 class cmGeneratorExpressionEvaluationFile;
 class cmGlobalGenerator;
 class cmInstallGenerator;
@@ -963,7 +964,9 @@
   bool EnforceUniqueDir(const std::string& srcPath,
                         const std::string& binPath) const;
 
-  typedef std::vector<std::unique_ptr<cmFunctionBlocker>> FunctionBlockersType;
+  using FunctionBlockerPtr = std::unique_ptr<cmFunctionBlocker>;
+  using FunctionBlockersType =
+    std::stack<FunctionBlockerPtr, std::vector<FunctionBlockerPtr>>;
   FunctionBlockersType FunctionBlockers;
   std::vector<FunctionBlockersType::size_type> FunctionBlockerBarriers;
   void PushFunctionBlockerBarrier();
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index b5a6246..008248c 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -28,6 +28,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx
index 58f3b2f..66d3c88 100644
--- a/Source/cmMessageCommand.cxx
+++ b/Source/cmMessageCommand.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMessageCommand.h"
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmMessenger.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx
index 0d2b21f..07d011e 100644
--- a/Source/cmMessenger.cxx
+++ b/Source/cmMessenger.cxx
@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMessenger.h"
 
-#include "cmAlgorithms.h"
 #include "cmDocumentationFormatter.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 88040f8..cd84c03 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -33,6 +33,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 08c92ff..8b0a6ba 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -31,6 +31,7 @@
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmOptionCommand.cxx b/Source/cmOptionCommand.cxx
index 52f63a3..3724ba7 100644
--- a/Source/cmOptionCommand.cxx
+++ b/Source/cmOptionCommand.cxx
@@ -4,13 +4,13 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx
index f3276ec..a66af5a 100644
--- a/Source/cmOutputRequiredFilesCommand.cxx
+++ b/Source/cmOutputRequiredFilesCommand.cxx
@@ -11,6 +11,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 0f8aef7..04fa0fb 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -7,11 +7,11 @@
 #include <sstream>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cm_string_view.hxx"
 
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index ec40136..51a61dc 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -1,10 +1,10 @@
 #include "cmPolicies.h"
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 1159ef7..96d9843 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -10,11 +10,11 @@
 #include <sstream>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 3683edd..712e22c 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -5,6 +5,7 @@
 #include "cmAlgorithms.h"
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 4b12419..da6094d 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -4,7 +4,6 @@
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenGlobalInitializer.h"
 
-#include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
 #include "cmFilePathChecksum.h"
@@ -23,6 +22,7 @@
 #include "cmSourceGroup.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 44d2db0..2aefe8f 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -16,6 +16,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
 #include "cmQtAutoGen.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 #include "cmsys/FStream.hxx"
diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index 20885df..59f632d 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -11,6 +11,7 @@
 #include "cmFileLockResult.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 // -- Class methods
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index 2064275..a329f7d 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -4,6 +4,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx
index f98984e..879cc95 100644
--- a/Source/cmSearchPath.cxx
+++ b/Source/cmSearchPath.cxx
@@ -6,9 +6,9 @@
 #include <cassert>
 #include <utility>
 
-#include "cmAlgorithms.h"
 #include "cmFindCommon.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx
index b36878c..1a12785 100644
--- a/Source/cmSetCommand.cxx
+++ b/Source/cmSetCommand.cxx
@@ -2,12 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSetCommand.h"
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx
index d887627..6e2e820 100644
--- a/Source/cmSourceFileLocation.cxx
+++ b/Source/cmSourceFileLocation.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSourceFileLocation.h"
 
-#include "cmAlgorithms.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx
index 5cdacaa..04b4d72 100644
--- a/Source/cmSourceGroupCommand.cxx
+++ b/Source/cmSourceGroupCommand.cxx
@@ -63,15 +63,6 @@
   return true;
 }
 
-std::string prepareFilePathForTree(const std::string& path,
-                                   const std::string& currentSourceDir)
-{
-  if (!cmSystemTools::FileIsFullPath(path)) {
-    return cmSystemTools::CollapseFullPath(currentSourceDir + "/" + path);
-  }
-  return cmSystemTools::CollapseFullPath(path);
-}
-
 std::vector<std::string> prepareFilesPathsForTree(
   const std::vector<std::string>& filesPaths,
   const std::string& currentSourceDir)
@@ -80,9 +71,11 @@
   prepared.reserve(filesPaths.size());
 
   for (auto const& filePath : filesPaths) {
+    std::string fullPath =
+      cmSystemTools::CollapseFullPath(filePath, currentSourceDir);
     // If provided file path is actually not a file, silently ignore it.
-    if (cmSystemTools::FileExists(filePath, /*isFile=*/true)) {
-      prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir));
+    if (cmSystemTools::FileExists(fullPath, /*isFile=*/true)) {
+      prepared.emplace_back(std::move(fullPath));
     }
   }
 
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index 6f9c935..1ea72e1 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -11,7 +11,6 @@
 
 #include "cm_memory.hxx"
 
-#include "cmAlgorithms.h"
 #include "cmCacheManager.h"
 #include "cmCommand.h"
 #include "cmDefinitions.h"
@@ -22,6 +21,7 @@
 #include "cmMakefile.h"
 #include "cmStatePrivate.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h
index 6956594..fe15563 100644
--- a/Source/cmStateDirectory.h
+++ b/Source/cmStateDirectory.h
@@ -14,6 +14,7 @@
 #include "cmListFileCache.h"
 #include "cmStatePrivate.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 
 class cmStateDirectory
 {
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
new file mode 100644
index 0000000..149e0ad
--- /dev/null
+++ b/Source/cmStringAlgorithms.h
@@ -0,0 +1,139 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmStringAlgorithms_h
+#define cmStringAlgorithms_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmRange.h"
+#include "cm_string_view.hxx"
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <string.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+typedef cmRange<std::vector<std::string>::const_iterator> cmStringRange;
+
+struct cmStrCmp
+{
+  cmStrCmp(const char* test)
+    : m_test(test)
+  {
+  }
+  cmStrCmp(std::string test)
+    : m_test(std::move(test))
+  {
+  }
+
+  bool operator()(const std::string& input) const { return m_test == input; }
+
+  bool operator()(const char* input) const
+  {
+    return strcmp(input, m_test.c_str()) == 0;
+  }
+
+private:
+  const std::string m_test;
+};
+
+template <typename Range>
+std::string cmJoin(Range const& r, const char* delimiter)
+{
+  if (r.empty()) {
+    return std::string();
+  }
+  std::ostringstream os;
+  typedef typename Range::value_type ValueType;
+  typedef typename Range::const_iterator InputIt;
+  const InputIt first = r.begin();
+  InputIt last = r.end();
+  --last;
+  std::copy(first, last, std::ostream_iterator<ValueType>(os, delimiter));
+
+  os << *last;
+
+  return os.str();
+}
+
+template <typename Range>
+std::string cmJoin(Range const& r, std::string const& delimiter)
+{
+  return cmJoin(r, delimiter.c_str());
+}
+
+template <typename Range>
+std::string cmWrap(std::string const& prefix, Range const& r,
+                   std::string const& suffix, std::string const& sep)
+{
+  if (r.empty()) {
+    return std::string();
+  }
+  return prefix + cmJoin(r, suffix + sep + prefix) + suffix;
+}
+
+template <typename Range>
+std::string cmWrap(char prefix, Range const& r, char suffix,
+                   std::string const& sep)
+{
+  return cmWrap(std::string(1, prefix), r, std::string(1, suffix), sep);
+}
+
+/** Returns true if string @a str starts with the character @a prefix.  **/
+inline bool cmHasPrefix(cm::string_view str, char prefix)
+{
+  return !str.empty() && (str.front() == prefix);
+}
+
+/** Returns true if string @a str starts with string @a prefix.  **/
+inline bool cmHasPrefix(cm::string_view str, cm::string_view prefix)
+{
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+/** Returns true if string @a str starts with string @a prefix.  **/
+template <size_t N>
+inline bool cmHasLiteralPrefix(cm::string_view str, const char (&prefix)[N])
+{
+  return cmHasPrefix(str, cm::string_view(prefix, N - 1));
+}
+
+/** Returns true if string @a str ends with the character @a suffix.  **/
+inline bool cmHasSuffix(cm::string_view str, char suffix)
+{
+  return !str.empty() && (str.back() == suffix);
+}
+
+/** Returns true if string @a str ends with string @a suffix.  **/
+inline bool cmHasSuffix(cm::string_view str, cm::string_view suffix)
+{
+  return str.size() >= suffix.size() &&
+    str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+/** Returns true if string @a str ends with string @a suffix.  **/
+template <size_t N>
+inline bool cmHasLiteralSuffix(cm::string_view str, const char (&suffix)[N])
+{
+  return cmHasSuffix(str, cm::string_view(suffix, N - 1));
+}
+
+/** Removes an existing suffix character of from the string @a str.  **/
+inline void cmStripSuffixIfExists(std::string& str, char suffix)
+{
+  if (cmHasSuffix(str, suffix)) {
+    str.pop_back();
+  }
+}
+
+/** Removes an existing suffix string of from the string @a str.  **/
+inline void cmStripSuffixIfExists(std::string& str, cm::string_view suffix)
+{
+  if (cmHasSuffix(str, suffix)) {
+    str.resize(str.size() - suffix.size());
+  }
+}
+
+#endif
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 259d92a..8b3b1e3 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -18,6 +18,7 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index e0005a0..5f4e1fc 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -6,6 +6,7 @@
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cm_uv.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index a808bb4..4e5141b 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -16,6 +16,7 @@
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmTargetLinkLibraryType.h"
 
 class cmCustomCommand;
diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx
index c4dc838..b64646a 100644
--- a/Source/cmTargetCompileDefinitionsCommand.cxx
+++ b/Source/cmTargetCompileDefinitionsCommand.cxx
@@ -4,9 +4,9 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx
index c9e394b..976c8cb 100644
--- a/Source/cmTargetCompileFeaturesCommand.cxx
+++ b/Source/cmTargetCompileFeaturesCommand.cxx
@@ -4,9 +4,9 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 
 class cmExecutionStatus;
 class cmTarget;
diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx
index 8b4763a..7dadb82 100644
--- a/Source/cmTargetCompileOptionsCommand.cxx
+++ b/Source/cmTargetCompileOptionsCommand.cxx
@@ -4,10 +4,10 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx
index 269f751..435c392 100644
--- a/Source/cmTargetLinkDirectoriesCommand.cxx
+++ b/Source/cmTargetLinkDirectoriesCommand.cxx
@@ -4,11 +4,11 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
diff --git a/Source/cmTargetLinkOptionsCommand.cxx b/Source/cmTargetLinkOptionsCommand.cxx
index 5366486..2866cf1 100644
--- a/Source/cmTargetLinkOptionsCommand.cxx
+++ b/Source/cmTargetLinkOptionsCommand.cxx
@@ -4,10 +4,10 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx
index eac300f..baab8da 100644
--- a/Source/cmTargetPropertyComputer.cxx
+++ b/Source/cmTargetPropertyComputer.cxx
@@ -11,6 +11,7 @@
 #include "cmMessenger.h"
 #include "cmPolicies.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 
 bool cmTargetPropertyComputer::HandleLocationPropertyPolicy(
   std::string const& tgtName, cmMessenger* messenger,
diff --git a/Source/cmTargetPropertyComputer.h b/Source/cmTargetPropertyComputer.h
index efbf95f..3b11acd 100644
--- a/Source/cmTargetPropertyComputer.h
+++ b/Source/cmTargetPropertyComputer.h
@@ -7,9 +7,9 @@
 
 #include <string>
 
-#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmMessenger;
diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx
index 11e288f..eb5f37c 100644
--- a/Source/cmTargetSourcesCommand.cxx
+++ b/Source/cmTargetSourcesCommand.cxx
@@ -4,11 +4,11 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
diff --git a/Source/cmUnsetCommand.cxx b/Source/cmUnsetCommand.cxx
index cfaa1fd2..0e903c7 100644
--- a/Source/cmUnsetCommand.cxx
+++ b/Source/cmUnsetCommand.cxx
@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmUnsetCommand.h"
 
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 4a151b3..ed6e4d9 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -1235,8 +1235,11 @@
   if (this->IPOEnabledConfigurations.count(config) > 0) {
     e1.Element("WholeProgramOptimization", "true");
   }
-  if (this->SpectreMitigationConfigurations.count(config) > 0) {
-    e1.Element("SpectreMitigation", "Spectre");
+  {
+    auto s = this->SpectreMitigation.find(config);
+    if (s != this->SpectreMitigation.end()) {
+      e1.Element("SpectreMitigation", s->second);
+    }
   }
 }
 
@@ -2766,8 +2769,8 @@
     }
   }
 
-  if (clOptions.HasFlag("SpectreMitigation")) {
-    this->SpectreMitigationConfigurations.insert(configName);
+  if (const char* s = clOptions.GetFlag("SpectreMitigation")) {
+    this->SpectreMitigation[configName] = s;
     clOptions.RemoveFlag("SpectreMitigation");
   }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 860b809..6607e77 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -215,7 +215,7 @@
   unsigned int NsightTegraVersion[4];
   bool TargetCompileAsWinRT;
   std::set<std::string> IPOEnabledConfigurations;
-  std::set<std::string> SpectreMitigationConfigurations;
+  std::map<std::string, std::string> SpectreMitigation;
   cmGlobalVisualStudio10Generator* const GlobalGenerator;
   cmLocalVisualStudio10Generator* const LocalGenerator;
   std::set<std::string> CSharpCustomCommandNames;
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index eb57947..b97152f 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -21,6 +21,7 @@
 #include "cmMessenger.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index cfb3cee..10a6825 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -7,6 +7,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 #include "cmcmd.h"
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 503dce1..aecc978 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -13,6 +13,7 @@
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmUtils.hxx"
 #include "cmVersion.h"
diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt
index 1aeab8b..19f3f79 100644
--- a/Tests/CMakeOnly/CMakeLists.txt
+++ b/Tests/CMakeOnly/CMakeLists.txt
@@ -84,5 +84,5 @@
 endfunction()
 
 add_major_test(PythonLibs VERSIONS 2 3 VERSION_VAR PYTHONLIBS_VERSION_STRING)
-add_major_test(PythonInterp NOLANG VERSIONS 2 3 VERSION_VAR PYTHON_VERSION_STRING)
+add_major_test(PythonInterp NOLANG VERSIONS 3 VERSION_VAR PYTHON_VERSION_STRING)
 add_major_test(Qt VERSIONS 3 4 VERSION_VAR QT_VERSION_STRING)
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 5b2c7cb..1cb4ce5 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -28,3 +28,7 @@
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   run_cmake(VsJustMyCode)
 endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.20)
+  run_cmake(VsSpectreMitigation)
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsSpectreMitigation-check.cmake b/Tests/RunCMake/VS10Project/VsSpectreMitigation-check.cmake
new file mode 100644
index 0000000..6117763
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSpectreMitigation-check.cmake
@@ -0,0 +1,30 @@
+macro(VsSpectreMitigation_check tgt spectre_expect)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
+    return()
+  endif()
+
+  set(HAVE_SpectreMitigation 0)
+
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<SpectreMitigation>([^<>]+)</SpectreMitigation>")
+      set(spectre_actual "${CMAKE_MATCH_1}")
+      if(NOT "${spectre_actual}" STREQUAL "${spectre_expect}")
+        set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has <SpectreMitigation> '${spectre_actual}', not '${spectre_expect}'.")
+        return()
+      endif()
+      set(HAVE_SpectreMitigation 1)
+      break()
+    endif()
+  endforeach()
+
+  if(NOT HAVE_SpectreMitigation AND NOT "${spectre_expect}" STREQUAL "")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a <SpectreMitigation> field.")
+    return()
+  endif()
+endmacro()
+
+VsSpectreMitigation_check(SpectreMitigationOn-C "Spectre")
+VsSpectreMitigation_check(SpectreMitigationOff-C "false")
diff --git a/Tests/RunCMake/VS10Project/VsSpectreMitigation.cmake b/Tests/RunCMake/VS10Project/VsSpectreMitigation.cmake
new file mode 100644
index 0000000..b3779d7
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSpectreMitigation.cmake
@@ -0,0 +1,8 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+enable_language(C)
+
+add_library(SpectreMitigationOn-C empty.c)
+target_compile_options(SpectreMitigationOn-C PRIVATE -Qspectre)
+
+add_library(SpectreMitigationOff-C empty.c)
+target_compile_options(SpectreMitigationOff-C PRIVATE -Qspectre-)
diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt
index e5f8588..f4cbac2 100644
--- a/Tests/SwiftOnly/CMakeLists.txt
+++ b/Tests/SwiftOnly/CMakeLists.txt
@@ -8,3 +8,6 @@
 endif()
 
 add_executable(SwiftOnly main.swift)
+
+# Dummy to make sure generation works with such targets.
+add_library(SwiftIface INTERFACE)
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index ce4cfaf..f52caed 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -7,7 +7,7 @@
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
-  include(${CMake_SOURCE_DIR}/Source/CMakeVersionCompute.cmake)
+  include(${CMake_SOURCE_DIR}/Source/CMakeVersion.cmake)
   include(${CMake_SOURCE_DIR}/Source/CMakeInstallDestinations.cmake)
   unset(CMAKE_DATA_DIR)
   unset(CMAKE_DATA_DIR CACHE)
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index e4e6e05..736a7c0 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -7,7 +7,7 @@
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
-  include(${CMake_SOURCE_DIR}/Source/CMakeVersionCompute.cmake)
+  include(${CMake_SOURCE_DIR}/Source/CMakeVersion.cmake)
   include(${CMake_SOURCE_DIR}/Source/CMakeInstallDestinations.cmake)
   unset(CMAKE_DATA_DIR)
   unset(CMAKE_DATA_DIR CACHE)