Merge topic 'control-block3'

41364824ad cmFunctionBlocker: Recycle functions
6491270e0d cmFunctionBlocker: Move check for matching args
af24e4ef6e cmFunctionBlocker: Move common logic to base
ef38ff22f7 cm*FunctionBlocker: Extract function Replay
b51fba6298 cmMakefile: Add OnExecuteCommand callback
c76500949d cm*FunctionBlocker: Move to source file

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3632
diff --git a/.clang-tidy b/.clang-tidy
index bfcb67c..57e571a 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -3,16 +3,21 @@
 bugprone-*,\
 -bugprone-macro-parentheses,\
 -bugprone-misplaced-widening-cast,\
+-bugprone-narrowing-conversions,\
+-bugprone-too-small-loop-variable,\
 google-readability-casting,\
 misc-*,\
 -misc-incorrect-roundings,\
 -misc-macro-parentheses,\
 -misc-misplaced-widening-cast,\
+-misc-non-private-member-variables-in-classes,\
 -misc-static-assert,\
 modernize-*,\
+-modernize-avoid-c-arrays,\
 -modernize-deprecated-headers,\
 -modernize-return-braced-init-list,\
 -modernize-use-auto,\
+-modernize-use-nodiscard,\
 -modernize-use-noexcept,\
 -modernize-use-transparent-functors,\
 -modernize-use-using,\
@@ -24,8 +29,11 @@
 -readability-implicit-bool-cast,\
 -readability-implicit-bool-conversion,\
 -readability-inconsistent-declaration-parameter-name,\
+-readability-isolate-declaration,\
+-readability-magic-numbers,\
 -readability-named-parameter,\
 -readability-redundant-declaration,\
+-readability-uppercase-literal-suffix,\
 "
 HeaderFilterRegex: 'Source/cm[^/]*\.(h|hxx|cxx)$'
 CheckOptions:
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/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 1914f52..0fcbbb7 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -138,7 +138,7 @@
 
   set(_variant "")
   if("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xClang")
-    if(CMAKE_HOST_WIN32 AND "x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC")
+    if("x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC")
       if(CMAKE_GENERATOR MATCHES "Visual Studio")
         set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "MSVC")
       else()
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/Modules/Platform/Windows-OpenWatcom.cmake b/Modules/Platform/Windows-OpenWatcom.cmake
index d38d616..76cd28b 100644
--- a/Modules/Platform/Windows-OpenWatcom.cmake
+++ b/Modules/Platform/Windows-OpenWatcom.cmake
@@ -10,7 +10,7 @@
 
 set(CMAKE_LIBRARY_PATH_FLAG "libpath ")
 set(CMAKE_LINK_LIBRARY_FLAG "library ")
-set(CMAKE_LINK_LIBRARY_FILE_FLAG "library")
+set(CMAKE_LINK_LIBRARY_FILE_FLAG "library ")
 
 if(CMAKE_VERBOSE_MAKEFILE)
   set(CMAKE_WCL_QUIET)
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index a6babe5..b1f4ca5 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -404,6 +404,7 @@
   cmStateSnapshot.cxx
   cmStateSnapshot.h
   cmStateTypes.h
+  cmStringAlgorithms.cxx
   cmStringAlgorithms.h
   cmSystemTools.cxx
   cmSystemTools.h
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 63a4207..3bbbd28 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 15)
-set(CMake_VERSION_PATCH 20190730)
+set(CMake_VERSION_PATCH 20190805)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 512ac7a..3fd124b 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -21,6 +21,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmVersion.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLSafe.h"
@@ -772,7 +773,7 @@
     // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory
     // exists:
     //
-    if (cmSystemTools::StringStartsWith(dir.c_str(), "/")) {
+    if (cmHasLiteralPrefix(dir, "/")) {
       dir = tempInstallDirectory + dir;
     } else {
       dir = tempInstallDirectory + "/" + dir;
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index f6028c4..54fe612 100644
--- a/Source/CTest/cmCTestCoverageHandler.cxx
+++ b/Source/CTest/cmCTestCoverageHandler.cxx
@@ -13,6 +13,7 @@
 #include "cmParseGTMCoverage.h"
 #include "cmParseJacocoCoverage.h"
 #include "cmParsePHPCoverage.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
@@ -1181,7 +1182,7 @@
         // gcov 4.7 can have output lines saying "No executable lines" and
         // "Removing 'filename.gcov'"... Don't log those as "errors."
         if (line != "No executable lines" &&
-            !cmSystemTools::StringStartsWith(line.c_str(), "Removing ")) {
+            !cmHasLiteralPrefix(line, "Removing ")) {
           cmCTestLog(this->CTest, ERROR_MESSAGE,
                      "Unknown gcov output line: [" << line << "]"
                                                    << std::endl);
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 237ca82..bbf490e 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -16,6 +16,7 @@
 #include "cmProcessOutput.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
@@ -594,7 +595,7 @@
 bool cmCTestLaunch::MatchesFilterPrefix(std::string const& line) const
 {
   return !this->OptionFilterPrefix.empty() &&
-    cmSystemTools::StringStartsWith(line, this->OptionFilterPrefix.c_str());
+    cmHasPrefix(line, this->OptionFilterPrefix);
 }
 
 int cmCTestLaunch::Main(int argc, const char* const argv[])
diff --git a/Source/CTest/cmParseBlanketJSCoverage.cxx b/Source/CTest/cmParseBlanketJSCoverage.cxx
index 63d6a15..b74decb 100644
--- a/Source/CTest/cmParseBlanketJSCoverage.cxx
+++ b/Source/CTest/cmParseBlanketJSCoverage.cxx
@@ -110,7 +110,8 @@
 {
 }
 
-bool cmParseBlanketJSCoverage::LoadCoverageData(std::vector<std::string> files)
+bool cmParseBlanketJSCoverage::LoadCoverageData(
+  std::vector<std::string> const& files)
 {
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Found " << files.size() << " Files" << std::endl,
diff --git a/Source/CTest/cmParseBlanketJSCoverage.h b/Source/CTest/cmParseBlanketJSCoverage.h
index 696121f..cd1b225 100644
--- a/Source/CTest/cmParseBlanketJSCoverage.h
+++ b/Source/CTest/cmParseBlanketJSCoverage.h
@@ -29,7 +29,7 @@
 public:
   cmParseBlanketJSCoverage(cmCTestCoverageHandlerContainer& cont,
                            cmCTest* ctest);
-  bool LoadCoverageData(std::vector<std::string> files);
+  bool LoadCoverageData(std::vector<std::string> const& files);
   //  Read the JSON output
   bool ReadJSONFile(std::string const& file);
 
diff --git a/Source/CTest/cmParseJacocoCoverage.cxx b/Source/CTest/cmParseJacocoCoverage.cxx
index b78142a..5f1e712 100644
--- a/Source/CTest/cmParseJacocoCoverage.cxx
+++ b/Source/CTest/cmParseJacocoCoverage.cxx
@@ -2,6 +2,7 @@
 
 #include "cmCTest.h"
 #include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 
@@ -118,7 +119,7 @@
     // Check if any of the locations found match our package.
     for (std::string const& f : files) {
       std::string dir = cmsys::SystemTools::GetParentDirectory(f);
-      if (cmsys::SystemTools::StringEndsWith(dir, this->PackageName.c_str())) {
+      if (cmHasSuffix(dir, this->PackageName)) {
         cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                            "Found package directory for " << fileName << ": "
                                                           << dir << std::endl,
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 610e9f9..026250d 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -1965,7 +1965,7 @@
   if (this->CheckArgument(arg, "-N", "--show-only")) {
     this->Impl->ShowOnly = true;
   }
-  if (cmSystemTools::StringStartsWith(arg.c_str(), "--show-only=")) {
+  if (cmHasLiteralPrefix(arg, "--show-only=")) {
     this->Impl->ShowOnly = true;
 
     // Check if a specific format is requested. Defaults to human readable
@@ -2227,7 +2227,7 @@
     // attempts are simply ignored since previous ctest versions ignore
     // this too. (As well as many other unknown command line args.)
     //
-    if (arg != "-D" && cmSystemTools::StringStartsWith(arg.c_str(), "-D")) {
+    if (arg != "-D" && cmHasLiteralPrefix(arg, "-D")) {
       std::string input = arg.substr(2);
       this->AddVariableDefinition(input);
     }
diff --git a/Source/cmCallVisualStudioMacro.cxx b/Source/cmCallVisualStudioMacro.cxx
index f7a2244..9e152ff 100644
--- a/Source/cmCallVisualStudioMacro.cxx
+++ b/Source/cmCallVisualStudioMacro.cxx
@@ -4,6 +4,7 @@
 
 #include <sstream>
 
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #if defined(_MSC_VER)
@@ -328,8 +329,7 @@
   if (SUCCEEDED(hr)) {
     std::map<std::string, IUnknownPtr>::iterator it;
     for (it = mrot.begin(); it != mrot.end(); ++it) {
-      if (cmSystemTools::StringStartsWith(it->first.c_str(),
-                                          "!VisualStudio.DTE.")) {
+      if (cmHasLiteralPrefix(it->first, "!VisualStudio.DTE.")) {
         IDispatchPtr disp(it->second);
         if (disp != (IDispatch*)0) {
           std::string slnName;
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 78cddf0..5f46631 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -990,11 +990,6 @@
     return;
   }
 
-  // If this platform wants a flag before the full path, add it.
-  if (!this->LibLinkFileFlag.empty()) {
-    this->Items.emplace_back(this->LibLinkFileFlag, false);
-  }
-
   // For compatibility with CMake 2.4 include the item's directory in
   // the linker search path.
   if (this->OldLinkDirMode && !target->IsFrameworkOnApple() &&
@@ -1057,11 +1052,6 @@
     this->OldLinkDirItems.push_back(item);
   }
 
-  // If this platform wants a flag before the full path, add it.
-  if (!this->LibLinkFileFlag.empty()) {
-    this->Items.emplace_back(this->LibLinkFileFlag, false);
-  }
-
   // Now add the full path to the library.
   this->Items.emplace_back(item, true);
 }
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 3be2c7f..784d3fa 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -56,6 +56,11 @@
   std::string GetChrpathString() const;
   std::set<cmGeneratorTarget const*> const& GetSharedLibrariesLinked() const;
 
+  std::string const& GetLibLinkFileFlag() const
+  {
+    return this->LibLinkFileFlag;
+  }
+
   std::string const& GetRPathLinkFlag() const { return this->RPathLinkFlag; }
   std::string GetRPathLinkString() const;
 
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 8fd2947..87f9b4c 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -1209,8 +1209,8 @@
         targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
     for (auto& prop : cmSystemTools::ExpandedListArgument(exportProperties)) {
       /* Black list reserved properties */
-      if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") ||
-          cmSystemTools::StringStartsWith(prop, "INTERFACE_")) {
+      if (cmHasLiteralPrefix(prop, "IMPORTED_") ||
+          cmHasLiteralPrefix(prop, "INTERFACE_")) {
         std::ostringstream e;
         e << "Target \"" << gte->Target->GetName() << "\" contains property \""
           << prop << "\" in EXPORT_PROPERTIES but IMPORTED_* and INTERFACE_* "
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index aa84396..2594287 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -684,7 +684,6 @@
 
 Json::Value cmFileAPI::BuildCodeModel(Object const& object)
 {
-  using namespace std::placeholders;
   Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
   codemodel["kind"] = this->ObjectKindName(object.Kind);
 
@@ -719,7 +718,6 @@
 
 Json::Value cmFileAPI::BuildCache(Object const& object)
 {
-  using namespace std::placeholders;
   Json::Value cache = cmFileAPICacheDump(*this, object.Version);
   cache["kind"] = this->ObjectKindName(object.Kind);
 
@@ -754,7 +752,6 @@
 
 Json::Value cmFileAPI::BuildCMakeFiles(Object const& object)
 {
-  using namespace std::placeholders;
   Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version);
   cmakeFiles["kind"] = this->ObjectKindName(object.Kind);
 
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 7b916cd..172897c 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 cd);
+  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;
   }
 }
 
@@ -915,7 +1032,7 @@
   return source;
 }
 
-Json::Value Target::DumpCompileData(CompileData cd)
+Json::Value Target::DumpCompileData(CompileData const& cd)
 {
   Json::Value result = Json::objectValue;
 
@@ -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;
 }
@@ -1026,12 +1144,9 @@
 Json::Value Target::DumpInstallDestinations()
 {
   Json::Value destinations = Json::arrayValue;
-  auto installGens = this->GT->Makefile->GetInstallGenerators();
-  for (auto iGen : installGens) {
-    auto itGen = dynamic_cast<cmInstallTargetGenerator*>(iGen);
-    if (itGen != nullptr && itGen->GetTarget() == this->GT) {
-      destinations.append(this->DumpInstallDestination(itGen));
-    }
+  auto installGens = this->GT->Target->GetInstallGenerators();
+  for (auto itGen : installGens) {
+    destinations.append(this->DumpInstallDestination(itGen));
   }
   return destinations;
 }
@@ -1190,16 +1305,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/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx
index 782f746..8fcf1ac 100644
--- a/Source/cmFindProgramCommand.cxx
+++ b/Source/cmFindProgramCommand.cxx
@@ -4,6 +4,7 @@
 
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
@@ -71,7 +72,7 @@
   bool CheckDirectoryForName(std::string const& path, std::string const& name)
   {
     for (std::string const& ext : this->Extensions) {
-      if (!ext.empty() && cmSystemTools::StringEndsWith(name, ext.c_str())) {
+      if (!ext.empty() && cmHasSuffix(name, ext)) {
         continue;
       }
       this->TestNameExt = name;
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 38f34ac..d9e5e71 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -4192,7 +4192,7 @@
         if (stripResources) {
           flags.MacFolder = "";
         }
-      } else if (cmSystemTools::StringStartsWith(location, "Resources/")) {
+      } else if (cmHasLiteralPrefix(location, "Resources/")) {
         flags.Type = cmGeneratorTarget::SourceFileTypeDeepResource;
         if (stripResources) {
           flags.MacFolder += strlen("Resources/");
@@ -5219,7 +5219,7 @@
   const std::string& config, cmOptionalLinkInterface& iface,
   cmGeneratorTarget const* headTarget) const
 {
-  if (iface.ExplicitLibraries) {
+  if (iface.Explicit) {
     if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
         this->GetType() == cmStateEnums::STATIC_LIBRARY ||
         this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
@@ -5603,8 +5603,9 @@
   // libraries and executables that export symbols.
   const char* explicitLibraries = nullptr;
   std::string linkIfaceProp;
-  if (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
-      this->GetPolicyStatusCMP0022() != cmPolicies::WARN) {
+  bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
+                           this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
+  if (cmp0022NEW) {
     // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
     linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
     explicitLibraries = this->GetProperty(linkIfaceProp);
@@ -5659,15 +5660,14 @@
     return;
   }
   iface.Exists = true;
-  iface.ExplicitLibraries = explicitLibraries;
+  iface.Explicit = cmp0022NEW || explicitLibraries != nullptr;
 
   if (explicitLibraries) {
     // The interface libraries have been explicitly set.
     this->ExpandLinkItems(linkIfaceProp, explicitLibraries, config, headTarget,
                           usage_requirements_only, iface.Libraries,
                           iface.HadHeadSensitiveCondition);
-  } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN ||
-             this->GetPolicyStatusCMP0022() == cmPolicies::OLD)
+  } else if (!cmp0022NEW)
   // If CMP0022 is NEW then the plain tll signature sets the
   // INTERFACE_LINK_LIBRARIES, so if we get here then the project
   // cleared the property explicitly and we should not fall back
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/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index bad715d..0b68966 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -415,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);
@@ -556,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;
+      }
     }
   }
 }
@@ -580,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/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 8401efb..bead0e3 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -8,7 +8,9 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmUuid.h"
+#include "cm_string_view.hxx"
 #include "cmake.h"
 #include "cmsys/Encoding.hxx"
 
@@ -432,16 +434,15 @@
 
 void cmGlobalVisualStudio7Generator::WriteFolders(std::ostream& fout)
 {
-  const char* prefix = "CMAKE_FOLDER_GUID_";
-  const std::string::size_type skip_prefix = strlen(prefix);
+  cm::string_view const prefix = "CMAKE_FOLDER_GUID_";
   std::string guidProjectTypeFolder = "2150E333-8FDC-42A3-9474-1A3956D46DE8";
   for (auto const& iter : VisualStudioFolders) {
     std::string fullName = iter.first;
     std::string guid = this->GetGUID(fullName);
 
     std::replace(fullName.begin(), fullName.end(), '/', '\\');
-    if (cmSystemTools::StringStartsWith(fullName.c_str(), prefix)) {
-      fullName = fullName.substr(skip_prefix);
+    if (cmHasPrefix(fullName, prefix)) {
+      fullName = fullName.substr(prefix.size());
     }
 
     std::string nameOnly = cmSystemTools::GetFilenameName(fullName);
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index aca7268..5349a9d 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -43,11 +43,13 @@
   target.SetHaveInstallRule(true);
   const char* component = namelink ? args.GetNamelinkComponent().c_str()
                                    : args.GetComponent().c_str();
-  return new cmInstallTargetGenerator(
+  auto g = new cmInstallTargetGenerator(
     target.GetName(), destination.c_str(), impLib,
     args.GetPermissions().c_str(), args.GetConfigurations(), component,
     message, args.GetExcludeFromAll(), args.GetOptional() || forceOpt,
     backtrace);
+  target.AddInstallGenerator(g);
+  return g;
 }
 
 static cmInstallTargetGenerator* CreateInstallTargetGenerator(
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index 6450c62..d71ff49 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -87,7 +87,7 @@
   bool LibrariesDone = false;
   bool AllDone = false;
   bool Exists = false;
-  const char* ExplicitLibraries = nullptr;
+  bool Explicit = false;
 };
 
 struct cmHeadToLinkInterfaceMap
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 8746b35..4430f97 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -63,6 +63,7 @@
       continue;
     }
     if (item.IsPath) {
+      linkLibs += cli.GetLibLinkFileFlag();
       linkLibs +=
         this->ConvertToOutputFormat(this->ConvertToLinkReference(item.Value));
     } else {
diff --git a/Source/cmNewLineStyle.cxx b/Source/cmNewLineStyle.cxx
index 3f6523e..1ff741e 100644
--- a/Source/cmNewLineStyle.cxx
+++ b/Source/cmNewLineStyle.cxx
@@ -41,7 +41,7 @@
   return true;
 }
 
-const std::string cmNewLineStyle::GetCharacters() const
+std::string cmNewLineStyle::GetCharacters() const
 {
   switch (NewLineStyle) {
     case Invalid:
diff --git a/Source/cmNewLineStyle.h b/Source/cmNewLineStyle.h
index f1a7bc6..ab9002e 100644
--- a/Source/cmNewLineStyle.h
+++ b/Source/cmNewLineStyle.h
@@ -30,7 +30,7 @@
   bool ReadFromArguments(const std::vector<std::string>& args,
                          std::string& errorString);
 
-  const std::string GetCharacters() const;
+  std::string GetCharacters() const;
 
 private:
   Style NewLineStyle = Invalid;
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 2aefe8f..0801c24 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -1894,7 +1894,7 @@
           std::list<std::string>::iterator it = includes.begin();
           while (it != includes.end()) {
             std::string const& path = *it;
-            if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
+            if (cmHasPrefix(path, *ppath)) {
               MocConst_.IncludePaths.push_back(path);
               it = includes.erase(it);
             } else {
diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx
index c5de742..8fcb710 100644
--- a/Source/cmServerProtocol.cxx
+++ b/Source/cmServerProtocol.cxx
@@ -378,8 +378,7 @@
   SendSignal(kFILE_CHANGE_SIGNAL, obj);
 }
 
-const cmServerResponse cmServerProtocol1::Process(
-  const cmServerRequest& request)
+cmServerResponse cmServerProtocol1::Process(const cmServerRequest& request)
 {
   assert(this->m_State >= STATE_ACTIVE);
 
diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h
index 2f55a20..5da4344 100644
--- a/Source/cmServerProtocol.h
+++ b/Source/cmServerProtocol.h
@@ -80,7 +80,7 @@
 
   virtual std::pair<int, int> ProtocolVersion() const = 0;
   virtual bool IsExperimental() const = 0;
-  virtual const cmServerResponse Process(const cmServerRequest& request) = 0;
+  virtual cmServerResponse Process(const cmServerRequest& request) = 0;
 
   bool Activate(cmServer* server, const cmServerRequest& request,
                 std::string* errorMessage);
@@ -106,7 +106,7 @@
 public:
   std::pair<int, int> ProtocolVersion() const override;
   bool IsExperimental() const override;
-  const cmServerResponse Process(const cmServerRequest& request) override;
+  cmServerResponse Process(const cmServerRequest& request) override;
 
 private:
   bool DoActivate(const cmServerRequest& request,
diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx
index 5cdacaa..ffdd0ce 100644
--- a/Source/cmSourceGroupCommand.cxx
+++ b/Source/cmSourceGroupCommand.cxx
@@ -9,6 +9,7 @@
 
 #include "cmMakefile.h"
 #include "cmSourceGroup.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 namespace {
@@ -54,7 +55,7 @@
                   const std::vector<std::string>& files, std::string& error)
 {
   for (std::string const& file : files) {
-    if (!cmSystemTools::StringStartsWith(file, root.c_str())) {
+    if (!cmHasPrefix(file, root)) {
       error = "ROOT: " + root + " is not a prefix of file: " + file;
       return false;
     }
@@ -63,15 +64,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 +72,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/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx
new file mode 100644
index 0000000..5deb9b0
--- /dev/null
+++ b/Source/cmStringAlgorithms.cxx
@@ -0,0 +1,73 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmStringAlgorithms.h"
+
+#include <algorithm>
+#include <cstdio>
+
+namespace {
+template <std::size_t N, typename T>
+inline void MakeDigits(cm::string_view& view, char (&digits)[N],
+                       const char* pattern, T value)
+{
+  int res = std::snprintf(digits, N, pattern, value);
+  if (res > 0 && res < static_cast<int>(N)) {
+    view = cm::string_view(digits, static_cast<std::size_t>(res));
+  }
+}
+} // unnamed namespace
+
+cmAlphaNum::cmAlphaNum(int val)
+{
+  MakeDigits(View_, Digits_, "%i", val);
+}
+
+cmAlphaNum::cmAlphaNum(unsigned int val)
+{
+  MakeDigits(View_, Digits_, "%u", val);
+}
+
+cmAlphaNum::cmAlphaNum(long int val)
+{
+  MakeDigits(View_, Digits_, "%li", val);
+}
+
+cmAlphaNum::cmAlphaNum(unsigned long int val)
+{
+  MakeDigits(View_, Digits_, "%lu", val);
+}
+
+cmAlphaNum::cmAlphaNum(long long int val)
+{
+  MakeDigits(View_, Digits_, "%lli", val);
+}
+
+cmAlphaNum::cmAlphaNum(unsigned long long int val)
+{
+  MakeDigits(View_, Digits_, "%llu", val);
+}
+
+cmAlphaNum::cmAlphaNum(float val)
+{
+  MakeDigits(View_, Digits_, "%g", static_cast<double>(val));
+}
+
+cmAlphaNum::cmAlphaNum(double val)
+{
+  MakeDigits(View_, Digits_, "%g", val);
+}
+
+std::string cmCatViews(std::initializer_list<cm::string_view> views)
+{
+  std::size_t total_size = 0;
+  for (cm::string_view const& view : views) {
+    total_size += view.size();
+  }
+
+  std::string result(total_size, '\0');
+  std::string::iterator sit = result.begin();
+  for (cm::string_view const& view : views) {
+    sit = std::copy_n(view.data(), view.size(), sit);
+  }
+  return result;
+}
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 149e0ad..cdb494f 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -7,120 +7,158 @@
 
 #include "cmRange.h"
 #include "cm_string_view.hxx"
-#include <algorithm>
-#include <iterator>
+#include <initializer_list>
 #include <sstream>
 #include <string.h>
 #include <string>
 #include <utility>
 #include <vector>
 
+/** String range type.  */
 typedef cmRange<std::vector<std::string>::const_iterator> cmStringRange;
 
+/** Callable string comparison struct.  */
 struct cmStrCmp
 {
-  cmStrCmp(const char* test)
-    : m_test(test)
-  {
-  }
-  cmStrCmp(std::string test)
-    : m_test(std::move(test))
+  cmStrCmp(std::string str)
+    : Test_(std::move(str))
   {
   }
 
-  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;
-  }
+  bool operator()(cm::string_view sv) const { return Test_ == sv; }
 
 private:
-  const std::string m_test;
+  std::string const Test_;
 };
 
+/** Joins elements of a range with separator into a single string.  */
 template <typename Range>
-std::string cmJoin(Range const& r, const char* delimiter)
+std::string cmJoin(Range const& rng, cm::string_view separator)
 {
-  if (r.empty()) {
+  if (rng.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;
-
+  auto it = rng.begin();
+  auto const end = rng.end();
+  os << *it;
+  while (++it != end) {
+    os << separator << *it;
+  }
   return os.str();
 }
 
-template <typename Range>
-std::string cmJoin(Range const& r, std::string const& delimiter)
+/** Concatenate string pieces into a single string.  */
+std::string cmCatViews(std::initializer_list<cm::string_view> views);
+
+/** Utility class for cmStrCat.  */
+class cmAlphaNum
 {
-  return cmJoin(r, delimiter.c_str());
+public:
+  cmAlphaNum(cm::string_view view)
+    : View_(view)
+  {
+  }
+  cmAlphaNum(std::string const& str)
+    : View_(str)
+  {
+  }
+  cmAlphaNum(const char* str)
+    : View_(str)
+  {
+  }
+  cmAlphaNum(char ch)
+    : View_(Digits_, 1)
+  {
+    Digits_[0] = ch;
+  }
+  cmAlphaNum(int val);
+  cmAlphaNum(unsigned int val);
+  cmAlphaNum(long int val);
+  cmAlphaNum(unsigned long int val);
+  cmAlphaNum(long long int val);
+  cmAlphaNum(unsigned long long int val);
+  cmAlphaNum(float val);
+  cmAlphaNum(double val);
+
+  cm::string_view View() const { return View_; }
+
+private:
+  cm::string_view View_;
+  char Digits_[32];
+};
+
+/** Concatenate string pieces and numbers into a single string.  */
+template <typename... AV>
+inline std::string cmStrCat(cmAlphaNum const& a, cmAlphaNum const& b,
+                            AV const&... args)
+{
+  return cmCatViews(
+    { a.View(), b.View(), static_cast<cmAlphaNum const&>(args).View()... });
 }
 
+/** Joins wrapped elements of a range with separator into a single string.  */
 template <typename Range>
-std::string cmWrap(std::string const& prefix, Range const& r,
-                   std::string const& suffix, std::string const& sep)
+std::string cmWrap(cm::string_view prefix, Range const& rng,
+                   cm::string_view suffix, cm::string_view sep)
 {
-  if (r.empty()) {
+  if (rng.empty()) {
     return std::string();
   }
-  return prefix + cmJoin(r, suffix + sep + prefix) + suffix;
+  return cmCatViews(
+    { prefix, cmJoin(rng, cmCatViews({ suffix, sep, prefix })), suffix });
 }
 
+/** Joins wrapped elements of a range with separator into a single string.  */
 template <typename Range>
-std::string cmWrap(char prefix, Range const& r, char suffix,
-                   std::string const& sep)
+std::string cmWrap(char prefix, Range const& rng, char suffix,
+                   cm::string_view sep)
 {
-  return cmWrap(std::string(1, prefix), r, std::string(1, suffix), sep);
+  return cmWrap(cm::string_view(&prefix, 1), rng, cm::string_view(&suffix, 1),
+                sep);
 }
 
-/** Returns true if string @a str starts with the character @a prefix.  **/
+/** 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.  **/
+/** 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.  **/
+/** 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.  **/
+/** 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.  **/
+/** 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.  **/
+/** 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.  **/
+/** Removes an existing suffix character of from the string @a str.  */
 inline void cmStripSuffixIfExists(std::string& str, char suffix)
 {
   if (cmHasSuffix(str, suffix)) {
@@ -128,7 +166,7 @@
   }
 }
 
-/** Removes an existing suffix string of from the string @a str.  **/
+/** 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)) {
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index beccfce..7ca2391 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -180,6 +180,7 @@
   std::vector<cmCustomCommand> PreBuildCommands;
   std::vector<cmCustomCommand> PreLinkCommands;
   std::vector<cmCustomCommand> PostBuildCommands;
+  std::vector<cmInstallTargetGenerator*> InstallGenerators;
   std::set<std::string> SystemIncludeDirectories;
   cmTarget::LinkLibraryVectorType OriginalLinkLibraries;
   std::vector<std::string> IncludeDirectoriesEntries;
@@ -873,6 +874,17 @@
   impl->HaveInstallRule = hir;
 }
 
+void cmTarget::AddInstallGenerator(cmInstallTargetGenerator* g)
+{
+  impl->InstallGenerators.emplace_back(g);
+}
+
+std::vector<cmInstallTargetGenerator*> const& cmTarget::GetInstallGenerators()
+  const
+{
+  return impl->InstallGenerators;
+}
+
 bool cmTarget::GetIsGeneratorProvided() const
 {
   return impl->IsGeneratorProvided;
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 4e5141b..2b75879 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -21,6 +21,7 @@
 
 class cmCustomCommand;
 class cmGlobalGenerator;
+class cmInstallTargetGenerator;
 class cmMakefile;
 class cmMessenger;
 class cmPropertyMap;
@@ -147,6 +148,9 @@
   bool GetHaveInstallRule() const;
   void SetHaveInstallRule(bool hir);
 
+  void AddInstallGenerator(cmInstallTargetGenerator* g);
+  std::vector<cmInstallTargetGenerator*> const& GetInstallGenerators() const;
+
   /**
    * Get/Set whether this target was auto-created by a generator.
    */
diff --git a/Source/cmTargetDepend.h b/Source/cmTargetDepend.h
index 5ea0085..4ca78fa 100644
--- a/Source/cmTargetDepend.h
+++ b/Source/cmTargetDepend.h
@@ -31,7 +31,7 @@
   operator cmGeneratorTarget const*() const { return this->Target; }
   cmGeneratorTarget const* operator->() const { return this->Target; }
   cmGeneratorTarget const& operator*() const { return *this->Target; }
-  friend bool operator<(cmTargetDepend l, cmTargetDepend r)
+  friend bool operator<(cmTargetDepend const& l, cmTargetDepend const& r)
   {
     return l.Target < r.Target;
   }
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/cmVisualStudioSlnParser.cxx b/Source/cmVisualStudioSlnParser.cxx
index 9353276..3e7e142 100644
--- a/Source/cmVisualStudioSlnParser.cxx
+++ b/Source/cmVisualStudioSlnParser.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVisualStudioSlnParser.h"
 
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVisualStudioSlnData.h"
 #include "cmsys/FStream.hxx"
@@ -192,8 +193,8 @@
   assert(!line.IsComment());
   switch (this->Stack.top()) {
     case FileStateStart:
-      if (!cmSystemTools::StringStartsWith(
-            line.GetTag().c_str(), "Microsoft Visual Studio Solution File")) {
+      if (!cmHasLiteralPrefix(line.GetTag(),
+                              "Microsoft Visual Studio Solution File")) {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
         return false;
       }
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 3b4a6c0..a81b7e4 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -28,6 +28,7 @@
 #include "cmUtils.hxx"
 #include "cmVersionConfig.h"
 #include "cmWorkingDirectory.h"
+#include "cm_string_view.hxx"
 #include "cm_sys_stat.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
@@ -132,22 +133,15 @@
 }
 
 cmake::cmake(Role role, cmState::Mode mode)
+  : FileTimeCache(cm::make_unique<cmFileTimeCache>())
+#ifdef CMAKE_BUILD_WITH_CMAKE
+  , VariableWatch(cm::make_unique<cmVariableWatch>())
+#endif
+  , State(cm::make_unique<cmState>())
+  , Messenger(cm::make_unique<cmMessenger>())
 {
-  this->Trace = false;
-  this->TraceExpand = false;
-  this->WarnUninitialized = false;
-  this->WarnUnused = false;
-  this->WarnUnusedCli = true;
-  this->CheckSystemVars = false;
-  this->DebugOutput = false;
-  this->DebugTryCompile = false;
-  this->ClearBuildSystem = false;
-  this->FileTimeCache = cm::make_unique<cmFileTimeCache>();
-
-  this->State = cm::make_unique<cmState>();
   this->State->SetMode(mode);
   this->CurrentSnapshot = this->State->CreateBaseSnapshot();
-  this->Messenger = cm::make_unique<cmMessenger>();
 
 #ifdef __APPLE__
   struct rlimit rlp;
@@ -159,16 +153,6 @@
   }
 #endif
 
-  this->GlobalGenerator = nullptr;
-  this->GeneratorInstanceSet = false;
-  this->GeneratorPlatformSet = false;
-  this->GeneratorToolsetSet = false;
-  this->CurrentWorkingMode = NORMAL_MODE;
-
-#ifdef CMAKE_BUILD_WITH_CMAKE
-  this->VariableWatch = cm::make_unique<cmVariableWatch>();
-#endif
-
   this->AddDefaultGenerators();
   this->AddDefaultExtraGenerators();
   if (role == RoleScript || role == RoleProject) {
@@ -188,32 +172,25 @@
   // Set up a list of source and header extensions.
   // These are used to find files when the extension is not given.
   {
-    auto fillExts = [](FileExtensions& exts,
-                       std::initializer_list<const char*> extList) {
+    auto setupExts = [](FileExtensions& exts,
+                        std::initializer_list<cm::string_view> extList) {
       // Fill ordered vector
       exts.ordered.reserve(extList.size());
-      for (const char* ext : extList) {
+      for (cm::string_view ext : extList) {
         exts.ordered.emplace_back(ext);
       };
       // Fill unordered set
       exts.unordered.insert(exts.ordered.begin(), exts.ordered.end());
     };
 
-    // Source extensions
     // The "c" extension MUST precede the "C" extension.
-    fillExts(this->SourceFileExtensions,
-             { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" });
-
-    // Header extensions
-    fillExts(this->HeaderFileExtensions,
-             { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
-
-    // Cuda extensions
-    fillExts(this->CudaFileExtensions, { "cu" });
-
-    // Fortran extensions
-    fillExts(this->FortranFileExtensions,
-             { "f", "F", "for", "f77", "f90", "f95", "f03" });
+    setupExts(this->SourceFileExtensions,
+              { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" });
+    setupExts(this->HeaderFileExtensions,
+              { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
+    setupExts(this->CudaFileExtensions, { "cu" });
+    setupExts(this->FortranFileExtensions,
+              { "f", "F", "for", "f77", "f90", "f95", "f03" });
   }
 }
 
@@ -620,7 +597,7 @@
     this->EnvironmentGenerator = envGenVar;
   }
 
-  auto readGeneratorVar = [&](std::string name, std::string& key) {
+  auto readGeneratorVar = [&](std::string const& name, std::string& key) {
     std::string varValue;
     if (cmSystemTools::GetEnv(name, varValue)) {
       if (hasEnvironmentGenerator) {
@@ -2031,8 +2008,7 @@
   for (cmGlobalGeneratorFactory* g : this->Generators) {
     cmDocumentationEntry e;
     g->GetDocumentation(e);
-    if (!foundDefaultOne &&
-        cmSystemTools::StringStartsWith(e.Name, defaultName.c_str())) {
+    if (!foundDefaultOne && cmHasPrefix(e.Name, defaultName)) {
       e.CustomNamePrefix = '*';
       foundDefaultOne = true;
     }
diff --git a/Source/cmake.h b/Source/cmake.h
index 6aa00e1..92494ae 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -509,14 +509,14 @@
   void AddDefaultGenerators();
   void AddDefaultExtraGenerators();
 
-  cmGlobalGenerator* GlobalGenerator;
+  cmGlobalGenerator* GlobalGenerator = nullptr;
   std::map<std::string, DiagLevel> DiagLevels;
   std::string GeneratorInstance;
   std::string GeneratorPlatform;
   std::string GeneratorToolset;
-  bool GeneratorInstanceSet;
-  bool GeneratorPlatformSet;
-  bool GeneratorToolsetSet;
+  bool GeneratorInstanceSet = false;
+  bool GeneratorPlatformSet = false;
+  bool GeneratorToolsetSet = false;
 
   //! read in a cmake list file to initialize the cache
   void ReadListFile(const std::vector<std::string>& args,
@@ -543,14 +543,14 @@
 
 private:
   ProgressCallbackType ProgressCallback;
-  WorkingMode CurrentWorkingMode;
-  bool DebugOutput;
-  bool Trace;
-  bool TraceExpand;
-  bool WarnUninitialized;
-  bool WarnUnused;
-  bool WarnUnusedCli;
-  bool CheckSystemVars;
+  WorkingMode CurrentWorkingMode = NORMAL_MODE;
+  bool DebugOutput = false;
+  bool Trace = false;
+  bool TraceExpand = false;
+  bool WarnUninitialized = false;
+  bool WarnUnused = false;
+  bool WarnUnusedCli = true;
+  bool CheckSystemVars = false;
   std::map<std::string, bool> UsedCliVariables;
   std::string CMakeEditCommand;
   std::string CXXEnvironment;
@@ -564,8 +564,8 @@
   FileExtensions HeaderFileExtensions;
   FileExtensions CudaFileExtensions;
   FileExtensions FortranFileExtensions;
-  bool ClearBuildSystem;
-  bool DebugTryCompile;
+  bool ClearBuildSystem = false;
+  bool DebugTryCompile = false;
   std::unique_ptr<cmFileTimeCache> FileTimeCache;
   std::string GraphVizFile;
   InstalledFilesMap InstalledFiles;
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index a25f25a..cd4dbc8 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -10,6 +10,7 @@
   testRST.cxx
   testRange.cxx
   testString.cxx
+  testStringAlgorithms.cxx
   testSystemTools.cxx
   testUTF8.cxx
   testXMLParser.cxx
diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx
new file mode 100644
index 0000000..95616ff
--- /dev/null
+++ b/Tests/CMakeLib/testStringAlgorithms.cxx
@@ -0,0 +1,134 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <cmConfigure.h> // IWYU pragma: keep
+
+#include "cm_string_view.hxx"
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "cmStringAlgorithms.h"
+
+int testStringAlgorithms(int /*unused*/, char* /*unused*/ [])
+{
+  int failed = 0;
+
+  auto assert_ok = [&failed](bool test, cm::string_view title) {
+    if (test) {
+      std::cout << "Passed: " << title << "\n";
+    } else {
+      std::cout << "Failed: " << title << "\n";
+      ++failed;
+    }
+  };
+
+  auto assert_string = [&failed](cm::string_view generated,
+                                 cm::string_view expected,
+                                 cm::string_view title) {
+    if (generated == expected) {
+      std::cout << "Passed: " << title << "\n";
+    } else {
+      std::cout << "Failed: " << title << "\n";
+      std::cout << "Expected: " << expected << "\n";
+      std::cout << "Got: " << generated << "\n";
+      ++failed;
+    }
+  };
+
+  // ----------------------------------------------------------------------
+  // Test cmJoin
+  {
+    typedef std::string ST;
+    typedef std::vector<std::string> VT;
+    assert_string(cmJoin(ST("abc"), ";"), "a;b;c", "cmJoin std::string");
+    assert_string(cmJoin(VT{}, ";"), "", "cmJoin std::vector empty");
+    assert_string(cmJoin(VT{ "a" }, ";"), "a", "cmJoin std::vector single");
+    assert_string(cmJoin(VT{ "a", "b", "c" }, ";"), "a;b;c",
+                  "cmJoin std::vector multiple");
+    assert_string(cmJoin(VT{ "a", "b", "c" }, "<=>"), "a<=>b<=>c",
+                  "cmJoin std::vector long sep");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmStrCat
+  {
+    int ni = -1100;
+    unsigned int nui = 1100u;
+    long int nli = -12000l;
+    unsigned long int nuli = 12000ul;
+    long long int nlli = -130000ll;
+    unsigned long long int nulli = 130000ull;
+    std::string val =
+      cmStrCat("<test>", ni, ',', nui, ',', nli, ",", nuli, ", ", nlli,
+               std::string(", "), nulli, cm::string_view("</test>"));
+    std::string expect =
+      "<test>-1100,1100,-12000,12000, -130000, 130000</test>";
+    assert_string(val, expect, "cmStrCat strings and integers");
+  }
+  {
+    float const val = 1.5f;
+    float const div = 0.00001f;
+    float f = 0.0f;
+    std::istringstream(cmStrCat("", val)) >> f;
+    f -= val;
+    assert_ok((f < div) && (f > -div), "cmStrCat float");
+  }
+  {
+    double const val = 1.5;
+    double const div = 0.00001;
+    double d = 0.0;
+    std::istringstream(cmStrCat("", val)) >> d;
+    d -= val;
+    assert_ok((d < div) && (d > -div), "cmStrCat double");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmWrap
+  {
+    typedef std::vector<std::string> VT;
+    assert_string(cmWrap("<", VT{}, ">", "; "), //
+                  "",                           //
+                  "cmWrap empty, string prefix and suffix");
+    assert_string(cmWrap("<", VT{ "abc" }, ">", "; "), //
+                  "<abc>",                             //
+                  "cmWrap single, string prefix and suffix");
+    assert_string(cmWrap("<", VT{ "a1", "a2", "a3" }, ">", "; "), //
+                  "<a1>; <a2>; <a3>",                             //
+                  "cmWrap multiple, string prefix and suffix");
+
+    assert_string(cmWrap('<', VT{}, '>', "; "), //
+                  "",                           //
+                  "cmWrap empty, char prefix and suffix");
+    assert_string(cmWrap('<', VT{ "abc" }, '>', "; "), //
+                  "<abc>",                             //
+                  "cmWrap single, char prefix and suffix");
+    assert_string(cmWrap('<', VT{ "a1", "a2", "a3" }, '>', "; "), //
+                  "<a1>; <a2>; <a3>",                             //
+                  "cmWrap multiple, char prefix and suffix");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmHas(Literal)Prefix and cmHas(Literal)Suffix
+  {
+    std::string str("abc");
+    assert_ok(cmHasPrefix(str, 'a'), "cmHasPrefix char");
+    assert_ok(!cmHasPrefix(str, 'c'), "cmHasPrefix char not");
+    assert_ok(cmHasPrefix(str, "ab"), "cmHasPrefix string");
+    assert_ok(!cmHasPrefix(str, "bc"), "cmHasPrefix string not");
+    assert_ok(cmHasPrefix(str, str), "cmHasPrefix complete string");
+    assert_ok(cmHasLiteralPrefix(str, "ab"), "cmHasLiteralPrefix string");
+    assert_ok(!cmHasLiteralPrefix(str, "bc"), "cmHasLiteralPrefix string not");
+
+    assert_ok(cmHasSuffix(str, 'c'), "cmHasSuffix char");
+    assert_ok(!cmHasSuffix(str, 'a'), "cmHasSuffix char not");
+    assert_ok(cmHasSuffix(str, "bc"), "cmHasSuffix string");
+    assert_ok(!cmHasSuffix(str, "ab"), "cmHasSuffix string not");
+    assert_ok(cmHasSuffix(str, str), "cmHasSuffix complete string");
+    assert_ok(cmHasLiteralSuffix(str, "bc"), "cmHasLiteralSuffix string");
+    assert_ok(!cmHasLiteralSuffix(str, "ab"), "cmHasLiteralPrefix string not");
+  }
+
+  return failed;
+}
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/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 3b0ec6e..89f63d0 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -2092,7 +2092,40 @@
             ],
             "build": "^cxx$",
             "source": "^cxx$",
-            "install": None,
+            "install": {
+                "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$",
+                "destinations": [
+                    {
+                        "path": "bin",
+                        "backtrace": [
+                            {
+                                "file": "^codemodel-v2\\.cmake$",
+                                "line": 37,
+                                "command": "install",
+                                "hasParent": True,
+                            },
+                            {
+                                "file": "^codemodel-v2\\.cmake$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": True,
+                            },
+                            {
+                                "file": "^CMakeLists\\.txt$",
+                                "line": 3,
+                                "command": "include",
+                                "hasParent": True,
+                            },
+                            {
+                                "file": "^CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                ],
+            },
             "link": {
                 "language": "CXX",
                 "lto": None,
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2.cmake b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
index 72073d5..c98a84c 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2.cmake
+++ b/Tests/RunCMake/FileAPI/codemodel-v2.cmake
@@ -33,3 +33,5 @@
   set_property(TARGET c_static_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
   file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "")
 endif()
+
+install(TARGETS cxx_exe)
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/RuntimePath/CMakeLists.txt b/Tests/RuntimePath/CMakeLists.txt
index 6583a87..bb87440 100644
--- a/Tests/RuntimePath/CMakeLists.txt
+++ b/Tests/RuntimePath/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.6)
+cmake_minimum_required (VERSION 3.15)
 project(RuntimePath C)
 
 # Add a simple chain of shared libraries that must be found.
@@ -31,3 +31,14 @@
   set_property(TARGET bar2 PROPERTY LIBRARY_OUTPUT_DIRECTORY A)
   target_link_libraries(bar2 foo2)
 endif()
+
+# Add a library that is missing the rpath for its dependency.
+add_library(bar1_no_rpath SHARED bar1.c)
+set_property(TARGET bar1_no_rpath PROPERTY LIBRARY_OUTPUT_DIRECTORY B)
+set_property(TARGET bar1_no_rpath PROPERTY SKIP_BUILD_RPATH 1)
+target_link_libraries(bar1_no_rpath PRIVATE foo1)
+
+# Add an executable linking to the library with a missing dependency rpath.
+# CMake should generate the proper rpath-link flag to find it at build time.
+add_executable(main_with_bar1_no_rpath main.c)
+target_link_libraries(main_with_bar1_no_rpath bar1_no_rpath)
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/bootstrap b/bootstrap
index 5742d53..06e11b5 100755
--- a/bootstrap
+++ b/bootstrap
@@ -423,6 +423,7 @@
   cmState \
   cmStateDirectory \
   cmStateSnapshot \
+  cmStringAlgorithms \
   cmStringReplaceHelper \
   cmStringCommand \
   cmSubdirCommand \