Merge topic 'vs-v142-csharp-flags'

bbf216fb6b VS: Add toolset v142 CSharp flag table

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3908
diff --git a/.clang-format b/.clang-format
index 162c56d..0c7d6b0 100644
--- a/.clang-format
+++ b/.clang-format
@@ -18,4 +18,28 @@
 IndentPPDirectives: AfterHash
 SortUsingDeclarations: false
 SpaceAfterTemplateKeyword: true
+IncludeBlocks: Regroup
+IncludeCategories:
+  - Regex:           '^[<"]cmConfigure\.h'
+    Priority:        -1
+  - Regex:           '^(<|")cm/'
+    Priority:        2
+  - Regex:           '^(<|")windows\.h'
+    Priority:        3
+  - Regex:           '^<sys/'
+    Priority:        5
+  - Regex:           '^(<|")Qt?[A-Z]'
+    Priority:        6
+  - Regex:           '^(<|")cmsys/'
+    Priority:        7
+  - Regex:           '^(<|")cm_'
+    Priority:        8
+  - Regex:           '^(<|")cm[A-Z][^.]+\.h'
+    Priority:        9
+  - Regex:           '^<[^.]+\.h'
+    Priority:        4
+  - Regex:           '^<'
+    Priority:        1
+  - Regex:           '.*'
+    Priority:        10
 ...
diff --git a/.clang-tidy b/.clang-tidy
index bfcb67c..a520679 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -3,29 +3,27 @@
 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-deprecated-headers,\
--modernize-return-braced-init-list,\
--modernize-use-auto,\
+-modernize-avoid-c-arrays,\
+-modernize-use-nodiscard,\
 -modernize-use-noexcept,\
 -modernize-use-transparent-functors,\
--modernize-use-using,\
 performance-*,\
--performance-inefficient-string-concatenation,\
 readability-*,\
 -readability-function-size,\
 -readability-identifier-naming,\
--readability-implicit-bool-cast,\
 -readability-implicit-bool-conversion,\
 -readability-inconsistent-declaration-parameter-name,\
+-readability-magic-numbers,\
 -readability-named-parameter,\
 -readability-redundant-declaration,\
+-readability-uppercase-literal-suffix,\
 "
 HeaderFilterRegex: 'Source/cm[^/]*\.(h|hxx|cxx)$'
 CheckOptions:
@@ -33,4 +31,6 @@
     value: '1'
   - key:   modernize-use-equals-default.IgnoreMacros
     value: '0'
+  - key:   modernize-use-auto.MinTypeNameLength
+    value: '80'
 ...
diff --git a/.gitattributes b/.gitattributes
index 24fd9c2..3da2d60 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.
@@ -11,6 +11,7 @@
 bootstrap        eol=lf
 configure        eol=lf
 *.[1-9]          eol=lf
+*.bash           eol=lf
 *.sh             eol=lf
 *.sh.in          eol=lf
 
diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake
index 638b1c4..d8d2c86 100644
--- a/Auxiliary/bash-completion/cmake
+++ b/Auxiliary/bash-completion/cmake
@@ -76,8 +76,8 @@
                 compopt -o nospace
             else
             # complete variable names
-                COMPREPLY=( $( compgen -W '$( cmake -LA -N | tail -n +2 |
-                    cut -f1 -d: )' -P "$prefix" -- "$cur" ) )
+                COMPREPLY=( $( compgen -W '$( cmake -LA -N 2>/dev/null |
+                    tail -n +2 | cut -f1 -d: )' -P "$prefix" -- "$cur" ) )
                 compopt -o nospace
             fi
             return
diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el
index e4fa6c1..caaf0d5 100644
--- a/Auxiliary/cmake-mode.el
+++ b/Auxiliary/cmake-mode.el
@@ -1,5 +1,7 @@
 ;;; cmake-mode.el --- major-mode for editing CMake sources
 
+;; Package-Requires: ((emacs "24.1"))
+
 ; Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 ; file Copyright.txt or https://cmake.org/licensing for details.
 
@@ -224,17 +226,11 @@
 ;;
 (defvar cmake-mode-hook nil)
 
-;------------------------------------------------------------------------------
-
-;; For compatibility with Emacs < 24
-(defalias 'cmake--parent-mode
-  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
-
 ;;------------------------------------------------------------------------------
 ;; Mode definition.
 ;;
 ;;;###autoload
-(define-derived-mode cmake-mode cmake--parent-mode "CMake"
+(define-derived-mode cmake-mode prog-mode "CMake"
   "Major mode for editing CMake source files."
 
   ; Setup font-lock mode.
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index cd8385b..5de117b 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -288,6 +288,7 @@
             \ SKIP_AUTORCC
             \ SKIP_AUTOUIC
             \ SKIP_BUILD_RPATH
+            \ SKIP_REGULAR_EXPRESSION
             \ SKIP_RETURN_CODE
             \ SOURCES
             \ SOURCE_DIR
@@ -915,6 +916,7 @@
             \ CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES
             \ CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT
             \ CMAKE_ECLIPSE_MAKE_ARGUMENTS
+            \ CMAKE_ECLIPSE_RESOURCE_ENCODING
             \ CMAKE_ECLIPSE_VERSION
             \ CMAKE_EDIT_COMMAND
             \ CMAKE_ENABLE_EXPORTS
@@ -948,6 +950,12 @@
             \ CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
             \ CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
             \ CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
+            \ CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH
+            \ CMAKE_FIND_USE_CMAKE_PATH
+            \ CMAKE_FIND_USE_CMAKE_SYSTEM_PATH
+            \ CMAKE_FIND_USE_PACKAGE_REGISTRY
+            \ CMAKE_FIND_USE_PACKAGE_ROOT_PATH
+            \ CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH
             \ CMAKE_FOLDER
             \ CMAKE_FRAMEWORK
             \ CMAKE_FRAMEWORK_PATH
@@ -1765,6 +1773,7 @@
             \ NAME
             \ OFF
             \ PASS_REGULAR_EXPRESSION
+            \ SKIP_REGULAR_EXPRESSION
             \ TARGET_FILE
             \ WILL_FAIL
             \ WORKING_DIRECTORY
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e60b6c5..da99a6e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,13 +1,24 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.1...3.15 FATAL_ERROR)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
 project(CMake)
 unset(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX)
 unset(CMAKE_USER_MAKE_RULES_OVERRIDE_C)
 
+# FIXME: This block should go away after a transition period.
+if(MSVC AND NOT CMAKE_VERSION VERSION_LESS 3.15)
+  # Filter out MSVC runtime library flags that may have come from
+  # the cache of an existing build tree or from scripts.
+  foreach(l C CXX)
+    foreach(c DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
+      string(REGEX REPLACE "[-/]M[DT]d?( |$)" "" "CMAKE_${l}_FLAGS_${c}" "${CMAKE_${l}_FLAGS_${c}}")
+    endforeach()
+  endforeach()
+endif()
+
 # Make sure we can find internal find_package modules only used for
 # building CMake and not for shipping externally
 list(INSERT CMAKE_MODULE_PATH 0 ${CMake_SOURCE_DIR}/Source/Modules)
@@ -18,6 +29,10 @@
   unset(CMAKE_BOOTSTRAP CACHE)
 endif()
 
+if(CMake_TEST_HOST_CMAKE)
+  get_filename_component(CMake_TEST_EXTERNAL_CMAKE "${CMAKE_COMMAND}" DIRECTORY)
+endif()
+
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX")
     message(FATAL_ERROR
@@ -353,11 +368,24 @@
   # Setup third-party libraries.
   # Everything in the tree should be able to include files from the
   # Utilities directory.
+  if (CMAKE_SYSTEM_NAME STREQUAL "AIX" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    # using -isystem option generate error "template with C linkage"
+    include_directories("${CMake_SOURCE_DIR}/Utilities/std")
+  else()
+    include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities/std")
+  endif()
+
   include_directories(
     ${CMake_BINARY_DIR}/Utilities
     ${CMake_SOURCE_DIR}/Utilities
     )
 
+  #---------------------------------------------------------------------
+  # Build CMake std library for CMake and CTest.
+  set(CMAKE_STD_LIBRARY cmstd)
+  add_subdirectory(Utilities/std)
+  CMAKE_SET_TARGET_FOLDER(cmstd "Utilities/std")
+
   # check for the use of system libraries versus builtin ones
   # (a macro defined in this file)
   CMAKE_HANDLE_SYSTEM_LIBRARIES()
@@ -429,10 +457,7 @@
     set(_CMAKE_USE_OPENSSL_DEFAULT OFF)
     if(NOT DEFINED CMAKE_USE_OPENSSL AND NOT WIN32 AND NOT APPLE
         AND CMAKE_SYSTEM_NAME MATCHES "(Linux|FreeBSD)")
-      find_package(OpenSSL QUIET)
-      if(OPENSSL_FOUND)
-        set(_CMAKE_USE_OPENSSL_DEFAULT ON)
-      endif()
+      set(_CMAKE_USE_OPENSSL_DEFAULT ON)
     endif()
     option(CMAKE_USE_OPENSSL "Use OpenSSL." ${_CMAKE_USE_OPENSSL_DEFAULT})
     mark_as_advanced(CMAKE_USE_OPENSSL)
@@ -629,8 +654,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()
@@ -677,7 +701,7 @@
 # to a cdash4simpletest database. In these cases, the CDash dashboards
 # should be run first.
 #
-if("x${CMAKE_TESTS_CDASH_SERVER}" STREQUAL "x")
+if("x${CMAKE_TESTS_CDASH_SERVER}" STREQUAL "x" AND NOT CMake_TEST_NO_NETWORK)
   set(CMAKE_TESTS_CDASH_SERVER "http://open.cdash.org")
 endif()
 
@@ -817,7 +841,7 @@
     PATTERN "*.sh*" PERMISSIONS OWNER_READ OWNER_EXECUTE OWNER_WRITE
                                 GROUP_READ GROUP_EXECUTE
                                 WORLD_READ WORLD_EXECUTE
-    REGEX "Help/dev($|/)" EXCLUDE
+    REGEX "Help/(dev|guide)($|/)" EXCLUDE
     )
 
   # Install auxiliary files integrating with other tools.
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 7e71111..7983be1 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -27,14 +27,15 @@
 #. Run `Utilities/SetupForDevelopment.sh`_ for local git configuration.
 #. See `Building CMake`_ for building CMake locally.
 #. See the `CMake Source Code Guide`_ for coding guidelines.
-#. Base all new work on the upstream ``master`` branch.
+#. Create a topic branch named suitably for your work.
+   Base all new work on the upstream ``master`` branch.
    Base work on the upstream ``release`` branch only if it fixes a
    regression or bug in a feature new to that release.
    If in doubt, prefer ``master``.  Reviewers may simply ask for
    a rebase if deemed appropriate in particular cases.
 #. Create commits making incremental, distinct, logically complete changes
    with appropriate `commit messages`_.
-#. Push a topic branch to a personal repository fork on GitLab.
+#. Push the topic branch to a personal repository fork on GitLab.
 #. Create a GitLab Merge Request targeting the upstream ``master`` branch
    (even if the change is intended for merge to the ``release`` branch).
    Check the box labelled "Allow commits from members who can merge to the
diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt
index dde4dbb..40f1c1a 100644
--- a/Help/command/FIND_XXX.txt
+++ b/Help/command/FIND_XXX.txt
@@ -89,7 +89,8 @@
    searched after paths from the current module,
    i.e. ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``,
    ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc.
-   This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed.
+   This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
+   the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
    See policy :policy:`CMP0074`.
 
    * |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX|
@@ -97,7 +98,8 @@
 2. Search paths specified in cmake-specific cache variables.
    These are intended to be used on the command line with a ``-DVAR=value``.
    The values are interpreted as :ref:`semicolon-separated lists <CMake Language Lists>`.
-   This can be skipped if ``NO_CMAKE_PATH`` is passed.
+   This can be skipped if ``NO_CMAKE_PATH`` is passed or by setting the
+   :variable:`CMAKE_FIND_USE_CMAKE_PATH` to ``FALSE``.
 
    * |CMAKE_PREFIX_PATH_XXX|
    * |CMAKE_XXX_PATH|
@@ -107,7 +109,8 @@
    These are intended to be set in the user's shell configuration,
    and therefore use the host's native path separator
    (``;`` on Windows and ``:`` on UNIX).
-   This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed.
+   This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed or
+   by setting the :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH` to ``FALSE``.
 
    * |CMAKE_PREFIX_PATH_XXX|
    * |CMAKE_XXX_PATH|
@@ -119,13 +122,16 @@
    Hard-coded guesses should be specified with the ``PATHS`` option.
 
 5. Search the standard system environment variables.
-   This can be skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is an argument.
+   This can be skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is passed or by
+   setting the :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` to ``FALSE``.
 
    * |SYSTEM_ENVIRONMENT_PATH_XXX|
+   * |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX|
 
 6. Search cmake variables defined in the Platform files
    for the current system.  This can be skipped if ``NO_CMAKE_SYSTEM_PATH``
-   is passed.
+   is passed or by setting the :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`
+   to ``FALSE``.
 
    * |CMAKE_SYSTEM_PREFIX_PATH_XXX|
    * |CMAKE_SYSTEM_XXX_PATH|
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index ed321fc..df7cc4e 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -68,9 +68,6 @@
   order-only dependencies to ensure the byproducts will be
   available before their dependents build.
 
-  The ``BYPRODUCTS`` option is ignored on non-Ninja generators
-  except to mark byproducts ``GENERATED``.
-
 ``COMMAND``
   Specify the command-line(s) to execute at build time.
   If more than one ``COMMAND`` is specified they will be executed in order,
@@ -82,25 +79,33 @@
   will be ignored.
 
   If ``COMMAND`` specifies an executable target name (created by the
-  :command:`add_executable` command) it will automatically be replaced
-  by the location of the executable created at build time. If set, the
-  :prop_tgt:`CROSSCOMPILING_EMULATOR` executable target property will
-  also be prepended to the command to allow the executable to run on
-  the host.
-  (Use the ``TARGET_FILE``
-  :manual:`generator expression <cmake-generator-expressions(7)>` to
-  reference an executable later in the command line.)
-  Additionally a target-level dependency will be added so that the
-  executable target will be built before any target using this custom
-  command.  However this does NOT add a file-level dependency that
-  would cause the custom command to re-run whenever the executable is
-  recompiled.
+  :command:`add_executable` command), it will automatically be replaced
+  by the location of the executable created at build time if either of
+  the following is true:
+
+  * The target is not being cross-compiled (i.e. the
+    :variable:`CMAKE_CROSSCOMPILING` variable is not set to true).
+  * The target is being cross-compiled and an emulator is provided (i.e.
+    its :prop_tgt:`CROSSCOMPILING_EMULATOR` target property is set).
+    In this case, the contents of :prop_tgt:`CROSSCOMPILING_EMULATOR` will be
+    prepended to the command before the location of the target executable.
+
+  If neither of the above conditions are met, it is assumed that the
+  command name is a program to be found on the ``PATH`` at build time.
 
   Arguments to ``COMMAND`` may use
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
-  References to target names in generator expressions imply target-level
-  dependencies, but NOT file-level dependencies.  List target names with
-  the ``DEPENDS`` option to add file-level dependencies.
+  Use the ``TARGET_FILE`` generator expression to refer to the location of
+  a target later in the command line (i.e. as a command argument rather
+  than as the command to execute).
+
+  Whenever a target is used as a command to execute or is mentioned in a
+  generator expression as a command argument, a target-level dependency
+  will be added automatically so that the mentioned target will be built
+  before any target using this custom command.  However this does NOT add
+  a file-level dependency that would cause the custom command to re-run
+  whenever the executable is recompiled.  List target names with
+  the ``DEPENDS`` option to add such file-level dependencies.
 
 ``COMMENT``
   Display the given message before the commands are executed at
@@ -111,6 +116,9 @@
   an ``OUTPUT`` of another custom command in the same directory
   (``CMakeLists.txt`` file) CMake automatically brings the other
   custom command into the target in which this command is built.
+  A target-level dependency is added if any dependency is listed as
+  ``BYPRODUCTS`` of a target or any of its build events in the same
+  directory to ensure the byproducts will be available.
   If ``DEPENDS`` is not specified the command will run whenever
   the ``OUTPUT`` is missing; if the command does not actually
   create the ``OUTPUT`` then the rule will always run.
diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst
index 08b9516..2d5f5f0 100644
--- a/Help/command/add_custom_target.rst
+++ b/Help/command/add_custom_target.rst
@@ -49,9 +49,6 @@
   order-only dependencies to ensure the byproducts will be
   available before their dependents build.
 
-  The ``BYPRODUCTS`` option is ignored on non-Ninja generators
-  except to mark byproducts ``GENERATED``.
-
 ``COMMAND``
   Specify the command-line(s) to execute at build time.
   If more than one ``COMMAND`` is specified they will be executed in order,
@@ -61,18 +58,30 @@
   a ``COMMAND`` to launch it.)
 
   If ``COMMAND`` specifies an executable target name (created by the
-  :command:`add_executable` command) it will automatically be replaced
-  by the location of the executable created at build time. If set, the
-  :prop_tgt:`CROSSCOMPILING_EMULATOR` executable target property will
-  also be prepended to the command to allow the executable to run on
-  the host.
-  Additionally a target-level dependency will be added so that the
-  executable target will be built before this custom target.
+  :command:`add_executable` command), it will automatically be replaced
+  by the location of the executable created at build time if either of
+  the following is true:
+
+  * The target is not being cross-compiled (i.e. the
+    :variable:`CMAKE_CROSSCOMPILING` variable is not set to true).
+  * The target is being cross-compiled and an emulator is provided (i.e.
+    its :prop_tgt:`CROSSCOMPILING_EMULATOR` target property is set).
+    In this case, the contents of :prop_tgt:`CROSSCOMPILING_EMULATOR` will be
+    prepended to the command before the location of the target executable.
+
+  If neither of the above conditions are met, it is assumed that the
+  command name is a program to be found on the ``PATH`` at build time.
 
   Arguments to ``COMMAND`` may use
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
-  References to target names in generator expressions imply target-level
-  dependencies.
+  Use the ``TARGET_FILE`` generator expression to refer to the location of
+  a target later in the command line (i.e. as a command argument rather
+  than as the command to execute).
+
+  Whenever a target is used as a command to execute or is mentioned in a
+  generator expression as a command argument, a target-level dependency
+  will be added automatically so that the mentioned target will be built
+  before this custom target.
 
   The command and arguments are optional and if not specified an empty
   target will be created.
@@ -86,6 +95,9 @@
   :command:`add_custom_command` command calls in the same directory
   (``CMakeLists.txt`` file).  They will be brought up to date when
   the target is built.
+  A target-level dependency is added if any dependency is a byproduct
+  of a target or any of its build events in the same directory to ensure
+  the byproducts will be available before this target is built.
 
   Use the :command:`add_dependencies` command to add dependencies
   on other targets.
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst
index 46b9b63..a77ba37 100644
--- a/Help/command/add_test.rst
+++ b/Help/command/add_test.rst
@@ -7,7 +7,8 @@
 
   add_test(NAME <name> COMMAND <command> [<arg>...]
            [CONFIGURATIONS <config>...]
-           [WORKING_DIRECTORY <dir>])
+           [WORKING_DIRECTORY <dir>]
+           [COMMAND_EXPAND_LISTS])
 
 Adds a test called ``<name>``.  The test name may not contain spaces,
 quotes, or other characters special in CMake syntax.  The options are:
@@ -28,12 +29,18 @@
   directory set to the build directory corresponding to the
   current source directory.
 
+``COMMAND_EXPAND_LISTS``
+  Lists in ``COMMAND`` arguments will be expanded, including those
+  created with
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
 The given test command is expected to exit with code ``0`` to pass and
 non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test
 property is set.  Any output written to stdout or stderr will be
 captured by :manual:`ctest(1)` but does not affect the pass/fail status
-unless the :prop_test:`PASS_REGULAR_EXPRESSION` or
-:prop_test:`FAIL_REGULAR_EXPRESSION` test property is used.
+unless the :prop_test:`PASS_REGULAR_EXPRESSION`,
+:prop_test:`FAIL_REGULAR_EXPRESSION` or
+:prop_test:`SKIP_REGULAR_EXPRESSION` test property is used.
 
 The ``COMMAND`` and ``WORKING_DIRECTORY`` options may use "generator
 expressions" with the syntax ``$<...>``.  See the
diff --git a/Help/command/ctest_coverage.rst b/Help/command/ctest_coverage.rst
index 8d27b9c..d50f634 100644
--- a/Help/command/ctest_coverage.rst
+++ b/Help/command/ctest_coverage.rst
@@ -8,7 +8,7 @@
   ctest_coverage([BUILD <build-dir>] [APPEND]
                  [LABELS <label>...]
                  [RETURN_VALUE <result-var>]
-                 [CAPTURE_CMAKE_ERROR <result-var]
+                 [CAPTURE_CMAKE_ERROR <result-var>]
                  [QUIET]
                  )
 
diff --git a/Help/command/ctest_start.rst b/Help/command/ctest_start.rst
index 6db9a48..f0704ac 100644
--- a/Help/command/ctest_start.rst
+++ b/Help/command/ctest_start.rst
@@ -5,9 +5,9 @@
 
 ::
 
-  ctest_start(<model> [<source> [<binary>]] [TRACK <track>] [QUIET])
+  ctest_start(<model> [<source> [<binary>]] [GROUP <group>] [QUIET])
 
-  ctest_start([<model> [<source> [<binary>]]] [TRACK <track>] APPEND [QUIET])
+  ctest_start([<model> [<source> [<binary>]]] [GROUP <group>] APPEND [QUIET])
 
 Starts the testing for a given model.  The command should be called
 after the binary directory is initialized.
@@ -26,20 +26,21 @@
   Set the binary directory. If not specified, the value of
   :variable:`CTEST_BINARY_DIRECTORY` is used instead.
 
-``TRACK <track>``
-  If ``TRACK`` is used, the submissions will go to the specified track on the
-  CDash server. If no ``TRACK`` is specified, the name of the model is used by
-  default.
+``GROUP <group>``
+  If ``GROUP`` is used, the submissions will go to the specified group on the
+  CDash server. If no ``GROUP`` is specified, the name of the model is used by
+  default. This replaces the deprecated option ``TRACK``. Despite the name
+  change its behavior is unchanged.
 
 ``APPEND``
   If ``APPEND`` is used, the existing ``TAG`` is used rather than creating a new
   one based on the current time stamp. If you use ``APPEND``, you can omit the
-  ``<model>`` and ``TRACK <track>`` parameters, because they will be read from
+  ``<model>`` and ``GROUP <group>`` parameters, because they will be read from
   the generated ``TAG`` file. For example:
 
   .. code-block:: cmake
 
-    ctest_start(Experimental TRACK TrackExperimental)
+    ctest_start(Experimental GROUP GroupExperimental)
 
   Later, in another ``ctest -S`` script:
 
@@ -48,11 +49,11 @@
     ctest_start(APPEND)
 
   When the second script runs ``ctest_start(APPEND)``, it will read the
-  ``Experimental`` model and ``TrackExperimental`` track from the ``TAG`` file
+  ``Experimental`` model and ``GroupExperimental`` group from the ``TAG`` file
   generated by the first ``ctest_start()`` command. Please note that if you
-  call ``ctest_start(APPEND)`` and specify a different model or track than
+  call ``ctest_start(APPEND)`` and specify a different model or group than
   in the first ``ctest_start()`` command, a warning will be issued, and the
-  new model and track will be used.
+  new model and group will be used.
 
 ``QUIET``
   If ``QUIET`` is used, CTest will suppress any non-error messages that it
@@ -65,11 +66,11 @@
 
 .. code-block:: cmake
 
-  ctest_start(Experimental path/to/source path/to/binary TRACK SomeTrack QUIET APPEND)
+  ctest_start(Experimental path/to/source path/to/binary GROUP SomeGroup QUIET APPEND)
 
-  ctest_start(TRACK SomeTrack Experimental QUIET path/to/source APPEND path/to/binary)
+  ctest_start(GROUP SomeGroup Experimental QUIET path/to/source APPEND path/to/binary)
 
-  ctest_start(APPEND QUIET Experimental path/to/source TRACK SomeTrack path/to/binary)
+  ctest_start(APPEND QUIET Experimental path/to/source GROUP SomeGroup path/to/binary)
 
 However, for the sake of readability, it is recommended that you order your
 parameters in the order listed at the top of this page.
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst
index 4a69491..0a33da3 100644
--- a/Help/command/ctest_test.rst
+++ b/Help/command/ctest_test.rst
@@ -17,6 +17,7 @@
              [EXCLUDE_FIXTURE_SETUP <regex>]
              [EXCLUDE_FIXTURE_CLEANUP <regex>]
              [PARALLEL_LEVEL <level>]
+             [HARDWARE_SPEC_FILE <file>]
              [TEST_LOAD <threshold>]
              [SCHEDULE_RANDOM <ON|OFF>]
              [STOP_TIME <time-of-day>]
@@ -82,6 +83,11 @@
   Specify a positive number representing the number of tests to
   be run in parallel.
 
+``HARDWARE_SPEC_FILE <file>``
+  Specify a
+  :ref:`hardware specification file <ctest-hardware-specification-file>`. See
+  :ref:`ctest-hardware-allocation` for more information.
+
 ``TEST_LOAD <threshold>``
   While running tests in parallel, try not to start tests when they
   may cause the CPU load to pass above a given threshold.  If not
diff --git a/Help/command/enable_language.rst b/Help/command/enable_language.rst
index fb49b44..fdc44f2 100644
--- a/Help/command/enable_language.rst
+++ b/Help/command/enable_language.rst
@@ -1,7 +1,6 @@
 enable_language
 ---------------
-
-Enable a language (CXX/C/Fortran/etc)
+Enable a language (CXX/C/OBJC/OBJCXX/Fortran/etc)
 
 .. code-block:: cmake
 
@@ -10,7 +9,7 @@
 Enables support for the named language in CMake.  This is
 the same as the :command:`project` command but does not create any of the extra
 variables that are created by the project command.  Example languages
-are ``CXX``, ``C``, ``CUDA``, ``Fortran``, and ``ASM``.
+are ``CXX``, ``C``, ``CUDA``, ``OBJC``, ``OBJCXX``, ``Fortran``, and ``ASM``.
 
 If enabling ``ASM``, enable it last so that CMake can check whether
 compilers for other languages like ``C`` work for assembly too.
diff --git a/Help/command/file.rst b/Help/command/file.rst
index f99021e..c06451a 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -13,6 +13,7 @@
     file(`STRINGS`_ <filename> <out-var> [...])
     file(`\<HASH\> <HASH_>`_ <filename> <out-var>)
     file(`TIMESTAMP`_ <filename> <out-var> [...])
+    file(`GET_RUNTIME_DEPENDENCIES`_ [...])
 
   `Writing`_
     file({`WRITE`_ | `APPEND`_} <filename> <content>...)
@@ -130,6 +131,273 @@
 See the :command:`string(TIMESTAMP)` command for documentation of
 the ``<format>`` and ``UTC`` options.
 
+.. _GET_RUNTIME_DEPENDENCIES:
+
+.. code-block:: cmake
+
+  file(GET_RUNTIME_DEPENDENCIES
+    [RESOLVED_DEPENDENCIES_VAR <deps_var>]
+    [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
+    [CONFLICTING_DEPENDENICES_PREFIX <conflicting_deps_prefix>]
+    [EXECUTABLES [<executable_files>...]]
+    [LIBRARIES [<library_files>...]]
+    [MODULES [<module_files>...]]
+    [DIRECTORIES [<directories>...]]
+    [BUNDLE_EXECUTABLE <bundle_executable_file>]
+    [PRE_INCLUDE_REGEXES [<regexes>...]]
+    [PRE_EXCLUDE_REGEXES [<regexes>...]]
+    [POST_INCLUDE_REGEXES [<regexes>...]]
+    [POST_EXCLUDE_REGEXES [<regexes>...]]
+    )
+
+Recursively get the list of libraries depended on by the given files.
+
+Please note that this sub-command is not intended to be used in project mode.
+Instead, use it in an :command:`install(CODE)` or :command:`install(SCRIPT)`
+block. For example:
+
+.. code-block:: cmake
+
+  install(CODE [[
+    file(GET_RUNTIME_DEPENDENCIES
+      # ...
+      )
+    ]])
+
+The arguments are as follows:
+
+``RESOLVED_DEPENDENCIES_VAR <deps_var>``
+  Name of the variable in which to store the list of resolved dependencies.
+
+``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>``
+  Name of the variable in which to store the list of unresolved dependencies.
+  If this variable is not specified, and there are any unresolved dependencies,
+  an error is issued.
+
+``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>``
+  Variable prefix in which to store conflicting dependency information.
+  Dependencies are conflicting if two files with the same name are found in
+  two different directories. The list of filenames that conflict are stored in
+  ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list of paths
+  that were found for that filename are stored in
+  ``<conflicting_deps_prefix>_<filename>``.
+
+``EXECUTABLES <executable_files>``
+  List of executable files to read for dependencies. These are executables that
+  are typically created with :command:`add_executable`, but they do not have to
+  be created by CMake. On Apple platforms, the paths to these files determine
+  the value of ``@executable_path`` when recursively resolving the libraries.
+  Specifying ``STATIC`` libraries, ``MODULE`` s, or ``SHARED`` libraries here
+  will result in undefined behavior.
+
+``LIBRARIES <library_files>``
+  List of library files to read for dependencies. These are libraries that are
+  typically created with :command:`add_library(SHARED)`, but they do not have
+  to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE`` s, or
+  executables here will result in undefined behavior.
+
+``MODULES <module_files>``
+  List of loadable module files to read for dependencies. These are modules
+  that are typically created with :command:`add_library(MODULE)`, but they do
+  not have to be created by CMake. They are typically used by calling
+  ``dlopen()`` at runtime rather than linked at link time with ``ld -l``.
+  Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here
+  will result in undefined behavior.
+
+``DIRECTORIES <directories>``
+  List of additional directories to search for dependencies. On Linux
+  platforms, these directories are searched if the dependency is not found in
+  any of the other usual paths. If it is found in such a directory, a warning
+  is issued, because it means that the file is incomplete (it does not list all
+  of the directories that contain its dependencies.) On Windows platforms,
+  these directories are searched if the dependency is not found in any of the
+  other search paths, but no warning is issued, because searching other paths
+  is a normal part of Windows dependency resolution. On Apple platforms, this
+  argument has no effect.
+
+``BUNDLE_EXECTUBLE <bundle_executable_file>``
+  Executable to treat as the "bundle executable" when resolving libraries. On
+  Apple platforms, this argument determines the value of ``@executable_path``
+  when recursively resolving libraries for ``LIBRARIES`` and ``MODULES`` files.
+  It has no effect on ``EXECUTABLES`` files. On other platforms, it has no
+  effect. This is typically (but not always) one of the executables in the
+  ``EXECUTABLES`` argument which designates the "main" executable of the
+  package.
+
+The following arguments specify filters for including or excluding libraries to
+be resolved. See below for a full description of how they work.
+
+``PRE_INCLUDE_REGEXES <regexes>``
+  List of pre-include regexes through which to filter the names of
+  not-yet-resolved dependencies.
+
+``PRE_EXCLUDE_REGEXES <regexes>``
+  List of pre-exclude regexes through which to filter the names of
+  not-yet-resolved dependencies.
+
+``POST_INCLUDE_REGEXES <regexes>``
+  List of post-include regexes through which to filter the names of resolved
+  dependencies.
+
+``POST_EXCLUDE_REGEXES <regexes>``
+  List of post-exclude regexes through which to filter the names of resolved
+  dependencies.
+
+These arguments can be used to blacklist unwanted system libraries when
+resolving the dependencies, or to whitelist libraries from a specific
+directory. The filtering works as follows:
+
+1. If the not-yet-resolved dependency matches any of the
+   ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency
+   resolution proceeds to step 4.
+2. If the not-yet-resolved dependency matches any of the
+   ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency.
+3. Otherwise, dependency resolution proceeds.
+4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according to
+   the linking rules of the platform (see below).
+5. If the dependency is found, and its full path matches one of the
+   ``POST_INCLUDE_REGEXES``, the full path is added to the resolved
+   dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves
+   that library's own dependencies. Otherwise, resolution proceeds to step 6.
+6. If the dependency is found, but its full path matches one of the
+   ``POST_EXCLUDE_REGEXES``, it is not added to the resolved dependencies, and
+   dependency resolution stops for that dependency.
+7. If the dependency is found, and its full path does not match either
+   ``POST_INCLUDE_REGEXES`` or ``POST_EXCLUDE_REGEXES``, the full path is added
+   to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)``
+   recursively resolves that library's own dependencies.
+
+Different platforms have different rules for how dependencies are resolved.
+These specifics are described here.
+
+On Linux platforms, library resolution works as follows:
+
+1. If the depending file does not have any ``RUNPATH`` entries, and the library
+   exists in one of the depending file's ``RPATH`` entries, or its parents', in
+   that order, the dependency is resolved to that file.
+2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the
+   library exists in one of those entries, the dependency is resolved to that
+   file.
+3. Otherwise, if the library exists in one of the directories listed by
+   ``ldconfig``, the dependency is resolved to that file.
+4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, the
+   dependency is resolved to that file. In this case, a warning is issued,
+   because finding a file in one of the ``DIRECTORIES`` means that the
+   depending file is not complete (it does not list all the directories from
+   which it pulls dependencies.)
+5. Otherwise, the dependency is unresolved.
+
+On Windows platforms, library resolution works as follows:
+
+1. The dependent DLL name is converted to lowercase. Windows DLL names are
+   case-insensitive, and some linkers mangle the case of the DLL dependency
+   names. However, this makes it more difficult for ``PRE_INCLUDE_REGEXES``,
+   ``PRE_EXCLUDE_REGEXES``, ``POST_INCLUDE_REGEXES``, and
+   ``POST_EXCLUDE_REGEXES`` to properly filter DLL names - every regex would
+   have to check for both uppercase and lowercase letters. For example:
+
+   .. code-block:: cmake
+
+     file(GET_RUNTIME_DEPENDENCIES
+       # ...
+       PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$"
+       )
+
+   Converting the DLL name to lowercase allows the regexes to only match
+   lowercase names, thus simplifying the regex. For example:
+
+   .. code-block:: cmake
+
+     file(GET_RUNTIME_DEPENDENCIES
+       # ...
+       PRE_INCLUDE_REGEXES "^mylibrary\\.dll$"
+       )
+
+   This regex will match ``mylibrary.dll`` regardless of how it is cased,
+   either on disk or in the depending file. (For example, it will match
+   ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.)
+
+   Please note that the directory portion of any resolved DLLs retains its
+   casing and is not converted to lowercase. Only the filename portion is
+   converted.
+
+2. (**Not yet implemented**) If the depending file is a Windows Store app, and
+   the dependency is listed as a dependency in the application's package
+   manifest, the dependency is resolved to that file.
+3. Otherwise, if the library exists in the same directory as the depending
+   file, the dependency is resolved to that file.
+4. Otherwise, if the library exists in either the operating system's
+   ``system32`` directory or the ``Windows`` directory, in that order, the
+   dependency is resolved to that file.
+5. Otherwise, if the library exists in one of the directories specified by
+   ``DIRECTORIES``, in the order they are listed, the dependency is resolved to
+   that file. (In this case, a warning is not issued, because searching other
+   directories is a normal part of Windows library resolution.)
+6. Otherwise, the dependency is unresolved.
+
+On Apple platforms, library resolution works as follows:
+
+1. If the dependency starts with ``@executable_path/``, and an ``EXECUTABLES``
+   argument is in the process of being resolved, and replacing
+   ``@executable_path/`` with the directory of the executable yields an
+   existing file, the dependency is resolved to that file.
+2. Otherwise, if the dependency starts with ``@executable_path/``, and there is
+   a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` with
+   the directory of the bundle executable yields an existing file, the
+   dependency is resolved to that file.
+3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing
+   ``@loader_path/`` with the directory of the depending file yields an
+   existing file, the dependency is resolved to that file.
+4. Otherwise, if the dependency starts with ``@rpath/``, and replacing
+   ``@rpath/`` with one of the ``RPATH`` entries of the depending file yields
+   an existing file, the dependency is resolved to that file. (Note that
+   ``RPATH`` entries that start with ``@executable_path/`` or ``@loader_path/``
+   also have these items replaced with the appropriate path.)
+5. Otherwise, if the dependency is an absolute file that exists, the dependency
+   is resolved to that file.
+6. Otherwise, the dependency is unresolved.
+
+This function accepts several variables that determine which tool is used for
+dependency resolution:
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
+
+  Determines which operating system and executable format the files are built
+  for. This could be one of several values:
+
+  * ``linux+elf``
+  * ``windows+pe``
+  * ``macos+macho``
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
+
+  Determines the tool to use for dependency resolution. It could be one of
+  several values, depending on the value of
+  :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`:
+
+  ================================================= =============================================
+     ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM``       ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL``
+  ================================================= =============================================
+  ``linux+elf``                                     ``objdump``
+  ``windows+pe``                                    ``dumpbin``
+  ``windows+pe``                                    ``objdump``
+  ``macos+macho``                                   ``otool``
+  ================================================= =============================================
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
+.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
+
+  Determines the path to the tool to use for dependency resolution. This is the
+  actual path to ``objdump``, ``dumpbin``, or ``otool``.
+
+  If this variable is not specified, it is determined automatically by system
+  introspection.
+
 Writing
 ^^^^^^^
 
@@ -235,6 +503,11 @@
 specified, the results will be returned as relative paths to the given
 path.  The results will be ordered lexicographically.
 
+On Windows and macOS, globbing is case-insensitive even if the underlying
+filesystem is case-sensitive (both filenames and globbing expressions are
+converted to lowercase before matching).  On other platforms, globbing is
+case-sensitive.
+
 If the ``CONFIGURE_DEPENDS`` flag is specified, CMake will add logic
 to the main build system check target to rerun the flagged ``GLOB`` commands
 at build time. If any of the outputs change, CMake will regenerate the build
diff --git a/Help/command/find_file.rst b/Help/command/find_file.rst
index 2a14ad7..3f03f37 100644
--- a/Help/command/find_file.rst
+++ b/Help/command/find_file.rst
@@ -17,11 +17,10 @@
 .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_INCLUDE_PATH`
 .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH`
 
-.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``INCLUDE``.
-   On Windows hosts:
-   ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
-   is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|, and the
-   directories in ``PATH`` itself.
+.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
+      ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
diff --git a/Help/command/find_library.rst b/Help/command/find_library.rst
index 0861d67..8a55aca 100644
--- a/Help/command/find_library.rst
+++ b/Help/command/find_library.rst
@@ -17,11 +17,10 @@
 .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_LIBRARY_PATH`
 .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH`
 
-.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``LIB``.
-   On Windows hosts:
-   ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set,
-   and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|,
-   and the directories in ``PATH`` itself.
+.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
+      ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set,
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index e5e5b2c..2186bd8 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -293,13 +293,15 @@
    The package root variables are maintained as a stack so if
    called from within a find module, root paths from the parent's find
    module will also be searched after paths for the current package.
-   This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed.
+   This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
+   the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
    See policy :policy:`CMP0074`.
 
 2. Search paths specified in cmake-specific cache variables.  These
    are intended to be used on the command line with a ``-DVAR=value``.
    The values are interpreted as :ref:`semicolon-separated lists <CMake Language Lists>`.
-   This can be skipped if ``NO_CMAKE_PATH`` is passed::
+   This can be skipped if ``NO_CMAKE_PATH`` is passed or by setting the
+   :variable:`CMAKE_FIND_USE_CMAKE_PATH` to ``FALSE``::
 
      CMAKE_PREFIX_PATH
      CMAKE_FRAMEWORK_PATH
@@ -309,7 +311,8 @@
    These are intended to be set in the user's shell configuration,
    and therefore use the host's native path separator
    (``;`` on Windows and ``:`` on UNIX).
-   This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed::
+   This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed or by setting
+   the :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH` to ``FALSE``::
 
      <PackageName>_DIR
      CMAKE_PREFIX_PATH
@@ -322,7 +325,8 @@
    be specified with the ``PATHS`` option.
 
 5. Search the standard system environment variables.  This can be
-   skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is passed.  Path entries
+   skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is passed  or by setting the
+   :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` to ``FALSE``. Path entries
    ending in ``/bin`` or ``/sbin`` are automatically converted to their
    parent directories::
 
@@ -330,14 +334,17 @@
 
 6. Search paths stored in the CMake :ref:`User Package Registry`.
    This can be skipped if ``NO_CMAKE_PACKAGE_REGISTRY`` is passed or by
-   setting the :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`
-   to ``TRUE``.
+   setting the variable :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`
+   to ``FALSE`` or the deprecated variable
+   :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` to ``TRUE``.
+
    See the :manual:`cmake-packages(7)` manual for details on the user
    package registry.
 
 7. Search cmake variables defined in the Platform files for the
    current system.  This can be skipped if ``NO_CMAKE_SYSTEM_PATH`` is
-   passed::
+   passed or by setting the :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`
+   to ``FALSE``::
 
      CMAKE_SYSTEM_PREFIX_PATH
      CMAKE_SYSTEM_FRAMEWORK_PATH
diff --git a/Help/command/find_path.rst b/Help/command/find_path.rst
index 988a3fa..52ffe3c 100644
--- a/Help/command/find_path.rst
+++ b/Help/command/find_path.rst
@@ -17,11 +17,10 @@
 .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_INCLUDE_PATH`
 .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH`
 
-.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``INCLUDE``.
-   On Windows hosts:
-   ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
-   is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|, and the
-   directories in ``PATH`` itself.
+.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
+      ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
diff --git a/Help/command/find_program.rst b/Help/command/find_program.rst
index 4f00773..e2ff693 100644
--- a/Help/command/find_program.rst
+++ b/Help/command/find_program.rst
@@ -15,7 +15,8 @@
 .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_PROGRAM_PATH`
 .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_APPBUNDLE_PATH`
 
-.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: ``PATH``
+.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` itself.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts no extra search paths are included
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    |CMAKE_SYSTEM_PREFIX_PATH_XXX_SUBDIR|
diff --git a/Help/command/if.rst b/Help/command/if.rst
index d8e3a45..be992df 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -29,6 +29,8 @@
 repeat of the argument of the opening
 ``if`` command.
 
+.. _`Condition Syntax`:
+
 Condition Syntax
 ^^^^^^^^^^^^^^^^
 
diff --git a/Help/command/install.rst b/Help/command/install.rst
index ab6fef6..5affc5b 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -126,6 +126,8 @@
   marked with the ``FRAMEWORK`` property on macOS (see ``FRAMEWORK``
   below.) For DLL platforms (all Windows-based systems including
   Cygwin), the DLL import library is treated as an ``ARCHIVE`` target.
+  On AIX, the linker import file created for executables with
+  :prop_tgt:`ENABLE_EXPORTS` is treated as an ``ARCHIVE`` target.
 
 ``LIBRARY``
   Module libraries are always treated as ``LIBRARY`` targets. For non-
diff --git a/Help/command/message.rst b/Help/command/message.rst
index 5dca6b4..c614286 100644
--- a/Help/command/message.rst
+++ b/Help/command/message.rst
@@ -57,9 +57,14 @@
 :manual:`CMake GUI <cmake-gui(1)>` displays all messages in its log area.
 The :manual:`curses interface <ccmake(1)>` shows ``STATUS`` to ``TRACE``
 messages one at a time on a status line and other messages in an
-interactive pop-up box.  The ``--loglevel`` command-line option to each of
+interactive pop-up box.  The ``--log-level`` command-line option to each of
 these tools can be used to control which messages will be shown.
 
+Messages of log levels ``NOTICE`` and below will also have each line preceded
+by the content of the :variable:`CMAKE_MESSAGE_INDENT` variable (converted to
+a single string by concatenating its list items).  For ``STATUS`` to ``TRACE``
+messages, this indenting content will be inserted after the hyphens.
+
 CMake Warning and Error message text displays using a simple markup
 language.  Non-indented text is formatted in line-wrapped paragraphs
 delimited by newlines.  Indented text is considered pre-formatted.
diff --git a/Help/command/project.rst b/Help/command/project.rst
index baf18be..3951456 100644
--- a/Help/command/project.rst
+++ b/Help/command/project.rst
@@ -87,7 +87,8 @@
   Can also be specified without ``LANGUAGES`` keyword per the first, short signature.
 
   Selects which programming languages are needed to build the project.
-  Supported languages include ``C``, ``CXX`` (i.e.  C++), ``CUDA``, ``Fortran``, and ``ASM``.
+  Supported languages include ``C``, ``CXX`` (i.e.  C++), ``CUDA``,
+  ``OBJC`` (i.e. Objective-C), ``OBJCXX``, ``Fortran``, and ``ASM``.
   By default ``C`` and ``CXX`` are enabled if no language options are given.
   Specify language ``NONE``, or use the ``LANGUAGES`` keyword and list no languages,
   to skip enabling any languages.
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 2e89d7b..81a2061 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -22,8 +22,8 @@
     string(`PREPEND`_ <string-var> [<input>...])
     string(`CONCAT`_ <out-var> [<input>...])
     string(`JOIN`_ <glue> <out-var> [<input>...])
-    string(`TOLOWER`_ <string1> <out-var>)
-    string(`TOUPPER`_ <string1> <out-var>)
+    string(`TOLOWER`_ <string> <out-var>)
+    string(`TOUPPER`_ <string> <out-var>)
     string(`LENGTH`_ <string> <out-var>)
     string(`SUBSTRING`_ <string> <begin> <length> <out-var>)
     string(`STRIP`_ <string> <out-var>)
@@ -38,7 +38,7 @@
 
   `Generation`_
     string(`ASCII`_ <number>... <out-var>)
-    string(`CONFIGURE`_ <string1> <out-var> [...])
+    string(`CONFIGURE`_ <string> <out-var> [...])
     string(`MAKE_C_IDENTIFIER`_ <string> <out-var>)
     string(`RANDOM`_ [<option>...] <out-var>)
     string(`TIMESTAMP`_ <out-var> [<format string>] [UTC])
@@ -51,23 +51,28 @@
 
 .. code-block:: cmake
 
-  string(FIND <string> <substring> <output variable> [REVERSE])
+  string(FIND <string> <substring> <output_variable> [REVERSE])
 
-Return the position where the given substring was found in
-the supplied string.  If the ``REVERSE`` flag was used, the command will
+Return the position where the given ``<substring>`` was found in
+the supplied ``<string>``.  If the ``REVERSE`` flag was used, the command will
 search for the position of the last occurrence of the specified
-substring.  If the substring is not found, a position of -1 is returned.
+``<substring>``.  If the ``<substring>`` is not found, a position of -1 is
+returned.
+
+The ``string(FIND)`` subcommand treats all strings as ASCII-only characters.
+The index stored in ``<output_variable>`` will also be counted in bytes,
+so strings containing multi-byte characters may lead to unexpected results.
 
 .. _REPLACE:
 
 .. code-block:: cmake
 
   string(REPLACE <match_string>
-         <replace_string> <output variable>
+         <replace_string> <output_variable>
          <input> [<input>...])
 
-Replace all occurrences of ``match_string`` in the input
-with ``replace_string`` and store the result in the output.
+Replace all occurrences of ``<match_string>`` in the ``<input>``
+with ``<replace_string>`` and store the result in the ``<output_variable>``.
 
 Regular Expressions
 ^^^^^^^^^^^^^^^^^^^
@@ -77,9 +82,10 @@
 .. code-block:: cmake
 
   string(REGEX MATCH <regular_expression>
-         <output variable> <input> [<input>...])
+         <output_variable> <input> [<input>...])
 
-Match the regular expression once and store the match in the output variable.
+Match the ``<regular_expression>`` once and store the match in the
+``<output_variable>``.
 All ``<input>`` arguments are concatenated before matching.
 
 .. _`REGEX MATCHALL`:
@@ -87,10 +93,10 @@
 .. code-block:: cmake
 
   string(REGEX MATCHALL <regular_expression>
-         <output variable> <input> [<input>...])
+         <output_variable> <input> [<input>...])
 
-Match the regular expression as many times as possible and store the matches
-in the output variable as a list.
+Match the ``<regular_expression>`` as many times as possible and store the
+matches in the ``<output_variable>`` as a list.
 All ``<input>`` arguments are concatenated before matching.
 
 .. _`REGEX REPLACE`:
@@ -98,16 +104,17 @@
 .. code-block:: cmake
 
   string(REGEX REPLACE <regular_expression>
-         <replace_expression> <output variable>
+         <replacement_expression> <output_variable>
          <input> [<input>...])
 
-Match the regular expression as many times as possible and substitute the
-replacement expression for the match in the output.
+Match the ``<regular_expression>`` as many times as possible and substitute
+the ``<replacement_expression>`` for the match in the output.
 All ``<input>`` arguments are concatenated before matching.
 
-The replace expression may refer to paren-delimited subexpressions of the
-match using ``\1``, ``\2``, ..., ``\9``.  Note that two backslashes (``\\1``)
-are required in CMake code to get a backslash through argument parsing.
+The ``<replacement_expression>`` may refer to parenthesis-delimited
+subexpressions of the match using ``\1``, ``\2``, ..., ``\9``.  Note that
+two backslashes (``\\1``) are required in CMake code to get a backslash
+through argument parsing.
 
 .. _`Regex Specification`:
 
@@ -180,103 +187,109 @@
 
 .. code-block:: cmake
 
-  string(APPEND <string variable> [<input>...])
+  string(APPEND <string_variable> [<input>...])
 
-Append all the input arguments to the string.
+Append all the ``<input>`` arguments to the string.
 
 .. _PREPEND:
 
 .. code-block:: cmake
 
-  string(PREPEND <string variable> [<input>...])
+  string(PREPEND <string_variable> [<input>...])
 
-Prepend all the input arguments to the string.
+Prepend all the ``<input>`` arguments to the string.
 
 .. _CONCAT:
 
 .. code-block:: cmake
 
-  string(CONCAT <output variable> [<input>...])
+  string(CONCAT <output_variable> [<input>...])
 
-Concatenate all the input arguments together and store
-the result in the named output variable.
+Concatenate all the ``<input>`` arguments together and store
+the result in the named ``<output_variable>``.
 
 .. _JOIN:
 
 .. code-block:: cmake
 
-  string(JOIN <glue> <output variable> [<input>...])
+  string(JOIN <glue> <output_variable> [<input>...])
 
-Join all the input arguments together using the glue
-string and store the result in the named output variable.
+Join all the ``<input>`` arguments together using the ``<glue>``
+string and store the result in the named ``<output_variable>``.
 
-To join list's elements, use preferably the ``JOIN`` operator
-from :command:`list` command. This allows for the elements to have
+To join a list's elements, prefer to use the ``JOIN`` operator
+from the :command:`list` command.  This allows for the elements to have
 special characters like ``;`` in them.
 
 .. _TOLOWER:
 
 .. code-block:: cmake
 
-  string(TOLOWER <string1> <output variable>)
+  string(TOLOWER <string> <output_variable>)
 
-Convert string to lower characters.
+Convert ``<string>`` to lower characters.
 
 .. _TOUPPER:
 
 .. code-block:: cmake
 
-  string(TOUPPER <string1> <output variable>)
+  string(TOUPPER <string> <output_variable>)
 
-Convert string to upper characters.
+Convert ``<string>`` to upper characters.
 
 .. _LENGTH:
 
 .. code-block:: cmake
 
-  string(LENGTH <string> <output variable>)
+  string(LENGTH <string> <output_variable>)
 
-Store in an output variable a given string's length.
+Store in an ``<output_variable>`` a given string's length in bytes.
+Note that this means if ``<string>`` contains multi-byte characters, the
+result stored in ``<output_variable>`` will *not* be the number of characters.
 
 .. _SUBSTRING:
 
 .. code-block:: cmake
 
-  string(SUBSTRING <string> <begin> <length> <output variable>)
+  string(SUBSTRING <string> <begin> <length> <output_variable>)
 
-Store in an output variable a substring of a given string.  If length is
-``-1`` the remainder of the string starting at begin will be returned.
-If string is shorter than length then end of string is used instead.
+Store in an ``<output_variable>`` a substring of a given ``<string>``.  If
+``<length>`` is ``-1`` the remainder of the string starting at ``<begin>``
+will be returned.  If ``<string>`` is shorter than ``<length>`` then the
+end of the string is used instead.
+
+Both ``<begin>`` and ``<length>`` are counted in bytes, so care must
+be exercised if ``<string>`` could contain multi-byte characters.
 
 .. note::
-  CMake 3.1 and below reported an error if length pointed past
-  the end of string.
+  CMake 3.1 and below reported an error if ``<length>`` pointed past
+  the end of ``<string>``.
 
 .. _STRIP:
 
 .. code-block:: cmake
 
-  string(STRIP <string> <output variable>)
+  string(STRIP <string> <output_variable>)
 
-Store in an output variable a substring of a given string with leading and
-trailing spaces removed.
+Store in an ``<output_variable>`` a substring of a given ``<string>`` with
+leading and trailing spaces removed.
 
 .. _GENEX_STRIP:
 
 .. code-block:: cmake
 
-  string(GENEX_STRIP <input string> <output variable>)
+  string(GENEX_STRIP <string> <output_variable>)
 
 Strip any :manual:`generator expressions <cmake-generator-expressions(7)>`
-from the ``input string`` and store the result in the ``output variable``.
+from the input ``<string>`` and store the result in the ``<output_variable>``.
 
 .. _REPEAT:
 
 .. code-block:: cmake
 
-  string(REPEAT <input string> <count> <output variable>)
+  string(REPEAT <string> <count> <output_variable>)
 
-Produce the output string as repetion of ``input string`` ``count`` times.
+Produce the output string as the input ``<string>`` repeated ``<count>`` times.
 
 Comparison
 ^^^^^^^^^^
@@ -285,14 +298,14 @@
 
 .. code-block:: cmake
 
-  string(COMPARE LESS <string1> <string2> <output variable>)
-  string(COMPARE GREATER <string1> <string2> <output variable>)
-  string(COMPARE EQUAL <string1> <string2> <output variable>)
-  string(COMPARE NOTEQUAL <string1> <string2> <output variable>)
-  string(COMPARE LESS_EQUAL <string1> <string2> <output variable>)
-  string(COMPARE GREATER_EQUAL <string1> <string2> <output variable>)
+  string(COMPARE LESS <string1> <string2> <output_variable>)
+  string(COMPARE GREATER <string1> <string2> <output_variable>)
+  string(COMPARE EQUAL <string1> <string2> <output_variable>)
+  string(COMPARE NOTEQUAL <string1> <string2> <output_variable>)
+  string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
+  string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)
 
-Compare the strings and store true or false in the output variable.
+Compare the strings and store true or false in the ``<output_variable>``.
 
 .. _`Supported Hash Algorithms`:
 
@@ -303,9 +316,9 @@
 
 .. code-block:: cmake
 
-  string(<HASH> <output variable> <input>)
+  string(<HASH> <output_variable> <input>)
 
-Compute a cryptographic hash of the input string.
+Compute a cryptographic hash of the ``<input>`` string.
 The supported ``<HASH>`` algorithm names are:
 
 ``MD5``
@@ -336,7 +349,7 @@
 
 .. code-block:: cmake
 
-  string(ASCII <number> [<number> ...] <output variable>)
+  string(ASCII <number> [<number> ...] <output_variable>)
 
 Convert all numbers into corresponding ASCII characters.
 
@@ -344,31 +357,31 @@
 
 .. code-block:: cmake
 
-  string(CONFIGURE <string1> <output variable>
+  string(CONFIGURE <string> <output_variable>
          [@ONLY] [ESCAPE_QUOTES])
 
-Transform a string like :command:`configure_file` transforms a file.
+Transform a ``<string>`` like :command:`configure_file` transforms a file.
 
 .. _MAKE_C_IDENTIFIER:
 
 .. code-block:: cmake
 
-  string(MAKE_C_IDENTIFIER <input string> <output variable>)
+  string(MAKE_C_IDENTIFIER <string> <output_variable>)
 
-Convert each non-alphanumeric character in the ``<input string>`` to an
-underscore and store the result in the ``<output variable>``.  If the first
-character of the string is a digit, an underscore will also be prepended to
-the result.
+Convert each non-alphanumeric character in the input ``<string>`` to an
+underscore and store the result in the ``<output_variable>``.  If the first
+character of the ``<string>`` is a digit, an underscore will also be prepended
+to the result.
 
 .. _RANDOM:
 
 .. code-block:: cmake
 
   string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>]
-         [RANDOM_SEED <seed>] <output variable>)
+         [RANDOM_SEED <seed>] <output_variable>)
 
-Return a random string of given length consisting of
-characters from the given alphabet.  Default length is 5 characters
+Return a random string of given ``<length>`` consisting of
+characters from the given ``<alphabet>``.  Default length is 5 characters
 and default alphabet is all numbers and upper and lower case letters.
 If an integer ``RANDOM_SEED`` is given, its value will be used to seed the
 random number generator.
@@ -377,18 +390,18 @@
 
 .. code-block:: cmake
 
-  string(TIMESTAMP <output variable> [<format string>] [UTC])
+  string(TIMESTAMP <output_variable> [<format_string>] [UTC])
 
 Write a string representation of the current date
-and/or time to the output variable.
+and/or time to the ``<output_variable>``.
 
-Should the command be unable to obtain a timestamp the output variable
-will be set to the empty string "".
+If the command is unable to obtain a timestamp, the ``<output_variable>``
+will be set to the empty string ``""``.
 
 The optional ``UTC`` flag requests the current date/time representation to
 be in Coordinated Universal Time (UTC) rather than local time.
 
-The optional ``<format string>`` may contain the following format
+The optional ``<format_string>`` may contain the following format
 specifiers:
 
 ::
@@ -415,7 +428,7 @@
 Unknown format specifiers will be ignored and copied to the output
 as-is.
 
-If no explicit ``<format string>`` is given it will default to:
+If no explicit ``<format_string>`` is given, it will default to:
 
 ::
 
@@ -432,7 +445,7 @@
 
 .. code-block:: cmake
 
-  string(UUID <output variable> NAMESPACE <namespace> NAME <name>
+  string(UUID <output_variable> NAMESPACE <namespace> NAME <name>
          TYPE <MD5|SHA1> [UPPER])
 
 Create a universally unique identifier (aka GUID) as per RFC4122
@@ -441,6 +454,6 @@
 The hash algorithm can be either ``MD5`` (Version 3 UUID) or
 ``SHA1`` (Version 5 UUID).
 A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``
-where each `x` represents a lower case hexadecimal character.
-Where required an uppercase representation can be requested
+where each ``x`` represents a lower case hexadecimal character.
+Where required, an uppercase representation can be requested
 with the optional ``UPPER`` flag.
diff --git a/Help/command/target_precompile_headers.rst b/Help/command/target_precompile_headers.rst
new file mode 100644
index 0000000..3a32f41
--- /dev/null
+++ b/Help/command/target_precompile_headers.rst
@@ -0,0 +1,85 @@
+target_precompile_headers
+-------------------------
+
+Add a list of header files to precompile.
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    <INTERFACE|PUBLIC|PRIVATE> [header1...]
+    [<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
+
+  target_precompile_headers(<target> REUSE_FROM <other_target>)
+
+Adds header files to :prop_tgt:`PRECOMPILE_HEADERS` or
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` target properties.
+
+The second signature will reuse an already precompiled header file artefact
+from another target. This is done by setting the
+:prop_tgt:`PRECOMPILE_HEADERS_REUSE_FROM` to ``<other_target>`` value.
+The ``<other_target>`` will become a dependency of ``<target>``.
+
+.. note::
+
+  The second signature will require the same set of compiler options,
+  compiler flags, compiler definitions for both ``<target>``, and
+  ``<other_target>``. Compilers (e.g. GCC) will issue a warning if the
+  precompiled header file cannot be used (``-Winvalid-pch``).
+
+Precompiling header files can speed up compilation by creating a partially
+processed version of some header files, and then using that version during
+compilations rather than repeatedly parsing the original headers.
+
+The named ``<target>`` must have been created by a command such as
+:command:`add_executable` or :command:`add_library` and must not be an
+:ref:`ALIAS target <Alias Targets>`.
+
+The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
+specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
+items will populate the :prop_tgt:`PRECOMPILE_HEADERS` property of
+``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` property of ``<target>``.
+(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items.)
+Repeated calls for the same ``<target>`` append items in the order called.
+
+Arguments to ``target_precompile_headers`` may use "generator expressions"
+with the syntax ``$<...>``.
+See the :manual:`cmake-generator-expressions(7)` manual for available
+expressions.  See the :manual:`cmake-compile-features(7)` manual for
+information on compile features and a list of supported compilers.
+
+Usage
+^^^^^
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    PUBLIC
+      project_header.h
+    PRIVATE
+      [["other_header.h"]]
+      <unordered_map>
+  )
+
+The list of header files is used to generate a header file named
+``cmake_pch.h|xx`` which is used to generate the precompiled header file
+(``.pch``, ``.gch``, ``.pchi``) artifact.  The ``cmake_pch.h|xx`` header
+file will be force included (``-include`` for GCC, ``/FI`` for MSVC) to
+all source files, so sources do not need to have ``#include "pch.h"``.
+
+Header file names specified with angle brackets (e.g. ``<unordered_map>``) or
+explicit double quotes (escaped for the :manual:`cmake-language(7)`,
+e.g. ``[["other_header.h"]]``) will be treated as is, and include directories
+must be available for the compiler to find them.  Other header file names
+(e.g. ``project_header.h``) are interpreted as being relative to the current
+source directory (e.g. :variable:`CMAKE_CURRENT_SOURCE_DIR`) and will be
+included by absolute path.
+
+See Also
+^^^^^^^^
+
+For disabling precompile headers for specific targets there is the
+property :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`.
+
+For skipping certain source files there is the source file property
+:prop_sf:`SKIP_PRECOMPILE_HEADERS`.
diff --git a/Help/cpack_gen/archive.rst b/Help/cpack_gen/archive.rst
index b288aad..d455f4b 100644
--- a/Help/cpack_gen/archive.rst
+++ b/Help/cpack_gen/archive.rst
@@ -9,6 +9,7 @@
   - TGZ (.tar.gz)
   - TXZ (.tar.xz)
   - TZ (.tar.Z)
+  - TZST (.tar.zst)
   - ZIP (.zip)
 
 Variables specific to CPack Archive generator
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst
index 23f0515..db71c87 100644
--- a/Help/cpack_gen/deb.rst
+++ b/Help/cpack_gen/deb.rst
@@ -179,16 +179,24 @@
  * Default   : ``CPACK_PACKAGE_CONTACT``
 
 .. variable:: CPACK_DEBIAN_PACKAGE_DESCRIPTION
-              CPACK_COMPONENT_<COMPONENT>_DESCRIPTION
+              CPACK_DEBIAN_<COMPONENT>_DESCRIPTION
 
  The Debian package description
 
  * Mandatory : YES
  * Default   :
 
-   - :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` if set or
-   - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY`
+   - :variable:`CPACK_DEBIAN_<COMPONENT>_DESCRIPTION` (component
+     based installers only) if set, or :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` if set, or
+   - :variable:`CPACK_COMPONENT_<compName>_DESCRIPTION` (component
+     based installers only) if set, or :variable:`CPACK_PACKAGE_DESCRIPTION` if set, or
+   - content of the file specified in :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` if set
 
+ If after that description is not set, :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` going to be
+ used if set. Otherwise, :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` will be added as the first
+ line of description as defined in `Debian Policy Manual`_.
+
+.. _Debian Policy Manual: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description
 
 .. variable:: CPACK_DEBIAN_PACKAGE_SECTION
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_SECTION
diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst
index dde4943..7fb5a12 100644
--- a/Help/cpack_gen/wix.rst
+++ b/Help/cpack_gen/wix.rst
@@ -95,6 +95,10 @@
 
  If this variable is not set, it will be initialized with CPACK_PACKAGE_NAME
 
+ If this variable is set to ``.``, then application shortcuts will be
+ created directly in the start menu and the uninstaller shortcut will be
+ omitted.
+
 .. variable:: CPACK_WIX_CULTURES
 
  Language(s) of the installer
diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst
index 1153a09..37a1d3a 100644
--- a/Help/dev/maint.rst
+++ b/Help/dev/maint.rst
@@ -92,6 +92,45 @@
 .. _`CMake Review Process`: review.rst
 .. _`CMake CDash Page`: https://open.cdash.org/index.php?project=CMake
 
+Create Release Version
+======================
+
+When the ``release`` branch is ready to create a new release, follow the
+steps in the above `Maintain Current Release`_ section to checkout a local
+``release-$ver`` branch, where ``$ver`` is the version number of the
+current release in the form ``$major.$minor``.
+
+Edit ``Source/CMakeVersion.cmake`` to set the full version:
+
+.. code-block:: cmake
+
+  # CMake version number components.
+  set(CMake_VERSION_MAJOR $major)
+  set(CMake_VERSION_MINOR $minor)
+  set(CMake_VERSION_PATCH $patch)
+  #set(CMake_VERSION_RC $rc) # uncomment for release candidates
+
+In the following we use the placeholder ``$fullver`` for the full version
+number of the new release with the form ``$major.$minor.$patch[-rc$rc]``.
+If the version is not a release candidate, comment out the RC version
+component above and leave off the ``-rc$rc`` suffix from ``$fullver``.
+
+Commit the release version with the **exact** message ``CMake $fullver``:
+
+.. code-block:: shell
+
+  git commit -m "CMake $fullver"
+
+Tag the release using an annotated tag with the same message as the
+commit and named with the **exact** form ``v$fullver``:
+
+.. code-block:: shell
+
+  git tag -s -m "CMake $fullver" "v$fullver"
+
+Follow the steps in the above `Maintain Current Release`_ section to
+merge the ``release-$ver`` branch into ``master`` and publish both.
+
 Branch a New Release
 ====================
 
@@ -178,7 +217,7 @@
   the CMake Release Notes index page.
 
 Update ``Source/CMakeVersion.cmake`` to set the version to
-``$major.$minor.0-rc1``:
+``$major.$minor.0-rc0``:
 
 .. code-block:: cmake
 
@@ -186,7 +225,7 @@
   set(CMake_VERSION_MAJOR $major)
   set(CMake_VERSION_MINOR $minor)
   set(CMake_VERSION_PATCH 0)
-  set(CMake_VERSION_RC 1)
+  set(CMake_VERSION_RC 0)
 
 Update uses of ``DEVEL_CMAKE_VERSION`` in the source tree to mention the
 actual version number:
@@ -197,7 +236,7 @@
 
 Commit with a message such as::
 
-  CMake $major.$minor.0-rc1 version update
+  Begin $ver release versioning
 
 Merge the ``release-$ver`` branch to ``master``:
 
@@ -225,7 +264,7 @@
   set(CMake_VERSION_MAJOR $major)
   set(CMake_VERSION_MINOR $minor)
   set(CMake_VERSION_PATCH $date)
-  #set(CMake_VERSION_RC 1)
+  #set(CMake_VERSION_RC 0)
 
 Commit with a message such as::
 
diff --git a/Help/dev/source.rst b/Help/dev/source.rst
index 47baff4..0f7488b 100644
--- a/Help/dev/source.rst
+++ b/Help/dev/source.rst
@@ -74,6 +74,8 @@
 
   * ``Utilities/Release/``:
     Scripts used to package CMake itself for distribution on ``cmake.org``.
+    See `Utilities/Release/README.rst`_.
 
 .. _`CMake Documentation Guide`: documentation.rst
 .. _`Tests/README.rst`: ../../Tests/README.rst
+.. _`Utilities/Release/README.rst`: ../../Utilities/Release/README.rst
diff --git a/Help/generator/Ninja.rst b/Help/generator/Ninja.rst
index 51ef49b..c75d2c4 100644
--- a/Help/generator/Ninja.rst
+++ b/Help/generator/Ninja.rst
@@ -3,9 +3,9 @@
 
 Generates build.ninja files.
 
-A build.ninja file is generated into the build tree.  Recent versions
-of the ninja program can build the project through the ``all`` target.
-An ``install`` target is also provided.
+A ``build.ninja`` file is generated into the build tree.  Use the ninja
+program to build the project through the ``all`` target and install the
+project through the ``install`` (or ``install/strip``) target.
 
 For each subdirectory ``sub/dir`` of the project, additional targets
 are generated:
@@ -16,6 +16,13 @@
 ``sub/dir/install``
   Runs the install step in the subdirectory, if any.
 
+``sub/dir/install/strip``
+  Runs the install step in the subdirectory followed by a ``CMAKE_STRIP`` command,
+  if any.
+
+  The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which
+  removes symbols information from generated binaries.
+
 ``sub/dir/test``
   Runs the test step in the subdirectory, if any.
 
diff --git a/Help/generator/Unix Makefiles.rst b/Help/generator/Unix Makefiles.rst
index 1e65ee1..dfe4ecb 100644
--- a/Help/generator/Unix Makefiles.rst
+++ b/Help/generator/Unix Makefiles.rst
@@ -3,6 +3,29 @@
 
 Generates standard UNIX makefiles.
 
-A hierarchy of UNIX makefiles is generated into the build tree.  Any
-standard UNIX-style make program can build the project through the
-default ``all`` target.  An ``install`` target is also provided.
+A hierarchy of UNIX makefiles is generated into the build tree.  Use
+any standard UNIX-style make program to build the project through
+the ``all`` target and install the project through the ``install``
+(or ``install/strip``) target.
+
+For each subdirectory ``sub/dir`` of the project a UNIX makefile will
+be created, containing the following targets:
+
+``all``
+  Depends on all targets required by the subdirectory.
+
+``install``
+  Runs the install step in the subdirectory, if any.
+
+``install/strip``
+  Runs the install step in the subdirectory followed by a ``CMAKE_STRIP`` command,
+  if any.
+
+  The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which
+  removes symbols information from generated binaries.
+
+``test``
+  Runs the test step in the subdirectory, if any.
+
+``package``
+  Runs the package step in the subdirectory, if any.
diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt
new file mode 100644
index 0000000..eca79d9
--- /dev/null
+++ b/Help/guide/tutorial/Complete/CMakeLists.txt
@@ -0,0 +1,120 @@
+cmake_minimum_required(VERSION 3.15)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
+
+# 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)
+
+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(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
+
+# install the configuration targets
+install(EXPORT MathFunctionsTargets
+  FILE MathFunctionsTargets.cmake
+  DESTINATION lib/cmake/MathFunctions
+)
+
+include(CMakePackageConfigHelpers)
+# generate the config file that is includes the exports
+configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
+  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
+  INSTALL_DESTINATION "lib/cmake/example"
+  NO_SET_AND_CHECK_MACRO
+  NO_CHECK_REQUIRED_COMPONENTS_MACRO
+  )
+# generate the version file for the config file
+write_basic_package_version_file(
+  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
+  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
+  COMPATIBILITY AnyNewerVersion
+)
+
+# install the configuration file
+install(FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
+  DESTINATION lib/cmake/MathFunctions
+  )
+
+# generate the export targets for the build tree
+# needs to be after the install(TARGETS ) command
+export(EXPORT MathFunctionsTargets
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
+)
diff --git a/Help/guide/tutorial/Complete/CTestConfig.cmake b/Help/guide/tutorial/Complete/CTestConfig.cmake
new file mode 100644
index 0000000..73efdb1
--- /dev/null
+++ b/Help/guide/tutorial/Complete/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Tests/Tutorial/Complete/Config.cmake.in b/Help/guide/tutorial/Complete/Config.cmake.in
similarity index 100%
rename from Tests/Tutorial/Complete/Config.cmake.in
rename to Help/guide/tutorial/Complete/Config.cmake.in
diff --git a/Tests/Tutorial/Complete/License.txt b/Help/guide/tutorial/Complete/License.txt
similarity index 100%
rename from Tests/Tutorial/Complete/License.txt
rename to Help/guide/tutorial/Complete/License.txt
diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..dfa84c9
--- /dev/null
+++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
@@ -0,0 +1,63 @@
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+                            $<INSTALL_INTERFACE:include>
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+  target_link_libraries(MakeTable tutorial_compiler_flags)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  # state that SqrtLibrary need PIC when the default is shared libraries
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
+
+# define the symbol stating we are using the declspec(dllexport) when
+# building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+# setup the version numbering
+set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
+set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
+
+# install rules
+install(TARGETS MathFunctions tutorial_compiler_flags
+        DESTINATION lib
+        EXPORT MathFunctionsTargets)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Complete/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Complete/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx
diff --git a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..0145300
--- /dev/null
+++ b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}
diff --git a/Tests/Tutorial/Complete/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Complete/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..8153f18
--- /dev/null
+++ b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx
@@ -0,0 +1,37 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+namespace mathfunctions {
+namespace detail {
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
+}
+}
diff --git a/Tests/Tutorial/Complete/MathFunctions/mysqrt.h b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.h
similarity index 100%
rename from Tests/Tutorial/Complete/MathFunctions/mysqrt.h
rename to Help/guide/tutorial/Complete/MathFunctions/mysqrt.h
diff --git a/Help/guide/tutorial/Complete/TutorialConfig.h.in b/Help/guide/tutorial/Complete/TutorialConfig.h.in
new file mode 100644
index 0000000..7e4d7fa
--- /dev/null
+++ b/Help/guide/tutorial/Complete/TutorialConfig.h.in
@@ -0,0 +1,3 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Help/guide/tutorial/Complete/tutorial.cxx b/Help/guide/tutorial/Complete/tutorial.cxx
new file mode 100644
index 0000000..586d183
--- /dev/null
+++ b/Help/guide/tutorial/Complete/tutorial.cxx
@@ -0,0 +1,26 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // calculate square root
+  const double outputValue = mathfunctions::sqrt(inputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Consumer/CMakeLists.txt b/Help/guide/tutorial/Consumer/CMakeLists.txt
new file mode 100644
index 0000000..a0e4598
--- /dev/null
+++ b/Help/guide/tutorial/Consumer/CMakeLists.txt
@@ -0,0 +1,51 @@
+cmake_minimum_required(VERSION 3.10)
+
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED True)
+endif()
+
+
+function(find_external_dependency name)
+  set(${name}_ROOT ""  CACHE PATH "Root directory to find ${name}")
+  mark_as_advanced(${name}_DIR)
+  find_package(${name} PATHS ${${name}_ROOT} REQUIRED)
+endfunction()
+
+
+project(Consumer)
+
+find_external_dependency(MathFunctions)
+
+add_library(consumer consumer.cxx)
+target_link_libraries(consumer PUBLIC MathFunctions)
+
+# install the consumer library
+install(TARGETS consumer DESTINATION bin EXPORT ConsumerTargets)
+
+# install the configuration targets
+install(EXPORT ConsumerTargets
+  FILE ConsumerTargets.cmake
+  DESTINATION lib/cmake/Consumer
+)
+
+include(CMakePackageConfigHelpers)
+# generate the config file that is includes the exports
+configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
+  "${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake"
+  INSTALL_DESTINATION "lib/cmake/example"
+  NO_SET_AND_CHECK_MACRO
+  NO_CHECK_REQUIRED_COMPONENTS_MACRO
+  )
+
+# install the configuration file
+install(FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake
+  DESTINATION lib/cmake/Consumer
+  )
+
+# generate the export targets for the build tree
+# needs to be after the install(TARGETS ) command
+export(EXPORT ConsumerTargets
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/ConsumerTargets.cmake"
+)
diff --git a/Tests/Tutorial/Consumer/Config.cmake.in b/Help/guide/tutorial/Consumer/Config.cmake.in
similarity index 100%
rename from Tests/Tutorial/Consumer/Config.cmake.in
rename to Help/guide/tutorial/Consumer/Config.cmake.in
diff --git a/Tests/Tutorial/Consumer/consumer.cxx b/Help/guide/tutorial/Consumer/consumer.cxx
similarity index 100%
rename from Tests/Tutorial/Consumer/consumer.cxx
rename to Help/guide/tutorial/Consumer/consumer.cxx
diff --git a/Help/guide/tutorial/MultiPackage/CMakeLists.txt b/Help/guide/tutorial/MultiPackage/CMakeLists.txt
new file mode 100644
index 0000000..01d417a
--- /dev/null
+++ b/Help/guide/tutorial/MultiPackage/CMakeLists.txt
@@ -0,0 +1,112 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+
+# 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)
+
+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(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
+
+# install the configuration targets
+install(EXPORT MathFunctionsTargets
+  FILE MathFunctionsTargets.cmake
+  DESTINATION lib/cmake/MathFunctions
+)
+
+include(CMakePackageConfigHelpers)
+# generate the config file that is includes the exports
+configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
+  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
+  INSTALL_DESTINATION "lib/cmake/example"
+  NO_SET_AND_CHECK_MACRO
+  NO_CHECK_REQUIRED_COMPONENTS_MACRO
+  )
+# generate the version file for the config file
+write_basic_package_version_file(
+  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
+  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
+  COMPATIBILITY AnyNewerVersion
+)
+
+# install the configuration file
+install(FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
+  DESTINATION lib/cmake/MathFunctions
+  )
+
+# generate the export targets for the build tree
+# needs to be after the install(TARGETS ) command
+export(EXPORT MathFunctionsTargets
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
+)
diff --git a/Tests/Tutorial/MultiPackage/Config.cmake.in b/Help/guide/tutorial/MultiPackage/Config.cmake.in
similarity index 100%
rename from Tests/Tutorial/MultiPackage/Config.cmake.in
rename to Help/guide/tutorial/MultiPackage/Config.cmake.in
diff --git a/Tests/Tutorial/MultiPackage/License.txt b/Help/guide/tutorial/MultiPackage/License.txt
similarity index 100%
rename from Tests/Tutorial/MultiPackage/License.txt
rename to Help/guide/tutorial/MultiPackage/License.txt
diff --git a/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..a2df2a7
--- /dev/null
+++ b/Help/guide/tutorial/MultiPackage/MathFunctions/CMakeLists.txt
@@ -0,0 +1,59 @@
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+                            $<INSTALL_INTERFACE:include>
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  # state that SqrtLibrary need PIC when the default is shared libraries
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+# define the symbol stating we are using the declspec(dllexport) when
+# building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+# setup the version numbering
+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)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/MultiPackage/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/MultiPackage/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/MultiPackage/MathFunctions/MakeTable.cxx
diff --git a/Help/guide/tutorial/MultiPackage/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/MultiPackage/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..0145300
--- /dev/null
+++ b/Help/guide/tutorial/MultiPackage/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.h b/Help/guide/tutorial/MultiPackage/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/MultiPackage/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/MultiPackage/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/MultiPackage/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..5e622be
--- /dev/null
+++ b/Help/guide/tutorial/MultiPackage/MathFunctions/mysqrt.cxx
@@ -0,0 +1,38 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+namespace mathfunctions {
+namespace detail {
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
+}
+}
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.h b/Help/guide/tutorial/MultiPackage/MathFunctions/mysqrt.h
similarity index 100%
rename from Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.h
rename to Help/guide/tutorial/MultiPackage/MathFunctions/mysqrt.h
diff --git a/Tests/Tutorial/MultiPackage/MultiCPackConfig.cmake b/Help/guide/tutorial/MultiPackage/MultiCPackConfig.cmake
similarity index 100%
rename from Tests/Tutorial/MultiPackage/MultiCPackConfig.cmake
rename to Help/guide/tutorial/MultiPackage/MultiCPackConfig.cmake
diff --git a/Tests/Tutorial/MultiPackage/TutorialConfig.h.in b/Help/guide/tutorial/MultiPackage/TutorialConfig.h.in
similarity index 100%
rename from Tests/Tutorial/MultiPackage/TutorialConfig.h.in
rename to Help/guide/tutorial/MultiPackage/TutorialConfig.h.in
diff --git a/Help/guide/tutorial/MultiPackage/tutorial.cxx b/Help/guide/tutorial/MultiPackage/tutorial.cxx
new file mode 100644
index 0000000..f97805b
--- /dev/null
+++ b/Help/guide/tutorial/MultiPackage/tutorial.cxx
@@ -0,0 +1,25 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step1/tutorial.cxx b/Help/guide/tutorial/Step1/tutorial.cxx
new file mode 100644
index 0000000..08323bf
--- /dev/null
+++ b/Help/guide/tutorial/Step1/tutorial.cxx
@@ -0,0 +1,22 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = atof(argv[1]);
+
+  // calculate square root
+  const double outputValue = sqrt(inputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt
new file mode 100644
index 0000000..34ae70c
--- /dev/null
+++ b/Help/guide/tutorial/Step10/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# 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)
+
+# configure a header file to pass the version number only
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+include(CTest)
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
diff --git a/Help/guide/tutorial/Step10/CTestConfig.cmake b/Help/guide/tutorial/Step10/CTestConfig.cmake
new file mode 100644
index 0000000..73efdb1
--- /dev/null
+++ b/Help/guide/tutorial/Step10/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Tests/Tutorial/Step10/License.txt b/Help/guide/tutorial/Step10/License.txt
similarity index 100%
rename from Tests/Tutorial/Step10/License.txt
rename to Help/guide/tutorial/Step10/License.txt
diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..e0c0621
--- /dev/null
+++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
@@ -0,0 +1,51 @@
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  # state that SqrtLibrary need PIC when the default is shared libraries
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+# define the symbol stating we are using the declspec(dllexport) when
+# building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step10/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step10/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx
diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..0145300
--- /dev/null
+++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}
diff --git a/Tests/Tutorial/Step10/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step10/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..8153f18
--- /dev/null
+++ b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
@@ -0,0 +1,37 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+namespace mathfunctions {
+namespace detail {
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
+}
+}
diff --git a/Tests/Tutorial/Step10/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.h
similarity index 100%
rename from Tests/Tutorial/Step10/MathFunctions/mysqrt.h
rename to Help/guide/tutorial/Step10/MathFunctions/mysqrt.h
diff --git a/Help/guide/tutorial/Step10/TutorialConfig.h.in b/Help/guide/tutorial/Step10/TutorialConfig.h.in
new file mode 100644
index 0000000..7e4d7fa
--- /dev/null
+++ b/Help/guide/tutorial/Step10/TutorialConfig.h.in
@@ -0,0 +1,3 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Help/guide/tutorial/Step10/tutorial.cxx b/Help/guide/tutorial/Step10/tutorial.cxx
new file mode 100644
index 0000000..37a0333
--- /dev/null
+++ b/Help/guide/tutorial/Step10/tutorial.cxx
@@ -0,0 +1,27 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step11/CMakeLists.txt b/Help/guide/tutorial/Step11/CMakeLists.txt
new file mode 100644
index 0000000..4763951
--- /dev/null
+++ b/Help/guide/tutorial/Step11/CMakeLists.txt
@@ -0,0 +1,81 @@
+cmake_minimum_required(VERSION 3.15)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
+
+# 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)
+
+# configure a header file to pass the version number only
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
diff --git a/Help/guide/tutorial/Step11/CTestConfig.cmake b/Help/guide/tutorial/Step11/CTestConfig.cmake
new file mode 100644
index 0000000..73efdb1
--- /dev/null
+++ b/Help/guide/tutorial/Step11/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Tests/Tutorial/Step11/License.txt b/Help/guide/tutorial/Step11/License.txt
similarity index 100%
rename from Tests/Tutorial/Step11/License.txt
rename to Help/guide/tutorial/Step11/License.txt
diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..e6cb8ba
--- /dev/null
+++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
@@ -0,0 +1,55 @@
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+  target_link_libraries(MakeTable tutorial_compiler_flags)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  # state that SqrtLibrary need PIC when the default is shared libraries
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
+
+# define the symbol stating we are using the declspec(dllexport) when
+#building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step11/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step11/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx
diff --git a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..0145300
--- /dev/null
+++ b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}
diff --git a/Tests/Tutorial/Step11/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step11/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..8153f18
--- /dev/null
+++ b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx
@@ -0,0 +1,37 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+namespace mathfunctions {
+namespace detail {
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
+}
+}
diff --git a/Tests/Tutorial/Step11/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.h
similarity index 100%
rename from Tests/Tutorial/Step11/MathFunctions/mysqrt.h
rename to Help/guide/tutorial/Step11/MathFunctions/mysqrt.h
diff --git a/Help/guide/tutorial/Step11/TutorialConfig.h.in b/Help/guide/tutorial/Step11/TutorialConfig.h.in
new file mode 100644
index 0000000..7e4d7fa
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialConfig.h.in
@@ -0,0 +1,3 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Help/guide/tutorial/Step11/tutorial.cxx b/Help/guide/tutorial/Step11/tutorial.cxx
new file mode 100644
index 0000000..a4f44d5
--- /dev/null
+++ b/Help/guide/tutorial/Step11/tutorial.cxx
@@ -0,0 +1,26 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step2/CMakeLists.txt b/Help/guide/tutorial/Step2/CMakeLists.txt
new file mode 100644
index 0000000..7aa59e9
--- /dev/null
+++ b/Help/guide/tutorial/Step2/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
diff --git a/Tests/Tutorial/Step2/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step2/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..1e4d97a
--- /dev/null
+++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx
@@ -0,0 +1,22 @@
+#include <iostream>
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  double result = x;
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+  return result;
+}
diff --git a/Help/guide/tutorial/Step2/TutorialConfig.h.in b/Help/guide/tutorial/Step2/TutorialConfig.h.in
new file mode 100644
index 0000000..7e4d7fa
--- /dev/null
+++ b/Help/guide/tutorial/Step2/TutorialConfig.h.in
@@ -0,0 +1,3 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Help/guide/tutorial/Step2/tutorial.cxx b/Help/guide/tutorial/Step2/tutorial.cxx
new file mode 100644
index 0000000..53b0810
--- /dev/null
+++ b/Help/guide/tutorial/Step2/tutorial.cxx
@@ -0,0 +1,26 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // calculate square root
+  const double outputValue = sqrt(inputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step3/CMakeLists.txt b/Help/guide/tutorial/Step3/CMakeLists.txt
new file mode 100644
index 0000000..1c12816
--- /dev/null
+++ b/Help/guide/tutorial/Step3/CMakeLists.txt
@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           ${EXTRA_INCLUDES}
+                           )
diff --git a/Tests/Tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
similarity index 100%
rename from Tests/Tutorial/Step3/MathFunctions/CMakeLists.txt
rename to Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
diff --git a/Tests/Tutorial/Step3/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step3/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..abe767d
--- /dev/null
+++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx
@@ -0,0 +1,24 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  double result = x;
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+  return result;
+}
diff --git a/Help/guide/tutorial/Step3/TutorialConfig.h.in b/Help/guide/tutorial/Step3/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step3/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step3/tutorial.cxx b/Help/guide/tutorial/Step3/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step3/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step4/CMakeLists.txt b/Help/guide/tutorial/Step4/CMakeLists.txt
new file mode 100644
index 0000000..38e9b1f
--- /dev/null
+++ b/Help/guide/tutorial/Step4/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
diff --git a/Tests/Tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
similarity index 100%
rename from Tests/Tutorial/Step4/MathFunctions/CMakeLists.txt
rename to Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
diff --git a/Tests/Tutorial/Step4/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step4/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..abe767d
--- /dev/null
+++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx
@@ -0,0 +1,24 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  double result = x;
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+  return result;
+}
diff --git a/Help/guide/tutorial/Step4/TutorialConfig.h.in b/Help/guide/tutorial/Step4/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step4/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step4/tutorial.cxx b/Help/guide/tutorial/Step4/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step4/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step5/CMakeLists.txt b/Help/guide/tutorial/Step5/CMakeLists.txt
new file mode 100644
index 0000000..c3b375a
--- /dev/null
+++ b/Help/guide/tutorial/Step5/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..b12f27d
--- /dev/null
+++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(MathFunctions mysqrt.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          )
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step5/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step5/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step5/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step5/MathFunctions/MakeTable.cxx
diff --git a/Tests/Tutorial/Step5/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step5/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..abe767d
--- /dev/null
+++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx
@@ -0,0 +1,24 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  double result = x;
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+  return result;
+}
diff --git a/Help/guide/tutorial/Step5/TutorialConfig.h.in b/Help/guide/tutorial/Step5/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step5/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step5/tutorial.cxx b/Help/guide/tutorial/Step5/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step5/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt
new file mode 100644
index 0000000..c3b375a
--- /dev/null
+++ b/Help/guide/tutorial/Step6/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..4bf6024
--- /dev/null
+++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
@@ -0,0 +1,22 @@
+add_library(MathFunctions mysqrt.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          )
+
+# does this system provide the log and exp functions?
+include(CheckSymbolExists)
+set(CMAKE_REQUIRED_LIBRARIES "m")
+check_symbol_exists(log "math.h" HAVE_LOG)
+check_symbol_exists(exp "math.h" HAVE_EXP)
+
+if(HAVE_LOG AND HAVE_EXP)
+  target_compile_definitions(MathFunctions
+                             PRIVATE "HAVE_LOG" "HAVE_EXP")
+endif()
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step6/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step6/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx
diff --git a/Tests/Tutorial/Step6/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step6/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..0637063
--- /dev/null
+++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
@@ -0,0 +1,32 @@
+#include <cmath>
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // if we have both log and exp then use them
+#if defined(HAVE_LOG) && defined(HAVE_EXP)
+  double result = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result
+            << " using log and exp" << std::endl;
+#else
+  double result = x;
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+#endif
+  return result;
+}
diff --git a/Help/guide/tutorial/Step6/TutorialConfig.h.in b/Help/guide/tutorial/Step6/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step6/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step6/tutorial.cxx b/Help/guide/tutorial/Step6/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step6/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt
new file mode 100644
index 0000000..c3b375a
--- /dev/null
+++ b/Help/guide/tutorial/Step7/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Tests/Tutorial/Step7/License.txt b/Help/guide/tutorial/Step7/License.txt
similarity index 100%
rename from Tests/Tutorial/Step7/License.txt
rename to Help/guide/tutorial/Step7/License.txt
diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..9ede4b3
--- /dev/null
+++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
@@ -0,0 +1,29 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+
+# add the command to generate the source code
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  DEPENDS MakeTable
+  )
+
+# add the main library
+add_library(MathFunctions
+            mysqrt.cxx
+            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+            )
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
+# TutorialConfig.h include is an implementation detail
+# state that we depend on our binary dir to find Table.h
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+          )
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step7/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step7/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step7/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step7/MathFunctions/MakeTable.cxx
diff --git a/Tests/Tutorial/Step7/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step7/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..7d80ee9
--- /dev/null
+++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
diff --git a/Help/guide/tutorial/Step7/TutorialConfig.h.in b/Help/guide/tutorial/Step7/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step7/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step7/tutorial.cxx b/Help/guide/tutorial/Step7/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step7/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt
new file mode 100644
index 0000000..19b9913
--- /dev/null
+++ b/Help/guide/tutorial/Step8/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+enable_testing()
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+# setup installer
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
diff --git a/Tests/Tutorial/Step8/License.txt b/Help/guide/tutorial/Step8/License.txt
similarity index 100%
rename from Tests/Tutorial/Step8/License.txt
rename to Help/guide/tutorial/Step8/License.txt
diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..9ede4b3
--- /dev/null
+++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
@@ -0,0 +1,29 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+
+# add the command to generate the source code
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  DEPENDS MakeTable
+  )
+
+# add the main library
+add_library(MathFunctions
+            mysqrt.cxx
+            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+            )
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
+# TutorialConfig.h include is an implementation detail
+# state that we depend on our binary dir to find Table.h
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+          )
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step8/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step8/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx
diff --git a/Tests/Tutorial/Step8/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step8/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..7d80ee9
--- /dev/null
+++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
diff --git a/Help/guide/tutorial/Step8/TutorialConfig.h.in b/Help/guide/tutorial/Step8/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step8/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step8/tutorial.cxx b/Help/guide/tutorial/Step8/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step8/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt
new file mode 100644
index 0000000..d5f1cc8
--- /dev/null
+++ b/Help/guide/tutorial/Step9/CMakeLists.txt
@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name and version
+project(Tutorial VERSION 1.0)
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(TutorialConfig.h.in TutorialConfig.h)
+
+# add the MathFunctions library
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# add the install targets
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
+
+# enable testing
+include(CTest)
+
+# does the application run
+add_test(NAME Runs COMMAND Tutorial 25)
+
+# does the usage message work?
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
+  )
+
+# define a function to simplify adding tests
+function(do_test target arg result)
+  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
+  set_tests_properties(Comp${arg}
+    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
+    )
+endfunction(do_test)
+
+# do a bunch of result based tests
+do_test(Tutorial 4 "4 is 2")
+do_test(Tutorial 9 "9 is 3")
+do_test(Tutorial 5 "5 is 2.236")
+do_test(Tutorial 7 "7 is 2.645")
+do_test(Tutorial 25 "25 is 5")
+do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial 0.0001 "0.0001 is 0.01")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
diff --git a/Help/guide/tutorial/Step9/CTestConfig.cmake b/Help/guide/tutorial/Step9/CTestConfig.cmake
new file mode 100644
index 0000000..73efdb1
--- /dev/null
+++ b/Help/guide/tutorial/Step9/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Tests/Tutorial/Step9/License.txt b/Help/guide/tutorial/Step9/License.txt
similarity index 100%
rename from Tests/Tutorial/Step9/License.txt
rename to Help/guide/tutorial/Step9/License.txt
diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..50f0701
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
@@ -0,0 +1,27 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+
+# add the command to generate the source code
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  DEPENDS MakeTable
+  )
+
+# add the main library
+add_library(MathFunctions
+            mysqrt.cxx
+            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+            )
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on our binary dir to find Table.h
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE   ${CMAKE_CURRENT_BINARY_DIR}
+          )
+
+# install rules
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step9/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx
similarity index 100%
rename from Tests/Tutorial/Step9/MathFunctions/MakeTable.cxx
rename to Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx
diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..0145300
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}
diff --git a/Tests/Tutorial/Step9/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h
similarity index 100%
rename from Tests/Tutorial/Step9/MathFunctions/MathFunctions.h
rename to Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h
diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx
new file mode 100644
index 0000000..7d80ee9
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx
@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "MathFunctions.h"
+
+// include the generated table
+#include "Table.h"
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    return 0;
+  }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    std::cout << "Use the table to help find an initial value " << std::endl;
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    double delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+  }
+
+  return result;
+}
diff --git a/Tests/Tutorial/Step9/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h
similarity index 100%
rename from Tests/Tutorial/Step9/MathFunctions/mysqrt.h
rename to Help/guide/tutorial/Step9/MathFunctions/mysqrt.h
diff --git a/Help/guide/tutorial/Step9/TutorialConfig.h.in b/Help/guide/tutorial/Step9/TutorialConfig.h.in
new file mode 100644
index 0000000..e23f521
--- /dev/null
+++ b/Help/guide/tutorial/Step9/TutorialConfig.h.in
@@ -0,0 +1,4 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step9/tutorial.cxx b/Help/guide/tutorial/Step9/tutorial.cxx
new file mode 100644
index 0000000..b3c6a4f
--- /dev/null
+++ b/Help/guide/tutorial/Step9/tutorial.cxx
@@ -0,0 +1,36 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+// should we include the MathFunctions header?
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    // report version
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MINOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  // convert input to double
+  const double inputValue = std::stod(argv[1]);
+
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}
diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst
new file mode 100644
index 0000000..d858c25
--- /dev/null
+++ b/Help/guide/tutorial/index.rst
@@ -0,0 +1,899 @@
+CMake Tutorial
+**************
+
+.. only:: html
+
+   .. contents::
+
+The CMake tutorial provides a step-by-step guide that covers common build
+system issues that CMake helps address. Seeing how various topics all
+work together in an example project can be very helpful. The tutorial
+documentation and source code for examples can be found in the
+``Help/guide/tutorial`` directory of the CMake source code tree. Each step has
+its own subdirectory containing code that may be used as a starting point. The
+tutorial examples are progressive so that each step provides the complete
+solution for the previous step.
+
+A Basic Starting Point (Step 1)
+===============================
+
+The most basic project is an executable built from source code files.
+For simple projects, a three line CMakeLists file is all that is required.
+This will be the starting point for our tutorial. Create a ``CMakeLists.txt``
+file in the ``Step1`` directory that looks like:
+
+.. code-block:: cmake
+
+  cmake_minimum_required(VERSION 3.10)
+
+  # set the project name
+  project(Tutorial)
+
+  # add the executable
+  add_executable(Tutorial tutorial.cxx)
+
+
+Note that this example uses lower case commands in the CMakeLists file.
+Upper, lower, and mixed case commands are supported by CMake. The source
+code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be
+used to compute the square root of a number.
+
+Adding a Version Number and Configured Header File
+--------------------------------------------------
+
+The first feature we will add is to provide our executable and project with a
+version number. While we could do this exclusively in the source code, using
+CMakeLists provides more flexibility.
+
+First, modify the CMakeLists file to set the version number.
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :language: cmake
+  :end-before: # specify the C++ standard
+
+Then, configure a header file to pass the version number to the source
+code:
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :language: cmake
+  :start-after: # to the source code
+  :end-before: # add the executable
+
+Since the configured file will be written into the binary tree, we
+must add that directory to the list of paths to search for include
+files. Add the following lines to the end of the CMakeLists file:
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :language: cmake
+  :start-after: # so that we will find TutorialConfig.h
+
+Using your favorite editor, create ``TutorialConfig.h.in`` in the source
+directory with the following contents:
+
+.. literalinclude:: Step2/TutorialConfig.h.in
+  :language: cmake
+
+When CMake configures this header file the values for
+``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will be
+replaced.
+
+Next modify ``tutorial.cxx`` to include the configured header file,
+``TutorialConfig.h``.
+
+Finally, let's print out the version number by updating ``tutorial.cxx`` as
+follows:
+
+.. literalinclude:: Step2/tutorial.cxx
+  :language: c++
+  :start-after: {
+  :end-before: // convert input to double
+
+Specify the C++ Standard
+-------------------------
+
+Next let's add some C++11 features to our project by replacing ``atof`` with
+``std::stod`` in ``tutorial.cxx``.  At the same time, remove
+``#include <cstdlib>``.
+
+.. literalinclude:: Step2/tutorial.cxx
+  :language: c++
+  :start-after: // convert input to double
+  :end-before: // calculate square root
+
+We will need to explicitly state in the CMake code that it should use the
+correct flags. The easiest way to enable support for a specific C++ standard
+in CMake is by using the ``CMAKE_CXX_STANDARD`` variable. For this tutorial,
+set the ``CMAKE_CXX_STANDARD`` variable in the CMakeLists file to 11 and
+``CMAKE_CXX_STANDARD_REQUIRED`` to True:
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :language: cmake
+  :end-before: # configure a header file to pass some of the CMake settings
+
+Build and Test
+--------------
+
+Run **cmake** or **cmake-gui** to configure the project and then build it
+with your chosen build tool.
+
+For example, from the command line we could navigate to the
+``Help/guide/tutorial`` directory of the CMake source code tree and run the
+following commands:
+
+.. code-block:: console
+
+  mkdir Step1_build
+  cd Step1_build
+  cmake ../Step1
+  cmake --build .
+
+Navigate to the directory where Tutorial was built (likely the make directory
+or a Debug or Release build configuration subdirectory) and run these commands:
+
+.. code-block:: console
+
+  Tutorial 4294967296
+  Tutorial 10
+  Tutorial
+
+Adding a Library (Step 2)
+=========================
+
+Now we will add a library to our project. This library will contain our own
+implementation for computing the square root of a number. The executable can
+then use this library instead of the standard square root function provided by
+the compiler.
+
+For this tutorial we will put the library into a subdirectory
+called MathFunctions. This directory already contains a header file,
+``MathFunctions.h``, and a source file ``mysqrt.cxx``. The source file has one
+function called ``mysqrt`` that provides similar functionality to the
+compiler's ``sqrt`` function.
+
+Add the following one line ``CMakeLists.txt`` file to the MathFunctions
+directory:
+
+.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+  :language: cmake
+
+To make use of the new library we will add an ``add_subdirectory`` call in the
+top-level CMakeLists file so that the library will get built. We add the new
+library to the executable, and add MathFunctions as an include directory so
+that the ``mqsqrt.h`` header file can be found. The last few lines of the
+top-level CMakeLists file should now look like:
+
+.. code-block:: cmake
+
+        # add the MathFunctions library
+        add_subdirectory(MathFunctions)
+
+        # add the executable
+        add_executable(Tutorial tutorial.cxx)
+
+        target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+        # add the binary tree to the search path for include files
+        # so that we will find TutorialConfig.h
+        target_include_directories(Tutorial PUBLIC
+                                  "${PROJECT_BINARY_DIR}"
+                                  "${PROJECT_SOURCE_DIR}/MathFunctions"
+                                  )
+
+Now let us make the MathFunctions library optional. While for the tutorial
+there really isn’t any need to do so, for larger projects this is a common
+occurrence. The first step is to add an option to the top-level CMakeLists
+file.
+
+.. literalinclude:: Step3/CMakeLists.txt
+  :language: cmake
+  :start-after: # should we use our own math functions
+  :end-before: # add the MathFunctions library
+
+This option will be displayed in the CMake GUI and ccmake with a default
+value of ON that can be changed by the user. This setting will be stored in
+the cache so that the user does not need to set the value each time they run
+CMake on a build directory.
+
+The next change is to make building and linking the MathFunctions library
+conditional. To do this we change the end of the top-level CMakeLists file to
+look like the following:
+
+.. literalinclude:: Step3/CMakeLists.txt
+  :language: cmake
+  :start-after: # add the MathFunctions library
+
+Note the use of the variable ``EXTRA_LIBS`` to collect up any optional
+libraries to later be linked into the executable. The variable
+``EXTRA_INCLUDES`` is used similarly for optional header files. This is a
+classic approach when dealing with many optional components, we will cover
+the modern approach in the next step.
+
+The corresponding changes to the source code are fairly straightforward. First,
+in ``tutorial.cxx``, include the MathFunctions header if we need it:
+
+.. literalinclude:: Step3/tutorial.cxx
+  :language: c++
+  :start-after: // should we include the MathFunctions header
+  :end-before: int main
+
+Then, in the same file, make which square root function is used dependent on
+``USE_MYMATH``:
+
+.. literalinclude:: Step3/tutorial.cxx
+  :language: c++
+  :start-after: // which square root function should we use?
+  :end-before: std::cout << "The square root of
+
+Since the source code now requires ``USE_MYMATH`` we can add it to
+``TutorialConfig.h.in`` with the following line:
+
+.. literalinclude:: Step3/TutorialConfig.h.in
+  :language: c
+  :lines: 4
+
+**Exercise**: Why is it important that we configure ``TutorialConfig.h.in``
+after the option for ``USE_MYMATH``? What would happen if we inverted the two?
+
+Run **cmake** or **cmake-gui** to configure the project and then build it
+with your chosen build tool. Then run the built Tutorial executable.
+
+Use ccmake or the CMake GUI to update the value of ``USE_MYMATH``. Rebuild and
+run the tutorial again. Which function gives better results, sqrt or mysqrt?
+
+Adding Usage Requirements for Library (Step 3)
+==============================================
+
+Usage requirements allow for far better control over a library or executable's
+link and include line while also giving more control over the transitive
+property of targets inside CMake. The primary commands that leverage usage
+requirements are:
+
+  - ``target_compile_definitions``
+  - ``target_compile_options``
+  - ``target_include_directories``
+  - ``target_link_libraries``
+
+Let's refactor our code from `Adding a Library (Step 2)`_ to use the modern
+CMake approach of usage requirements. We first state that anybody linking to
+MathFunctions needs to include the current source directory, while
+MathFunctions itself doesn't. So  this can become an ``INTERFACE`` usage
+requirement.
+
+Remember ``INTERFACE`` means things that consumers require but the producer
+doesn't. Add the following lines to the end of ``MathFunctions/CMakeLists.txt``:
+
+.. literalinclude:: Step4/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # to find MathFunctions.h
+
+Now that we've specified usage requirements for MathFunctions we can safely
+remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
+CMakeLists, here:
+
+.. literalinclude:: Step4/CMakeLists.txt
+  :language: cmake
+  :start-after: # add the MathFunctions library
+  :end-before: # add the executable
+
+And here:
+
+.. literalinclude:: Step4/CMakeLists.txt
+  :language: cmake
+  :start-after: # so that we will find TutorialConfig.h
+
+Once this is done, run **cmake** or **cmake-gui** to configure the project
+and then build it with your chosen build tool or by using ``cmake --build .``
+from the build directory.
+
+Installing and Testing (Step 4)
+===============================
+
+Now we can start adding install rules and testing support to our project.
+
+Install Rules
+-------------
+
+The install rules are fairly simple: for MathFunctions we want to install the
+library and header file and for the application we want to install the
+executable and configured header.
+
+So to the end of ``MathFunctions/CMakeLists.txt`` we add:
+
+.. literalinclude:: Step5/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # install rules
+
+And to the end of the top-level ``CMakeLists.txt`` we add:
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :language: cmake
+  :start-after: # add the install targets
+  :end-before: # enable testing
+
+That is all that is needed to create a basic local install of the tutorial.
+
+Run **cmake** or **cmake-gui** to configure the project and then build it
+with your chosen build tool. Run the install step by typing
+``cmake --install .`` (introduced in 3.15, older versions of CMake must use
+``make install``) from the command line, or build the ``INSTALL`` target from
+an IDE. This will install the appropriate header files, libraries, and
+executables.
+
+The CMake variable ``CMAKE_INSTALL_PREFIX`` is used to determine the root of
+where the files will be installed. If using ``cmake --install`` a custom
+installation directory can be given via ``--prefix`` argument. For
+multi-configuration tools, use the ``--config`` argument to specify the
+configuration.
+
+Verify that the installed Tutorial runs.
+
+Testing Support
+---------------
+
+Next let's test our application. At the end of the top-level CMakeLists file we
+can enable testing and then add a number of basic tests to verify that the
+application is working correctly.
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :language: cmake
+  :start-after: # enable testing
+
+The first test simply verifies that the application runs, does not segfault or
+otherwise crash, and has a zero return value. This is the basic form of a CTest
+test.
+
+The next test makes use of the ``PASS_REGULAR_EXPRESSION`` test property to
+verify that the output of the test contains certain strings. In this case,
+verifying that the the usage message is printed when an incorrect number of
+arguments are provided.
+
+Lastly, we have a function called ``do_test`` that runs the application and
+verifies that the computed square root is correct for given input. For each
+invocation of ``do_test``, another test is added to the project with a name,
+input, and expected results based on the passed arguments.
+
+Rebuild the application and then cd to the binary directory and run
+``ctest -N`` and ``ctest -VV``. For multi-config generators (e.g. Visual
+Studio), the configuration type must be specified. To run tests in Debug mode,
+for example, use ``ctest -C Debug -VV`` from the build directory (not the
+Debug subdirectory!). Alternatively, build the ``RUN_TESTS`` target from the
+IDE.
+
+Adding System Introspection (Step 5)
+====================================
+
+Let us consider adding some code to our project that depends on features the
+target platform may not have. For this example, we will add some code that
+depends on whether or not the target platform has the ``log`` and ``exp``
+functions. Of course almost every platform has these functions but for this
+tutorial assume that they are not common.
+
+If the platform has ``log`` and ``exp`` then we will use them to compute the
+square root in the ``mysqrt`` function. We first test for the availability of
+these functions using the ``CheckSymbolExists.cmake`` macro in the top-level
+CMakeLists. We're going to use the new defines in ``TutorialConfig.h.in``,
+so be sure to set them before that file is configured.
+
+.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # does this system provide the log and exp functions?
+  :end-before: if(HAVE_LOG AND HAVE_EXP)
+
+Now let's add these defines to ``TutorialConfig.h.in`` so that we can use them
+from ``mysqrt.cxx``:
+
+.. code-block:: console
+
+  // does the platform provide exp and log functions?
+  #cmakedefine HAVE_LOG
+  #cmakedefine HAVE_EXP
+
+Modify ``mysqrt.cxx`` to include cmath. Next, in that same file in the
+``mysqrt`` function we can provide an alternate implementation based on
+``log`` and ``exp`` if they are available on the system using the following
+code (don't forget the ``#endif`` before returning the result!):
+
+.. literalinclude:: Step6/MathFunctions/mysqrt.cxx
+  :language: c++
+  :start-after: // if we have both log and exp then use them
+  :end-before: // do ten iterations
+
+Run **cmake** or **cmake-gui** to configure the project and then build it
+with your chosen build tool and run the Tutorial executable.
+
+You will notice that we're not using ``log`` and ``exp``, even if we think they
+should be available. We should realize quickly that we have forgotten to include
+``TutorialConfig.h`` in ``mysqrt.cxx``.
+
+We will also need to update MathFunctions/CMakeLists so ``mysqrt.cxx`` knows
+where this file is located:
+
+
+.. code-block:: cmake
+
+  target_include_directories(MathFunctions
+            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+            PRIVATE ${CMAKE_BINARY_DIR}
+            )
+
+After making this update, go ahead and build the project again and run the built
+Tutorial executable. If ``log`` and ``exp`` are still not being used, open the
+generated ``TutorialConfig.h`` file from the build directory. Maybe they aren't
+available on the current system?
+
+Which function gives better results now, sqrt or mysqrt?
+
+Specify Compile Definition
+--------------------------
+
+Is there a better place for us to save the ``HAVE_LOG`` and ``HAVE_EXP`` values
+other than in ``TutorialConfig.h``? Let's try to use
+``target_compile_definitions``.
+
+First, remove the defines from ``TutorialConfig.h.in``. We no longer need to
+include ``TutorialConfig.h`` from ``mysqrt.cxx`` or the extra include in
+MathFunctions/CMakeLists.
+
+Next, we can move the check for ``HAVE_LOG`` and ``HAVE_EXP`` to
+MathFunctions/CMakeLists and then add specify those values as ``PRIVATE``
+compile definitions.
+
+.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # does this system provide the log and exp functions?
+  :end-before: # install rules
+
+After making these updates, go ahead and build the project again. Run the
+built  Tutorial executable and verify that the results are same as earlier in
+this step.
+
+Adding a Custom Command and Generated File (Step 6)
+===================================================
+
+Suppose, for the purpose of this tutorial, we decide that we never want to use
+the platform ``log`` and ``exp`` functions and instead would like to
+generate a table of precomputed values to use in the ``mysqrt`` function.
+In this section, we will create the table as part of the build process,
+and then compile that table into our application.
+
+First, let's remove the check for the ``log`` and ``exp`` functions in
+MathFunctions/CMakeLists. Then remove the check for ``HAVE_LOG`` and
+``HAVE_EXP`` from ``mysqrt.cxx``. At the same time, we can remove
+:code:`#include <cmath>`.
+
+In the MathFunctions subdirectory, a new source file named ``MakeTable.cxx``
+has been provided to generate the table.
+
+After reviewing the file, we can see that the table is produced as valid C++
+code and that the output filename is passed in as an argument.
+
+The next step is to add the appropriate commands to MathFunctions CMakeLists
+file to build the MakeTable executable and then run it as part of the build
+process. A few commands are needed to accomplish this.
+
+First, at the top of MathFunctions/CMakeLists, the executable for ``MakeTable``
+is added as any other executable would be added.
+
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # first we add the executable that generates the table
+  :end-before: # add the command to generate the source code
+
+Then we add a custom command that specifies how to produce ``Table.h``
+by running MakeTable.
+
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # add the command to generate the source code
+  :end-before: # add the main library
+
+Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated
+file ``Table.h``. This is done by adding the generated ``Table.h`` to the list
+of sources for the library MathFunctions.
+
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # add the main library
+  :end-before: # state that anybody linking
+
+We also have to add the current binary directory to the list of include
+directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``.
+
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+  :start-after: # state that we depend on our bin
+  :end-before: # install rules
+
+Now let's use the generated table. First, modify ``mysqrt.cxx`` to include
+``Table.h``. Next, we can rewrite the mysqrt function to use the table:
+
+.. literalinclude:: Step7/MathFunctions/mysqrt.cxx
+  :language: c++
+  :start-after: // a hack square root calculation using simple operations
+
+Run **cmake** or **cmake-gui** to configure the project and then build it
+with your chosen build tool.
+
+When this project is built it will first build the ``MakeTable`` executable.
+It will then run ``MakeTable`` to produce ``Table.h``. Finally, it will
+compile ``mysqrt.cxx`` which includes ``Table.h`` to produce the MathFunctions
+library.
+
+Run the Tutorial executable and verify that it is using the table.
+
+Building an Installer (Step 7)
+==============================
+
+Next suppose that we want to distribute our project to other people so that
+they can use it. We want to provide both binary and source distributions on a
+variety of platforms. This is a little different from the install we did
+previously in `Installing and Testing (Step 4)`_ , where we were
+installing the binaries that we had built from the source code. In this
+example we will be building installation packages that support binary
+installations and package management features. To accomplish this we will use
+CPack to create platform specific installers. Specifically we need to add
+a few lines to the bottom of our top-level ``CMakeLists.txt`` file.
+
+.. literalinclude:: Step8/CMakeLists.txt
+  :language: cmake
+  :start-after: # setup installer
+
+That is all there is to it. We start by including
+``InstallRequiredSystemLibraries``. This module will include any runtime
+libraries that are needed by the project for the current platform. Next we
+set some CPack variables to where we have stored the license and version
+information for this project. The version information was set earlier in this
+tutorial and the ``license.txt`` has been included in the top-level source
+directory for this step.
+
+Finally we include the CPack module which will use these variables and some
+other properties of the current system to setup an installer.
+
+The next step is to build the project in the usual manner and then run
+CPack on it. To build a binary distribution, from the binary directory run:
+
+.. code-block:: console
+
+  cpack
+
+To specify the generator, use the ``-G`` option. For multi-config builds, use
+``-C`` to specify the configuration. For example:
+
+.. code-block:: console
+
+  cpack -G ZIP -C Debug
+
+To create a source distribution you would type:
+
+.. code-block:: console
+
+  cpack --config CPackSourceConfig.cmake
+
+Alternatively, run ``make package`` or right click the ``Package`` target and
+``Build Project`` from an IDE.
+
+Run the installer found in the binary directory. Then run the
+installed executable and verify that it works.
+
+Adding Support for a Dashboard (Step 8)
+=======================================
+
+Adding support for submitting our test results to a dashboard is very easy. We
+already defined a number of tests for our project in `Testing Support`_. Now we
+just have to run those tests and submit them to a dashboard. To include support
+for dashboards we include the CTest module in our top-level ``CMakeLists.txt``.
+
+Replace:
+
+.. code-block:: cmake
+
+  # enable testing
+  enable_testing()
+
+With:
+
+.. code-block:: cmake
+
+  # enable dashboard scripting
+  include(CTest)
+
+The CTest module will automatically call ``enable_testing()``, so
+we can remove it from our CMake files.
+
+We will also need to create a ``CTestConfig.cmake`` file in the top-level
+directory where we can specify the name of the project and where to submit the
+dashboard.
+
+.. literalinclude:: Step9/CTestConfig.cmake
+  :language: cmake
+
+CTest will read in this file when it runs. To create a simple dashboard you can
+run **cmake** or **cmake-gui** to configure the project, but do not build it
+yet. Instead, change directory to the binary tree, and then run::
+
+  ctest [-VV] –D Experimental
+
+Remember, for multi-config generators (e.g. Visual Studio), the configuration
+type must be specified::
+
+  ctest [-VV] -C Debug –D Experimental
+
+Or, from an IDE, build the ``Experimental`` target.
+
+Ctest will build and test the project and submit the results to the Kitware
+public dashboard. The results of your dashboard will be uploaded to Kitware's
+public dashboard here: https://my.cdash.org/index.php?project=CMakeTutorial.
+
+Mixing Static and Shared (Step 9)
+=================================
+
+In this section we will show how by using the ``BUILD_SHARED_LIBS`` variable
+we can control the default behavior of ``add_library``, and allow control
+over how libraries without an explicit type (STATIC/SHARED/MODULE/OBJECT) are
+built.
+
+To accomplish this we need to add ``BUILD_SHARED_LIBS`` to the top-level
+``CMakeLists.txt``. We use the ``option`` command as it allows users to
+optionally select if the value should be On or Off.
+
+Next we are going to refactor MathFunctions to become a real library that
+encapsulates using ``mysqrt`` or ``sqrt``, instead of requiring the calling
+code to do this logic. This will also mean that ``USE_MYMATH`` will not control
+building MathFuctions, but instead will control the behavior of this library.
+
+The first step is to update the starting section of the top-level
+``CMakeLists.txt`` to look like:
+
+.. literalinclude:: Step10/CMakeLists.txt
+  :language: cmake
+  :end-before: # add the binary tree
+
+Now that we have made MathFunctions always be used, we will need to update
+the logic of that library. So, in ``MathFunctions/CMakeLists.txt`` we need to
+create a SqrtLibrary that will conditionally be built when ``USE_MYMATH`` is
+enabled. Now, since this is a tutorial, we are going to explicitly require
+that SqrtLibrary is built statically.
+
+The end result is that ``MathFunctions/CMakeLists.txt`` should look like:
+
+.. literalinclude:: Step10/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :lines: 1-36,42-
+
+Next, update ``MathFunctions/mysqrt.cxx`` to use the ``mathfunctions`` and
+``detail`` namespaces:
+
+.. literalinclude:: Step10/MathFunctions/mysqrt.cxx
+  :language: c++
+
+We also need to make some changes in ``tutorial.cxx``, so that it no longer
+uses ``USE_MYMATH``:
+
+#. Always include ``MathFunctions.h``
+#. Always use ``mathfunctions::sqrt``
+#. Don't include cmath
+
+Finally, update ``MathFunctions/MathFunctions.h`` to use dll export defines:
+
+.. literalinclude:: Step10/MathFunctions/MathFunctions.h
+  :language: c++
+
+At this point, if you build everything, you will notice that linking fails
+as we are combining a static library without position enabled code with a
+library that has position enabled code. The solution to this is to explicitly
+set the ``POSITION_INDEPENDENT_CODE`` target property of SqrtLibrary to be
+True no matter the build type.
+
+.. literalinclude:: Step10/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :lines: 37-42
+
+**Exercise**: We modified ``MathFunctions.h`` to use dll export defines.
+Using CMake documentation can you find a helper module to simplify this?
+
+
+Adding Generator Expressions (Step 10)
+======================================
+
+Generator expressions are evaluated during build system generation to produce
+information specific to each build configuration.
+
+Generator expressions are allowed in the context of many target properties,
+such as ``LINK_LIBRARIES``, ``INCLUDE_DIRECTORIES``, ``COMPILE_DEFINITIONS``
+and others. They may also be used when using commands to populate those
+properties, such as ``target_link_libraries()``,
+``target_include_directories()``,
+``target_compile_definitions()`` and others.
+
+Generator expressions may be used to enable conditional linking, conditional
+definitions used when compiling, conditional include directories and more.
+The conditions may be based on the build configuration, target properties,
+platform information or any other queryable information.
+
+There are different types of generator expressions including Logical,
+Informational, and Output expressions.
+
+Logical expressions are used to create conditional output. The basic
+expressions are the 0 and 1 expressions. A ``$<0:...>`` results in the empty
+string, and ``<1:...>`` results in the content of "...".  They can also be
+nested.
+
+A common usage of generator expressions is to conditionally add compiler
+flags, such as those as language levels or warnings. A nice pattern is
+to associate this information to an ``INTERFACE`` target allowing this
+information to propagate. Lets start by constructing an ``INTERFACE``
+target and specifying the required C++ standard level of ``11`` instead
+of using ``CMAKE_CXX_STANDARD``.
+
+So the following code:
+
+.. literalinclude:: Step10/CMakeLists.txt
+  :language: cmake
+  :start-after: project(Tutorial VERSION 1.0)
+  :end-before: # control where the static and shared libraries are built so that on windows
+
+Would be replaced with:
+
+.. literalinclude:: Step11/CMakeLists.txt
+  :language: cmake
+  :start-after: project(Tutorial VERSION 1.0)
+  :end-before: # add compiler warning flags just when building this project via
+
+
+Next we add the desired compiler warning flags that we want for our
+project. As warning flags vary based on the compiler we use
+the ``COMPILE_LANG_AND_ID`` generator expression to control which
+flags to apply given a language and a set of compiler ids as seen
+below:
+
+.. literalinclude:: Step11/CMakeLists.txt
+  :language: cmake
+  :start-after: # the BUILD_INTERFACE genex
+  :end-before: # control where the static and shared libraries are built so that on windows
+
+Looking at this we see that the warning flags are encapsulated inside a
+``BUILD_INTERFACE`` condition. This is done so that consumers of our installed
+project will not inherit our warning flags.
+
+
+**Exercise**: Modify ``MathFunctions/CMakeLists.txt`` so that
+all targets have a ``target_link_libraries()`` call to ``tutorial_compiler_flags``.
+
+
+Adding Export Configuration (Step 11)
+=====================================
+
+During `Installing and Testing (Step 4)`_ of the tutorial we added the ability
+for CMake to install the library and headers of the project. During
+`Building an Installer (Step 7)`_ we added the ability to package up this
+information so it could be distributed to other people.
+
+The next step is to add the necessary information so that other CMake projects
+can use our project, be it from a build directory, a local install or when
+packaged.
+
+The first step is to update our ``install(TARGETS)`` commands to not only
+specify a ``DESTINATION`` but also an ``EXPORT``. The ``EXPORT`` keyword
+generates and installs a CMake file containing code to import all targets
+listed in the install command from the installation tree. So let's go ahead
+and explicitly ``EXPORT`` the MathFunctions library by updating the
+``install`` command in ``MathFunctions/CMakeLists.txt`` to look like:
+
+.. literalinclude:: Complete/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # install rules
+
+Now that we have MathFunctions being exported, we also need to explicitly
+install the generated ``MathFunctionsTargets.cmake`` file. This is done by
+adding the following to the bottom of the top-level ``CMakeLists.txt``:
+
+.. literalinclude:: Complete/CMakeLists.txt
+  :language: cmake
+  :start-after: # install the configuration targets
+  :end-before: include(CMakePackageConfigHelpers)
+
+At this point you should try and run CMake. If everything is setup properly
+you will see that CMake will generate an error that looks like:
+
+.. code-block:: console
+
+  Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
+  path:
+
+    "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"
+
+  which is prefixed in the source directory.
+
+What CMake is trying to say is that during generating the export information
+it will export a path that is intrinsically tied to the current machine and
+will not be valid on other machines. The solution to this is to update the
+MathFunctions ``target_include_directories`` to understand that it needs
+different ``INTERFACE`` locations when being used from within the build
+directory and from an install / package. This means converting the
+``target_include_directories`` call for MathFunctions to look like:
+
+.. literalinclude:: Complete/MathFunctions/CMakeLists.txt
+  :language: cmake
+  :start-after: # to find MathFunctions.h, while we don't.
+  :end-before: # should we use our own math functions
+
+Once this has been updated, we can re-run CMake and see verify that it doesn't
+warn anymore.
+
+At this point, we have CMake properly packaging the target information that is
+required but we will still need to generate a ``MathFunctionsConfig.cmake`` so
+that the CMake ``find_package command`` can find our project. So let's go
+ahead and add a new file to the top-level of the project called
+``Config.cmake.in`` with the following contents:
+
+.. literalinclude:: Complete/Config.cmake.in
+
+Then, to properly configure and install that file, add the following to the
+bottom of the top-level CMakeLists:
+
+.. literalinclude:: Complete/CMakeLists.txt
+  :language: cmake
+  :start-after: # install the configuration targets
+  :end-before: # generate the export
+
+At this point, we have generated a relocatable CMake Configuration for our
+project that can be used after the project has been installed or packaged. If
+we want our project to also be used from a build directory we only have to add
+the following to the bottom of the top level CMakeLists:
+
+.. literalinclude:: Complete/CMakeLists.txt
+  :language: cmake
+  :start-after: # needs to be after the install(TARGETS ) command
+
+With this export call we now generate a ``Targets.cmake``, allowing the
+configured ``MathFunctionsConfig.cmake`` in the build directory to be used by
+other projects, without needing it to be installed.
+
+Import a CMake Project (Consumer)
+=================================
+
+This examples shows how a project can find other CMake packages that
+generate ``Config.cmake`` files.
+
+It also shows how to state a project's external dependencies when generating
+a ``Config.cmake``.
+
+Packaging Debug and Release (MultiPackage)
+==========================================
+
+By default CMake is model is that a build directory only contains a single
+configuration, be it Debug, Release, MinSizeRel, or RelWithDebInfo.
+
+But it is possible to setup CPack to bundle multiple build directories at the
+same time to build a package that contains multiple configurations of the
+same project.
+
+First we need to ahead and construct a directory called ``multi_config`` this
+will contain all the builds that we want to package together.
+
+Second create a ``debug`` and ``release`` directory underneath
+``multi_config``. At the end you should have a layout that looks like:
+
+─ multi_config
+    ├── debug
+    └── release
+
+Now we need to setup debug and release builds, which would roughly entail
+the following:
+
+.. code-block:: console
+
+  cd debug
+  cmake -DCMAKE_BUILD_TYPE=Debug ../../MultiPackage/
+  cmake --build .
+  cd ../release
+  cmake -DCMAKE_BUILD_TYPE=Release ../../MultiPackage/
+  cmake --build .
+  cd ..
+
+
+Now that both the debug and release builds are complete we can now use
+the custom MultiCPackConfig to package both builds into a single release.
+
+.. code-block:: console
+
+  cpack --config ../../MultiPackage/MultiCPackConfig.cmake
diff --git a/Help/index.rst b/Help/index.rst
index a948939..cc6cee6 100644
--- a/Help/index.rst
+++ b/Help/index.rst
@@ -44,6 +44,16 @@
    /manual/cmake-variables.7
    /manual/cpack-generators.7
 
+.. only:: not man
+
+ Guides
+ ######
+
+ .. toctree::
+    :maxdepth: 1
+
+    /guide/tutorial/index
+
 .. only:: html or text
 
  Release Notes
diff --git a/Help/manual/OPTIONS_BUILD.txt b/Help/manual/OPTIONS_BUILD.txt
index 810aaa9..0947e41 100644
--- a/Help/manual/OPTIONS_BUILD.txt
+++ b/Help/manual/OPTIONS_BUILD.txt
@@ -18,6 +18,9 @@
  containing :command:`set` commands that use the ``CACHE`` option, not a
  cache-format file.
 
+ References to :variable:`CMAKE_SOURCE_DIR` and :variable:`CMAKE_BINARY_DIR`
+ within the script evaluate to the top-level source and build tree.
+
 ``-D <var>:<type>=<value>, -D <var>=<value>``
  Create or update a CMake ``CACHE`` entry.
 
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst
index 8cd6e68..d8142a2 100644
--- a/Help/manual/cmake-buildsystem.7.rst
+++ b/Help/manual/cmake-buildsystem.7.rst
@@ -811,6 +811,10 @@
   executable target created by the :command:`add_executable` command
   when its :prop_tgt:`ENABLE_EXPORTS` target property is set.
 
+* On AIX: the linker import file (e.g. ``.imp``) of an executable target
+  created by the :command:`add_executable` command when its
+  :prop_tgt:`ENABLE_EXPORTS` target property is set.
+
 The :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
 target properties may be used to control archive output artifact locations
 and names in the build tree.
diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst
index a3bc465..59ba897 100644
--- a/Help/manual/cmake-commands.7.rst
+++ b/Help/manual/cmake-commands.7.rst
@@ -112,6 +112,7 @@
    /command/target_link_directories
    /command/target_link_libraries
    /command/target_link_options
+   /command/target_precompile_headers
    /command/target_sources
    /command/try_compile
    /command/try_run
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index c0449fb..6e7f9b5 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -130,6 +130,16 @@
   ``1`` if the CMake's compiler id of the CUDA compiler matches any one
   of the entries in ``compiler_ids``, otherwise ``0``.
   See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+``$<OBJC_COMPILER_ID:compiler_ids>``
+  where ``compiler_ids`` is a comma-separated list.
+  ``1`` if the CMake's compiler id of the Objective-C compiler matches any one
+  of the entries in ``compiler_ids``, otherwise ``0``.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+``$<OBJCXX_COMPILER_ID:compiler_ids>``
+  where ``compiler_ids`` is a comma-separated list.
+  ``1`` if the CMake's compiler id of the Objective-C++ compiler matches any one
+  of the entries in ``compiler_ids``, otherwise ``0``.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
 ``$<Fortran_COMPILER_ID:compiler_ids>``
   where ``compiler_ids`` is a comma-separated list.
   ``1`` if the CMake's compiler id of the Fortran compiler matches any one
@@ -144,6 +154,12 @@
 ``$<CUDA_COMPILER_VERSION:version>``
   ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``.
   See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<OBJC_COMPILER_VERSION:version>``
+  ``1`` if the version of the OBJC compiler matches ``version``, otherwise ``0``.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<OBJCXX_COMPILER_VERSION:version>``
+  ``1`` if the version of the OBJCXX compiler matches ``version``, otherwise ``0``.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
 ``$<Fortran_COMPILER_VERSION:version>``
   ``1`` if the version of the Fortran compiler matches ``version``, otherwise ``0``.
   See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
@@ -401,6 +417,12 @@
 ``$<CUDA_COMPILER_ID>``
   The CMake's compiler id of the CUDA compiler used.
   See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+``$<OBJC_COMPILER_ID>``
+  The CMake's compiler id of the OBJC compiler used.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+``$<OBJCXX_COMPILER_ID>``
+  The CMake's compiler id of the OBJCXX compiler used.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
 ``$<Fortran_COMPILER_ID>``
   The CMake's compiler id of the Fortran compiler used.
   See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
@@ -413,6 +435,12 @@
 ``$<CUDA_COMPILER_VERSION>``
   The version of the CUDA compiler used.
   See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<OBJC_COMPILER_VERSION>``
+  The version of the OBJC compiler used.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+``$<OBJCXX_COMPILER_VERSION>``
+  The version of the OBJCXX compiler used.
+  See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
 ``$<Fortran_COMPILER_VERSION>``
   The version of the Fortran compiler used.
   See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index fc4bfdc..c60dc40 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -36,6 +36,12 @@
    /module/CheckIncludeFiles
    /module/CheckLanguage
    /module/CheckLibraryExists
+   /module/CheckOBJCCompilerFlag
+   /module/CheckOBJCSourceCompiles
+   /module/CheckOBJCSourceRuns
+   /module/CheckOBJCXXCompilerFlag
+   /module/CheckOBJCXXSourceCompiles
+   /module/CheckOBJCXXSourceRuns
    /module/CheckPIESupported
    /module/CheckPrototypeDefinition
    /module/CheckStructHasMember
diff --git a/Help/manual/cmake-packages.7.rst b/Help/manual/cmake-packages.7.rst
index f5aa42d..4b2934a 100644
--- a/Help/manual/cmake-packages.7.rst
+++ b/Help/manual/cmake-packages.7.rst
@@ -654,8 +654,13 @@
   :command:`export(PACKAGE)` populates the user package registry unless
   the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable explicitly
   disables it.
-* :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` disables the
-  User Package Registry in all the :command:`find_package` calls.
+* :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` disables the
+  User Package Registry in all the :command:`find_package` calls when
+  set to ``FALSE``.
+* Deprecated :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` disables the
+  User Package Registry in all the :command:`find_package` calls when set
+  to ``TRUE``. This variable is ignored when
+  :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` has been set.
 * :variable:`CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY` disables
   the System Package Registry in all the :command:`find_package` calls.
 
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 1d023cb..44ea1a8 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,16 @@
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.16
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0097: ExternalProject_Add with GIT_SUBMODULES "" initializes no submodules. </policy/CMP0097>
+   CMP0096: project() preserves leading zeros in version components. </policy/CMP0096>
+   CMP0095: RPATH entries are properly escaped in the intermediary CMake install script. </policy/CMP0095>
+
 Policies Introduced by CMake 3.15
 =================================
 
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 77b1ae8..1369aa3 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -134,6 +134,7 @@
    /prop_tgt/AUTOMOC_EXECUTABLE
    /prop_tgt/AUTOMOC_MACRO_NAMES
    /prop_tgt/AUTOMOC_MOC_OPTIONS
+   /prop_tgt/AUTOMOC_PATH_PREFIX
    /prop_tgt/AUTOMOC
    /prop_tgt/AUTOUIC
    /prop_tgt/AUTOUIC_EXECUTABLE
@@ -181,6 +182,7 @@
    /prop_tgt/DEFINE_SYMBOL
    /prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY
    /prop_tgt/DEPLOYMENT_ADDITIONAL_FILES
+   /prop_tgt/DISABLE_PRECOMPILE_HEADERS
    /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/EchoString
    /prop_tgt/ENABLE_EXPORTS
@@ -228,6 +230,7 @@
    /prop_tgt/IMPORT_SUFFIX
    /prop_tgt/INCLUDE_DIRECTORIES
    /prop_tgt/INSTALL_NAME_DIR
+   /prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH
    /prop_tgt/INSTALL_RPATH
    /prop_tgt/INSTALL_RPATH_USE_LINK_PATH
    /prop_tgt/INTERFACE_AUTOUIC_OPTIONS
@@ -239,6 +242,7 @@
    /prop_tgt/INTERFACE_LINK_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_LINK_OPTIONS
+   /prop_tgt/INTERFACE_PRECOMPILE_HEADERS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
    /prop_tgt/INTERFACE_SOURCES
    /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
@@ -294,6 +298,8 @@
    /prop_tgt/PDB_OUTPUT_DIRECTORY_CONFIG
    /prop_tgt/PDB_OUTPUT_DIRECTORY
    /prop_tgt/POSITION_INDEPENDENT_CODE
+   /prop_tgt/PRECOMPILE_HEADERS
+   /prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM
    /prop_tgt/PREFIX
    /prop_tgt/PRIVATE_HEADER
    /prop_tgt/PROJECT_LABEL
@@ -315,9 +321,14 @@
    /prop_tgt/STATIC_LIBRARY_OPTIONS
    /prop_tgt/SUFFIX
    /prop_tgt/Swift_DEPENDENCIES_FILE
+   /prop_tgt/Swift_LANGUAGE_VERSION
    /prop_tgt/Swift_MODULE_DIRECTORY
    /prop_tgt/Swift_MODULE_NAME
    /prop_tgt/TYPE
+   /prop_tgt/UNITY_BUILD
+   /prop_tgt/UNITY_BUILD_BATCH_SIZE
+   /prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE
+   /prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE
    /prop_tgt/VERSION
    /prop_tgt/VISIBILITY_INLINES_HIDDEN
    /prop_tgt/VS_CONFIGURATION_TYPE
@@ -331,6 +342,7 @@
    /prop_tgt/VS_DOTNET_REFERENCES
    /prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL
    /prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION
+   /prop_tgt/VS_DPI_AWARE
    /prop_tgt/VS_GLOBAL_KEYWORD
    /prop_tgt/VS_GLOBAL_PROJECT_TYPES
    /prop_tgt/VS_GLOBAL_ROOTNAMESPACE
@@ -363,6 +375,7 @@
    /prop_tgt/XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN
    /prop_tgt/XCODE_SCHEME_ARGUMENTS
    /prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT
+   /prop_tgt/XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
    /prop_tgt/XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
    /prop_tgt/XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
    /prop_tgt/XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
@@ -401,11 +414,13 @@
    /prop_test/LABELS
    /prop_test/MEASUREMENT
    /prop_test/PASS_REGULAR_EXPRESSION
+   /prop_test/PROCESSES
    /prop_test/PROCESSOR_AFFINITY
    /prop_test/PROCESSORS
    /prop_test/REQUIRED_FILES
    /prop_test/RESOURCE_LOCK
    /prop_test/RUN_SERIAL
+   /prop_test/SKIP_REGULAR_EXPRESSION
    /prop_test/SKIP_RETURN_CODE
    /prop_test/TIMEOUT
    /prop_test/TIMEOUT_AFTER_MATCH
@@ -442,6 +457,8 @@
    /prop_sf/SKIP_AUTOMOC
    /prop_sf/SKIP_AUTORCC
    /prop_sf/SKIP_AUTOUIC
+   /prop_sf/SKIP_PRECOMPILE_HEADERS
+   /prop_sf/SKIP_UNITY_BUILD_INCLUSION
    /prop_sf/Swift_DEPENDENCIES_FILE
    /prop_sf/Swift_DIAGNOSTICS_FILE
    /prop_sf/SYMBOLIC
diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst
index 7435d9a..f233d08 100644
--- a/Help/manual/cmake-toolchains.7.rst
+++ b/Help/manual/cmake-toolchains.7.rst
@@ -399,8 +399,10 @@
   be false unless using a NDK that does not provide unified headers.
 
 :variable:`CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION`
-  Set to the version of the NDK toolchain to be selected as the compiler.
-  If not specified, the default will be the latest available GCC toolchain.
+  On NDK r19 or above, this variable must be unset or set to ``clang``.
+  On NDK r18 or below, set this to the version of the NDK toolchain to
+  be selected as the compiler.  If not specified, the default will be
+  the latest available GCC toolchain.
 
 :variable:`CMAKE_ANDROID_STL_TYPE`
   Set to specify which C++ standard library to use.  If not specified,
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index e3c2f9f..0442d89 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -62,6 +62,7 @@
    /variable/CMAKE_JOB_POOLS
    /variable/CMAKE_LANG_COMPILER_AR
    /variable/CMAKE_LANG_COMPILER_RANLIB
+   /variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX
    /variable/CMAKE_LINK_LIBRARY_SUFFIX
    /variable/CMAKE_LINK_SEARCH_END_STATIC
    /variable/CMAKE_LINK_SEARCH_START_STATIC
@@ -69,6 +70,7 @@
    /variable/CMAKE_MAKE_PROGRAM
    /variable/CMAKE_MATCH_COUNT
    /variable/CMAKE_MATCH_n
+   /variable/CMAKE_MESSAGE_INDENT
    /variable/CMAKE_MINIMUM_REQUIRED_VERSION
    /variable/CMAKE_MINOR_VERSION
    /variable/CMAKE_NETRC
@@ -110,6 +112,7 @@
    /variable/CMAKE_VS_PLATFORM_NAME_DEFAULT
    /variable/CMAKE_VS_PLATFORM_TOOLSET
    /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA
+   /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR
    /variable/CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE
    /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
@@ -158,6 +161,7 @@
    /variable/CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES
    /variable/CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT
    /variable/CMAKE_ECLIPSE_MAKE_ARGUMENTS
+   /variable/CMAKE_ECLIPSE_RESOURCE_ENCODING
    /variable/CMAKE_ECLIPSE_VERSION
    /variable/CMAKE_ERROR_DEPRECATED
    /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
@@ -181,6 +185,12 @@
    /variable/CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
    /variable/CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
    /variable/CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
+   /variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH
+   /variable/CMAKE_FIND_USE_CMAKE_PATH
+   /variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH
+   /variable/CMAKE_FIND_USE_PACKAGE_REGISTRY
+   /variable/CMAKE_FIND_USE_PACKAGE_ROOT_PATH
+   /variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH
    /variable/CMAKE_FRAMEWORK_PATH
    /variable/CMAKE_IGNORE_PATH
    /variable/CMAKE_INCLUDE_DIRECTORIES_BEFORE
@@ -224,6 +234,7 @@
    /variable/CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY
    /variable/CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER
    /variable/CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN
+   /variable/CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
    /variable/CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
    /variable/CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
    /variable/CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
@@ -332,6 +343,7 @@
    /variable/CMAKE_AUTOMOC_DEPEND_FILTERS
    /variable/CMAKE_AUTOMOC_MACRO_NAMES
    /variable/CMAKE_AUTOMOC_MOC_OPTIONS
+   /variable/CMAKE_AUTOMOC_PATH_PREFIX
    /variable/CMAKE_AUTORCC
    /variable/CMAKE_AUTORCC_OPTIONS
    /variable/CMAKE_AUTOUIC
@@ -345,7 +357,9 @@
    /variable/CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_CONFIG
    /variable/CMAKE_CONFIG_POSTFIX
    /variable/CMAKE_CUDA_SEPARABLE_COMPILATION
+   /variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
    /variable/CMAKE_DEBUG_POSTFIX
+   /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
@@ -364,6 +378,7 @@
    /variable/CMAKE_INCLUDE_CURRENT_DIR
    /variable/CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
    /variable/CMAKE_INSTALL_NAME_DIR
+   /variable/CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH
    /variable/CMAKE_INSTALL_RPATH
    /variable/CMAKE_INSTALL_RPATH_USE_LINK_PATH
    /variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION
@@ -374,6 +389,8 @@
    /variable/CMAKE_LANG_CPPCHECK
    /variable/CMAKE_LANG_CPPLINT
    /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE
+   /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
+   /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -417,6 +434,8 @@
    /variable/CMAKE_TRY_COMPILE_CONFIGURATION
    /variable/CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
    /variable/CMAKE_TRY_COMPILE_TARGET_TYPE
+   /variable/CMAKE_UNITY_BUILD
+   /variable/CMAKE_UNITY_BUILD_BATCH_SIZE
    /variable/CMAKE_USE_RELATIVE_PATHS
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VS_GLOBALS
@@ -602,7 +621,6 @@
    /variable/CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
    /variable/CPACK_INCLUDE_TOPLEVEL_DIRECTORY
    /variable/CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
-   /variable/CPACK_INSTALL_SCRIPT
    /variable/CPACK_PACKAGING_INSTALL_PREFIX
    /variable/CPACK_SET_DESTDIR
    /variable/CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 7b5399d..4ab55a0 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -200,12 +200,15 @@
  from the top of a binary tree for a CMake project it will dump
  additional information such as the cache, log files etc.
 
-``--loglevel=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>``
+``--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>``
  Set the log level.
 
  The :command:`message` command will only output messages of the specified
  log level or higher.  The default log level is ``STATUS``.
 
+ For backward compatibility reasons, ``--loglevel`` is also accepted as a
+ synonym for this option.
+
 ``--debug-trycompile``
  Do not delete the :command:`try_compile` build tree.
  Only useful on one :command:`try_compile` at a time.
@@ -238,6 +241,9 @@
 
  Multiple options are allowed.
 
+``--trace-redirect=<file>``
+ Put cmake in trace mode and redirect trace output to a file instead of stderr.
+
 ``--warn-uninitialized``
  Warn about uninitialized values.
 
@@ -459,7 +465,7 @@
   but the files or directories it point to.
 
 ``copy_directory <dir>... <destination>``
-  Copy directories to ``<destination>`` directory.
+  Copy content of ``<dir>...`` directories to ``<destination>`` directory.
   If ``<destination>`` directory does not exist it will be created.
   ``copy_directory`` does follow symlinks.
 
@@ -470,6 +476,12 @@
   directory and it must exist.
   ``copy_if_different`` does follow symlinks.
 
+``create_symlink <old> <new>``
+  Create a symbolic link ``<new>`` naming ``<old>``.
+
+  .. note::
+    Path to where ``<new>`` symbolic link will be created has to exist beforehand.
+
 ``echo [<string>...]``
   Displays arguments as text.
 
@@ -482,6 +494,9 @@
 ``environment``
   Display the current environment variables.
 
+``false``
+  Do nothing, with an exit code of 1.
+
 ``make_directory <dir>...``
   Create ``<dir>`` directories.  If necessary, create parent
   directories too.  If a directory already exists it will be
@@ -533,7 +548,8 @@
 
 ``remove_directory <dir>...``
   Remove ``<dir>`` directories and their contents.  If a directory does
-  not exist it will be silently ignored.
+  not exist it will be silently ignored.  If ``<dir>`` is a symlink to
+  a directory, just the symlink will be removed.
 
 ``rename <oldname> <newname>``
   Rename a file or directory (on one volume). If file with the ``<newname>`` name
@@ -598,11 +614,8 @@
   Touch a file if it exists but do not create it.  If a file does
   not exist it will be silently ignored.
 
-``create_symlink <old> <new>``
-  Create a symbolic link ``<new>`` naming ``<old>``.
-
-.. note::
-  Path to where ``<new>`` symbolic link will be created has to exist beforehand.
+``true``
+  Do nothing, with an exit code of 0.
 
 Windows-specific Command-Line Tools
 -----------------------------------
diff --git a/Help/manual/cpack.1.rst b/Help/manual/cpack.1.rst
index 10f617e..f82c466 100644
--- a/Help/manual/cpack.1.rst
+++ b/Help/manual/cpack.1.rst
@@ -48,9 +48,11 @@
   the :variable:`CPACK_GENERATOR` variable determines the default set of
   generators that will be used.
 
-``-C <Configuration>``
-  Specify the project configuration to be packaged (e.g. ``Debug``,
-  ``Release``, etc.).  When the CMake project uses a multi-configuration
+``-C <configs>``
+  Specify the project configuration(s) to be packaged (e.g. ``Debug``,
+  ``Release``, etc.), where ``<configs>`` is a
+  :ref:`semicolon-separated list <CMake Language Lists>`.
+  When the CMake project uses a multi-configuration
   generator such as Xcode or Visual Studio, this option is needed to tell
   ``cpack`` which built executables to include in the package.
 
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 5773176..a18d43f 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -90,6 +90,15 @@
 
  See `Label and Subproject Summary`_.
 
+``--hardware-spec-file <file>``
+ Run CTest with :ref:`hardware allocation <ctest-hardware-allocation>` enabled,
+ using the
+ :ref:`hardware specification file <ctest-hardware-specification-file>`
+ specified in ``<file>``.
+
+ When ``ctest`` is run as a `Dashboard Client`_ this sets the
+ ``HardwareSpecFile`` option of the `CTest Test Step`_.
+
 ``--test-load <level>``
  While running tests in parallel (e.g. with ``-j``), try not to start
  tests when they may cause the CPU load to pass above a given threshold.
@@ -443,7 +452,8 @@
  Specify the name of the project to build.
 
 ``--build-makeprogram``
- Override the make program chosen by CTest with a given one.
+ Specify the explicit make program to be used by CMake when configuring and
+ building the project. Only applicable for Make and Ninja based generators.
 
 ``--build-noclean``
  Skip the make clean step.
@@ -481,14 +491,17 @@
 
 Options for Dashboard Client include:
 
-``--track <track>``
- Specify the track to submit dashboard to
+``--group <group>``
+ Specify what group you'd like to submit results to
 
- Submit dashboard to specified track instead of default one.  By
+ Submit dashboard to specified group instead of default one.  By
  default, the dashboard is submitted to Nightly, Experimental, or
- Continuous track, but by specifying this option, the track can be
+ Continuous group, but by specifying this option, the group can be
  arbitrary.
 
+ This replaces the deprecated option ``--track``.
+ Despite the name change its behavior is unchanged.
+
 ``-A <file>, --add-notes <file>``
  Add a notes file with submission.
 
@@ -954,6 +967,11 @@
 
 Configuration settings include:
 
+``HardwareSpecFile``
+  Specify a
+  :ref:`hardware specification file <ctest-hardware-specification-file>`. See
+  :ref:`ctest-hardware-allocation` for more information.
+
 ``LabelsForSubprojects``
   Specify a semicolon-separated list of labels that will be treated as
   subprojects. This mapping will be passed on to CDash when configure, test or
@@ -1263,6 +1281,221 @@
     Test properties.
     Can contain keys for each of the supported test properties.
 
+.. _`ctest-hardware-allocation`:
+
+Hardware Allocation
+===================
+
+CTest provides a mechanism for tests to specify the hardware that they need and
+how much of it they need, and for users to specify the hardware availiable on
+the running machine. This allows CTest to internally keep track of which
+hardware is in use and which is free, scheduling tests in a way that prevents
+them from trying to claim hardware that is not available.
+
+A common use case for this feature is for tests that require the use of a GPU.
+Multiple tests can simultaneously allocate memory from a GPU, but if too many
+tests try to do this at once, some of them will fail to allocate, resulting in
+a failed test, even though the test would have succeeded if it had the memory
+it needed. By using the hardware allocation feature, each test can specify how
+much memory it requires from a GPU, allowing CTest to schedule tests in a way
+that running several of these tests at once does not exhaust the GPU's memory
+pool.
+
+Please note that CTest has no concept of what a GPU is or how much memory it
+has, nor does it have any way of communicating with a GPU to retrieve this
+information or perform any memory management. CTest simply keeps track of a
+list of abstract resource types, each of which has a certain number of slots
+available for tests to use. Each test specifies the number of slots that it
+requires from a certain resource, and CTest then schedules them in a way that
+prevents the total number of slots in use from exceeding the listed capacity.
+When a test is executed, and slots from a resource are allocated to that test,
+tests may assume that they have exclusive use of those slots for the duration
+of the test's process.
+
+The CTest hardware allocation feature consists of two inputs:
+
+* The :ref:`hardware specification file <ctest-hardware-specification-file>`,
+  described below, which describes the hardware resources available on the
+  system, and
+* The :prop_test:`PROCESSES` property of tests, which describes the resources
+  required by the test
+
+When CTest runs a test, the hardware allocated to that test is passed in the
+form of a set of
+:ref:`environment variables <ctest-hardware-environment-variables>` as
+described below. Using this information to decide which resource to connect to
+is left to the test writer.
+
+Please note that these processes are not spawned by CTest. The ``PROCESSES``
+property merely tells CTest what processes the test expects to launch. It is up
+to the test itself to do this process spawning, and read the :ref:`environment
+variables <ctest-hardware-environment-variables>` to determine which resources
+each process has been allocated.
+
+.. _`ctest-hardware-specification-file`:
+
+Hardware Specification File
+---------------------------
+
+The hardware specification file is a JSON file which is passed to CTest, either
+on the :manual:`ctest(1)` command line as ``--hardware-spec-file``, or as the
+``HARDWARE_SPEC_FILE`` argument of :command:`ctest_test`. The hardware
+specification file must be a JSON object. All examples in this document assume
+the following hardware specification file:
+
+.. code-block:: json
+
+  {
+    "local": [
+      {
+        "gpus": [
+          {
+            "id": "0",
+            "slots": 2
+          },
+          {
+            "id": "1",
+            "slots": 4
+          },
+          {
+            "id": "2",
+            "slots": 2
+          },
+          {
+            "id": "3"
+          }
+        ],
+        "crypto_chips": [
+          {
+            "id": "card0",
+            "slots": 4
+          }
+        ]
+      }
+    ]
+  }
+
+The members are:
+
+``local``
+  A JSON array consisting of CPU sockets present on the system. Currently, only
+  one socket is supported.
+
+  Each socket is a JSON object with members whose names are equal to the
+  desired resource types, such as ``gpu``. These names must start with a
+  lowercase letter or an underscore, and subsequent characters can be a
+  lowercase letter, a digit, or an underscore. Uppercase letters are not
+  allowed, because certain platforms have case-insensitive environment
+  variables. See the `Environment Variables`_ section below for
+  more information. It is recommended that the resource type name be the plural
+  of a noun, such as ``gpus`` or ``crypto_chips`` (and not ``gpu`` or
+  ``crypto_chip``.)
+
+  Please note that the names ``gpus`` and ``crypto_chips`` are just examples,
+  and CTest does not interpret them in any way. You are free to make up any
+  resource type you want to meet your own requirements.
+
+  The value for each resource type is a JSON array consisting of JSON objects,
+  each of which describe a specific instance of the specified resource. These
+  objects have the following members:
+
+  ``id``
+    A string consisting of an identifier for the resource. Each character in
+    the identifier can be a lowercase letter, a digit, or an underscore.
+    Uppercase letters are not allowed.
+
+    Identifiers must be unique within a resource type. However, they do not
+    have to be unique across resource types. For example, it is valid to have a
+    ``gpus`` resource named ``0`` and a ``crypto_chips`` resource named ``0``,
+    but not two ``gpus`` resources both named ``0``.
+
+    Please note that the IDs ``0``, ``1``, ``2``, ``3``, and ``card0`` are just
+    examples, and CTest does not interpret them in any way. You are free to
+    make up any IDs you want to meet your own requirements.
+
+  ``slots``
+    An optional unsigned number specifying the number of slots available on the
+    resource. For example, this could be megabytes of RAM on a GPU, or
+    cryptography units available on a cryptography chip. If ``slots`` is not
+    specified, a default value of ``1`` is assumed.
+
+In the example file above, there are four GPUs with ID's 0 through 3. GPU 0 has
+2 slots, GPU 1 has 4, GPU 2 has 2, and GPU 3 has a default of 1 slot. There is
+also one cryptography chip with 4 slots.
+
+``PROCESSES`` Property
+----------------------
+
+See :prop_test:`PROCESSES` for a description of this property.
+
+.. _`ctest-hardware-environment-variables`:
+
+Environment Variables
+---------------------
+
+Once CTest has decided which resources to allocate to a test, it passes this
+information to the test executable as a series of environment variables. For
+each example below, we will assume that the test in question has a
+:prop_test:`PROCESSES` property of ``2,gpus:2;gpus:4,gpus:1,crypto_chips:2``.
+
+The following variables are passed to the test process:
+
+.. envvar:: CTEST_PROCESS_COUNT
+
+  The total number of processes specified by the :prop_test:`PROCESSES`
+  property. For example:
+
+  * ``CTEST_PROCESS_COUNT=3``
+
+  This variable will only be defined if :manual:`ctest(1)` has been given a
+  ``--hardware-spec-file``, or if :command:`ctest_test` has been given a
+  ``HARDWARE_SPEC_FILE``. If no hardware specification file has been given,
+  this variable will not be defined.
+
+.. envvar:: CTEST_PROCESS_<num>
+
+  The list of resource types allocated to each process, with each item
+  separated by a comma. ``<num>`` is a number from zero to
+  ``CTEST_PROCESS_COUNT`` minus one. ``CTEST_PROCESS_<num>`` is defined for
+  each ``<num>`` in this range. For example:
+
+  * ``CTEST_PROCESS_0=gpus``
+  * ``CTEST_PROCESS_1=gpus``
+  * ``CTEST_PROCESS_2=crypto_chips,gpus``
+
+.. envvar:: CTEST_PROCESS_<num>_<resource-type>
+
+  The list of resource IDs and number of slots from each ID allocated to each
+  process for a given resource type. This variable consists of a series of
+  pairs, each pair separated by a semicolon, and with the two items in the pair
+  separated by a comma. The first item in each pair is ``id:`` followed by the
+  ID of a resource of type ``<resource-type>``, and the second item is
+  ``slots:`` followed by the number of slots from that resource allocated to
+  the given process. For example:
+
+  * ``CTEST_PROCESS_0_GPUS=id:0,slots:2``
+  * ``CTEST_PROCESS_1_GPUS=id:2,slots:2``
+  * ``CTEST_PROCESS_2_GPUS=id:1,slots:4;id:3,slots:1``
+  * ``CTEST_PROCESS_2_CRYPTO_CHIPS=id:card0,slots:2``
+
+  In this example, process 0 gets 2 slots from GPU ``0``, process 1 gets 2 slots
+  from GPU ``2``, and process 2 gets 4 slots from GPU ``1`` and 2 slots from
+  cryptography chip ``card0``.
+
+  ``<num>`` is a number from zero to ``CTEST_PROCESS_COUNT`` minus one.
+  ``<resource-type>`` is the name of a resource type, converted to uppercase.
+  ``CTEST_PROCESS_<num>_<resource-type>`` is defined for the product of each
+  ``<num>`` in the range listed above and each resource type listed in
+  ``CTEST_PROCESS_<num>``.
+
+  Because some platforms have case-insensitive names for environment variables,
+  the names of resource types may not clash in a case-insensitive environment.
+  Because of this, for the sake of simplicity, all resource types must be
+  listed in all lowercase in the
+  :ref:`hardware specification file <ctest-hardware-specification-file>` and in
+  the :prop_test:`PROCESSES` property, and they are converted to all uppercase
+  in the ``CTEST_PROCESS_<num>_<resource-type>`` environment variable.
+
 See Also
 ========
 
diff --git a/Help/module/CheckOBJCCompilerFlag.rst b/Help/module/CheckOBJCCompilerFlag.rst
new file mode 100644
index 0000000..e4bd6fd
--- /dev/null
+++ b/Help/module/CheckOBJCCompilerFlag.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCCompilerFlag.cmake
diff --git a/Help/module/CheckOBJCSourceCompiles.rst b/Help/module/CheckOBJCSourceCompiles.rst
new file mode 100644
index 0000000..d4a1484
--- /dev/null
+++ b/Help/module/CheckOBJCSourceCompiles.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCSourceCompiles.cmake
diff --git a/Help/module/CheckOBJCSourceRuns.rst b/Help/module/CheckOBJCSourceRuns.rst
new file mode 100644
index 0000000..c72f0db
--- /dev/null
+++ b/Help/module/CheckOBJCSourceRuns.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCSourceRuns.cmake
diff --git a/Help/module/CheckOBJCXXCompilerFlag.rst b/Help/module/CheckOBJCXXCompilerFlag.rst
new file mode 100644
index 0000000..1518a48
--- /dev/null
+++ b/Help/module/CheckOBJCXXCompilerFlag.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCXXCompilerFlag.cmake
diff --git a/Help/module/CheckOBJCXXSourceCompiles.rst b/Help/module/CheckOBJCXXSourceCompiles.rst
new file mode 100644
index 0000000..a1c8ae9
--- /dev/null
+++ b/Help/module/CheckOBJCXXSourceCompiles.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCXXSourceCompiles.cmake
diff --git a/Help/module/CheckOBJCXXSourceRuns.rst b/Help/module/CheckOBJCXXSourceRuns.rst
new file mode 100644
index 0000000..5198e1b
--- /dev/null
+++ b/Help/module/CheckOBJCXXSourceRuns.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckOBJCXXSourceRuns.cmake
diff --git a/Help/policy/CMP0095.rst b/Help/policy/CMP0095.rst
new file mode 100644
index 0000000..4c56a05
--- /dev/null
+++ b/Help/policy/CMP0095.rst
@@ -0,0 +1,30 @@
+CMP0095
+-------
+
+``RPATH`` entries are properly escaped in the intermediary CMake install script.
+
+In CMake 3.15 and earlier, ``RPATH`` entries set via
+:variable:`CMAKE_INSTALL_RPATH` or via :prop_tgt:`INSTALL_RPATH` have not been
+escaped before being inserted into the ``cmake_install.cmake`` script. Dynamic
+linkers on ELF-based systems (e.g. Linux and FreeBSD) allow certain keywords in
+``RPATH`` entries, such as ``${ORIGIN}`` (More details are available in the
+``ld.so`` man pages on those systems). The syntax of these keywords can match
+CMake's variable syntax. In order to not be substituted (usually to an empty
+string) already by the intermediary ``cmake_install.cmake`` script, the user had
+to double-escape such ``RPATH`` keywords, e.g.
+``set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")``. Since the intermediary
+``cmake_install.cmake`` script is an implementation detail of CMake, CMake 3.16
+and later will make sure ``RPATH`` entries are inserted literally by escaping
+any coincidental CMake syntax.
+
+The ``OLD`` behavior of this policy is to not escape ``RPATH`` entries in the
+intermediary ``cmake_install.cmake`` script. The ``NEW`` behavior is to properly
+escape coincidental CMake syntax in ``RPATH`` entries when generating the
+intermediary ``cmake_install.cmake`` script.
+
+This policy was introduced in CMake version 3.16. CMake version |release| warns
+when the policy is not set and detected usage of CMake-like syntax and uses
+``OLD`` behavior. Use the :command:`cmake_policy` command to set it to ``OLD``
+or ``NEW`` explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0096.rst b/Help/policy/CMP0096.rst
new file mode 100644
index 0000000..8eaf0f9
--- /dev/null
+++ b/Help/policy/CMP0096.rst
@@ -0,0 +1,25 @@
+CMP0096
+-------
+
+The :command:`project` command preserves leading zeros in version components.
+
+When a ``VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]`` argument is given
+to the :command:`project` command, it stores the version string in the
+``PROJECT_VERSION`` variable and stores individual integer version components
+in ``PROJECT_VERSION_{MAJOR,MINOR,PATCH,TWEAK}`` variables (see policy
+:policy:`CMP0048`).  CMake 3.15 and below dropped leading zeros from each
+component.  CMake 3.16 and higher prefer to preserve leading zeros.  This
+policy provides compatibility for projects that have not been updated to
+expect the new behavior.
+
+The ``OLD`` behavior of this policy drops leading zeros in all components,
+e.g.  such that version ``1.07.06`` becomes ``1.7.6``.  The ``NEW`` behavior
+of this policy preserves the leading zeros in all components, such that
+version ``1.07.06`` remains unchanged.
+
+This policy was introduced in CMake version 3.16.  Unlike many policies, CMake
+version |release| does *not* warn when this policy is not set and simply uses
+the ``OLD`` behavior.  Use the :command:`cmake_policy` command to set it to
+``OLD`` or ``NEW`` explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst
new file mode 100644
index 0000000..8a5ff88
--- /dev/null
+++ b/Help/policy/CMP0097.rst
@@ -0,0 +1,23 @@
+CMP0097
+-------
+
+:command:`ExternalProject_Add` with ``GIT_SUBMODULES ""`` initializes no
+submodules.
+
+The module provides a ``GIT_SUBMODULES`` option which controls what submodules
+to initialize and update. Starting with CMake 3.16, explicitly setting
+``GIT_SUBMODULES`` to an empty string means no submodules will be initialized
+or updated.
+
+This policy provides compatibility for projects that have not been updated
+to expect the new behavior.
+
+The ``OLD`` behavior for this policy is for ``GIT_SUBMODULES`` when set to
+an empty string to initialize and update all git submodules.
+The ``New`` behavior for this policy is for ``GIT_SUBMODULES`` when set to
+an empty string to initialize and update no git submodules.
+
+This policy was introduced in CMake version 3.16.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike most policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
diff --git a/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..5f39f30
--- /dev/null
+++ b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,13 @@
+SKIP_PRECOMPILE_HEADERS
+-----------------------
+
+Is this source file skipped by :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+This property helps with build problems that one would run into
+when using the :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+One example would be the usage of Objective-C (*.m) files, and
+Objective-C++ (*.mm) files, which lead to compilation failure
+because they are treated (in case of Ninja / Makefile generator)
+as C, and CXX respectively. The precompile headers are not
+compatible between languages.
diff --git a/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst b/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst
new file mode 100644
index 0000000..53f3970
--- /dev/null
+++ b/Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst
@@ -0,0 +1,7 @@
+SKIP_UNITY_BUILD_INCLUSION
+--------------------------
+
+Is this source file skipped by :prop_tgt:`UNITY_BUILD` feature.
+
+This property helps with "ODR (One definition rule)" problems
+that one would run into when using an :prop_tgt:`UNITY_BUILD`.
diff --git a/Help/prop_test/PROCESSES.rst b/Help/prop_test/PROCESSES.rst
new file mode 100644
index 0000000..d09c6d1
--- /dev/null
+++ b/Help/prop_test/PROCESSES.rst
@@ -0,0 +1,54 @@
+PROCESSES
+----------
+
+Set to specify the number of processes spawned by a test, and the resources
+that they require. See :ref:`hardware allocation <ctest-hardware-allocation>`
+for more information on how this property integrates into the CTest hardware
+allocation feature.
+
+The ``PROCESSES`` property is a :ref:`semicolon-separated list <CMake Language
+Lists>` of process descriptions. Each process description consists of an
+optional number of processes for the description followed by a series of
+resource requirements for those processes. These requirements (and the number
+of processes) are separated by commas. The resource requirements consist of the
+name of a resource type, followed by a colon, followed by an unsigned integer
+specifying the number of slots required on one resource of the given type.
+
+Please note that these processes are not spawned by CTest. The ``PROCESSES``
+property merely tells CTest what processes the test expects to launch. It is up
+to the test itself to do this process spawning, and read the :ref:`environment
+variables <ctest-hardware-environment-variables>` to determine which resources
+each process has been allocated.
+
+Consider the following example:
+
+.. code-block:: cmake
+
+  add_test(NAME MyTest COMMAND MyExe)
+  set_property(TEST MyTest PROPERTY PROCESSES
+    "2,gpus:2"
+    "gpus:4,crypto_chips:2")
+
+In this example, there are two process descriptions (implicitly separated by a
+semicolon.) The content of the first description is ``2,gpus:2``. This
+description spawns 2 processes, each of which requires 2 slots from a single
+GPU. The content of the second description is ``gpus:4,crypto_chips:2``. This
+description does not specify a process count, so a default of 1 is assumed.
+This single process requires 4 slots from a single GPU and 2 slots from a
+single cryptography chip. In total, 3 processes are spawned from this test,
+each with their own unique requirements.
+
+When CTest sets the :ref:`environment variables
+<ctest-hardware-environment-variables>` for a test, it assigns a process number
+based on the process description, starting at 0 on the left and the number of
+processes minus 1 on the right. For example, in the example above, the two
+processes in the first description would have IDs of 0 and 1, and the single
+process in the second description would have an ID of 2.
+
+Both the ``PROCESSES`` and :prop_test:`RESOURCE_LOCK` properties serve similar
+purposes, but they are distinct and orthogonal. Resources specified by
+``PROCESSES`` do not affect :prop_test:`RESOURCE_LOCK`, and vice versa. Whereas
+:prop_test:`RESOURCE_LOCK` is a simpler property that is used for locking one
+global resource, ``PROCESSES`` is a more advanced property that allows multiple
+tests to simultaneously use multiple resources of the same type, specifying
+their requirements in a fine-grained manner.
diff --git a/Help/prop_test/RESOURCE_LOCK.rst b/Help/prop_test/RESOURCE_LOCK.rst
index 755e0aa..7d61f77 100644
--- a/Help/prop_test/RESOURCE_LOCK.rst
+++ b/Help/prop_test/RESOURCE_LOCK.rst
@@ -8,3 +8,11 @@
 
 See also :prop_test:`FIXTURES_REQUIRED` if the resource requires any setup or
 cleanup steps.
+
+Both the :prop_test:`PROCESSES` and ``RESOURCE_LOCK`` properties serve similar
+purposes, but they are distinct and orthogonal. Resources specified by
+:prop_test:`PROCESSES` do not affect ``RESOURCE_LOCK``, and vice versa. Whereas
+``RESOURCE_LOCK`` is a simpler property that is used for locking one global
+resource, :prop_test:`PROCESSES` is a more advanced property that allows
+multiple tests to simultaneously use multiple resources of the same type,
+specifying their requirements in a fine-grained manner.
diff --git a/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst
new file mode 100644
index 0000000..2c6d980
--- /dev/null
+++ b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst
@@ -0,0 +1,17 @@
+SKIP_REGULAR_EXPRESSION
+-----------------------
+
+If the output matches this regular expression the test will be marked as skipped.
+
+If set, if the output matches one of specified regular expressions,
+the test will be marked as skipped.  Example:
+
+.. code-block:: cmake
+
+  set_property(TEST mytest PROPERTY
+    SKIP_REGULAR_EXPRESSION "[^a-z]Skip" "SKIP" "Skipped"
+  )
+
+``SKIP_REGULAR_EXPRESSION`` expects a list of regular expressions.
+
+See also the :prop_test:`SKIP_RETURN_CODE` property.
diff --git a/Help/prop_test/SKIP_RETURN_CODE.rst b/Help/prop_test/SKIP_RETURN_CODE.rst
index a05fbf3..23c4c62 100644
--- a/Help/prop_test/SKIP_RETURN_CODE.rst
+++ b/Help/prop_test/SKIP_RETURN_CODE.rst
@@ -6,4 +6,7 @@
 Sometimes only a test itself can determine if all requirements for the
 test are met. If such a situation should not be considered a hard failure
 a return code of the process can be specified that will mark the test as
-``Not Run`` if it is encountered.
+``Not Run`` if it is encountered. Valid values are in the range of
+0 to 255, inclusive.
+
+See also the :prop_test:`SKIP_REGULAR_EXPRESSION` property.
diff --git a/Help/prop_tgt/AUTOMOC_PATH_PREFIX.rst b/Help/prop_tgt/AUTOMOC_PATH_PREFIX.rst
new file mode 100644
index 0000000..e2ebb3f
--- /dev/null
+++ b/Help/prop_tgt/AUTOMOC_PATH_PREFIX.rst
@@ -0,0 +1,32 @@
+AUTOMOC_PATH_PREFIX
+-------------------
+
+When this property is ``ON``, CMake will generate the ``-p`` path prefix
+option for ``moc`` on :prop_tgt:`AUTOMOC` enabled Qt targets.
+
+To generate the path prefix, CMake tests if the header compiled by ``moc``
+is in any of the target
+:command:`include directories <target_include_directories>`.  If so, CMake will
+compute the relative path accordingly.  If the header is not in the
+:command:`include directories <target_include_directories>`, CMake will omit
+the ``-p`` path prefix option.  ``moc`` usually generates a
+relative include path in that case.
+
+:prop_tgt:`AUTOMOC_PATH_PREFIX` is initialized from the variable
+:variable:`CMAKE_AUTOMOC_PATH_PREFIX`, which is ``ON`` by default.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
+
+Reproducible builds
+^^^^^^^^^^^^^^^^^^^
+
+For reproducible builds is is recommended to keep headers that are ``moc``
+compiled in one of the target
+:command:`include directories <target_include_directories>` and set
+:prop_tgt:`AUTOMOC_PATH_PREFIX` to ``ON`` (which is the default).  This ensures
+that
+
+- ``moc`` output files are identical on different build setups,
+- ``moc`` output files will compile correctly when the source and/or
+  build directory is a symbolic link.
diff --git a/Help/prop_tgt/BUILD_RPATH.rst b/Help/prop_tgt/BUILD_RPATH.rst
index 13c9c1d..d978b94 100644
--- a/Help/prop_tgt/BUILD_RPATH.rst
+++ b/Help/prop_tgt/BUILD_RPATH.rst
@@ -8,3 +8,6 @@
 
 This property is initialized by the value of the variable
 :variable:`CMAKE_BUILD_RPATH` if it is set when a target is created.
+
+This property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst b/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst
index ef74ae2..dae960f 100644
--- a/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst
+++ b/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst
@@ -1,12 +1,16 @@
 CUDA_RESOLVE_DEVICE_SYMBOLS
 ---------------------------
 
-CUDA only: Enables device linking for the specific library target
+CUDA only: Enables device linking for the specific library target where
+required.
 
-If set this will enable device linking on the library target. Normally
-device linking is deferred until a shared library or executable is generated,
-allowing for multiple static libraries to resolve device symbols at the same
-time when they are used by a shared library or executable.
+If set, this will tell the required compilers to enable device linking
+on the library target. Device linking is an additional link step
+required by some CUDA compilers when :prop_tgt:`CUDA_SEPARABLE_COMPILATION` is
+enabled. Normally device linking is deferred until a shared library or
+executable is generated, allowing for multiple static libraries to resolve
+device symbols at the same time when they are used by a shared library or
+executable.
 
 By default static library targets have this property is disabled,
 while shared, module, and executable targets have this property enabled.
diff --git a/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst b/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..4cef023
--- /dev/null
+++ b/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,8 @@
+DISABLE_PRECOMPILE_HEADERS
+--------------------------
+
+Disables the precompilation of header files specified by
+:prop_tgt:`PRECOMPILE_HEADERS` property.
+
+If the property is not set, CMake will use the value provided
+by :variable:`CMAKE_DISABLE_PRECOMPILE_HEADERS`.
diff --git a/Help/prop_tgt/ENABLE_EXPORTS.rst b/Help/prop_tgt/ENABLE_EXPORTS.rst
index 581c2b9..0b1064a 100644
--- a/Help/prop_tgt/ENABLE_EXPORTS.rst
+++ b/Help/prop_tgt/ENABLE_EXPORTS.rst
@@ -7,16 +7,25 @@
 final program.  It is possible for an executable to export symbols to
 be used by loadable modules.  When this property is set to true CMake
 will allow other targets to "link" to the executable with the
-:command:`TARGET_LINK_LIBRARIES` command.  On all platforms a target-level
+:command:`target_link_libraries` command.  On all platforms a target-level
 dependency on the executable is created for targets that link to it.
-For DLL platforms an import library will be created for the exported
-symbols and then used for linking.  All Windows-based systems
-including Cygwin are DLL platforms.  For non-DLL platforms that
-require all symbols to be resolved at link time, such as macOS, the
-module will "link" to the executable using a flag like
-``-bundle_loader``.  For other non-DLL platforms the link rule is simply
-ignored since the dynamic loader will automatically bind symbols when
-the module is loaded.
+Handling of the executable on the link lines of the loadable modules
+varies by platform:
+
+* On Windows-based systems (including Cygwin) an "import library" is
+  created along with the executable to list the exported symbols.
+  Loadable modules link to the import library to get the symbols.
+
+* On macOS, loadable modules link to the executable itself using the
+  ``-bundle_loader`` flag.
+
+* On AIX, a linker "import file" is created along with the executable
+  to list the exported symbols for import when linking other targets.
+  Loadable modules link to the import file to get the symbols.
+
+* On other platforms, loadable modules are simply linked without
+  referencing the executable since the dynamic loader will
+  automatically bind symbols when the module is loaded.
 
 This property is initialized by the value of the variable
 :variable:`CMAKE_ENABLE_EXPORTS` if it is set when a target is created.
diff --git a/Help/prop_tgt/IMPORTED_IMPLIB.rst b/Help/prop_tgt/IMPORTED_IMPLIB.rst
index 77fb552..c8b6fde 100644
--- a/Help/prop_tgt/IMPORTED_IMPLIB.rst
+++ b/Help/prop_tgt/IMPORTED_IMPLIB.rst
@@ -3,5 +3,7 @@
 
 Full path to the import library for an ``IMPORTED`` target.
 
-Set this to the location of the ``.lib`` part of a Windows DLL.  Ignored
-for non-imported targets.
+Set this to the location of the ``.lib`` part of a Windows DLL, or on
+AIX set it to an import file created for executables that export symbols
+(see the :prop_tgt:`ENABLE_EXPORTS` target property).
+Ignored for non-imported targets.
diff --git a/Help/prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH.rst b/Help/prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH.rst
new file mode 100644
index 0000000..a474fc6
--- /dev/null
+++ b/Help/prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH.rst
@@ -0,0 +1,10 @@
+INSTALL_REMOVE_ENVIRONMENT_RPATH
+--------------------------------
+
+Removes compiler defined rpaths durimg installation.
+
+``INSTALL_REMOVE_ENVIRONMENT_RPATH`` is a boolean that if set to ``True`` will
+remove compiler defined rpaths from the project if the user also defines rpath
+with :prop_tgt:`INSTALL_RPATH`.  This property is initialized by whether the
+value of :variable:`CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH` is set when a
+target is created.
diff --git a/Help/prop_tgt/INSTALL_RPATH.rst b/Help/prop_tgt/INSTALL_RPATH.rst
index 6403f4c..93b4488 100644
--- a/Help/prop_tgt/INSTALL_RPATH.rst
+++ b/Help/prop_tgt/INSTALL_RPATH.rst
@@ -7,3 +7,6 @@
 targets (for platforms that support it).  This property is initialized
 by the value of the variable :variable:`CMAKE_INSTALL_RPATH` if it is set when
 a target is created.
+
+This property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst b/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..8ff7e8b
--- /dev/null
+++ b/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,14 @@
+INTERFACE_PRECOMPILE_HEADERS
+----------------------------
+
+List of interface header files to precompile into consuming targets.
+
+Targets may populate this property to publish the header files
+for consuming targets to precompile.  The :command:`target_precompile_headers`
+command populates this property with values given to the ``PUBLIC`` and
+``INTERFACE`` keywords.  Projects may also get and set the property directly.
+
+Contents of ``INTERFACE_PRECOMPILE_HEADERS`` may use "generator expressions"
+with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.
diff --git a/Help/prop_tgt/PRECOMPILE_HEADERS.rst b/Help/prop_tgt/PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..9e70b65
--- /dev/null
+++ b/Help/prop_tgt/PRECOMPILE_HEADERS.rst
@@ -0,0 +1,12 @@
+PRECOMPILE_HEADERS
+------------------
+
+List of header files to precompile.
+
+This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
+of header files to precompile specified so far for its target.
+Use the :command:`target_precompile_headers` command to append more header
+files.
+
+This property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM.rst b/Help/prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM.rst
new file mode 100644
index 0000000..9c3e7ea
--- /dev/null
+++ b/Help/prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM.rst
@@ -0,0 +1,7 @@
+PRECOMPILE_HEADERS_REUSE_FROM
+-----------------------------
+
+Target from which to reuse the precompiled headers build artifact.
+
+See the second signature of :command:`target_precompile_headers` command
+for more detailed information.
diff --git a/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst b/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst
new file mode 100644
index 0000000..7579447
--- /dev/null
+++ b/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst
@@ -0,0 +1,6 @@
+Swift_LANGUAGE_VERSION
+----------------------
+
+This property sets the language version for the Swift sources in the target.  If
+one is not specified, it will default to ``<CMAKE_Swift_LANGUAGE_VERSION>`` if
+specified, otherwise it is the latest version supported by the compiler.
diff --git a/Help/prop_tgt/UNITY_BUILD.rst b/Help/prop_tgt/UNITY_BUILD.rst
new file mode 100644
index 0000000..beac5d4
--- /dev/null
+++ b/Help/prop_tgt/UNITY_BUILD.rst
@@ -0,0 +1,55 @@
+UNITY_BUILD
+-----------
+
+Should the target source files be processed into batches for
+faster compilation. This feature is known as "Unity build",
+or "Jumbo build".
+
+The `C` and `CXX` source files are grouped separately.
+
+This property is initialized by the value of the
+:variable:`CMAKE_UNITY_BUILD` variable if it is set when
+a target is created.
+
+.. note ::
+
+   It's not recommended to directly set :prop_tgt:`UNITY_BUILD`
+   to `ON`, but to instead set :variable:`CMAKE_UNITY_BUILD` from
+   the command line. However, it IS recommended to set
+   :prop_tgt:`UNITY_BUILD` to `OFF` if you need to ensure that a
+   target doesn't get a unity build.
+
+The batch size can be specified by setting
+:prop_tgt:`UNITY_BUILD_BATCH_SIZE`.
+
+The batching of source files is done by adding new sources files
+wich will `#include` the source files, and exclude them from
+building by setting :prop_sf:`HEADER_FILE_ONLY` to `ON`.
+
+
+ODR (One definition rule) errors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Since multiple source files are included into one source file,
+it can lead to ODR errors. This section contains properties
+which help fixing these errors.
+
+The source files marked by :prop_sf:`GENERATED` will be skipped
+from unity build. This applies also for the source files marked
+with :prop_sf:`SKIP_UNITY_BUILD_INCLUSION`.
+
+The source files that have :prop_sf:`COMPILE_OPTIONS`,
+:prop_sf:`COMPILE_DEFINITIONS`, :prop_sf:`COMPILE_FLAGS`, or
+:prop_sf:`INCLUDE_DIRECTORIES` will also be skipped.
+
+With the :prop_tgt:`UNITY_BUILD_CODE_BEFORE_INCLUDE` and
+:prop_tgt:`UNITY_BUILD_CODE_AFTER_INCLUDE` one can specify code
+to be injected in the unity source file before and after every
+`#include` statement.
+
+.. note ::
+
+   The order of source files defined in the `CMakeLists.txt` will
+   be preserved into the generated unity source files. This can
+   be used to manually enforce a specific grouping based on the
+   :prop_tgt:`UNITY_BUILD_BATCH_SIZE`.
diff --git a/Help/prop_tgt/UNITY_BUILD_BATCH_SIZE.rst b/Help/prop_tgt/UNITY_BUILD_BATCH_SIZE.rst
new file mode 100644
index 0000000..84047f2
--- /dev/null
+++ b/Help/prop_tgt/UNITY_BUILD_BATCH_SIZE.rst
@@ -0,0 +1,13 @@
+UNITY_BUILD_BATCH_SIZE
+----------------------
+
+Specifies how many source code files will be included into a
+:prop_tgt:`UNITY_BUILD` source file.
+
+If the property is not set, CMake will use the value provided
+by :variable:`CMAKE_UNITY_BUILD_BATCH_SIZE`.
+
+By setting it to value `0` the generated unity source file will
+contain all the source files that would otherwise be split
+into multiple batches. It is not recommended to do so, since it
+would affect performance.
diff --git a/Help/prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE.rst b/Help/prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE.rst
new file mode 100644
index 0000000..7795289
--- /dev/null
+++ b/Help/prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE.rst
@@ -0,0 +1,8 @@
+UNITY_BUILD_CODE_AFTER_INCLUDE
+------------------------------
+
+Code snippet which is included verbatim by the :prop_tgt:`UNITY_BUILD`
+feature just after the `#include` statement of the targeted source
+files.
+
+This could be something like `#undef NOMINMAX`.
diff --git a/Help/prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE.rst b/Help/prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE.rst
new file mode 100644
index 0000000..f335463
--- /dev/null
+++ b/Help/prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE.rst
@@ -0,0 +1,8 @@
+UNITY_BUILD_CODE_BEFORE_INCLUDE
+-------------------------------
+
+Code snippet which is included verbatim by the :prop_tgt:`UNITY_BUILD`
+feature just before the `#include` statement of the targeted source
+files.
+
+This could be something like `#define NOMINMAX`.
diff --git a/Help/prop_tgt/VS_CONFIGURATION_TYPE.rst b/Help/prop_tgt/VS_CONFIGURATION_TYPE.rst
index ff987ff..640bed5 100644
--- a/Help/prop_tgt/VS_CONFIGURATION_TYPE.rst
+++ b/Help/prop_tgt/VS_CONFIGURATION_TYPE.rst
@@ -4,6 +4,8 @@
 Visual Studio project configuration type.
 
 Sets the ``ConfigurationType`` attribute for a generated Visual Studio project.
+The property value may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
 If this property is set, it overrides the default setting that is based on the
 target type (e.g. ``StaticLibrary``, ``Application``, ...).
 
diff --git a/Help/prop_tgt/VS_DPI_AWARE.rst b/Help/prop_tgt/VS_DPI_AWARE.rst
new file mode 100644
index 0000000..82640cc
--- /dev/null
+++ b/Help/prop_tgt/VS_DPI_AWARE.rst
@@ -0,0 +1,14 @@
+VS_DPI_AWARE
+------------
+
+Set the Manifest Tool -> Input and Output -> DPI Awareness in the Visual Studio
+target project properties.
+
+Valid values are ``PerMonitor``, ``ON``, or ``OFF``.
+
+For example:
+
+.. code-block:: cmake
+
+  add_executable(myproject myproject.cpp)
+  set_property(TARGET myproject PROPERTY VS_DPI_AWARE "PerMonitor")
diff --git a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
index 0adb5db..0e182cf 100644
--- a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
+++ b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
@@ -31,9 +31,10 @@
 - :prop_tgt:`XCODE_SCHEME_ZOMBIE_OBJECTS`
 
 The following target properties will be applied on the
-"Info" and "Arguments" tab:
+"Info", "Arguments", and "Options" tab:
 
 - :prop_tgt:`XCODE_SCHEME_ARGUMENTS`
 - :prop_tgt:`XCODE_SCHEME_DEBUG_AS_ROOT`
+- :prop_tgt:`XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING`
 - :prop_tgt:`XCODE_SCHEME_ENVIRONMENT`
 - :prop_tgt:`XCODE_SCHEME_EXECUTABLE`
diff --git a/Help/prop_tgt/XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst b/Help/prop_tgt/XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst
new file mode 100644
index 0000000..9afeedd
--- /dev/null
+++ b/Help/prop_tgt/XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst
@@ -0,0 +1,13 @@
+XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
+--------------------------------------
+
+Whether to enable
+``Allow debugging when using document Versions Browser``
+in the Options section of the generated Xcode scheme.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING`
+if it is set when a target is created.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/release/3.16.rst b/Help/release/3.16.rst
new file mode 100644
index 0000000..a14effe
--- /dev/null
+++ b/Help/release/3.16.rst
@@ -0,0 +1,275 @@
+CMake 3.16 Release Notes
+************************
+
+.. only:: html
+
+  .. contents::
+
+Changes made since CMake 3.15 include the following.
+
+New Features
+============
+
+Languages
+---------
+
+* CMake learned to support the Objective C (``OBJC``) and Objective C++
+  (``OBJCXX``) languages.  They may be enabled via the :command:`project`
+  and :command:`enable_language` commands.  When ``OBJC`` or ``OBJCXX``
+  is enabled, source files with the ``.m`` or ``.mm``, respectively,
+  will be compiled as Objective C or C++.  Otherwise they will be treated
+  as plain C++ sources as they were before.
+
+Compilers
+---------
+
+* The ``Clang`` compiler is now supported on ``Solaris``.
+
+Platforms
+---------
+
+* On AIX, executables using the :prop_tgt:`ENABLE_EXPORTS` target property
+  now produce a linker import file with a ``.imp`` extension in addition
+  to the executable file.  Plugins (created via :command:`add_library` with
+  the ``MODULE`` option) that use :command:`target_link_libraries` to link
+  to the executable for its symbols are now linked using the import file.
+  The :command:`install(TARGETS)` command now installs the import file as
+  an ``ARCHIVE`` artifact.
+
+* On AIX, runtime linking is no longer enabled by default.  CMake provides
+  the linker enough information to resolve all symbols up front.
+  One may manually enable runtime linking for shared libraries and/or
+  loadable modules by adding ``-Wl,-G`` to their link flags
+  (e.g. in the :variable:`CMAKE_SHARED_LINKER_FLAGS` or
+  :variable:`CMAKE_MODULE_LINKER_FLAGS` variable).
+  One may manually enable runtime linking for executables by adding
+  ``-Wl,-brtl`` to their link flags (e.g. in the
+  :variable:`CMAKE_EXE_LINKER_FLAGS` variable).
+
+Command-Line
+------------
+
+* :manual:`cmake(1)` ``-E`` now supports ``true`` and ``false`` commands,
+  which do nothing while returning exit codes of 0 and 1, respectively.
+
+* :manual:`cmake(1)` gained a ``--trace-redirect=<file>`` command line
+  option that can be used to redirect ``--trace`` output to a file instead
+  of ``stderr``.
+
+* The :manual:`cmake(1)` ``--loglevel`` command line option has been
+  renamed to ``--log-level`` to make it consistent with the naming of other
+  command line options.  The ``--loglevel`` option is still supported to
+  preserve backward compatibility.
+
+Commands
+--------
+
+* The :command:`add_test` command learned the option ``COMMAND_EXPAND_LISTS``
+  which causes lists in the ``COMMAND`` argument to be expanded, including
+  lists created by generator expressions.
+
+* The :command:`file` command learned a new sub-command,
+  ``GET_RUNTIME_DEPENDENCIES``, which allows you to recursively get the list of
+  libraries linked by an executable or library. This sub-command is intended as
+  a replacement for :module:`GetPrerequisites`.
+
+* The :command:`find_file`, :command:`find_library`, :command:`find_path`,
+  :command:`find_package`, and :command:`find_program` commands have learned to
+  check the following variables to control searching
+
+  * :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH` - Controls the searching
+    the cmake-specific environment variables.
+
+  * :variable:`CMAKE_FIND_USE_CMAKE_PATH` - Controls the searching the
+    cmake-specific cache variables.
+
+  * :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH` - Controls the searching
+    cmake platform specific variables.
+
+  * :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` - Controls the searching of
+    :variable:`<PackageName>_ROOT` variables.
+
+  * :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` - Controls the searching
+    the standard system environment variables.
+
+* The :command:`find_package` command has learned to check the following
+  variables to control searching
+
+  * :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` - Controls the searching the
+    cmake user registry.
+
+* The :command:`message` command learned indentation control with the new
+  :variable:`CMAKE_MESSAGE_INDENT` variable.
+
+* The :command:`target_precompile_headers` command was added to specify
+  a list of headers to precompile for faster compilation times.
+
+Variables
+---------
+
+* The :variable:`CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS` variable has been
+  introduced to optionally initialize the
+  :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` target property.
+
+* The :variable:`CMAKE_ECLIPSE_RESOURCE_ENCODING` variable was added to
+  specify the resource encoding for the the :generator:`Eclipse CDT4` extra
+  generator.
+
+Properties
+----------
+
+* The :prop_tgt:`BUILD_RPATH` and :prop_tgt:`INSTALL_RPATH` target properties
+  now support :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+* The :prop_tgt:`INSTALL_REMOVE_ENVIRONMENT_RPATH` target property was
+  added to remove compiler-defined ``RPATH`` entries from a target.
+  This property is initialized by the
+  :variable:`CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH` variable.
+
+* The :prop_tgt:`PRECOMPILE_HEADERS` target property was added to specify
+  a list of headers to precompile for faster compilation times.
+  Set it using the :command:`target_precompile_headers` command.
+
+* The :prop_tgt:`UNITY_BUILD` target property was added to tell
+  generators to batch include source files for faster compilation
+  times.
+
+* The :prop_tgt:`VS_CONFIGURATION_TYPE` target property now supports
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+* The :prop_tgt:`VS_DPI_AWARE` target property was added to tell
+  :ref:`Visual Studio Generators` to set the ``EnableDpiAwareness``
+  property in ``.vcxproj`` files.
+
+* The :prop_tgt:`XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING` target property was
+  added to tell the :generator:`Xcode` generator to set the value of the
+  ``Allow debugging when using document Versions Browser`` schema option.
+
+Modules
+-------
+
+* The :module:`FindDoxygen` module :command:`doxygen_add_docs` command
+  gained a new ``USE_STAMP_FILE`` option.  When this option present,
+  the custom target created by the command will only re-run Doxygen if
+  any of the source files have changed since the last successful run.
+
+* The :module:`FindGnuTLS` module now provides an imported target.
+
+* The :module:`FindPackageHandleStandardArgs` module
+  :command:`find_package_handle_standard_args` command gained
+  a new ``REASON_FAILURE_MESSAGE`` option to specify a message
+  giving the reason for the failure.
+
+* The :module:`FindPkgConfig` module :command:`pkg_search_module` macro
+  now defines a ``<prefix>_MODULE_NAME`` result variable containing the
+  first matching module name.
+
+* The :module:`FindPython3` and :module:`FindPython` modules gained
+  options to control which ``ABIs`` will be searched.
+
+* The :module:`FindPython3`, :module:`FindPython2`, and :module:`FindPython`
+  modules now support direct specification of artifacts via cache entries.
+
+Autogen
+-------
+
+* When using :prop_tgt:`AUTOMOC`, CMake now generates the ``-p`` path prefix
+  option for ``moc``.  This ensures that ``moc`` output files are identical
+  on different build setups (given, that the headers compiled by ``moc`` are
+  in an :command:`include directory <target_include_directories>`).
+  Also it ensures that ``moc`` output files will compile correctly when the
+  source and/or build directory is a symbolic link.
+
+  The ``moc`` path prefix generation behavior can be configured by setting
+  the new :variable:`CMAKE_AUTOMOC_PATH_PREFIX` variable and/or
+  :prop_tgt:`AUTOMOC_PATH_PREFIX` target property.
+
+CTest
+-----
+
+* :manual:`ctest(1)` now has the ability to serialize tests based on hardware
+  requirements for each test. See :ref:`ctest-hardware-allocation` for
+  details.
+
+* A new test property, :prop_test:`SKIP_REGULAR_EXPRESSION`, has been added.
+  This property is similar to :prop_test:`FAIL_REGULAR_EXPRESSION` and
+  :prop_test:`PASS_REGULAR_EXPRESSION`, but with the same meaning as
+  :prop_test:`SKIP_RETURN_CODE`. This is useful, for example, in cases where
+  the user has no control over the return code of the test. For example, in
+  Catch2, the return value is the number of assertion failed, therefore it is
+  impossible to use it for :prop_test:`SKIP_RETURN_CODE`.
+
+CPack
+-----
+
+* CPack variable :variable:`CPACK_INSTALL_CMAKE_CONFIGURATIONS` was added to
+  control what configurations are to be packaged for multi-configuration
+  CMake generators.
+
+* The :cpack_gen:`CPack DEB Generator` is now able to format generic text
+  (usually used as the description for multiple CPack generators) according
+  to the `Debian Policy Manual`_.  See the
+  :variable:`CPACK_PACKAGE_DESCRIPTION_FILE` and
+  :variable:`CPACK_DEBIAN_<COMPONENT>_DESCRIPTION` variables.
+
+* The :cpack_gen:`CPack Archive Generator` learned to generate ``.tar.zst``
+  packages with Zstandard compression.
+
+.. _`Debian Policy Manual`: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description
+
+Deprecated and Removed Features
+===============================
+
+* An explicit deprecation diagnostic was added for policy ``CMP0067``
+  (``CMP0066`` and below were already deprecated).
+  The :manual:`cmake-policies(7)` manual explains that the OLD behaviors
+  of all policies are deprecated and that projects should port to the
+  NEW behaviors.
+
+* The :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY` variable has been
+  deprecated.  Use the :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` variable
+  instead.
+
+* The :module:`GetPrerequisites` module has been deprecated, as it has been
+  superceded by :command:`file(GET_RUNTIME_DEPENDENCIES)`.
+
+* The ``CPACK_INSTALL_SCRIPT`` variable has been deprecated in favor of the
+  new, more accurately named :variable:`CPACK_INSTALL_SCRIPTS` variable.
+
+Other Changes
+=============
+
+* The :manual:`cmake(1)` ``-C <initial-cache>`` option now evaluates the
+  initial cache script with :variable:`CMAKE_SOURCE_DIR` and
+  :variable:`CMAKE_BINARY_DIR` set to the top-level source and build trees.
+
+* The :manual:`cmake(1)` ``-E remove_directory`` command-line tool,
+  when given the path to a symlink to a directory, now removes just
+  the symlink.  It no longer removes content of the linked directory.
+
+* The :manual:`ctest(1)`  ``--build-makeprogram`` command-line option now
+  specifies the make program used when configuring a project with the
+  :generator:`Ninja` generator or the :ref:`Makefile Generators`.
+
+* The :module:`ExternalProject` module :command:`ExternalProject_Add` command
+  has been updated so that ``GIT_SUBMODULES ""`` initializes no submodules.
+  See policy :policy:`CMP0097`.
+
+* The :module:`FindGTest` module has been updated to recognize
+  MSVC build trees generated by GTest 1.8.1.
+
+* The :command:`project` command no longer strips leading zeros in version
+  components.  See policy :policy:`CMP0096`.
+
+* The Qt Compressed Help file is now named ``CMake.qch``, which no longer
+  contains the release version in the file name.  When CMake is upgraded
+  in-place, the name and location of this file will remain constant.
+  Tools such as IDEs, help viewers, etc. should now be able to refer to this
+  file at a fixed location that remains valid across CMake upgrades.
+
+* ``RPATH`` entries are properly escaped in the generated CMake scripts
+  used for installation.  See policy :policy:`CMP0095`.
+
+* When using :variable:`CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS` on Windows the
+  auto-generated exports are now updated only when the object files
+  providing the symbols are updated.
diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000..e4cc01e
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+  Developers should add similar notes for each topic branch
+  making a noteworthy change.  Each document should be named
+  and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/deprecate-policy-old.rst b/Help/release/dev/deprecate-policy-old.rst
new file mode 100644
index 0000000..401f4b2
--- /dev/null
+++ b/Help/release/dev/deprecate-policy-old.rst
@@ -0,0 +1,8 @@
+deprecate-policy-old
+--------------------
+
+* An explicit deprecation diagnostic was added for policy ``CMP0068``
+  and policy ``CMP0069`` (``CMP0067`` and below were already deprecated).
+  The :manual:`cmake-policies(7)` manual explains that the OLD behaviors
+  of all policies are deprecated and that projects should port to the
+  NEW behaviors.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index 35a47aa..a4585a5 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,12 +7,15 @@
   This file should include the adjacent "dev.txt" file
   in development versions but not in release versions.
 
+.. include:: dev.txt
+
 Releases
 ========
 
 .. toctree::
    :maxdepth: 1
 
+   3.16 <3.16>
    3.15 <3.15>
    3.14 <3.14>
    3.13 <3.13>
diff --git a/Help/variable/CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION.rst b/Help/variable/CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION.rst
index 5ae55d1..22808e3 100644
--- a/Help/variable/CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION.rst
+++ b/Help/variable/CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION.rst
@@ -3,7 +3,11 @@
 
 When :ref:`Cross Compiling for Android with the NDK`, this variable
 may be set to specify the version of the toolchain to be used
-as the compiler.  The variable must be set to one of these forms:
+as the compiler.
+
+On NDK r19 or above, this variable must be unset or set to ``clang``.
+
+On NDK r18 or below, this variable must be set to one of these forms:
 
 * ``<major>.<minor>``: GCC of specified version
 * ``clang<major>.<minor>``: Clang of specified version
diff --git a/Help/variable/CMAKE_AUTOMOC_PATH_PREFIX.rst b/Help/variable/CMAKE_AUTOMOC_PATH_PREFIX.rst
new file mode 100644
index 0000000..dca0b06
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOMOC_PATH_PREFIX.rst
@@ -0,0 +1,11 @@
+CMAKE_AUTOMOC_PATH_PREFIX
+-------------------------
+
+Whether to generate the ``-p`` path prefix option for ``moc`` on
+:prop_tgt:`AUTOMOC` enabled Qt targets.
+
+This variable is used to initialize the :prop_tgt:`AUTOMOC_PATH_PREFIX`
+property on all the targets.  See that target property for additional
+information.
+
+The default value is ``ON``.
diff --git a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
index eea2c4f..6d34c5c 100644
--- a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
+++ b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
@@ -4,4 +4,5 @@
 Executable to use when compiling host code when compiling ``CUDA`` language
 files. Maps to the ``nvcc -ccbin`` option.  Will only be used by CMake on the first
 configuration to determine a valid host compiler for ``CUDA``. After a valid
-host compiler has been found, this value is read-only.
+host compiler has been found, this value is read-only.  This variable takes
+priority over the :envvar:`CUDAHOSTCXX` environment variable.
diff --git a/Help/variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS.rst b/Help/variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS.rst
new file mode 100644
index 0000000..fc835cd
--- /dev/null
+++ b/Help/variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS.rst
@@ -0,0 +1,6 @@
+CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
+---------------------------------
+
+Default value for :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` target
+property. This variable is used to initialize the property on each target as
+it is created.
diff --git a/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst b/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..7c30ede
--- /dev/null
+++ b/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,6 @@
+CMAKE_DISABLE_PRECOMPILE_HEADERS
+--------------------------------
+
+Default value for :prop_tgt:`DISABLE_PRECOMPILE_HEADERS` of targets.
+
+By default ``CMAKE_DISABLE_PRECOMPILE_HEADERS`` is ``OFF``.
diff --git a/Help/variable/CMAKE_ECLIPSE_RESOURCE_ENCODING.rst b/Help/variable/CMAKE_ECLIPSE_RESOURCE_ENCODING.rst
new file mode 100644
index 0000000..314efe5
--- /dev/null
+++ b/Help/variable/CMAKE_ECLIPSE_RESOURCE_ENCODING.rst
@@ -0,0 +1,6 @@
+CMAKE_ECLIPSE_RESOURCE_ENCODING
+-------------------------------
+
+This cache variable tells the :generator:`Eclipse CDT4` project generator
+to set the resource encoding to the given value in generated project files.
+If no value is given, no encoding will be set.
diff --git a/Help/variable/CMAKE_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_ENABLE_EXPORTS.rst
index 7ec4d63..8848da1 100644
--- a/Help/variable/CMAKE_ENABLE_EXPORTS.rst
+++ b/Help/variable/CMAKE_ENABLE_EXPORTS.rst
@@ -1,22 +1,8 @@
 CMAKE_ENABLE_EXPORTS
 --------------------
 
-Specify whether an executable exports symbols for loadable modules.
+Specify whether executables export symbols for loadable modules.
 
-Normally an executable does not export any symbols because it is the
-final program.  It is possible for an executable to export symbols to
-be used by loadable modules.  When this property is set to true CMake
-will allow other targets to ``link`` to the executable with the
-:command:`TARGET_LINK_LIBRARIES` command.  On all platforms a target-level
-dependency on the executable is created for targets that link to it.
-For DLL platforms an import library will be created for the exported
-symbols and then used for linking.  All Windows-based systems
-including Cygwin are DLL platforms.  For non-DLL platforms that
-require all symbols to be resolved at link time, such as macOS, the
-module will ``link`` to the executable using a flag like
-``-bundle_loader``.  For other non-DLL platforms the link rule is simply
-ignored since the dynamic loader will automatically bind symbols when
-the module is loaded.
-
-This variable is used to initialize the target property
-:prop_tgt:`ENABLE_EXPORTS` for executable targets.
+This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
+property for executable targets when they are created by calls to the
+:command:`add_executable` command.  See the property documentation for details.
diff --git a/Help/variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY.rst b/Help/variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY.rst
index 9058471..ffb8a2c 100644
--- a/Help/variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY.rst
+++ b/Help/variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY.rst
@@ -1,12 +1,23 @@
 CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
 --------------------------------------
 
-Skip :ref:`User Package Registry` in :command:`find_package` calls.
+.. deprecated:: 3.16
+
+  Use the :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` variable instead.
+
+By default this variable is not set. If neither
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` nor
+``CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`` is set, then
+:command:`find_package()` will use the `User Package Registry` unless the
+`NO_CMAKE_PACKAGE_REGISTRY` option is provided.
+
+``CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`` is ignored if
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` is set.
 
 In some cases, for example to locate only system wide installations, it
 is not desirable to use the :ref:`User Package Registry` when searching
 for packages. If the :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`
-variable is enabled, all the :command:`find_package` commands will skip
+variable is ``TRUE``, all the :command:`find_package` commands will skip
 the :ref:`User Package Registry` as if they were called with the
 ``NO_CMAKE_PACKAGE_REGISTRY`` argument.
 
diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst
new file mode 100644
index 0000000..2db5081
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst
@@ -0,0 +1,18 @@
+CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH
+-------------------------------------
+
+Controls the searching the cmake-specific environment variables by the
+:command:`find_program`, :command:`find_library`, :command:`find_file`,
+:command:`find_path`, and command:`find_package` commands.
+This is useful in cross-compiling environments.
+
+By default this variable is not set, which is equivalent to it having
+a value of ``TRUE``.  Explicit options given to the :command:`find_program`,
+:command:`find_library`, :command:`find_file`, and :command:`find_path`
+commands take precedence over this variable.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`,
+:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` variables.
diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst
new file mode 100644
index 0000000..4ca7ad1
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst
@@ -0,0 +1,18 @@
+CMAKE_FIND_USE_CMAKE_PATH
+-------------------------
+
+Controls the searching the cmake-specific cache variables by the
+:command:`find_program`, :command:`find_library`, :command:`find_file`,
+:command:`find_path`, and command:`find_package` commands.
+This is useful in cross-compiling environments.
+
+By default this variable is not set, which is equivalent to it having
+a value of ``TRUE``.  Explicit options given to the :command:`find_program`,
+:command:`find_library`, :command:`find_file`, and :command:`find_path`
+commands take precedence over this variable.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`,
+:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` variables.
diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst
new file mode 100644
index 0000000..d3259ae
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst
@@ -0,0 +1,18 @@
+CMAKE_FIND_USE_CMAKE_SYSTEM_PATH
+--------------------------------
+
+Controls the searching cmake platform specific variables by the
+:command:`find_program`, :command:`find_library`, :command:`find_file`,
+:command:`find_path`, and command:`find_package` commands.
+This is useful in cross-compiling environments.
+
+By default this variable is not set, which is equivalent to it having
+a value of ``TRUE``.  Explicit options given to the :command:`find_program`,
+:command:`find_library`, :command:`find_file`, and :command:`find_path`
+commands take precedence over this variable.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` variables.
diff --git a/Help/variable/CMAKE_FIND_USE_PACKAGE_REGISTRY.rst b/Help/variable/CMAKE_FIND_USE_PACKAGE_REGISTRY.rst
new file mode 100644
index 0000000..75e910f
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_PACKAGE_REGISTRY.rst
@@ -0,0 +1,29 @@
+CMAKE_FIND_USE_PACKAGE_REGISTRY
+-------------------------------
+
+Controls the searching the :ref:`User Package Registry` by the :command:`find_package`
+command.
+
+By default this variable is not set and the behavior will fall back
+to that determined by the deprecated :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`
+variable.  If that is also not set, then `find_package()` will use the
+`User Package Registry` unless the `NO_CMAKE_PACKAGE_REGISTRY` option
+is provided.
+
+This variable takes precedence over :variable:`CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY`
+when both are set.
+
+In some cases, for example to locate only system wide installations, it
+is not desirable to use the :ref:`User Package Registry` when searching
+for packages. If the :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`
+variable is ``FALSE``, all the :command:`find_package` commands will skip
+the :ref:`User Package Registry` as if they were called with the
+``NO_CMAKE_PACKAGE_REGISTRY`` argument.
+
+See also :ref:`Disabling the Package Registry`.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`,
+:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` variables.
diff --git a/Help/variable/CMAKE_FIND_USE_PACKAGE_ROOT_PATH.rst b/Help/variable/CMAKE_FIND_USE_PACKAGE_ROOT_PATH.rst
new file mode 100644
index 0000000..e17fdcc
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_PACKAGE_ROOT_PATH.rst
@@ -0,0 +1,18 @@
+CMAKE_FIND_USE_PACKAGE_ROOT_PATH
+--------------------------------
+
+Controls the searching of :variable:`<PackageName>_ROOT` variables by the
+:command:`find_program`, :command:`find_library`, :command:`find_file`,
+:command:`find_path`, and command:`find_package` commands.
+This is useful in cross-compiling environments.
+
+By default this variable is not set, which is equivalent to it having
+a value of ``TRUE``.  Explicit options given to the :command:`find_program`,
+:command:`find_library`, :command:`find_file`, and :command:`find_path`
+commands take precedence over this variable.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`,
+:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY` variables.
diff --git a/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst b/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst
new file mode 100644
index 0000000..71432f6
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst
@@ -0,0 +1,18 @@
+CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH
+--------------------------------------
+
+Controls the searching the standard system environment variables by the
+:command:`find_program`, :command:`find_library`, :command:`find_file`,
+:command:`find_path`, and command:`find_package` commands.
+This is useful in cross-compiling environments.
+
+By default this variable is not set, which is equivalent to it having
+a value of ``TRUE``.  Explicit options given to the :command:`find_program`,
+:command:`find_library`, :command:`find_file`, and :command:`find_path`
+commands take precedence over this variable.
+
+See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`,
+:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`,
+:variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`,
+and :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` variables.
diff --git a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
index a01a8b7..222824f 100644
--- a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
+++ b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
@@ -40,10 +40,13 @@
 specify generator-specific details of the toolset selection.
 Supported pairs are:
 
-``cuda=<version>``
-  Specify the CUDA toolkit version to use.  Supported by VS 2010
-  and above with the CUDA toolkit VS integration installed.
-  See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA` variable.
+``cuda=<version>|<path>``
+  Specify the CUDA toolkit version to use or the path to a
+  standalone CUDA toolkit directory.  Supported by VS 2010
+  and above. The version can only be used with the CUDA
+  toolkit VS integration globally installed.
+  See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA` and
+  :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR` variables.
 
 ``host=<arch>``
   Specify the host tools architecture as ``x64`` or ``x86``.
diff --git a/Help/variable/CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH.rst b/Help/variable/CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH.rst
new file mode 100644
index 0000000..19ae5f3
--- /dev/null
+++ b/Help/variable/CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH.rst
@@ -0,0 +1,9 @@
+CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH
+--------------------------------------
+
+Removes compiler defined rpaths durimg installation.
+
+``CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH`` is a boolean that if set to ``true``
+removes compiler defined rpaths from the project if the user also defines rpath
+with :prop_tgt:`INSTALL_RPATH`. This is used to initialize the target property
+:prop_tgt:`INSTALL_REMOVE_ENVIRONMENT_RPATH` for all targets.
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG.rst
new file mode 100644
index 0000000..d54f080
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG.rst
@@ -0,0 +1,8 @@
+CMAKE_<LANG>_LINK_LIBRARY_FILE_FLAG
+-----------------------------------
+
+Language-specific flag to be used to link a library specified by
+a path to its file.
+
+The flag will be used before a library file path is given to the
+linker.  This is needed only on very few platforms.
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_FLAG.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FLAG.rst
new file mode 100644
index 0000000..d7bb0d8
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FLAG.rst
@@ -0,0 +1,7 @@
+CMAKE_<LANG>_LINK_LIBRARY_FLAG
+------------------------------
+
+Flag to be used to link a library into a shared library or executable.
+
+This flag will be used to specify a library to link to a shared library or an
+executable for the specific language.  On most compilers this is ``-l``.
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX.rst
new file mode 100644
index 0000000..a378657
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX.rst
@@ -0,0 +1,6 @@
+CMAKE_<LANG>_LINK_LIBRARY_SUFFIX
+--------------------------------
+
+Language-specific suffix for libraries that you link to.
+
+The suffix to use for the end of a library filename, ``.lib`` on Windows.
diff --git a/Help/variable/CMAKE_MESSAGE_INDENT.rst b/Help/variable/CMAKE_MESSAGE_INDENT.rst
new file mode 100644
index 0000000..7e44a4c
--- /dev/null
+++ b/Help/variable/CMAKE_MESSAGE_INDENT.rst
@@ -0,0 +1,32 @@
+CMAKE_MESSAGE_INDENT
+--------------------
+
+The :command:`message` command joins the strings from this list and for
+log levels of ``NOTICE`` and below, it prepends the resultant string to
+each line of the message.
+
+Example:
+
+.. code-block:: cmake
+
+  list(APPEND listVar one two three)
+
+  message(VERBOSE [[Collected items in the "listVar":]])
+  list(APPEND CMAKE_MESSAGE_INDENT "  ")
+
+  foreach(item IN LISTS listVar)
+    message(VERBOSE ${item})
+  endforeach()
+
+  list(POP_BACK CMAKE_MESSAGE_INDENT)
+  message(VERBOSE "No more indent")
+
+Which results in the following output:
+
+.. code-block:: none
+
+  -- Collected items in the "listVar":
+  --   one
+  --   two
+  --   three
+  -- No more indent
diff --git a/Help/variable/CMAKE_UNITY_BUILD.rst b/Help/variable/CMAKE_UNITY_BUILD.rst
new file mode 100644
index 0000000..3096954
--- /dev/null
+++ b/Help/variable/CMAKE_UNITY_BUILD.rst
@@ -0,0 +1,6 @@
+CMAKE_UNITY_BUILD
+-----------------
+
+Default value for :prop_tgt:`UNITY_BUILD` of targets.
+
+By default ``CMAKE_UNITY_BUILD`` is ``OFF``.
diff --git a/Help/variable/CMAKE_UNITY_BUILD_BATCH_SIZE.rst b/Help/variable/CMAKE_UNITY_BUILD_BATCH_SIZE.rst
new file mode 100644
index 0000000..3ab2344
--- /dev/null
+++ b/Help/variable/CMAKE_UNITY_BUILD_BATCH_SIZE.rst
@@ -0,0 +1,6 @@
+CMAKE_UNITY_BUILD_BATCH_SIZE
+----------------------------
+
+Default value for :prop_tgt:`UNITY_BUILD_BATCH_SIZE` of targets.
+
+By default ``CMAKE_UNITY_BUILD_BATCH_SIZE`` is set to ``8``.
diff --git a/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA.rst b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA.rst
index 1604a76..67b7f74 100644
--- a/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA.rst
+++ b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA.rst
@@ -6,7 +6,9 @@
 The :ref:`Visual Studio Generators` for VS 2010 and above support using
 a CUDA toolset provided by a CUDA Toolkit.  The toolset version number
 may be specified by a field in :variable:`CMAKE_GENERATOR_TOOLSET` of
-the form ``cuda=8.0``.  If none is specified CMake will choose a default
-version.  CMake provides the selected CUDA toolset version in this variable.
+the form ``cuda=8.0``. Or it is automatically detected if a path to
+a standalone CUDA directory is specified in the form ``cuda=C:\path\to\cuda``.
+If none is specified CMake will choose a default version.
+CMake provides the selected CUDA toolset version in this variable.
 The value may be empty if no CUDA Toolkit with Visual Studio integration
 is installed.
diff --git a/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR.rst b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR.rst
new file mode 100644
index 0000000..060648a
--- /dev/null
+++ b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR.rst
@@ -0,0 +1,16 @@
+CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR
+-----------------------------------------
+
+Path to standalone NVIDIA CUDA Toolkit (eg. extracted from installer).
+
+The :ref:`Visual Studio Generators` for VS 2010 and above support using
+a standalone (non-installed) NVIDIA CUDA toolkit.  The path
+may be specified by a field in :variable:`CMAKE_GENERATOR_TOOLSET` of
+the form ``cuda=C:\path\to\cuda``.  The given directory must at least
+contain a folder ``.\nvcc`` and must provide Visual Studio integration
+files in path ``.\CUDAVisualStudioIntegration\extras\
+visual_studio_integration\MSBuildExtensions\``. One can create a standalone
+CUDA toolkit directory by either opening a installer with 7zip or
+copying the files that are extracted by the running installer.
+The value may be empty if no path to a standalone CUDA Toolkit was
+specified.
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst b/Help/variable/CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst
new file mode 100644
index 0000000..a264d36
--- /dev/null
+++ b/Help/variable/CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING.rst
@@ -0,0 +1,13 @@
+CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
+--------------------------------------------
+
+Whether to enable
+``Allow debugging when using document Versions Browser``
+in the Options section of the generated Xcode scheme.
+
+This variable initializes the
+:prop_tgt:`XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING`
+property on all targets.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/variable/CPACK_INSTALL_SCRIPT.rst b/Help/variable/CPACK_INSTALL_SCRIPT.rst
deleted file mode 100644
index 12a48a4..0000000
--- a/Help/variable/CPACK_INSTALL_SCRIPT.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-CPACK_INSTALL_SCRIPT
---------------------
-
-Extra CMake script provided by the user.
-
-If set this CMake script will be executed by CPack during its local
-[CPack-private] installation which is done right before packaging the
-files.  The script is not called by e.g.: ``make install``.
diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake
index f94fc5c..2f3b9e1 100644
--- a/Modules/BundleUtilities.cmake
+++ b/Modules/BundleUtilities.cmake
@@ -243,11 +243,13 @@
   endif()
 endif()
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0057 NEW) # if IN_LIST
+
 # The functions defined in this file depend on the get_prerequisites function
 # (and possibly others) found in:
 #
-get_filename_component(BundleUtilities_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
-include("${BundleUtilities_cmake_dir}/GetPrerequisites.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/GetPrerequisites.cmake")
 
 
 function(get_bundle_main_executable bundle result_var)
@@ -280,7 +282,7 @@
       endif()
     endforeach()
 
-    if(NOT "${bundle_executable}" STREQUAL "")
+    if(NOT bundle_executable STREQUAL "")
       if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}")
         set(result "${bundle}/Contents/MacOS/${bundle_executable}")
       else()
@@ -600,17 +602,9 @@
       set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
 
       set(prereqs "")
-      set(ignoreFile FALSE)
       get_filename_component(prereq_filename ${lib} NAME)
-      if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" )
-        foreach(item ${CFG_IGNORE_ITEM})
-            if("${item}" STREQUAL "${prereq_filename}")
-              set(ignoreFile TRUE)
-            endif()
-        endforeach()
-      endif()
 
-      if(NOT ignoreFile)
+      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
         get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}" "${main_rpaths}")
         foreach(pr ${prereqs})
           set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1 "${main_rpaths}")
@@ -627,7 +621,7 @@
     foreach(exe ${exes})
       # Main executable is scanned first above:
       #
-      if(NOT "${exe}" STREQUAL "${executable}")
+      if(NOT exe STREQUAL executable)
         # Add the exe itself to the keys:
         #
         set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
@@ -643,17 +637,9 @@
       # Add each prerequisite to the keys:
       #
       set(prereqs "")
-      set(ignoreFile FALSE)
       get_filename_component(prereq_filename ${exe} NAME)
-      if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" )
-        foreach(item ${CFG_IGNORE_ITEM})
-            if("${item}" STREQUAL "${prereq_filename}")
-              set(ignoreFile TRUE)
-            endif()
-        endforeach()
-      endif()
 
-      if(NOT ignoreFile)
+      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
         get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}" "${exe_rpaths}")
         foreach(pr ${prereqs})
           set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1 "${exe_rpaths}")
@@ -665,7 +651,7 @@
 
     # preserve library symlink structure
     foreach(key ${${keys_var}})
-      if("${${key}_COPYFLAG}" STREQUAL 1)
+      if("${${key}_COPYFLAG}" STREQUAL "1")
         if(IS_SYMLINK "${${key}_RESOLVED_ITEM}")
           get_filename_component(target "${${key}_RESOLVED_ITEM}" REALPATH)
           set_bundle_key_values(${keys_var} "${exe}" "${target}" "${exepath}" "${dirs}" 1 "${exe_rpaths}")
@@ -682,7 +668,7 @@
           get_filename_component(resolved_item_compare "${resolved_item_compare}" NAME)
           get_filename_component(resolved_embedded_item_compare "${resolved_embedded_item_compare}" NAME)
 
-          if(NOT "${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+          if(NOT resolved_item_compare STREQUAL resolved_embedded_item_compare)
             set(${key}_COPYFLAG "2")
             set(${key}_RESOLVED_ITEM "${${targetkey}_RESOLVED_EMBEDDED_ITEM}")
           endif()
@@ -716,7 +702,7 @@
     set(resolved_embedded_item_compare "${resolved_embedded_item}")
   endif()
 
-  if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
     message(STATUS "warning: resolved_item == resolved_embedded_item - not linking...")
   else()
     get_filename_component(target_dir "${resolved_embedded_item}" DIRECTORY)
@@ -738,7 +724,7 @@
     set(resolved_embedded_item_compare "${resolved_embedded_item}")
   endif()
 
-  if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
     message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
   else()
     #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
@@ -761,7 +747,7 @@
     set(resolved_embedded_item_compare "${resolved_embedded_item}")
   endif()
 
-  if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+  if(resolved_item_compare STREQUAL resolved_embedded_item_compare)
     message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
   else()
     if(BU_COPY_FULL_FRAMEWORK_CONTENTS)
@@ -841,12 +827,12 @@
   string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
   set(path_too_short 0)
   set(is_embedded 0)
-  if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length})
+  if(resolved_embedded_item_length LESS exe_dotapp_dir_length)
     set(path_too_short 1)
   endif()
   if(NOT path_too_short)
     string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
-    if("${exe_dotapp_dir}/" STREQUAL "${item_substring}")
+    if("${exe_dotapp_dir}/" STREQUAL item_substring)
       set(is_embedded 1)
     endif()
   endif()
@@ -1032,18 +1018,9 @@
       message(STATUS "executable file ${count}: ${f}")
 
       set(prereqs "")
-      set(ignoreFile FALSE)
       get_filename_component(prereq_filename ${f} NAME)
 
-      if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" )
-        foreach(item ${CFG_IGNORE_ITEM})
-            if("${item}" STREQUAL "${prereq_filename}")
-              set(ignoreFile TRUE)
-            endif()
-        endforeach()
-      endif()
-
-      if(NOT ignoreFile)
+      if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM)
         get_item_rpaths(${f} _main_exe_rpaths)
         get_prerequisites("${f}" prereqs 1 1 "${exepath}" "${_main_exe_rpaths}")
 
@@ -1063,11 +1040,11 @@
           gp_file_type("${f}" "${p}" p_type)
 
           if(APPLE)
-            if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
+            if(NOT p_type STREQUAL "embedded" AND NOT p_type STREQUAL "system")
               set(external_prereqs ${external_prereqs} "${p}")
             endif()
           else()
-            if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system")
+            if(NOT p_type STREQUAL "local" AND NOT p_type STREQUAL "system")
               set(external_prereqs ${external_prereqs} "${p}")
             endif()
           endif()
@@ -1142,3 +1119,5 @@
     message(FATAL_ERROR "error: verify_app failed")
   endif()
 endfunction()
+
+cmake_policy(POP)
diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake
index 43ae989..b0d80d1 100644
--- a/Modules/CMakeCUDAInformation.cmake
+++ b/Modules/CMakeCUDAInformation.cmake
@@ -171,7 +171,8 @@
     "<CMAKE_CUDA_HOST_LINK_LAUNCHER> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>${__IMPLICT_LINKS}")
 endif()
 
-if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0")
+if( CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND
+    CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0")
   set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "-Wno-deprecated-gpu-targets")
 else()
   set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "")
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index e7f0e70..ef65021 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -42,8 +42,17 @@
   set(MINGW 1)
 endif()
 set(CMAKE_CXX_COMPILER_ID_RUN 1)
+set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;CPP)
 set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
-set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP)
+
+foreach (lang OBJC OBJCXX)
+  if (CMAKE_${lang}_COMPILER_ID_RUN)
+    foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS)
+      list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension})
+    endforeach()
+  endif()
+endforeach()
+
 set(CMAKE_CXX_LINKER_PREFERENCE 30)
 set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1)
 
diff --git a/Modules/CMakeCXXCompilerId.cpp.in b/Modules/CMakeCXXCompilerId.cpp.in
index 34639b4..a743ce7 100644
--- a/Modules/CMakeCXXCompilerId.cpp.in
+++ b/Modules/CMakeCXXCompilerId.cpp.in
@@ -27,10 +27,20 @@
 @CMAKE_CXX_COMPILER_ID_PLATFORM_CONTENT@
 @CMAKE_CXX_COMPILER_ID_ERROR_FOR_TEST@
 
-#if defined(_MSC_VER) && defined(_MSVC_LANG)
-#define CXX_STD _MSVC_LANG
+#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L
+#  if defined(__INTEL_CXX11_MODE__)
+#    if defined(__cpp_aggregate_nsdmi)
+#      define CXX_STD 201402L
+#    else
+#      define CXX_STD 201103L
+#    endif
+#  else
+#    define CXX_STD 199711L
+#  endif
+#elif defined(_MSC_VER) && defined(_MSVC_LANG)
+#  define CXX_STD _MSVC_LANG
 #else
-#define CXX_STD __cplusplus
+#  define CXX_STD __cplusplus
 #endif
 
 const char* info_language_dialect_default = "INFO" ":" "dialect_default["
diff --git a/Modules/CMakeDependentOption.cmake b/Modules/CMakeDependentOption.cmake
index 6046d85..99d5070 100644
--- a/Modules/CMakeDependentOption.cmake
+++ b/Modules/CMakeDependentOption.cmake
@@ -12,7 +12,7 @@
 is used, but any value set by the user is preserved for when the
 option is presented again.  Example invocation:
 
-::
+.. code-block:: cmake
 
   CMAKE_DEPENDENT_OPTION(USE_FOO "Use Foo" ON
                          "USE_BAR;NOT USE_ZOT" OFF)
@@ -21,7 +21,8 @@
 called USE_FOO that defaults to ON.  Otherwise, it sets USE_FOO to
 OFF.  If the status of USE_BAR or USE_ZOT ever changes, any value for
 the USE_FOO option is saved so that when the option is re-enabled it
-retains its old value.
+retains its old value.  Each element in the fourth parameter is
+evaluated as an if-condition, so :ref:`Condition Syntax` can be used.
 #]=======================================================================]
 
 macro(CMAKE_DEPENDENT_OPTION option doc default depends force)
diff --git a/Modules/CMakeDetermineCSharpCompiler.cmake b/Modules/CMakeDetermineCSharpCompiler.cmake
index dab9414..da860a8 100644
--- a/Modules/CMakeDetermineCSharpCompiler.cmake
+++ b/Modules/CMakeDetermineCSharpCompiler.cmake
@@ -18,7 +18,6 @@
   set(CMAKE_CSharp_COMPILER_ID_RUN 1)
 
   # Try to identify the compiler.
-  set(CMAKE_CSharp_COMPILER_ID_STRINGS_PARAMETERS ENCODING UTF-16LE)
   set(CMAKE_CSharp_COMPILER_ID)
   include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
   CMAKE_DETERMINE_COMPILER_ID(CSharp CSFLAGS CMakeCSharpCompilerId.cs)
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 02bc14b..908e530 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -284,7 +284,13 @@
           set(id_cl icl.exe)
         endif()
         if(CMAKE_VS_PLATFORM_TOOLSET_VERSION)
-          set(id_toolset_version_props "<Import Project=\"${CMAKE_GENERATOR_INSTANCE}\\VC\\Auxiliary\\Build\\${CMAKE_VS_PLATFORM_TOOLSET_VERSION}\\Microsoft.VCToolsVersion.${CMAKE_VS_PLATFORM_TOOLSET_VERSION}.props\" />")
+          if(CMAKE_VS_PLATFORM_TOOLSET_VERSION VERSION_GREATER_EQUAL "14.20")
+            set(id_sep ".")
+          else()
+            set(id_sep "\\")
+          endif()
+          set(id_toolset_version_props "<Import Project=\"${CMAKE_GENERATOR_INSTANCE}\\VC\\Auxiliary\\Build${id_sep}${CMAKE_VS_PLATFORM_TOOLSET_VERSION}\\Microsoft.VCToolsVersion.${CMAKE_VS_PLATFORM_TOOLSET_VERSION}.props\" />")
+          unset(id_sep)
         endif()
       endif()
     else()
@@ -347,8 +353,14 @@
       set(cuda_tools "CUDA ${CMAKE_VS_PLATFORM_TOOLSET_CUDA}")
       set(id_compile "CudaCompile")
       set(id_PostBuildEvent_Command [[echo CMAKE_CUDA_COMPILER=$(CudaToolkitBinDir)\nvcc.exe]])
-      string(CONCAT id_Import_props [[<Import Project="$(VCTargetsPath)\BuildCustomizations\]] "${cuda_tools}" [[.props" />]])
-      string(CONCAT id_Import_targets [[<Import Project="$(VCTargetsPath)\BuildCustomizations\]] "${cuda_tools}" [[.targets" />]])
+      if(CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR)
+        set(id_CudaToolkitCustomDir "<CudaToolkitCustomDir>${CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR}nvcc</CudaToolkitCustomDir>")
+        string(CONCAT id_Import_props "<Import Project=\"${CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR}\\CUDAVisualStudioIntegration\\extras\\visual_studio_integration\\MSBuildExtensions\\${cuda_tools}.props\" />")
+        string(CONCAT id_Import_targets "<Import Project=\"${CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR}\\CUDAVisualStudioIntegration\\extras\\visual_studio_integration\\MSBuildExtensions\\${cuda_tools}.targets\" />")
+      else()
+        string(CONCAT id_Import_props [[<Import Project="$(VCTargetsPath)\BuildCustomizations\]] "${cuda_tools}" [[.props" />]])
+        string(CONCAT id_Import_targets [[<Import Project="$(VCTargetsPath)\BuildCustomizations\]] "${cuda_tools}" [[.targets" />]])
+      endif()
       if(CMAKE_VS_PLATFORM_NAME STREQUAL x64)
         set(id_ItemDefinitionGroup_entry "<CudaCompile><TargetMachinePlatform>64</TargetMachinePlatform></CudaCompile>")
       endif()
@@ -640,10 +652,14 @@
     set(ARCHITECTURE_ID)
     set(SIMULATE_ID)
     set(SIMULATE_VERSION)
-    file(STRINGS ${file}
-      CMAKE_${lang}_COMPILER_ID_STRINGS LIMIT_COUNT 38
-      ${CMAKE_${lang}_COMPILER_ID_STRINGS_PARAMETERS}
-      REGEX ".?I.?N.?F.?O.?:.?[A-Za-z0-9_]+\\[[^]]*\\]")
+    foreach(encoding "" "ENCODING;UTF-16LE" "ENCODING;UTF-16BE")
+      file(STRINGS "${file}" CMAKE_${lang}_COMPILER_ID_STRINGS
+        LIMIT_COUNT 38 ${encoding}
+        REGEX ".?I.?N.?F.?O.?:.?[A-Za-z0-9_]+\\[[^]]*\\]")
+      if(NOT CMAKE_${lang}_COMPILER_ID_STRINGS STREQUAL "")
+        break()
+      endif()
+    endforeach()
     set(COMPILER_ID_TWICE)
     # With the IAR Compiler, some strings are found twice, first time as incomplete
     # list like "?<Constant "INFO:compiler[IAR]">".  Remove the incomplete copies.
diff --git a/Modules/CMakeDetermineOBJCCompiler.cmake b/Modules/CMakeDetermineOBJCCompiler.cmake
new file mode 100644
index 0000000..ad13eab
--- /dev/null
+++ b/Modules/CMakeDetermineOBJCCompiler.cmake
@@ -0,0 +1,189 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# determine the compiler to use for Objective-C programs
+# NOTE, a generator may set CMAKE_OBJC_COMPILER before
+# loading this file to force a compiler.
+# use environment variable OBJC first if defined by user, next use
+# the cmake variable CMAKE_GENERATOR_OBJC which can be defined by a generator
+# as a default compiler
+#
+# Sets the following variables:
+#   CMAKE_OBJC_COMPILER
+#   CMAKE_AR
+#   CMAKE_RANLIB
+#   CMAKE_COMPILER_IS_GNUOBJC
+#   CMAKE_COMPILER_IS_CLANGOBJC
+#
+# If not already set before, it also sets
+#   _CMAKE_TOOLCHAIN_PREFIX
+
+include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
+
+# Load system-specific compiler preferences for this language.
+include(Platform/${CMAKE_SYSTEM_NAME}-Determine-OBJC OPTIONAL)
+include(Platform/${CMAKE_SYSTEM_NAME}-OBJC OPTIONAL)
+if(NOT CMAKE_OBJC_COMPILER_NAMES)
+  set(CMAKE_OBJC_COMPILER_NAMES clang)
+endif()
+
+if("${CMAKE_GENERATOR}" MATCHES "Xcode")
+  set(CMAKE_OBJC_COMPILER_XCODE_TYPE sourcecode.c.objc)
+else()
+  if(NOT CMAKE_OBJC_COMPILER)
+    set(CMAKE_OBJC_COMPILER_INIT NOTFOUND)
+
+    # prefer the environment variable OBJC
+    if($ENV{OBJC} MATCHES ".+")
+      get_filename_component(CMAKE_OBJC_COMPILER_INIT $ENV{OBJC} PROGRAM PROGRAM_ARGS CMAKE_OBJC_FLAGS_ENV_INIT)
+      if(CMAKE_OBJC_FLAGS_ENV_INIT)
+        set(CMAKE_OBJC_COMPILER_ARG1 "${CMAKE_OBJC_FLAGS_ENV_INIT}" CACHE STRING "First argument to Objective-C compiler")
+      endif()
+      if(NOT EXISTS ${CMAKE_OBJC_COMPILER_INIT})
+        message(FATAL_ERROR "Could not find compiler set in environment variable OBJC:\n$ENV{OBJC}.")
+      endif()
+    endif()
+
+    # next try prefer the compiler specified by the generator
+    if(CMAKE_GENERATOR_OBJC)
+      if(NOT CMAKE_OBJC_COMPILER_INIT)
+        set(CMAKE_OBJC_COMPILER_INIT ${CMAKE_GENERATOR_OBJC})
+      endif()
+    endif()
+
+    # finally list compilers to try
+    if(NOT CMAKE_OBJC_COMPILER_INIT)
+      set(CMAKE_OBJC_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}cc ${_CMAKE_TOOLCHAIN_PREFIX}gcc clang)
+    endif()
+
+    _cmake_find_compiler(OBJC)
+
+  else()
+    # we only get here if CMAKE_OBJC_COMPILER was specified using -D or a pre-made CMakeCache.txt
+    # (e.g. via ctest) or set in CMAKE_TOOLCHAIN_FILE
+    # if CMAKE_OBJC_COMPILER is a list of length 2, use the first item as
+    # CMAKE_OBJC_COMPILER and the 2nd one as CMAKE_OBJC_COMPILER_ARG1
+
+    list(LENGTH CMAKE_OBJC_COMPILER _CMAKE_OBJC_COMPILER_LIST_LENGTH)
+    if("${_CMAKE_OBJC_COMPILER_LIST_LENGTH}" EQUAL 2)
+      list(GET CMAKE_OBJC_COMPILER 1 CMAKE_OBJC_COMPILER_ARG1)
+      list(GET CMAKE_OBJC_COMPILER 0 CMAKE_OBJC_COMPILER)
+    endif()
+
+    # if a compiler was specified by the user but without path,
+    # now try to find it with the full path
+    # if it is found, force it into the cache,
+    # if not, don't overwrite the setting (which was given by the user) with "NOTFOUND"
+    # if the C compiler already had a path, reuse it for searching the CXX compiler
+    get_filename_component(_CMAKE_USER_OBJC_COMPILER_PATH "${CMAKE_OBJC_COMPILER}" PATH)
+    if(NOT _CMAKE_USER_OBJC_COMPILER_PATH)
+      find_program(CMAKE_OBJC_COMPILER_WITH_PATH NAMES ${CMAKE_OBJC_COMPILER})
+      if(CMAKE_OBJC_COMPILER_WITH_PATH)
+        set(CMAKE_OBJC_COMPILER ${CMAKE_OBJC_COMPILER_WITH_PATH} CACHE STRING "Objective-C compiler" FORCE)
+      endif()
+      unset(CMAKE_OBJC_COMPILER_WITH_PATH CACHE)
+    endif()
+  endif()
+  mark_as_advanced(CMAKE_OBJC_COMPILER)
+
+  # Each entry in this list is a set of extra flags to try
+  # adding to the compile line to see if it helps produce
+  # a valid identification file.
+  set(CMAKE_OBJC_COMPILER_ID_TEST_FLAGS_FIRST)
+  set(CMAKE_OBJC_COMPILER_ID_TEST_FLAGS
+    # Try compiling to an object file only.
+    "-c"
+
+    )
+endif()
+
+# Build a small source file to identify the compiler.
+if(NOT CMAKE_OBJC_COMPILER_ID_RUN)
+  set(CMAKE_OBJC_COMPILER_ID_RUN 1)
+
+  # Try to identify the compiler.
+  set(CMAKE_OBJC_COMPILER_ID)
+  file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in
+    CMAKE_OBJC_COMPILER_ID_PLATFORM_CONTENT)
+
+  # Match the link line from xcodebuild output of the form
+  #  Ld ...
+  #      ...
+  #      /path/to/cc ...CompilerIdOBJC/...
+  # to extract the compiler front-end for the language.
+  set(CMAKE_OBJC_COMPILER_ID_TOOL_MATCH_REGEX "\nLd[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]*-o[^\r\n]*CompilerIdOBJC/(\\./)?(CompilerIdOBJC.(framework|xctest)/)?CompilerIdOBJC[ \t\n\\\"]")
+  set(CMAKE_OBJC_COMPILER_ID_TOOL_MATCH_INDEX 2)
+
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
+  CMAKE_DETERMINE_COMPILER_ID(OBJC OBJCCFLAGS CMakeOBJCCompilerId.m)
+
+  # Set old compiler and platform id variables.
+  if(CMAKE_OBJC_COMPILER_ID STREQUAL "GNU")
+    set(CMAKE_COMPILER_IS_GNUOBJC 1)
+  endif()
+  if(CMAKE_OBJC_COMPILER_ID STREQUAL "Clang")
+    set(CMAKE_COMPILER_IS_CLANGOBJC 1)
+  endif()
+endif()
+
+if (NOT _CMAKE_TOOLCHAIN_LOCATION)
+  get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_OBJC_COMPILER}" PATH)
+endif ()
+
+# If we have a gcc cross compiler, they have usually some prefix, like
+# e.g. powerpc-linux-gcc, arm-elf-gcc or i586-mingw32msvc-gcc, optionally
+# with a 3-component version number at the end (e.g. arm-eabi-gcc-4.5.2).
+# The other tools of the toolchain usually have the same prefix
+# NAME_WE cannot be used since then this test will fail for names like
+# "arm-unknown-nto-qnx6.3.0-gcc.exe", where BASENAME would be
+# "arm-unknown-nto-qnx6" instead of the correct "arm-unknown-nto-qnx6.3.0-"
+if (CMAKE_CROSSCOMPILING  AND NOT _CMAKE_TOOLCHAIN_PREFIX)
+
+  if(CMAKE_OBJC_COMPILER_ID MATCHES "GNU|Clang|QCC")
+    get_filename_component(COMPILER_BASENAME "${CMAKE_OBJC_COMPILER}" NAME)
+    if (COMPILER_BASENAME MATCHES "^(.+-)(clang|g?cc)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
+      set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
+      set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5})
+    elseif(CMAKE_OBJC_COMPILER_ID MATCHES "Clang")
+      if(CMAKE_OBJC_COMPILER_TARGET)
+        set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_OBJC_COMPILER_TARGET}-)
+      endif()
+    elseif(COMPILER_BASENAME MATCHES "qcc(\\.exe)?$")
+      if(CMAKE_OBJC_COMPILER_TARGET MATCHES "gcc_nto([a-z0-9]+_[0-9]+|[^_le]+)(le)?")
+        set(_CMAKE_TOOLCHAIN_PREFIX nto${CMAKE_MATCH_1}-)
+      endif()
+    endif ()
+
+    # if "llvm-" is part of the prefix, remove it, since llvm doesn't have its own binutils
+    # but uses the regular ar, objcopy, etc. (instead of llvm-objcopy etc.)
+    if ("${_CMAKE_TOOLCHAIN_PREFIX}" MATCHES "(.+-)?llvm-$")
+      set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
+    endif ()
+  endif()
+
+endif ()
+
+set(_CMAKE_PROCESSING_LANGUAGE "OBJC")
+include(CMakeFindBinUtils)
+include(Compiler/${CMAKE_OBJC_COMPILER_ID}-FindBinUtils OPTIONAL)
+unset(_CMAKE_PROCESSING_LANGUAGE)
+
+if(CMAKE_OBJC_COMPILER_ARCHITECTURE_ID)
+  set(_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID
+    "set(CMAKE_OBJC_COMPILER_ARCHITECTURE_ID ${CMAKE_OBJC_COMPILER_ARCHITECTURE_ID})")
+else()
+  set(_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID "")
+endif()
+
+if(CMAKE_OBJC_XCODE_ARCHS)
+  set(SET_CMAKE_XCODE_ARCHS
+    "set(CMAKE_XCODE_ARCHS \"${CMAKE_OBJC_XCODE_ARCHS}\")")
+endif()
+
+# configure variables set in this file for fast reload later on
+configure_file(${CMAKE_ROOT}/Modules/CMakeOBJCCompiler.cmake.in
+  ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake
+  @ONLY
+  )
+set(CMAKE_OBJC_COMPILER_ENV_VAR "OBJC")
diff --git a/Modules/CMakeDetermineOBJCXXCompiler.cmake b/Modules/CMakeDetermineOBJCXXCompiler.cmake
new file mode 100644
index 0000000..60fcbb3
--- /dev/null
+++ b/Modules/CMakeDetermineOBJCXXCompiler.cmake
@@ -0,0 +1,197 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# determine the compiler to use for Objective-C++ programs
+# NOTE, a generator may set CMAKE_OBJCXX_COMPILER before
+# loading this file to force a compiler.
+# use environment variable OBJCXX first if defined by user, next use
+# the cmake variable CMAKE_GENERATOR_OBJCXX which can be defined by a generator
+# as a default compiler
+# If the internal cmake variable _CMAKE_TOOLCHAIN_PREFIX is set, this is used
+# as prefix for the tools (e.g. arm-elf-g++, arm-elf-ar etc.)
+#
+# Sets the following variables:
+#   CMAKE_OBJCXX_COMPILER
+#   CMAKE_COMPILER_IS_GNUOBJCXX
+#   CMAKE_COMPILER_IS_CLANGOBJCXX
+#   CMAKE_AR
+#   CMAKE_RANLIB
+#
+# If not already set before, it also sets
+#   _CMAKE_TOOLCHAIN_PREFIX
+
+include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
+
+# Load system-specific compiler preferences for this language.
+include(Platform/${CMAKE_SYSTEM_NAME}-Determine-OBJCXX OPTIONAL)
+include(Platform/${CMAKE_SYSTEM_NAME}-OBJCXX OPTIONAL)
+if(NOT CMAKE_OBJCXX_COMPILER_NAMES)
+  set(CMAKE_OBJCXX_COMPILER_NAMES clang++)
+endif()
+
+if("${CMAKE_GENERATOR}" MATCHES "Xcode")
+  set(CMAKE_OBJCXX_COMPILER_XCODE_TYPE sourcecode.cpp.objcpp)
+else()
+  if(NOT CMAKE_OBJCXX_COMPILER)
+    set(CMAKE_OBJCXX_COMPILER_INIT NOTFOUND)
+
+    # prefer the environment variable OBJCXX
+    if($ENV{OBJCXX} MATCHES ".+")
+      get_filename_component(CMAKE_OBJCXX_COMPILER_INIT $ENV{OBJCXX} PROGRAM PROGRAM_ARGS CMAKE_OBJCXX_FLAGS_ENV_INIT)
+      if(CMAKE_OBJCXX_FLAGS_ENV_INIT)
+        set(CMAKE_OBJCXX_COMPILER_ARG1 "${CMAKE_OBJCXX_FLAGS_ENV_INIT}" CACHE STRING "First argument to Objective-C++ compiler")
+      endif()
+      if(NOT EXISTS ${CMAKE_OBJCXX_COMPILER_INIT})
+        message(FATAL_ERROR "Could not find compiler set in environment variable OBJCXX:\n$ENV{OBJCXX}.\n${CMAKE_OBJCXX_COMPILER_INIT}")
+      endif()
+    endif()
+
+    # next prefer the generator specified compiler
+    if(CMAKE_GENERATOR_OBJCXX)
+      if(NOT CMAKE_OBJCXX_COMPILER_INIT)
+        set(CMAKE_OBJCXX_COMPILER_INIT ${CMAKE_GENERATOR_OBJCXX})
+      endif()
+    endif()
+
+    # finally list compilers to try
+    if(NOT CMAKE_OBJCXX_COMPILER_INIT)
+      set(CMAKE_OBJCXX_COMPILER_LIST ${_CMAKE_TOOLCHAIN_PREFIX}c++ ${_CMAKE_TOOLCHAIN_PREFIX}g++ clang++)
+    endif()
+
+    _cmake_find_compiler(OBJCXX)
+
+  else()
+    # we only get here if CMAKE_OBJCXX_COMPILER was specified using -D or a pre-made CMakeCache.txt
+    # (e.g. via ctest) or set in CMAKE_TOOLCHAIN_FILE
+    # if CMAKE_OBJCXX_COMPILER is a list of length 2, use the first item as
+    # CMAKE_OBJCXX_COMPILER and the 2nd one as CMAKE_OBJCXX_COMPILER_ARG1
+
+    list(LENGTH CMAKE_OBJCXX_COMPILER _CMAKE_OBJCXX_COMPILER_LIST_LENGTH)
+    if("${_CMAKE_OBJCXX_COMPILER_LIST_LENGTH}" EQUAL 2)
+      list(GET CMAKE_OBJCXX_COMPILER 1 CMAKE_OBJCXX_COMPILER_ARG1)
+      list(GET CMAKE_OBJCXX_COMPILER 0 CMAKE_OBJCXX_COMPILER)
+    endif()
+
+    # if a compiler was specified by the user but without path,
+    # now try to find it with the full path
+    # if it is found, force it into the cache,
+    # if not, don't overwrite the setting (which was given by the user) with "NOTFOUND"
+    # if the C compiler already had a path, reuse it for searching the CXX compiler
+    get_filename_component(_CMAKE_USER_OBJCXX_COMPILER_PATH "${CMAKE_OBJCXX_COMPILER}" PATH)
+    if(NOT _CMAKE_USER_OBJCXX_COMPILER_PATH)
+      find_program(CMAKE_OBJCXX_COMPILER_WITH_PATH NAMES ${CMAKE_OBJCXX_COMPILER})
+      if(CMAKE_OBJCXX_COMPILER_WITH_PATH)
+        set(CMAKE_OBJCXX_COMPILER ${CMAKE_OBJCXX_COMPILER_WITH_PATH} CACHE STRING "Objective-C++ compiler" FORCE)
+      endif()
+      unset(CMAKE_OBJCXX_COMPILER_WITH_PATH CACHE)
+    endif()
+
+  endif()
+  mark_as_advanced(CMAKE_OBJCXX_COMPILER)
+
+  # Each entry in this list is a set of extra flags to try
+  # adding to the compile line to see if it helps produce
+  # a valid identification file.
+  set(CMAKE_OBJCXX_COMPILER_ID_TEST_FLAGS_FIRST)
+  set(CMAKE_OBJCXX_COMPILER_ID_TEST_FLAGS
+    # Try compiling to an object file only.
+    "-c"
+
+    # ARMClang need target options
+    "--target=arm-arm-none-eabi -mcpu=cortex-m3"
+    )
+endif()
+
+# Build a small source file to identify the compiler.
+if(NOT CMAKE_OBJCXX_COMPILER_ID_RUN)
+  set(CMAKE_OBJCXX_COMPILER_ID_RUN 1)
+
+  # Try to identify the compiler.
+  set(CMAKE_OBJCXX_COMPILER_ID)
+  file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in
+    CMAKE_OBJCXX_COMPILER_ID_PLATFORM_CONTENT)
+
+  # Match the link line from xcodebuild output of the form
+  #  Ld ...
+  #      ...
+  #      /path/to/cc ...CompilerIdOBJCXX/...
+  # to extract the compiler front-end for the language.
+  set(CMAKE_OBJCXX_COMPILER_ID_TOOL_MATCH_REGEX "\nLd[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]*-o[^\r\n]*CompilerIdOBJCXX/(\\./)?(CompilerIdOBJCXX.(framework|xctest)/)?CompilerIdOBJCXX[ \t\n\\\"]")
+  set(CMAKE_OBJCXX_COMPILER_ID_TOOL_MATCH_INDEX 2)
+
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
+  CMAKE_DETERMINE_COMPILER_ID(OBJCXX OBJCXXFLAGS CMakeOBJCXXCompilerId.mm)
+
+  # Set old compiler and platform id variables.
+  if(CMAKE_OBJCXX_COMPILER_ID MATCHES "GNU")
+    set(CMAKE_COMPILER_IS_GNUOBJCXX 1)
+  endif()
+  if(CMAKE_OBJCXX_COMPILER_ID MATCHES "Clang")
+    set(CMAKE_COMPILER_IS_CLANGOBJCXX 1)
+  endif()
+endif()
+
+if (NOT _CMAKE_TOOLCHAIN_LOCATION)
+  get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_OBJCXX_COMPILER}" PATH)
+endif ()
+
+# if we have a g++ cross compiler, they have usually some prefix, like
+# e.g. powerpc-linux-g++, arm-elf-g++ or i586-mingw32msvc-g++ , optionally
+# with a 3-component version number at the end (e.g. arm-eabi-gcc-4.5.2).
+# The other tools of the toolchain usually have the same prefix
+# NAME_WE cannot be used since then this test will fail for names like
+# "arm-unknown-nto-qnx6.3.0-gcc.exe", where BASENAME would be
+# "arm-unknown-nto-qnx6" instead of the correct "arm-unknown-nto-qnx6.3.0-"
+
+
+if (CMAKE_CROSSCOMPILING  AND NOT  _CMAKE_TOOLCHAIN_PREFIX)
+
+  if("${CMAKE_OBJCXX_COMPILER_ID}" MATCHES "GNU|Clang|QCC")
+    get_filename_component(COMPILER_BASENAME "${CMAKE_OBJCXX_COMPILER}" NAME)
+    if (COMPILER_BASENAME MATCHES "^(.+-)(clan)?[gc]\\+\\+(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
+      set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
+      set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5})
+    elseif("${CMAKE_OBJCXX_COMPILER_ID}" MATCHES "Clang")
+      if(CMAKE_OBJCXX_COMPILER_TARGET)
+        set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_OBJCXX_COMPILER_TARGET}-)
+      endif()
+    elseif(COMPILER_BASENAME MATCHES "QCC(\\.exe)?$")
+      if(CMAKE_OBJCXX_COMPILER_TARGET MATCHES "gcc_nto([a-z0-9]+_[0-9]+|[^_le]+)(le)")
+        set(_CMAKE_TOOLCHAIN_PREFIX nto${CMAKE_MATCH_1}-)
+      endif()
+    endif ()
+
+    # if "llvm-" is part of the prefix, remove it, since llvm doesn't have its own binutils
+    # but uses the regular ar, objcopy, etc. (instead of llvm-objcopy etc.)
+    if ("${_CMAKE_TOOLCHAIN_PREFIX}" MATCHES "(.+-)?llvm-$")
+      set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
+    endif ()
+  endif()
+
+endif ()
+
+set(_CMAKE_PROCESSING_LANGUAGE "OBJCXX")
+include(CMakeFindBinUtils)
+include(Compiler/${CMAKE_OBJCXX_COMPILER_ID}-FindBinUtils OPTIONAL)
+unset(_CMAKE_PROCESSING_LANGUAGE)
+
+if(CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID)
+  set(_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID
+    "set(CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID ${CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID})")
+else()
+  set(_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID "")
+endif()
+
+if(CMAKE_OBJCXX_XCODE_ARCHS)
+  set(SET_CMAKE_XCODE_ARCHS
+    "set(CMAKE_XCODE_ARCHS \"${CMAKE_OBJCXX_XCODE_ARCHS}\")")
+endif()
+
+# configure all variables set in this file
+configure_file(${CMAKE_ROOT}/Modules/CMakeOBJCXXCompiler.cmake.in
+  ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake
+  @ONLY
+  )
+
+set(CMAKE_OBJCXX_COMPILER_ENV_VAR "OBJCXX")
diff --git a/Modules/CMakeDetermineSwiftCompiler.cmake b/Modules/CMakeDetermineSwiftCompiler.cmake
index 2fcf7b0..9aafe48 100644
--- a/Modules/CMakeDetermineSwiftCompiler.cmake
+++ b/Modules/CMakeDetermineSwiftCompiler.cmake
@@ -53,7 +53,7 @@
     list(APPEND CMAKE_Swift_COMPILER_ID_MATCH_VENDORS Apple)
     set(CMAKE_Swift_COMPILER_ID_MATCH_VENDOR_REGEX_Apple "com.apple.xcode.tools.swift.compiler")
 
-    set(CMAKE_Swift_COMPILER_ID_TOOL_MATCH_REGEX "\nCompileSwiftSources[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]* -c[^\r\n]*CompilerIdSwift/CompilerId/main.swift")
+    set(CMAKE_Swift_COMPILER_ID_TOOL_MATCH_REGEX "\nCompileSwift[^\n]*(\n[ \t]+[^\n]*)*\n[ \t]+([^ \t\r\n]+)[^\r\n]* -c[^\r\n]*CompilerIdSwift/CompilerId/main.swift")
     set(CMAKE_Swift_COMPILER_ID_TOOL_MATCH_INDEX 2)
   endif()
 
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index 0e84116..3887b2d 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -57,6 +57,44 @@
 __resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker")
 __resolve_tool_path(CMAKE_MT     "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool")
 
+function(__get_compiler_component CMAKE_TOOL NAME)
+  get_property(_CMAKE_TOOL_CACHED CACHE ${CMAKE_TOOL} PROPERTY TYPE)
+  # If CMAKE_TOOL is present in the CMake Cache, return
+  if(_CMAKE_TOOL_CACHED)
+    return()
+  endif()
+
+  cmake_parse_arguments(_COMPILER_COMP_ARGS "" "DOC" "HINTS;NAMES" ${ARGN})
+
+  set(_LOCATION_FROM_COMPILER )
+  set(_NAME_FROM_COMPILER )
+
+  if (NOT DEFINED ${CMAKE_TOOL})
+    if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xGNU" OR
+       "x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang")
+      execute_process(
+        COMMAND ${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER} -print-prog-name=${NAME}
+        RESULT_VARIABLE _CMAKE_TOOL_PROG_NAME_RESULT
+        OUTPUT_VARIABLE _CMAKE_TOOL_PROG_NAME_OUTPUT
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+      )
+      if (_CMAKE_TOOL_PROG_NAME_RESULT STREQUAL "0" AND IS_ABSOLUTE "${_CMAKE_TOOL_PROG_NAME_OUTPUT}")
+        get_filename_component(_LOCATION_FROM_COMPILER "${_CMAKE_TOOL_PROG_NAME_OUTPUT}" DIRECTORY)
+        get_filename_component(_NAME_FROM_COMPILER "${_CMAKE_TOOL_PROG_NAME_OUTPUT}" NAME)
+      endif()
+    endif()
+  endif()
+
+  if (NOT _COMPILER_COMP_ARGS_DOC)
+    set(_COMPILER_COMP_ARGS_DOC "Path to ${NAME} program")
+  endif()
+  find_program(${CMAKE_TOOL}
+    NAMES ${_NAME_FROM_COMPILER} ${_COMPILER_COMP_ARGS_NAMES}
+    HINTS ${_LOCATION_FROM_COMPILER} ${_COMPILER_COMP_ARGS_HINTS}
+    DOC "${_COMPILER_COMP_ARGS_DOC}"
+  )
+endfunction()
+
 set(_CMAKE_TOOL_VARS "")
 
 # if it's the MS C/CXX compiler, search for link
@@ -69,10 +107,15 @@
    OR (CMAKE_GENERATOR MATCHES "Visual Studio"
        AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "Tegra-Android"))
 
-  find_program(CMAKE_LINKER NAMES link HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang")
+    find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm llvm-nm HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+    set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link")
+  endif()
+
+  find_program(CMAKE_LINKER NAMES ${_CMAKE_ADDITIONAL_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,46 +127,77 @@
   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})
+  __get_compiler_component(CMAKE_AR 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})
+  __get_compiler_component(CMAKE_RANLIB 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})
+  __get_compiler_component(CMAKE_STRIP strip NAMES ${_CMAKE_TOOLCHAIN_PREFIX}strip${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_STRIP_NAMES}
+                                             HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_LINKER ld NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES}
+                                           HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_NM nm NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES}
+                                       HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_OBJDUMP objdump NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES}
+                                       HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_OBJCOPY objcopy NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES}
+                                       HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_READELF readelf NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES}
+                                       HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_DLLTOOL dlltool NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES}
+                                       HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_ADDR2LINE 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)
 
+
+  unset(_CMAKE_ADDITIONAL_AR_NAMES)
+  unset(_CMAKE_ADDITIONAL_RANLIB_NAMES)
+  unset(_CMAKE_ADDITIONAL_STRIP_NAMES)
+  unset(_CMAKE_ADDITIONAL_LINKER_NAMES)
+  unset(_CMAKE_ADDITIONAL_NM_NAMES)
+  unset(_CMAKE_ADDITIONAL_OBJDUMP_NAMES)
+  unset(_CMAKE_ADDITIONAL_OBJCOPY_NAMES)
+  unset(_CMAKE_ADDITIONAL_READELF_NAMES)
+  unset(_CMAKE_ADDITIONAL_DLLTOOL_NAMES)
+  unset(_CMAKE_ADDITIONAL_ADDR2LINE_NAMES)
 endif()
 
 if(CMAKE_PLATFORM_HAS_INSTALLNAME)
-  find_program(CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+  __get_compiler_component(CMAKE_INSTALL_NAME_TOOL install_name_tool NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
 
   if(NOT CMAKE_INSTALL_NAME_TOOL)
     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/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake
index ddfc7bd..77d8cfd 100644
--- a/Modules/CMakeGenericSystem.cmake
+++ b/Modules/CMakeGenericSystem.cmake
@@ -26,6 +26,7 @@
 
 set(CMAKE_AUTOGEN_ORIGIN_DEPENDS ON)
 set(CMAKE_AUTOMOC_COMPILER_PREDEFINES ON)
+set(CMAKE_AUTOMOC_PATH_PREFIX ON)
 set(CMAKE_AUTOMOC_MACRO_NAMES "Q_OBJECT" "Q_GADGET" "Q_NAMESPACE")
 
 # basically all general purpose OSs support shared libs
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in
new file mode 100644
index 0000000..1555517
--- /dev/null
+++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -0,0 +1,69 @@
+set(CMAKE_OBJC_COMPILER "@CMAKE_OBJC_COMPILER@")
+set(CMAKE_OBJC_COMPILER_ARG1 "@CMAKE_OBJC_COMPILER_ARG1@")
+set(CMAKE_OBJC_COMPILER_ID "@CMAKE_OBJC_COMPILER_ID@")
+set(CMAKE_OBJC_COMPILER_VERSION "@CMAKE_OBJC_COMPILER_VERSION@")
+set(CMAKE_OBJC_COMPILER_VERSION_INTERNAL "@CMAKE_OBJC_COMPILER_VERSION_INTERNAL@")
+set(CMAKE_OBJC_COMPILER_WRAPPER "@CMAKE_OBJC_COMPILER_WRAPPER@")
+set(CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_OBJC_COMPILE_FEATURES "@CMAKE_OBJC_COMPILE_FEATURES@")
+set(CMAKE_OBJC90_COMPILE_FEATURES "@CMAKE_OBJC90_COMPILE_FEATURES@")
+set(CMAKE_OBJC99_COMPILE_FEATURES "@CMAKE_OBJC99_COMPILE_FEATURES@")
+set(CMAKE_OBJC11_COMPILE_FEATURES "@CMAKE_OBJC11_COMPILE_FEATURES@")
+
+set(CMAKE_OBJC_PLATFORM_ID "@CMAKE_OBJC_PLATFORM_ID@")
+set(CMAKE_OBJC_SIMULATE_ID "@CMAKE_OBJC_SIMULATE_ID@")
+set(CMAKE_OBJC_COMPILER_FRONTEND_VARIANT "@CMAKE_OBJC_COMPILER_FRONTEND_VARIANT@")
+set(CMAKE_OBJC_SIMULATE_VERSION "@CMAKE_OBJC_SIMULATE_VERSION@")
+@_SET_CMAKE_OBJC_COMPILER_ARCHITECTURE_ID@
+@SET_CMAKE_XCODE_ARCHS@
+set(CMAKE_AR "@CMAKE_AR@")
+set(CMAKE_OBJC_COMPILER_AR "@CMAKE_OBJC_COMPILER_AR@")
+set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
+set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
+set(CMAKE_OBJC_COMPILER_LOADED 1)
+set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@)
+set(CMAKE_OBJC_ABI_COMPILED @CMAKE_OBJC_ABI_COMPILED@)
+
+set(CMAKE_OBJC_COMPILER_ENV_VAR "OBJC")
+
+set(CMAKE_OBJC_COMPILER_ID_RUN 1)
+set(CMAKE_OBJC_SOURCE_FILE_EXTENSIONS m)
+set(CMAKE_OBJC_IGNORE_EXTENSIONS h;H;o;O)
+set(CMAKE_OBJC_LINKER_PREFERENCE 5)
+
+foreach (lang C CXX OBJCXX)
+  foreach(extension IN LISTS CMAKE_OBJC_SOURCE_FILE_EXTENSIONS)
+    if (CMAKE_${lang}_COMPILER_ID_RUN)
+      list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension})
+    endif()
+  endforeach()
+endforeach()
+
+# Save compiler ABI information.
+set(CMAKE_OBJC_SIZEOF_DATA_PTR "@CMAKE_OBJC_SIZEOF_DATA_PTR@")
+set(CMAKE_OBJC_COMPILER_ABI "@CMAKE_OBJC_COMPILER_ABI@")
+set(CMAKE_OBJC_LIBRARY_ARCHITECTURE "@CMAKE_OBJC_LIBRARY_ARCHITECTURE@")
+
+if(CMAKE_OBJC_SIZEOF_DATA_PTR)
+  set(CMAKE_SIZEOF_VOID_P "${CMAKE_OBJC_SIZEOF_DATA_PTR}")
+endif()
+
+if(CMAKE_OBJC_COMPILER_ABI)
+  set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_OBJC_COMPILER_ABI}")
+endif()
+
+if(CMAKE_OBJC_LIBRARY_ARCHITECTURE)
+  set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_OBJC_LIBRARY_ARCHITECTURE@")
+endif()
+
+@CMAKE_OBJC_COMPILER_CUSTOM_CODE@
+@CMAKE_OBJC_SYSROOT_FLAG_CODE@
+@CMAKE_OBJC_OSX_DEPLOYMENT_TARGET_FLAG_CODE@
+
+set(CMAKE_OBJC_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_INCLUDE_DIRECTORIES@")
+set(CMAKE_OBJC_IMPLICIT_LINK_LIBRARIES "@CMAKE_OBJC_IMPLICIT_LINK_LIBRARIES@")
+set(CMAKE_OBJC_IMPLICIT_LINK_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_LINK_DIRECTORIES@")
+set(CMAKE_OBJC_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_OBJC_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
diff --git a/Modules/CMakeOBJCCompilerABI.m b/Modules/CMakeOBJCCompilerABI.m
new file mode 100644
index 0000000..8fa8511
--- /dev/null
+++ b/Modules/CMakeOBJCCompilerABI.m
@@ -0,0 +1,20 @@
+#ifdef __cplusplus
+# error "A C++ compiler has been selected for Objective-C."
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+#include "CMakeCompilerABI.h"
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+  int require = 0;
+  require += info_sizeof_dptr[argc];
+#if defined(ABI_ID)
+  require += info_abi[argc];
+#endif
+  (void)argv;
+  return require;
+}
diff --git a/Modules/CMakeOBJCCompilerId.m.in b/Modules/CMakeOBJCCompilerId.m.in
new file mode 100644
index 0000000..2b8aa30
--- /dev/null
+++ b/Modules/CMakeOBJCCompilerId.m.in
@@ -0,0 +1,63 @@
+#ifdef __cplusplus
+# error "An Objective-C++ compiler has been selected for Objective-C."
+#endif
+
+@CMAKE_OBJC_COMPILER_ID_CONTENT@
+
+/* Construct the string literal in pieces to prevent the source from
+   getting matched.  Store it in a pointer rather than an array
+   because some compilers will just produce instructions to fill the
+   array rather than assigning a pointer to a static array.  */
+char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
+#ifdef SIMULATE_ID
+char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
+#endif
+
+#ifdef __QNXNTO__
+char const* qnxnto = "INFO" ":" "qnxnto[]";
+#endif
+
+@CMAKE_OBJC_COMPILER_ID_PLATFORM_CONTENT@
+@CMAKE_OBJC_COMPILER_ID_ERROR_FOR_TEST@
+
+#if !defined(__STDC__)
+# if (defined(_MSC_VER) && !defined(__clang__)) \
+  || (defined(__ibmxl__) || defined(__IBMC__))
+#  define C_DIALECT "90"
+# else
+#  define C_DIALECT
+# endif
+#elif __STDC_VERSION__ >= 201000L
+# define C_DIALECT "11"
+#elif __STDC_VERSION__ >= 199901L
+# define C_DIALECT "99"
+#else
+# define C_DIALECT "90"
+#endif
+const char* info_language_dialect_default =
+  "INFO" ":" "dialect_default[" C_DIALECT "]";
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char* argv[])
+{
+  int require = 0;
+  require += info_compiler[argc];
+  require += info_platform[argc];
+  require += info_arch[argc];
+#ifdef COMPILER_VERSION_MAJOR
+  require += info_version[argc];
+#endif
+#ifdef COMPILER_VERSION_INTERNAL
+  require += info_version_internal[argc];
+#endif
+#ifdef SIMULATE_ID
+  require += info_simulate[argc];
+#endif
+#ifdef SIMULATE_VERSION_MAJOR
+  require += info_simulate_version[argc];
+#endif
+  require += info_language_dialect_default[argc];
+  (void)argv;
+  return require;
+}
diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake
new file mode 100644
index 0000000..2baad4a
--- /dev/null
+++ b/Modules/CMakeOBJCInformation.cmake
@@ -0,0 +1,188 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# This file sets the basic flags for the Objective-C language in CMake.
+# It also loads the available platform file for the system-compiler
+# if it exists.
+# It also loads a system - compiler - processor (or target hardware)
+# specific file, which is mainly useful for crosscompiling and embedded systems.
+
+include(CMakeLanguageInformation)
+
+# some compilers use different extensions (e.g. sdcc uses .rel)
+# so set the extension here first so it can be overridden by the compiler specific file
+set(CMAKE_OBJC_OUTPUT_EXTENSION .o)
+
+if(NOT CMAKE_INCLUDE_FLAG_OBJC)
+  set(CMAKE_INCLUDE_FLAG_OBJC ${CMAKE_INCLUDE_FLAG_C})
+endif()
+
+set(_INCLUDED_FILE 0)
+
+# Load compiler-specific information.
+if(CMAKE_OBJC_COMPILER_ID)
+  include(Compiler/${CMAKE_OBJC_COMPILER_ID}-OBJC OPTIONAL)
+endif()
+
+set(CMAKE_BASE_NAME)
+get_filename_component(CMAKE_BASE_NAME "${CMAKE_OBJC_COMPILER}" NAME_WE)
+if(CMAKE_COMPILER_IS_GNUOBJC)
+  set(CMAKE_BASE_NAME gcc)
+endif()
+
+
+# load a hardware specific file, mostly useful for embedded compilers
+if(CMAKE_SYSTEM_PROCESSOR)
+  if(CMAKE_OBJC_COMPILER_ID)
+    include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_ID}-OBJC-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
+  endif()
+  if (NOT _INCLUDED_FILE)
+    include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME}-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL)
+  endif ()
+endif()
+
+
+# load the system- and compiler specific files
+if(CMAKE_OBJC_COMPILER_ID)
+  include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_ID}-OBJC
+    OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
+endif()
+if (NOT _INCLUDED_FILE)
+  include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME}
+    OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
+endif ()
+
+# load any compiler-wrapper specific information
+if (CMAKE_OBJC_COMPILER_WRAPPER)
+  __cmake_include_compiler_wrapper(OBJC)
+endif ()
+
+# We specify the compiler information in the system file for some
+# platforms, but this language may not have been enabled when the file
+# was first included.  Include it again to get the language info.
+# Remove this when all compiler info is removed from system files.
+if (NOT _INCLUDED_FILE)
+  include(Platform/${CMAKE_SYSTEM_NAME} OPTIONAL)
+endif ()
+
+if(CMAKE_OBJC_SIZEOF_DATA_PTR)
+  foreach(f ${CMAKE_OBJC_ABI_FILES})
+    include(${f})
+  endforeach()
+  unset(CMAKE_OBJC_ABI_FILES)
+endif()
+
+# This should be included before the _INIT variables are
+# used to initialize the cache.  Since the rule variables
+# have if blocks on them, users can still define them here.
+# But, it should still be after the platform file so changes can
+# be made to those values.
+
+if(CMAKE_USER_MAKE_RULES_OVERRIDE)
+  # Save the full path of the file so try_compile can use it.
+  include(${CMAKE_USER_MAKE_RULES_OVERRIDE} RESULT_VARIABLE _override)
+  set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_override}")
+endif()
+
+if(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC)
+  # Save the full path of the file so try_compile can use it.
+  include(${CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC} RESULT_VARIABLE _override)
+  set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC "${_override}")
+endif()
+
+
+# for most systems a module is the same as a shared library
+# so unless the variable CMAKE_MODULE_EXISTS is set just
+# copy the values from the LIBRARY variables
+if(NOT CMAKE_MODULE_EXISTS)
+  set(CMAKE_SHARED_MODULE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS})
+  set(CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS})
+endif()
+
+set(CMAKE_OBJC_FLAGS_INIT "$ENV{OBJCFLAGS} ${CMAKE_OBJC_FLAGS_INIT}")
+
+cmake_initialize_per_config_variable(CMAKE_OBJC_FLAGS "Flags used by the Objective-C compiler")
+
+if(CMAKE_OBJC_STANDARD_LIBRARIES_INIT)
+  set(CMAKE_OBJC_STANDARD_LIBRARIES "${CMAKE_OBJC_STANDARD_LIBRARIES_INIT}"
+    CACHE STRING "Libraries linked by default with all Objective-C applications.")
+  mark_as_advanced(CMAKE_OBJC_STANDARD_LIBRARIES)
+endif()
+
+include(CMakeCommonLanguageInclude)
+
+# now define the following rule variables
+
+# CMAKE_OBJC_CREATE_SHARED_LIBRARY
+# CMAKE_OBJC_CREATE_SHARED_MODULE
+# CMAKE_OBJC_COMPILE_OBJECT
+# CMAKE_OBJC_LINK_EXECUTABLE
+
+# variables supplied by the generator at use time
+# <TARGET>
+# <TARGET_BASE> the target without the suffix
+# <OBJECTS>
+# <OBJECT>
+# <LINK_LIBRARIES>
+# <FLAGS>
+# <LINK_FLAGS>
+
+# Objective-C compiler information
+# <CMAKE_OBJC_COMPILER>
+# <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS>
+# <CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS>
+# <CMAKE_OBJC_LINK_FLAGS>
+
+# Static library tools
+# <CMAKE_AR>
+# <CMAKE_RANLIB>
+
+
+# create an Objective-C shared library
+if(NOT CMAKE_OBJC_CREATE_SHARED_LIBRARY)
+  set(CMAKE_OBJC_CREATE_SHARED_LIBRARY
+      "<CMAKE_OBJC_COMPILER> <CMAKE_SHARED_LIBRARY_OBJC_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
+endif()
+
+# create an Objective-C shared module just copy the shared library rule
+if(NOT CMAKE_OBJC_CREATE_SHARED_MODULE)
+  set(CMAKE_OBJC_CREATE_SHARED_MODULE ${CMAKE_OBJC_CREATE_SHARED_LIBRARY})
+endif()
+
+# Create an static archive incrementally for large object file counts.
+# If CMAKE_OBJC_CREATE_STATIC_LIBRARY is set it will override these.
+if(NOT DEFINED CMAKE_OBJC_ARCHIVE_CREATE)
+  set(CMAKE_OBJC_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")
+endif()
+if(NOT DEFINED CMAKE_OBJC_ARCHIVE_APPEND)
+  set(CMAKE_OBJC_ARCHIVE_APPEND "<CMAKE_AR> q  <TARGET> <LINK_FLAGS> <OBJECTS>")
+endif()
+if(NOT DEFINED CMAKE_OBJC_ARCHIVE_FINISH)
+  set(CMAKE_OBJC_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
+endif()
+
+# compile an Objective-C file into an object file
+if(NOT CMAKE_OBJC_COMPILE_OBJECT)
+  set(CMAKE_OBJC_COMPILE_OBJECT
+    "<CMAKE_OBJC_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>   -c <SOURCE>")
+endif()
+
+if(NOT CMAKE_OBJC_LINK_EXECUTABLE)
+  set(CMAKE_OBJC_LINK_EXECUTABLE
+    "<CMAKE_OBJC_COMPILER> <FLAGS> <CMAKE_OBJC_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES>")
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG)
+  set(CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG})
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG_SEP)
+  set(CMAKE_EXECUTABLE_RUNTIME_OBJC_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP})
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG)
+  set(CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG})
+endif()
+
+set(CMAKE_OBJC_INFORMATION_LOADED 1)
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in
new file mode 100644
index 0000000..b6452c4
--- /dev/null
+++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -0,0 +1,79 @@
+set(CMAKE_OBJCXX_COMPILER "@CMAKE_OBJCXX_COMPILER@")
+set(CMAKE_OBJCXX_COMPILER_ARG1 "@CMAKE_OBJCXX_COMPILER_ARG1@")
+set(CMAKE_OBJCXX_COMPILER_ID "@CMAKE_OBJCXX_COMPILER_ID@")
+set(CMAKE_OBJCXX_COMPILER_VERSION "@CMAKE_OBJCXX_COMPILER_VERSION@")
+set(CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL "@CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL@")
+set(CMAKE_OBJCXX_COMPILER_WRAPPER "@CMAKE_OBJCXX_COMPILER_WRAPPER@")
+set(CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_OBJCXX_COMPILE_FEATURES "@CMAKE_OBJCXX_COMPILE_FEATURES@")
+set(CMAKE_OBJCXX98_COMPILE_FEATURES "@CMAKE_OBJCXX98_COMPILE_FEATURES@")
+set(CMAKE_OBJCXX11_COMPILE_FEATURES "@CMAKE_OBJCXX11_COMPILE_FEATURES@")
+set(CMAKE_OBJCXX14_COMPILE_FEATURES "@CMAKE_OBJCXX14_COMPILE_FEATURES@")
+set(CMAKE_OBJCXX17_COMPILE_FEATURES "@CMAKE_OBJCXX17_COMPILE_FEATURES@")
+set(CMAKE_OBJCXX20_COMPILE_FEATURES "@CMAKE_OBJCXX20_COMPILE_FEATURES@")
+
+set(CMAKE_OBJCXX_PLATFORM_ID "@CMAKE_OBJCXX_PLATFORM_ID@")
+set(CMAKE_OBJCXX_SIMULATE_ID "@CMAKE_OBJCXX_SIMULATE_ID@")
+set(CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT "@CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT@")
+set(CMAKE_OBJCXX_SIMULATE_VERSION "@CMAKE_OBJCXX_SIMULATE_VERSION@")
+@_SET_CMAKE_OBJCXX_COMPILER_ARCHITECTURE_ID@
+@SET_CMAKE_XCODE_ARCHS@
+set(CMAKE_AR "@CMAKE_AR@")
+set(CMAKE_OBJCXX_COMPILER_AR "@CMAKE_OBJCXX_COMPILER_AR@")
+set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
+set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
+set(CMAKE_OBJCXX_COMPILER_LOADED 1)
+set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@)
+set(CMAKE_OBJCXX_ABI_COMPILED @CMAKE_OBJCXX_ABI_COMPILED@)
+
+set(CMAKE_OBJCXX_COMPILER_ENV_VAR "OBJCXX")
+
+set(CMAKE_OBJCXX_COMPILER_ID_RUN 1)
+set(CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS M;m;mm)
+set(CMAKE_OBJCXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O)
+
+if (CMAKE_OBJC_COMPILER_ID_RUN)
+  foreach(extension IN LISTS CMAKE_OBJC_SOURCE_FILE_EXTENSIONS)
+    list(REMOVE_ITEM CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS ${extension})
+  endforeach()
+endif()
+
+foreach (lang C CXX OBJC)
+  foreach(extension IN LISTS CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS)
+    if (CMAKE_${lang}_COMPILER_ID_RUN)
+      list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension})
+    endif()
+  endforeach()
+endforeach()
+
+set(CMAKE_OBJCXX_LINKER_PREFERENCE 25)
+set(CMAKE_OBJCXX_LINKER_PREFERENCE_PROPAGATES 1)
+
+# Save compiler ABI information.
+set(CMAKE_OBJCXX_SIZEOF_DATA_PTR "@CMAKE_OBJCXX_SIZEOF_DATA_PTR@")
+set(CMAKE_OBJCXX_COMPILER_ABI "@CMAKE_OBJCXX_COMPILER_ABI@")
+set(CMAKE_OBJCXX_LIBRARY_ARCHITECTURE "@CMAKE_OBJCXX_LIBRARY_ARCHITECTURE@")
+
+if(CMAKE_OBJCXX_SIZEOF_DATA_PTR)
+  set(CMAKE_SIZEOF_VOID_P "${CMAKE_OBJCXX_SIZEOF_DATA_PTR}")
+endif()
+
+if(CMAKE_OBJCXX_COMPILER_ABI)
+  set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_OBJCXX_COMPILER_ABI}")
+endif()
+
+if(CMAKE_OBJCXX_LIBRARY_ARCHITECTURE)
+  set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_OBJCXX_LIBRARY_ARCHITECTURE@")
+endif()
+
+@CMAKE_OBJCXX_COMPILER_CUSTOM_CODE@
+@CMAKE_OBJCXX_SYSROOT_FLAG_CODE@
+@CMAKE_OBJCXX_OSX_DEPLOYMENT_TARGET_FLAG_CODE@
+
+set(CMAKE_OBJCXX_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_INCLUDE_DIRECTORIES@")
+set(CMAKE_OBJCXX_IMPLICIT_LINK_LIBRARIES "@CMAKE_OBJCXX_IMPLICIT_LINK_LIBRARIES@")
+set(CMAKE_OBJCXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_LINK_DIRECTORIES@")
+set(CMAKE_OBJCXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_OBJCXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
diff --git a/Modules/CMakeOBJCXXCompilerABI.mm b/Modules/CMakeOBJCXXCompilerABI.mm
new file mode 100644
index 0000000..288a58c
--- /dev/null
+++ b/Modules/CMakeOBJCXXCompilerABI.mm
@@ -0,0 +1,20 @@
+#ifndef __cplusplus
+# error "A C compiler has been selected for Objective-C++."
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+#include "CMakeCompilerABI.h"
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+  int require = 0;
+  require += info_sizeof_dptr[argc];
+#if defined(ABI_ID)
+  require += info_abi[argc];
+#endif
+  (void)argv;
+  return require;
+}
diff --git a/Modules/CMakeOBJCXXCompilerId.mm.in b/Modules/CMakeOBJCXXCompilerId.mm.in
new file mode 100644
index 0000000..fe04de1
--- /dev/null
+++ b/Modules/CMakeOBJCXXCompilerId.mm.in
@@ -0,0 +1,68 @@
+/* This source file must have a .cpp extension so that all C++ compilers
+   recognize the extension without flags.  Borland does not know .cxx for
+   example.  */
+#ifndef __cplusplus
+# error "An Objective-C compiler has been selected for Objective-C++."
+#endif
+
+@CMAKE_OBJCXX_COMPILER_ID_CONTENT@
+
+/* Construct the string literal in pieces to prevent the source from
+   getting matched.  Store it in a pointer rather than an array
+   because some compilers will just produce instructions to fill the
+   array rather than assigning a pointer to a static array.  */
+char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
+#ifdef SIMULATE_ID
+char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
+#endif
+
+#ifdef __QNXNTO__
+char const* qnxnto = "INFO" ":" "qnxnto[]";
+#endif
+
+@CMAKE_OBJCXX_COMPILER_ID_PLATFORM_CONTENT@
+@CMAKE_OBJCXX_COMPILER_ID_ERROR_FOR_TEST@
+
+#if defined(_MSC_VER) && defined(_MSVC_LANG)
+#define CXX_STD _MSVC_LANG
+#else
+#define CXX_STD __cplusplus
+#endif
+
+const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+#if CXX_STD > 201703L
+  "20"
+#elif CXX_STD >= 201703L
+  "17"
+#elif CXX_STD >= 201402L
+  "14"
+#elif CXX_STD >= 201103L
+  "11"
+#else
+  "98"
+#endif
+"]";
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char* argv[])
+{
+  int require = 0;
+  require += info_compiler[argc];
+  require += info_platform[argc];
+#ifdef COMPILER_VERSION_MAJOR
+  require += info_version[argc];
+#endif
+#ifdef COMPILER_VERSION_INTERNAL
+  require += info_version_internal[argc];
+#endif
+#ifdef SIMULATE_ID
+  require += info_simulate[argc];
+#endif
+#ifdef SIMULATE_VERSION_MAJOR
+  require += info_simulate_version[argc];
+#endif
+  require += info_language_dialect_default[argc];
+  (void)argv;
+  return require;
+}
diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake
new file mode 100644
index 0000000..3f55b01
--- /dev/null
+++ b/Modules/CMakeOBJCXXInformation.cmake
@@ -0,0 +1,273 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# This file sets the basic flags for the Objective-C++ language in CMake.
+# It also loads the available platform file for the system-compiler
+# if it exists.
+# It also loads a system - compiler - processor (or target hardware)
+# specific file, which is mainly useful for crosscompiling and embedded systems.
+
+include(CMakeLanguageInformation)
+
+# some compilers use different extensions (e.g. sdcc uses .rel)
+# so set the extension here first so it can be overridden by the compiler specific file
+set(CMAKE_OBJCXX_OUTPUT_EXTENSION .o)
+
+set(_INCLUDED_FILE 0)
+
+# Load compiler-specific information.
+if(CMAKE_OBJCXX_COMPILER_ID)
+  include(Compiler/${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX OPTIONAL)
+endif()
+
+set(CMAKE_BASE_NAME)
+get_filename_component(CMAKE_BASE_NAME "${CMAKE_OBJCXX_COMPILER}" NAME_WE)
+# since the gnu compiler has several names force g++
+if(CMAKE_COMPILER_IS_GNUOBJCXX)
+  set(CMAKE_BASE_NAME g++)
+endif()
+
+
+# load a hardware specific file, mostly useful for embedded compilers
+if(CMAKE_SYSTEM_PROCESSOR)
+  if(CMAKE_OBJCXX_COMPILER_ID)
+    include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
+  endif()
+  if (NOT _INCLUDED_FILE)
+    include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME}-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL)
+  endif ()
+endif()
+
+# load the system- and compiler specific files
+if(CMAKE_OBJCXX_COMPILER_ID)
+  include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_ID}-OBJCXX OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
+endif()
+if (NOT _INCLUDED_FILE)
+  include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL
+          RESULT_VARIABLE _INCLUDED_FILE)
+endif ()
+
+# load any compiler-wrapper specific information
+if (CMAKE_OBJCXX_COMPILER_WRAPPER)
+  __cmake_include_compiler_wrapper(OBJCXX)
+endif ()
+
+# We specify the compiler information in the system file for some
+# platforms, but this language may not have been enabled when the file
+# was first included.  Include it again to get the language info.
+# Remove this when all compiler info is removed from system files.
+if (NOT _INCLUDED_FILE)
+  include(Platform/${CMAKE_SYSTEM_NAME} OPTIONAL)
+endif ()
+
+if(CMAKE_OBJCXX_SIZEOF_DATA_PTR)
+  foreach(f ${CMAKE_OBJCXX_ABI_FILES})
+    include(${f})
+  endforeach()
+  unset(CMAKE_OBJCXX_ABI_FILES)
+endif()
+
+# This should be included before the _INIT variables are
+# used to initialize the cache.  Since the rule variables
+# have if blocks on them, users can still define them here.
+# But, it should still be after the platform file so changes can
+# be made to those values.
+
+if(CMAKE_USER_MAKE_RULES_OVERRIDE)
+  # Save the full path of the file so try_compile can use it.
+  include(${CMAKE_USER_MAKE_RULES_OVERRIDE} RESULT_VARIABLE _override)
+  set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_override}")
+endif()
+
+if(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX)
+  # Save the full path of the file so try_compile can use it.
+  include(${CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX} RESULT_VARIABLE _override)
+  set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJCXX "${_override}")
+endif()
+
+
+# Create a set of shared library variable specific to Objective-C++
+# For 90% of the systems, these are the same flags as the Objective-C versions
+# so if these are not set just copy the flags from the Objective-C version
+if(NOT CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS)
+  set(CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS})
+endif()
+
+if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIC)
+  set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIC ${CMAKE_OBJC_COMPILE_OPTIONS_PIC})
+endif()
+
+if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIE)
+  set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIE ${CMAKE_OBJC_COMPILE_OPTIONS_PIE})
+endif()
+if(NOT CMAKE_OBJCXX_LINK_OPTIONS_PIE)
+  set(CMAKE_OBJCXX_LINK_OPTIONS_PIE ${CMAKE_OBJC_LINK_OPTIONS_PIE})
+endif()
+if(NOT CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE)
+  set(CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE ${CMAKE_OBJC_LINK_OPTIONS_NO_PIE})
+endif()
+
+if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_DLL)
+  set(CMAKE_OBJCXX_COMPILE_OPTIONS_DLL ${CMAKE_OBJC_COMPILE_OPTIONS_DLL})
+endif()
+
+if(NOT CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS)
+  set(CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS})
+endif()
+
+if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS)
+  set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_OBJC_FLAGS})
+endif()
+
+if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG)
+  set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG})
+endif()
+
+if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP)
+  set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP})
+endif()
+
+if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG)
+  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG})
+endif()
+
+if(NOT DEFINED CMAKE_EXE_EXPORTS_OBJCXX_FLAG)
+  set(CMAKE_EXE_EXPORTS_OBJCXX_FLAG ${CMAKE_EXE_EXPORTS_OBJC_FLAG})
+endif()
+
+if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG)
+  set(CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_OBJC_FLAG})
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG)
+  set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG})
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP)
+  set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP})
+endif()
+
+if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG)
+  set(CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG})
+endif()
+
+if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH)
+  set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_OBJC_WITH_RUNTIME_PATH})
+endif()
+
+if(NOT CMAKE_INCLUDE_FLAG_OBJCXX)
+  set(CMAKE_INCLUDE_FLAG_OBJCXX ${CMAKE_INCLUDE_FLAG_C})
+endif()
+
+# for most systems a module is the same as a shared library
+# so unless the variable CMAKE_MODULE_EXISTS is set just
+# copy the values from the LIBRARY variables
+if(NOT CMAKE_MODULE_EXISTS)
+  set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS})
+  set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS})
+endif()
+
+# repeat for modules
+if(NOT CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS)
+  set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS})
+endif()
+
+if(NOT CMAKE_SHARED_MODULE_OBJCXX_FLAGS)
+  set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_OBJC_FLAGS})
+endif()
+
+# Initialize OBJCXX link type selection flags from OBJC versions.
+foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
+  if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS)
+    set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS
+      ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS})
+  endif()
+  if(NOT CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS)
+    set(CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS
+      ${CMAKE_${type}_LINK_DYNAMIC_OBJC_FLAGS})
+  endif()
+endforeach()
+
+# add the flags to the cache based
+# on the initial values computed in the platform/*.cmake files
+# use _INIT variables so that this only happens the first time
+# and you can set these flags in the cmake cache
+set(CMAKE_OBJCXX_FLAGS_INIT "$ENV{OBJCXXFLAGS} ${CMAKE_OBJCXX_FLAGS_INIT}")
+
+cmake_initialize_per_config_variable(CMAKE_OBJCXX_FLAGS "Flags used by the Objective-C++ compiler")
+
+if(CMAKE_OBJCXX_STANDARD_LIBRARIES_INIT)
+  set(CMAKE_OBJCXX_STANDARD_LIBRARIES "${CMAKE_OBJCXX_STANDARD_LIBRARIES_INIT}"
+    CACHE STRING "Libraries linked by default with all Objective-C++ applications.")
+  mark_as_advanced(CMAKE_OBJCXX_STANDARD_LIBRARIES)
+endif()
+
+include(CMakeCommonLanguageInclude)
+
+# now define the following rules:
+# CMAKE_OBJCXX_CREATE_SHARED_LIBRARY
+# CMAKE_OBJCXX_CREATE_SHARED_MODULE
+# CMAKE_OBJCXX_COMPILE_OBJECT
+# CMAKE_OBJCXX_LINK_EXECUTABLE
+
+# variables supplied by the generator at use time
+# <TARGET>
+# <TARGET_BASE> the target without the suffix
+# <OBJECTS>
+# <OBJECT>
+# <LINK_LIBRARIES>
+# <FLAGS>
+# <LINK_FLAGS>
+
+# Objective-C++ compiler information
+# <CMAKE_OBJCXX_COMPILER>
+# <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS>
+# <CMAKE_OBJCXX_SHARED_MODULE_CREATE_FLAGS>
+# <CMAKE_OBJCXX_LINK_FLAGS>
+
+# Static library tools
+# <CMAKE_AR>
+# <CMAKE_RANLIB>
+
+
+# create a shared Objective-C++ library
+if(NOT CMAKE_OBJCXX_CREATE_SHARED_LIBRARY)
+  set(CMAKE_OBJCXX_CREATE_SHARED_LIBRARY
+      "<CMAKE_OBJCXX_COMPILER> <CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
+endif()
+
+# create an Objective-C++ shared module copy the shared library rule by default
+if(NOT CMAKE_OBJCXX_CREATE_SHARED_MODULE)
+  set(CMAKE_OBJCXX_CREATE_SHARED_MODULE ${CMAKE_OBJCXX_CREATE_SHARED_LIBRARY})
+endif()
+
+
+# Create a static archive incrementally for large object file counts.
+# If CMAKE_OBJCXX_CREATE_STATIC_LIBRARY is set it will override these.
+if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_CREATE)
+  set(CMAKE_OBJCXX_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")
+endif()
+if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_APPEND)
+  set(CMAKE_OBJCXX_ARCHIVE_APPEND "<CMAKE_AR> q  <TARGET> <LINK_FLAGS> <OBJECTS>")
+endif()
+if(NOT DEFINED CMAKE_OBJCXX_ARCHIVE_FINISH)
+  set(CMAKE_OBJCXX_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
+endif()
+
+# compile an Objective-C++ file into an object file
+if(NOT CMAKE_OBJCXX_COMPILE_OBJECT)
+  set(CMAKE_OBJCXX_COMPILE_OBJECT
+    "<CMAKE_OBJCXX_COMPILER>  <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")
+endif()
+
+if(NOT CMAKE_OBJCXX_LINK_EXECUTABLE)
+  set(CMAKE_OBJCXX_LINK_EXECUTABLE
+    "<CMAKE_OBJCXX_COMPILER>  <FLAGS> <CMAKE_OBJCXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES>")
+endif()
+
+mark_as_advanced(
+CMAKE_VERBOSE_MAKEFILE
+)
+
+set(CMAKE_OBJCXX_INFORMATION_LOADED 1)
diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake
index 30659eb..0465515 100644
--- a/Modules/CMakeParseImplicitLinkInfo.cmake
+++ b/Modules/CMakeParseImplicitLinkInfo.cmake
@@ -47,6 +47,18 @@
       endif()
       separate_arguments(args NATIVE_COMMAND "${line}")
       list(GET args 0 cmd)
+    else()
+      #check to see if the link line is comma-separated instead of space separated
+      string(REGEX REPLACE "," " " line "${line}")
+      if("${line}" MATCHES "${linker_regex}" AND
+        NOT "${line}" MATCHES "${linker_exclude_regex}")
+        separate_arguments(args NATIVE_COMMAND "${line}")
+        list(GET args 0 cmd)
+        if("${cmd}" MATCHES "exec:")
+          # ibm xl sometimes has 'exec: ' in-front of the linker
+          list(GET args 1 cmd)
+        endif()
+      endif()
     endif()
     set(is_msvc 0)
     if("${cmd}" MATCHES "${linker_regex}")
diff --git a/Modules/CMakePlatformId.h.in b/Modules/CMakePlatformId.h.in
index 542a6fe..95465ce 100644
--- a/Modules/CMakePlatformId.h.in
+++ b/Modules/CMakePlatformId.h.in
@@ -174,6 +174,9 @@
 # elif defined(__ICC430__)
 #  define ARCHITECTURE_ID "MSP430"
 
+# elif defined(__ICCV850__)
+#  define ARCHITECTURE_ID "V850"
+
 # else /* unknown architecture */
 #  define ARCHITECTURE_ID ""
 # endif
diff --git a/Modules/CMakeRCInformation.cmake b/Modules/CMakeRCInformation.cmake
index 7bf6567..7c3a5ab 100644
--- a/Modules/CMakeRCInformation.cmake
+++ b/Modules/CMakeRCInformation.cmake
@@ -17,6 +17,17 @@
   ${CMAKE_ROOT}/Modules/Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME}.cmake)
 include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL)
 
+# This should be included before the _INIT variables are
+# used to initialize the cache.  Since the rule variables
+# have if blocks on them, users can still define them here.
+# But, it should still be after the platform file so changes can
+# be made to those values.
+if(CMAKE_USER_MAKE_RULES_OVERRIDE)
+  # Save the full path of the file so try_compile can use it.
+  include(${CMAKE_USER_MAKE_RULES_OVERRIDE} RESULT_VARIABLE _override)
+  set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_override}")
+endif()
+
 set(CMAKE_RC_FLAGS_INIT "$ENV{RCFLAGS} ${CMAKE_RC_FLAGS_INIT}")
 
 cmake_initialize_per_config_variable(CMAKE_RC_FLAGS "Flags for Windows Resource Compiler")
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index f6510b9..4f1d4f0 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -18,16 +18,25 @@
 endif()
 
 set(CMAKE_INCLUDE_FLAG_Swift "-I ")
-if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows AND NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+  set(CMAKE_SHARED_LIBRARY_SONAME_Swift_FLAG "-Xlinker -install_name -Xlinker ")
+elseif(NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
   set(CMAKE_SHARED_LIBRARY_SONAME_Swift_FLAG "-Xlinker -soname -Xlinker ")
 endif()
 
 set(CMAKE_Swift_COMPILE_OPTIONS_TARGET "-target ")
-set(CMAKE_Swift_COMPILER_ARG1 -frontend)
+set(CMAKE_Swift_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN "-tools-directory ")
+# NOTE(compnerd) the `-sdk` support is not yet ready in the compiler; when that
+# is fully working, we should be able to enable this.
+# set(CMAKE_Swift_COMPILE_OPTIONS_SYSROOT "-sdk ")
+# NOTE(compnerd) do not setup `-frontend` as we use the compiler as the driver
+# during the link phase and use that to drive the compilation
+set(CMAKE_Swift_COMPILER_ARG1 "")
 set(CMAKE_Swift_DEFINE_FLAG -D)
 set(CMAKE_Swift_FRAMEWORK_SEARCH_FLAG "-F ")
 set(CMAKE_Swift_LIBRARY_PATH_FLAG "-L ")
 set(CMAKE_Swift_LIBRARY_PATH_TERMINATOR "")
+set(CMAKE_Swift_LINK_LIBRARY_FLAG "-l")
 set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ")
 set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @)
 
@@ -47,6 +56,8 @@
 set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
 set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
 
+cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags")
+
 # NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step
 if(NOT CMAKE_Swift_COMPILE_OBJECT)
   set(CMAKE_Swift_COMPILE_OBJECT ":")
@@ -56,12 +67,12 @@
   cmake_host_system_information(RESULT CMAKE_Swift_NUM_THREADS QUERY NUMBER_OF_LOGICAL_CORES)
 endif()
 
+if(CMAKE_SYSTEM_NAME STREQUAL Windows)
+  set(CMAKE_Swift_IMPLIB_LINKER_FLAGS "-Xlinker -implib:<TARGET_IMPLIB>")
+endif()
+
 if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY)
-  if(CMAKE_Swift_COMPILER_TARGET)
-    set(CMAKE_Swift_CREATE_SHARED_LIBRARY "${CMAKE_Swift_COMPILER} -target <CMAKE_Swift_COMPILER_TARGET> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_SONAME> <LINK_LIBRARIES>")
-  else()
-    set(CMAKE_Swift_CREATE_SHARED_LIBRARY "${CMAKE_Swift_COMPILER} -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_SONAME> <LINK_LIBRARIES>")
-  endif()
+  set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
 endif()
 
 if(NOT CMAKE_Swift_CREATE_SHARED_MODULE)
@@ -69,19 +80,11 @@
 endif()
 
 if(NOT CMAKE_Swift_LINK_EXECUTABLE)
-  if(CMAKE_Swift_COMPILER_TARGET)
-    set(CMAKE_Swift_LINK_EXECUTABLE "${CMAKE_Swift_COMPILER} -target <CMAKE_Swift_COMPILER_TARGET> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
-  else()
-    set(CMAKE_Swift_LINK_EXECUTABLE "${CMAKE_Swift_COMPILER} -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
-  endif()
+  set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
 endif()
 
 if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY)
-  if(CMAKE_Swift_COMPILER_TARGET)
-    set(CMAKE_Swift_CREATE_STATIC_LIBRARY "${CMAKE_Swift_COMPILER} -target <CMAKE_Swift_COMPILER_TARGET> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
-  else()
-    set(CMAKE_Swift_CREATE_STATIC_LIBRARY "${CMAKE_Swift_COMPILER} -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
-  endif()
+  set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -output-file-map <SWIFT_OUTPUT_FILE_MAP> -incremental -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
 
   set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>")
   set(CMAKE_Swift_ARCHIVE_FINISH "")
diff --git a/Modules/CMakeTestOBJCCompiler.cmake b/Modules/CMakeTestOBJCCompiler.cmake
new file mode 100644
index 0000000..0030683
--- /dev/null
+++ b/Modules/CMakeTestOBJCCompiler.cmake
@@ -0,0 +1,94 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+if(CMAKE_OBJC_COMPILER_FORCED)
+  # The compiler configuration was forced by the user.
+  # Assume the user has configured all compiler information.
+  set(CMAKE_OBJC_COMPILER_WORKS TRUE)
+  return()
+endif()
+
+include(CMakeTestCompilerCommon)
+
+# work around enforced code signing and / or missing exectuable target type
+set(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE})
+if(_CMAKE_FEATURE_DETECTION_TARGET_TYPE)
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_CMAKE_FEATURE_DETECTION_TARGET_TYPE})
+endif()
+
+# Remove any cached result from an older CMake version.
+# We now store this in CMakeCCompiler.cmake.
+unset(CMAKE_OBJC_COMPILER_WORKS CACHE)
+
+# This file is used by EnableLanguage in cmGlobalGenerator to
+# determine that that selected Objective-C compiler can actually compile
+# and link the most basic of programs.   If not, a fatal error
+# is set and cmake stops processing commands and will not generate
+# any makefiles or projects.
+if(NOT CMAKE_OBJC_COMPILER_WORKS)
+  PrintTestCompilerStatus("OBJC" "")
+  __TestCompiler_setTryCompileTargetType()
+  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m
+    "#ifdef __cplusplus\n"
+    "# error \"The CMAKE_OBJC_COMPILER is set to a C++ compiler\"\n"
+    "#endif\n"
+    "#ifndef __OBJC__\n"
+    "# error \"The CMAKE_OBJC_COMPILER is not an Objective-C compiler\"\n"
+    "#endif\n"
+    "int main(int argc, char* argv[])\n"
+    "{ (void)argv; return argc-1;}\n")
+  try_compile(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_BINARY_DIR}
+    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m
+    OUTPUT_VARIABLE __CMAKE_OBJC_COMPILER_OUTPUT)
+  # Move result from cache to normal variable.
+  set(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_OBJC_COMPILER_WORKS})
+  unset(CMAKE_OBJC_COMPILER_WORKS CACHE)
+  set(OBJC_TEST_WAS_RUN 1)
+  __TestCompiler_restoreTryCompileTargetType()
+endif()
+
+if(NOT CMAKE_OBJC_COMPILER_WORKS)
+  PrintTestCompilerStatus("OBJC" " -- broken")
+  file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+    "Determining if the Objective-C compiler works failed with "
+    "the following output:\n${__CMAKE_OBJC_COMPILER_OUTPUT}\n\n")
+  string(REPLACE "\n" "\n  " _output "${__CMAKE_OBJC_COMPILER_OUTPUT}")
+  message(FATAL_ERROR "The Objective-C compiler\n  \"${CMAKE_OBJC_COMPILER}\"\n"
+    "is not able to compile a simple test program.\nIt fails "
+    "with the following output:\n  ${_output}\n\n"
+    "CMake will not be able to correctly generate this project.")
+else()
+  if(OBJC_TEST_WAS_RUN)
+    PrintTestCompilerStatus("OBJC" " -- works")
+    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+      "Determining if the Objective-C compiler works passed with "
+      "the following output:\n${__CMAKE_OBJC_COMPILER_OUTPUT}\n\n")
+  endif()
+
+  # Try to identify the ABI and configure it into CMakeOBJCCompiler.cmake
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake)
+  CMAKE_DETERMINE_COMPILER_ABI(OBJC ${CMAKE_ROOT}/Modules/CMakeOBJCCompilerABI.m)
+  # Try to identify the compiler features
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake)
+  CMAKE_DETERMINE_COMPILE_FEATURES(OBJC)
+
+  # Re-configure to save learned information.
+  configure_file(
+    ${CMAKE_ROOT}/Modules/CMakeOBJCCompiler.cmake.in
+    ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake
+    @ONLY
+    )
+  include(${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCCompiler.cmake)
+
+  if(CMAKE_OBJC_SIZEOF_DATA_PTR)
+    foreach(f ${CMAKE_OBJC_ABI_FILES})
+      include(${f})
+    endforeach()
+    unset(CMAKE_OBJC_ABI_FILES)
+  endif()
+endif()
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE ${__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE})
+unset(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE)
+unset(__CMAKE_OBJC_COMPILER_OUTPUT)
diff --git a/Modules/CMakeTestOBJCXXCompiler.cmake b/Modules/CMakeTestOBJCXXCompiler.cmake
new file mode 100644
index 0000000..bcce2f1
--- /dev/null
+++ b/Modules/CMakeTestOBJCXXCompiler.cmake
@@ -0,0 +1,93 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+if(CMAKE_OBJCXX_COMPILER_FORCED)
+  # The compiler configuration was forced by the user.
+  # Assume the user has configured all compiler information.
+  set(CMAKE_OBJCXX_COMPILER_WORKS TRUE)
+  return()
+endif()
+
+include(CMakeTestCompilerCommon)
+
+# work around enforced code signing and / or missing exectuable target type
+set(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE})
+if(_CMAKE_FEATURE_DETECTION_TARGET_TYPE)
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_CMAKE_FEATURE_DETECTION_TARGET_TYPE})
+endif()
+
+# Remove any cached result from an older CMake version.
+# We now store this in CMakeOBJCXXCompiler.cmake.
+unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE)
+
+# This file is used by EnableLanguage in cmGlobalGenerator to
+# determine that the selected Objective-C++ compiler can actually compile
+# and link the most basic of programs.   If not, a fatal error
+# is set and cmake stops processing commands and will not generate
+# any makefiles or projects.
+if(NOT CMAKE_OBJCXX_COMPILER_WORKS)
+  PrintTestCompilerStatus("OBJCXX" "")
+  __TestCompiler_setTryCompileTargetType()
+  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm
+    "#ifndef __cplusplus\n"
+    "# error \"The CMAKE_OBJCXX_COMPILER is set to a C compiler\"\n"
+    "#endif\n"
+    "#ifndef __OBJC__\n"
+    "# error \"The CMAKE_OBJCXX_COMPILER is not an Objective-C++ compiler\"\n"
+    "#endif\n"
+    "int main(){return 0;}\n")
+  try_compile(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_BINARY_DIR}
+    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm
+    OUTPUT_VARIABLE __CMAKE_OBJCXX_COMPILER_OUTPUT)
+  # Move result from cache to normal variable.
+  set(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_OBJCXX_COMPILER_WORKS})
+  unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE)
+  set(OBJCXX_TEST_WAS_RUN 1)
+  __TestCompiler_restoreTryCompileTargetType()
+endif()
+
+if(NOT CMAKE_OBJCXX_COMPILER_WORKS)
+  PrintTestCompilerStatus("OBJCXX" " -- broken")
+  file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+    "Determining if the Objective-C++ compiler works failed with "
+    "the following output:\n${__CMAKE_OBJCXX_COMPILER_OUTPUT}\n\n")
+  string(REPLACE "\n" "\n  " _output "${__CMAKE_OBJCXX_COMPILER_OUTPUT}")
+  message(FATAL_ERROR "The Objective-C++ compiler\n  \"${CMAKE_OBJCXX_COMPILER}\"\n"
+    "is not able to compile a simple test program.\nIt fails "
+    "with the following output:\n  ${_output}\n\n"
+    "CMake will not be able to correctly generate this project.")
+else()
+  if(OBJCXX_TEST_WAS_RUN)
+    PrintTestCompilerStatus("OBJCXX" " -- works")
+    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+      "Determining if the Objective-C++ compiler works passed with "
+      "the following output:\n${__CMAKE_OBJCXX_COMPILER_OUTPUT}\n\n")
+  endif()
+
+  # Try to identify the ABI and configure it into CMakeOBJCXXCompiler.cmake
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake)
+  CMAKE_DETERMINE_COMPILER_ABI(OBJCXX ${CMAKE_ROOT}/Modules/CMakeOBJCXXCompilerABI.mm)
+  # Try to identify the compiler features
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake)
+  CMAKE_DETERMINE_COMPILE_FEATURES(OBJCXX)
+
+  # Re-configure to save learned information.
+  configure_file(
+    ${CMAKE_ROOT}/Modules/CMakeOBJCXXCompiler.cmake.in
+    ${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake
+    @ONLY
+    )
+  include(${CMAKE_PLATFORM_INFO_DIR}/CMakeOBJCXXCompiler.cmake)
+
+  if(CMAKE_OBJCXX_SIZEOF_DATA_PTR)
+    foreach(f ${CMAKE_OBJCXX_ABI_FILES})
+      include(${f})
+    endforeach()
+    unset(CMAKE_OBJCXX_ABI_FILES)
+  endif()
+endif()
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE ${__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE})
+unset(__CMAKE_SAVED_TRY_COMPILE_TARGET_TYPE)
+unset(__CMAKE_OBJCXX_COMPILER_OUTPUT)
diff --git a/Modules/CPack.STGZ_Header.sh.in b/Modules/CPack.STGZ_Header.sh.in
deleted file mode 100755
index 70f63d2..0000000
--- a/Modules/CPack.STGZ_Header.sh.in
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/bin/sh
-
-# Display usage
-cpack_usage()
-{
-  cat <<EOF
-Usage: $0 [options]
-Options: [defaults in brackets after descriptions]
-  --help            print this message
-  --version         print cmake installer version
-  --prefix=dir      directory in which to install
-  --include-subdir  include the @CPACK_PACKAGE_FILE_NAME@ subdirectory
-  --exclude-subdir  exclude the @CPACK_PACKAGE_FILE_NAME@ subdirectory
-  --skip-license    accept license
-EOF
-  exit 1
-}
-
-cpack_echo_exit()
-{
-  echo $1
-  exit 1
-}
-
-# Display version
-cpack_version()
-{
-  echo "@CPACK_PACKAGE_NAME@ Installer Version: @CPACK_PACKAGE_VERSION@, Copyright (c) @CPACK_PACKAGE_VENDOR@"
-}
-
-# Helper function to fix windows paths.
-cpack_fix_slashes ()
-{
-  echo "$1" | sed 's/\\/\//g'
-}
-
-interactive=TRUE
-cpack_skip_license=FALSE
-cpack_include_subdir=""
-for a in "$@CPACK_AT_SIGN@"; do
-  if echo $a | grep "^--prefix=" > /dev/null 2> /dev/null; then
-    cpack_prefix_dir=`echo $a | sed "s/^--prefix=//"`
-    cpack_prefix_dir=`cpack_fix_slashes "${cpack_prefix_dir}"`
-  fi
-  if echo $a | grep "^--help" > /dev/null 2> /dev/null; then
-    cpack_usage 
-  fi
-  if echo $a | grep "^--version" > /dev/null 2> /dev/null; then
-    cpack_version 
-    exit 2
-  fi
-  if echo $a | grep "^--include-subdir" > /dev/null 2> /dev/null; then
-    cpack_include_subdir=TRUE
-  fi
-  if echo $a | grep "^--exclude-subdir" > /dev/null 2> /dev/null; then
-    cpack_include_subdir=FALSE
-  fi
-  if echo $a | grep "^--skip-license" > /dev/null 2> /dev/null; then
-    cpack_skip_license=TRUE
-  fi
-done
-
-if [ "x${cpack_include_subdir}x" != "xx" -o "x${cpack_skip_license}x" = "xTRUEx" ]
-then
-  interactive=FALSE
-fi
-
-cpack_version
-echo "This is a self-extracting archive."
-toplevel="`pwd`"
-if [ "x${cpack_prefix_dir}x" != "xx" ]
-then
-  toplevel="${cpack_prefix_dir}"
-fi
-
-echo "The archive will be extracted to: ${toplevel}"
-
-if [ "x${interactive}x" = "xTRUEx" ]
-then
-  echo ""
-  echo "If you want to stop extracting, please press <ctrl-C>."
-
-  if [ "x${cpack_skip_license}x" != "xTRUEx" ]
-  then
-    more << '____cpack__here_doc____'
-@CPACK_RESOURCE_FILE_LICENSE_CONTENT@
-____cpack__here_doc____
-    echo
-    echo "Do you accept the license? [yN]: "
-    read line leftover
-    case ${line} in
-      y* | Y*)
-        cpack_license_accepted=TRUE;;
-      *)
-        echo "License not accepted. Exiting ..."
-        exit 1;;
-    esac
-  fi
-
-  if [ "x${cpack_include_subdir}x" = "xx" ]
-  then
-    echo "By default the @CPACK_PACKAGE_NAME@ will be installed in:"
-    echo "  \"${toplevel}/@CPACK_PACKAGE_FILE_NAME@\""
-    echo "Do you want to include the subdirectory @CPACK_PACKAGE_FILE_NAME@?"
-    echo "Saying no will install in: \"${toplevel}\" [Yn]: "
-    read line leftover
-    cpack_include_subdir=TRUE
-    case ${line} in
-      n* | N*)
-        cpack_include_subdir=FALSE
-    esac
-  fi
-fi
-
-if [ "x${cpack_include_subdir}x" = "xTRUEx" ]
-then
-  toplevel="${toplevel}/@CPACK_PACKAGE_FILE_NAME@"
-  mkdir -p "${toplevel}"
-fi
-echo
-echo "Using target directory: ${toplevel}"
-echo "Extracting, please wait..."
-echo ""
-
-# take the archive portion of this file and pipe it to tar
-# the NUMERIC parameter in this command should be one more
-# than the number of lines in this header file
-# there are tails which don't understand the "-n" argument, e.g. on SunOS
-# OTOH there are tails which complain when not using the "-n" argument (e.g. GNU)
-# so at first try to tail some file to see if tail fails if used with "-n"
-# if so, don't use "-n"
-use_new_tail_syntax="-n"
-tail $use_new_tail_syntax +1 "$0" > /dev/null 2> /dev/null || use_new_tail_syntax=""
-
-extractor="pax -r"
-command -v pax > /dev/null 2> /dev/null || extractor="tar xf -"
-
-tail $use_new_tail_syntax +###CPACK_HEADER_LENGTH### "$0" | gunzip | (cd "${toplevel}" && ${extractor}) || cpack_echo_exit "Problem unpacking the @CPACK_PACKAGE_FILE_NAME@"
-
-echo "Unpacking finished successfully"
-
-exit 0
-#-----------------------------------------------------------
-#      Start of TAR.GZ file
-#-----------------------------------------------------------;
-
diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake
index c9008db..1809846 100644
--- a/Modules/CPack.cmake
+++ b/Modules/CPack.cmake
@@ -301,6 +301,13 @@
   project.  Defaults to the value of :variable:`CMAKE_GENERATOR`.  Few users
   will want to change this setting.
 
+.. variable:: CPACK_INSTALL_CMAKE_CONFIGURATIONS
+
+  Specify the project configurations to be packaged (e.g. ``Debug``, ``Release``,
+  etc.). When the CMake project uses a multi-configuration generator such as Xcode
+  or Visual Studio, this option can be used to specify what configurations
+  to include in the package.
+
 .. variable:: CPACK_INSTALL_CMAKE_PROJECTS
 
   List of four values that specify what project to install.  The four values
@@ -325,7 +332,21 @@
 
 .. variable:: CPACK_INSTALL_COMMANDS
 
-  Extra commands to install components.
+  Extra commands to install components.  The environment variable
+  ``CMAKE_INSTALL_PREFIX`` is set to the temporary install directory
+  during execution.
+
+.. variable:: CPACK_INSTALL_SCRIPTS
+
+  Extra CMake scripts executed by CPack during its local staging
+  installation, which is done right before packaging the files.
+  The scripts are not called by a standalone install (e.g.: ``make install``).
+  For every script, the following variables will be set:
+  :variable:`CMAKE_CURRENT_SOURCE_DIR`, :variable:`CMAKE_CURRENT_BINARY_DIR`
+  and :variable:`CMAKE_INSTALL_PREFIX` (which is set to the staging install
+  directory).  The singular form ``CMAKE_INSTALL_SCRIPT`` is supported as
+  an alternative variable for historical reasons, but its value is ignored if
+  ``CMAKE_INSTALL_SCRIPTS`` is set and a warning will be issued.
 
 .. variable:: CPACK_INSTALLED_DIRECTORIES
 
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index 2cccd09..5c9079d 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -5,26 +5,38 @@
 CheckCXXSymbolExists
 --------------------
 
-Check if a symbol exists as a function, variable, or macro in C++
+Check if a symbol exists as a function, variable, or macro in ``C++``.
 
-.. command:: CHECK_CXX_SYMBOL_EXISTS
+.. command:: check_cxx_symbol_exists
 
   .. code-block:: cmake
 
-    CHECK_CXX_SYMBOL_EXISTS(<symbol> <files> <variable>)
+    check_cxx_symbol_exists(<symbol> <files> <variable>)
 
   Check that the ``<symbol>`` is available after including given header
   ``<files>`` and store the result in a ``<variable>``.  Specify the list of
   files in one argument as a semicolon-separated list.
-  ``CHECK_CXX_SYMBOL_EXISTS()`` can be used to check in C++ files, as
-  opposed to ``CHECK_SYMBOL_EXISTS()``, which works only for ``C``.
+  ``check_cxx_symbol_exists()`` can be used to check for symbols as seen by
+  the C++ compiler, as opposed to :command:`check_symbol_exists`, which always
+  uses the ``C`` compiler.
 
   If the header files define the symbol as a macro it is considered
   available and assumed to work.  If the header files declare the symbol
   as a function or variable then the symbol must also be available for
-  linking.  If the symbol is a type or enum value it will not be
-  recognized (consider using :module:`CheckTypeSize`
-  or :module:`CheckCXXSourceCompiles`).
+  linking.  If the symbol is a type, enum value, or C++ template it will
+  not be recognized: consider using the :module:`CheckTypeSize`
+  or :module:`CheckCXXSourceCompiles` module instead.
+
+.. note::
+
+  This command is unreliable when ``<symbol>`` is (potentially) an overloaded
+  function. Since there is no reliable way to predict whether a given function
+  in the system environment may be defined as an overloaded function or may be
+  an overloaded function on other systems or will become so in the future, it
+  is generally advised to use the :module:`CheckCXXSourceCompiles` module for
+  checking any function symbol (unless somehow you surely know the checked
+  function is not overloaded on other systems or will not be so in the
+  future).
 
 The following variables may be set before calling this macro to modify
 the way the check is run:
@@ -43,6 +55,17 @@
   command. See policy :policy:`CMP0075`.
 ``CMAKE_REQUIRED_QUIET``
   execute quietly without messages.
+
+For example:
+
+.. code-block:: cmake
+
+  include(CheckCXXSymbolExists)
+
+  # Check for macro SEEK_SET
+  check_cxx_symbol_exists(SEEK_SET "cstdio" HAVE_SEEK_SET)
+  # Check for function std::fopen
+  check_cxx_symbol_exists(std::fopen "cstdio" HAVE_STD_FOPEN)
 #]=======================================================================]
 
 include_guard(GLOBAL)
diff --git a/Modules/CheckOBJCCompilerFlag.cmake b/Modules/CheckOBJCCompilerFlag.cmake
new file mode 100644
index 0000000..1d975da
--- /dev/null
+++ b/Modules/CheckOBJCCompilerFlag.cmake
@@ -0,0 +1,64 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCCompilerFlag
+---------------------
+
+Check whether the Objective-C compiler supports a given flag.
+
+.. command:: check_objc_compiler_flag
+
+  .. code-block:: cmake
+
+    check_objc_compiler_flag(<flag> <var>)
+
+  Check that the ``<flag>`` is accepted by the compiler without
+  a diagnostic.  Stores the result in an internal cache entry
+  named ``<var>``.
+
+This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
+and calls the ``check_objc_source_compiles`` macro from the
+:module:`CheckOBJCSourceCompiles` module.  See documentation of that
+module for a listing of variables that can otherwise modify the build.
+
+A positive result from this check indicates only that the compiler did not
+issue a diagnostic message when given the flag.  Whether the flag has any
+effect or even a specific one is beyond the scope of this module.
+
+.. note::
+  Since the :command:`try_compile` command forwards flags from variables
+  like :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
+  in such variables may cause a false negative for this check.
+#]=======================================================================]
+
+include_guard(GLOBAL)
+include(CheckOBJCSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+macro (CHECK_OBJC_COMPILER_FLAG _FLAG _RESULT)
+  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+
+   # Normalize locale during test compilation.
+  set(_CheckOBJCCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
+  foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS})
+    set(_CheckOBJCCompilerFlag_SAVED_${v} "$ENV{${v}}")
+    set(ENV{${v}} OBJC)
+  endforeach()
+  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCCompilerFlag_COMMON_PATTERNS)
+  CHECK_OBJC_SOURCE_COMPILES("#ifndef __OBJC__\n#  error \"Not an Objective-C compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT}
+    # Some compilers do not fail with a bad flag
+    FAIL_REGEX "command line option .* is valid for .* but not for Objective-C" # GNU
+    FAIL_REGEX "argument unused during compilation: .*" # Clang
+    ${_CheckOBJCCompilerFlag_COMMON_PATTERNS}
+    )
+  foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS})
+    set(ENV{${v}} ${_CheckOBJCCompilerFlag_SAVED_${v}})
+    unset(_CheckOBJCCompilerFlag_SAVED_${v})
+  endforeach()
+  unset(_CheckOBJCCompilerFlag_LOCALE_VARS)
+  unset(_CheckOBJCCompilerFlag_COMMON_PATTERNS)
+
+  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+endmacro ()
diff --git a/Modules/CheckOBJCSourceCompiles.cmake b/Modules/CheckOBJCSourceCompiles.cmake
new file mode 100644
index 0000000..a4676ad
--- /dev/null
+++ b/Modules/CheckOBJCSourceCompiles.cmake
@@ -0,0 +1,145 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCSourceCompiles
+-----------------------
+
+Check if given Objective-C source compiles and links into an executable.
+
+.. command:: check_objc_source_compiles
+
+  .. code-block:: cmake
+
+    check_objc_source_compiles(<code> <resultVar>
+                               [FAIL_REGEX <regex1> [<regex2>...]])
+
+  Check that the source supplied in ``<code>`` can be compiled as a Objectie-C source
+  file and linked as an executable (so it must contain at least a ``main()``
+  function). The result will be stored in the internal cache variable specified
+  by ``<resultVar>``, with a boolean true value for success and boolean false
+  for failure. If ``FAIL_REGEX`` is provided, then failure is determined by
+  checking if anything in the output matches any of the specified regular
+  expressions.
+
+  The underlying check is performed by the :command:`try_compile` command. The
+  compile and link commands can be influenced by setting any of the following
+  variables prior to calling ``check_objc_source_compiles()``:
+
+  ``CMAKE_REQUIRED_FLAGS``
+    Additional flags to pass to the compiler. Note that the contents of
+    :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
+    configuration-specific variable are automatically added to the compiler
+    command before the contents of ``CMAKE_REQUIRED_FLAGS``.
+
+  ``CMAKE_REQUIRED_DEFINITIONS``
+    A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
+    ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
+    ``<resultVar>`` will also be added automatically.
+
+  ``CMAKE_REQUIRED_INCLUDES``
+    A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
+    the compiler. These will be the only header search paths used by
+    ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
+    directory property will be ignored.
+
+  ``CMAKE_REQUIRED_LINK_OPTIONS``
+    A :ref:`;-list <CMake Language Lists>` of options to add to the link
+    command (see :command:`try_compile` for further details).
+
+  ``CMAKE_REQUIRED_LIBRARIES``
+    A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
+    command. These can be the name of system libraries or they can be
+    :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
+    further details).
+
+  ``CMAKE_REQUIRED_QUIET``
+    If this variable evaluates to a boolean true value, all status messages
+    associated with the check will be suppressed.
+
+  The check is only performed once, with the result cached in the variable
+  named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
+  value rather than performing the check again, even if the ``<code>`` changes.
+  In order to force the check to be re-evaluated, the variable named by
+  ``<resultVar>`` must be manually removed from the cache.
+
+#]=======================================================================]
+
+include_guard(GLOBAL)
+
+macro(CHECK_OBJC_SOURCE_COMPILES SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(_FAIL_REGEX)
+    set(_key)
+    foreach(arg ${ARGN})
+      if("${arg}" MATCHES "^(FAIL_REGEX)$")
+        set(_key "${arg}")
+      elseif(_key)
+        list(APPEND _${_key} "${arg}")
+      else()
+        message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
+      endif()
+    endforeach()
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LINK_OPTIONS)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS
+        LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS})
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS)
+    endif()
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_compile(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS}
+      ${CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      "${CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+
+    foreach(_regex ${_FAIL_REGEX})
+      if("${OUTPUT}" MATCHES "${_regex}")
+        set(${VAR} 0)
+      endif()
+    endforeach()
+
+    if(${VAR})
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Objective-C SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Objective-C SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/Modules/CheckOBJCSourceRuns.cmake b/Modules/CheckOBJCSourceRuns.cmake
new file mode 100644
index 0000000..00a1ebd
--- /dev/null
+++ b/Modules/CheckOBJCSourceRuns.cmake
@@ -0,0 +1,145 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCSourceRuns
+-------------------
+
+Check if given Objective-C source compiles and links into an executable and can
+subsequently be run.
+
+.. command:: check_objc_source_runs
+
+  .. code-block:: cmake
+
+    check_objc_source_runs(<code> <resultVar>)
+
+  Check that the source supplied in ``<code>`` can be compiled as a Objective-C source
+  file, linked as an executable and then run. The ``<code>`` must contain at
+  least a ``main()`` function. If the ``<code>`` could be built and run
+  successfully, the internal cache variable specified by ``<resultVar>`` will
+  be set to 1, otherwise it will be set to an value that evaluates to boolean
+  false (e.g. an empty string or an error message).
+
+  The underlying check is performed by the :command:`try_run` command. The
+  compile and link commands can be influenced by setting any of the following
+  variables prior to calling ``check_objc_source_runs()``:
+
+  ``CMAKE_REQUIRED_FLAGS``
+    Additional flags to pass to the compiler. Note that the contents of
+    :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
+    configuration-specific variable are automatically added to the compiler
+    command before the contents of ``CMAKE_REQUIRED_FLAGS``.
+
+  ``CMAKE_REQUIRED_DEFINITIONS``
+    A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
+    ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
+    ``<resultVar>`` will also be added automatically.
+
+  ``CMAKE_REQUIRED_INCLUDES``
+    A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
+    the compiler. These will be the only header search paths used by
+    ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
+    directory property will be ignored.
+
+  ``CMAKE_REQUIRED_LINK_OPTIONS``
+    A :ref:`;-list <CMake Language Lists>` of options to add to the link
+    command (see :command:`try_run` for further details).
+
+  ``CMAKE_REQUIRED_LIBRARIES``
+    A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
+    command. These can be the name of system libraries or they can be
+    :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
+    further details).
+
+  ``CMAKE_REQUIRED_QUIET``
+    If this variable evaluates to a boolean true value, all status messages
+    associated with the check will be suppressed.
+
+  The check is only performed once, with the result cached in the variable
+  named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
+  value rather than performing the check again, even if the ``<code>`` changes.
+  In order to force the check to be re-evaluated, the variable named by
+  ``<resultVar>`` must be manually removed from the cache.
+
+#]=======================================================================]
+
+include_guard(GLOBAL)
+
+macro(CHECK_OBJC_SOURCE_RUNS SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LINK_OPTIONS)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS
+        LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS})
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS)
+    endif()
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_run(${VAR}_EXITCODE ${VAR}_COMPILED
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.m
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_OBJC_SOURCE_COMPILES_ADD_LINK_OPTIONS}
+      ${CHECK_OBJC_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+      "${CHECK_OBJC_SOURCE_COMPILES_ADD_INCLUDES}"
+      COMPILE_OUTPUT_VARIABLE OUTPUT
+      RUN_OUTPUT_VARIABLE RUN_OUTPUT)
+    # if it did not compile make the return value fail code of 1
+    if(NOT ${VAR}_COMPILED)
+      set(${VAR}_EXITCODE 1)
+    endif()
+    # if the return value was 0 then it worked
+    if("${${VAR}_EXITCODE}" EQUAL 0)
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Objective-C SOURCE FILE Test ${VAR} succeeded with the following compile output:\n"
+        "${OUTPUT}\n"
+        "...and run output:\n"
+        "${RUN_OUTPUT}\n"
+        "Return value: ${${VAR}}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(CMAKE_CROSSCOMPILING AND "${${VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+        set(${VAR} "${${VAR}_EXITCODE}")
+      else()
+        set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      endif()
+
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Objective-C SOURCE FILE Test ${VAR} failed with the following compile output:\n"
+        "${OUTPUT}\n"
+        "...and run output:\n"
+        "${RUN_OUTPUT}\n"
+        "Return value: ${${VAR}_EXITCODE}\n"
+        "Source file was:\n${SOURCE}\n")
+
+    endif()
+  endif()
+endmacro()
diff --git a/Modules/CheckOBJCXXCompilerFlag.cmake b/Modules/CheckOBJCXXCompilerFlag.cmake
new file mode 100644
index 0000000..c32741b
--- /dev/null
+++ b/Modules/CheckOBJCXXCompilerFlag.cmake
@@ -0,0 +1,64 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCXXCompilerFlag
+-----------------------
+
+Check whether the Objective-C++ compiler supports a given flag.
+
+.. command:: check_objcxx_compiler_flag
+
+  .. code-block:: cmake
+
+    check_objcxx_compiler_flag(<flag> <var>)
+
+  Check that the ``<flag>`` is accepted by the compiler without
+  a diagnostic.  Stores the result in an internal cache entry
+  named ``<var>``.
+
+This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
+and calls the ``check_objcxx_source_compiles`` macro from the
+:module:`CheckOBJCXXSourceCompiles` module.  See documentation of that
+module for a listing of variables that can otherwise modify the build.
+
+A positive result from this check indicates only that the compiler did not
+issue a diagnostic message when given the flag.  Whether the flag has any
+effect or even a specific one is beyond the scope of this module.
+
+.. note::
+  Since the :command:`try_compile` command forwards flags from variables
+  like :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
+  in such variables may cause a false negative for this check.
+#]=======================================================================]
+
+include_guard(GLOBAL)
+include(CheckOBJCXXSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+macro (CHECK_OBJCXX_COMPILER_FLAG _FLAG _RESULT)
+  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+
+  # Normalize locale during test compilation.
+  set(_CheckOBJCXXCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
+  foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS})
+    set(_CheckOBJCXXCompilerFlag_SAVED_${v} "$ENV{${v}}")
+    set(ENV{${v}} OBJCXX)
+  endforeach()
+  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS)
+  CHECK_OBJCXX_SOURCE_COMPILES("#ifndef __OBJC__\n#  error \"Not an Objective-C++ compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT}
+    # Some compilers do not fail with a bad flag
+    FAIL_REGEX "command line option .* is valid for .* but not for Objective-C\\\\+\\\\+" # GNU
+    FAIL_REGEX "argument unused during compilation: .*" # Clang
+    ${_CheckOBJCXXCompilerFlag_COMMON_PATTERNS}
+    )
+  foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS})
+    set(ENV{${v}} ${_CheckOBJCXXCompilerFlag_SAVED_${v}})
+    unset(_CheckOBJCXXCompilerFlag_SAVED_${v})
+  endforeach()
+  unset(_CheckOBJCXXCompilerFlag_LOCALE_VARS)
+  unset(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS)
+
+  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+endmacro ()
diff --git a/Modules/CheckOBJCXXSourceCompiles.cmake b/Modules/CheckOBJCXXSourceCompiles.cmake
new file mode 100644
index 0000000..4c0fdd0
--- /dev/null
+++ b/Modules/CheckOBJCXXSourceCompiles.cmake
@@ -0,0 +1,146 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCXXSourceCompiles
+-------------------------
+
+Check if given Objective-C++ source compiles and links into an executable.
+
+.. command:: check_objcxx_source_compiles
+
+  .. code-block:: cmake
+
+    check_objcxx_source_compiles(<code> <resultVar>
+                                 [FAIL_REGEX <regex1> [<regex2>...]])
+
+  Check that the source supplied in ``<code>`` can be compiled as a Objective-C++ source
+  file and linked as an executable (so it must contain at least a ``main()``
+  function). The result will be stored in the internal cache variable specified
+  by ``<resultVar>``, with a boolean true value for success and boolean false
+  for failure. If ``FAIL_REGEX`` is provided, then failure is determined by
+  checking if anything in the output matches any of the specified regular
+  expressions.
+
+  The underlying check is performed by the :command:`try_compile` command. The
+  compile and link commands can be influenced by setting any of the following
+  variables prior to calling ``check_objcxx_source_compiles()``:
+
+  ``CMAKE_REQUIRED_FLAGS``
+    Additional flags to pass to the compiler. Note that the contents of
+    :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
+    configuration-specific variable are automatically added to the compiler
+    command before the contents of ``CMAKE_REQUIRED_FLAGS``.
+
+  ``CMAKE_REQUIRED_DEFINITIONS``
+    A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
+    ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
+    ``<resultVar>`` will also be added automatically.
+
+  ``CMAKE_REQUIRED_INCLUDES``
+    A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
+    the compiler. These will be the only header search paths used by
+    ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
+    directory property will be ignored.
+
+  ``CMAKE_REQUIRED_LINK_OPTIONS``
+    A :ref:`;-list <CMake Language Lists>` of options to add to the link
+    command (see :command:`try_compile` for further details).
+
+  ``CMAKE_REQUIRED_LIBRARIES``
+    A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
+    command. These can be the name of system libraries or they can be
+    :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
+    further details).
+
+  ``CMAKE_REQUIRED_QUIET``
+    If this variable evaluates to a boolean true value, all status messages
+    associated with the check will be suppressed.
+
+  The check is only performed once, with the result cached in the variable
+  named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
+  value rather than performing the check again, even if the ``<code>`` changes.
+  In order to force the check to be re-evaluated, the variable named by
+  ``<resultVar>`` must be manually removed from the cache.
+
+#]=======================================================================]
+
+include_guard(GLOBAL)
+
+macro(CHECK_OBJCXX_SOURCE_COMPILES SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(_FAIL_REGEX)
+    set(_key)
+    foreach(arg ${ARGN})
+      if("${arg}" MATCHES "^(FAIL_REGEX)$")
+        set(_key "${arg}")
+      elseif(_key)
+        list(APPEND _${_key} "${arg}")
+      else()
+        message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
+      endif()
+    endforeach()
+
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LINK_OPTIONS)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS
+        LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS})
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS)
+    endif()
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_compile(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS}
+      ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      "${CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+
+    foreach(_regex ${_FAIL_REGEX})
+      if("${OUTPUT}" MATCHES "${_regex}")
+        set(${VAR} 0)
+      endif()
+    endforeach()
+
+    if(${VAR})
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Objective-C++ SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Objective-C++ SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/Modules/CheckOBJCXXSourceRuns.cmake b/Modules/CheckOBJCXXSourceRuns.cmake
new file mode 100644
index 0000000..a3d5923
--- /dev/null
+++ b/Modules/CheckOBJCXXSourceRuns.cmake
@@ -0,0 +1,145 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckOBJCXXSourceRuns
+---------------------
+
+Check if given Objective-C++ source compiles and links into an executable and can
+subsequently be run.
+
+.. command:: check_objcxx_source_runs
+
+  .. code-block:: cmake
+
+    check_objcxx_source_runs(<code> <resultVar>)
+
+  Check that the source supplied in ``<code>`` can be compiled as a Objective-C++ source
+  file, linked as an executable and then run. The ``<code>`` must contain at
+  least a ``main()`` function. If the ``<code>`` could be built and run
+  successfully, the internal cache variable specified by ``<resultVar>`` will
+  be set to 1, otherwise it will be set to an value that evaluates to boolean
+  false (e.g. an empty string or an error message).
+
+  The underlying check is performed by the :command:`try_run` command. The
+  compile and link commands can be influenced by setting any of the following
+  variables prior to calling ``check_objcxx_source_runs()``:
+
+  ``CMAKE_REQUIRED_FLAGS``
+    Additional flags to pass to the compiler. Note that the contents of
+    :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
+    configuration-specific variable are automatically added to the compiler
+    command before the contents of ``CMAKE_REQUIRED_FLAGS``.
+
+  ``CMAKE_REQUIRED_DEFINITIONS``
+    A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
+    ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
+    ``<resultVar>`` will also be added automatically.
+
+  ``CMAKE_REQUIRED_INCLUDES``
+    A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
+    the compiler. These will be the only header search paths used by
+    ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
+    directory property will be ignored.
+
+  ``CMAKE_REQUIRED_LINK_OPTIONS``
+    A :ref:`;-list <CMake Language Lists>` of options to add to the link
+    command (see :command:`try_run` for further details).
+
+  ``CMAKE_REQUIRED_LIBRARIES``
+    A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
+    command. These can be the name of system libraries or they can be
+    :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
+    further details).
+
+  ``CMAKE_REQUIRED_QUIET``
+    If this variable evaluates to a boolean true value, all status messages
+    associated with the check will be suppressed.
+
+  The check is only performed once, with the result cached in the variable
+  named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
+  value rather than performing the check again, even if the ``<code>`` changes.
+  In order to force the check to be re-evaluated, the variable named by
+  ``<resultVar>`` must be manually removed from the cache.
+
+#]=======================================================================]
+
+include_guard(GLOBAL)
+
+macro(CHECK_OBJCXX_SOURCE_RUNS SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LINK_OPTIONS)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS
+        LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS})
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS)
+    endif()
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_run(${VAR}_EXITCODE ${VAR}_COMPILED
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.mm
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LINK_OPTIONS}
+      ${CHECK_OBJCXX_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+      "${CHECK_OBJCXX_SOURCE_COMPILES_ADD_INCLUDES}"
+      COMPILE_OUTPUT_VARIABLE OUTPUT
+      RUN_OUTPUT_VARIABLE RUN_OUTPUT)
+
+    # if it did not compile make the return value fail code of 1
+    if(NOT ${VAR}_COMPILED)
+      set(${VAR}_EXITCODE 1)
+    endif()
+    # if the return value was 0 then it worked
+    if("${${VAR}_EXITCODE}" EQUAL 0)
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Objective-C++ SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "...and run output:\n"
+        "${RUN_OUTPUT}\n"
+        "Return value: ${${VAR}}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(CMAKE_CROSSCOMPILING AND "${${VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+        set(${VAR} "${${VAR}_EXITCODE}")
+      else()
+        set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      endif()
+
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Objective-C++ SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "...and run output:\n"
+        "${RUN_OUTPUT}\n"
+        "Return value: ${${VAR}_EXITCODE}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake
index b9ef808..1053383 100644
--- a/Modules/CheckSymbolExists.cmake
+++ b/Modules/CheckSymbolExists.cmake
@@ -45,6 +45,17 @@
   command. See policy :policy:`CMP0075`.
 ``CMAKE_REQUIRED_QUIET``
   execute quietly without messages.
+
+For example:
+
+.. code-block:: cmake
+
+  include(CheckSymbolExists)
+
+  # Check for macro SEEK_SET
+  check_symbol_exists(SEEK_SET "stdio.h" HAVE_SEEK_SET)
+  # Check for function fopen
+  check_symbol_exists(fopen "stdio.h" HAVE_FOPEN)
 #]=======================================================================]
 
 include_guard(GLOBAL)
@@ -88,8 +99,28 @@
       string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT
         "#include <${FILE}>\n")
     endforeach()
-    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT
-      "\nint main(int argc, char** argv)\n{\n  (void)argv;\n#ifndef ${SYMBOL}\n  return ((int*)(&${SYMBOL}))[argc];\n#else\n  (void)argc;\n  return 0;\n#endif\n}\n")
+    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+int main(int argc, char** argv)
+{
+  (void)argv;")
+    set(_CSE_CHECK_NON_MACRO "return ((int*)(&${SYMBOL}))[argc];")
+    if("${SYMBOL}" MATCHES "^[a-zA-Z_][a-zA-Z0-9_]*$")
+      # The SYMBOL has a legal macro name.  Test whether it exists as a macro.
+      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+#ifndef ${SYMBOL}
+  ${_CSE_CHECK_NON_MACRO}
+#else
+  (void)argc;
+  return 0;
+#endif")
+    else()
+      # The SYMBOL cannot be a macro (e.g., a template function).
+      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+  ${_CSE_CHECK_NON_MACRO}")
+    endif()
+    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+}")
+    unset(_CSE_CHECK_NON_MACRO)
 
     configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
       "${SOURCEFILE}" @ONLY)
@@ -128,6 +159,7 @@
         "${OUTPUT}\nFile ${SOURCEFILE}:\n"
         "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
     endif()
+    unset(CMAKE_CONFIGURABLE_FILE_CONTENT)
   endif()
 endmacro()
 
diff --git a/Modules/Compiler/AppleClang-OBJC.cmake b/Modules/Compiler/AppleClang-OBJC.cmake
new file mode 100644
index 0000000..d1f3706
--- /dev/null
+++ b/Modules/Compiler/AppleClang-OBJC.cmake
@@ -0,0 +1,17 @@
+include(Compiler/Clang-OBJC)
+
+if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 4.0)
+  set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90")
+  set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90")
+  set(CMAKE_OBJC90_STANDARD__HAS_FULL_SUPPORT ON)
+
+  set(CMAKE_OBJC99_STANDARD_COMPILE_OPTION "-std=c99")
+  set(CMAKE_OBJC99_EXTENSION_COMPILE_OPTION "-std=gnu99")
+  set(CMAKE_OBJC99_STANDARD__HAS_FULL_SUPPORT ON)
+
+  set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11")
+  set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11")
+  set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+__compiler_check_default_language_standard(OBJC 4.0 99)
diff --git a/Modules/Compiler/AppleClang-OBJCXX.cmake b/Modules/Compiler/AppleClang-OBJCXX.cmake
new file mode 100644
index 0000000..7c6f763
--- /dev/null
+++ b/Modules/Compiler/AppleClang-OBJCXX.cmake
@@ -0,0 +1,37 @@
+include(Compiler/Clang-OBJCXX)
+
+if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.0)
+  set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "-std=c++98")
+  set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98")
+  set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON)
+
+  set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++11")
+  set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11")
+endif()
+
+if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1)
+  set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++14")
+  set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
+  set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON)
+elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 5.1)
+  # AppleClang 5.0 knows this flag, but does not set a __cplusplus macro greater than 201103L
+  set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++1y")
+  set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y")
+  set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1)
+  set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++1z")
+  set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z")
+endif()
+
+if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 8.0)
+    set(CMAKE_OBJCXX11_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 10.0)
+  set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
+  set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a")
+endif()
+
+__compiler_check_default_language_standard(OBJCXX 4.0 98)
diff --git a/Modules/Compiler/Clang-OBJC.cmake b/Modules/Compiler/Clang-OBJC.cmake
new file mode 100644
index 0000000..c61c497
--- /dev/null
+++ b/Modules/Compiler/Clang-OBJC.cmake
@@ -0,0 +1,18 @@
+include(Compiler/Clang)
+__compiler_clang(OBJC)
+
+if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 3.4)
+  set(CMAKE_OBJC90_STANDARD_COMPILE_OPTION "-std=c90")
+  set(CMAKE_OBJC90_EXTENSION_COMPILE_OPTION "-std=gnu90")
+  set(CMAKE_OBJC90_STANDARD__HAS_FULL_SUPPORT ON)
+
+  set(CMAKE_OBJC99_STANDARD_COMPILE_OPTION "-std=c99")
+  set(CMAKE_OBJC99_EXTENSION_COMPILE_OPTION "-std=gnu99")
+  set(CMAKE_OBJC99_STANDARD__HAS_FULL_SUPPORT ON)
+
+  set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11")
+  set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11")
+  set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+__compiler_check_default_language_standard(OBJC 3.4 99 3.6 11)
diff --git a/Modules/Compiler/Clang-OBJCXX.cmake b/Modules/Compiler/Clang-OBJCXX.cmake
new file mode 100644
index 0000000..b01ce64
--- /dev/null
+++ b/Modules/Compiler/Clang-OBJCXX.cmake
@@ -0,0 +1,70 @@
+include(Compiler/Clang)
+__compiler_clang(OBJCXX)
+
+if("x${CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+  if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 2.1)
+    set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "-std=c++98")
+    set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98")
+  endif()
+
+  if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.1)
+    set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON)
+    set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++11")
+    set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11")
+    set(CMAKE_OBJCXX11_STANDARD__HAS_FULL_SUPPORT ON)
+  elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 2.1)
+    set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++0x")
+    set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++0x")
+  endif()
+
+  if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.5)
+    set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++14")
+    set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
+    set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON)
+  elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.4)
+    set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++1y")
+    set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y")
+    set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON)
+  endif()
+
+  set(_clang_version_std17 5.0)
+
+  if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}")
+    set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++17")
+    set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17")
+  elseif (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 3.5)
+    set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++1z")
+    set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z")
+  endif()
+
+  if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}")
+    set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
+    set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a")
+  endif()
+
+  unset(_clang_version_std17)
+
+  __compiler_check_default_language_standard(OBJCXX 2.1 98)
+elseif(CMAKE_OBJCXX_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9
+    AND CMAKE_OBJCXX_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.0)
+  # This version of clang-cl and the MSVC version it simulates have
+  # support for -std: flags.
+  set(CMAKE_OBJCXX98_STANDARD_COMPILE_OPTION "")
+  set(CMAKE_OBJCXX98_EXTENSION_COMPILE_OPTION "")
+  set(CMAKE_OBJCXX98_STANDARD__HAS_FULL_SUPPORT ON)
+  set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "")
+  set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "")
+  set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std:c++14")
+  set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std:c++14")
+  if (CMAKE_OBJCXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)
+    set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std:c++17")
+    set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std:c++17")
+    set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std:c++latest")
+    set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std:c++latest")
+  else()
+    set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std:c++latest")
+    set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std:c++latest")
+  endif()
+
+  __compiler_check_default_language_standard(OBJCXX 3.9 14)
+endif()
diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake
index c3f13f3..ea5a3b3 100644
--- a/Modules/Compiler/Clang.cmake
+++ b/Modules/Compiler/Clang.cmake
@@ -96,5 +96,10 @@
     set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
       "\"${__ranlib}\" <TARGET>"
     )
+
+    set(CMAKE_PCH_EXTENSION .pch)
+    set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>)
   endmacro()
 endif()
diff --git a/Modules/Compiler/GNU-CXX-FeatureTests.cmake b/Modules/Compiler/GNU-CXX-FeatureTests.cmake
index d18adaf..45c5470 100644
--- a/Modules/Compiler/GNU-CXX-FeatureTests.cmake
+++ b/Modules/Compiler/GNU-CXX-FeatureTests.cmake
@@ -18,18 +18,18 @@
 set(_cmake_feature_test_cxx_decltype_auto "${GNU49_CXX14}")
 set(_cmake_feature_test_cxx_digit_separators "${GNU49_CXX14}")
 set(_cmake_feature_test_cxx_generic_lambdas "${GNU49_CXX14}")
-set(_cmake_feature_test_cxx_lambda_init_captures "${GNU49_CXX14}")
 # GNU 4.3 supports binary literals as an extension, but may warn about
 # use of extensions prior to GNU 4.9
 # http://stackoverflow.com/questions/16334024/difference-between-gcc-binary-literals-and-c14-ones
 set(_cmake_feature_test_cxx_binary_literals "${GNU49_CXX14}")
-# The feature below is documented as available in GNU 4.8 (by implementing an
+# The features below are documented as available in GNU 4.8 (by implementing an
 # earlier draft of the standard paper), but that version of the compiler
 # does not set __cplusplus to a value greater than 201103L until GNU 4.9:
 # http://gcc.gnu.org/onlinedocs/gcc-4.8.2/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros
 # http://gcc.gnu.org/onlinedocs/gcc-4.9.0/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros
 # So, CMake only reports availability for it with GNU 4.9 or later.
 set(_cmake_feature_test_cxx_return_type_deduction "${GNU49_CXX14}")
+set(_cmake_feature_test_cxx_lambda_init_captures "${GNU49_CXX14}")
 
 # Introduced in GCC 4.8.1
 set(GNU481_CXX11 "((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L")
diff --git a/Modules/Compiler/GNU-FindBinUtils.cmake b/Modules/Compiler/GNU-FindBinUtils.cmake
index 097fbf3..a47b7ad 100644
--- a/Modules/Compiler/GNU-FindBinUtils.cmake
+++ b/Modules/Compiler/GNU-FindBinUtils.cmake
@@ -15,21 +15,27 @@
 get_filename_component(__gcc_hints "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY)
 
 # http://manpages.ubuntu.com/manpages/wily/en/man1/gcc-ar.1.html
-find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x_y}"
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x}"
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar${_CMAKE_COMPILER_SUFFIX}"
-    HINTS ${__gcc_hints}
+__get_compiler_component(
+    CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR gcc-ar
+    HINTS
+      ${__gcc_hints}
+    NAMES
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x_y}"
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x}"
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar"
     DOC "A wrapper around 'ar' adding the appropriate '--plugin' option for the GCC compiler"
 )
 mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
 
 # http://manpages.ubuntu.com/manpages/wily/en/man1/gcc-ranlib.1.html
-find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x_y}"
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x}"
-    "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib${_CMAKE_COMPILER_SUFFIX}"
-    HINTS ${__gcc_hints}
+__get_compiler_component(
+    CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB gcc-ranlib
+    HINTS
+      ${__gcc_hints}
+    NAMES
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x_y}"
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x}"
+      "${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib"
     DOC "A wrapper around 'ranlib' adding the appropriate '--plugin' option for the GCC compiler"
 )
 mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB)
diff --git a/Modules/Compiler/GNU-OBJC.cmake b/Modules/Compiler/GNU-OBJC.cmake
new file mode 100644
index 0000000..5fba801
--- /dev/null
+++ b/Modules/Compiler/GNU-OBJC.cmake
@@ -0,0 +1,6 @@
+include(Compiler/GNU)
+__compiler_gnu(OBJC)
+
+if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.2)
+  set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden")
+endif()
diff --git a/Modules/Compiler/GNU-OBJCXX.cmake b/Modules/Compiler/GNU-OBJCXX.cmake
new file mode 100644
index 0000000..66a547e
--- /dev/null
+++ b/Modules/Compiler/GNU-OBJCXX.cmake
@@ -0,0 +1,10 @@
+include(Compiler/GNU)
+__compiler_gnu(OBJC)
+
+if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.2)
+  set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden")
+endif()
+
+if(NOT CMAKE_OBJCXX_LINK_FLAGS)
+  set(CMAKE_OBCXX_LINK_FLAGS "-lstdc++")
+endif()
diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake
index 6b1bd3a..e0ff174 100644
--- a/Modules/Compiler/GNU.cmake
+++ b/Modules/Compiler/GNU.cmake
@@ -11,6 +11,9 @@
 include(Compiler/CMakeCommonCompilerMacros)
 include(Internal/CMakeCheckCompilerFlag)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__compiler_gnu lang)
   # Feature flags.
   set(CMAKE_${lang}_VERBOSE_FLAG "-v")
@@ -104,4 +107,9 @@
     unset(_COMPILER_ARGS)
   endif()
   list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+  set(CMAKE_PCH_EXTENSION .gch)
+  set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
 endmacro()
diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake
index 437678e..413d9e2 100644
--- a/Modules/Compiler/IAR-ASM.cmake
+++ b/Modules/Compiler/IAR-ASM.cmake
@@ -37,6 +37,11 @@
   __compiler_iar_xlink(ASM)
   set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s43;asm;msa)
 
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
+  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+  __compiler_iar_xlink(ASM)
+  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS r85;asm;msa)
+
 else()
   message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
 endif()
diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake
index 18a4a75..9ad1ba0 100644
--- a/Modules/Compiler/IAR-C.cmake
+++ b/Modules/Compiler/IAR-C.cmake
@@ -15,7 +15,7 @@
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e)
   set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
   set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e)
-elseif()
+else()
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e)
 endif()
@@ -60,6 +60,11 @@
   __compiler_check_default_language_standard(C 1.10 90 5.10 99)
   set(CMAKE_C_OUTPUT_EXTENSION ".r43")
 
+elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
+  __compiler_iar_xlink(C)
+  __compiler_check_default_language_standard(C 1.10 90 4.10 99)
+  set(CMAKE_C_OUTPUT_EXTENSION ".r85")
+
 else()
   message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
 endif()
diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake
index e8f1142..549d242 100644
--- a/Modules/Compiler/IAR-CXX.cmake
+++ b/Modules/Compiler/IAR-CXX.cmake
@@ -68,6 +68,10 @@
   __compiler_check_default_language_standard(CXX 5.10 98)
   set(CMAKE_CXX_OUTPUT_EXTENSION ".r43")
 
+elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
+  __compiler_iar_xlink(CXX)
+  __compiler_check_default_language_standard(CXX 1.10 98)
+  set(CMAKE_C_OUTPUT_EXTENSION ".r85")
 else()
   message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected. This should be automatic." )
 endif()
diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake
index 57ca1c9..7e17778 100644
--- a/Modules/Compiler/IAR-DetermineCompiler.cmake
+++ b/Modules/Compiler/IAR-DetermineCompiler.cmake
@@ -31,7 +31,7 @@
 #  define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(((__VER__) / 1000) % 1000)
 #  define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@((__VER__) % 1000)
 #  define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__)
-# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__))
+# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__))
 #  define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 100)
 #  define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@((__VER__) - (((__VER__) / 100)*100))
 #  define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__SUBVERSION__)
diff --git a/Modules/Compiler/IAR-FindBinUtils.cmake b/Modules/Compiler/IAR-FindBinUtils.cmake
index b7d4664..6258cf3 100644
--- a/Modules/Compiler/IAR-FindBinUtils.cmake
+++ b/Modules/Compiler/IAR-FindBinUtils.cmake
@@ -45,7 +45,8 @@
 ")
 
 elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR" OR
-       "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430")
+       "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430" OR
+       "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
 
   # Find the "xlink" linker and "xar" archiver:
   find_program(CMAKE_IAR_LINKER xlink HINTS ${__iar_hints}
diff --git a/Modules/Compiler/Intel-Fortran.cmake b/Modules/Compiler/Intel-Fortran.cmake
index 5275ddf..156b533 100644
--- a/Modules/Compiler/Intel-Fortran.cmake
+++ b/Modules/Compiler/Intel-Fortran.cmake
@@ -8,6 +8,8 @@
 set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-fixed")
 set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free")
 
+set(CMAKE_Fortran_COMPILE_WITH_DEFINES 1)
+
 set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
 set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 
diff --git a/Modules/Compiler/Intel.cmake b/Modules/Compiler/Intel.cmake
index f2f16e0..d895ed0 100644
--- a/Modules/Compiler/Intel.cmake
+++ b/Modules/Compiler/Intel.cmake
@@ -32,5 +32,12 @@
       unset(_COMPILER_ARGS)
     endif()
     list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-QdM" "-P" "-Za" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+    # Precompile Headers
+    set(CMAKE_PCH_EXTENSION .pchi)
+    set(CMAKE_LINK_PCH ON)
+    set(CMAKE_PCH_EPILOGUE "#pragma hdrstop")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -Wno-pch-messages -pch-use <PCH_FILE> -include <PCH_HEADER>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -Wno-pch-messages -pch-create <PCH_FILE> -include <PCH_HEADER>)
   endmacro()
 endif()
diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake
index c0ccb71..b59deda 100644
--- a/Modules/Compiler/NVIDIA-CUDA.cmake
+++ b/Modules/Compiler/NVIDIA-CUDA.cmake
@@ -1,3 +1,4 @@
+set(CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE True)
 set(CMAKE_CUDA_VERBOSE_FLAG "-v")
 set(CMAKE_CUDA_VERBOSE_COMPILE_FLAG "-Xcompiler=-v")
 
diff --git a/Modules/Compiler/XL.cmake b/Modules/Compiler/XL.cmake
index a9cec11..fc71ab4 100644
--- a/Modules/Compiler/XL.cmake
+++ b/Modules/Compiler/XL.cmake
@@ -18,6 +18,8 @@
   set(CMAKE_${lang}_RESPONSE_FILE_FLAG "-qoptfile=")
   set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-qoptfile=")
 
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-qmkshrobj")
+
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
 
diff --git a/Modules/CompilerId/VS-10.vcxproj.in b/Modules/CompilerId/VS-10.vcxproj.in
index 32c4ffc..d742274 100644
--- a/Modules/CompilerId/VS-10.vcxproj.in
+++ b/Modules/CompilerId/VS-10.vcxproj.in
@@ -14,6 +14,7 @@
     @id_system_version@
     @id_WindowsTargetPlatformVersion@
     @id_WindowsSDKDesktopARMSupport@
+    @id_CudaToolkitCustomDir@
   </PropertyGroup>
   @id_toolset_version_props@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 20b37d2..66061a1 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -49,7 +49,7 @@
 
     ``STAMP_DIR <dir>``
       Directory in which to store the timestamps of each step. Log files from
-      individual steps are also created in here unless overriden by LOG_DIR
+      individual steps are also created in here unless overridden by LOG_DIR
       (see *Logging Options* below).
 
     ``LOG_DIR <dir>``
@@ -261,7 +261,9 @@
 
       ``GIT_SUBMODULES <module>...``
         Specific git submodules that should also be updated. If this option is
-        not provided, all git submodules will be updated.
+        not provided, all git submodules will be updated. When :policy:`CMP0097`
+        is set to ``NEW`` if this value is set to an empty string then no submodules
+        are initialized or updated.
 
       ``GIT_SHALLOW <bool>``
         When this option is enabled, the ``git clone`` operation will be given
@@ -423,7 +425,7 @@
       build directory or re-uses previous build contents.
 
       If the CMake generator is the ``Green Hills MULTI`` and not overridden then
-      the orginal projects settings for the GHS toolset and target system
+      the original project's settings for the GHS toolset and target system
       customization cache variables are propagated into the external project.
 
     ``SOURCE_SUBDIR <dir>``
@@ -1016,6 +1018,9 @@
       endif()
     else()
       set(key "${arg}")
+      if(key MATCHES GIT)
+       get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET)
+      endif()
     endif()
   endforeach()
 endfunction()
@@ -1060,7 +1065,7 @@
   "ExternalProject module."
   )
 
-function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_remote_name git_submodules git_shallow git_progress git_config src_name work_dir gitclone_infofile gitclone_stampfile tls_verify)
+function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag git_remote_name init_submodules git_submodules git_shallow git_progress git_config src_name work_dir gitclone_infofile gitclone_stampfile tls_verify)
   if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5)
     # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path.
     set(git_checkout_explicit-- "--")
@@ -1074,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")
@@ -1145,11 +1150,14 @@
   message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\")
 endif()
 
-execute_process(
-  COMMAND \"${git_EXECUTABLE}\" ${git_options} submodule update --recursive --init ${git_submodules}
-  WORKING_DIRECTORY \"${work_dir}/${src_name}\"
-  RESULT_VARIABLE error_code
-  )
+set(init_submodules ${init_submodules})
+if(init_submodules)
+  execute_process(
+    COMMAND \"${git_EXECUTABLE}\" ${git_options} submodule update --recursive --init ${git_submodules}
+    WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+    RESULT_VARIABLE error_code
+    )
+endif()
 if(error_code)
   message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\")
 endif()
@@ -1226,7 +1234,7 @@
 endfunction()
 
 
-function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_remote_name git_submodules git_repository work_dir)
+function(_ep_write_gitupdate_script script_filename git_EXECUTABLE git_tag git_remote_name init_submodules git_submodules git_repository work_dir)
   if("${git_tag}" STREQUAL "")
     message(FATAL_ERROR "Tag for git checkout should not be empty.")
   endif()
@@ -1383,11 +1391,14 @@
     endif()
   endif()
 
-  execute_process(
-    COMMAND \"${git_EXECUTABLE}\" submodule update --recursive --init ${git_submodules}
-    WORKING_DIRECTORY \"${work_dir}/${src_name}\"
-    RESULT_VARIABLE error_code
-    )
+  set(init_submodules ${init_submodules})
+  if(init_submodules)
+    execute_process(
+      COMMAND \"${git_EXECUTABLE}\" submodule update --recursive --init ${git_submodules}
+      WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+      RESULT_VARIABLE error_code
+      )
+  endif()
   if(error_code)
     message(FATAL_ERROR \"Failed to update submodules in: '${work_dir}/${src_name}'\")
   endif()
@@ -1972,7 +1983,7 @@
     set(stderr_log "${logbase}-err.log")
   endif()
   set(code "
-cmake_minimum_required(VERSION 3.13)
+cmake_minimum_required(VERSION 3.15)
 ${code_cygpath_make}
 set(command \"${command}\")
 set(log_merged \"${log_merged}\")
@@ -2420,7 +2431,15 @@
     if(NOT git_tag)
       set(git_tag "master")
     endif()
-    get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
+
+    set(git_init_submodules TRUE)
+    get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET)
+    if(git_submodules_set)
+      get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
+      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
+        set(git_init_submodules FALSE)
+      endif()
+    endif()
 
     get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME)
     if(NOT git_remote_name)
@@ -2458,7 +2477,7 @@
     # The script will delete the source directory and then call git clone.
     #
     _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir}
-      ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${git_remote_name} "${git_submodules}" "${git_shallow}" "${git_progress}" "${git_config}" ${src_name} ${work_dir}
+      ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules}" "${git_shallow}" "${git_progress}" "${git_config}" ${src_name} ${work_dir}
       ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt "${tls_verify}"
       )
     set(comment "Performing download step (git clone) for '${name}'")
@@ -2723,9 +2742,18 @@
     if(NOT git_remote_name)
       set(git_remote_name "origin")
     endif()
-    get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
+
+    set(git_init_submodules TRUE)
+    get_property(git_submodules_set TARGET ${name} PROPERTY _EP_GIT_SUBMODULES SET)
+    if(git_submodules_set)
+      get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
+      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
+        set(git_init_submodules FALSE)
+      endif()
+    endif()
+
     _ep_write_gitupdate_script(${tmp_dir}/${name}-gitupdate.cmake
-      ${GIT_EXECUTABLE} ${git_tag} ${git_remote_name} "${git_submodules}" ${git_repository} ${work_dir}
+      ${GIT_EXECUTABLE} ${git_tag} ${git_remote_name} ${git_init_submodules} "${git_submodules}" ${git_repository} ${work_dir}
       )
     set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitupdate.cmake)
     set(always 1)
@@ -3138,6 +3166,10 @@
 
 
 function(ExternalProject_Add name)
+  cmake_policy(GET CMP0097 _EP_CMP0097
+    PARENT_SCOPE # undocumented, do not use outside of CMake
+    )
+
   _ep_get_configuration_subdir_suffix(cfgdir)
 
   # Add a custom target for the external project.
diff --git a/Modules/FindBZip2.cmake b/Modules/FindBZip2.cmake
index 309b971..98ab72c 100644
--- a/Modules/FindBZip2.cmake
+++ b/Modules/FindBZip2.cmake
@@ -45,8 +45,8 @@
 find_path(BZIP2_INCLUDE_DIR bzlib.h ${_BZIP2_PATHS} PATH_SUFFIXES include)
 
 if (NOT BZIP2_LIBRARIES)
-    find_library(BZIP2_LIBRARY_RELEASE NAMES bz2 bzip2 ${_BZIP2_PATHS} PATH_SUFFIXES lib)
-    find_library(BZIP2_LIBRARY_DEBUG NAMES bz2d bzip2d ${_BZIP2_PATHS} PATH_SUFFIXES lib)
+    find_library(BZIP2_LIBRARY_RELEASE NAMES bz2 bzip2 libbz2 libbzip2 ${_BZIP2_PATHS} PATH_SUFFIXES lib)
+    find_library(BZIP2_LIBRARY_DEBUG NAMES bz2d bzip2d libbz2d libbzip2d ${_BZIP2_PATHS} PATH_SUFFIXES lib)
 
     include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
     SELECT_LIBRARY_CONFIGURATIONS(BZIP2)
diff --git a/Modules/FindBacktrace.cmake b/Modules/FindBacktrace.cmake
index cf1632a..3d8ce88 100644
--- a/Modules/FindBacktrace.cmake
+++ b/Modules/FindBacktrace.cmake
@@ -74,7 +74,7 @@
   if(Backtrace_INCLUDE_DIR)
     # OpenBSD has libbacktrace renamed to libexecinfo
     find_library(Backtrace_LIBRARY "execinfo")
-  elseif()     # respect user wishes
+  else()     # respect user wishes
     set(_Backtrace_HEADER_TRY "backtrace.h")
     find_path(Backtrace_INCLUDE_DIR ${_Backtrace_HEADER_TRY})
     find_library(Backtrace_LIBRARY "backtrace")
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index 078000f..744d2c7 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -2000,7 +2000,8 @@
     endif()
   else()
     set(Boost_${UPPERCOMPONENT}_HEADER ON)
-    message(WARNING "No header defined for ${COMPONENT}; skipping header check")
+    message(WARNING "No header defined for ${COMPONENT}; skipping header check "
+                    "(note: header-only libraries have no designated component)")
   endif()
 
   #
diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake
index 3315505..b6859aa 100644
--- a/Modules/FindCUDA.cmake
+++ b/Modules/FindCUDA.cmake
@@ -375,6 +375,11 @@
   CUDA_nvcuvid_LIBRARY  -- CUDA Video Decoder library.
                            Only available for CUDA version 3.2+.
                            Windows only.
+  CUDA_nvToolsExt_LIBRARY
+                        -- NVIDA CUDA Tools Extension library.
+                           Available for CUDA version 5+.
+  CUDA_OpenCL_LIBRARY   -- NVIDA CUDA OpenCL library.
+                           Available for CUDA version 5+.
 
 #]=======================================================================]
 
@@ -642,6 +647,8 @@
   unset(CUDA_npps_LIBRARY CACHE)
   unset(CUDA_nvcuvenc_LIBRARY CACHE)
   unset(CUDA_nvcuvid_LIBRARY CACHE)
+  unset(CUDA_nvToolsExt_LIBRARY CACHE)
+  unset(CUDA_OpenCL_LIBRARY CACHE)
   unset(CUDA_GPU_DETECT_OUTPUT CACHE)
 endmacro()
 
@@ -973,6 +980,11 @@
   find_cuda_helper_libs(cublas_device)
 endif()
 
+if(NOT CUDA_VERSION VERSION_LESS "5.0")
+  find_cuda_helper_libs(nvToolsExt)
+  find_cuda_helper_libs(OpenCL)
+endif()
+
 if(NOT CUDA_VERSION VERSION_LESS "9.0")
   # In CUDA 9.0 NPP was nppi was removed
   find_cuda_helper_libs(nppc)
diff --git a/Modules/FindCUDA/run_nvcc.cmake b/Modules/FindCUDA/run_nvcc.cmake
index 6fc2439..af15d55 100644
--- a/Modules/FindCUDA/run_nvcc.cmake
+++ b/Modules/FindCUDA/run_nvcc.cmake
@@ -75,7 +75,8 @@
 set(CUDA_NVCC_FLAGS @CUDA_NVCC_FLAGS@ ;; @CUDA_WRAP_OPTION_NVCC_FLAGS@) # list
 @CUDA_NVCC_FLAGS_CONFIG@
 set(nvcc_flags @nvcc_flags@) # list
-set(CUDA_NVCC_INCLUDE_DIRS "@CUDA_NVCC_INCLUDE_DIRS@") # list (needs to be in quotes to handle spaces properly).
+set(CUDA_NVCC_INCLUDE_DIRS [==[@CUDA_NVCC_INCLUDE_DIRS@]==]) # list (needs to be in lua quotes to address backslashes)
+string(REPLACE "\\" "/" CUDA_NVCC_INCLUDE_DIRS "${CUDA_NVCC_INCLUDE_DIRS}")
 set(CUDA_NVCC_COMPILE_DEFINITIONS [==[@CUDA_NVCC_COMPILE_DEFINITIONS@]==]) # list (needs to be in lua quotes see #16510 ).
 set(format_flag "@format_flag@") # string
 set(cuda_language_flag @cuda_language_flag@) # list
diff --git a/Modules/FindCurses.cmake b/Modules/FindCurses.cmake
index b3cd8fa..5abc08a 100644
--- a/Modules/FindCurses.cmake
+++ b/Modules/FindCurses.cmake
@@ -18,6 +18,8 @@
   The include directories needed to use Curses.
 ``CURSES_LIBRARIES``
   The libraries needed to use Curses.
+``CURSES_CFLAGS``
+  Parameters which ought be given to C/C++ compilers when using Curses.
 ``CURSES_HAVE_CURSES_H``
   True if curses.h is available.
 ``CURSES_HAVE_NCURSES_H``
@@ -56,7 +58,7 @@
   set(CURSES_NEED_NCURSES TRUE)
 endif()
 
-find_library(CURSES_CURSES_LIBRARY NAMES curses )
+find_library(CURSES_CURSES_LIBRARY NAMES curses)
 
 find_library(CURSES_NCURSES_LIBRARY NAMES "${NCURSES_LIBRARY_NAME}" )
 set(CURSES_USE_NCURSES FALSE)
@@ -118,7 +120,7 @@
   if(CURSES_NCURSES_INCLUDE_PATH)
     if (CURSES_NEED_WIDE)
       find_path(CURSES_INCLUDE_PATH
-        NAMES ncursesw/ncurses.h ncursesw/curses.h
+        NAMES ncursesw/ncurses.h ncursesw/curses.h ncursesw.h cursesw.h
         PATHS ${CURSES_NCURSES_INCLUDE_PATH}
         NO_DEFAULT_PATH
         )
@@ -133,7 +135,7 @@
 
   if (CURSES_NEED_WIDE)
     find_path(CURSES_INCLUDE_PATH
-      NAMES ncursesw/ncurses.h ncursesw/curses.h
+      NAMES ncursesw/ncurses.h ncursesw/curses.h ncursesw.h cursesw.h
       HINTS "${_cursesParentDir}/include"
       )
   else()
@@ -238,10 +240,16 @@
   set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_FORM_LIBRARY})
 endif()
 
-# Provide the *_INCLUDE_DIRS result.
+# Provide the *_INCLUDE_DIRS and *_CFLAGS results.
 set(CURSES_INCLUDE_DIRS ${CURSES_INCLUDE_PATH})
 set(CURSES_INCLUDE_DIR ${CURSES_INCLUDE_PATH}) # compatibility
 
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+  pkg_check_modules(NCURSES QUIET ${NCURSES_LIBRARY_NAME})
+  set(CURSES_CFLAGS ${NCURSES_CFLAGS_OTHER})
+endif()
+
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 FIND_PACKAGE_HANDLE_STANDARD_ARGS(Curses DEFAULT_MSG
   CURSES_LIBRARY CURSES_INCLUDE_PATH)
diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake
index ebd0b24..faa03f9 100644
--- a/Modules/FindDoxygen.cmake
+++ b/Modules/FindDoxygen.cmake
@@ -70,6 +70,7 @@
     doxygen_add_docs(targetName
         [filesOrDirs...]
         [ALL]
+        [USE_STAMP_FILE]
         [WORKING_DIRECTORY dir]
         [COMMENT comment])
 
@@ -92,7 +93,19 @@
   the :command:`add_custom_target` command used to create the custom target
   internally.
 
-  If ALL is set, the target will be added to the default build target.
+  If ``ALL`` is set, the target will be added to the default build target.
+
+  If ``USE_STAMP_FILE`` is set, the custom command defined by this function will
+  create a stamp file with the name ``<targetName>.stamp`` in the current
+  binary directory whenever doxygen is re-run.  With this option present, all
+  items in ``<filesOrDirs>`` must be files (i.e. no directories, symlinks or
+  wildcards) and each of the files must exist at the time
+  ``doxygen_add_docs()`` is called.  An error will be raised if any of the
+  items listed is missing or is not a file when ``USE_STAMP_FILE`` is given.
+  A dependency will be created on each of the files so that doxygen will only
+  be re-run if one of the files is updated.  Without the ``USE_STAMP_FILE``
+  option, doxygen will always be re-run if the ``<targetName>`` target is built
+  regardless of whether anything listed in ``<filesOrDirs>`` has changed.
 
   The contents of the generated ``Doxyfile`` can be customized by setting CMake
   variables before calling ``doxygen_add_docs()``. Any variable with a name of
@@ -801,7 +814,7 @@
 endfunction()
 
 function(doxygen_add_docs targetName)
-    set(_options ALL)
+    set(_options ALL USE_STAMP_FILE)
     set(_one_value_args WORKING_DIRECTORY COMMENT)
     set(_multi_value_args)
     cmake_parse_arguments(_args
@@ -978,9 +991,10 @@
     endif()
 
     # Build up a list of files we can identify from the inputs so we can list
-    # them as SOURCES in the custom target (makes them display in IDEs). We
-    # must do this before we transform the various DOXYGEN_... variables below
-    # because we need to process DOXYGEN_INPUT as a list first.
+    # them as DEPENDS and SOURCES in the custom command/target (the latter
+    # makes them display in IDEs). This must be done before we transform the
+    # various DOXYGEN_... variables below because we need to process
+    # DOXYGEN_INPUT as a list first.
     unset(_sources)
     foreach(_item IN LISTS DOXYGEN_INPUT)
         get_filename_component(_abs_item "${_item}" ABSOLUTE
@@ -989,11 +1003,13 @@
            NOT IS_DIRECTORY "${_abs_item}" AND
            NOT IS_SYMLINK "${_abs_item}")
             list(APPEND _sources "${_abs_item}")
+        elseif(_args_USE_STAMP_FILE)
+            message(FATAL_ERROR "Source does not exist or is not a file:\n"
+                "    ${_abs_item}\n"
+                "Only existing files may be specified when the "
+                "USE_STAMP_FILE option is given.")
         endif()
     endforeach()
-    if(_sources)
-        list(INSERT _sources 0 SOURCES)
-    endif()
 
     # Transform known list type options into space separated strings.
     set(_doxygen_list_options
@@ -1107,15 +1123,35 @@
         set(_all ALL)
     endif()
 
-    # Add the target
-    add_custom_target( ${targetName} ${_all} VERBATIM
-        COMMAND ${CMAKE_COMMAND} -E make_directory ${_original_doxygen_output_dir}
-        COMMAND "${DOXYGEN_EXECUTABLE}" "${_target_doxyfile}"
-        WORKING_DIRECTORY "${_args_WORKING_DIRECTORY}"
-        DEPENDS "${_target_doxyfile}"
-        COMMENT "${_args_COMMENT}"
-        ${_sources}
-    )
+    # Only create the stamp file if asked to. If we don't create it,
+    # the target will always be considered out-of-date.
+    if(_args_USE_STAMP_FILE)
+        set(__stamp_file "${CMAKE_CURRENT_BINARY_DIR}/${targetName}.stamp")
+        add_custom_command(
+            VERBATIM
+            OUTPUT ${__stamp_file}
+            COMMAND ${CMAKE_COMMAND} -E make_directory ${_original_doxygen_output_dir}
+            COMMAND "${DOXYGEN_EXECUTABLE}" "${_target_doxyfile}"
+            COMMAND ${CMAKE_COMMAND} -E touch ${__stamp_file}
+            WORKING_DIRECTORY "${_args_WORKING_DIRECTORY}"
+            DEPENDS "${_target_doxyfile}" ${_sources}
+            COMMENT "${_args_COMMENT}"
+        )
+        add_custom_target(${targetName} ${_all}
+            DEPENDS ${__stamp_file}
+            SOURCES ${_sources}
+        )
+        unset(__stamp_file)
+    else()
+        add_custom_target( ${targetName} ${_all} VERBATIM
+            COMMAND ${CMAKE_COMMAND} -E make_directory ${_original_doxygen_output_dir}
+            COMMAND "${DOXYGEN_EXECUTABLE}" "${_target_doxyfile}"
+            WORKING_DIRECTORY "${_args_WORKING_DIRECTORY}"
+            DEPENDS "${_target_doxyfile}" ${_sources}
+            COMMENT "${_args_COMMENT}"
+            SOURCES ${_sources}
+        )
+    endif()
 
 endfunction()
 
diff --git a/Modules/FindGTK2.cmake b/Modules/FindGTK2.cmake
index e3af676..83091f3 100644
--- a/Modules/FindGTK2.cmake
+++ b/Modules/FindGTK2.cmake
@@ -259,6 +259,7 @@
         gtkmm-2.4
         libglade-2.0
         libglademm-2.4
+        harfbuzz
         pango-1.0
         pangomm-1.4
         sigc++-2.0
@@ -711,6 +712,8 @@
         _GTK2_FIND_LIBRARY    (PANGO pango false true)
         _GTK2_ADD_TARGET      (PANGO GTK2_DEPENDS gobject glib)
 
+        _GTK2_FIND_INCLUDE_DIR(HARFBUZZ hb.h)
+
         _GTK2_FIND_LIBRARY    (PANGOCAIRO pangocairo false true)
         _GTK2_ADD_TARGET      (PANGOCAIRO GTK2_DEPENDS pango cairo gobject glib)
 
diff --git a/Modules/FindGTest.cmake b/Modules/FindGTest.cmake
index b0175fe..e015a98 100644
--- a/Modules/FindGTest.cmake
+++ b/Modules/FindGTest.cmake
@@ -160,6 +160,10 @@
             msvc/gtest-md/Release
             msvc/x64/Debug
             msvc/x64/Release
+            msvc/2010/gtest-md/Win32-Debug
+            msvc/2010/gtest-md/Win32-Release
+            msvc/2010/gtest-md/x64-Debug
+            msvc/2010/gtest-md/x64-Release
             )
     elseif(GTEST_MSVC_SEARCH STREQUAL "MT")
         list(APPEND _gtest_libpath_suffixes
@@ -167,6 +171,10 @@
             msvc/gtest/Release
             msvc/x64/Debug
             msvc/x64/Release
+            msvc/2010/gtest/Win32-Debug
+            msvc/2010/gtest/Win32-Release
+            msvc/2010/gtest/x64-Debug
+            msvc/2010/gtest/x64-Release
             )
     endif()
 endif()
diff --git a/Modules/FindGnuTLS.cmake b/Modules/FindGnuTLS.cmake
index 123a0f5..819f000 100644
--- a/Modules/FindGnuTLS.cmake
+++ b/Modules/FindGnuTLS.cmake
@@ -7,16 +7,25 @@
 
 Find the GNU Transport Layer Security library (gnutls)
 
+IMPORTED Targets
+^^^^^^^^^^^^^^^^
 
+This module defines :prop_tgt:`IMPORTED` target ``GnuTLS::GnuTLS``, if
+gnutls has been found.
 
-Once done this will define
+Result Variables
+^^^^^^^^^^^^^^^^
 
-::
-
-  GNUTLS_FOUND - System has gnutls
-  GNUTLS_INCLUDE_DIR - The gnutls include directory
-  GNUTLS_LIBRARIES - The libraries needed to use gnutls
-  GNUTLS_DEFINITIONS - Compiler switches required for using gnutls
+``GNUTLS_FOUND``
+  System has gnutls
+``GNUTLS_INCLUDE_DIR``
+  The gnutls include directory
+``GNUTLS_LIBRARIES``
+  The libraries needed to use gnutls
+``GNUTLS_DEFINITIONS``
+  Compiler switches required for using gnutls
+``GNUTLS_VERSION``
+  version of gnutls.
 #]=======================================================================]
 
 # Note that this doesn't try to find the gnutls-extra package.
@@ -34,6 +43,8 @@
   find_package(PkgConfig QUIET)
   PKG_CHECK_MODULES(PC_GNUTLS QUIET gnutls)
   set(GNUTLS_DEFINITIONS ${PC_GNUTLS_CFLAGS_OTHER})
+  set(GNUTLS_VERSION ${PC_GNUTLS_VERSION})
+  # keep for backward compatibility
   set(GNUTLS_VERSION_STRING ${PC_GNUTLS_VERSION})
 endif ()
 
@@ -59,4 +70,13 @@
 if(GNUTLS_FOUND)
   set(GNUTLS_LIBRARIES    ${GNUTLS_LIBRARY})
   set(GNUTLS_INCLUDE_DIRS ${GNUTLS_INCLUDE_DIR})
+
+  if(NOT TARGET GnuTLS::GnuTLS)
+    add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED)
+    set_target_properties(GnuTLS::GnuTLS PROPERTIES
+      INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}"
+      INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}"
+      IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+      IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
+  endif()
 endif()
diff --git a/Modules/FindJNI.cmake b/Modules/FindJNI.cmake
index 237ea96..3a5bd31 100644
--- a/Modules/FindJNI.cmake
+++ b/Modules/FindJNI.cmake
@@ -143,7 +143,7 @@
 
 if (WIN32)
   set (_JNI_HINTS)
-  execute_process(COMMAND REG QUERY HKLM\\SOFTWARE\\JavaSoft\\JDK /f "." /k
+  execute_process(COMMAND REG QUERY HKLM\\SOFTWARE\\JavaSoft\\JDK
     RESULT_VARIABLE _JNI_RESULT
     OUTPUT_VARIABLE _JNI_VERSIONS
     ERROR_QUIET)
diff --git a/Modules/FindJava.cmake b/Modules/FindJava.cmake
index 0d62cd6..945df3c 100644
--- a/Modules/FindJava.cmake
+++ b/Modules/FindJava.cmake
@@ -85,7 +85,7 @@
 endif()
 if (WIN32)
   macro (_JAVA_GET_INSTALLED_VERSIONS _KIND)
-    execute_process(COMMAND REG QUERY HKLM\\SOFTWARE\\JavaSoft\\${_KIND} /f "." /k
+    execute_process(COMMAND REG QUERY HKLM\\SOFTWARE\\JavaSoft\\${_KIND}
       RESULT_VARIABLE _JAVA_RESULT
       OUTPUT_VARIABLE _JAVA_VERSIONS
       ERROR_QUIET)
@@ -153,7 +153,7 @@
 )
 
 if(Java_JAVA_EXECUTABLE)
-    execute_process(COMMAND ${Java_JAVA_EXECUTABLE} -version
+    execute_process(COMMAND "${Java_JAVA_EXECUTABLE}" -version
       RESULT_VARIABLE res
       OUTPUT_VARIABLE var
       ERROR_VARIABLE var # sun-java output to stderr
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake
index c9c3cce..844d36d 100644
--- a/Modules/FindLAPACK.cmake
+++ b/Modules/FindLAPACK.cmake
@@ -173,11 +173,15 @@
   #message("DEBUG: ${LIBRARIES} = ${${LIBRARIES}}")
 endif()
 
-if(_libraries_work)
-  set(${LIBRARIES} ${${LIBRARIES}} ${_blas} ${_threads})
-else()
-  set(${LIBRARIES} FALSE)
-endif()
+ if(_libraries_work)
+   if("${_list}" STREQUAL "")
+     set(${LIBRARIES} "${LIBRARIES}-PLACEHOLDER-FOR-EMPTY-LIBRARIES")
+   else()
+     set(${LIBRARIES} ${${LIBRARIES}} ${_blas} ${_threads})
+   endif()
+ else()
+    set(${LIBRARIES} FALSE)
+ endif()
 
 endmacro()
 
@@ -206,6 +210,7 @@
 
 #intel lapack
 if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All")
+  if(NOT LAPACK_LIBRARIES)
   if (NOT WIN32)
     set(LAPACK_mkl_LM "-lm")
     set(LAPACK_mkl_LDL "-ldl")
@@ -280,6 +285,7 @@
     unset(LAPACK_mkl_LM)
     unset(LAPACK_mkl_LDL)
   endif ()
+  endif()
 endif()
 
 if (BLA_VENDOR STREQUAL "Goto" OR BLA_VENDOR STREQUAL "All")
@@ -426,5 +432,11 @@
   endif()
 endif()
 
+# On compilers that implicitly link LAPACK (such as ftn, cc, and CC on Cray HPC machines)
+# we used a placeholder for empty LAPACK_LIBRARIES to get through our logic above.
+if (LAPACK_LIBRARIES STREQUAL "LAPACK_LIBRARIES-PLACEHOLDER-FOR-EMPTY-LIBRARIES")
+  set(LAPACK_LIBRARIES "")
+endif()
+
 cmake_pop_check_state()
 set(CMAKE_FIND_LIBRARY_SUFFIXES ${_lapack_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
diff --git a/Modules/FindLibLZMA.cmake b/Modules/FindLibLZMA.cmake
index fc97655..200d6bf 100644
--- a/Modules/FindLibLZMA.cmake
+++ b/Modules/FindLibLZMA.cmake
@@ -42,7 +42,14 @@
 #]=======================================================================]
 
 find_path(LIBLZMA_INCLUDE_DIR lzma.h )
-find_library(LIBLZMA_LIBRARY NAMES lzma liblzma)
+if(NOT LIBLZMA_LIBRARY)
+  find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma liblzma PATH_SUFFIXES lib)
+  find_library(LIBLZMA_LIBRARY_DEBUG NAMES lzmad liblzmad PATH_SUFFIXES lib)
+  include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
+  select_library_configurations(LIBLZMA)
+else()
+  file(TO_CMAKE_PATH "${LIBLZMA_LIBRARY}" LIBLZMA_LIBRARY)
+endif()
 
 if(LIBLZMA_INCLUDE_DIR AND EXISTS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h")
     file(STRINGS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h" LIBLZMA_HEADER_CONTENTS REGEX "#define LZMA_VERSION_[A-Z]+ [0-9]+")
@@ -62,9 +69,17 @@
   include(${CMAKE_CURRENT_LIST_DIR}/CheckLibraryExists.cmake)
   set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
   set(CMAKE_REQUIRED_QUIET ${LibLZMA_FIND_QUIETLY})
-  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY} lzma_auto_decoder "" LIBLZMA_HAS_AUTO_DECODER)
-  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY} lzma_easy_encoder "" LIBLZMA_HAS_EASY_ENCODER)
-  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY} lzma_lzma_preset "" LIBLZMA_HAS_LZMA_PRESET)
+  if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
+    set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY})
+  elseif(LIBLZMA_LIBRARY_RELEASE)
+    set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_RELEASE})
+  elseif(LIBLZMA_LIBRARY_DEBUG)
+    set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_DEBUG})
+  endif()
+  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_auto_decoder "" LIBLZMA_HAS_AUTO_DECODER)
+  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_easy_encoder "" LIBLZMA_HAS_EASY_ENCODER)
+  CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_lzma_preset "" LIBLZMA_HAS_LZMA_PRESET)
+  unset(LIBLZMA_LIBRARY_check)
   set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
 endif ()
 
@@ -85,7 +100,25 @@
         add_library(LibLZMA::LibLZMA UNKNOWN IMPORTED)
         set_target_properties(LibLZMA::LibLZMA PROPERTIES
                               INTERFACE_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIR}
-                              IMPORTED_LINK_INTERFACE_LANGUAGES C
-                              IMPORTED_LOCATION ${LIBLZMA_LIBRARY})
+                              IMPORTED_LINK_INTERFACE_LANGUAGES C)
+
+        if(LIBLZMA_LIBRARY_RELEASE)
+            set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
+                IMPORTED_CONFIGURATIONS RELEASE)
+            set_target_properties(LibLZMA::LibLZMA PROPERTIES
+                IMPORTED_LOCATION_RELEASE "${LIBLZMA_LIBRARY_RELEASE}")
+        endif()
+
+        if(LIBLZMA_LIBRARY_DEBUG)
+            set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
+                IMPORTED_CONFIGURATIONS DEBUG)
+            set_target_properties(LibLZMA::LibLZMA PROPERTIES
+                IMPORTED_LOCATION_DEBUG "${LIBLZMA_LIBRARY_DEBUG}")
+        endif()
+
+        if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
+            set_target_properties(LibLZMA::LibLZMA PROPERTIES
+                IMPORTED_LOCATION "${LIBLZMA_LIBRARY}")
+        endif()
     endif()
 endif ()
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake
index 2b9b20c..2779032 100644
--- a/Modules/FindMPI.cmake
+++ b/Modules/FindMPI.cmake
@@ -310,11 +310,15 @@
   set(_MPI_${LANG}_COMPILER_NAMES "")
   foreach (id IN ITEMS GNU Intel MSVC PGI XL)
     if (NOT CMAKE_${LANG}_COMPILER_ID OR CMAKE_${LANG}_COMPILER_ID STREQUAL id)
-      list(APPEND _MPI_${LANG}_COMPILER_NAMES ${_MPI_${id}_${LANG}_COMPILER_NAMES}${MPI_EXECUTABLE_SUFFIX})
+      foreach(_COMPILER_NAME IN LISTS _MPI_${id}_${LANG}_COMPILER_NAMES)
+        list(APPEND _MPI_${LANG}_COMPILER_NAMES ${_COMPILER_NAME}${MPI_EXECUTABLE_SUFFIX})
+      endforeach()
     endif()
     unset(_MPI_${id}_${LANG}_COMPILER_NAMES)
   endforeach()
-  list(APPEND _MPI_${LANG}_COMPILER_NAMES ${_MPI_${LANG}_GENERIC_COMPILER_NAMES}${MPI_EXECUTABLE_SUFFIX})
+  foreach(_COMPILER_NAME IN LISTS _MPI_${LANG}_GENERIC_COMPILER_NAMES)
+    list(APPEND _MPI_${LANG}_COMPILER_NAMES ${_COMPILER_NAME}${MPI_EXECUTABLE_SUFFIX})
+  endforeach()
   unset(_MPI_${LANG}_GENERIC_COMPILER_NAMES)
 endforeach()
 
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake
index 3547642..c8cae2e 100644
--- a/Modules/FindMatlab.cmake
+++ b/Modules/FindMatlab.cmake
@@ -224,6 +224,9 @@
   this list.
 #]=======================================================================]
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0057 NEW) # if IN_LIST
+
 set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}")
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
@@ -395,7 +398,7 @@
       )
 
 
-    if(${resultMatlab} EQUAL 0)
+    if(resultMatlab EQUAL 0)
 
       string(
         REGEX MATCHALL "MATLAB\\\\([0-9]+(\\.[0-9]+)?)"
@@ -606,15 +609,25 @@
     set(devnull INPUT_FILE NUL)
   endif()
 
+  if(WIN32)
+    # this environment variable is used to determine the arch on Windows
+    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+      set(ENV{MATLAB_ARCH} "win64")
+    else()
+      set(ENV{MATLAB_ARCH} "win32")
+    endif()
+  endif()
+
   # this is the preferred way. If this does not work properly (eg. MCR on Windows), then we use our own knowledge
   execute_process(
     COMMAND ${Matlab_MEXEXTENSIONS_PROG}
     OUTPUT_VARIABLE _matlab_mex_extension
-    #RESULT_VARIABLE _matlab_mex_extension_call
     ERROR_VARIABLE _matlab_mex_extension_error
+    OUTPUT_STRIP_TRAILING_WHITESPACE
     ${devnull})
+  unset(ENV{MATLAB_ARCH})
 
-  if(NOT "${_matlab_mex_extension_error}" STREQUAL "")
+  if(_matlab_mex_extension_error)
     if(WIN32)
       # this is only for intel architecture
       if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -627,7 +640,7 @@
 
   string(STRIP "${_matlab_mex_extension}"  _matlab_mex_extension)
   if(MATLAB_FIND_DEBUG)
-    message(STATUS "[MATLAB] '${Matlab_MEXEXTENSIONS_PROG}' : returned '${_matlab_mex_extension_call}', determined extension '${_matlab_mex_extension}' and error string is '${_matlab_mex_extension_error}'")
+    message(STATUS "[MATLAB] '${Matlab_MEXEXTENSIONS_PROG}' : determined extension '${_matlab_mex_extension}' and error string is '${_matlab_mex_extension_error}'")
   endif()
 
   unset(Matlab_MEXEXTENSIONS_PROG CACHE)
@@ -700,7 +713,7 @@
     ${devnull}
     )
 
-  if("${_matlab_result_version_call}" MATCHES "timeout")
+  if(_matlab_result_version_call MATCHES "timeout")
     if(MATLAB_FIND_DEBUG)
       message(WARNING "[MATLAB] Unable to determine the version of Matlab."
         " Matlab call timed out after 120 seconds.")
@@ -885,6 +898,7 @@
          [DOCUMENTATION file.txt]
          [LINK_TO target1 target2 ...]
          [R2017b | R2018a]
+         [EXCLUDE_FROM_ALL]
          [...]
      )
 
@@ -914,6 +928,10 @@
   ``MODULE`` or ``SHARED`` may be given to specify the type of library to be
     created. ``EXECUTABLE`` may be given to create an executable instead of
     a library. If no type is given explicitly, the type is ``SHARED``.
+  ``EXCLUDE_FROM_ALL``
+    This option has the same meaning as for :prop_tgt:`EXCLUDE_FROM_ALL` and
+    is forwarded to :command:`add_library` or :command:`add_executable`
+    commands.
 
   The documentation file is not processed and should be in the following
   format:
@@ -940,7 +958,7 @@
 
   endif()
 
-  set(options EXECUTABLE MODULE SHARED R2017b R2018a)
+  set(options EXECUTABLE MODULE SHARED R2017b R2018a EXCLUDE_FROM_ALL)
   set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME)
   set(multiValueArgs LINK_TO SRC)
 
@@ -955,14 +973,14 @@
     set(${prefix}_OUTPUT_NAME ${${prefix}_NAME})
   endif()
 
-  if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file
+  if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file
     # TODO: check the file extensions in ${${prefix}_SRC} to see if they're C or C++ files
     # Currently, the C and C++ versions of the version files are identical, so this doesn't matter.
     set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/c_mexapi_version.c")
     #set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/cpp_mexapi_version.cpp")
   endif()
 
-  if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.4") # For 9.4 (R2018a) and newer, add API macro
+  if(NOT Matlab_VERSION_STRING VERSION_LESS "9.4") # For 9.4 (R2018a) and newer, add API macro
     if(${${prefix}_R2018a})
       set(MEX_API_MACRO "MATLAB_DEFAULT_RELEASE=R2018a")
     else()
@@ -970,8 +988,14 @@
     endif()
   endif()
 
+  set(_option_EXCLUDE_FROM_ALL)
+  if(${prefix}_EXCLUDE_FROM_ALL)
+    set(_option_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
+  endif()
+
   if(${prefix}_EXECUTABLE)
     add_executable(${${prefix}_NAME}
+      ${_option_EXCLUDE_FROM_ALL}
       ${${prefix}_SRC}
       ${MEX_VERSION_FILE}
       ${${prefix}_DOCUMENTATION}
@@ -985,6 +1009,7 @@
 
     add_library(${${prefix}_NAME}
       ${type}
+      ${_option_EXCLUDE_FROM_ALL}
       ${${prefix}_SRC}
       ${MEX_VERSION_FILE}
       ${${prefix}_DOCUMENTATION}
@@ -1023,18 +1048,13 @@
     if (MSVC)
 
       set(_link_flags "${_link_flags} /EXPORT:mexFunction")
-      if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version
+      if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version
         set(_link_flags "${_link_flags} /EXPORT:mexfilerequiredapiversion")
       endif()
 
-      if(Matlab_HAS_CPP_API)
-        set(_link_flags "${_link_flags} /EXPORT:mexCreateMexFunction /EXPORT:mexDestroyMexFunction /EXPORT:mexFunctionAdapter")
-        #TODO: Is this necessary?
-      endif()
-
       set_property(TARGET ${${prefix}_NAME} APPEND PROPERTY LINK_FLAGS ${_link_flags})
 
-    endif() # TODO: what if there's a different compiler on Windows?
+    endif() # No other compiler currently supported on Windows.
 
     set_target_properties(${${prefix}_NAME}
       PROPERTIES
@@ -1042,18 +1062,18 @@
 
   else()
 
-    if(${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For versions prior to 9.1 (R2016b)
+    if(Matlab_VERSION_STRING VERSION_LESS "9.1") # For versions prior to 9.1 (R2016b)
       set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/mexFunction.map)
     else()                                          # For 9.1 (R2016b) and newer
       set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/c_exportsmexfileversion.map)
     endif()
 
-    if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.5") # For 9.5 (R2018b) (and newer?)
+    if(NOT Matlab_VERSION_STRING VERSION_LESS "9.5") # For 9.5 (R2018b) (and newer?)
       target_compile_options(${${prefix}_NAME} PRIVATE "-fvisibility=default")
       # This one is weird, it might be a bug in <mex.h> for R2018b. When compiling with
       # -fvisibility=hidden, the symbol `mexFunction` cannot be exported. Reading the
       # source code for <mex.h>, it seems that the preprocessor macro `MW_NEEDS_VERSION_H`
-      # needs to be defined for `__attribute__ ((visibility("default")))` to be added
+      # needs to be defined for `__attribute__((visibility("default")))` to be added
       # in front of the declaration of `mexFunction`. In previous versions of MATLAB this
       # was not the case, there `DLL_EXPORT_SYM` needed to be defined.
       # Adding `-fvisibility=hidden` to the `mex` command causes the build to fail.
@@ -1089,11 +1109,13 @@
       set(_link_flags "${_link_flags} -Wl,${_export_flag_name},${_file}")
     endforeach()
 
+    # The `mex` command doesn't add this define. It is specified here in order
+    # to export the symbol in case the client code decides to hide its symbols
     set_target_properties(${${prefix}_NAME}
       PROPERTIES
-        DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__ ((visibility (\"default\")))"
+        DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__((visibility(\"default\")))"
         LINK_FLAGS "${_link_flags}"
-    ) # The `mex` command doesn't add this define. Is it necessary?
+    )
 
   endif()
 
@@ -1112,14 +1134,14 @@
   #  set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version")
   #endif()
 
-  if(NOT ${matlab_known_version} STREQUAL "NOTFOUND")
+  if(NOT matlab_known_version STREQUAL "NOTFOUND")
     # the version is known, we just return it
     set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE)
     set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
     return()
   endif()
 
-  if("${matlab_or_mcr}" STREQUAL "UNKNOWN")
+  if(matlab_or_mcr STREQUAL "UNKNOWN")
     if(MATLAB_FIND_DEBUG)
       message(WARNING "[MATLAB] Determining Matlab or MCR")
     endif()
@@ -1134,10 +1156,10 @@
 
       # default fallback to Matlab
       set(matlab_or_mcr "MATLAB")
-      if(NOT "${CMAKE_MATCH_1}" STREQUAL "")
+      if(NOT CMAKE_MATCH_1 STREQUAL "")
         string(TOLOWER "${CMAKE_MATCH_1}" product_reg_match)
 
-        if("${product_reg_match}" STREQUAL "matlab runtime")
+        if(product_reg_match STREQUAL "matlab runtime")
           set(matlab_or_mcr "MCR")
         endif()
       endif()
@@ -1151,7 +1173,7 @@
   # UNKNOWN is the default behaviour in case we
   # - have an erroneous matlab_root
   # - have an initial 'UNKNOWN'
-  if("${matlab_or_mcr}" STREQUAL "MATLAB" OR "${matlab_or_mcr}" STREQUAL "UNKNOWN")
+  if(matlab_or_mcr STREQUAL "MATLAB" OR matlab_or_mcr STREQUAL "UNKNOWN")
     # MATLAB versions
     set(_matlab_current_program ${Matlab_MAIN_PROGRAM})
 
@@ -1203,7 +1225,7 @@
     matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions)
 
     list(LENGTH matlab_list_of_all_versions list_of_all_versions_length)
-    if(${list_of_all_versions_length} GREATER 0)
+    if(list_of_all_versions_length GREATER 0)
       list(GET matlab_list_of_all_versions 0 _matlab_version_tmp)
     else()
       set(_matlab_version_tmp "unknown")
@@ -1213,7 +1235,7 @@
     set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
 
     # warning, just in case several versions found (should not happen)
-    if((${list_of_all_versions_length} GREATER 1) AND MATLAB_FIND_DEBUG)
+    if((list_of_all_versions_length GREATER 1) AND MATLAB_FIND_DEBUG)
       message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})")
     endif()
 
@@ -1233,10 +1255,8 @@
              ${versioninfo_string}
             )
 
-      if(NOT "${version_reg_match}" STREQUAL "")
-        if("${CMAKE_MATCH_1}" MATCHES "(([0-9])\\.([0-9]))[\\.0-9]*")
-          set(_matlab_version_tmp "${CMAKE_MATCH_1}")
-        endif()
+      if(CMAKE_MATCH_1 MATCHES "(([0-9])\\.([0-9]))[\\.0-9]*")
+        set(_matlab_version_tmp "${CMAKE_MATCH_1}")
       endif()
     endif()
     set(${matlab_final_version} "${_matlab_version_tmp}" PARENT_SCOPE)
@@ -1442,14 +1462,28 @@
 set(Matlab_VERSION_STRING "NOTFOUND")
 set(Matlab_Or_MCR "UNKNOWN")
 if(_numbers_of_matlab_roots GREATER 0)
-  list(GET _matlab_possible_roots 0 Matlab_Or_MCR)
-  list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING)
-  list(GET _matlab_possible_roots 2 Matlab_ROOT_DIR)
+  if(Matlab_FIND_VERSION_EXACT)
+    list(FIND _matlab_possible_roots ${Matlab_FIND_VERSION} _list_index)
+    if(_list_index LESS 0)
+      set(_list_index 1)
+    endif()
 
-  # adding a warning in case of ambiguity
-  if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
-    message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
-                    " If this is not the desired behaviour, provide the -DMatlab_ROOT_DIR=... on the command line")
+    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
+    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
+
+    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
+    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
+    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
+  else()
+    list(GET _matlab_possible_roots 0 Matlab_Or_MCR)
+    list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING)
+    list(GET _matlab_possible_roots 2 Matlab_ROOT_DIR)
+
+    # adding a warning in case of ambiguity
+    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
+      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
+                      " If this is not the desired behaviour, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
+    endif()
   endif()
 endif()
 
@@ -1503,7 +1537,9 @@
   message(STATUS "[MATLAB] Current version is ${Matlab_VERSION_STRING} located ${Matlab_ROOT_DIR}")
 endif()
 
-if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.4") # MATLAB 9.4 (R2018a) and newer have a new C++ API
+# MATLAB 9.4 (R2018a) and newer have a new C++ API
+# This API pulls additional required libraries.
+if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.4")
   set(Matlab_HAS_CPP_API 1)
 endif()
 
@@ -1589,6 +1625,10 @@
 
 set(_matlab_required_variables)
 
+# Order is as follow:
+# - unconditionally required libraries/headers first
+# - then library components
+# - then program components
 
 # the MEX library/header are required
 find_path(
@@ -1614,38 +1654,6 @@
 # the matlab root is required
 list(APPEND _matlab_required_variables Matlab_ROOT_DIR)
 
-# component Mex Compiler
-list(FIND Matlab_FIND_COMPONENTS MEX_COMPILER _matlab_find_mex_compiler)
-if(_matlab_find_mex_compiler GREATER -1)
-  find_program(
-    Matlab_MEX_COMPILER
-    "mex"
-    PATHS ${Matlab_BINARIES_DIR}
-    DOC "Matlab MEX compiler"
-    NO_DEFAULT_PATH
-  )
-  if(Matlab_MEX_COMPILER)
-    set(Matlab_MEX_COMPILER_FOUND TRUE)
-  endif()
-endif()
-unset(_matlab_find_mex_compiler)
-
-# component Matlab program
-list(FIND Matlab_FIND_COMPONENTS MAIN_PROGRAM _matlab_find_matlab_program)
-if(_matlab_find_matlab_program GREATER -1)
-  find_program(
-    Matlab_MAIN_PROGRAM
-    matlab
-    PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin
-    DOC "Matlab main program"
-    NO_DEFAULT_PATH
-  )
-  if(Matlab_MAIN_PROGRAM)
-    set(Matlab_MAIN_PROGRAM_FOUND TRUE)
-  endif()
-endif()
-unset(_matlab_find_matlab_program)
-
 # The MX library is required
 _Matlab_find_library(
   ${_matlab_lib_prefix_for_search}
@@ -1659,70 +1667,6 @@
   set(Matlab_MX_LIBRARY_FOUND TRUE)
 endif()
 
-# Component ENG library
-list(FIND Matlab_FIND_COMPONENTS ENG_LIBRARY _matlab_find_eng)
-if(_matlab_find_eng GREATER -1)
-  _Matlab_find_library(
-    ${_matlab_lib_prefix_for_search}
-    Matlab_ENG_LIBRARY
-    eng
-    PATHS ${_matlab_lib_dir_for_search}
-    NO_DEFAULT_PATH
-  )
-  if(Matlab_ENG_LIBRARY)
-    set(Matlab_ENG_LIBRARY_FOUND TRUE)
-  endif()
-endif()
-unset(_matlab_find_eng)
-
-# Component MAT library
-list(FIND Matlab_FIND_COMPONENTS MAT_LIBRARY _matlab_find_mat)
-if(_matlab_find_mat GREATER -1)
-  _Matlab_find_library(
-    ${_matlab_lib_prefix_for_search}
-    Matlab_MAT_LIBRARY
-    mat
-    PATHS ${_matlab_lib_dir_for_search}
-    NO_DEFAULT_PATH
-  )
-  if(Matlab_MAT_LIBRARY)
-    set(Matlab_MAT_LIBRARY_FOUND TRUE)
-  endif()
-endif()
-unset(_matlab_find_mat)
-
-# Component Simulink
-list(FIND Matlab_FIND_COMPONENTS SIMULINK _matlab_find_simulink)
-if(_matlab_find_simulink GREATER -1)
-  find_path(
-    Matlab_SIMULINK_INCLUDE_DIR
-    simstruc.h
-    PATHS "${Matlab_ROOT_DIR}/simulink/include"
-    NO_DEFAULT_PATH
-    )
-  if(Matlab_SIMULINK_INCLUDE_DIR)
-    set(Matlab_SIMULINK_FOUND TRUE)
-    list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}")
-  endif()
-endif()
-unset(_matlab_find_simulink)
-
-# component MCC Compiler
-list(FIND Matlab_FIND_COMPONENTS MCC_COMPILER _matlab_find_mcc_compiler)
-if(_matlab_find_mcc_compiler GREATER -1)
-  find_program(
-    Matlab_MCC_COMPILER
-    "mcc"
-    PATHS ${Matlab_BINARIES_DIR}
-    DOC "Matlab MCC compiler"
-    NO_DEFAULT_PATH
-  )
-  if(Matlab_MCC_COMPILER)
-    set(Matlab_MCC_COMPILER_FOUND TRUE)
-  endif()
-endif()
-unset(_matlab_find_mcc_compiler)
-
 if(Matlab_HAS_CPP_API)
 
   # The MatlabEngine library is required for R2018a+
@@ -1755,7 +1699,89 @@
 
 endif()
 
-unset(_matlab_lib_dir_for_search)
+# Component ENG library
+if("ENG_LIBRARY" IN_LIST Matlab_FIND_COMPONENTS)
+  _Matlab_find_library(
+    ${_matlab_lib_prefix_for_search}
+    Matlab_ENG_LIBRARY
+    eng
+    PATHS ${_matlab_lib_dir_for_search}
+    NO_DEFAULT_PATH
+  )
+  if(Matlab_ENG_LIBRARY)
+    set(Matlab_ENG_LIBRARY_FOUND TRUE)
+  endif()
+endif()
+
+# Component MAT library
+if("MAT_LIBRARY" IN_LIST Matlab_FIND_COMPONENTS)
+  _Matlab_find_library(
+    ${_matlab_lib_prefix_for_search}
+    Matlab_MAT_LIBRARY
+    mat
+    PATHS ${_matlab_lib_dir_for_search}
+    NO_DEFAULT_PATH
+  )
+  if(Matlab_MAT_LIBRARY)
+    set(Matlab_MAT_LIBRARY_FOUND TRUE)
+  endif()
+endif()
+
+# Component Simulink
+if("SIMULINK" IN_LIST Matlab_FIND_COMPONENTS)
+  find_path(
+    Matlab_SIMULINK_INCLUDE_DIR
+    simstruc.h
+    PATHS "${Matlab_ROOT_DIR}/simulink/include"
+    NO_DEFAULT_PATH
+    )
+  if(Matlab_SIMULINK_INCLUDE_DIR)
+    set(Matlab_SIMULINK_FOUND TRUE)
+    list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}")
+  endif()
+endif()
+
+# component Matlab program
+if("MAIN_PROGRAM" IN_LIST Matlab_FIND_COMPONENTS)
+  find_program(
+    Matlab_MAIN_PROGRAM
+    matlab
+    PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin
+    DOC "Matlab main program"
+    NO_DEFAULT_PATH
+  )
+  if(Matlab_MAIN_PROGRAM)
+    set(Matlab_MAIN_PROGRAM_FOUND TRUE)
+  endif()
+endif()
+
+# component Mex Compiler
+if("MEX_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
+  find_program(
+    Matlab_MEX_COMPILER
+    "mex"
+    PATHS ${Matlab_BINARIES_DIR}
+    DOC "Matlab MEX compiler"
+    NO_DEFAULT_PATH
+  )
+  if(Matlab_MEX_COMPILER)
+    set(Matlab_MEX_COMPILER_FOUND TRUE)
+  endif()
+endif()
+
+# component MCC Compiler
+if("MCC_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
+  find_program(
+    Matlab_MCC_COMPILER
+    "mcc"
+    PATHS ${Matlab_BINARIES_DIR}
+    DOC "Matlab MCC compiler"
+    NO_DEFAULT_PATH
+  )
+  if(Matlab_MCC_COMPILER)
+    set(Matlab_MCC_COMPILER_FOUND TRUE)
+  endif()
+endif()
 
 set(Matlab_LIBRARIES
   ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY}
@@ -1792,3 +1818,5 @@
     Matlab_MEX_EXTENSION
   )
 endif()
+
+cmake_policy(POP)
diff --git a/Modules/FindOpenACC.cmake b/Modules/FindOpenACC.cmake
index dc8321d..743e0e2 100644
--- a/Modules/FindOpenACC.cmake
+++ b/Modules/FindOpenACC.cmake
@@ -22,6 +22,14 @@
   Variable indicating if OpenACC support for ``<lang>`` was detected.
 ``OpenACC_<lang>_FLAGS``
   OpenACC compiler flags for ``<lang>``, separated by spaces.
+``OpenACC_<lang>_OPTIONS``
+  OpenACC compiler flags for ``<lang>``, as a list. Suitable for usage
+  with target_compile_options or target_link_options.
+
+Additionally, the module provides :prop_tgt:`IMPORTED` targets:
+
+``OpenACC::OpenACC_<lang>``
+  Target for using OpenACC from ``<lang>``.
 
 The module will also try to provide the OpenACC version variables:
 
@@ -60,9 +68,7 @@
 set(OpenACC_Fortran_TEST_SOURCE
 "
 program test
-#ifdef _OPENACC
-  return 0;
-#else
+#ifndef _OPENACC
   breaks_on_purpose
 #endif
 endprogram test
@@ -241,6 +247,9 @@
     if(NOT DEFINED OpenACC_${LANG}_FLAGS)
       _OPENACC_GET_FLAGS("${LANG}" OpenACC_${LANG}_FLAGS)
     endif()
+    if(NOT DEFINED OpenACC_${LANG}_OPTIONS)
+      separate_arguments(OpenACC_${LANG}_OPTIONS NATIVE_COMMAND "${OpenACC_${LANG}_FLAGS}")
+    endif()
     _OPENACC_GET_SPEC_DATE("${LANG}" OpenACC_${LANG}_SPEC_DATE)
     _OPENACC_SET_VERSION_BY_SPEC_DATE("${LANG}")
 
@@ -251,6 +260,23 @@
   endif()
 endforeach()
 
+foreach (LANG IN ITEMS C CXX Fortran)
+  if(OpenACC_${LANG}_FOUND AND NOT TARGET OpenACC::OpenACC_${LANG})
+    add_library(OpenACC::OpenACC_${LANG} INTERFACE IMPORTED)
+  endif()
+  if(OpenACC_${LANG}_LIBRARIES)
+    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
+      INTERFACE_LINK_LIBRARIES "${OpenACC_${LANG}_LIBRARIES}")
+  endif()
+  if(OpenACC_${LANG}_FLAGS)
+    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
+      INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenACC_${LANG}_OPTIONS}>")
+    set_property(TARGET OpenACC::OpenACC_${LANG} PROPERTY
+      INTERFACE_LINK_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenACC_${LANG}_OPTIONS}>")
+    unset(_OpenACC_${LANG}_OPTIONS)
+  endif()
+endforeach()
+
 unset(OpenACC_C_CXX_TEST_SOURCE)
 unset(OpenACC_Fortran_TEST_SOURCE)
 unset(OpenACC_C_CXX_CHECK_VERSION_SOURCE)
diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake
index 5f947fe..33ceab7 100644
--- a/Modules/FindOpenSSL.cmake
+++ b/Modules/FindOpenSSL.cmake
@@ -35,10 +35,14 @@
   The OpenSSL include directory.
 ``OPENSSL_CRYPTO_LIBRARY``
   The OpenSSL crypto library.
+``OPENSSL_CRYPTO_LIBRARIES``
+  The OpenSSL crypto library and its dependencies.
 ``OPENSSL_SSL_LIBRARY``
   The OpenSSL SSL library.
+``OPENSSL_SSL_LIBRARIES``
+  The OpenSSL SSL library and its dependencies.
 ``OPENSSL_LIBRARIES``
-  All OpenSSL libraries.
+  All OpenSSL libraries and their dependencies.
 ``OPENSSL_VERSION``
   This is set to ``$major.$minor.$revision$patch`` (e.g. ``0.9.8s``).
 
@@ -50,6 +54,32 @@
 Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib.
 #]=======================================================================]
 
+macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library)
+  if((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND
+     (("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR
+      ("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$")))
+    set(_OpenSSL_has_dependencies TRUE)
+    find_package(Threads)
+  else()
+    set(_OpenSSL_has_dependencies FALSE)
+  endif()
+endmacro()
+
+function(_OpenSSL_add_dependencies libraries_var library)
+  if(CMAKE_THREAD_LIBS_INIT)
+    list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT})
+  endif()
+  list(APPEND ${libraries_var} ${CMAKE_DL_LIBS})
+  set(${libraries_var} ${${libraries_var}} PARENT_SCOPE)
+endfunction()
+
+function(_OpenSSL_target_add_dependencies target)
+  if(_OpenSSL_has_dependencies)
+    set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads )
+    set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} )
+  endif()
+endfunction()
+
 if (UNIX)
   find_package(PkgConfig QUIET)
   pkg_check_modules(_OPENSSL QUIET openssl)
@@ -306,10 +336,15 @@
 
   mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY)
 
-  # compat defines
-  set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY})
-  set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+endif()
 
+# compat defines
+set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY})
+set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+_OpenSSL_test_and_find_dependencies("${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}")
+if(_OpenSSL_has_dependencies)
+  _OpenSSL_add_dependencies( OPENSSL_SSL_LIBRARIES "${OPENSSL_SSL_LIBRARY}" )
+  _OpenSSL_add_dependencies( OPENSSL_CRYPTO_LIBRARIES "${OPENSSL_CRYPTO_LIBRARY}" )
 endif()
 
 function(from_hex HEX DEC)
@@ -379,7 +414,8 @@
   endif ()
 endif ()
 
-set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} )
+set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} )
+list(REMOVE_DUPLICATES OPENSSL_LIBRARIES)
 
 foreach(_comp IN LISTS OpenSSL_FIND_COMPONENTS)
   if(_comp STREQUAL "Crypto")
@@ -451,6 +487,7 @@
         IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
         IMPORTED_LOCATION_DEBUG "${LIB_EAY_LIBRARY_DEBUG}")
     endif()
+    _OpenSSL_target_add_dependencies(OpenSSL::Crypto)
   endif()
 
   if(NOT TARGET OpenSSL::SSL AND
@@ -484,6 +521,7 @@
       set_target_properties(OpenSSL::SSL PROPERTIES
         INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
     endif()
+    _OpenSSL_target_add_dependencies(OpenSSL::SSL)
   endif()
 endif()
 
diff --git a/Modules/FindPNG.cmake b/Modules/FindPNG.cmake
index a7908c5..bd400c7 100644
--- a/Modules/FindPNG.cmake
+++ b/Modules/FindPNG.cmake
@@ -69,8 +69,8 @@
     unset(_PNG_VERSION_SUFFIX_MIN)
   endif ()
   foreach(v IN LISTS _PNG_VERSION_SUFFIXES)
-    list(APPEND PNG_NAMES png${v} libpng${v})
-    list(APPEND PNG_NAMES_DEBUG png${v}d libpng${v}d)
+    list(APPEND PNG_NAMES png${v} libpng${v} libpng${v}_static)
+    list(APPEND PNG_NAMES_DEBUG png${v}d libpng${v}d libpng${v}_staticd)
   endforeach()
   unset(_PNG_VERSION_SUFFIXES)
   # For compatibility with versions prior to this multi-config search, honor
diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake
index 1722d6a..d824ee8 100644
--- a/Modules/FindPackageHandleStandardArgs.cmake
+++ b/Modules/FindPackageHandleStandardArgs.cmake
@@ -27,6 +27,7 @@
       [VERSION_VAR <version-var>]
       [HANDLE_COMPONENTS]
       [CONFIG_MODE]
+      [REASON_FAILURE_MESSAGE <reason-failure-message>]
       [FAIL_MESSAGE <custom-failure-message>]
       )
 
@@ -81,6 +82,10 @@
     will automatically check whether the package configuration file
     was found.
 
+  ``REASON_FAILURE_MESSAGE <reason-failure-message>``
+    Specify a custom message of the reason for the failure which will be
+    appended to the default generated message.
+
   ``FAIL_MESSAGE <custom-failure-message>``
     Specify a custom failure message instead of using the default
     generated message.  Not recommended.
@@ -133,11 +138,15 @@
 
 # internal helper macro
 macro(_FPHSA_FAILURE_MESSAGE _msg)
+  set (__msg "${_msg}")
+  if (FPHSA_REASON_FAILURE_MESSAGE)
+    string(APPEND __msg "\n    Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n")
+  endif()
   if (${_NAME}_FIND_REQUIRED)
-    message(FATAL_ERROR "${_msg}")
+    message(FATAL_ERROR "${__msg}")
   else ()
     if (NOT ${_NAME}_FIND_QUIETLY)
-      message(STATUS "${_msg}")
+      message(STATUS "${__msg}")
     endif ()
   endif ()
 endmacro()
@@ -158,12 +167,18 @@
       foreach(currentConfigIndex RANGE ${configsCount})
         list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
         list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
-        string(APPEND configsText "    ${filename} (version ${version})\n")
+        string(APPEND configsText "\n    ${filename} (version ${version})")
       endforeach()
       if (${_NAME}_NOT_FOUND_MESSAGE)
-        string(APPEND configsText "    Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n")
+        if (FPHSA_REASON_FAILURE_MESSAGE)
+          string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n    ")
+        else()
+          set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}")
+        endif()
+      else()
+        string(APPEND configsText "\n")
       endif()
-      _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}")
+      _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}")
 
     else()
       # Simple case: No Config-file was found at all:
@@ -177,7 +192,7 @@
 
 # Set up the arguments for `cmake_parse_arguments`.
   set(options  CONFIG_MODE  HANDLE_COMPONENTS)
-  set(oneValueArgs  FAIL_MESSAGE  VERSION_VAR  FOUND_VAR)
+  set(oneValueArgs  FAIL_MESSAGE  REASON_FAILURE_MESSAGE VERSION_VAR  FOUND_VAR)
   set(multiValueArgs REQUIRED_VARS)
 
 # Check whether we are in 'simple' or 'extended' mode:
@@ -264,14 +279,14 @@
       if(${_NAME}_${comp}_FOUND)
 
         if(NOT DEFINED FOUND_COMPONENTS_MSG)
-          set(FOUND_COMPONENTS_MSG "found components: ")
+          set(FOUND_COMPONENTS_MSG "found components:")
         endif()
         string(APPEND FOUND_COMPONENTS_MSG " ${comp}")
 
       else()
 
         if(NOT DEFINED MISSING_COMPONENTS_MSG)
-          set(MISSING_COMPONENTS_MSG "missing components: ")
+          set(MISSING_COMPONENTS_MSG "missing components:")
         endif()
         string(APPEND MISSING_COMPONENTS_MSG " ${comp}")
 
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index e05d5c8..5162a44 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -214,7 +214,11 @@
                  NAMES ${_pkg_search}
                  ${_find_opts})
     mark_as_advanced(pkgcfg_lib_${_prefix}_${_pkg_search})
-    list(APPEND _libs "${pkgcfg_lib_${_prefix}_${_pkg_search}}")
+    if(pkgcfg_lib_${_prefix}_${_pkg_search})
+      list(APPEND _libs "${pkgcfg_lib_${_prefix}_${_pkg_search}}")
+    else()
+      list(APPEND _libs ${_pkg_search})
+    endif()
   endforeach()
 
   set(${_prefix}_LINK_LIBRARIES "${_libs}" PARENT_SCOPE)
@@ -363,6 +367,7 @@
   _pkgconfig_unset(${_prefix}_PREFIX)
   _pkgconfig_unset(${_prefix}_INCLUDEDIR)
   _pkgconfig_unset(${_prefix}_LIBDIR)
+  _pkgconfig_unset(${_prefix}_MODULE_NAME)
   _pkgconfig_unset(${_prefix}_LIBS)
   _pkgconfig_unset(${_prefix}_LIBS_L)
   _pkgconfig_unset(${_prefix}_LIBS_PATHS)
@@ -480,6 +485,7 @@
         foreach (variable IN ITEMS PREFIX INCLUDEDIR LIBDIR)
           _pkgconfig_set("${_pkg_check_prefix}_${variable}" "${${_pkg_check_prefix}_${variable}}")
         endforeach ()
+          _pkgconfig_set("${_pkg_check_prefix}_MODULE_NAME" "${_pkg_check_modules_pkg}")
 
         if (NOT ${_is_silent})
           message(STATUS "  Found ${_pkg_check_modules_pkg}, version ${_pkgconfig_VERSION}")
@@ -664,6 +670,10 @@
                       [IMPORTED_TARGET [GLOBAL]]
                       <moduleSpec> [<moduleSpec>...])
 
+  If a module is found, the ``<prefix>_MODULE_NAME`` variable will contain the
+  name of the matching module. This variable can be used if you need to run
+  :command:`pkg_get_variable`.
+
   Example:
 
   .. code-block:: cmake
@@ -688,6 +698,7 @@
 
       if (${_prefix}_FOUND)
         set(_pkg_modules_found 1)
+        break()
       endif()
     endforeach()
 
diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake
index 76bc873..2d45965 100644
--- a/Modules/FindProtobuf.cmake
+++ b/Modules/FindProtobuf.cmake
@@ -521,6 +521,16 @@
             set_target_properties(protobuf::libprotobuf PROPERTIES
               IMPORTED_LOCATION_DEBUG "${Protobuf_LIBRARY_DEBUG}")
           endif()
+          if (Protobuf_VERSION VERSION_GREATER_EQUAL "3.6")
+            set_property(TARGET protobuf::libprotobuf APPEND PROPERTY
+              INTERFACE_COMPILE_FEATURES cxx_std_11
+            )
+          endif()
+          if (MSVC AND NOT Protobuf_USE_STATIC_LIBS)
+            set_property(TARGET protobuf::libprotobuf APPEND PROPERTY
+              INTERFACE_COMPILE_DEFINITIONS "PROTOBUF_USE_DLLS"
+            )
+          endif()
           if(UNIX AND TARGET Threads::Threads)
             set_property(TARGET protobuf::libprotobuf APPEND PROPERTY
                 INTERFACE_LINK_LIBRARIES Threads::Threads)
@@ -549,6 +559,11 @@
             set_target_properties(protobuf::libprotobuf-lite PROPERTIES
               IMPORTED_LOCATION_DEBUG "${Protobuf_LITE_LIBRARY_DEBUG}")
           endif()
+          if (MSVC AND NOT Protobuf_USE_STATIC_LIBS)
+            set_property(TARGET protobuf::libprotobuf-lite APPEND PROPERTY
+              INTERFACE_COMPILE_DEFINITIONS "PROTOBUF_USE_DLLS"
+            )
+          endif()
           if(UNIX AND TARGET Threads::Threads)
             set_property(TARGET protobuf::libprotobuf-lite APPEND PROPERTY
                 INTERFACE_LINK_LIBRARIES Threads::Threads)
@@ -577,6 +592,16 @@
             set_target_properties(protobuf::libprotoc PROPERTIES
               IMPORTED_LOCATION_DEBUG "${Protobuf_PROTOC_LIBRARY_DEBUG}")
           endif()
+          if (Protobuf_VERSION VERSION_GREATER_EQUAL "3.6")
+            set_property(TARGET protobuf::libprotoc APPEND PROPERTY
+              INTERFACE_COMPILE_FEATURES cxx_std_11
+            )
+          endif()
+          if (MSVC AND NOT Protobuf_USE_STATIC_LIBS)
+            set_property(TARGET protobuf::libprotoc APPEND PROPERTY
+              INTERFACE_COMPILE_DEFINITIONS "PROTOBUF_USE_DLLS"
+            )
+          endif()
           if(UNIX AND TARGET Threads::Threads)
             set_property(TARGET protobuf::libprotoc APPEND PROPERTY
                 INTERFACE_LINK_LIBRARIES Threads::Threads)
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index e2f3bf3..3cc08a1 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -137,6 +137,51 @@
   * If set to TRUE, search **only** for static libraries.
   * If set to FALSE, search **only** for shared libraries.
 
+``Python_FIND_ABI``
+  This variable defines which ABIs, as defined in
+  `PEP 3149 <https://www.python.org/dev/peps/pep-3149/>`_, should be searched.
+
+  .. note::
+
+    This hint will be honored only when searched for ``Python`` version 3.
+
+  .. note::
+
+    If ``Python_FIND_ABI`` is not defined, any ABI will be searched.
+
+  The ``Python_FIND_ABI`` variable is a 3-tuple specifying, in that order,
+  ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags.
+  Each element can be set to one of the following:
+
+  * ``ON``: Corresponding flag is selected.
+  * ``OFF``: Corresponding flag is not selected.
+  * ``ANY``: The two posibilties (``ON`` and ``OFF``) will be searched.
+
+  From this 3-tuple, various ABIs will be searched starting from the most
+  specialized to the most general. Moreover, ``debug`` versions will be
+  searched **after** ``non-debug`` ones.
+
+  For example, if we have::
+
+    set (Python_FIND_ABI "ON" "ANY" "ANY")
+
+  The following flags combinations will be appended, in that order, to the
+  artifact names: ``dmu``, ``dm``, ``du``, and ``d``.
+
+  And to search any possible ABIs::
+
+    set (Python_FIND_ABI "ANY" "ANY" "ANY")
+
+  The following combinations, in that order, will be used: ``mu``, ``m``,
+  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``.
+
+  .. note::
+
+    This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems,
+    when ``Python_FIND_ABI`` is defined, ``Python`` distributions from
+    `python.org <https://www.python.org/>`_ will be found only if value for
+    each flag is ``OFF`` or ``ANY``.
+
 ``Python_FIND_STRATEGY``
   This variable defines how lookup will be done.
   The ``Python_FIND_STRATEGY`` variable can be set to empty or one of the
@@ -192,6 +237,50 @@
     ``NEVER`` to select preferably the interpreter from the virtual
     environment.
 
+  .. note::
+
+    If the component ``Development`` is requested, it is **strongly**
+    recommended to also include the component ``Interpreter`` to get expected
+    result.
+
+Artifacts Specification
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To solve special cases, it is possible to specify directly the artifacts by
+setting the following variables:
+
+``Python_EXECUTABLE``
+  The path to the interpreter.
+
+``Python_COMPILER``
+  The path to the compiler.
+
+``Python_LIBRARY``
+  The path to the library. It will be used to compute the
+  variables ``Python_LIBRARIES``, ``Python_LIBRAY_DIRS`` and
+  ``Python_RUNTIME_LIBRARY_DIRS``.
+
+``Python_INCLUDE_DIR``
+  The path to the directory of the ``Python`` headers. It will be used to
+  compute the variable ``Python_INCLUDE_DIRS``.
+
+``Python_NumPy_INCLUDE_DIR``
+  The path to the directory of the ``NumPy`` headers. It will be used to
+  compute the variable ``Python_NumPy_INCLUDE_DIRS``.
+
+.. note::
+
+  All paths must be absolute. Any artifact specified with a relative path
+  will be ignored.
+
+.. note::
+
+  When an artifact is specified, all ``HINTS`` will be ignored and no search
+  will be performed for this artifact.
+
+  If more than one artifact is specified, it is the user's responsability to
+  ensure the consistency of the various artifacts.
+
 Commands
 ^^^^^^^^
 
@@ -224,6 +313,14 @@
   set (_Python_REQUIRED_VERSIONS 3 2)
   set (_Python_REQUIRED_VERSION_LAST 2)
 
+  unset (_Python_INPUT_VARS)
+  foreach (_Python_ITEM IN ITEMS Python_EXECUTABLE Python_COMPILER Python_LIBRARY
+                                 Python_INCLUDE_DIR Python_NumPy_INCLUDE_DIR)
+    if (NOT DEFINED ${_Python_ITEM})
+      list (APPEND _Python_INPUT_VARS ${_Python_ITEM})
+    endif()
+  endforeach()
+
   foreach (_Python_REQUIRED_VERSION_MAJOR IN LISTS _Python_REQUIRED_VERSIONS)
     set (Python_FIND_VERSION ${_Python_REQUIRED_VERSION_MAJOR})
     include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake)
@@ -231,6 +328,10 @@
         _Python_REQUIRED_VERSION_MAJOR EQUAL _Python_REQUIRED_VERSION_LAST)
       break()
     endif()
+    # clean-up INPUT variables not set by the user
+    foreach (_Python_ITEM IN LISTS _Python_INPUT_VARS)
+      unset (${_Python_ITEM})
+    endforeach()
     # clean-up some CACHE variables to ensure look-up restart from scratch
     foreach (_Python_ITEM IN LISTS _Python_CACHED_VARS)
       unset (${_Python_ITEM} CACHE)
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 590c7af..b67d563 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -96,71 +96,347 @@
        PARENT_SCOPE)
 endfunction()
 
-function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES _PYTHON_VERSION _PYTHON_TYPE)
-  set (path_suffixes)
 
-  if (_PYTHON_TYPE STREQUAL "LIBRARY")
+function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS)
+  set (abiflags)
+  list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug)
+  list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc)
+  list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode)
+
+  if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY")
+    set (abiflags "mu" "m" "u" "")
+  elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON")
+    set (abiflags "mu" "u")
+  elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF")
+    set (abiflags "m" "")
+  elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
+    set (abiflags "mu" "m")
+  elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON")
+    set (abiflags "mu")
+  elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF")
+    set (abiflags "m")
+  elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
+    set (abiflags "u" "")
+  elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON")
+    set (abiflags "u")
+  endif()
+
+  if (pydebug STREQUAL "ON")
+    if (abiflags)
+      list (TRANSFORM abiflags PREPEND "d")
+    else()
+      set (abiflags "d")
+    endif()
+  elseif (pydebug STREQUAL "ANY")
+    if (abiflags)
+      set (flags "${abiflags}")
+      list (TRANSFORM flags PREPEND "d")
+      list (APPEND abiflags "${flags}")
+    else()
+      set (abiflags "" "d")
+    endif()
+  endif()
+
+  set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE)
+endfunction()
+
+function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES)
+  cmake_parse_arguments (PARSE_ARGV 1 _PGPS "LIBRARY;INCLUDE" "VERSION" "")
+
+  if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
+    set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
+  else()
+    set (abi "mu" "m" "u" "")
+  endif()
+
+  set (path_suffixes)
+  if (_PGPS_LIBRARY)
     if (CMAKE_LIBRARY_ARCHITECTURE)
       list (APPEND path_suffixes lib/${CMAKE_LIBRARY_ARCHITECTURE})
     endif()
     list (APPEND path_suffixes lib libs)
 
     if (CMAKE_LIBRARY_ARCHITECTURE)
-      list (APPEND path_suffixes lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}mu-${CMAKE_LIBRARY_ARCHITECTURE}
-                                 lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}m-${CMAKE_LIBRARY_ARCHITECTURE}
-                                 lib/python${_PYTHON_VERSION}/config-${CMAKE_MATCH_1}u-${CMAKE_LIBRARY_ARCHITECTURE}
-                                 lib/python${_PYTHON_VERSION}/config-${CMAKE_MATCH_1}-${CMAKE_LIBRARY_ARCHITECTURE})
+      set (suffixes "${abi}")
+      if (suffixes)
+        list (TRANSFORM suffixes PREPEND "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}")
+        list (TRANSFORM suffixes APPEND "-${CMAKE_LIBRARY_ARCHITECTURE}")
+      else()
+        set (suffixes "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}-${CMAKE_LIBRARY_ARCHITECTURE}")
+      endif()
+      list (APPEND path_suffixes ${suffixes})
     endif()
-    list (APPEND path_suffixes lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}mu
-                               lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}m
-                               lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}u
-                               lib/python${_PYTHON_VERSION}/config-${_PYTHON_VERSION}
-                               lib/python${_PYTHON_VERSION}/config)
-
-  elseif (_PYTHON_TYPE STREQUAL "INCLUDE")
-    list (APPEND path_suffixes include/python${_PYTHON_VERSION}mu
-                               include/python${_PYTHON_VERSION}m
-                               include/python${_PYTHON_VERSION}u
-                               include/python${_PYTHON_VERSION}
-                               include)
+    set (suffixes "${abi}")
+    if (suffixes)
+      list (TRANSFORM suffixes PREPEND "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}")
+    else()
+      set (suffixes "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}")
+    endif()
+    list (APPEND path_suffixes ${suffixes})
+  elseif (_PGPS_INCLUDE)
+    set (suffixes "${abi}")
+    if (suffixes)
+      list (TRANSFORM suffixes PREPEND "include/python${_PGPS_VERSION}")
+    else()
+      set (suffixes "include/python${_PGPS_VERSION}")
+    endif()
+    list (APPEND path_suffixes ${suffixes} include)
   endif()
 
   set (${_PYTHON_PGPS_PATH_SUFFIXES} ${path_suffixes} PARENT_SCOPE)
 endfunction()
 
-function (_PYTHON_GET_LIB_NAMES _PYTHON_PGLN_NAMES _PYTHON_VERSION)
-  string (REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${_PYTHON_VERSION})
+function (_PYTHON_GET_NAMES _PYTHON_PGN_NAMES)
+  cmake_parse_arguments (PARSE_ARGV 1 _PGN "POSIX;EXECUTABLE;CONFIG;LIBRARY;WIN32;DEBUG" "VERSION" "")
 
-  if (ARGC EQUAL 3 AND ARGV2 STREQUAL "DEBUG")
-    set (${_PYTHON_PGLN_NAMES} python${_PYTHON_VERSION_NO_DOTS}_d PARENT_SCOPE)
+  set (names)
+
+  if (_PGN_WIN32)
+    string (REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${_PGN_VERSION})
+
+    set (name python${_PYTHON_VERSION_NO_DOTS})
+    if (_PGN_DEBUG)
+      string (APPEND name "_d")
+    endif()
+
+    list (APPEND names "${name}")
+  endif()
+
+  if (_PGN_POSIX)
+    if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
+      set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
+    else()
+      if (_PGN_EXECUTABLE OR _PGN_CONFIG)
+        set (abi "")
+      else()
+        set (abi "mu" "m" "u" "")
+      endif()
+    endif()
+
+    if (abi)
+      if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
+        set (abinames "${abi}")
+        list (TRANSFORM abinames PREPEND "${CMAKE_LIBRARY_ARCHITECTURE}-python${_PGN_VERSION}")
+        list (TRANSFORM abinames APPEND "-config")
+        list (APPEND names ${abinames})
+      endif()
+      set (abinames "${abi}")
+      list (TRANSFORM abinames PREPEND "python${_PGN_VERSION}")
+      if (_PGN_CONFIG)
+        list (TRANSFORM abinames APPEND "-config")
+      endif()
+      list (APPEND names ${abinames})
+    else()
+      if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
+        set (abinames "${CMAKE_LIBRARY_ARCHITECTURE}-python${_PGN_VERSION}")
+      endif()
+      list (APPEND abinames "python${_PGN_VERSION}")
+      if (_PGN_CONFIG)
+        list (TRANSFORM abinames APPEND "-config")
+      endif()
+      list (APPEND names ${abinames})
+    endif()
+  endif()
+
+  set (${_PYTHON_PGN_NAMES} ${names} PARENT_SCOPE)
+endfunction()
+
+function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
+  unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE)
+
+  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS)$")
+    return()
+  endif()
+
+  if (_${_PYTHON_PREFIX}_CONFIG)
+    set (config_flag "--${NAME}")
+    string (TOLOWER "${config_flag}" config_flag)
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" ${config_flag}
+                     RESULT_VARIABLE _result
+                     OUTPUT_VARIABLE _values
+                     ERROR_QUIET
+                     OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if (_result)
+      unset (_values)
+    else()
+      if (NAME STREQUAL "INCLUDES")
+        # do some clean-up
+        string (REGEX MATCHALL "(-I|-iwithsysroot)[ ]*[^ ]+" _values "${_values}")
+        string (REGEX REPLACE "(-I|-iwithsysroot)[ ]*" "" _values "${_values}")
+        list (REMOVE_DUPLICATES _values)
+      endif()
+    endif()
+  endif()
+
+  if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING)
+    if (NAME STREQUAL "PREFIX")
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))"
+                       RESULT_VARIABLE _result
+                       OUTPUT_VARIABLE _values
+                       ERROR_QUIET
+                       OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (_result)
+        unset (_values)
+      else()
+        list (REMOVE_DUPLICATES _values)
+      endif()
+    elseif (NAME STREQUAL "INCLUDES")
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))"
+                       RESULT_VARIABLE _result
+                       OUTPUT_VARIABLE _values
+                       ERROR_QUIET
+                       OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (_result)
+        unset (_values)
+      endif()
+    else()
+      set (config_flag "${NAME}")
+      if (NAME STREQUAL "CONFIGDIR")
+        set (config_flag "LIBPL")
+      endif()
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_config_var('${config_flag}'))"
+                       RESULT_VARIABLE _result
+                       OUTPUT_VARIABLE _values
+                       ERROR_QUIET
+                       OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (_result)
+        unset (_values)
+      endif()
+    endif()
+  endif()
+
+  if (config_flag STREQUAL "ABIFLAGS")
+    set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE)
+    return()
+  endif()
+
+  if (NOT _values OR _values STREQUAL "None")
+    return()
+  endif()
+
+  if (NAME STREQUAL "LIBS")
+    # do some clean-up
+    string (REGEX MATCHALL "-(l|framework)[ ]*[^ ]+" _values "${_values}")
+    # remove elements relative to python library itself
+    list (FILTER _values EXCLUDE REGEX "-lpython")
+    list (REMOVE_DUPLICATES _values)
+  endif()
+
+  set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE)
+endfunction()
+
+function (_PYTHON_GET_VERSION)
+  cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;INCLUDE" "PREFIX" "")
+
+  unset (${_PGV_PREFIX}VERSION PARENT_SCOPE)
+  unset (${_PGV_PREFIX}VERSION_MAJOR PARENT_SCOPE)
+  unset (${_PGV_PREFIX}VERSION_MINOR PARENT_SCOPE)
+  unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE)
+  unset (${_PGV_PREFIX}ABI PARENT_SCOPE)
+
+  if (_PGV_LIBRARY)
+    # retrieve version and abi from library name
+    if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+      # extract version from library name
+      if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])([0-9]+)")
+        set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
+      elseif (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])\\.([0-9]+)([dmu]*)")
+        set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE)
+        set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE)
+      endif()
+    endif()
   else()
-    set (${_PYTHON_PGLN_NAMES} python${_PYTHON_VERSION_NO_DOTS}
-                               python${_PYTHON_VERSION}mu
-                               python${_PYTHON_VERSION}m
-                               python${_PYTHON_VERSION}u
-                               python${_PYTHON_VERSION}
-         PARENT_SCOPE)
+    if (_${_PYTHON_PREFIX}_INCLUDE_DIR)
+      # retrieve version from header file
+      file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" version
+            REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"")
+      string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1"
+                            version "${version}")
+      string (REGEX MATCHALL "[0-9]+" versions "${version}")
+      list (GET versions 0 version_major)
+      list (GET versions 1 version_minor)
+      list (GET versions 2 version_patch)
+
+      set (${_PGV_PREFIX}VERSION "${version_major}.${version_minor}" PARENT_SCOPE)
+      set (${_PGV_PREFIX}VERSION_MAJOR ${version_major} PARENT_SCOPE)
+      set (${_PGV_PREFIX}VERSION_MINOR ${version_minor} PARENT_SCOPE)
+      set (${_PGV_PREFIX}VERSION_PATCH ${version_patch} PARENT_SCOPE)
+
+      # compute ABI flags
+      if (version_major VERSION_GREATER 2)
+        file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(Py_DEBUG|WITH_PYMALLOC|Py_UNICODE_SIZE|MS_WIN32)")
+        set (abi)
+        if (config MATCHES "#[ ]*define[ ]+MS_WIN32")
+          # ABI not used on Windows
+          set (abi "")
+        else()
+          if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1")
+            string (APPEND abi "d")
+          endif()
+          if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1")
+            string (APPEND abi "m")
+          endif()
+          if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4")
+            string (APPEND abi "u")
+          endif()
+          set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE)
+        endif()
+      else()
+        # ABI not supported
+        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
+      endif()
+    endif()
   endif()
 endfunction()
 
 
 function (_PYTHON_VALIDATE_INTERPRETER)
-  if (NOT ${_PYTHON_PREFIX}_EXECUTABLE)
+  if (NOT _${_PYTHON_PREFIX}_EXECUTABLE)
     return()
   endif()
 
-  cmake_parse_arguments (_PVI "EXACT" "" "" ${ARGN})
+  cmake_parse_arguments (PARSE_ARGV 0 _PVI "EXACT;CHECK_EXISTS" "" "")
   if (_PVI_UNPARSED_ARGUMENTS)
     set (expected_version ${_PVI_UNPARSED_ARGUMENTS})
   else()
     unset (expected_version)
   endif()
 
-  get_filename_component (python_name "${${_PYTHON_PREFIX}_EXECUTABLE}" NAME)
+  if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}")
+    # interpreter does not exist anymore
+    set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "_${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
+    return()
+  endif()
 
-  if (expected_version AND NOT python_name STREQUAL "python${expected_version}${CMAKE_EXECUTABLE_SUFFIX}")
+  # validate ABI compatibility
+  if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI)
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+                             "import sys; sys.stdout.write(sys.abiflags)"
+                     RESULT_VARIABLE result
+                     OUTPUT_VARIABLE abi
+                     ERROR_QUIET
+                     OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if (result)
+      # assume ABI is not supported
+      set (abi "")
+    endif()
+    if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+      # incompatible ABI
+      set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "_${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
+      return()
+    endif()
+  endif()
+
+  get_filename_component (python_name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME)
+
+  if (expected_version AND NOT python_name STREQUAL "python${expected_version}${abi}${CMAKE_EXECUTABLE_SUFFIX}")
     # executable found must have a specific version
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                              "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))"
                      RESULT_VARIABLE result
                      OUTPUT_VARIABLE version
@@ -168,14 +444,14 @@
                      OUTPUT_STRIP_TRAILING_WHITESPACE)
     if (result OR (_PVI_EXACT AND NOT version VERSION_EQUAL expected_version) OR (version VERSION_LESS expected_version))
       # interpreter not usable or has wrong major version
-      set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE)
+      set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "_${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
       return()
     endif()
   else()
     if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}")
       # executable found do not have version in name
       # ensure major version is OK
-      execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                                "import sys; sys.stdout.write(str(sys.version_info[0]))"
                        RESULT_VARIABLE result
                        OUTPUT_VARIABLE version
@@ -183,7 +459,7 @@
                        OUTPUT_STRIP_TRAILING_WHITESPACE)
       if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # interpreter not usable or has wrong major version
-        set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE)
+        set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "_${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
         return()
       endif()
     endif()
@@ -192,7 +468,7 @@
   if (CMAKE_SIZEOF_VOID_P AND "Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
       AND NOT CMAKE_CROSSCOMPILING)
     # In this case, interpreter must have same architecture as environment
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                              "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))"
                      RESULT_VARIABLE result
                      OUTPUT_VARIABLE size
@@ -200,7 +476,7 @@
                      OUTPUT_STRIP_TRAILING_WHITESPACE)
     if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P)
       # interpreter not usable or has wrong architecture
-      set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE)
+      set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "_${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
       return()
     endif()
   endif()
@@ -208,11 +484,11 @@
 
 
 function (_PYTHON_VALIDATE_COMPILER expected_version)
-  if (NOT ${_PYTHON_PREFIX}_COMPILER)
+  if (NOT _${_PYTHON_PREFIX}_COMPILER)
     return()
   endif()
 
-  cmake_parse_arguments (_PVC "EXACT" "" "" ${ARGN})
+  cmake_parse_arguments (_PVC "EXACT;CHECK_EXISTS" "" "" ${ARGN})
   if (_PVC_UNPARSED_ARGUMENTS)
     set (major_version FALSE)
     set (expected_version ${_PVC_UNPARSED_ARGUMENTS})
@@ -222,6 +498,12 @@
     set (_PVC_EXACT TRUE)
   endif()
 
+  if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}")
+    # Compiler does not exist anymore
+    set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "_${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
+    return()
+  endif()
+
   # retrieve python environment version from compiler
   set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir")
   if (major_version)
@@ -230,7 +512,7 @@
   else()
     file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))\n")
   endif()
-  execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${working_dir}/version.py"
+  execute_process (COMMAND "${_${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${working_dir}/version.py"
                    WORKING_DIRECTORY "${working_dir}"
                    OUTPUT_QUIET
                    ERROR_QUIET
@@ -244,7 +526,98 @@
 
   if (result OR (_PVC_EXACT AND NOT version VERSION_EQUAL expected_version) OR (version VERSION_LESS expected_version))
     # Compiler not usable or has wrong version
-    set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE)
+    set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "_${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
+  endif()
+endfunction()
+
+
+function (_PYTHON_VALIDATE_LIBRARY)
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+    return()
+  endif()
+
+  cmake_parse_arguments (PARSE_ARGV 0 _PVL "EXACT;CHECK_EXISTS" "" "")
+  if (_PVL_UNPARSED_ARGUMENTS)
+    set (expected_version ${_PVL_UNPARSED_ARGUMENTS})
+  else()
+    unset (expected_version)
+  endif()
+
+  if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
+    # library does not exist anymore
+    set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+    if (WIN32)
+      set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND")
+    endif()
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+    return()
+  endif()
+
+  # retrieve version and abi from library name
+  _python_get_version (LIBRARY PREFIX lib_)
+
+  if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+    # incompatible ABI
+    set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+  else()
+    if (expected_version)
+      if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL expected_version) OR (lib_VERSION VERSION_LESS expected_version))
+        # library has wrong version
+        set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+      endif()
+    else()
+      if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
+        # library has wrong major version
+        set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+      endif()
+    endif()
+  endif()
+
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+    if (WIN32)
+      set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND")
+    endif()
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+  endif()
+endfunction()
+
+
+function (_PYTHON_VALIDATE_INCLUDE_DIR)
+  if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+    return()
+  endif()
+
+  cmake_parse_arguments (PARSE_ARGV 0 _PVID "EXACT;CHECK_EXISTS" "" "")
+  if (_PVID_UNPARSED_ARGUMENTS)
+    set (expected_version ${_PVID_UNPARSED_ARGUMENTS})
+  else()
+    unset (expected_version)
+  endif()
+
+  if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
+    # include file does not exist anymore
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+    return()
+  endif()
+
+  # retrieve version from header file
+  _python_get_version (INCLUDE PREFIX inc_)
+
+  if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+    # incompatible ABI
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+  else()
+    if (expected_version)
+      if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version))
+        # include dir has wrong version
+        set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+      endif()
+    else()
+      if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
+        # include dir has wrong major version
+        set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+      endif()
+    endif()
   endif()
 endfunction()
 
@@ -267,8 +640,7 @@
 
 function (_PYTHON_SET_LIBRARY_DIRS _PYTHON_SLD_RESULT)
   unset (_PYTHON_DIRS)
-  set (_PYTHON_LIBS ${ARGV})
-  list (REMOVE_AT _PYTHON_LIBS 0)
+  set (_PYTHON_LIBS ${ARGN})
   foreach (_PYTHON_LIB IN LISTS _PYTHON_LIBS)
     if (${_PYTHON_LIB})
       get_filename_component (_PYTHON_DIR "${${_PYTHON_LIB}}" DIRECTORY)
@@ -298,7 +670,7 @@
   list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development")
   list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS)
 endif()
-foreach (_${_PYTHON_PREFIX}_COMPONENT IN LISTS ${_PYTHON_PREFIX}_FIND_COMPONENTS)
+foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy)
   set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE)
 endforeach()
 unset (_${_PYTHON_PREFIX}_FIND_VERSIONS)
@@ -321,6 +693,28 @@
   endif()
 endif()
 
+# Set ABIs to search
+## default: search any ABI
+if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS 3)
+  # ABI not supported
+  unset (_${_PYTHON_PREFIX}_FIND_ABI)
+  set (_${_PYTHON_PREFIX}_ABIFLAGS "")
+else()
+  unset (_${_PYTHON_PREFIX}_FIND_ABI)
+  unset (_${_PYTHON_PREFIX}_ABIFLAGS)
+  if (DEFINED ${_PYTHON_PREFIX}_FIND_ABI)
+    # normalization
+    string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI)
+    list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(TRUE|Y(ES)?|1)$" "ON")
+    list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF")
+    if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)$")
+      message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it")
+      unset (_${_PYTHON_PREFIX}_FIND_ABI)
+    endif()
+    _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS)
+  endif()
+endif()
+
 # Define lookup strategy
 if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW")
   set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION")
@@ -426,311 +820,374 @@
 endif()
 
 
+# Compute search signature
+# This signature will be used to check validity of cached variables on new search
+set (_${_PYTHON_PREFIX}_SIGNATURE "${${_PYTHON_PREFIX}_ROOT_DIR}:${${_PYTHON_PREFIX}_FIND_STRATEGY}:${${_PYTHON_PREFIX}_FIND_VIRTUALENV}")
+if (NOT WIN32)
+  string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_USE_STATIC_LIBS}:")
+endif()
+if (CMAKE_HOST_APPLE)
+  string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_FIND_FRAMEWORK}")
+endif()
+if (CMAKE_HOST_WIN32)
+  string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_FIND_REGISTRY}")
+endif()
+
+
 unset (_${_PYTHON_PREFIX}_REQUIRED_VARS)
 unset (_${_PYTHON_PREFIX}_CACHED_VARS)
 
 
 # first step, search for the interpreter
 if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
-  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_EXECUTABLE)
+  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE)
   if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter)
     list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE)
   endif()
 
-  set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
-
-  if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
-    unset (_${_PYTHON_PREFIX}_NAMES)
-    unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
-    unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
-
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      # build all executable names
-      list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_VERSION})
-
-      # Framework Paths
-      _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION})
-      list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
-
-      # Registry Paths
-      _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION})
-      list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS}
-                   [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath])
-    endforeach()
-    list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
-
-    while (TRUE)
-      # Virtual environments handling
-      if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ENV VIRTUAL_ENV
-                      PATH_SUFFIXES bin Scripts
-                      NO_CMAKE_PATH
-                      NO_CMAKE_ENVIRONMENT_PATH
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-        if (NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
-          break()
-        endif()
-      endif()
-
-      # Apple frameworks handling
-      if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                      PATH_SUFFIXES bin
-                      NO_CMAKE_PATH
-                      NO_CMAKE_ENVIRONMENT_PATH
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-      endif()
-      # Windows registry
-      if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-      endif()
-
-      # try using HINTS and standard paths
-      find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                    NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                          ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                    NAMES_PER_DIR
-                    HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES})
-      _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        break()
-      endif()
-
-      # Apple frameworks handling
-      if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                      PATH_SUFFIXES bin
-                      NO_DEFAULT_PATH)
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-      endif()
-      # Windows registry
-      if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                      NAMES_PER_DIR
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_DEFAULT_PATH)
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-      endif()
-
-      break()
-    endwhile()
-  else()
-    # look-up for various versions and locations
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      set (_${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_VERSION}
-                                    python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}
-                                    python)
-
-      _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION})
-      _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION})
-
-      # Virtual environments handling
-      if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ENV VIRTUAL_ENV
-                      PATH_SUFFIXES bin Scripts
-                      NO_CMAKE_PATH
-                      NO_CMAKE_ENVIRONMENT_PATH
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-
-        _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-        if (${_PYTHON_PREFIX}_EXECUTABLE)
-          break()
-        endif()
-        if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
-          continue()
-        endif()
-      endif()
-
-      # Apple frameworks handling
-      if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                      PATH_SUFFIXES bin
-                      NO_CMAKE_PATH
-                      NO_CMAKE_ENVIRONMENT_PATH
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-      endif()
-
-      # Windows registry
-      if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                            [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
-                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-      endif()
-      _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        break()
-      endif()
-
-      # try using HINTS
-      find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                    NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                          ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                    NAMES_PER_DIR
-                    HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                    NO_SYSTEM_ENVIRONMENT_PATH
-                    NO_CMAKE_SYSTEM_PATH)
-      _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        break()
-      endif()
-      # try using standard paths.
-      # NAMES_PER_DIR is not defined on purpose to have a chance to find
-      # expected version.
-      # For example, typical systems have 'python' for version 2.* and 'python3'
-      # for version 3.*. So looking for names per dir will find, potentially,
-      # systematically 'python' (i.e. version 2) even if version 3 is searched.
-      if (CMAKE_HOST_WIN32)
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES python${_${_PYTHON_PREFIX}_VERSION}
-                            python
-                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES})
+  if (DEFINED ${_PYTHON_PREFIX}_EXECUTABLE
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_EXECUTABLE}")
+    set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "")
+  elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE)
+    # compute interpreter signature and check validity of definition
+    string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}")
+    if (__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE)
+      # check version validity
+      if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT)
+        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS)
       else()
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES python${_${_PYTHON_PREFIX}_VERSION})
+        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS)
       endif()
-      _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        break()
-      endif()
-
-      # Apple frameworks handling
-      if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      NAMES_PER_DIR
-                      PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                      PATH_SUFFIXES bin
-                      NO_DEFAULT_PATH)
-      endif()
-
-      # Windows registry
-      if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
-                      NAMES_PER_DIR
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                            [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
-                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_DEFAULT_PATH)
-      endif()
-
-      _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        break()
-      endif()
-    endforeach()
-
-    if (NOT ${_PYTHON_PREFIX}_EXECUTABLE AND
-        NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
-      # No specific version found. Retry with generic names and standard paths.
-      # NAMES_PER_DIR is not defined on purpose to have a chance to find
-      # expected version.
-      # For example, typical systems have 'python' for version 2.* and 'python3'
-      # for version 3.*. So looking for names per dir will find, potentially,
-      # systematically 'python' (i.e. version 2) even if version 3 is searched.
-      find_program (${_PYTHON_PREFIX}_EXECUTABLE
-                    NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}
-                          python
-                          ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES})
-
-      _python_validate_interpreter ()
+    else()
+      unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE)
+      unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE)
     endif()
   endif()
 
+  if (NOT _${_PYTHON_PREFIX}_EXECUTABLE)
+    set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+    if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
+      unset (_${_PYTHON_PREFIX}_NAMES)
+      unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+      unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
+
+      foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+        # build all executable names
+        _python_get_names (_${_PYTHON_PREFIX}_VERSION_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX EXECUTABLE)
+        list (APPEND _${_PYTHON_PREFIX}_NAMES ${_${_PYTHON_PREFIX}_VERSION_NAMES})
+
+        # Framework Paths
+        _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+        list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
+
+        # Registry Paths
+        _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+        list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS}
+                     [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath])
+      endforeach()
+      list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
+
+      while (TRUE)
+        # Virtual environments handling
+        if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ENV VIRTUAL_ENV
+                        PATH_SUFFIXES bin Scripts
+                        NO_CMAKE_PATH
+                        NO_CMAKE_ENVIRONMENT_PATH
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+
+          _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+          if (NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
+            break()
+          endif()
+        endif()
+
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_CMAKE_PATH
+                        NO_CMAKE_ENVIRONMENT_PATH
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+          _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+        endif()
+        # Windows registry
+        if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+          _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+        endif()
+
+        # try using HINTS
+        find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                      NAMES_PER_DIR
+                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                      NO_SYSTEM_ENVIRONMENT_PATH
+                      NO_CMAKE_SYSTEM_PATH)
+        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+        # try using standard paths
+        find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                      NAMES_PER_DIR
+                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES})
+        _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_DEFAULT_PATH)
+          _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+        endif()
+        # Windows registry
+        if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_DEFAULT_PATH)
+          _python_validate_interpreter (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+        endif()
+
+        break()
+      endwhile()
+    else()
+      # look-up for various versions and locations
+      foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+        _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX EXECUTABLE)
+        list (APPEND _${_PYTHON_PREFIX}_NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}
+                                              python)
+
+        _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+        _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+
+        # Virtual environments handling
+        if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ENV VIRTUAL_ENV
+                        PATH_SUFFIXES bin Scripts
+                        NO_CMAKE_PATH
+                        NO_CMAKE_ENVIRONMENT_PATH
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+          _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+          if (_${_PYTHON_PREFIX}_EXECUTABLE)
+            break()
+          endif()
+          if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
+            continue()
+          endif()
+        endif()
+
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_CMAKE_PATH
+                        NO_CMAKE_ENVIRONMENT_PATH
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+        endif()
+
+        # Windows registry
+        if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                              [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
+                        PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+        endif()
+
+        _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+
+        # try using HINTS
+        find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                      NAMES_PER_DIR
+                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                      PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                      NO_SYSTEM_ENVIRONMENT_PATH
+                      NO_CMAKE_SYSTEM_PATH)
+        _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+        # try using standard paths.
+        # NAMES_PER_DIR is not defined on purpose to have a chance to find
+        # expected version.
+        # For example, typical systems have 'python' for version 2.* and 'python3'
+        # for version 3.*. So looking for names per dir will find, potentially,
+        # systematically 'python' (i.e. version 2) even if version 3 is searched.
+        if (CMAKE_HOST_WIN32)
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                              python
+                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES})
+        else()
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES})
+        endif()
+        _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_DEFAULT_PATH)
+        endif()
+
+        # Windows registry
+        if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                        NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                              [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
+                        PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_DEFAULT_PATH)
+        endif()
+
+        _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+        if (_${_PYTHON_PREFIX}_EXECUTABLE)
+          break()
+        endif()
+      endforeach()
+
+      if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND
+          NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
+        # No specific version found. Retry with generic names and standard paths.
+        # NAMES_PER_DIR is not defined on purpose to have a chance to find
+        # expected version.
+        # For example, typical systems have 'python' for version 2.* and 'python3'
+        # for version 3.*. So looking for names per dir will find, potentially,
+        # systematically 'python' (i.e. version 2) even if version 3 is searched.
+        find_program (_${_PYTHON_PREFIX}_EXECUTABLE
+                      NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}
+                            python
+                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES})
+        _python_validate_interpreter ()
+      endif()
+    endif()
+  endif()
+
+  set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}")
+
   # retrieve exact version of executable found
-  if (${_PYTHON_PREFIX}_EXECUTABLE)
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
+  if (_${_PYTHON_PREFIX}_EXECUTABLE)
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                              "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))"
                      RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                      OUTPUT_VARIABLE ${_PYTHON_PREFIX}_VERSION
                      ERROR_QUIET
                      OUTPUT_STRIP_TRAILING_WHITESPACE)
     if (NOT _${_PYTHON_PREFIX}_RESULT)
+      set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE TRUE)
       string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}")
       list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR)
       list (GET _${_PYTHON_PREFIX}_VERSIONS 1 ${_PYTHON_PREFIX}_VERSION_MINOR)
       list (GET _${_PYTHON_PREFIX}_VERSIONS 2 ${_PYTHON_PREFIX}_VERSION_PATCH)
     else()
       # Interpreter is not usable
-      set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE)
+      set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE)
       unset (${_PYTHON_PREFIX}_VERSION)
     endif()
   endif()
 
-  if (${_PYTHON_PREFIX}_EXECUTABLE
+  if (_${_PYTHON_PREFIX}_EXECUTABLE AND _${_PYTHON_PREFIX}_EXECUTABLE_USABLE
       AND ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
     set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE)
-    # Use interpreter version for future searches to ensure consistency
+    # Use interpreter version and ABI for future searches to ensure consistency
     set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR})
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.abiflags)"
+                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_ABIFLAGS
+                     ERROR_QUIET
+                     OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if (_${_PYTHON_PREFIX}_RESULT)
+      # assunme ABI is not supported
+      set (_${_PYTHON_PREFIX}_ABIFLAGS "")
+    endif()
   endif()
 
   if (${_PYTHON_PREFIX}_Interpreter_FOUND)
+    # compute and save interpreter signature
+    string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}")
+    set (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}" CACHE INTERNAL "")
+
     if (NOT CMAKE_SIZEOF_VOID_P)
       # determine interpreter architecture
-      execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.maxsize > 2**32)"
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.maxsize > 2**32)"
                        RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                        OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT
                        ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT)
@@ -746,7 +1203,7 @@
     endif()
 
     # retrieve interpreter identity
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -V
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -V
                      RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                      OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID
                      ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID)
@@ -759,7 +1216,7 @@
         string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}")
         if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python")
           # try to get a more precise ID
-          execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.copyright)"
+          execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.copyright)"
                            RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                            OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT
                            ERROR_QUIET)
@@ -771,144 +1228,169 @@
     else()
       set (${_PYTHON_PREFIX}_INTERPRETER_ID Python)
     endif()
+
+    # retrieve various package installation directories
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))"
+
+                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS
+                     ERROR_QUIET)
+    if (NOT _${_PYTHON_PREFIX}_RESULT)
+      list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB)
+      list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH)
+      list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB)
+      list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH)
+    else()
+      unset (${_PYTHON_PREFIX}_STDLIB)
+      unset (${_PYTHON_PREFIX}_STDARCH)
+      unset (${_PYTHON_PREFIX}_SITELIB)
+      unset (${_PYTHON_PREFIX}_SITEARCH)
+    endif()
   else()
+    unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE)
     unset (${_PYTHON_PREFIX}_INTERPRETER_ID)
   endif()
 
-  # retrieve various package installation directories
-  execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))"
-
-                   RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                   OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS
-                   ERROR_QUIET)
-  if (NOT _${_PYTHON_PREFIX}_RESULT)
-    list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB)
-    list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH)
-    list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB)
-    list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH)
-  else()
-    unset (${_PYTHON_PREFIX}_STDLIB)
-    unset (${_PYTHON_PREFIX}_STDARCH)
-    unset (${_PYTHON_PREFIX}_SITELIB)
-    unset (${_PYTHON_PREFIX}_SITEARCH)
-  endif()
-
-  mark_as_advanced (${_PYTHON_PREFIX}_EXECUTABLE)
+  mark_as_advanced (_${_PYTHON_PREFIX}_EXECUTABLE
+                    _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE)
 endif()
 
 
 # second step, search for compiler (IronPython)
 if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
-  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_COMPILER)
+  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_COMPILER)
   if (${_PYTHON_PREFIX}_FIND_REQUIRED_Compiler)
     list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_COMPILER)
   endif()
 
-  # IronPython specific artifacts
-  # If IronPython interpreter is found, use its path
-  unset (_${_PYTHON_PREFIX}_IRON_ROOT)
-  if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython")
-    get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY)
+  if (DEFINED ${_PYTHON_PREFIX}_COMPILER
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_COMPILER}")
+    set (_${_PYTHON_PREFIX}_COMPILER "${${_PYTHON_PREFIX}_COMPILER}" CACHE INTERNAL "")
+  elseif (DEFINED _${_PYTHON_PREFIX}_COMPILER)
+    # compute compiler signature and check validity of definition
+    string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}")
+    if (__${_PYTHON_PREFIX}_COMPILER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_COMPILER_SIGNATURE)
+      # check version validity
+      if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT)
+        _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS)
+      else()
+        _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS)
+      endif()
+    else()
+      unset (_${_PYTHON_PREFIX}_COMPILER CACHE)
+      unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE)
+    endif()
   endif()
 
-  if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
-    set (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
+  if (NOT _${_PYTHON_PREFIX}_COMPILER)
+    # IronPython specific artifacts
+    # If IronPython interpreter is found, use its path
+    unset (_${_PYTHON_PREFIX}_IRON_ROOT)
+    if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython")
+      get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY)
+    endif()
 
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      # Registry Paths
-      list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS
-                   [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath])
-    endforeach()
+    if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
+      set (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
 
-    while (TRUE)
-      if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_COMPILER
+      foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+        # Registry Paths
+        list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS
+                     [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath])
+      endforeach()
+
+      while (TRUE)
+        if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_COMPILER
+                        NAMES ipyc
+                        HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+          _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION})
+          if (_${_PYTHON_PREFIX}_COMPILER)
+            break()
+          endif()
+        endif()
+
+        find_program (_${_PYTHON_PREFIX}_COMPILER
                       NAMES ipyc
                       HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
                       NO_CMAKE_SYSTEM_PATH)
         _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION})
-        if (${_PYTHON_PREFIX}_COMPILER)
+        if (_${_PYTHON_PREFIX}_COMPILER)
           break()
         endif()
-      endif()
 
-      find_program (${_PYTHON_PREFIX}_COMPILER
-                    NAMES ipyc
-                    HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                    NO_SYSTEM_ENVIRONMENT_PATH
-                    NO_CMAKE_SYSTEM_PATH)
-      _python_validate_compiler (${${_PYTHON_PREFIX}_FIND_VERSION})
-      if (${_PYTHON_PREFIX}_COMPILER)
+        if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_COMPILER
+                        NAMES ipyc
+                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_DEFAULT_PATH)
+        endif()
+
         break()
-      endif()
+      endwhile()
+    else()
+      # try using root dir and registry
+      foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+        if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_COMPILER
+                        NAMES ipyc
+                        HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+          _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+          if (_${_PYTHON_PREFIX}_COMPILER)
+            break()
+          endif()
+        endif()
 
-      if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_COMPILER
-                      NAMES ipyc
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_DEFAULT_PATH)
-      endif()
-
-      break()
-    endwhile()
-  else()
-    # try using root dir and registry
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_program (${_PYTHON_PREFIX}_COMPILER
+        find_program (_${_PYTHON_PREFIX}_COMPILER
                       NAMES ipyc
                       HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
                       NO_CMAKE_SYSTEM_PATH)
         _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-        if (${_PYTHON_PREFIX}_COMPILER)
+        if (_${_PYTHON_PREFIX}_COMPILER)
           break()
         endif()
-      endif()
 
-      find_program (${_PYTHON_PREFIX}_COMPILER
+        if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_COMPILER
+                        NAMES ipyc
+                        PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
+                        NO_DEFAULT_PATH)
+          _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT)
+          if (_${_PYTHON_PREFIX}_COMPILER)
+            break()
+          endif()
+        endif()
+      endforeach()
+
+      # no specific version found, re-try in standard paths
+      find_program (_${_PYTHON_PREFIX}_COMPILER
                     NAMES ipyc
                     HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                    NO_SYSTEM_ENVIRONMENT_PATH
-                    NO_CMAKE_SYSTEM_PATH)
-      _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-      if (${_PYTHON_PREFIX}_COMPILER)
-        break()
-      endif()
-
-      if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-        find_program (${_PYTHON_PREFIX}_COMPILER
-                      NAMES ipyc
-                      PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath]
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}
-                      NO_DEFAULT_PATH)
-        _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION} EXACT)
-        if (${_PYTHON_PREFIX}_COMPILER)
-          break()
-        endif()
-      endif()
-    endforeach()
-
-    # no specific version found, re-try in standard paths
-    find_program (${_PYTHON_PREFIX}_COMPILER
-                  NAMES ipyc
-                  HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                  PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES})
+                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES})
+    endif()
   endif()
 
-  if (${_PYTHON_PREFIX}_COMPILER)
+  set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}")
+
+  if (_${_PYTHON_PREFIX}_COMPILER)
     # retrieve python environment version from compiler
     set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir")
     file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n")
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py"
+    execute_process (COMMAND "${_${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py"
                      WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}"
                      OUTPUT_QUIET
                      ERROR_QUIET)
@@ -918,6 +1400,7 @@
                      OUTPUT_VARIABLE _${_PYTHON_PREFIX}_VERSION
                      ERROR_QUIET)
     if (NOT _${_PYTHON_PREFIX}_RESULT)
+      set (_${_PYTHON_PREFIX}_COMPILER_USABLE TRUE)
       string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}")
       list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR)
       list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR)
@@ -932,12 +1415,12 @@
       endif()
     else()
       # compiler not usable
-      set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE)
+      set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE)
     endif()
     file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}")
   endif()
 
-  if (${_PYTHON_PREFIX}_COMPILER)
+  if (_${_PYTHON_PREFIX}_COMPILER AND _${_PYTHON_PREFIX}_COMPILER_USABLE)
     if (${_PYTHON_PREFIX}_Interpreter_FOUND)
       # Compiler must be compatible with interpreter
       if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR})
@@ -951,12 +1434,18 @@
   endif()
 
   if (${_PYTHON_PREFIX}_Compiler_FOUND)
+    # compute and save compiler signature
+    string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}")
+    set (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${__${_PYTHON_PREFIX}_COMPILER_SIGNATURE}" CACHE INTERNAL "")
+
     set (${_PYTHON_PREFIX}_COMPILER_ID IronPython)
   else()
+    unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE)
     unset (${_PYTHON_PREFIX}_COMPILER_ID)
   endif()
 
-  mark_as_advanced (${_PYTHON_PREFIX}_COMPILER)
+  mark_as_advanced (_${_PYTHON_PREFIX}_COMPILER
+                    _${_PYTHON_PREFIX}_COMPILER_SIGNATURE)
 endif()
 
 
@@ -964,15 +1453,50 @@
 ## Development environment is not compatible with IronPython interpreter
 if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
     AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython")
-  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_LIBRARY
-                                              ${_PYTHON_PREFIX}_LIBRARY_RELEASE
+  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_LIBRARY_RELEASE
                                               ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
-                                              ${_PYTHON_PREFIX}_LIBRARY_DEBUG
+                                              _${_PYTHON_PREFIX}_LIBRARY_DEBUG
                                               ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
-                                              ${_PYTHON_PREFIX}_INCLUDE_DIR)
+                                              _${_PYTHON_PREFIX}_INCLUDE_DIR)
   if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development)
-    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARY
-                                                  ${_PYTHON_PREFIX}_INCLUDE_DIR)
+    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES
+                                                  ${_PYTHON_PREFIX}_INCLUDE_DIRS)
+  endif()
+
+  if (DEFINED _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR DEFINED _${_PYTHON_PREFIX}_INCLUDE_DIR)
+    # compute development signature and check validity of definition
+    string (MD5 __${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
+    if (WIN32 AND NOT DEFINED _${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+      set (_${_PYTHON_PREFIX}_LIBRARY_DEBUG "_${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND" CACHE INTERNAL "")
+    endif()
+    if (NOT DEFINED _${_PYTHON_PREFIX}_INCLUDE_DIR)
+      set (_${_PYTHON_PREFIX}_INCLUDE_DIR "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND" CACHE INTERNAL "")
+    endif()
+    if (__${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE)
+      # check version validity
+      if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT)
+        _python_validate_library (${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS)
+        _python_validate_include_dir (${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS)
+      else()
+        _python_validate_library (${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS)
+        _python_validate_include_dir (${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS)
+      endif()
+    else()
+      unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE)
+      unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
+      unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
+      unset (_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE CACHE)
+    endif()
+  endif()
+  if (DEFINED ${_PYTHON_PREFIX}_LIBRARY
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_LIBRARY}")
+    set (_${_PYTHON_PREFIX}_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_LIBRARY}" CACHE INTERNAL "")
+    unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
+    unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
+  endif()
+  if (DEFINED ${_PYTHON_PREFIX}_INCLUDE_DIR
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_INCLUDE_DIR}")
+    set (_${_PYTHON_PREFIX}_INCLUDE_DIR "${${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE INTERNAL "")
   endif()
 
   # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
@@ -986,360 +1510,282 @@
     endif()
   endif()
 
-  # if python interpreter is found, use its location and version to ensure consistency
-  # between interpreter and development environment
-  unset (_${_PYTHON_PREFIX}_PREFIX)
-  unset (_${_PYTHON_PREFIX}_EXEC_PREFIX)
-  unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX)
-  if (${_PYTHON_PREFIX}_Interpreter_FOUND)
-    execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
-                             "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.EXEC_PREFIX)"
-                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_EXEC_PREFIX
-                     ERROR_QUIET
-                     OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (_${_PYTHON_PREFIX}_RESULT)
-      unset (_${_PYTHON_PREFIX}_EXEC_PREFIX)
-    endif()
-
-    if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "STANDARD")
-      execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
-                               "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.BASE_EXEC_PREFIX)"
-                       RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                       OUTPUT_VARIABLE _${_PYTHON_PREFIX}_BASE_EXEC_PREFIX
-                       ERROR_QUIET
-                       OUTPUT_STRIP_TRAILING_WHITESPACE)
-      if (_${_PYTHON_PREFIX}_RESULT)
-        unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX)
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+    # if python interpreter is found, use it to look-up for artifacts
+    # to ensure consistency between interpreter and development environments.
+    # If not, try to locate a compatible config tool
+    if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND OR CMAKE_CROSSCOMPILING)
+      set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+      unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
+      if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+        set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV)
       endif()
-    endif()
-  endif()
-  set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_EXEC_PREFIX}" "${_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
-
-  if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
-    set (_${_PYTHON_PREFIX}_CONFIG_NAMES)
-
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      if (DEFINED CMAKE_LIBRARY_ARCHITECTURE)
-        list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "${CMAKE_LIBRARY_ARCHITECTURE}-python${_${_PYTHON_PREFIX}_VERSION}-config")
-      endif()
-      list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "python${_${_PYTHON_PREFIX}_VERSION}-config")
-    endforeach()
-
-    find_program (_${_PYTHON_PREFIX}_CONFIG
-                  NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
-                  NAMES_PER_DIR
-                  HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                  PATH_SUFFIXES bin)
-
-    if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
-      # check that config tool match library architecture
-      execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
-                       RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                       OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
-                       ERROR_QUIET
-                       OUTPUT_STRIP_TRAILING_WHITESPACE)
-      if (_${_PYTHON_PREFIX}_RESULT)
-        unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
-      else()
-        string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
-        if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
-          unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
-        endif()
-      endif()
-    endif()
-  else()
-    foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-      # try to use pythonX.Y-config tool
-      set (_${_PYTHON_PREFIX}_CONFIG_NAMES)
-      if (DEFINED CMAKE_LIBRARY_ARCHITECTURE)
-        set (_${_PYTHON_PREFIX}_CONFIG_NAMES "${CMAKE_LIBRARY_ARCHITECTURE}-python${_${_PYTHON_PREFIX}_VERSION}-config")
-      endif()
-      list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "python${_${_PYTHON_PREFIX}_VERSION}-config")
-      find_program (_${_PYTHON_PREFIX}_CONFIG
-                    NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
-                    NAMES_PER_DIR
-                    HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES bin)
-      unset (_${_PYTHON_PREFIX}_CONFIG_NAMES)
-
-      if (NOT _${_PYTHON_PREFIX}_CONFIG)
-        continue()
-      endif()
-      if (DEFINED CMAKE_LIBRARY_ARCHITECTURE)
-        # check that config tool match library architecture
-        execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
-                         RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                         OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
-                         ERROR_QUIET
-                         OUTPUT_STRIP_TRAILING_WHITESPACE)
-        if (_${_PYTHON_PREFIX}_RESULT)
-          unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
-          continue()
-        endif()
-        string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
-        if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
-          unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
-          continue()
-        endif()
-      endif()
-
-      if (_${_PYTHON_PREFIX}_CONFIG)
-        break()
-      endif()
-    endforeach()
-  endif()
-
-  if (_${_PYTHON_PREFIX}_CONFIG)
-    # retrieve root install directory
-    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --prefix
-                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PREFIX
-                     ERROR_QUIET
-                     OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (_${_PYTHON_PREFIX}_RESULT)
-      # python-config is not usable
-      unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
-    endif()
-  endif()
-
-  if (_${_PYTHON_PREFIX}_CONFIG)
-    set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
-
-    unset (_${_PYTHON_PREFIX}_LIB_DIRS)
-    unset (_${_PYTHON_PREFIX}_PATH_SUFFIXES)
-    unset (_${_PYTHON_PREFIX}_LIB_NAMES)
-
-    # retrieve library
-    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags
-                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS
-                     ERROR_QUIET
-                     OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (NOT _${_PYTHON_PREFIX}_RESULT)
-      # retrieve library directory
-      string (REGEX MATCHALL "-L[^ ]+" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_FLAGS}")
-      string (REPLACE "-L" "" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_LIB_DIRS}")
-      if (_${_PYTHON_PREFIX}_CONFIG MATCHES "python([0-9.]+)-config")
-        _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${CMAKE_MATCH_1} LIBRARY)
-      endif()
-
-      # retrieve library name
-      string (REGEX MATCHALL "-lpython[^ ]+" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_FLAGS}")
-      string (REPLACE "-l" "" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_LIB_NAMES}")
-      list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_NAMES)
-    endif()
-
-    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
-                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
-                     ERROR_QUIET
-                     OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (NOT _${_PYTHON_PREFIX}_RESULT)
-      list (APPEND _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_CONFIGDIR}")
-    endif()
-    list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_DIRS)
-    list (APPEND _${_PYTHON_PREFIX}_HINTS ${_${_PYTHON_PREFIX}_LIB_DIRS})
-
-    if (NOT _${_PYTHON_PREFIX}_LIB_NAMES)
-      # config tool do not specify "-l" option (it is the case starting with 3.8)
-      # extract version from the config tool name and list all possible lib names
-      if (_${_PYTHON_PREFIX}_CONFIG MATCHES "python([0-9.]+)-config")
-        _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${CMAKE_MATCH_1})
-      endif()
-    endif()
-
-    list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
-
-    find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
-                  NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
-                  NAMES_PER_DIR
-                  HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                  PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                  NO_SYSTEM_ENVIRONMENT_PATH
-                  NO_CMAKE_SYSTEM_PATH)
-
-    # retrieve runtime library
-    if (${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-      get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
-      get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
-      _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
-                                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
-                                    NAMES_PER_DIR
-                                    HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
-                                    PATH_SUFFIXES bin
-                                    NO_SYSTEM_ENVIRONMENT_PATH
-                                    NO_CMAKE_SYSTEM_PATH)
-    endif()
-
-    # retrieve include directory
-    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --includes
-                     RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                     OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS
-                     ERROR_QUIET
-                     OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (NOT _${_PYTHON_PREFIX}_RESULT)
-      # retrieve include directory
-      string (REGEX MATCHALL "-I[^ ]+" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_FLAGS}")
-      string (REPLACE "-I" "" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIRS}")
-      list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_DIRS)
-
-      find_path (${_PYTHON_PREFIX}_INCLUDE_DIR
-                 NAMES Python.h
-                 HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS}
-                 NO_SYSTEM_ENVIRONMENT_PATH
-                 NO_CMAKE_SYSTEM_PATH)
-    endif()
-  endif()
-
-  # Rely on HINTS and standard paths if config tool failed to locate artifacts
-  if (NOT ${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT ${_PYTHON_PREFIX}_INCLUDE_DIR)
-    set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
-
-    if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
-      unset (_${_PYTHON_PREFIX}_LIB_NAMES)
-      unset (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG)
       unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
-      unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
-      unset (_${_PYTHON_PREFIX}_PATH_SUFFIXES)
 
-      foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-        # library names
-        _python_get_lib_names (_${_PYTHON_PREFIX}_VERSION_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION})
-        list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_VERSION_NAMES})
-        _python_get_lib_names (_${_PYTHON_PREFIX}_VERSION_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION} DEBUG)
-        list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION_NAMES})
+      if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
+        set (_${_PYTHON_PREFIX}_CONFIG_NAMES)
 
-        # Framework Paths
-        _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
-        list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
+        foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+          _python_get_names (_${_PYTHON_PREFIX}_VERSION_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX CONFIG)
+          list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES ${_${_PYTHON_PREFIX}_VERSION_NAMES})
 
-        # Registry Paths
-        _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
-        list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
+          # Framework Paths
+          _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+          list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
+        endforeach()
 
-        # Paths suffixes
-        _python_get_path_suffixes (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY)
-        list (APPEND _${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_VERSION_PATHS})
-      endforeach()
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+          find_program (_${_PYTHON_PREFIX}_CONFIG
+                        NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                              ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_CMAKE_PATH
+                        NO_CMAKE_ENVIRONMENT_PATH
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+        endif()
 
-      if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
-        find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
-                      NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+        find_program (_${_PYTHON_PREFIX}_CONFIG
+                      NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
                       NAMES_PER_DIR
                       HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                      NO_CMAKE_PATH
-                      NO_CMAKE_ENVIRONMENT_PATH
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-      endif()
+                      PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                      PATH_SUFFIXES bin)
 
-      if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
-                      NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
-                      NAMES_PER_DIR
-                      HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                      PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                      NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-      endif()
+        # Apple frameworks handling
+        if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+          find_program (_${_PYTHON_PREFIX}_CONFIG
+                        NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATH_SUFFIXES bin
+                        NO_DEFAULT_PATH)
+        endif()
 
-      # search in HINTS locations
-      find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
+        if (_${_PYTHON_PREFIX}_CONFIG)
+          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help
+                           RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                           OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP
+                           ERROR_QUIET
+                           OUTPUT_STRIP_TRAILING_WHITESPACE)
+          if (_${_PYTHON_PREFIX}_RESULT)
+            # assume config tool is not usable
+            unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+          endif()
+        endif()
+
+        if (_${_PYTHON_PREFIX}_CONFIG)
+          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags
+                           RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                           OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS
+                           ERROR_QUIET
+                           OUTPUT_STRIP_TRAILING_WHITESPACE)
+          if (_${_PYTHON_PREFIX}_RESULT)
+            # assume ABI is not supported
+            set (__${_PYTHON_PREFIX}_ABIFLAGS "")
+          endif()
+          if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+            # Wrong ABI
+            unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+          endif()
+        endif()
+
+        if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
+          # check that config tool match library architecture
+          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
+                           RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                           OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
+                           ERROR_QUIET
+                           OUTPUT_STRIP_TRAILING_WHITESPACE)
+          if (_${_PYTHON_PREFIX}_RESULT)
+            unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+          else()
+            string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
+            if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
+              unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            endif()
+          endif()
+        endif()
+      else()
+        foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+          # try to use pythonX.Y-config tool
+          _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX CONFIG)
+
+          # Framework Paths
+          _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION})
+
+          # Apple frameworks handling
+          if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+            find_program (_${_PYTHON_PREFIX}_CONFIG
+                          NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
+                          NAMES_PER_DIR
+                          HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                          PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                          PATH_SUFFIXES bin
+                          NO_CMAKE_PATH
+                          NO_CMAKE_ENVIRONMENT_PATH
+                          NO_SYSTEM_ENVIRONMENT_PATH
+                          NO_CMAKE_SYSTEM_PATH)
+          endif()
+
+          find_program (_${_PYTHON_PREFIX}_CONFIG
+                        NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                        PATH_SUFFIXES bin)
+
+          # Apple frameworks handling
+          if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+            find_program (_${_PYTHON_PREFIX}_CONFIG
+                          NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES}
+                          NAMES_PER_DIR
+                          PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                          PATH_SUFFIXES bin
+                          NO_DEFAULT_PATH)
+          endif()
+
+          unset (_${_PYTHON_PREFIX}_CONFIG_NAMES)
+
+          if (_${_PYTHON_PREFIX}_CONFIG)
+            execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help
+                             RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                             OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP
+                             ERROR_QUIET
+                             OUTPUT_STRIP_TRAILING_WHITESPACE)
+            if (_${_PYTHON_PREFIX}_RESULT)
+              # assume config tool is not usable
+              unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            endif()
+          endif()
+
+          if (NOT _${_PYTHON_PREFIX}_CONFIG)
+            continue()
+          endif()
+
+          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags
+                           RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                           OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS
+                           ERROR_QUIET
+                           OUTPUT_STRIP_TRAILING_WHITESPACE)
+          if (_${_PYTHON_PREFIX}_RESULT)
+            # assume ABI is not supported
+            set (__${_PYTHON_PREFIX}_ABIFLAGS "")
+          endif()
+          if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+            # Wrong ABI
+            unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            continue()
+          endif()
+
+          if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
+            # check that config tool match library architecture
+            execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
+                             RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+                             OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
+                             ERROR_QUIET
+                             OUTPUT_STRIP_TRAILING_WHITESPACE)
+            if (_${_PYTHON_PREFIX}_RESULT)
+              unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              continue()
+            endif()
+            string (FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
+            if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
+              unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              continue()
+            endif()
+          endif()
+
+          if (_${_PYTHON_PREFIX}_CONFIG)
+            break()
+          endif()
+        endforeach()
+      endif()
+    endif()
+  endif()
+
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+    if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+      # retrieve root install directory
+      _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX)
+
+      # enforce current ABI
+      _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS)
+
+      set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
+
+      # retrieve library
+      ## compute some paths and artifact names
+      if (_${_PYTHON_PREFIX}_CONFIG)
+        string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}")
+      else()
+        set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}")
+      endif()
+      _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY)
+      _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY)
+
+      _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR)
+      list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}")
+
+      list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+      find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
                     NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                     NAMES_PER_DIR
                     HINTS ${_${_PYTHON_PREFIX}_HINTS}
                     PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                     NO_SYSTEM_ENVIRONMENT_PATH
                     NO_CMAKE_SYSTEM_PATH)
+    endif()
 
-      if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
-        set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
-      else()
-        unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+    # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts
+    if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+      set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+      unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
+      if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+        set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV)
       endif()
 
-      if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-        set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
-      else()
-        unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
-      endif()
+      if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION")
+        unset (_${_PYTHON_PREFIX}_LIB_NAMES)
+        unset (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG)
+        unset (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+        unset (_${_PYTHON_PREFIX}_REGISTRY_PATHS)
+        unset (_${_PYTHON_PREFIX}_PATH_SUFFIXES)
 
-      # search in all default paths
-      find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
-                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
-                    NAMES_PER_DIR
-                    PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
-                          ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
+        foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+          # library names
+          _python_get_names (_${_PYTHON_PREFIX}_VERSION_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 POSIX LIBRARY)
+          list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_VERSION_NAMES})
+          _python_get_names (_${_PYTHON_PREFIX}_VERSION_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 DEBUG)
+          list (APPEND _${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION_NAMES})
 
-      if (${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-        # extract version from library name
-        if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])([0-9]+)")
-          set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}")
-        elseif (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "python([23])\\.([0-9]+)")
-          set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}")
-        endif()
-      endif()
+          # Framework Paths
+          _python_get_frameworks (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
+          list (APPEND _${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
 
-      if (WIN32)
-        # search for debug library
-        if (${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-          # use library location as a hint
-          _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION} DEBUG)
-          get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
-          find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                        NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                        NAMES_PER_DIR
-                        HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
-                        NO_DEFAULT_PATH)
-        else()
-          # search first in known locations
-          if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-            find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                          NAMES_PER_DIR
-                          HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                          PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                          PATH_SUFFIXES lib libs
-                          NO_SYSTEM_ENVIRONMENT_PATH
-                          NO_CMAKE_SYSTEM_PATH)
-          endif()
-          # search in all default paths
-          find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                        NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                        NAMES_PER_DIR
-                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                        PATHS ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                        PATH_SUFFIXES lib libs)
+          # Registry Paths
+          _python_get_registries (_${_PYTHON_PREFIX}_VERSION_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
+          list (APPEND _${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION_PATHS})
 
-          # extract version from library name
-          if (${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "python([23])([0-9]+)")
-            set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}")
-          elseif (${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "python([23])\\.([0-9]+)")
-            set (_${_PYTHON_PREFIX}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}")
-          endif()
-        endif()
-      endif()
-    else()
-      foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
-        _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_LIB_VERSION})
-        _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_LIB_VERSION} DEBUG)
-
-        _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
-        _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
-
-        _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY)
+          # Paths suffixes
+          _python_get_path_suffixes (_${_PYTHON_PREFIX}_VERSION_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY)
+          list (APPEND _${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_VERSION_PATHS})
+        endforeach()
 
         if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
-          find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
+          find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
                         NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                         NAMES_PER_DIR
                         HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                        PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                        PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                              ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
@@ -1348,152 +1794,209 @@
         endif()
 
         if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-          find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
+          find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
                         NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                         NAMES_PER_DIR
                         HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                        PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                              ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                         NO_SYSTEM_ENVIRONMENT_PATH
                         NO_CMAKE_SYSTEM_PATH)
         endif()
 
         # search in HINTS locations
-        find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
+        find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
                       NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                       NAMES_PER_DIR
                       HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                      PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
                       NO_CMAKE_SYSTEM_PATH)
 
-       if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
-         set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
-       else()
-         unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
-       endif()
+        if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+          set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
+        else()
+          unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+        endif()
 
-       if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
-         set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
-       else()
-         unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
-       endif()
+        if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+          set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
+        else()
+          unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
+        endif()
 
-       # search in all default paths
-       find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE
+        # search in all default paths
+        find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
                       NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                       NAMES_PER_DIR
                       PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                             ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
+      else()
+        foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
+          _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 POSIX LIBRARY)
+          _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 DEBUG)
 
-        if (WIN32)
-          # search for debug library
-          if (${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-            # use library location as a hint
-            get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
-            find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                          NAMES_PER_DIR
-                          HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
-                          NO_DEFAULT_PATH)
-          else()
-            # search first in known locations
-            if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-              find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                            NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                            NAMES_PER_DIR
-                            HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                            PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                            PATH_SUFFIXES lib libs
-                            NO_SYSTEM_ENVIRONMENT_PATH
-                            NO_CMAKE_SYSTEM_PATH)
-            endif()
-            # search in all default paths
-            find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG
-                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+          _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
+          _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_LIB_VERSION})
+
+          _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY)
+
+          if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
+            find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
+                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
                           NAMES_PER_DIR
                           HINTS ${_${_PYTHON_PREFIX}_HINTS}
-                          PATHS ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
-                          PATH_SUFFIXES lib libs)
+                          PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                          PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                          NO_CMAKE_PATH
+                          NO_CMAKE_ENVIRONMENT_PATH
+                          NO_SYSTEM_ENVIRONMENT_PATH
+                          NO_CMAKE_SYSTEM_PATH)
           endif()
-        endif()
 
-        if (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
-          set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION})
-          break()
-        endif()
-      endforeach()
+          if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
+            find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
+                          NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                          NAMES_PER_DIR
+                          HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                          PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                                ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                          PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                          NO_SYSTEM_ENVIRONMENT_PATH
+                          NO_CMAKE_SYSTEM_PATH)
+          endif()
+
+          # search in HINTS locations
+          find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
+                        NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                        NAMES_PER_DIR
+                        HINTS ${_${_PYTHON_PREFIX}_HINTS}
+                        PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        NO_SYSTEM_ENVIRONMENT_PATH
+                        NO_CMAKE_SYSTEM_PATH)
+
+         if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST")
+           set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS})
+         else()
+           unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS)
+         endif()
+
+         if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
+           set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS})
+         else()
+           unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
+         endif()
+
+         # search in all default paths
+         find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
+                        NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                        NAMES_PER_DIR
+                        PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                              ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                        PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
+
+          if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+            break()
+          endif()
+        endforeach()
+      endif()
+    endif()
+  endif()
+
+  # finalize library version information
+  _python_get_version (LIBRARY PREFIX _${_PYTHON_PREFIX}_)
+
+  set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" CACHE FILEPATH "Path to a library." FORCE)
+
+  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
+    set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "_${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
+  endif()
+
+  set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+  if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+    # search for debug library
+    # use release library location as a hint
+    _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
+    find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG
+                  NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+                  NAMES_PER_DIR
+                  HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS}
+                  NO_DEFAULT_PATH)
+  endif()
+
+  # retrieve runtime libraries
+  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
+    _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
+    _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
+                                  NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
+                                  NAMES_PER_DIR
+                                  HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
+                                  PATH_SUFFIXES bin)
+  endif()
+  if (_${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+    _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
+    _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
+                                  NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
+                                  NAMES_PER_DIR
+                                  HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
+                                  PATH_SUFFIXES bin)
+  endif()
+
+  # Don't search for include dir if no library was founded
+  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+    if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG)
+      _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES)
+
+      find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
+                 NAMES Python.h
+                 HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS}
+                 NO_SYSTEM_ENVIRONMENT_PATH
+                 NO_CMAKE_SYSTEM_PATH)
     endif()
 
-    # retrieve runtime libraries
-    if (${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-      _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES ${_${_PYTHON_PREFIX}_VERSION})
-      get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
-      get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
-      _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
-                                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES}
-                                    NAMES_PER_DIR
-                                    HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
-                                    PATH_SUFFIXES bin)
-    endif()
-    if (${_PYTHON_PREFIX}_LIBRARY_DEBUG)
-      _python_get_lib_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG ${_${_PYTHON_PREFIX}_VERSION} DEBUG)
-      get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY)
-      get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY)
-      _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
-                                    NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG}
-                                    NAMES_PER_DIR
-                                    HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS}
-                                    PATH_SUFFIXES bin)
-    endif()
-
-    # Don't search for include dir if no library was founded
-    if (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+    # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts
+    if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+      unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS)
+      if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+        set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV)
+      endif()
       unset (_${_PYTHON_PREFIX}_INCLUDE_HINTS)
 
-      if (${_PYTHON_PREFIX}_EXECUTABLE)
-        # pick up include directory from configuration
-        execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
-                                 "import sys; import sysconfig; sys.stdout.write(sysconfig.get_path('include'))"
-                         RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                         OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PATH
-                         ERROR_QUIET
-                         OUTPUT_STRIP_TRAILING_WHITESPACE)
-        if (NOT _${_PYTHON_PREFIX}_RESULT)
-          file (TO_CMAKE_PATH "${_${_PYTHON_PREFIX}_PATH}" _${_PYTHON_PREFIX}_PATH)
-          list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PATH}")
-        endif()
+      # Use the library's install prefix as a hint
+      if (${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)")
+        list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+      elseif (${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config")
+        list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+      elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
+        list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
+      else()
+        # assume library is in a directory under root
+        get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY)
+        get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY)
+        list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
       endif()
 
-      foreach (_${_PYTHON_PREFIX}_LIB IN ITEMS ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
-        if (${_${_PYTHON_PREFIX}_LIB})
-          # Use the library's install prefix as a hint
-          if (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)")
-            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
-          elseif (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config")
-            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
-          elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
-            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}")
-          else()
-            # assume library is in a directory under root
-            get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${${_${_PYTHON_PREFIX}_LIB}}" DIRECTORY)
-            get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY)
-            list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}")
-          endif()
-        endif()
-      endforeach()
-      list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_HINTS)
-
       _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION})
       _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_VERSION})
-      _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES ${_${_PYTHON_PREFIX}_VERSION} INCLUDE)
+      _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INCLUDE)
 
       if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
-        find_path (${_PYTHON_PREFIX}_INCLUDE_DIR
+        find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
                    NAMES Python.h
                    HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS}
-                   PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                   PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                         ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                    NO_CMAKE_PATH
                    NO_CMAKE_ENVIRONMENT_PATH
@@ -1502,10 +2005,11 @@
       endif()
 
       if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST")
-        find_path (${_PYTHON_PREFIX}_INCLUDE_DIR
+        find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
                    NAMES Python.h
                    HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS}
-                   PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
+                   PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                         ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                    NO_SYSTEM_ENVIRONMENT_PATH
                    NO_CMAKE_SYSTEM_PATH)
@@ -1523,10 +2027,11 @@
         unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS)
       endif()
 
-      find_path (${_PYTHON_PREFIX}_INCLUDE_DIR
+      find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
                  NAMES Python.h
                  HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS}
-                 PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
+                 PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS}
+                       ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                        ${__${_PYTHON_PREFIX}_REGISTRY_PATHS}
                  PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                  NO_SYSTEM_ENVIRONMENT_PATH
@@ -1534,31 +2039,37 @@
     endif()
 
     # search header file in standard locations
-    find_path (${_PYTHON_PREFIX}_INCLUDE_DIR
+    find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR
                NAMES Python.h)
   endif()
 
-  if (${_PYTHON_PREFIX}_INCLUDE_DIR)
-    # retrieve version from header file
-    file (STRINGS "${${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" _${_PYTHON_PREFIX}_VERSION
-          REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"")
-    string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1"
-                          _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_VERSION}")
-    string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}")
-    list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR)
-    list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR)
-    list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH)
+  set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
 
-    if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND)
-      # set public version information
-      set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION})
-      set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR})
-      set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR})
-      set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH})
+  if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
+    set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
+  endif()
+
+  if (_${_PYTHON_PREFIX}_INCLUDE_DIR)
+    # retrieve version from header file
+    _python_get_version (INCLUDE PREFIX _${_PYTHON_PREFIX}_INC_)
+
+    # update versioning
+    if (_${_PYTHON_PREFIX}_INC_VERSION VERSION_EQUAL ${_${_PYTHON_PREFIX}_VERSION})
+      set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH})
     endif()
   endif()
 
+  if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND)
+    # set public version information
+    set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION})
+    set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR})
+    set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR})
+    set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH})
+  endif()
+
   # define public variables
+  set (${_PYTHON_PREFIX}_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}" CACHE FILEPATH "Path to a library." FORCE)
+
   include (${CMAKE_CURRENT_LIST_DIR}/../SelectLibraryConfigurations.cmake)
   select_library_configurations (${_PYTHON_PREFIX})
   if (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
@@ -1566,14 +2077,13 @@
   elseif (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)
     set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}")
   else()
-    set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "$${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND")
+    set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND")
   endif()
 
   _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS
                             ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
   if (UNIX)
-    if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$"
-        OR ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$")
+    if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$")
       set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS})
     endif()
   else()
@@ -1581,66 +2091,114 @@
                                 ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)
   endif()
 
-  set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${${_PYTHON_PREFIX}_INCLUDE_DIR}")
-
-  mark_as_advanced (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
-                    ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
-                    ${_PYTHON_PREFIX}_INCLUDE_DIR)
-
-  if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
-      AND ${_PYTHON_PREFIX}_INCLUDE_DIR)
+  if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND _${_PYTHON_PREFIX}_INCLUDE_DIR)
     if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND)
       # development environment must be compatible with interpreter/compiler
-      if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR})
+      if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}
+          AND ${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR} VERSION_EQUAL ${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR})
         set (${_PYTHON_PREFIX}_Development_FOUND TRUE)
       endif()
-    elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
+    elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR
+            AND ${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR} VERSION_EQUAL ${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR})
       set (${_PYTHON_PREFIX}_Development_FOUND TRUE)
     endif()
+    if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND
+        (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS
+          OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS))
+      set (${_PYTHON_PREFIX}_Development_FOUND FALSE)
+    endif()
+  endif()
+
+  if (${_PYTHON_PREFIX}_Development_FOUND)
+    # compute and save development signature
+    string (MD5 __${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
+    set (_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE "${__${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE}" CACHE INTERNAL "")
+  else()
+    unset (_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE CACHE)
   endif()
 
   # Restore the original find library ordering
   if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES)
     set (CMAKE_FIND_LIBRARY_SUFFIXES ${_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES})
   endif()
+
+  mark_as_advanced (_${_PYTHON_PREFIX}_LIBRARY_RELEASE
+                    _${_PYTHON_PREFIX}_LIBRARY_DEBUG
+                    ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
+                    ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
+                    _${_PYTHON_PREFIX}_INCLUDE_DIR
+                    _${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE)
 endif()
 
 if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND)
-  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
+  list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
   if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy)
-    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
+    list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS)
   endif()
-  execute_process(
+
+  if (DEFINED ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR
+      AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+    set (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE INTERNAL "")
+  elseif (DEFINED _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
+    # compute numpy signature. Depends on interpreter and development signatures
+    string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE}:${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+    if (NOT __${_PYTHON_PREFIX}_NUMPY_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_NUMPY_SIGNATURE
+        OR NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+      unset (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR CACHE)
+      unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE)
+    endif()
+  endif()
+
+  if (NOT _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
+    execute_process(
       COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
               "from __future__ import print_function\ntry: import numpy; print(numpy.get_include(), end='')\nexcept:pass\n"
       RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
       OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH
       ERROR_QUIET
       OUTPUT_STRIP_TRAILING_WHITESPACE)
-  if (NOT _${_PYTHON_PREFIX}_RESULT)
-    find_path(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR
-              NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h"
-              HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}"
-              NO_DEFAULT_PATH)
+
+    if (NOT _${_PYTHON_PREFIX}_RESULT)
+      find_path (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR
+                 NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h"
+                 HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}"
+                 NO_DEFAULT_PATH)
+    endif()
   endif()
-  if(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
-    set(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
-    set(${_PYTHON_PREFIX}_NumPy_FOUND TRUE)
+
+  set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+
+  if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+    set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND")
   endif()
-  if(${_PYTHON_PREFIX}_NumPy_FOUND)
-    execute_process(
+
+  if (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
+    execute_process (
             COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c
             "from __future__ import print_function\ntry: import numpy; print(numpy.__version__, end='')\nexcept:pass\n"
             RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
             OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION)
     if (NOT _${_PYTHON_PREFIX}_RESULT)
-       set(${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}")
+      set (${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}")
+    else()
+      unset (${_PYTHON_PREFIX}_NumPy_VERSION)
     endif()
+
+    # final step: set NumPy founded only if Development component is founded as well
+    set(${_PYTHON_PREFIX}_NumPy_FOUND ${${_PYTHON_PREFIX}_Development_FOUND})
+  else()
+    set (${_PYTHON_PREFIX}_NumPy_FOUND FALSE)
   endif()
-  # final step: set NumPy founded only if Development component is founded as well
-  if (NOT ${_PYTHON_PREFIX}_Development_FOUND)
-    set(${_PYTHON_PREFIX}_NumPy_FOUND FALSE)
+
+  if (${_PYTHON_PREFIX}_NumPy_FOUND)
+    # compute and save numpy signature
+    string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}")
+    set (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${__${_PYTHON_PREFIX}_NUMPY_SIGNATURE}" CACHE INTERNAL "")
+  else()
+    unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE)
   endif()
+
+  mark_as_advanced (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR)
 endif()
 
 # final validation
@@ -1678,20 +2236,20 @@
 
     macro (__PYTHON_IMPORT_LIBRARY __name)
       if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$"
-          OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$"
-          OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)
+          OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
         set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED)
       else()
         set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC)
       endif()
 
-      add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED)
+      if (NOT TARGET ${__name})
+        add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED)
+      endif()
 
       set_property (TARGET ${__name}
-                    PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}")
+                    PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}")
 
-      if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
-          OR (${_PYTHON_PREFIX}_LIBRARY_DEBUG AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG))
+      if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE)
         # System manage shared libraries in two parts: import and runtime
         if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG)
           set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG)
@@ -1725,53 +2283,41 @@
         endif()
       endif()
 
-      if (_${_PYTHON_PREFIX}_CONFIG AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC")
+      if (_${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC")
         # extend link information with dependent libraries
-        execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags
-                         RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
-                         OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS
-                         ERROR_QUIET
-                         OUTPUT_STRIP_TRAILING_WHITESPACE)
-        if (NOT _${_PYTHON_PREFIX}_RESULT)
-          string (REGEX MATCHALL "-[Ll][^ ]+" _${_PYTHON_PREFIX}_LINK_LIBRARIES "${_${_PYTHON_PREFIX}_FLAGS}")
-          # remove elements relative to python library itself
-          list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-lpython")
-          foreach (_${_PYTHON_PREFIX}_DIR IN LISTS ${_PYTHON_PREFIX}_LIBRARY_DIRS)
-            list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-L${${_PYTHON_PREFIX}_DIR}")
-          endforeach()
+        _python_get_config_var (_${_PYTHON_PREFIX}_LINK_LIBRARIES LIBS)
+        if (_${_PYTHON_PREFIX}_LINK_LIBRARIES)
           set_property (TARGET ${__name}
                         PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES})
         endif()
       endif()
     endmacro()
 
-    if (NOT TARGET ${_PYTHON_PREFIX}::Python)
-      __python_import_library (${_PYTHON_PREFIX}::Python)
-    endif()
+    __python_import_library (${_PYTHON_PREFIX}::Python)
 
-    if (NOT TARGET ${_PYTHON_PREFIX}::Module)
-      if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$")
-        # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python
-        # but ALIAS cannot be used because the imported library is not GLOBAL.
-        __python_import_library (${_PYTHON_PREFIX}::Module)
-      else()
+    if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$")
+      # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python
+      # but ALIAS cannot be used because the imported library is not GLOBAL.
+      __python_import_library (${_PYTHON_PREFIX}::Module)
+    else()
+      if (NOT TARGET ${_PYTHON_PREFIX}::Module )
         add_library (${_PYTHON_PREFIX}::Module INTERFACE IMPORTED)
-        set_property (TARGET ${_PYTHON_PREFIX}::Module
-                      PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}")
+      endif()
+      set_property (TARGET ${_PYTHON_PREFIX}::Module
+                    PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}")
 
-        # When available, enforce shared library generation with undefined symbols
-        if (APPLE)
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
-        endif()
-        if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs")
-         endif()
-        if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
-          set_property (TARGET ${_PYTHON_PREFIX}::Module
-                        PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok")
-         endif()
+      # When available, enforce shared library generation with undefined symbols
+      if (APPLE)
+        set_property (TARGET ${_PYTHON_PREFIX}::Module
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
+      endif()
+      if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+        set_property (TARGET ${_PYTHON_PREFIX}::Module
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs")
+      endif()
+      if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+        set_property (TARGET ${_PYTHON_PREFIX}::Module
+                      PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok")
       endif()
     endif()
 
@@ -1810,7 +2356,7 @@
       AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Module)
     add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED)
     set_property (TARGET ${_PYTHON_PREFIX}::NumPy
-                  PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
+                  PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS}")
     target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module)
   endif()
 endif()
diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake
index 8372ce7..0cecdc6 100644
--- a/Modules/FindPython2.cmake
+++ b/Modules/FindPython2.cmake
@@ -193,6 +193,50 @@
     ``NEVER`` to select preferably the interpreter from the virtual
     environment.
 
+  .. note::
+
+    If the component ``Development`` is requested, it is **strongly**
+    recommended to also include the component ``Interpreter`` to get expected
+    result.
+
+Artifacts Specification
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To solve special cases, it is possible to specify directly the artifacts by
+setting the following variables:
+
+``Python2_EXECUTABLE``
+  The path to the interpreter.
+
+``Python2_COMPILER``
+  The path to the compiler.
+
+``Python2_LIBRARY``
+  The path to the library. It will be used to compute the
+  variables ``Python2_LIBRARIES``, ``Python2_LIBRAY_DIRS`` and
+  ``Python2_RUNTIME_LIBRARY_DIRS``.
+
+``Python2_INCLUDE_DIR``
+  The path to the directory of the ``Python`` headers. It will be used to
+  compute the variable ``Python2_INCLUDE_DIRS``.
+
+``Python2_NumPy_INCLUDE_DIR``
+  The path to the directory of the ``NumPy`` headers. It will be used to
+  compute the variable ``Python2_NumPy_INCLUDE_DIRS``.
+
+.. note::
+
+  All paths must be absolute. Any artifact specified with a relative path
+  will be ignored.
+
+.. note::
+
+  When an artifact is specified, all ``HINTS`` will be ignored and no search
+  will be performed for this artifact.
+
+  If more than one artifact is specified, it is the user's responsability to
+  ensure the consistency of the various artifacts.
+
 Commands
 ^^^^^^^^
 
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index 2ead5b6..1edb9c9 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -138,6 +138,47 @@
   * If set to TRUE, search **only** for static libraries.
   * If set to FALSE, search **only** for shared libraries.
 
+``Python3_FIND_ABI``
+  This variable defines which ABIs, as defined in
+  `PEP 3149 <https://www.python.org/dev/peps/pep-3149/>`_, should be searched.
+
+  .. note::
+
+    If ``Python3_FIND_ABI`` is not defined, any ABI will be searched.
+
+  The ``Python3_FIND_ABI`` variable is a 3-tuple specifying, in that order,
+  ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags.
+  Each element can be set to one of the following:
+
+  * ``ON``: Corresponding flag is selected.
+  * ``OFF``: Corresponding flag is not selected.
+  * ``ANY``: The two posibilties (``ON`` and ``OFF``) will be searched.
+
+  From this 3-tuple, various ABIs will be searched starting from the most
+  specialized to the most general. Moreover, ``debug`` versions will be
+  searched **after** ``non-debug`` ones.
+
+  For example, if we have::
+
+    set (Python3_FIND_ABI "ON" "ANY" "ANY")
+
+  The following flags combinations will be appended, in that order, to the
+  artifact names: ``dmu``, ``dm``, ``du``, and ``d``.
+
+  And to search any possible ABIs::
+
+    set (Python3_FIND_ABI "ANY" "ANY" "ANY")
+
+  The following combinations, in that order, will be used: ``mu``, ``m``,
+  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``.
+
+  .. note::
+
+    This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems,
+    when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from
+    `python.org <https://www.python.org/>`_ will be found only if value for
+    each flag is ``OFF`` or ``ANY``.
+
 ``Python3_FIND_STRATEGY``
   This variable defines how lookup will be done.
   The ``Python3_FIND_STRATEGY`` variable can be set to empty or one of the
@@ -193,6 +234,50 @@
     ``NEVER`` to select preferably the interpreter from the virtual
     environment.
 
+  .. note::
+
+    If the component ``Development`` is requested, it is **strongly**
+    recommended to also include the component ``Interpreter`` to get expected
+    result.
+
+Artifacts Specification
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To solve special cases, it is possible to specify directly the artifacts by
+setting the following variables:
+
+``Python3_EXECUTABLE``
+  The path to the interpreter.
+
+``Python3_COMPILER``
+  The path to the compiler.
+
+``Python3_LIBRARY``
+  The path to the library. It will be used to compute the
+  variables ``Python3_LIBRARIES``, ``Python3_LIBRAY_DIRS`` and
+  ``Python3_RUNTIME_LIBRARY_DIRS``.
+
+``Python3_INCLUDE_DIR``
+  The path to the directory of the ``Python`` headers. It will be used to
+  compute the variable ``Python3_INCLUDE_DIRS``.
+
+``Python3_NumPy_INCLUDE_DIR``
+  The path to the directory of the ``NumPy`` headers. It will be used to
+  compute the variable ``Python3_NumPy_INCLUDE_DIRS``.
+
+.. note::
+
+  All paths must be absolute. Any artifact specified with a relative path
+  will be ignored.
+
+.. note::
+
+  When an artifact is specified, all ``HINTS`` will be ignored and no search
+  will be performed for this artifact.
+
+  If more than one artifact is specified, it is the user's responsability to
+  ensure the consistency of the various artifacts.
+
 Commands
 ^^^^^^^^
 
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/FindQt4.cmake b/Modules/FindQt4.cmake
index a145b46..3993968 100644
--- a/Modules/FindQt4.cmake
+++ b/Modules/FindQt4.cmake
@@ -670,7 +670,7 @@
           get_filename_component(qt_headers "${QT_QTCORE_INCLUDE_DIR}/../" ABSOLUTE)
           set(QT_HEADERS_DIR "${qt_headers}" CACHE INTERNAL "" FORCE)
         endif()
-      elseif()
+      else()
         message("Warning: QT_QMAKE_EXECUTABLE reported QT_INSTALL_HEADERS as ${qt_headers}")
         message("Warning: But QtCore couldn't be found.  Qt must NOT be installed correctly.")
       endif()
diff --git a/Modules/FindThreads.cmake b/Modules/FindThreads.cmake
index b0c91b2..d39fe33 100644
--- a/Modules/FindThreads.cmake
+++ b/Modules/FindThreads.cmake
@@ -77,7 +77,7 @@
   if(NOT Threads_FOUND)
      CHECK_LIBRARY_EXISTS(${LIBNAME} ${FUNCNAME} "" ${VARNAME})
      if(${VARNAME})
-       set(CMAKE_THREAD_LIBS_INIT "-l${LIBNAME}")
+       set(CMAKE_THREAD_LIBS_INIT "${LIBNAME}")
        set(CMAKE_HAVE_THREADS_LIBRARY 1)
        set(Threads_FOUND TRUE)
      endif()
@@ -88,7 +88,7 @@
 # Do NOT even think about using it outside of this file!
 macro(_check_pthreads_flag)
   if(NOT Threads_FOUND)
-    # If we did not found -lpthread, -lpthread, or -lthread, look for -pthread
+    # If we did not find a thread library look for -pthread compiler option.
     if(NOT DEFINED THREADS_HAVE_PTHREAD_ARG)
       message(STATUS "Check if compiler accepts -pthread")
       if(CMAKE_C_COMPILER_LOADED)
@@ -164,7 +164,7 @@
       _check_threads_lib(pthreads pthread_create CMAKE_HAVE_PTHREADS_CREATE)
       _check_threads_lib(pthread  pthread_create CMAKE_HAVE_PTHREAD_CREATE)
       if(CMAKE_SYSTEM_NAME MATCHES "SunOS")
-          # On sun also check for -lthread
+          # On sun also check for thread library with thr_create
           _check_threads_lib(thread thr_create CMAKE_HAVE_THR_CREATE)
       endif()
     endif()
@@ -195,7 +195,7 @@
     # are available.
     CHECK_LIBRARY_EXISTS(cma pthread_attr_create "" CMAKE_HAVE_HP_CMA)
     if(CMAKE_HAVE_HP_CMA)
-      set(CMAKE_THREAD_LIBS_INIT "-lcma")
+      set(CMAKE_THREAD_LIBS_INIT "cma")
       set(CMAKE_HP_PTHREADS_INIT 1)
       set(Threads_FOUND TRUE)
     endif()
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake
index ae8d72d..55da55f 100644
--- a/Modules/FindVulkan.cmake
+++ b/Modules/FindVulkan.cmake
@@ -5,7 +5,7 @@
 FindVulkan
 ----------
 
-Find Vulkan, which isis a low-overhead, cross-platform 3D graphics
+Find Vulkan, which is a low-overhead, cross-platform 3D graphics
 and computing API.
 
 IMPORTED Targets
diff --git a/Modules/GNUInstallDirs.cmake b/Modules/GNUInstallDirs.cmake
index 4db4e18..f95e6e2 100644
--- a/Modules/GNUInstallDirs.cmake
+++ b/Modules/GNUInstallDirs.cmake
@@ -222,7 +222,8 @@
     # updated to the new default, unless the user explicitly changed it.
   endif()
   if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
-      AND NOT CMAKE_CROSSCOMPILING)
+      AND NOT CMAKE_CROSSCOMPILING
+      AND NOT EXISTS "/etc/arch-release")
     if (EXISTS "/etc/debian_version") # is this a debian system ?
       if(CMAKE_LIBRARY_ARCHITECTURE)
         if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index 5be4676..57ae446 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -5,6 +5,10 @@
 GetPrerequisites
 ----------------
 
+.. deprecated:: 3.16
+
+  Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead.
+
 Functions to analyze and list executable file prerequisites.
 
 This module provides functions to list the .dll, .dylib or .so files
@@ -706,7 +710,9 @@
       find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
       if(gp_dumpbin)
         set(gp_tool "dumpbin")
-      else() # Try harder. Maybe we're on MinGW
+      elseif(CMAKE_OBJDUMP) # Try harder. Maybe we're on MinGW
+        set(gp_tool "${CMAKE_OBJDUMP}")
+      else()
         set(gp_tool "objdump")
       endif()
     endif()
@@ -723,7 +729,7 @@
 
   if(gp_tool MATCHES "ldd$")
     set(gp_cmd_args "")
-    set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
+    set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+([^\t\(]+)( \(.+\))?${eol_char}$")
     set(gp_regex_error "not found${eol_char}$")
     set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
     set(gp_regex_cmp_count 1)
diff --git a/Modules/CPack.DS_Store.in b/Modules/Internal/CPack/CPack.DS_Store.in
similarity index 100%
rename from Modules/CPack.DS_Store.in
rename to Modules/Internal/CPack/CPack.DS_Store.in
Binary files differ
diff --git a/Modules/CPack.Description.plist.in b/Modules/Internal/CPack/CPack.Description.plist.in
similarity index 100%
rename from Modules/CPack.Description.plist.in
rename to Modules/Internal/CPack/CPack.Description.plist.in
diff --git a/Modules/CPack.Info.plist.in b/Modules/Internal/CPack/CPack.Info.plist.in
similarity index 100%
rename from Modules/CPack.Info.plist.in
rename to Modules/Internal/CPack/CPack.Info.plist.in
diff --git a/Modules/CPack.NuGet.nuspec.in b/Modules/Internal/CPack/CPack.NuGet.nuspec.in
similarity index 100%
rename from Modules/CPack.NuGet.nuspec.in
rename to Modules/Internal/CPack/CPack.NuGet.nuspec.in
diff --git a/Modules/CPack.OSXScriptLauncher.in b/Modules/Internal/CPack/CPack.OSXScriptLauncher.in
old mode 100755
new mode 100644
similarity index 100%
rename from Modules/CPack.OSXScriptLauncher.in
rename to Modules/Internal/CPack/CPack.OSXScriptLauncher.in
Binary files differ
diff --git a/Modules/CPack.OSXScriptLauncher.rsrc.in b/Modules/Internal/CPack/CPack.OSXScriptLauncher.rsrc.in
similarity index 100%
rename from Modules/CPack.OSXScriptLauncher.rsrc.in
rename to Modules/Internal/CPack/CPack.OSXScriptLauncher.rsrc.in
Binary files differ
diff --git a/Modules/CPack.OSXX11.Info.plist.in b/Modules/Internal/CPack/CPack.OSXX11.Info.plist.in
similarity index 100%
rename from Modules/CPack.OSXX11.Info.plist.in
rename to Modules/Internal/CPack/CPack.OSXX11.Info.plist.in
diff --git a/Modules/CPack.OSXX11.main.scpt.in b/Modules/Internal/CPack/CPack.OSXX11.main.scpt.in
similarity index 100%
rename from Modules/CPack.OSXX11.main.scpt.in
rename to Modules/Internal/CPack/CPack.OSXX11.main.scpt.in
Binary files differ
diff --git a/Modules/CPack.RuntimeScript.in b/Modules/Internal/CPack/CPack.RuntimeScript.in
similarity index 100%
rename from Modules/CPack.RuntimeScript.in
rename to Modules/Internal/CPack/CPack.RuntimeScript.in
diff --git a/Modules/Internal/CPack/CPack.STGZ_Header.sh.in b/Modules/Internal/CPack/CPack.STGZ_Header.sh.in
new file mode 100755
index 0000000..a857aa5
--- /dev/null
+++ b/Modules/Internal/CPack/CPack.STGZ_Header.sh.in
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+# Display usage
+cpack_usage()
+{
+  cat <<EOF
+Usage: $0 [options]
+Options: [defaults in brackets after descriptions]
+  --help            print this message
+  --version         print cmake installer version
+  --prefix=dir      directory in which to install
+  --include-subdir  include the @CPACK_PACKAGE_FILE_NAME@ subdirectory
+  --exclude-subdir  exclude the @CPACK_PACKAGE_FILE_NAME@ subdirectory
+  --skip-license    accept license
+EOF
+  exit 1
+}
+
+cpack_echo_exit()
+{
+  echo $1
+  exit 1
+}
+
+# Display version
+cpack_version()
+{
+  echo "@CPACK_PACKAGE_NAME@ Installer Version: @CPACK_PACKAGE_VERSION@, Copyright (c) @CPACK_PACKAGE_VENDOR@"
+}
+
+# Helper function to fix windows paths.
+cpack_fix_slashes ()
+{
+  echo "$1" | sed 's/\\/\//g'
+}
+
+interactive=TRUE
+cpack_skip_license=FALSE
+cpack_include_subdir=""
+for a in "$@CPACK_AT_SIGN@"; do
+  if echo $a | grep "^--prefix=" > /dev/null 2> /dev/null; then
+    cpack_prefix_dir=`echo $a | sed "s/^--prefix=//"`
+    cpack_prefix_dir=`cpack_fix_slashes "${cpack_prefix_dir}"`
+  fi
+  if echo $a | grep "^--help" > /dev/null 2> /dev/null; then
+    cpack_usage
+  fi
+  if echo $a | grep "^--version" > /dev/null 2> /dev/null; then
+    cpack_version
+    exit 2
+  fi
+  if echo $a | grep "^--include-subdir" > /dev/null 2> /dev/null; then
+    cpack_include_subdir=TRUE
+  fi
+  if echo $a | grep "^--exclude-subdir" > /dev/null 2> /dev/null; then
+    cpack_include_subdir=FALSE
+  fi
+  if echo $a | grep "^--skip-license" > /dev/null 2> /dev/null; then
+    cpack_skip_license=TRUE
+  fi
+done
+
+if [ "x${cpack_include_subdir}x" != "xx" -o "x${cpack_skip_license}x" = "xTRUEx" ]
+then
+  interactive=FALSE
+fi
+
+cpack_version
+echo "This is a self-extracting archive."
+toplevel="`pwd`"
+if [ "x${cpack_prefix_dir}x" != "xx" ]
+then
+  toplevel="${cpack_prefix_dir}"
+fi
+
+echo "The archive will be extracted to: ${toplevel}"
+
+if [ "x${interactive}x" = "xTRUEx" ]
+then
+  echo ""
+  echo "If you want to stop extracting, please press <ctrl-C>."
+
+  if [ "x${cpack_skip_license}x" != "xTRUEx" ]
+  then
+    more << '____cpack__here_doc____'
+@CPACK_RESOURCE_FILE_LICENSE_CONTENT@
+____cpack__here_doc____
+    echo
+    while true
+      do
+        echo "Do you accept the license? [yn]: "
+        read line leftover
+        case ${line} in
+          y* | Y*)
+            cpack_license_accepted=TRUE
+            break;;
+          n* | N* | q* | Q* | e* | E*)
+            echo "License not accepted. Exiting ..."
+            exit 1;;
+        esac
+      done
+  fi
+
+  if [ "x${cpack_include_subdir}x" = "xx" ]
+  then
+    echo "By default the @CPACK_PACKAGE_NAME@ will be installed in:"
+    echo "  \"${toplevel}/@CPACK_PACKAGE_FILE_NAME@\""
+    echo "Do you want to include the subdirectory @CPACK_PACKAGE_FILE_NAME@?"
+    echo "Saying no will install in: \"${toplevel}\" [Yn]: "
+    read line leftover
+    cpack_include_subdir=TRUE
+    case ${line} in
+      n* | N*)
+        cpack_include_subdir=FALSE
+    esac
+  fi
+fi
+
+if [ "x${cpack_include_subdir}x" = "xTRUEx" ]
+then
+  toplevel="${toplevel}/@CPACK_PACKAGE_FILE_NAME@"
+  mkdir -p "${toplevel}"
+fi
+echo
+echo "Using target directory: ${toplevel}"
+echo "Extracting, please wait..."
+echo ""
+
+# take the archive portion of this file and pipe it to tar
+# the NUMERIC parameter in this command should be one more
+# than the number of lines in this header file
+# there are tails which don't understand the "-n" argument, e.g. on SunOS
+# OTOH there are tails which complain when not using the "-n" argument (e.g. GNU)
+# so at first try to tail some file to see if tail fails if used with "-n"
+# if so, don't use "-n"
+use_new_tail_syntax="-n"
+tail $use_new_tail_syntax +1 "$0" > /dev/null 2> /dev/null || use_new_tail_syntax=""
+
+extractor="pax -r"
+command -v pax > /dev/null 2> /dev/null || extractor="tar xf -"
+
+tail $use_new_tail_syntax +###CPACK_HEADER_LENGTH### "$0" | gunzip | (cd "${toplevel}" && ${extractor}) || cpack_echo_exit "Problem unpacking the @CPACK_PACKAGE_FILE_NAME@"
+
+echo "Unpacking finished successfully"
+
+exit 0
+#-----------------------------------------------------------
+#      Start of TAR.GZ file
+#-----------------------------------------------------------;
diff --git a/Modules/CPack.VolumeIcon.icns.in b/Modules/Internal/CPack/CPack.VolumeIcon.icns.in
similarity index 100%
rename from Modules/CPack.VolumeIcon.icns.in
rename to Modules/Internal/CPack/CPack.VolumeIcon.icns.in
Binary files differ
diff --git a/Modules/CPack.background.png.in b/Modules/Internal/CPack/CPack.background.png.in
similarity index 100%
rename from Modules/CPack.background.png.in
rename to Modules/Internal/CPack/CPack.background.png.in
Binary files differ
diff --git a/Modules/CPack.distribution.dist.in b/Modules/Internal/CPack/CPack.distribution.dist.in
similarity index 100%
rename from Modules/CPack.distribution.dist.in
rename to Modules/Internal/CPack/CPack.distribution.dist.in
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 89dc6f0..ad8e078 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -45,7 +45,7 @@
       ERROR_QUIET
       OUTPUT_STRIP_TRAILING_WHITESPACE)
     if(result EQUAL 0)
-      string(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
+      string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
       set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE)
       set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE)
     else()
@@ -56,6 +56,67 @@
   endif()
 endfunction()
 
+function(cpack_deb_check_description SUMMARY LINES RESULT_VARIABLE)
+  set(_result TRUE)
+
+  # Get the summary line
+  if(NOT SUMMARY MATCHES "^[^\\s].*$")
+    set(_result FALSE)
+    set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE)
+    return()
+  endif()
+
+  foreach(_line IN LISTS LINES)
+    if(NOT _line MATCHES "^ +[^ ]+.*$")
+      set(_result FALSE)
+      break()
+    endif()
+  endforeach()
+
+  set(${RESULT_VARIABLE} ${_result} PARENT_SCOPE)
+endfunction()
+
+function(cpack_deb_format_package_description TEXT OUTPUT_VAR)
+  # Turn the possible multi-line string into a list
+  string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1)
+  string(REPLACE ";" "${uuid}" _text "${TEXT}")
+  string(REPLACE "\n" ";" _lines "${_text}")
+  list(POP_FRONT _lines _summary)
+
+  # Check if reformatting required
+  cpack_deb_check_description("${_summary}" "${_lines}" _result)
+  if(_result)
+    # Ok, no formatting required
+    set(${OUTPUT_VAR} "${TEXT}" PARENT_SCOPE)
+    return()
+  endif()
+
+  # Format the summary line
+  string(STRIP "${_summary}" _summary)
+
+  # Make sure the rest formatted properly
+  set(_result)
+  foreach(_line IN LISTS _lines)
+    string(STRIP "${_line}" _line_strip)
+    if(NOT _line_strip)
+      # Replace empty lines w/ a _single full stop character_
+      set(_line " .")
+    else()
+      # Prepend the normal lines w/ a single space.
+      # If the line already starts w/ at least one space,
+      # it'll become _verbatim_ (assuming it supposed to be
+      # verbatim in the original text).
+      string(PREPEND _line " ")
+    endif()
+    list(APPEND _result "${_line}")
+  endforeach()
+
+  list(PREPEND _result "${_summary}")
+  list(JOIN _result "\n" _result)
+  string(REPLACE "${uuid}"  ";" _result "${_result}")
+  set(${OUTPUT_VAR} "${_result}" PARENT_SCOPE)
+endfunction()
+
 function(cpack_deb_prepare_package_vars)
   # CPACK_DEBIAN_PACKAGE_SHLIBDEPS
   # If specify OFF, only user depends are used
@@ -102,7 +163,7 @@
         RESULT_VARIABLE FILE_RESULT_
         OUTPUT_VARIABLE INSTALL_FILE_)
       if(NOT FILE_RESULT_ EQUAL 0)
-        message (FATAL_ERROR "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with exit code: ${FILE_RESULT_}")
+        message(FATAL_ERROR "CPackDeb: execution of command: '${FILE_EXECUTABLE} ./${FILE_}' failed with exit code: ${FILE_RESULT_}")
       endif()
       list(APPEND CPACK_DEB_INSTALL_FILES "${INSTALL_FILE_}")
     endforeach()
@@ -210,7 +271,7 @@
       if(_TMP_VERSION MATCHES "dpkg-shlibdeps version ([0-9]+\\.[0-9]+\\.[0-9]+)")
         set(SHLIBDEPS_EXECUTABLE_VERSION "${CMAKE_MATCH_1}")
       else()
-        set(SHLIBDEPS_EXECUTABLE_VERSION "")
+        unset(SHLIBDEPS_EXECUTABLE_VERSION)
       endif()
 
       if(CPACK_DEBIAN_PACKAGE_DEBUG)
@@ -253,7 +314,7 @@
           message( "CPackDeb Debug: dpkg-shlibdeps warnings \n${SHLIBDEPS_ERROR}")
         endif()
         if(NOT SHLIBDEPS_RESULT EQUAL 0)
-          message (FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n"
+          message(FATAL_ERROR "CPackDeb: dpkg-shlibdeps: '${SHLIBDEPS_ERROR}';\n"
               "executed command: '${SHLIBDEPS_EXECUTABLE} ${IGNORE_MISSING_INFO_FLAG} -O ${CPACK_DEB_BINARY_FILES}';\n"
               "found files: '${INSTALL_FILE_}';\n"
               "files info: '${CPACK_DEB_INSTALL_FILES}';\n"
@@ -388,7 +449,7 @@
   # if per-component variable, overrides the global CPACK_DEBIAN_PACKAGE_${variable_type_}
   # automatic dependency discovery will be performed afterwards.
   if(CPACK_DEB_PACKAGE_COMPONENT)
-    foreach(value_type_ DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES SOURCE SECTION PRIORITY NAME)
+    foreach(value_type_ IN ITEMS DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES SOURCE SECTION PRIORITY NAME)
       set(_component_var "CPACK_DEBIAN_${_local_component_name}_PACKAGE_${value_type_}")
 
       # if set, overrides the global variable
@@ -402,21 +463,15 @@
     endforeach()
 
     if(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS)
-      set(COMPONENT_DEPENDS "")
-      foreach (_PACK ${CPACK_COMPONENT_${_local_component_name}_DEPENDS})
+      unset(COMPONENT_DEPENDS)
+      foreach(_PACK IN LISTS CPACK_COMPONENT_${_local_component_name}_DEPENDS)
         get_component_package_name(_PACK_NAME "${_PACK}")
-        if(COMPONENT_DEPENDS)
-          set(COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION}), ${COMPONENT_DEPENDS}")
-        else()
-          set(COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION})")
-        endif()
+        list(PREPEND COMPONENT_DEPENDS "${_PACK_NAME} (= ${CPACK_DEBIAN_PACKAGE_VERSION})")
       endforeach()
+      list(JOIN COMPONENT_DEPENDS ", " COMPONENT_DEPENDS)
       if(COMPONENT_DEPENDS)
-        if(CPACK_DEBIAN_PACKAGE_DEPENDS)
-          set(CPACK_DEBIAN_PACKAGE_DEPENDS "${COMPONENT_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}")
-        else()
-          set(CPACK_DEBIAN_PACKAGE_DEPENDS "${COMPONENT_DEPENDS}")
-        endif()
+        list(PREPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${COMPONENT_DEPENDS})
+        list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS)
       endif()
     endif()
   endif()
@@ -424,12 +479,9 @@
   # at this point, the CPACK_DEBIAN_PACKAGE_DEPENDS is properly set
   # to the minimal dependency of the package
   # Append automatically discovered dependencies .
-  if(NOT "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}" STREQUAL "")
-    if (CPACK_DEBIAN_PACKAGE_DEPENDS)
-      set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}")
-    else ()
-      set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}")
-    endif ()
+  if(CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS)
+    list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS})
+    list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS)
   endif()
 
   if(NOT CPACK_DEBIAN_PACKAGE_DEPENDS)
@@ -445,27 +497,54 @@
   endif()
 
   # Description: (mandatory)
-  if(NOT CPACK_DEB_PACKAGE_COMPONENT)
-    if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION)
-      if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY)
-        message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION")
-      endif()
-      set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
-    endif()
+  # Try package description first
+  if(CPACK_DEB_PACKAGE_COMPONENT)
+    cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION"
+      "CPACK_DEBIAN_${_local_component_name}_DESCRIPTION"
+      "CPACK_COMPONENT_${_local_component_name}_DESCRIPTION")
   else()
-    set(component_description_var CPACK_COMPONENT_${_local_component_name}_DESCRIPTION)
-
-    # component description overrides package description
-    if(${component_description_var})
-      set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${${component_description_var}})
-    elseif(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION)
-      if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY)
-        message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION or ${component_description_var}")
-      endif()
-      set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
-    endif()
+    cpack_deb_variable_fallback("CPACK_DEBIAN_PACKAGE_DESCRIPTION"
+      "CPACK_DEBIAN_PACKAGE_DESCRIPTION"
+      "CPACK_PACKAGE_DESCRIPTION")
   endif()
 
+  # Still no description? ... and description file has set ...
+  if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION AND CPACK_PACKAGE_DESCRIPTION_FILE)
+    # Read `CPACK_PACKAGE_DESCRIPTION_FILE` then...
+    file(READ ${CPACK_PACKAGE_DESCRIPTION_FILE} CPACK_DEBIAN_PACKAGE_DESCRIPTION)
+  endif()
+
+  # Still no description? #2
+  if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION)
+    # Try to get `CPACK_PACKAGE_DESCRIPTION_SUMMARY` as the last hope
+    if(CPACK_PACKAGE_DESCRIPTION_SUMMARY)
+      set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
+    else()
+      # Giving up! Report an error...
+      set(_description_failure_message
+        "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION")
+      if(CPACK_DEB_PACKAGE_COMPONENT)
+        string(APPEND _description_failure_message
+          " or CPACK_DEBIAN_${_local_component_name}_DESCRIPTION")
+      endif()
+      message(FATAL_ERROR _description_failure_message)
+    endif()
+
+  # Ok, description has set. According to the `Debian Policy Manual`_ the frist
+  # line is a pacakge summary.  Try to get it as well...
+  # See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description
+  elseif(CPACK_PACKAGE_DESCRIPTION_SUMMARY)
+    # Merge summary w/ the detailed description
+    string(PREPEND CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n")
+  endif()
+  # assert(CPACK_DEBIAN_PACKAGE_DESCRIPTION)
+
+  # Make sure description is properly formatted
+  cpack_deb_format_package_description(
+    "${CPACK_DEBIAN_PACKAGE_DESCRIPTION}"
+    CPACK_DEBIAN_PACKAGE_DESCRIPTION
+  )
+
   # Homepage: (optional)
   if(NOT CPACK_DEBIAN_PACKAGE_HOMEPAGE AND CMAKE_PROJECT_HOMEPAGE_URL)
     set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "${CMAKE_PROJECT_HOMEPAGE_URL}")
@@ -519,7 +598,7 @@
   # Are we packaging components ?
   if(CPACK_DEB_PACKAGE_COMPONENT)
     # override values with per component version if set
-    foreach(VAR_NAME_ "PACKAGE_CONTROL_EXTRA" "PACKAGE_CONTROL_STRICT_PERMISSION")
+    foreach(VAR_NAME_ IN ITEMS PACKAGE_CONTROL_EXTRA PACKAGE_CONTROL_STRICT_PERMISSION)
       if(CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_})
         set(CPACK_DEBIAN_${VAR_NAME_} "${CPACK_DEBIAN_${_local_component_name}_${VAR_NAME_}}")
       endif()
@@ -527,12 +606,12 @@
     get_component_package_name(CPACK_DEBIAN_PACKAGE_NAME ${_local_component_name})
   endif()
 
-  set(CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "")
-
-  if (NOT CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY)
+  if(NOT CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY)
     set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY "=")
   endif()
 
+  unset(CPACK_DEBIAN_PACKAGE_SHLIBS_LIST)
+
   if(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS)
     if(READELF_EXECUTABLE)
       foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES)
@@ -544,9 +623,7 @@
           message(AUTHOR_WARNING "Shared library '${_FILE}' is missing soname or soversion. Library will not be added to DEBIAN/shlibs control file.")
         endif()
       endforeach()
-      if (CPACK_DEBIAN_PACKAGE_SHLIBS_LIST)
-        string(REPLACE ";" "\n" CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "${CPACK_DEBIAN_PACKAGE_SHLIBS_LIST}")
-      endif()
+      list(JOIN CPACK_DEBIAN_PACKAGE_SHLIBS_LIST "\n" CPACK_DEBIAN_PACKAGE_SHLIBS_LIST)
     else()
       message(FATAL_ERROR "Readelf utility is not available. CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS option is not available.")
     endif()
@@ -554,7 +631,7 @@
 
   # add ldconfig call in default postrm and postint
   set(CPACK_ADD_LDCONFIG_CALL 0)
-  foreach(_FILE ${CPACK_DEB_SHARED_OBJECT_FILES})
+  foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES)
     get_filename_component(_DIR ${_FILE} DIRECTORY)
     # all files in CPACK_DEB_SHARED_OBJECT_FILES have dot at the beginning
     if(_DIR STREQUAL "./lib" OR _DIR STREQUAL "./usr/lib")
@@ -565,12 +642,12 @@
   if(CPACK_ADD_LDCONFIG_CALL)
     set(CPACK_DEBIAN_GENERATE_POSTINST 1)
     set(CPACK_DEBIAN_GENERATE_POSTRM 1)
-    foreach(f ${PACKAGE_CONTROL_EXTRA})
+    foreach(f IN LISTS PACKAGE_CONTROL_EXTRA)
       get_filename_component(n "${f}" NAME)
-      if("${n}" STREQUAL "postinst")
+      if(n STREQUAL "postinst")
         set(CPACK_DEBIAN_GENERATE_POSTINST 0)
       endif()
-      if("${n}" STREQUAL "postrm")
+      if(n STREQUAL "postrm")
         set(CPACK_DEBIAN_GENERATE_POSTRM 0)
       endif()
     endforeach()
@@ -671,7 +748,7 @@
   if(BUILD_IDS)
     set(GEN_DBGSYMDIR "${DBGSYMDIR}" PARENT_SCOPE)
     set(GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_DBGSYM_OUTPUT_FILE_NAME}" PARENT_SCOPE)
-    string(REPLACE ";" " " BUILD_IDS "${BUILD_IDS}")
+    list(JOIN BUILD_IDS " " BUILD_IDS)
     set(GEN_BUILD_IDS "${BUILD_IDS}" PARENT_SCOPE)
   endif()
 endfunction()
diff --git a/Modules/Internal/CPack/CPackNuGet.cmake b/Modules/Internal/CPack/CPackNuGet.cmake
index 4b2ce92..82053b2 100644
--- a/Modules/Internal/CPack/CPackNuGet.cmake
+++ b/Modules/Internal/CPack/CPackNuGet.cmake
@@ -262,7 +262,7 @@
     # NuGet will name it properly.
     _cpack_nuget_debug("Rendering `${CPACK_TEMPORARY_DIRECTORY}/CPack.NuGet.nuspec` file...")
     configure_file(
-        "${CMAKE_ROOT}/Modules/CPack.NuGet.nuspec.in"
+        "${CMAKE_ROOT}/Modules/Internal/CPack/CPack.NuGet.nuspec.in"
         "${CPACK_TEMPORARY_DIRECTORY}/CPack.NuGet.nuspec"
         @ONLY
       )
diff --git a/Modules/NSIS.InstallOptions.ini.in b/Modules/Internal/CPack/NSIS.InstallOptions.ini.in
similarity index 100%
rename from Modules/NSIS.InstallOptions.ini.in
rename to Modules/Internal/CPack/NSIS.InstallOptions.ini.in
diff --git a/Modules/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in
similarity index 100%
rename from Modules/NSIS.template.in
rename to Modules/Internal/CPack/NSIS.template.in
diff --git a/Modules/WIX.template.in b/Modules/Internal/CPack/WIX.template.in
similarity index 100%
rename from Modules/WIX.template.in
rename to Modules/Internal/CPack/WIX.template.in
diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake
index 0abbb61..61d213a 100644
--- a/Modules/Platform/AIX-GNU.cmake
+++ b/Modules/Platform/AIX-GNU.cmake
@@ -8,23 +8,26 @@
 endif()
 set(__AIX_COMPILER_GNU 1)
 
-#
-# By default, runtime linking is enabled. All shared objects specified on the command line
-# will be listed, even if there are no symbols referenced, in the output file.
-string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " -Wl,-brtl")
-string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " -Wl,-brtl")
-string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " -Wl,-brtl")
-
-
 macro(__aix_compiler_gnu lang)
   set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-blibpath:")
   set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":")
-  string(APPEND CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS " -Wl,-G,-bnoipath")
-  set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall")
+  string(APPEND CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS " -Wl,-bnoipath")
+  set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall") # CMP0065 old behavior
   set(CMAKE_${lang}_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH 1)
 
   set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath")
   if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1)
     unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY)
   endif()
+
+  # Construct the export list ourselves to pass only the object files so
+  # that we export only the symbols actually provided by the sources.
+  set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/objects.exp <OBJECTS>"
+    "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/objects.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
+    )
+
+  set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <OBJECTS>"
+    "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 endmacro()
diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake
index 06a806b..64b0bc1 100644
--- a/Modules/Platform/AIX-XL.cmake
+++ b/Modules/Platform/AIX-XL.cmake
@@ -8,43 +8,24 @@
 endif()
 set(__AIX_COMPILER_XL 1)
 
-#
-# By default, runtime linking is enabled. All shared objects specified on the command line
-# will be listed, even if there are no symbols referenced, in the output file.
-string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " -Wl,-brtl")
-string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " -Wl,-brtl")
-string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " -Wl,-brtl")
-
-
 macro(__aix_compiler_xl lang)
   set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-blibpath:")
   set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":")
-  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-G -Wl,-bnoipath")  # -shared
-  set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall")
+  string(APPEND CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS " -Wl,-bnoipath")
+  set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall") # CMP0065 old behavior
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS " ")
   set(CMAKE_SHARED_MODULE_${lang}_FLAGS  " ")
 
   set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath")
 
-  # Find the CreateExportList program that comes with this toolchain.
-  find_program(CMAKE_XL_CreateExportList
-    NAMES CreateExportList
-    DOC "IBM XL CreateExportList tool"
+  # Construct the export list ourselves to pass only the object files so
+  # that we export only the symbols actually provided by the sources.
+  set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/objects.exp <OBJECTS>"
+    "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/objects.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
     )
 
-  # CMAKE_XL_CreateExportList is part of the AIX XL compilers but not the linux ones.
-  # If we found the tool, we'll use it to create exports, otherwise stick with the regular
-  # create shared library compile line.
-  if (CMAKE_XL_CreateExportList)
-    # The compiler front-end passes all object files, archive files, and shared
-    # library files named on the command line to CreateExportList to create a
-    # list of all symbols to be exported from the shared library.  This causes
-    # all archive members to be copied into the shared library whether they are
-    # needed or not.  Instead we run the tool ourselves to pass only the object
-    # files so that we export only the symbols actually provided by the sources.
-    set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-      "${CMAKE_XL_CreateExportList} <OBJECT_DIR>/objects.exp <OBJECTS>"
-      "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/objects.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
-      )
-  endif()
+  set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <OBJECTS>"
+    "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 endmacro()
diff --git a/Modules/Platform/AIX.cmake b/Modules/Platform/AIX.cmake
index 551a995..03cef51 100644
--- a/Modules/Platform/AIX.cmake
+++ b/Modules/Platform/AIX.cmake
@@ -1,5 +1,7 @@
 set(CMAKE_SHARED_LIBRARY_PREFIX "lib")          # lib
 set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")          # .so
+set(CMAKE_AIX_IMPORT_FILE_PREFIX "")
+set(CMAKE_AIX_IMPORT_FILE_SUFFIX ".imp")
 set(CMAKE_DL_LIBS "-lld")
 
 # RPATH support on AIX is called libpath.  By default the runtime
diff --git a/Modules/Platform/AIX/ExportImportList b/Modules/Platform/AIX/ExportImportList
new file mode 100755
index 0000000..4f67ef5
--- /dev/null
+++ b/Modules/Platform/AIX/ExportImportList
@@ -0,0 +1,53 @@
+#!/bin/sh
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# This script is internal to CMake and meant only to be
+# invoked by CMake-generated build systems on AIX.
+
+usage='usage: ExportImportList -o <out-file> [-l <lib>] [--] <objects>...'
+
+die() {
+    echo "$@" 1>&2; exit 1
+}
+
+# Process command-line arguments.
+out=''
+lib=''
+while test "$#" != 0; do
+    case "$1" in
+    -l) shift; lib="$1" ;;
+    -o) shift; out="$1" ;;
+    --) shift; break ;;
+    -*) die "$usage" ;;
+    *)  break ;;
+    esac
+    shift
+done
+test -n "$out" || die "$usage"
+
+# Collect symbols exported from all object files.
+out_tmp="$out.tmp$$"
+trap 'rm -f "$out_tmp"' EXIT INT TERM
+for f in "$@"; do
+    dump -tov -X 32_64 "$f" |
+    awk '
+        BEGIN {
+            V["EXPORTED"]=" export"
+            V["PROTECTED"]=" protected"
+        }
+        /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / {
+            if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) {
+                print $NF V[$(NF-1)]
+            }
+        }
+    '
+done > "$out_tmp"
+
+# Generate the export/import file.
+{
+    if test -n "$lib"; then
+        echo "#! $lib"
+    fi
+    sort -u "$out_tmp"
+} > "$out"
diff --git a/Modules/Platform/Android-Clang.cmake b/Modules/Platform/Android-Clang.cmake
index 9ed1e01..847178f 100644
--- a/Modules/Platform/Android-Clang.cmake
+++ b/Modules/Platform/Android-Clang.cmake
@@ -40,6 +40,9 @@
   endif()
   if(NOT CMAKE_${lang}_COMPILER_TARGET)
     set(CMAKE_${lang}_COMPILER_TARGET "${_ANDROID_ABI_CLANG_TARGET}")
+    if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
+      string(APPEND CMAKE_${lang}_COMPILER_TARGET "${CMAKE_SYSTEM_VERSION}")
+    endif()
     list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "--target=${CMAKE_${lang}_COMPILER_TARGET}")
   endif()
 endmacro()
diff --git a/Modules/Platform/Android-Common.cmake b/Modules/Platform/Android-Common.cmake
index f8b9346..1affcd0 100644
--- a/Modules/Platform/Android-Common.cmake
+++ b/Modules/Platform/Android-Common.cmake
@@ -47,7 +47,41 @@
 endif()
 
 if(CMAKE_ANDROID_STL_TYPE)
-  if(CMAKE_ANDROID_NDK)
+  if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
+    if(CMAKE_ANDROID_STL_TYPE STREQUAL "system")
+      set(_ANDROID_STL_EXCEPTIONS 0)
+      set(_ANDROID_STL_RTTI 0)
+      macro(__android_stl lang)
+        string(APPEND CMAKE_${lang}_FLAGS_INIT " -stdlib=libstdc++")
+      endmacro()
+    elseif(CMAKE_ANDROID_STL_TYPE STREQUAL "c++_static")
+      set(_ANDROID_STL_EXCEPTIONS 1)
+      set(_ANDROID_STL_RTTI 1)
+      macro(__android_stl lang)
+        string(APPEND CMAKE_${lang}_FLAGS_INIT " -stdlib=libc++")
+        string(APPEND CMAKE_${lang}_STANDARD_LIBRARIES " -static-libstdc++")
+      endmacro()
+    elseif(CMAKE_ANDROID_STL_TYPE STREQUAL "c++_shared")
+      set(_ANDROID_STL_EXCEPTIONS 1)
+      set(_ANDROID_STL_RTTI 1)
+      macro(__android_stl lang)
+        string(APPEND CMAKE_${lang}_FLAGS_INIT " -stdlib=libc++")
+      endmacro()
+    elseif(CMAKE_ANDROID_STL_TYPE STREQUAL "none")
+      set(_ANDROID_STL_RTTI 0)
+      set(_ANDROID_STL_EXCEPTIONS 0)
+      macro(__android_stl lang)
+        # FIXME: Add a way to add project-wide language-specific compile-only flags.
+        set(CMAKE_CXX_COMPILE_OBJECT
+          "<CMAKE_CXX_COMPILER>  <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE> -nostdinc++")
+        string(APPEND CMAKE_${lang}_STANDARD_LIBRARIES " -nostdlib++")
+      endmacro()
+    else()
+      message(FATAL_ERROR
+        "Android: STL '${CMAKE_ANDROID_STL_TYPE}' not supported by this NDK."
+        )
+    endif()
+  elseif(CMAKE_ANDROID_NDK)
 
     macro(__android_stl_inc lang dir req)
       if(EXISTS "${dir}")
@@ -152,6 +186,10 @@
     __android_stl(CXX)
   endif()
 
+  if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
+    string(APPEND CMAKE_${lang}_STANDARD_LIBRARIES " -latomic -lm")
+  endif()
+
   # <ndk>/build/core/definitions.mk appends the sysroot's include directory
   # explicitly at the end of the command-line include path so that it
   # precedes the toolchain's builtin include directories.  This is
@@ -161,7 +199,7 @@
   #
   # Do not do this for a standalone toolchain because it is already
   # tied to a specific API version.
-  if(CMAKE_ANDROID_NDK)
+  if(CMAKE_ANDROID_NDK AND NOT CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
     if(CMAKE_SYSROOT_COMPILE)
       set(_cmake_sysroot_compile "${CMAKE_SYSROOT_COMPILE}")
     else()
@@ -170,7 +208,7 @@
     if(NOT CMAKE_ANDROID_NDK_DEPRECATED_HEADERS)
       list(APPEND CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES
         "${_cmake_sysroot_compile}/usr/include"
-        "${_cmake_sysroot_compile}/usr/include/${CMAKE_ANDROID_ARCH_HEADER_TRIPLE}"
+        "${_cmake_sysroot_compile}/usr/include/${CMAKE_ANDROID_ARCH_TRIPLE}"
         )
     else()
       list(APPEND CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES "${_cmake_sysroot_compile}/usr/include")
diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake
index bb42eed..e7c1b48 100644
--- a/Modules/Platform/Android-Determine.cmake
+++ b/Modules/Platform/Android-Determine.cmake
@@ -198,32 +198,66 @@
   message(FATAL_ERROR "Android: The API specified by CMAKE_SYSTEM_VERSION='${CMAKE_SYSTEM_VERSION}' is not an integer.")
 endif()
 
+if(CMAKE_ANDROID_NDK)
+  # Identify the host platform.
+  if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+    if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "darwin-x86_64")
+    else()
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "darwin-x86")
+    endif()
+  elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
+    if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "linux-x86_64")
+    else()
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "linux-x86")
+    endif()
+  elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+    if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "windows-x86_64")
+    else()
+      set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "windows")
+    endif()
+  else()
+    message(FATAL_ERROR "Android: Builds hosted on '${CMAKE_HOST_SYSTEM_NAME}' not supported.")
+  endif()
+
+  # Look for a unified toolchain/sysroot provided with the NDK.
+  set(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED "${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}")
+  if(NOT IS_DIRECTORY "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/sysroot")
+    set(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED "")
+  endif()
+else()
+  set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "")
+  set(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED "")
+endif()
+
 # https://developer.android.com/ndk/guides/abis.html
 
 set(_ANDROID_ABI_arm64-v8a_PROC     "aarch64")
 set(_ANDROID_ABI_arm64-v8a_ARCH     "arm64")
-set(_ANDROID_ABI_arm64-v8a_HEADER   "aarch64-linux-android")
+set(_ANDROID_ABI_arm64-v8a_TRIPLE   "aarch64-linux-android")
 set(_ANDROID_ABI_armeabi-v7a_PROC   "armv7-a")
 set(_ANDROID_ABI_armeabi-v7a_ARCH   "arm")
-set(_ANDROID_ABI_armeabi-v7a_HEADER "arm-linux-androideabi")
+set(_ANDROID_ABI_armeabi-v7a_TRIPLE "arm-linux-androideabi")
 set(_ANDROID_ABI_armeabi-v6_PROC    "armv6")
 set(_ANDROID_ABI_armeabi-v6_ARCH    "arm")
-set(_ANDROID_ABI_armeabi-v6_HEADER  "arm-linux-androideabi")
+set(_ANDROID_ABI_armeabi-v6_TRIPLE  "arm-linux-androideabi")
 set(_ANDROID_ABI_armeabi_PROC       "armv5te")
 set(_ANDROID_ABI_armeabi_ARCH       "arm")
-set(_ANDROID_ABI_armeabi_HEADER     "arm-linux-androideabi")
+set(_ANDROID_ABI_armeabi_TRIPLE     "arm-linux-androideabi")
 set(_ANDROID_ABI_mips_PROC          "mips")
 set(_ANDROID_ABI_mips_ARCH          "mips")
-set(_ANDROID_ABI_mips_HEADER        "mipsel-linux-android")
+set(_ANDROID_ABI_mips_TRIPLE        "mipsel-linux-android")
 set(_ANDROID_ABI_mips64_PROC        "mips64")
 set(_ANDROID_ABI_mips64_ARCH        "mips64")
-set(_ANDROID_ABI_mips64_HEADER      "mips64el-linux-android")
+set(_ANDROID_ABI_mips64_TRIPLE      "mips64el-linux-android")
 set(_ANDROID_ABI_x86_PROC           "i686")
 set(_ANDROID_ABI_x86_ARCH           "x86")
-set(_ANDROID_ABI_x86_HEADER         "i686-linux-android")
+set(_ANDROID_ABI_x86_TRIPLE         "i686-linux-android")
 set(_ANDROID_ABI_x86_64_PROC        "x86_64")
 set(_ANDROID_ABI_x86_64_ARCH        "x86_64")
-set(_ANDROID_ABI_x86_64_HEADER      "x86_64-linux-android")
+set(_ANDROID_ABI_x86_64_TRIPLE      "x86_64-linux-android")
 
 set(_ANDROID_PROC_aarch64_ARCH_ABI "arm64-v8a")
 set(_ANDROID_PROC_armv7-a_ARCH_ABI "armeabi-v7a")
@@ -308,7 +342,7 @@
     "does not match architecture '${CMAKE_ANDROID_ARCH}' for the ABI '${CMAKE_ANDROID_ARCH_ABI}'."
     )
 endif()
-set(CMAKE_ANDROID_ARCH_HEADER_TRIPLE "${_ANDROID_ABI_${CMAKE_ANDROID_ARCH_ABI}_HEADER}")
+set(CMAKE_ANDROID_ARCH_TRIPLE "${_ANDROID_ABI_${CMAKE_ANDROID_ARCH_ABI}_TRIPLE}")
 
 # Select a processor.
 if(NOT CMAKE_SYSTEM_PROCESSOR)
@@ -321,7 +355,7 @@
 endif()
 
 if(CMAKE_ANDROID_NDK AND NOT DEFINED CMAKE_ANDROID_NDK_DEPRECATED_HEADERS)
-  if(IS_DIRECTORY "${CMAKE_ANDROID_NDK}/sysroot/usr/include/${CMAKE_ANDROID_ARCH_HEADER_TRIPLE}")
+  if(IS_DIRECTORY "${CMAKE_ANDROID_NDK}/sysroot/usr/include/${CMAKE_ANDROID_ARCH_TRIPLE}")
     # Unified headers exist so we use them by default.
     set(CMAKE_ANDROID_NDK_DEPRECATED_HEADERS 0)
   else()
@@ -340,8 +374,10 @@
 
 if(CMAKE_ANDROID_NDK)
   string(APPEND CMAKE_SYSTEM_CUSTOM_CODE
-    "set(CMAKE_ANDROID_ARCH_HEADER_TRIPLE \"${CMAKE_ANDROID_ARCH_HEADER_TRIPLE}\")\n"
+    "set(CMAKE_ANDROID_ARCH_TRIPLE \"${CMAKE_ANDROID_ARCH_TRIPLE}\")\n"
     "set(CMAKE_ANDROID_NDK_DEPRECATED_HEADERS \"${CMAKE_ANDROID_NDK_DEPRECATED_HEADERS}\")\n"
+    "set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG \"${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}\")\n"
+    "set(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED \"${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}\")\n"
     )
 endif()
 
diff --git a/Modules/Platform/Android-Initialize.cmake b/Modules/Platform/Android-Initialize.cmake
index a434f90..a5d2820 100644
--- a/Modules/Platform/Android-Initialize.cmake
+++ b/Modules/Platform/Android-Initialize.cmake
@@ -17,6 +17,13 @@
   return()
 endif()
 
+set(CMAKE_BUILD_TYPE_INIT Debug)
+
+# Skip sysroot selection if the NDK has a unified toolchain.
+if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
+  return()
+endif()
+
 if(NOT CMAKE_SYSROOT)
   if(CMAKE_ANDROID_NDK)
     set(CMAKE_SYSROOT "${CMAKE_ANDROID_NDK}/platforms/android-${CMAKE_SYSTEM_VERSION}/arch-${CMAKE_ANDROID_ARCH}")
@@ -40,5 +47,3 @@
     "Android: No CMAKE_SYSROOT was selected."
     )
 endif()
-
-set(CMAKE_BUILD_TYPE_INIT Debug)
diff --git a/Modules/Platform/Android/Determine-Compiler-NDK.cmake b/Modules/Platform/Android/Determine-Compiler-NDK.cmake
index 5f2cc52..e009c10 100644
--- a/Modules/Platform/Android/Determine-Compiler-NDK.cmake
+++ b/Modules/Platform/Android/Determine-Compiler-NDK.cmake
@@ -1,6 +1,31 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
+# In Android NDK r19 and above there is a single clang toolchain.
+if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
+  if(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION AND NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION STREQUAL "clang")
+    message(FATAL_ERROR
+      "Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value '${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}' "
+      "is not supported by this NDK.  It must be 'clang' or not set at all."
+      )
+  endif()
+  message(STATUS "Android: Selected unified Clang toolchain")
+  set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "clang")
+  set(_ANDROID_TOOL_C_COMPILER "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/clang${_ANDROID_HOST_EXT}")
+  set(_ANDROID_TOOL_C_TOOLCHAIN_MACHINE "${CMAKE_ANDROID_ARCH_TRIPLE}")
+  set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "")
+  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN "")
+  set(_ANDROID_TOOL_C_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/${CMAKE_ANDROID_ARCH_TRIPLE}-")
+  set(_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
+  set(_ANDROID_TOOL_CXX_COMPILER "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/clang++${_ANDROID_HOST_EXT}")
+  set(_ANDROID_TOOL_CXX_TOOLCHAIN_MACHINE "${CMAKE_ANDROID_ARCH_TRIPLE}")
+  set(_ANDROID_TOOL_CXX_TOOLCHAIN_VERSION "")
+  set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "")
+  set(_ANDROID_TOOL_CXX_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/${CMAKE_ANDROID_ARCH_TRIPLE}-")
+  set(_ANDROID_TOOL_CXX_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
+  return()
+endif()
+
 # In Android NDK releases there is build system toolchain selection logic in
 # these files:
 #
@@ -195,40 +220,16 @@
   set(_ANDROID_TOOL_PREFIX "${CMAKE_MATCH_1}")
 endif()
 
-# Identify the host platform.
-if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
-  if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
-    set(_ANDROID_HOST_DIR "darwin-x86_64")
-  else()
-    set(_ANDROID_HOST_DIR "darwin-x86")
-  endif()
-elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
-  if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
-    set(_ANDROID_HOST_DIR "linux-x86_64")
-  else()
-    set(_ANDROID_HOST_DIR "linux-x86")
-  endif()
-elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
-  if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
-    set(_ANDROID_HOST_DIR "windows-x86_64")
-  else()
-    set(_ANDROID_HOST_DIR "windows")
-  endif()
-else()
-  message(FATAL_ERROR "Android: Builds hosted on '${CMAKE_HOST_SYSTEM_NAME}' not supported.")
-endif()
-
 # Help CMakeFindBinUtils locate things.
 set(_CMAKE_TOOLCHAIN_PREFIX "${_ANDROID_TOOL_PREFIX}")
 
-set(_ANDROID_TOOL_NDK_TOOLCHAIN_HOST_TAG "${_ANDROID_HOST_DIR}")
 set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "${_ANDROID_TOOL_VERS_NDK}")
 
 # _ANDROID_TOOL_PREFIX should now match `gcc -dumpmachine`.
 string(REGEX REPLACE "-$" "" _ANDROID_TOOL_C_TOOLCHAIN_MACHINE "${_ANDROID_TOOL_PREFIX}")
 
 set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "${_ANDROID_TOOL_VERS}")
-set(_ANDROID_TOOL_C_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${_ANDROID_HOST_DIR}/bin/${_ANDROID_TOOL_PREFIX}")
+set(_ANDROID_TOOL_C_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/${_ANDROID_TOOL_PREFIX}")
 set(_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
 
 set(_ANDROID_TOOL_CXX_TOOLCHAIN_MACHINE "${_ANDROID_TOOL_C_TOOLCHAIN_MACHINE}")
@@ -238,9 +239,9 @@
 
 if(_ANDROID_TOOL_CLANG_NAME)
   message(STATUS "Android: Selected Clang toolchain '${_ANDROID_TOOL_CLANG_NAME}' with GCC toolchain '${_ANDROID_TOOL_NAME}'")
-  set(_ANDROID_TOOL_C_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${_ANDROID_HOST_DIR}/bin/clang${_ANDROID_HOST_EXT}")
-  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN ${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${_ANDROID_HOST_DIR})
-  set(_ANDROID_TOOL_CXX_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${_ANDROID_HOST_DIR}/bin/clang++${_ANDROID_HOST_EXT}")
+  set(_ANDROID_TOOL_C_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/clang${_ANDROID_HOST_EXT}")
+  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN ${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG})
+  set(_ANDROID_TOOL_CXX_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/clang++${_ANDROID_HOST_EXT}")
   set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN}")
 else()
   message(STATUS "Android: Selected GCC toolchain '${_ANDROID_TOOL_NAME}'")
@@ -267,4 +268,3 @@
 unset(_ANDROID_TOOL_CLANG_NAME)
 unset(_ANDROID_TOOL_CLANG_VERS)
 unset(_ANDROID_TOOL_LLVM_NAME)
-unset(_ANDROID_HOST_DIR)
diff --git a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
index 4c1ac1f..5095aff 100644
--- a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
+++ b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
@@ -62,5 +62,4 @@
   set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "")
 endif()
 
-set(_ANDROID_TOOL_NDK_TOOLCHAIN_HOST_TAG "")
 set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "")
diff --git a/Modules/Platform/Android/Determine-Compiler.cmake b/Modules/Platform/Android/Determine-Compiler.cmake
index a03ebcc..5c6b97b 100644
--- a/Modules/Platform/Android/Determine-Compiler.cmake
+++ b/Modules/Platform/Android/Determine-Compiler.cmake
@@ -40,7 +40,6 @@
 elseif(CMAKE_ANDROID_STANDALONE_TOOLCHAIN)
   include(Platform/Android/Determine-Compiler-Standalone)
 else()
-  set(_ANDROID_TOOL_NDK_TOOLCHAIN_HOST_TAG "")
   set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "")
   set(_ANDROID_TOOL_C_COMPILER "")
   set(_ANDROID_TOOL_C_TOOLCHAIN_MACHINE "")
@@ -65,7 +64,6 @@
 
     # Save the Android-specific information in CMake${lang}Compiler.cmake.
     set(CMAKE_${lang}_COMPILER_CUSTOM_CODE "
-set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG \"${_ANDROID_TOOL_NDK_TOOLCHAIN_HOST_TAG}\")
 set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION \"${_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION}\")
 set(CMAKE_${lang}_ANDROID_TOOLCHAIN_MACHINE \"${_ANDROID_TOOL_${lang}_TOOLCHAIN_MACHINE}\")
 set(CMAKE_${lang}_ANDROID_TOOLCHAIN_VERSION \"${_ANDROID_TOOL_${lang}_TOOLCHAIN_VERSION}\")
diff --git a/Modules/Platform/Android/abi-common.cmake b/Modules/Platform/Android/abi-common.cmake
index 6bce3c7..b01ef61 100644
--- a/Modules/Platform/Android/abi-common.cmake
+++ b/Modules/Platform/Android/abi-common.cmake
@@ -3,7 +3,7 @@
   " -no-canonical-prefixes"
   )
 
-if(CMAKE_ANDROID_NDK AND NOT CMAKE_ANDROID_NDK_DEPRECATED_HEADERS)
+if(CMAKE_ANDROID_NDK AND NOT CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED AND NOT CMAKE_ANDROID_NDK_DEPRECATED_HEADERS)
   string(APPEND _ANDROID_ABI_INIT_CFLAGS " -D__ANDROID_API__=${CMAKE_SYSTEM_VERSION}")
 endif()
 
diff --git a/Modules/Platform/Apple-AppleClang-OBJC.cmake b/Modules/Platform/Apple-AppleClang-OBJC.cmake
new file mode 100644
index 0000000..b78edb1
--- /dev/null
+++ b/Modules/Platform/Apple-AppleClang-OBJC.cmake
@@ -0,0 +1,6 @@
+include(Platform/Apple-Clang-OBJC)
+if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 4.2)
+  set(CMAKE_OBJC_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ")
+else()
+  unset(CMAKE_OBJC_SYSTEM_FRAMEWORK_SEARCH_FLAG)
+endif()
diff --git a/Modules/Platform/Apple-AppleClang-OBJCXX.cmake b/Modules/Platform/Apple-AppleClang-OBJCXX.cmake
new file mode 100644
index 0000000..ed172f1
--- /dev/null
+++ b/Modules/Platform/Apple-AppleClang-OBJCXX.cmake
@@ -0,0 +1,6 @@
+include(Platform/Apple-Clang-OBJCXX)
+if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 4.2)
+  set(CMAKE_OBJCXX_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ")
+else()
+  unset(CMAKE_OBJCXX_SYSTEM_FRAMEWORK_SEARCH_FLAG)
+endif()
diff --git a/Modules/Platform/Apple-Clang-ASM.cmake b/Modules/Platform/Apple-Clang-ASM.cmake
new file mode 100644
index 0000000..935cce9
--- /dev/null
+++ b/Modules/Platform/Apple-Clang-ASM.cmake
@@ -0,0 +1,2 @@
+include(Platform/Apple-Clang)
+__apple_compiler_clang(ASM)
diff --git a/Modules/Platform/Apple-Clang-OBJC.cmake b/Modules/Platform/Apple-Clang-OBJC.cmake
new file mode 100644
index 0000000..63cd846
--- /dev/null
+++ b/Modules/Platform/Apple-Clang-OBJC.cmake
@@ -0,0 +1,2 @@
+include(Platform/Apple-Clang)
+__apple_compiler_clang(OBJC)
diff --git a/Modules/Platform/Apple-Clang-OBJCXX.cmake b/Modules/Platform/Apple-Clang-OBJCXX.cmake
new file mode 100644
index 0000000..28fc352
--- /dev/null
+++ b/Modules/Platform/Apple-Clang-OBJCXX.cmake
@@ -0,0 +1,2 @@
+include(Platform/Apple-Clang)
+__apple_compiler_clang(OBJCXX)
diff --git a/Modules/Platform/Apple-GNU-OBJC.cmake b/Modules/Platform/Apple-GNU-OBJC.cmake
new file mode 100644
index 0000000..aa8b33f
--- /dev/null
+++ b/Modules/Platform/Apple-GNU-OBJC.cmake
@@ -0,0 +1,4 @@
+include(Platform/Apple-GNU)
+__apple_compiler_gnu(OBJC)
+cmake_gnu_set_sysroot_flag(OBJC)
+cmake_gnu_set_osx_deployment_target_flag(OBJC)
diff --git a/Modules/Platform/Apple-GNU-OBJCXX.cmake b/Modules/Platform/Apple-GNU-OBJCXX.cmake
new file mode 100644
index 0000000..919e11d
--- /dev/null
+++ b/Modules/Platform/Apple-GNU-OBJCXX.cmake
@@ -0,0 +1,4 @@
+include(Platform/Apple-GNU)
+__apple_compiler_gnu(OBJCXX)
+cmake_gnu_set_sysroot_flag(OBJCXX)
+cmake_gnu_set_osx_deployment_target_flag(OBJCXX)
diff --git a/Modules/Platform/Apple-XL-C.cmake b/Modules/Platform/Apple-XL-C.cmake
index 2aeb132..e4fc3dd 100644
--- a/Modules/Platform/Apple-XL-C.cmake
+++ b/Modules/Platform/Apple-XL-C.cmake
@@ -1,4 +1,3 @@
-set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-qmkshrobj")
 set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle")
 
 # Enable shared library versioning.
diff --git a/Modules/Platform/Apple-XL-CXX.cmake b/Modules/Platform/Apple-XL-CXX.cmake
index f8e1906..ea330c8 100644
--- a/Modules/Platform/Apple-XL-CXX.cmake
+++ b/Modules/Platform/Apple-XL-CXX.cmake
@@ -1,4 +1,3 @@
-set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-qmkshrobj")
 set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS "-bundle")
 
 # Enable shared library versioning.
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index 7e02814..d0bb359 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -119,10 +119,18 @@
 set(CMAKE_CXX_CREATE_MACOSX_FRAMEWORK
       "<CMAKE_CXX_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
 
+set(CMAKE_OBJC_CREATE_MACOSX_FRAMEWORK
+      "<CMAKE_OBJC_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
+
+set(CMAKE_OBJCXX_CREATE_MACOSX_FRAMEWORK
+      "<CMAKE_OBJCXX_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS> <LINK_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
+
 # Set default framework search path flag for languages known to use a
 # preprocessor that may find headers in frameworks.
 set(CMAKE_C_FRAMEWORK_SEARCH_FLAG -F)
 set(CMAKE_CXX_FRAMEWORK_SEARCH_FLAG -F)
+set(CMAKE_OBJC_FRAMEWORK_SEARCH_FLAG -F)
+set(CMAKE_OBJCXX_FRAMEWORK_SEARCH_FLAG -F)
 set(CMAKE_Fortran_FRAMEWORK_SEARCH_FLAG -F)
 
 # default to searching for frameworks first
@@ -222,7 +230,7 @@
 include(Platform/UnixPaths)
 if(_CMAKE_OSX_SYSROOT_PATH AND EXISTS ${_CMAKE_OSX_SYSROOT_PATH}/usr/include)
   list(APPEND CMAKE_SYSTEM_PREFIX_PATH ${_CMAKE_OSX_SYSROOT_PATH}/usr)
-  foreach(lang C CXX)
+  foreach(lang C CXX OBJC OBJCXX)
     list(APPEND _CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES_INIT ${_CMAKE_OSX_SYSROOT_PATH}/usr/include)
   endforeach()
 endif()
diff --git a/Modules/Platform/Linux-XL-C.cmake b/Modules/Platform/Linux-XL-C.cmake
index d595e44..ef0c52b 100644
--- a/Modules/Platform/Linux-XL-C.cmake
+++ b/Modules/Platform/Linux-XL-C.cmake
@@ -1,2 +1 @@
-set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-qmkshrobj")
 set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,-export-dynamic")
diff --git a/Modules/Platform/Linux-XL-CXX.cmake b/Modules/Platform/Linux-XL-CXX.cmake
index 5ceb255..aa57d6e 100644
--- a/Modules/Platform/Linux-XL-CXX.cmake
+++ b/Modules/Platform/Linux-XL-CXX.cmake
@@ -1,2 +1 @@
-set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-qmkshrobj")
 set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-Wl,-export-dynamic")
diff --git a/Modules/Platform/Linux-XL-Fortran.cmake b/Modules/Platform/Linux-XL-Fortran.cmake
index a878991..d9b4c2d 100644
--- a/Modules/Platform/Linux-XL-Fortran.cmake
+++ b/Modules/Platform/Linux-XL-Fortran.cmake
@@ -1,2 +1 @@
-set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "-qmkshrobj")
 set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-Wl,-export-dynamic")
diff --git a/Modules/Platform/SunOS-Clang-C.cmake b/Modules/Platform/SunOS-Clang-C.cmake
new file mode 100644
index 0000000..f06eb8f
--- /dev/null
+++ b/Modules/Platform/SunOS-Clang-C.cmake
@@ -0,0 +1 @@
+include(Platform/SunOS-GNU-C)
diff --git a/Modules/Platform/SunOS-Clang-CXX.cmake b/Modules/Platform/SunOS-Clang-CXX.cmake
new file mode 100644
index 0000000..869182c
--- /dev/null
+++ b/Modules/Platform/SunOS-Clang-CXX.cmake
@@ -0,0 +1 @@
+include(Platform/SunOS-GNU-CXX)
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index b317da6..f226553 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -8,6 +8,9 @@
 endif()
 set(__WINDOWS_CLANG 1)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_LIBRARY_PATH_FLAG "-L")
   set(CMAKE_LINK_LIBRARY_FLAG "-l")
@@ -44,15 +47,22 @@
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1)
 
+  set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-flto")
+  set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
+  set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
+  set(CMAKE_${lang}_ARCHIVE_CREATE_IPO "<CMAKE_AR> cr <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND_IPO "<CMAKE_AR> r <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH_IPO "<CMAKE_RANLIB> <TARGET>")
+
   # Create archiving rules to support large object file lists for static libraries.
   set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q  <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES>")
+    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES>")
   set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>")
+    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>")
 
   set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -Xclang -flto-visibility-public-std -D_MT -Xclang --dependent-lib=libcmt)
   set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -D_DLL -D_MT -Xclang --dependent-lib=msvcrt)
@@ -73,6 +83,11 @@
   string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG -Xclang -gcodeview ${__ADDED_FLAGS}")
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
 
+  set(CMAKE_PCH_EXTENSION .pch)
+  set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>)
+
   unset(__ADDED_FLAGS)
   unset(__ADDED_FLAGS_DEBUG)
   string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)
@@ -99,6 +114,21 @@
             "or clang-cl as both C and C++ compilers.")
   endif()
 
+  if(NOT CMAKE_RC_COMPILER_INIT)
+    # Check if rc is already in the path
+    # This may happen in cases where the user is already in a visual studio environment when CMake is invoked
+    find_program(__RC_COMPILER_PATH NAMES rc)
+
+    # Default to rc if it's available, otherwise fall back to llvm-rc
+    if(__RC_COMPILER_PATH)
+      set(CMAKE_RC_COMPILER_INIT rc)
+    else()
+      set(CMAKE_RC_COMPILER_INIT llvm-rc)
+    endif()
+
+    unset(__RC_COMPILER_PATH CACHE)
+  endif()
+
   if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" )
     include(Platform/Windows-MSVC)
 
diff --git a/Modules/Platform/Windows-Embarcadero.cmake b/Modules/Platform/Windows-Embarcadero.cmake
index 48b936e..370b56e 100644
--- a/Modules/Platform/Windows-Embarcadero.cmake
+++ b/Modules/Platform/Windows-Embarcadero.cmake
@@ -119,6 +119,13 @@
     "tlib ${CMAKE_START_TEMP_FILE}/p512 <LINK_FLAGS> /a <TARGET_QUOTED> <OBJECTS>${CMAKE_END_TEMP_FILE}"
     )
 
+  # Precompile Headers
+  if (EMBARCADERO)
+    set(CMAKE_PCH_EXTENSION .pch)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>)
+  endif()
+
   # Initial configuration flags.
   string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_tM}")
   string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Od -v")
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index 7a83859..34f5d03 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -329,6 +329,22 @@
   set(CMAKE_${lang}_LINK_EXECUTABLE
     "${_CMAKE_VS_LINK_EXE}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>${CMAKE_END_TEMP_FILE}")
 
+  set(CMAKE_PCH_EXTENSION .pch)
+  set(CMAKE_LINK_PCH ON)
+  if(MSVC_VERSION GREATER_EQUAL 1910)
+    # VS 2017 or greater
+    if (NOT ${CMAKE_${lang}_COMPILER_ID} STREQUAL "Clang")
+        set(CMAKE_PCH_PROLOGUE "#pragma system_header")
+    else()
+        set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
+    endif()
+  endif()
+  if (NOT ${CMAKE_${lang}_COMPILER_ID} STREQUAL "Clang")
+    set(CMAKE_PCH_COPY_COMPILE_PDB ON)
+  endif()
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH /Yu<PCH_HEADER> /Fp<PCH_FILE> /FI<PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH /Yc<PCH_HEADER> /Fp<PCH_FILE> /FI<PCH_HEADER>)
+
   if("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
     set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
     set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
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/Modules/ProcessorCount.cmake b/Modules/ProcessorCount.cmake
index 8c25256..43ec889 100644
--- a/Modules/ProcessorCount.cmake
+++ b/Modules/ProcessorCount.cmake
@@ -168,9 +168,13 @@
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
         OUTPUT_VARIABLE psrinfo_output)
-      string(REGEX MATCH "([0-9]+) virtual processor" procs "${psrinfo_output}")
-      set(count "${CMAKE_MATCH_1}")
-      #message("ProcessorCount: trying psrinfo -p -v '${ProcessorCount_cmd_prvinfo}'")
+      string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}")
+      set(count "")
+      foreach(proc ${procs})
+        string(REGEX MATCH "has ([0-9]+) virtual" res ${proc})
+        math(EXPR count "${count} + ${CMAKE_MATCH_1}")
+      endforeach()
+      #message("ProcessorCount: trying '${ProcessorCount_cmd_psrinfo}' -p -v")
     else()
       # Sun (systems where uname -X emits "NumCPU" in its output):
       find_program(ProcessorCount_cmd_uname uname)
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 695e075..d89a69d 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -95,9 +95,6 @@
   ${CMake_HAIKU_INCLUDE_DIRS}
   )
 
-# let cmake know it is supposed to use it
-add_definitions(-DCMAKE_BUILD_WITH_CMAKE)
-
 # Check if we can build the ELF parser.
 if(CMAKE_USE_ELF_PARSER)
   set(ELF_SRCS cmELF.h cmELF.cxx)
@@ -142,12 +139,37 @@
 
   cmAffinity.cxx
   cmAffinity.h
+  cmAlgorithms.h
   cmArchiveWrite.cxx
   cmArgumentParser.cxx
   cmArgumentParser.h
   cmBase32.cxx
+  cmBinUtilsLinker.cxx
+  cmBinUtilsLinker.h
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
+  cmBinUtilsLinuxELFLinker.cxx
+  cmBinUtilsLinuxELFLinker.h
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
+  cmBinUtilsMacOSMachOLinker.cxx
+  cmBinUtilsMacOSMachOLinker.h
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
+  cmBinUtilsWindowsPELinker.cxx
+  cmBinUtilsWindowsPELinker.h
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
   cmCacheManager.cxx
   cmCacheManager.h
+  cmCheckCustomOutputs.h
+  cmCheckCustomOutputs.cxx
   cmCLocaleEnvironmentScope.h
   cmCLocaleEnvironmentScope.cxx
   cmCommandArgumentParserHelper.cxx
@@ -171,6 +193,9 @@
   cmCustomCommand.h
   cmCustomCommandGenerator.cxx
   cmCustomCommandGenerator.h
+  cmCustomCommandLines.cxx
+  cmCustomCommandLines.h
+  cmCustomCommandTypes.h
   cmDefinitions.cxx
   cmDefinitions.h
   cmDepends.cxx
@@ -204,8 +229,6 @@
   cmExportTryCompileFileGenerator.cxx
   cmExportSet.h
   cmExportSet.cxx
-  cmExportSetMap.h
-  cmExportSetMap.cxx
   cmExternalMakefileProjectGenerator.cxx
   cmExternalMakefileProjectGenerator.h
   cmExtraCodeBlocksGenerator.cxx
@@ -295,6 +318,10 @@
   cmInstallTargetGenerator.cxx
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.cxx
+  cmLDConfigLDConfigTool.cxx
+  cmLDConfigLDConfigTool.h
+  cmLDConfigTool.cxx
+  cmLDConfigTool.h
   cmLinkedTree.h
   cmLinkItem.cxx
   cmLinkItem.h
@@ -338,7 +365,6 @@
   cmProcessOutput.h
   cmProcessTools.cxx
   cmProcessTools.h
-  cmProperty.cxx
   cmProperty.h
   cmPropertyDefinition.cxx
   cmPropertyDefinition.h
@@ -360,6 +386,8 @@
   cmQtAutoRcc.h
   cmRST.cxx
   cmRST.h
+  cmRuntimeDependencyArchive.cxx
+  cmRuntimeDependencyArchive.h
   cmScriptGenerator.h
   cmScriptGenerator.cxx
   cmSourceFile.cxx
@@ -376,6 +404,8 @@
   cmStateSnapshot.cxx
   cmStateSnapshot.h
   cmStateTypes.h
+  cmStringAlgorithms.cxx
+  cmStringAlgorithms.h
   cmSystemTools.cxx
   cmSystemTools.h
   cmTarget.cxx
@@ -410,9 +440,6 @@
   cmXMLWriter.h
   cmake.cxx
   cmake.h
-  cm_string_view.cxx
-  cm_string_view.hxx
-  cm_static_string_view.hxx
 
   cmCommand.cxx
   cmCommand.h
@@ -466,8 +493,6 @@
   cmCreateTestSourceList.h
   cmDefinePropertyCommand.cxx
   cmDefinePropertyCommand.h
-  cmDisallowedCommand.cxx
-  cmDisallowedCommand.h
   cmEnableLanguageCommand.cxx
   cmEnableLanguageCommand.h
   cmEnableTestingCommand.cxx
@@ -502,6 +527,8 @@
   cmFindProgramCommand.h
   cmForEachCommand.cxx
   cmForEachCommand.h
+  cmFunctionBlocker.cxx
+  cmFunctionBlocker.h
   cmFunctionCommand.cxx
   cmFunctionCommand.h
   cmGetCMakePropertyCommand.cxx
@@ -607,6 +634,8 @@
   cmStringReplaceHelper.cxx
   cmStringCommand.cxx
   cmStringCommand.h
+  cmSubcommandTable.cxx
+  cmSubcommandTable.h
   cmSubdirCommand.cxx
   cmSubdirCommand.h
   cmSubdirDependsCommand.cxx
@@ -625,6 +654,8 @@
   cmTargetLinkDirectoriesCommand.h
   cmTargetLinkLibrariesCommand.cxx
   cmTargetLinkLibrariesCommand.h
+  cmTargetPrecompileHeadersCommand.cxx
+  cmTargetPrecompileHeadersCommand.h
   cmTargetPropCommandBase.cxx
   cmTargetPropCommandBase.h
   cmTargetSourcesCommand.cxx
@@ -635,8 +666,6 @@
   cmTryCompileCommand.h
   cmTryRunCommand.cxx
   cmTryRunCommand.h
-  cmUnexpectedCommand.cxx
-  cmUnexpectedCommand.h
   cmUnsetCommand.cxx
   cmUnsetCommand.h
   cmUseMangledMesaCommand.cxx
@@ -652,13 +681,13 @@
   cmWriteFileCommand.cxx
   cmWriteFileCommand.h
 
+  cm_static_string_view.hxx
   cm_get_date.h
   cm_get_date.c
   cm_utf8.h
   cm_utf8.c
   cm_codecvt.hxx
   cm_codecvt.cxx
-  cm_thread.hxx
 
   cmDuration.h
   cmDuration.cxx
@@ -824,6 +853,7 @@
 # create a library used by the command line and the GUI
 add_library(CMakeLib ${SRCS})
 target_link_libraries(CMakeLib cmsys
+  ${CMAKE_STD_LIBRARY}
   ${CMAKE_EXPAT_LIBRARIES} ${CMAKE_ZLIB_LIBRARIES}
   ${CMAKE_TAR_LIBRARIES}
   ${CMAKE_CURL_LIBRARIES}
@@ -869,6 +899,7 @@
 #
 set(CTEST_SRCS cmCTest.cxx
   CTest/cmProcess.cxx
+  CTest/cmCTestBinPacker.cxx
   CTest/cmCTestBuildAndTestHandler.cxx
   CTest/cmCTestBuildCommand.cxx
   CTest/cmCTestBuildHandler.cxx
@@ -888,10 +919,13 @@
   CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
   CTest/cmCTestGenericHandler.cxx
   CTest/cmCTestHandlerCommand.cxx
+  CTest/cmCTestHardwareAllocator.cxx
+  CTest/cmCTestHardwareSpec.cxx
   CTest/cmCTestLaunch.cxx
   CTest/cmCTestMemCheckCommand.cxx
   CTest/cmCTestMemCheckHandler.cxx
   CTest/cmCTestMultiProcessHandler.cxx
+  CTest/cmCTestProcessesLexerHelper.cxx
   CTest/cmCTestReadCustomFilesCommand.cxx
   CTest/cmCTestRunScriptCommand.cxx
   CTest/cmCTestRunTest.cxx
@@ -923,6 +957,10 @@
   CTest/cmCTestHG.h
   CTest/cmCTestP4.cxx
   CTest/cmCTestP4.h
+
+  LexerParser/cmCTestProcessesLexer.cxx
+  LexerParser/cmCTestProcessesLexer.h
+  LexerParser/cmCTestProcessesLexer.in.l
   )
 
 # Build CTestLib
@@ -949,12 +987,6 @@
   CPack/cmCPackNSISGenerator.cxx
   CPack/cmCPackNuGetGenerator.cxx
   CPack/cmCPackSTGZGenerator.cxx
-  CPack/cmCPackTGZGenerator.cxx
-  CPack/cmCPackTXZGenerator.cxx
-  CPack/cmCPackTarBZip2Generator.cxx
-  CPack/cmCPackTarCompressGenerator.cxx
-  CPack/cmCPackZIPGenerator.cxx
-  CPack/cmCPack7zGenerator.cxx
   )
 # CPack IFW generator
 set(CPACK_SRCS ${CPACK_SRCS}
@@ -1121,7 +1153,7 @@
 
 # Curses GUI
 if(BUILD_CursesDialog)
-  include(${CMake_SOURCE_DIR}/Source/CursesDialog/CMakeLists.txt)
+  add_subdirectory(CursesDialog)
 endif()
 
 # Qt GUI
@@ -1134,6 +1166,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)
 
@@ -1146,6 +1193,11 @@
   endforeach()
 endif()
 
+if(CMake_JOB_POOL_LINK_BIN)
+  set_property(TARGET ${_tools} PROPERTY JOB_POOL_LINK "link-bin")
+  set_property(GLOBAL APPEND PROPERTY JOB_POOLS "link-bin=${CMake_JOB_POOL_LINK_BIN}")
+endif()
+
 # Install tools
 
 foreach(_tool ${_tools})
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 6a64153..5670622 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 4)
+set(CMake_VERSION_MINOR 16)
+set(CMake_VERSION_PATCH 20191012)
 #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 72a5800..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(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/IFW/cmCPackIFWCommon.cxx b/Source/CPack/IFW/cmCPackIFWCommon.cxx
index 1e72641..9fa74be 100644
--- a/Source/CPack/IFW/cmCPackIFWCommon.cxx
+++ b/Source/CPack/IFW/cmCPackIFWCommon.cxx
@@ -2,18 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWCommon.h"
 
+#include <cstddef>
+#include <sstream>
+#include <utility>
+#include <vector>
+
 #include "cmCPackGenerator.h"
 #include "cmCPackIFWGenerator.h"
 #include "cmCPackLog.h" // IWYU pragma: keep
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmVersionConfig.h"
 #include "cmXMLWriter.h"
 
-#include <sstream>
-#include <utility>
-#include <vector>
-
 cmCPackIFWCommon::cmCPackIFWCommon()
   : Generator(nullptr)
 {
@@ -77,8 +79,7 @@
 void cmCPackIFWCommon::ExpandListArgument(
   const std::string& arg, std::map<std::string, std::string>& argsOut)
 {
-  std::vector<std::string> args;
-  cmSystemTools::ExpandListArgument(arg, args, false);
+  std::vector<std::string> args = cmExpandedList(arg, false);
   if (args.empty()) {
     return;
   }
@@ -99,8 +100,7 @@
 void cmCPackIFWCommon::ExpandListArgument(
   const std::string& arg, std::multimap<std::string, std::string>& argsOut)
 {
-  std::vector<std::string> args;
-  cmSystemTools::ExpandListArgument(arg, args, false);
+  std::vector<std::string> args = cmExpandedList(arg, false);
   if (args.empty()) {
     return;
   }
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
index c1b6eea..509ac65 100644
--- a/Source/CPack/IFW/cmCPackIFWGenerator.cxx
+++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
@@ -2,6 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWGenerator.h"
 
+#include <sstream>
+#include <utility>
+
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackIFWCommon.h"
@@ -11,11 +14,9 @@
 #include "cmCPackLog.h" // IWYU pragma: keep
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <sstream>
-#include <utility>
-
 cmCPackIFWGenerator::cmCPackIFWGenerator()
 {
   this->Generator = this;
@@ -34,29 +35,35 @@
   this->Installer.GeneratePackageFiles();
 
   std::string ifwTLD = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  std::string ifwTmpFile = ifwTLD;
-  ifwTmpFile += "/IFWOutput.log";
+  std::string ifwTmpFile = cmStrCat(ifwTLD, "/IFWOutput.log");
 
   // Run repogen
   if (!this->Installer.RemoteRepositories.empty()) {
-    std::string ifwCmd = this->RepoGen;
+    std::vector<std::string> ifwCmd;
+    std::string ifwArg;
+
+    ifwCmd.emplace_back(this->RepoGen);
 
     if (this->IsVersionLess("2.0.0")) {
-      ifwCmd += " -c " + this->toplevel + "/config/config.xml";
+      ifwCmd.emplace_back("-c");
+      ifwCmd.emplace_back(this->toplevel + "/config/config.xml");
     }
 
-    ifwCmd += " -p " + this->toplevel + "/packages";
+    ifwCmd.emplace_back("-p");
+    ifwCmd.emplace_back(this->toplevel + "/packages");
 
     if (!this->PkgsDirsVector.empty()) {
       for (std::string const& it : this->PkgsDirsVector) {
-        ifwCmd += " -p " + it;
+        ifwCmd.emplace_back("-p");
+        ifwCmd.emplace_back(it);
       }
     }
 
     if (!this->RepoDirsVector.empty()) {
       if (!this->IsVersionLess("3.1")) {
         for (std::string const& rd : this->RepoDirsVector) {
-          ifwCmd += " --repository " + rd;
+          ifwCmd.emplace_back("--repository");
+          ifwCmd.emplace_back(rd);
         }
       } else {
         cmCPackIFWLogger(WARNING,
@@ -69,18 +76,20 @@
     }
 
     if (!this->OnlineOnly && !this->DownloadedPackages.empty()) {
-      ifwCmd += " -i ";
-      std::set<cmCPackIFWPackage*>::iterator it =
-        this->DownloadedPackages.begin();
-      ifwCmd += (*it)->Name;
+      ifwCmd.emplace_back("-i");
+      auto it = this->DownloadedPackages.begin();
+      ifwArg = (*it)->Name;
       ++it;
       while (it != this->DownloadedPackages.end()) {
-        ifwCmd += "," + (*it)->Name;
+        ifwArg += "," + (*it)->Name;
         ++it;
       }
+      ifwCmd.emplace_back(ifwArg);
     }
-    ifwCmd += " " + this->toplevel + "/repository";
-    cmCPackIFWLogger(VERBOSE, "Execute: " << ifwCmd << std::endl);
+    ifwCmd.emplace_back(this->toplevel + "/repository");
+    cmCPackIFWLogger(VERBOSE,
+                     "Execute: " << cmSystemTools::PrintSingleCommand(ifwCmd)
+                                 << std::endl);
     std::string output;
     int retVal = 1;
     cmCPackIFWLogger(OUTPUT, "- Generate repository" << std::endl);
@@ -89,14 +98,15 @@
       cmDuration::zero());
     if (!res || retVal) {
       cmGeneratedFileStream ofs(ifwTmpFile);
-      ofs << "# Run command: " << ifwCmd << std::endl
+      ofs << "# Run command: " << cmSystemTools::PrintSingleCommand(ifwCmd)
+          << std::endl
           << "# Output:" << std::endl
           << output << std::endl;
-      cmCPackIFWLogger(ERROR,
-                       "Problem running IFW command: "
-                         << ifwCmd << std::endl
-                         << "Please check " << ifwTmpFile << " for errors"
-                         << std::endl);
+      cmCPackIFWLogger(
+        ERROR,
+        "Problem running IFW command: "
+          << cmSystemTools::PrintSingleCommand(ifwCmd) << std::endl
+          << "Please check \"" << ifwTmpFile << "\" for errors" << std::endl);
       return 0;
     }
 
@@ -104,46 +114,54 @@
         !this->Repository.PatchUpdatesXml()) {
       cmCPackIFWLogger(WARNING,
                        "Problem patch IFW \"Updates\" "
-                         << "file: "
-                         << this->toplevel + "/repository/Updates.xml"
-                         << std::endl);
+                         << "file: \"" << this->toplevel
+                         << "/repository/Updates.xml\"" << std::endl);
     }
 
     cmCPackIFWLogger(OUTPUT,
-                     "- repository: " << this->toplevel
-                                      << "/repository generated" << std::endl);
+                     "- repository: \"" << this->toplevel
+                                        << "/repository\" generated"
+                                        << std::endl);
   }
 
   // Run binary creator
   {
-    std::string ifwCmd = this->BinCreator;
-    ifwCmd += " -c " + this->toplevel + "/config/config.xml";
+    std::vector<std::string> ifwCmd;
+    std::string ifwArg;
+
+    ifwCmd.emplace_back(this->BinCreator);
+
+    ifwCmd.emplace_back("-c");
+    ifwCmd.emplace_back(this->toplevel + "/config/config.xml");
 
     if (!this->Installer.Resources.empty()) {
-      ifwCmd += " -r ";
-      std::vector<std::string>::iterator it =
-        this->Installer.Resources.begin();
+      ifwCmd.emplace_back("-r");
+      auto it = this->Installer.Resources.begin();
       std::string path = this->toplevel + "/resources/";
-      ifwCmd += path + *it;
+      ifwArg = path + *it;
       ++it;
       while (it != this->Installer.Resources.end()) {
-        ifwCmd += "," + path + *it;
+        ifwArg += "," + path + *it;
         ++it;
       }
+      ifwCmd.emplace_back(ifwArg);
     }
 
-    ifwCmd += " -p " + this->toplevel + "/packages";
+    ifwCmd.emplace_back("-p");
+    ifwCmd.emplace_back(this->toplevel + "/packages");
 
     if (!this->PkgsDirsVector.empty()) {
       for (std::string const& it : this->PkgsDirsVector) {
-        ifwCmd += " -p " + it;
+        ifwCmd.emplace_back("-p");
+        ifwCmd.emplace_back(it);
       }
     }
 
     if (!this->RepoDirsVector.empty()) {
       if (!this->IsVersionLess("3.1")) {
         for (std::string const& rd : this->RepoDirsVector) {
-          ifwCmd += " --repository " + rd;
+          ifwCmd.emplace_back("--repository");
+          ifwCmd.emplace_back(rd);
         }
       } else {
         cmCPackIFWLogger(WARNING,
@@ -156,44 +174,46 @@
     }
 
     if (this->OnlineOnly) {
-      ifwCmd += " --online-only";
+      ifwCmd.emplace_back("--online-only");
     } else if (!this->DownloadedPackages.empty() &&
                !this->Installer.RemoteRepositories.empty()) {
-      ifwCmd += " -e ";
-      std::set<cmCPackIFWPackage*>::iterator it =
-        this->DownloadedPackages.begin();
-      ifwCmd += (*it)->Name;
+      ifwCmd.emplace_back("-e");
+      auto it = this->DownloadedPackages.begin();
+      ifwArg = (*it)->Name;
       ++it;
       while (it != this->DownloadedPackages.end()) {
-        ifwCmd += "," + (*it)->Name;
+        ifwArg += "," + (*it)->Name;
         ++it;
       }
+      ifwCmd.emplace_back(ifwArg);
     } else if (!this->DependentPackages.empty()) {
-      ifwCmd += " -i ";
+      ifwCmd.emplace_back("-i");
+      ifwArg.clear();
       // Binary
-      std::set<cmCPackIFWPackage*>::iterator bit =
-        this->BinaryPackages.begin();
+      auto bit = this->BinaryPackages.begin();
       while (bit != this->BinaryPackages.end()) {
-        ifwCmd += (*bit)->Name + ",";
+        ifwArg += (*bit)->Name + ",";
         ++bit;
       }
       // Depend
-      DependenceMap::iterator it = this->DependentPackages.begin();
-      ifwCmd += it->second.Name;
+      auto it = this->DependentPackages.begin();
+      ifwArg += it->second.Name;
       ++it;
       while (it != this->DependentPackages.end()) {
-        ifwCmd += "," + it->second.Name;
+        ifwArg += "," + it->second.Name;
         ++it;
       }
+      ifwCmd.emplace_back(ifwArg);
     }
     // TODO: set correct name for multipackages
     if (!this->packageFileNames.empty()) {
-      ifwCmd += " " + this->packageFileNames[0];
+      ifwCmd.emplace_back(this->packageFileNames[0]);
     } else {
-      ifwCmd += " installer";
-      ifwCmd += this->OutputExtension;
+      ifwCmd.emplace_back("installer" + this->OutputExtension);
     }
-    cmCPackIFWLogger(VERBOSE, "Execute: " << ifwCmd << std::endl);
+    cmCPackIFWLogger(VERBOSE,
+                     "Execute: " << cmSystemTools::PrintSingleCommand(ifwCmd)
+                                 << std::endl);
     std::string output;
     int retVal = 1;
     cmCPackIFWLogger(OUTPUT, "- Generate package" << std::endl);
@@ -202,14 +222,15 @@
       cmDuration::zero());
     if (!res || retVal) {
       cmGeneratedFileStream ofs(ifwTmpFile);
-      ofs << "# Run command: " << ifwCmd << std::endl
+      ofs << "# Run command: " << cmSystemTools::PrintSingleCommand(ifwCmd)
+          << std::endl
           << "# Output:" << std::endl
           << output << std::endl;
-      cmCPackIFWLogger(ERROR,
-                       "Problem running IFW command: "
-                         << ifwCmd << std::endl
-                         << "Please check " << ifwTmpFile << " for errors"
-                         << std::endl);
+      cmCPackIFWLogger(
+        ERROR,
+        "Problem running IFW command: "
+          << cmSystemTools::PrintSingleCommand(ifwCmd) << std::endl
+          << "Please check \"" << ifwTmpFile << "\" for errors" << std::endl);
       return 0;
     }
   }
@@ -253,7 +274,7 @@
   // Look 'binarycreator' executable (needs)
 
   const char* BinCreatorStr = this->GetOption(BinCreatorOpt);
-  if (!BinCreatorStr || cmSystemTools::IsNOTFOUND(BinCreatorStr)) {
+  if (!BinCreatorStr || cmIsNOTFOUND(BinCreatorStr)) {
     this->BinCreator.clear();
   } else {
     this->BinCreator = BinCreatorStr;
@@ -270,7 +291,7 @@
   // Look 'repogen' executable (optional)
 
   const char* RepoGenStr = this->GetOption(RepoGenOpt);
-  if (!RepoGenStr || cmSystemTools::IsNOTFOUND(RepoGenStr)) {
+  if (!RepoGenStr || cmIsNOTFOUND(RepoGenStr)) {
     this->RepoGen.clear();
   } else {
     this->RepoGen = RepoGenStr;
@@ -292,14 +313,14 @@
   // Additional packages dirs
   this->PkgsDirsVector.clear();
   if (const char* dirs = this->GetOption("CPACK_IFW_PACKAGES_DIRECTORIES")) {
-    cmSystemTools::ExpandListArgument(dirs, this->PkgsDirsVector);
+    cmExpandList(dirs, this->PkgsDirsVector);
   }
 
   // Additional repositories dirs
   this->RepoDirsVector.clear();
   if (const char* dirs =
         this->GetOption("CPACK_IFW_REPOSITORIES_DIRECTORIES")) {
-    cmSystemTools::ExpandListArgument(dirs, this->RepoDirsVector);
+    cmExpandList(dirs, this->RepoDirsVector);
   }
 
   // Installer
@@ -316,18 +337,17 @@
 
   // Repositories
   if (const char* RepoAllStr = this->GetOption("CPACK_IFW_REPOSITORIES_ALL")) {
-    std::vector<std::string> RepoAllVector;
-    cmSystemTools::ExpandListArgument(RepoAllStr, RepoAllVector);
+    std::vector<std::string> RepoAllVector = cmExpandedList(RepoAllStr);
     for (std::string const& r : RepoAllVector) {
       this->GetRepository(r);
     }
   }
 
   if (const char* ifwDownloadAll = this->GetOption("CPACK_IFW_DOWNLOAD_ALL")) {
-    this->OnlineOnly = cmSystemTools::IsOn(ifwDownloadAll);
+    this->OnlineOnly = cmIsOn(ifwDownloadAll);
   } else if (const char* cpackDownloadAll =
                this->GetOption("CPACK_DOWNLOAD_ALL")) {
-    this->OnlineOnly = cmSystemTools::IsOn(cpackDownloadAll);
+    this->OnlineOnly = cmIsOn(cpackDownloadAll);
   } else {
     this->OnlineOnly = false;
   }
@@ -386,7 +406,7 @@
 cmCPackComponent* cmCPackIFWGenerator::GetComponent(
   const std::string& projectName, const std::string& componentName)
 {
-  ComponentsMap::iterator cit = this->Components.find(componentName);
+  auto cit = this->Components.find(componentName);
   if (cit != this->Components.end()) {
     return &(cit->second);
   }
@@ -398,7 +418,7 @@
   }
 
   std::string name = this->GetComponentPackageName(component);
-  PackagesMap::iterator pit = this->Packages.find(name);
+  auto pit = this->Packages.find(name);
   if (pit != this->Packages.end()) {
     return component;
   }
@@ -438,7 +458,7 @@
   }
 
   std::string name = this->GetGroupPackageName(group);
-  PackagesMap::iterator pit = this->Packages.find(name);
+  auto pit = this->Packages.find(name);
   if (pit != this->Packages.end()) {
     return group;
   }
@@ -569,23 +589,21 @@
 cmCPackIFWPackage* cmCPackIFWGenerator::GetGroupPackage(
   cmCPackComponentGroup* group) const
 {
-  std::map<cmCPackComponentGroup*, cmCPackIFWPackage*>::const_iterator pit =
-    this->GroupPackages.find(group);
+  auto pit = this->GroupPackages.find(group);
   return pit != this->GroupPackages.end() ? pit->second : nullptr;
 }
 
 cmCPackIFWPackage* cmCPackIFWGenerator::GetComponentPackage(
   cmCPackComponent* component) const
 {
-  std::map<cmCPackComponent*, cmCPackIFWPackage*>::const_iterator pit =
-    this->ComponentPackages.find(component);
+  auto pit = this->ComponentPackages.find(component);
   return pit != this->ComponentPackages.end() ? pit->second : nullptr;
 }
 
 cmCPackIFWRepository* cmCPackIFWGenerator::GetRepository(
   const std::string& repositoryName)
 {
-  RepositoriesMap::iterator rit = this->Repositories.find(repositoryName);
+  auto rit = this->Repositories.find(repositoryName);
   if (rit != this->Repositories.end()) {
     return &(rit->second);
   }
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.h b/Source/CPack/IFW/cmCPackIFWGenerator.h
index 0430122..86a73c8 100644
--- a/Source/CPack/IFW/cmCPackIFWGenerator.h
+++ b/Source/CPack/IFW/cmCPackIFWGenerator.h
@@ -5,6 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackIFWCommon.h"
@@ -12,11 +17,6 @@
 #include "cmCPackIFWPackage.h"
 #include "cmCPackIFWRepository.h"
 
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
 /** \class cmCPackIFWGenerator
  * \brief A generator for Qt Installer Framework tools
  *
@@ -29,12 +29,12 @@
 public:
   cmCPackTypeMacro(cmCPackIFWGenerator, cmCPackGenerator);
 
-  typedef std::map<std::string, cmCPackIFWPackage> PackagesMap;
-  typedef std::map<std::string, cmCPackIFWRepository> RepositoriesMap;
-  typedef std::map<std::string, cmCPackComponent> ComponentsMap;
-  typedef std::map<std::string, cmCPackComponentGroup> ComponentGoupsMap;
-  typedef std::map<std::string, cmCPackIFWPackage::DependenceStruct>
-    DependenceMap;
+  using PackagesMap = std::map<std::string, cmCPackIFWPackage>;
+  using RepositoriesMap = std::map<std::string, cmCPackIFWRepository>;
+  using ComponentsMap = std::map<std::string, cmCPackComponent>;
+  using ComponentGoupsMap = std::map<std::string, cmCPackComponentGroup>;
+  using DependenceMap =
+    std::map<std::string, cmCPackIFWPackage::DependenceStruct>;
 
   using cmCPackIFWCommon::GetOption;
   using cmCPackIFWCommon::IsOn;
diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx
index a075a17..4bad598 100644
--- a/Source/CPack/IFW/cmCPackIFWInstaller.cxx
+++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx
@@ -2,20 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWInstaller.h"
 
+#include <cstddef>
+#include <sstream>
+#include <utility>
+
 #include "cmCPackIFWCommon.h"
 #include "cmCPackIFWGenerator.h"
 #include "cmCPackIFWPackage.h"
 #include "cmCPackIFWRepository.h"
 #include "cmCPackLog.h" // IWYU pragma: keep
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
-#include <sstream>
-#include <stddef.h>
-#include <utility>
-
 cmCPackIFWInstaller::cmCPackIFWInstaller() = default;
 
 void cmCPackIFWInstaller::printSkippedOptionWarning(
@@ -192,8 +193,8 @@
     this->TargetDir = optIFW_TARGET_DIRECTORY;
   } else if (const char* optPACKAGE_INSTALL_DIRECTORY =
                this->GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) {
-    this->TargetDir = "@ApplicationsDir@/";
-    this->TargetDir += optPACKAGE_INSTALL_DIRECTORY;
+    this->TargetDir =
+      cmStrCat("@ApplicationsDir@/", optPACKAGE_INSTALL_DIRECTORY);
   } else {
     this->TargetDir = "@RootDir@/usr/local";
   }
@@ -244,8 +245,7 @@
   if (const char* optIFW_PACKAGE_RESOURCES =
         this->GetOption("CPACK_IFW_PACKAGE_RESOURCES")) {
     this->Resources.clear();
-    cmSystemTools::ExpandListArgument(optIFW_PACKAGE_RESOURCES,
-                                      this->Resources);
+    cmExpandList(optIFW_PACKAGE_RESOURCES, this->Resources);
   }
 }
 
@@ -292,7 +292,7 @@
   {
     if (this->file) {
       std::string content(data, data + length);
-      content = cmSystemTools::TrimWhitespace(content);
+      content = cmTrimWhitespace(content);
       std::string source = this->basePath + "/" + content;
       std::string destination = this->path + "/" + content;
       if (!cmSystemTools::CopyFileIfDifferent(source, destination)) {
diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.h b/Source/CPack/IFW/cmCPackIFWInstaller.h
index be51fa5..8b3f96a 100644
--- a/Source/CPack/IFW/cmCPackIFWInstaller.h
+++ b/Source/CPack/IFW/cmCPackIFWInstaller.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackIFWCommon.h"
-
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmCPackIFWCommon.h"
+
 class cmCPackIFWPackage;
 class cmCPackIFWRepository;
 
@@ -22,8 +22,8 @@
 public:
   // Types
 
-  typedef std::map<std::string, cmCPackIFWPackage*> PackagesMap;
-  typedef std::vector<cmCPackIFWRepository*> RepositoriesVector;
+  using PackagesMap = std::map<std::string, cmCPackIFWPackage*>;
+  using RepositoriesVector = std::vector<cmCPackIFWRepository*>;
 
 public:
   // Constructor
diff --git a/Source/CPack/IFW/cmCPackIFWPackage.cxx b/Source/CPack/IFW/cmCPackIFWPackage.cxx
index a1a52b1..9a9cd56 100644
--- a/Source/CPack/IFW/cmCPackIFWPackage.cxx
+++ b/Source/CPack/IFW/cmCPackIFWPackage.cxx
@@ -2,21 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWPackage.h"
 
+#include <cstddef>
+#include <map>
+#include <sstream>
+#include <utility>
+
 #include "cmCPackComponentGroup.h"
 #include "cmCPackIFWCommon.h"
 #include "cmCPackIFWGenerator.h"
 #include "cmCPackIFWInstaller.h"
 #include "cmCPackLog.h" // IWYU pragma: keep
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmXMLWriter.h"
 
-#include <map>
-#include <sstream>
-#include <stddef.h>
-#include <utility>
-
 //---------------------------------------------------------- CompareStruct ---
 cmCPackIFWPackage::CompareStruct::CompareStruct()
   : Type(cmCPackIFWPackage::CompareNone)
@@ -196,7 +197,7 @@
   // User interfaces
   if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
     this->UserInterfaces.clear();
-    cmSystemTools::ExpandListArgument(option, this->UserInterfaces);
+    cmExpandList(option, this->UserInterfaces);
   }
 
   // CMake dependencies
@@ -209,7 +210,7 @@
   // Licenses
   if (const char* option = this->GetOption(prefix + "LICENSES")) {
     this->Licenses.clear();
-    cmSystemTools::ExpandListArgument(option, this->Licenses);
+    cmExpandList(option, this->Licenses);
     if (this->Licenses.size() % 2 != 0) {
       cmCPackIFWLogger(
         WARNING,
@@ -281,13 +282,13 @@
   // User interfaces
   if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
     this->UserInterfaces.clear();
-    cmSystemTools::ExpandListArgument(option, this->UserInterfaces);
+    cmExpandList(option, this->UserInterfaces);
   }
 
   // Licenses
   if (const char* option = this->GetOption(prefix + "LICENSES")) {
     this->Licenses.clear();
-    cmSystemTools::ExpandListArgument(option, this->Licenses);
+    cmExpandList(option, this->Licenses);
     if (this->Licenses.size() % 2 != 0) {
       cmCPackIFWLogger(
         WARNING,
@@ -398,18 +399,18 @@
     this->Translations.clear();
   } else if (const char* value = this->GetOption(option)) {
     this->Translations.clear();
-    cmSystemTools::ExpandListArgument(value, this->Translations);
+    cmExpandList(value, this->Translations);
   }
 
   // QtIFW dependencies
   std::vector<std::string> deps;
   option = prefix + "DEPENDS";
   if (const char* value = this->GetOption(option)) {
-    cmSystemTools::ExpandListArgument(value, deps);
+    cmExpandList(value, deps);
   }
   option = prefix + "DEPENDENCIES";
   if (const char* value = this->GetOption(option)) {
-    cmSystemTools::ExpandListArgument(value, deps);
+    cmExpandList(value, deps);
   }
   for (std::string const& d : deps) {
     DependenceStruct dep(d);
@@ -430,8 +431,7 @@
   if (this->IsSetToEmpty(option)) {
     this->AlienAutoDependOn.clear();
   } else if (const char* value = this->GetOption(option)) {
-    std::vector<std::string> depsOn;
-    cmSystemTools::ExpandListArgument(value, depsOn);
+    std::vector<std::string> depsOn = cmExpandedList(value);
     for (std::string const& d : depsOn) {
       DependenceStruct dep(d);
       if (this->Generator->Packages.count(dep.Name)) {
@@ -488,7 +488,7 @@
     this->Replaces.clear();
   } else if (const char* value = this->GetOption(option)) {
     this->Replaces.clear();
-    cmSystemTools::ExpandListArgument(value, this->Replaces);
+    cmExpandList(value, this->Replaces);
   }
 
   // Requires admin rights
@@ -620,7 +620,7 @@
   // Write dependencies
   if (!compDepSet.empty()) {
     std::ostringstream dependencies;
-    std::set<DependenceStruct>::iterator it = compDepSet.begin();
+    auto it = compDepSet.begin();
     dependencies << it->NameWithCompare();
     ++it;
     while (it != compDepSet.end()) {
@@ -638,7 +638,7 @@
   // Write automatic dependency on
   if (!compAutoDepSet.empty()) {
     std::ostringstream dependencies;
-    std::set<DependenceStruct>::iterator it = compAutoDepSet.begin();
+    auto it = compAutoDepSet.begin();
     dependencies << it->NameWithCompare();
     ++it;
     while (it != compAutoDepSet.end()) {
@@ -674,7 +674,7 @@
   // Replaces
   if (!this->Replaces.empty()) {
     std::ostringstream replaces;
-    std::vector<std::string>::iterator it = this->Replaces.begin();
+    auto it = this->Replaces.begin();
     replaces << *it;
     ++it;
     while (it != this->Replaces.end()) {
diff --git a/Source/CPack/IFW/cmCPackIFWPackage.h b/Source/CPack/IFW/cmCPackIFWPackage.h
index ae41146..6a4a170 100644
--- a/Source/CPack/IFW/cmCPackIFWPackage.h
+++ b/Source/CPack/IFW/cmCPackIFWPackage.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackIFWCommon.h"
-
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmCPackIFWCommon.h"
+
 class cmCPackComponent;
 class cmCPackComponentGroup;
 class cmCPackIFWInstaller;
diff --git a/Source/CPack/IFW/cmCPackIFWRepository.cxx b/Source/CPack/IFW/cmCPackIFWRepository.cxx
index 8042167..a696549 100644
--- a/Source/CPack/IFW/cmCPackIFWRepository.cxx
+++ b/Source/CPack/IFW/cmCPackIFWRepository.cxx
@@ -2,14 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWRepository.h"
 
+#include <cstddef>
+
 #include "cmCPackIFWGenerator.h"
 #include "cmGeneratedFileStream.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
-#include <stddef.h>
-
 cmCPackIFWRepository::cmCPackIFWRepository()
   : Update(cmCPackIFWRepository::None)
 {
diff --git a/Source/CPack/IFW/cmCPackIFWRepository.h b/Source/CPack/IFW/cmCPackIFWRepository.h
index 227cfae..c293981 100644
--- a/Source/CPack/IFW/cmCPackIFWRepository.h
+++ b/Source/CPack/IFW/cmCPackIFWRepository.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackIFWCommon.h"
-
 #include <string>
 #include <vector>
 
+#include "cmCPackIFWCommon.h"
+
 class cmXMLWriter;
 
 /** \class cmCPackIFWRepository
@@ -28,7 +28,7 @@
     Replace
   };
 
-  typedef std::vector<cmCPackIFWRepository*> RepositoriesVector;
+  using RepositoriesVector = std::vector<cmCPackIFWRepository*>;
 
 public:
   // Constructor
diff --git a/Source/CPack/OSXScriptLauncher.cxx b/Source/CPack/OSXScriptLauncher.cxx
index 00d272c..21d27a0 100644
--- a/Source/CPack/OSXScriptLauncher.cxx
+++ b/Source/CPack/OSXScriptLauncher.cxx
@@ -1,15 +1,16 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
-#include "cmsys/SystemTools.hxx"
+#include <cstddef>
 #include <iostream>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
 #include <CoreFoundation/CoreFoundation.h>
 
+#include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
+#include "cmsys/SystemTools.hxx"
+
 // For the PATH_MAX constant
 #include <sys/syslimits.h>
 
diff --git a/Source/CPack/WiX/cmCMakeToWixPath.cxx b/Source/CPack/WiX/cmCMakeToWixPath.cxx
index b3889cf..8738501 100644
--- a/Source/CPack/WiX/cmCMakeToWixPath.cxx
+++ b/Source/CPack/WiX/cmCMakeToWixPath.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakeToWixPath.h"
 
-#include "cmSystemTools.h"
-
 #include <string>
 #include <vector>
 
+#include "cmStringAlgorithms.h"
+
 #ifdef __CYGWIN__
 #  include <sys/cygwin.h>
 std::string CMakeToWixPath(const std::string& cygpath)
@@ -29,7 +29,7 @@
     return cygpath;
   }
 
-  return cmSystemTools::TrimWhitespace(winpath_chars.data());
+  return cmTrimWhitespace(winpath_chars.data());
 }
 #else
 std::string CMakeToWixPath(const std::string& path)
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 045d93d..5fdbeab 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -2,26 +2,30 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackWIXGenerator.h"
 
-#include "cmCPackComponentGroup.h"
-#include "cmCPackLog.h"
-#include "cmCryptoHash.h"
-#include "cmGeneratedFileStream.h"
-#include "cmInstalledFile.h"
-#include "cmSystemTools.h"
-#include "cmUuid.h"
 #include <algorithm>
 
-#include "cmWIXDirectoriesSourceWriter.h"
-#include "cmWIXFeaturesSourceWriter.h"
-#include "cmWIXFilesSourceWriter.h"
-#include "cmWIXRichTextFormatWriter.h"
-#include "cmWIXSourceWriter.h"
+#include <cm/string_view>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/Encoding.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/SystemTools.hxx"
 
+#include "cmAlgorithms.h"
+#include "cmCPackComponentGroup.h"
+#include "cmCPackLog.h"
+#include "cmCryptoHash.h"
+#include "cmGeneratedFileStream.h"
+#include "cmInstalledFile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmUuid.h"
+#include "cmWIXDirectoriesSourceWriter.h"
+#include "cmWIXFeaturesSourceWriter.h"
+#include "cmWIXFilesSourceWriter.h"
+#include "cmWIXRichTextFormatWriter.h"
+#include "cmWIXSourceWriter.h"
+
 #ifdef _WIN32
 #  include <rpc.h> // for GUID generation (windows only)
 #else
@@ -225,8 +229,7 @@
 
   const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
   if (patchFilePath) {
-    std::vector<std::string> patchFilePaths;
-    cmSystemTools::ExpandListArgument(patchFilePath, patchFilePaths);
+    std::vector<std::string> patchFilePaths = cmExpandedList(patchFilePath);
 
     for (std::string const& p : patchFilePaths) {
       if (!this->Patch->LoadFragments(p)) {
@@ -237,7 +240,7 @@
 
   // if install folder is supposed to be set absolutely, the default
   // component guid "*" cannot be used
-  if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
+  if (cmIsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
     this->ComponentGuidType = cmWIXSourceWriter::CMAKE_GENERATED_GUID;
   }
 
@@ -300,7 +303,7 @@
   if (!cpackWixExtraSources)
     return;
 
-  cmSystemTools::ExpandListArgument(cpackWixExtraSources, this->WixSources);
+  cmExpandList(cpackWixExtraSources, this->WixSources);
 }
 
 void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
@@ -309,10 +312,8 @@
   if (!cpackWixExtraObjects)
     return;
 
-  std::vector<std::string> expandedExtraObjects;
-
-  cmSystemTools::ExpandListArgument(cpackWixExtraObjects,
-                                    expandedExtraObjects);
+  std::vector<std::string> expandedExtraObjects =
+    cmExpandedList(cpackWixExtraObjects);
 
   for (std::string const& obj : expandedExtraObjects) {
     stream << " " << QuotePath(obj);
@@ -518,9 +519,7 @@
     for (auto const& i : this->Components) {
       cmCPackComponent const& component = i.second;
 
-      std::string componentPath = toplevel;
-      componentPath += "/";
-      componentPath += component.Name;
+      std::string componentPath = cmStrCat(toplevel, '/', component.Name);
 
       std::string const componentFeatureId = "CM_C_" + component.Name;
 
@@ -539,9 +538,16 @@
     }
   }
 
-  bool emitUninstallShortcut =
-    emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) !=
-    emittedShortcutTypes.end();
+  bool emitUninstallShortcut = true;
+  const char* cpackWixProgramMenuFolder =
+    GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
+  if (cpackWixProgramMenuFolder &&
+      cm::string_view(cpackWixProgramMenuFolder) == ".") {
+    emitUninstallShortcut = false;
+  } else if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) ==
+             emittedShortcutTypes.end()) {
+    emitUninstallShortcut = false;
+  }
 
   if (!CreateShortcuts(std::string(), "ProductFeature", globalShortcuts,
                        emitUninstallShortcut, fileDefinitions,
@@ -582,7 +588,7 @@
 
 std::string cmCPackWIXGenerator::GetRootFolderId() const
 {
-  if (cmSystemTools::IsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
+  if (cmIsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) {
     return "";
   }
 
@@ -604,7 +610,7 @@
 
 bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate()
 {
-  std::string wixTemplate = FindTemplate("WIX.template.in");
+  std::string wixTemplate = FindTemplate("Internal/CPack/WIX.template.in");
   if (GetOption("CPACK_WIX_TEMPLATE") != 0) {
     wixTemplate = GetOption("CPACK_WIX_TEMPLATE");
   }
@@ -664,8 +670,7 @@
   std::vector<std::string> cpackPackageExecutablesList;
   const char* cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES");
   if (cpackPackageExecutables) {
-    cmSystemTools::ExpandListArgument(cpackPackageExecutables,
-                                      cpackPackageExecutablesList);
+    cmExpandList(cpackPackageExecutables, cpackPackageExecutablesList);
     if (cpackPackageExecutablesList.size() % 2 != 0) {
       cmCPackLogger(
         cmCPackLog::LOG_ERROR,
@@ -680,8 +685,7 @@
   const char* cpackPackageDesktopLinks =
     GetOption("CPACK_CREATE_DESKTOP_LINKS");
   if (cpackPackageDesktopLinks) {
-    cmSystemTools::ExpandListArgument(cpackPackageDesktopLinks,
-                                      cpackPackageDesktopLinksList);
+    cmExpandList(cpackPackageDesktopLinks, cpackPackageDesktopLinksList);
   }
 
   AddDirectoryAndFileDefinitions(
@@ -737,9 +741,16 @@
 {
   std::string directoryId;
   switch (type) {
-    case cmWIXShortcuts::START_MENU:
-      directoryId = "PROGRAM_MENU_FOLDER";
-      break;
+    case cmWIXShortcuts::START_MENU: {
+      const char* cpackWixProgramMenuFolder =
+        GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
+      if (cpackWixProgramMenuFolder &&
+          cm::string_view(cpackWixProgramMenuFolder) == ".") {
+        directoryId = "ProgramMenuFolder";
+      } else {
+        directoryId = "PROGRAM_MENU_FOLDER";
+      }
+    } break;
     case cmWIXShortcuts::DESKTOP:
       directoryId = "DesktopFolder";
       break;
@@ -793,8 +804,13 @@
                           fileDefinitions);
 
   if (type == cmWIXShortcuts::START_MENU) {
-    fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" +
-                                     idSuffix);
+    const char* cpackWixProgramMenuFolder =
+      GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
+    if (cpackWixProgramMenuFolder &&
+        cm::string_view(cpackWixProgramMenuFolder) != ".") {
+      fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" +
+                                       idSuffix);
+    }
   }
 
   if (emitUninstallShortcut) {
@@ -944,9 +960,7 @@
           shortcut.workingDirectoryId = directoryId;
           shortcuts.insert(cmWIXShortcuts::START_MENU, id, shortcut);
 
-          if (!desktopExecutables.empty() &&
-              std::find(desktopExecutables.begin(), desktopExecutables.end(),
-                        executableName) != desktopExecutables.end()) {
+          if (cmContains(desktopExecutables, executableName)) {
             shortcuts.insert(cmWIXShortcuts::DESKTOP, id, shortcut);
           }
         }
@@ -1090,8 +1104,7 @@
   cmCryptoHash sha1(cmCryptoHash::AlgoSHA1);
   std::string const hash = sha1.HashString(path);
 
-  std::string identifier;
-  identifier += hash.substr(0, 7) + "_";
+  std::string identifier = cmStrCat(cm::string_view(hash).substr(0, 7), '_');
 
   const size_t maxFileNameLength = 52;
   if (normalizedFilename.length() > maxFileNameLength) {
@@ -1136,8 +1149,7 @@
   if (!variableContent)
     return;
 
-  std::vector<std::string> list;
-  cmSystemTools::ExpandListArgument(variableContent, list);
+  std::vector<std::string> list = cmExpandedList(variableContent);
   extensions.insert(list.begin(), list.end());
 }
 
@@ -1148,8 +1160,7 @@
   if (!variableContent)
     return;
 
-  std::vector<std::string> list;
-  cmSystemTools::ExpandListArgument(variableContent, list);
+  std::vector<std::string> list = cmExpandedList(variableContent);
 
   for (std::string const& i : list) {
     stream << " " << QuotePath(i);
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h
index f8c7644..d193348 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.h
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.h
@@ -3,14 +3,13 @@
 #ifndef cmCPackWIXGenerator_h
 #define cmCPackWIXGenerator_h
 
-#include "cmCPackGenerator.h"
-
-#include "cmWIXPatch.h"
-#include "cmWIXShortcut.h"
-
 #include <map>
 #include <string>
 
+#include "cmCPackGenerator.h"
+#include "cmWIXPatch.h"
+#include "cmWIXShortcut.h"
+
 class cmWIXSourceWriter;
 class cmWIXDirectoriesSourceWriter;
 class cmWIXFilesSourceWriter;
@@ -44,9 +43,9 @@
   bool SupportsComponentInstallation() const override { return true; }
 
 private:
-  typedef std::map<std::string, std::string> id_map_t;
-  typedef std::map<std::string, size_t> ambiguity_map_t;
-  typedef std::set<std::string> extension_set_t;
+  using id_map_t = std::map<std::string, std::string>;
+  using ambiguity_map_t = std::map<std::string, size_t>;
+  using extension_set_t = std::set<std::string>;
 
   enum class DefinitionType
   {
diff --git a/Source/CPack/WiX/cmWIXAccessControlList.cxx b/Source/CPack/WiX/cmWIXAccessControlList.cxx
index 563de02..3668b46 100644
--- a/Source/CPack/WiX/cmWIXAccessControlList.cxx
+++ b/Source/CPack/WiX/cmWIXAccessControlList.cxx
@@ -3,7 +3,7 @@
 #include "cmWIXAccessControlList.h"
 
 #include "cmCPackGenerator.h"
-
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmWIXAccessControlList::cmWIXAccessControlList(
@@ -48,8 +48,7 @@
     user = user_and_domain;
   }
 
-  std::vector<std::string> permissions =
-    cmSystemTools::tokenize(permission_string, ",");
+  std::vector<std::string> permissions = cmTokenize(permission_string, ",");
 
   this->SourceWriter.BeginElement("Permission");
   this->SourceWriter.AddAttribute("User", user);
@@ -57,8 +56,7 @@
     this->SourceWriter.AddAttribute("Domain", domain);
   }
   for (std::string const& permission : permissions) {
-    this->EmitBooleanAttribute(entry,
-                               cmSystemTools::TrimWhitespace(permission));
+    this->EmitBooleanAttribute(entry, cmTrimWhitespace(permission));
   }
   this->SourceWriter.EndElement("Permission");
 }
diff --git a/Source/CPack/WiX/cmWIXAccessControlList.h b/Source/CPack/WiX/cmWIXAccessControlList.h
index 2a23f2f..64f9a13 100644
--- a/Source/CPack/WiX/cmWIXAccessControlList.h
+++ b/Source/CPack/WiX/cmWIXAccessControlList.h
@@ -3,10 +3,9 @@
 #ifndef cmWIXAccessControlList_h
 #define cmWIXAccessControlList_h
 
-#include "cmWIXSourceWriter.h"
-
 #include "cmCPackLog.h"
 #include "cmInstalledFile.h"
+#include "cmWIXSourceWriter.h"
 
 class cmWIXAccessControlList
 {
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
index 975dffb..0a83ca2 100644
--- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
@@ -14,10 +14,12 @@
   BeginElement("Directory");
   AddAttribute("Id", "ProgramMenuFolder");
 
-  BeginElement("Directory");
-  AddAttribute("Id", "PROGRAM_MENU_FOLDER");
-  AddAttribute("Name", startMenuFolder);
-  EndElement("Directory");
+  if (startMenuFolder != ".") {
+    BeginElement("Directory");
+    AddAttribute("Id", "PROGRAM_MENU_FOLDER");
+    AddAttribute("Name", startMenuFolder);
+    EndElement("Directory");
+  }
 
   EndElement("Directory");
 }
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
index 8233331..a907d6d 100644
--- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
+++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
@@ -3,11 +3,10 @@
 #ifndef cmWIXDirectoriesSourceWriter_h
 #define cmWIXDirectoriesSourceWriter_h
 
-#include "cmWIXSourceWriter.h"
+#include <string>
 
 #include "cmCPackGenerator.h"
-
-#include <string>
+#include "cmWIXSourceWriter.h"
 
 /** \class cmWIXDirectoriesSourceWriter
  * \brief Helper class to generate directories.wxs
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
index e751ca7..e03e87b 100644
--- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
+++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
@@ -3,11 +3,10 @@
 #ifndef cmWIXFeaturesSourceWriter_h
 #define cmWIXFeaturesSourceWriter_h
 
+#include "cmCPackGenerator.h"
 #include "cmWIXPatch.h"
 #include "cmWIXSourceWriter.h"
 
-#include "cmCPackGenerator.h"
-
 /** \class cmWIXFeaturesSourceWriter
  * \brief Helper class to generate features.wxs
  */
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
index dd3caf9..c0d879a 100644
--- a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
@@ -2,16 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWIXFilesSourceWriter.h"
 
-#include "cmWIXAccessControlList.h"
-
-#include "cmInstalledFile.h"
-
-#include "cmSystemTools.h"
-#include "cmUuid.h"
-
 #include "cm_sys_stat.h"
 
 #include "cmCMakeToWixPath.h"
+#include "cmInstalledFile.h"
+#include "cmSystemTools.h"
+#include "cmUuid.h"
+#include "cmWIXAccessControlList.h"
 
 cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger,
                                                std::string const& filename,
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h
index dc9c636..8cc98f5 100644
--- a/Source/CPack/WiX/cmWIXFilesSourceWriter.h
+++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h
@@ -3,12 +3,10 @@
 #ifndef cmWIXFilesSourceWriter_h
 #define cmWIXFilesSourceWriter_h
 
-#include "cmWIXSourceWriter.h"
-
+#include "cmCPackGenerator.h"
 #include "cmWIXPatch.h"
 #include "cmWIXShortcut.h"
-
-#include "cmCPackGenerator.h"
+#include "cmWIXSourceWriter.h"
 
 /** \class cmWIXFilesSourceWriter
  * \brief Helper class to generate files.wxs
diff --git a/Source/CPack/WiX/cmWIXPatch.h b/Source/CPack/WiX/cmWIXPatch.h
index a4c9e71..31a60f4 100644
--- a/Source/CPack/WiX/cmWIXPatch.h
+++ b/Source/CPack/WiX/cmWIXPatch.h
@@ -3,11 +3,11 @@
 #ifndef cmWIXPatch_h
 #define cmWIXPatch_h
 
+#include <string>
+
 #include "cmWIXPatchParser.h"
 #include "cmWIXSourceWriter.h"
 
-#include <string>
-
 /** \class cmWIXPatch
  * \brief Class that maintains and applies patch fragments
  */
diff --git a/Source/CPack/WiX/cmWIXPatchParser.cxx b/Source/CPack/WiX/cmWIXPatchParser.cxx
index c6ca944..fd9103b 100644
--- a/Source/CPack/WiX/cmWIXPatchParser.cxx
+++ b/Source/CPack/WiX/cmWIXPatchParser.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWIXPatchParser.h"
 
-#include "cmCPackGenerator.h"
-
 #include "cm_expat.h"
 
+#include "cmCPackGenerator.h"
+
 cmWIXPatchNode::Type cmWIXPatchText::type()
 {
   return cmWIXPatchNode::TEXT;
diff --git a/Source/CPack/WiX/cmWIXPatchParser.h b/Source/CPack/WiX/cmWIXPatchParser.h
index 52c7e35..87dd892 100644
--- a/Source/CPack/WiX/cmWIXPatchParser.h
+++ b/Source/CPack/WiX/cmWIXPatchParser.h
@@ -3,13 +3,12 @@
 #ifndef cmCPackWIXPatchParser_h
 #define cmCPackWIXPatchParser_h
 
-#include "cmCPackLog.h"
-
-#include "cmXMLParser.h"
-
 #include <map>
 #include <vector>
 
+#include "cmCPackLog.h"
+#include "cmXMLParser.h"
+
 struct cmWIXPatchNode
 {
   enum Type
@@ -36,8 +35,8 @@
 
   ~cmWIXPatchElement();
 
-  typedef std::vector<cmWIXPatchNode*> child_list_t;
-  typedef std::map<std::string, std::string> attributes_t;
+  using child_list_t = std::vector<cmWIXPatchNode*>;
+  using attributes_t = std::map<std::string, std::string>;
 
   std::string name;
   child_list_t children;
@@ -50,7 +49,7 @@
 class cmWIXPatchParser : public cmXMLParser
 {
 public:
-  typedef std::map<std::string, cmWIXPatchElement> fragment_map_t;
+  using fragment_map_t = std::map<std::string, cmWIXPatchElement>;
 
   cmWIXPatchParser(fragment_map_t& Fragments, cmCPackLog* logger);
 
diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
index 2c99a22..751f7dc 100644
--- a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
+++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
@@ -25,7 +25,7 @@
 
 void cmWIXRichTextFormatWriter::AddText(std::string const& text)
 {
-  typedef unsigned char rtf_byte_t;
+  using rtf_byte_t = unsigned char;
 
   for (size_t i = 0; i < text.size(); ++i) {
     rtf_byte_t c = rtf_byte_t(text[i]);
diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h
index 21be8ee..a879f3d 100644
--- a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h
+++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h
@@ -5,9 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/FStream.hxx"
 #include <string>
 
+#include "cmsys/FStream.hxx"
+
 /** \class cmWIXRichtTextFormatWriter
  * \brief Helper class to generate Rich Text Format (RTF) documents
  * from plain text (e.g. for license and welcome text)
diff --git a/Source/CPack/WiX/cmWIXShortcut.h b/Source/CPack/WiX/cmWIXShortcut.h
index 23ddc6a..c67baf3 100644
--- a/Source/CPack/WiX/cmWIXShortcut.h
+++ b/Source/CPack/WiX/cmWIXShortcut.h
@@ -3,13 +3,13 @@
 #ifndef cmWIXShortcut_h
 #define cmWIXShortcut_h
 
-#include "cmInstalledFile.h"
-
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmInstalledFile.h"
+
 class cmWIXFilesSourceWriter;
 
 struct cmWIXShortcut
@@ -28,8 +28,8 @@
     STARTUP
   };
 
-  typedef std::vector<cmWIXShortcut> shortcut_list_t;
-  typedef std::map<std::string, shortcut_list_t> shortcut_id_map_t;
+  using shortcut_list_t = std::vector<cmWIXShortcut>;
+  using shortcut_id_map_t = std::map<std::string, shortcut_list_t>;
 
   void insert(Type type, std::string const& id, cmWIXShortcut const& shortcut);
 
@@ -46,7 +46,7 @@
                             cmInstalledFile const& installedFile);
 
 private:
-  typedef std::map<Type, shortcut_id_map_t> shortcut_type_map_t;
+  using shortcut_type_map_t = std::map<Type, shortcut_id_map_t>;
 
   void CreateFromProperty(std::string const& propertyName, Type type,
                           std::string const& id,
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx
index 6adf80b..8e9bfdf 100644
--- a/Source/CPack/WiX/cmWIXSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx
@@ -2,12 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWIXSourceWriter.h"
 
-#include "cmCPackGenerator.h"
-
-#include "cmUuid.h"
-
 #include <windows.h>
 
+#include "cmCPackGenerator.h"
+#include "cmUuid.h"
+
 cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger,
                                      std::string const& filename,
                                      GuidType componentGuidType,
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h
index 4af1ed6..8cc2070 100644
--- a/Source/CPack/WiX/cmWIXSourceWriter.h
+++ b/Source/CPack/WiX/cmWIXSourceWriter.h
@@ -3,12 +3,12 @@
 #ifndef cmWIXSourceWriter_h
 #define cmWIXSourceWriter_h
 
-#include "cmCPackLog.h"
+#include <string>
+#include <vector>
 
 #include "cmsys/FStream.hxx"
 
-#include <string>
-#include <vector>
+#include "cmCPackLog.h"
 
 /** \class cmWIXSourceWriter
  * \brief Helper class to generate XML WiX source files
diff --git a/Source/CPack/cmCPack7zGenerator.cxx b/Source/CPack/cmCPack7zGenerator.cxx
deleted file mode 100644
index 7413770..0000000
--- a/Source/CPack/cmCPack7zGenerator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPack7zGenerator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPack7zGenerator::cmCPack7zGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip")
-{
-}
-
-cmCPack7zGenerator::~cmCPack7zGenerator() = default;
diff --git a/Source/CPack/cmCPack7zGenerator.h b/Source/CPack/cmCPack7zGenerator.h
deleted file mode 100644
index 8af4c4a..0000000
--- a/Source/CPack/cmCPack7zGenerator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPack7zGenerator_h
-#define cmCPack7zGenerator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPack7zGenerator
- * \brief A generator for 7z files
- */
-class cmCPack7zGenerator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPack7zGenerator, cmCPackArchiveGenerator);
-
-  /**
-   * Construct generator
-   */
-  cmCPack7zGenerator();
-  ~cmCPack7zGenerator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".7z"; }
-};
-
-#endif
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx
index 98fb29d..43f2946 100644
--- a/Source/CPack/cmCPackArchiveGenerator.cxx
+++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -2,24 +2,68 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackArchiveGenerator.h"
 
-#include "cmCPackComponentGroup.h"
-#include "cmCPackGenerator.h"
-#include "cmCPackLog.h"
-#include "cmGeneratedFileStream.h"
-#include "cmSystemTools.h"
-#include "cmWorkingDirectory.h"
-
 #include <cstring>
 #include <map>
 #include <ostream>
 #include <utility>
 #include <vector>
 
-cmCPackArchiveGenerator::cmCPackArchiveGenerator(cmArchiveWrite::Compress t,
-                                                 std::string const& format)
+#include "cmCPackComponentGroup.h"
+#include "cmCPackGenerator.h"
+#include "cmCPackLog.h"
+#include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmWorkingDirectory.h"
+
+cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
 {
-  this->Compress = t;
-  this->ArchiveFormat = format;
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip",
+                                     ".7z");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateTBZ2Generator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr",
+                                     ".tar.bz2");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateTGZGenerator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr",
+                                     ".tar.gz");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateTXZGenerator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr",
+                                     ".tar.xz");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateTZGenerator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, "paxr",
+                                     ".tar.Z");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateTZSTGenerator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressZstd, "paxr",
+                                     ".tar.zst");
+}
+
+cmCPackGenerator* cmCPackArchiveGenerator::CreateZIPGenerator()
+{
+  return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "zip",
+                                     ".zip");
+}
+
+cmCPackArchiveGenerator::cmCPackArchiveGenerator(
+  cmArchiveWrite::Compress compress, std::string format, std::string extension)
+  : Compress(compress)
+  , ArchiveFormat(std::move(format))
+  , OutputExtension(std::move(extension))
+{
 }
 
 cmCPackArchiveGenerator::~cmCPackArchiveGenerator() = default;
@@ -71,8 +115,7 @@
   }
   std::string filePrefix;
   if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
-    filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME");
-    filePrefix += "/";
+    filePrefix = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
   }
   const char* installPrefix =
     this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h
index 9983854..8d67720 100644
--- a/Source/CPack/cmCPackArchiveGenerator.h
+++ b/Source/CPack/cmCPackArchiveGenerator.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmArchiveWrite.h"
-#include "cmCPackGenerator.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmArchiveWrite.h"
+#include "cmCPackGenerator.h"
+
 class cmCPackComponent;
 
 /** \class cmCPackArchiveGenerator
@@ -22,12 +22,21 @@
 class cmCPackArchiveGenerator : public cmCPackGenerator
 {
 public:
-  typedef cmCPackGenerator Superclass;
+  using Superclass = cmCPackGenerator;
+
+  static cmCPackGenerator* Create7ZGenerator();
+  static cmCPackGenerator* CreateTBZ2Generator();
+  static cmCPackGenerator* CreateTGZGenerator();
+  static cmCPackGenerator* CreateTXZGenerator();
+  static cmCPackGenerator* CreateTZGenerator();
+  static cmCPackGenerator* CreateTZSTGenerator();
+  static cmCPackGenerator* CreateZIPGenerator();
 
   /**
    * Construct generator
    */
-  cmCPackArchiveGenerator(cmArchiveWrite::Compress, std::string const& format);
+  cmCPackArchiveGenerator(cmArchiveWrite::Compress t, std::string format,
+                          std::string extension);
   ~cmCPackArchiveGenerator() override;
   // Used to add a header to the archive
   virtual int GenerateHeader(std::ostream* os);
@@ -68,9 +77,19 @@
    * components will be put in a single installer.
    */
   int PackageComponentsAllInOne();
-  const char* GetOutputExtension() override = 0;
+
+private:
+  const char* GetNameOfClass() override { return "cmCPackArchiveGenerator"; }
+
+  const char* GetOutputExtension() override
+  {
+    return this->OutputExtension.c_str();
+  }
+
+private:
   cmArchiveWrite::Compress Compress;
   std::string ArchiveFormat;
+  std::string OutputExtension;
 };
 
 #endif
diff --git a/Source/CPack/cmCPackBundleGenerator.cxx b/Source/CPack/cmCPackBundleGenerator.cxx
index 3a476f4..4d5f43f 100644
--- a/Source/CPack/cmCPackBundleGenerator.cxx
+++ b/Source/CPack/cmCPackBundleGenerator.cxx
@@ -6,6 +6,7 @@
 #include <vector>
 
 #include "cmCPackLog.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmCPackBundleGenerator::cmCPackBundleGenerator() = default;
@@ -40,9 +41,8 @@
 
 const char* cmCPackBundleGenerator::GetPackagingInstallPrefix()
 {
-  this->InstallPrefix = "/";
-  this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME");
-  this->InstallPrefix += ".app/Contents/Resources";
+  this->InstallPrefix = cmStrCat('/', this->GetOption("CPACK_BUNDLE_NAME"),
+                                 ".app/Contents/Resources");
 
   return this->InstallPrefix.c_str();
 }
@@ -89,11 +89,10 @@
 
   // The staging directory contains everything that will end-up inside the
   // final disk image ...
-  std::ostringstream staging;
-  staging << toplevel;
+  std::string const staging = toplevel;
 
   std::ostringstream contents;
-  contents << staging.str() << "/" << cpack_bundle_name << ".app/"
+  contents << staging << "/" << cpack_bundle_name << ".app/"
            << "Contents";
 
   std::ostringstream application;
@@ -190,9 +189,8 @@
   if (!cpack_apple_cert_app.empty()) {
     std::string output;
     std::string bundle_path;
-    bundle_path = src_dir + "/";
-    bundle_path += this->GetOption("CPACK_BUNDLE_NAME");
-    bundle_path += ".app";
+    bundle_path =
+      cmStrCat(src_dir, '/', this->GetOption("CPACK_BUNDLE_NAME"), ".app");
 
     // A list of additional files to sign, ie. frameworks and plugins.
     const std::string sign_parameter =
@@ -205,8 +203,7 @@
       ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES")
       : "";
 
-    std::vector<std::string> relFiles;
-    cmSystemTools::ExpandListArgument(sign_files, relFiles);
+    std::vector<std::string> relFiles = cmExpandedList(sign_files);
 
     // sign the files supplied by the user, ie. frameworks.
     for (auto const& file : relFiles) {
diff --git a/Source/CPack/cmCPackComponentGroup.cxx b/Source/CPack/cmCPackComponentGroup.cxx
index f888a5f..d40e5fc 100644
--- a/Source/CPack/cmCPackComponentGroup.cxx
+++ b/Source/CPack/cmCPackComponentGroup.cxx
@@ -2,10 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackComponentGroup.h"
 
-#include "cmSystemTools.h"
-
 #include <string>
 
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 unsigned long cmCPackComponent::GetInstalledSize(
   const std::string& installDir) const
 {
@@ -14,9 +15,7 @@
   }
 
   for (std::string const& file : this->Files) {
-    std::string path = installDir;
-    path += '/';
-    path += file;
+    std::string path = cmStrCat(installDir, '/', file);
     this->TotalSize += cmSystemTools::FileLength(path);
   }
 
diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
index 49a9f15..b5abd5a 100644
--- a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackCygwinBinaryGenerator.h"
 
+#include "cmsys/SystemTools.hxx"
+
 #include "cmCPackLog.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
@@ -9,9 +11,8 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include "cmsys/SystemTools.hxx"
-
 cmCPackCygwinBinaryGenerator::cmCPackCygwinBinaryGenerator()
+  : cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr", ".tar.bz2")
 {
 }
 
@@ -28,13 +29,11 @@
 
 int cmCPackCygwinBinaryGenerator::PackageFiles()
 {
-  std::string packageName = this->GetOption("CPACK_PACKAGE_NAME");
-  packageName += "-";
-  packageName += this->GetOption("CPACK_PACKAGE_VERSION");
+  std::string packageName =
+    cmStrCat(this->GetOption("CPACK_PACKAGE_NAME"), '-',
+             this->GetOption("CPACK_PACKAGE_VERSION"));
   packageName = cmsys::SystemTools::LowerCase(packageName);
-  std::string manifest = "/usr/share/doc/";
-  manifest += packageName;
-  manifest += "/MANIFEST";
+  std::string manifest = cmStrCat("/usr/share/doc/", packageName, "/MANIFEST");
   std::string manifestFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
   // Create a MANIFEST file that contains all of the files in
   // the tar file
diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.h b/Source/CPack/cmCPackCygwinBinaryGenerator.h
index f87a134..47bd41e 100644
--- a/Source/CPack/cmCPackCygwinBinaryGenerator.h
+++ b/Source/CPack/cmCPackCygwinBinaryGenerator.h
@@ -3,15 +3,15 @@
 #ifndef cmCPackCygwinBinaryGenerator_h
 #define cmCPackCygwinBinaryGenerator_h
 
-#include "cmCPackTarBZip2Generator.h"
+#include "cmCPackArchiveGenerator.h"
 
 /** \class cmCPackCygwinBinaryGenerator
  * \brief A generator for TarBZip2 files
  */
-class cmCPackCygwinBinaryGenerator : public cmCPackTarBZip2Generator
+class cmCPackCygwinBinaryGenerator : public cmCPackArchiveGenerator
 {
 public:
-  cmCPackTypeMacro(cmCPackCygwinBinaryGenerator, cmCPackTarBZip2Generator);
+  cmCPackTypeMacro(cmCPackCygwinBinaryGenerator, cmCPackArchiveGenerator);
 
   /**
    * Construct generator
diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.cxx b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
index 889f29a..64a88eb 100644
--- a/Source/CPack/cmCPackCygwinSourceGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackCygwinSourceGenerator.h"
 
+#include "cmsys/SystemTools.hxx"
+
 #include "cmCPackLog.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
@@ -9,17 +11,17 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include "cmsys/SystemTools.hxx"
-
 // Includes needed for implementation of RenameFile.  This is not in
 // system tools because it is not implemented robustly enough to move
 // files across directories.
 #ifdef _WIN32
-#  include "cm_sys_stat.h"
 #  include <windows.h>
+
+#  include "cm_sys_stat.h"
 #endif
 
 cmCPackCygwinSourceGenerator::cmCPackCygwinSourceGenerator()
+  : cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr", ".tar.bz2")
 {
 }
 
@@ -37,15 +39,11 @@
 {
   // Create a tar file of the sources
   std::string packageDirFileName =
-    this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-  packageDirFileName += ".tar.bz2";
+    cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), ".tar.bz2");
   packageFileNames[0] = packageDirFileName;
   std::string output;
-  // skip one parent up to the cmCPackTarBZip2Generator
-  // to create tar.bz2 file with the list of source
-  // files
-  this->Compress = cmArchiveWrite::CompressBZip2;
-  if (!this->cmCPackTarBZip2Generator::PackageFiles()) {
+  // create tar.bz2 file with the list of source files
+  if (!this->cmCPackArchiveGenerator::PackageFiles()) {
     return 0;
   }
   // Now create a tar file that contains the above .tar.bz2 file
@@ -94,8 +92,8 @@
                     << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "]\n");
     return 0;
   }
-  std::string outerTarFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-  outerTarFile += "-";
+  std::string outerTarFile =
+    cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), '-');
   const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
   if (!patch) {
     cmCPackLogger(cmCPackLog::LOG_WARNING,
@@ -106,19 +104,18 @@
   outerTarFile += patch;
   outerTarFile += "-src.tar.bz2";
   std::string tmpDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  std::string buildScript = tmpDir;
-  buildScript += "/";
-  buildScript += cmSystemTools::GetFilenameName(
-    this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT"));
-  std::string patchFile = tmpDir;
-  patchFile += "/";
-  patchFile +=
-    cmSystemTools::GetFilenameName(this->GetOption("CPACK_CYGWIN_PATCH_FILE"));
+  std::string buildScript =
+    cmStrCat(tmpDir, '/',
+             cmSystemTools::GetFilenameName(
+               this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT")));
+  std::string patchFile =
+    cmStrCat(tmpDir, '/',
+             cmSystemTools::GetFilenameName(
+               this->GetOption("CPACK_CYGWIN_PATCH_FILE")));
 
   std::string file = cmSystemTools::GetFilenameName(compressOutFile);
-  std::string sourceTar = cmSystemTools::GetFilenamePath(compressOutFile);
-  sourceTar += "/";
-  sourceTar += file;
+  std::string sourceTar =
+    cmStrCat(cmSystemTools::GetFilenamePath(compressOutFile), '/', file);
   /* reset list of file to be packaged */
   files.clear();
   // a source release in cygwin should have the build script used
@@ -132,7 +129,7 @@
   packageFileNames[0] = outerTarFile;
   /* update the toplevel dir */
   toplevel = tmpDir;
-  if (!this->cmCPackTarBZip2Generator::PackageFiles()) {
+  if (!this->cmCPackArchiveGenerator::PackageFiles()) {
     return 0;
   }
   return 1;
@@ -140,8 +137,8 @@
 
 const char* cmCPackCygwinSourceGenerator::GetPackagingInstallPrefix()
 {
-  this->InstallPrefix = "/";
-  this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME");
+  this->InstallPrefix =
+    cmStrCat('/', this->GetOption("CPACK_PACKAGE_FILE_NAME"));
   return this->InstallPrefix.c_str();
 }
 
diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.h b/Source/CPack/cmCPackCygwinSourceGenerator.h
index a909b15..98d8f0a 100644
--- a/Source/CPack/cmCPackCygwinSourceGenerator.h
+++ b/Source/CPack/cmCPackCygwinSourceGenerator.h
@@ -3,15 +3,15 @@
 #ifndef cmCPackCygwinSourceGenerator_h
 #define cmCPackCygwinSourceGenerator_h
 
-#include "cmCPackTarBZip2Generator.h"
+#include "cmCPackArchiveGenerator.h"
 
 /** \class cmCPackCygwinSourceGenerator
  * \brief A generator for cygwin source files
  */
-class cmCPackCygwinSourceGenerator : public cmCPackTarBZip2Generator
+class cmCPackCygwinSourceGenerator : public cmCPackArchiveGenerator
 {
 public:
-  cmCPackTypeMacro(cmCPackCygwinSourceGenerator, cmCPackTarBZip2Generator);
+  cmCPackTypeMacro(cmCPackCygwinSourceGenerator, cmCPackArchiveGenerator);
 
   /**
    * Construct generator
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx
index cfb5efd..5b7d8fb 100644
--- a/Source/CPack/cmCPackDebGenerator.cxx
+++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -2,21 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackDebGenerator.h"
 
+#include <cstring>
+#include <map>
+#include <ostream>
+#include <set>
+#include <utility>
+
+#include "cmsys/Glob.hxx"
+
+#include "cm_sys_stat.h"
+
 #include "cmArchiveWrite.h"
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmCryptoHash.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cm_sys_stat.h"
-
-#include "cmsys/Glob.hxx"
-#include <map>
-#include <ostream>
-#include <set>
-#include <string.h>
-#include <utility>
 
 namespace {
 
@@ -148,8 +151,7 @@
 
   unsigned long totalSize = 0;
   {
-    std::string dirName = TemporaryDir;
-    dirName += '/';
+    std::string dirName = cmStrCat(TemporaryDir, '/');
     for (std::string const& file : PackageFiles) {
       totalSize += cmSystemTools::FileLength(file);
     }
@@ -247,8 +249,7 @@
 
   cmGeneratedFileStream out(md5filename);
 
-  std::string topLevelWithTrailingSlash = TemporaryDir;
-  topLevelWithTrailingSlash += '/';
+  std::string topLevelWithTrailingSlash = cmStrCat(TemporaryDir, '/');
   for (std::string const& file : PackageFiles) {
     // hash only regular files
     if (cmSystemTools::FileIsDirectory(file) ||
@@ -377,8 +378,7 @@
     // default
     control_tar.ClearPermissions();
 
-    std::vector<std::string> controlExtraList;
-    cmSystemTools::ExpandListArgument(ControlExtra, controlExtraList);
+    std::vector<std::string> controlExtraList = cmExpandedList(ControlExtra);
     for (std::string const& i : controlExtraList) {
       std::string filenamename = cmsys::SystemTools::GetFilenameName(i);
       std::string localcopy = WorkDir + "/" + filenamename;
@@ -439,7 +439,7 @@
 int cmCPackDebGenerator::InitializeInternal()
 {
   this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
-  if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
+  if (cmIsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
     this->SetOption("CPACK_SET_DESTDIR", "I_ON");
   }
   return this->Superclass::InitializeInternal();
@@ -468,8 +468,7 @@
   // Tell CPackDeb.cmake the name of the component GROUP.
   this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName.c_str());
   // Tell CPackDeb.cmake the path where the component is.
-  std::string component_path = "/";
-  component_path += packageName;
+  std::string component_path = cmStrCat('/', packageName);
   this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
                   component_path.c_str());
   if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
@@ -499,9 +498,8 @@
     retval = 0;
   }
   // add the generated package to package file names list
-  packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  packageFileName += "/";
-  packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME");
+  packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
+                             this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
   packageFileNames.push_back(std::move(packageFileName));
 
   if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE")) {
@@ -523,9 +521,9 @@
       retval = 0;
     }
     // add the generated package to package file names list
-    packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    packageFileName += "/";
-    packageFileName += this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME");
+    packageFileName =
+      cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
+               this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"));
     packageFileNames.push_back(std::move(packageFileName));
   }
 
@@ -613,8 +611,7 @@
 
   if (!compInstDirName.empty()) {
     // Tell CPackDeb.cmake the path where the component is.
-    std::string component_path = "/";
-    component_path += compInstDirName;
+    std::string component_path = cmStrCat('/', compInstDirName);
     this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
                     component_path.c_str());
   }
@@ -643,9 +640,8 @@
     retval = 0;
   }
   // add the generated package to package file names list
-  packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  packageFileName += "/";
-  packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME");
+  packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
+                             this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
   packageFileNames.push_back(std::move(packageFileName));
   return retval;
 }
diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h
index 2244fe7..ce77e08 100644
--- a/Source/CPack/cmCPackDebGenerator.h
+++ b/Source/CPack/cmCPackDebGenerator.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackGenerator.h"
-
 #include <string>
 #include <vector>
 
+#include "cmCPackGenerator.h"
+
 /** \class cmCPackDebGenerator
  * \brief A generator for Debian packages
  *
diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx
index 7a3742b..ea71007 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.cxx
+++ b/Source/CPack/cmCPackDragNDropGenerator.cxx
@@ -2,21 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackDragNDropGenerator.h"
 
+#include <algorithm>
+#include <cstdlib>
+#include <iomanip>
+#include <map>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <iomanip>
-#include <map>
-#include <stdlib.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
 #ifdef HAVE_CoreServices
 // For the old LocaleStringToLangAndRegionCodes() function, to convert
 // to the old Script Manager RegionCode values needed for the 'LPic' data
@@ -127,9 +129,8 @@
       return 0;
     }
 
-    std::vector<std::string> languages;
-    cmSystemTools::ExpandListArgument(
-      this->GetOption("CPACK_DMG_SLA_LANGUAGES"), languages);
+    std::vector<std::string> languages =
+      cmExpandedList(this->GetOption("CPACK_DMG_SLA_LANGUAGES"));
     if (languages.empty()) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "CPACK_DMG_SLA_LANGUAGES set but empty" << std::endl);
@@ -196,9 +197,7 @@
     full_package_name += std::string(GetOutputExtension());
     packageFileNames.push_back(full_package_name);
 
-    std::string src_dir = toplevel;
-    src_dir += "/";
-    src_dir += package_file;
+    std::string src_dir = cmStrCat(toplevel, '/', package_file);
 
     if (0 == this->CreateDMG(src_dir, full_package_name)) {
       return 0;
@@ -409,8 +408,8 @@
   }
 
   // Create a temporary read-write disk image ...
-  std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  temp_image += "/temp.dmg";
+  std::string temp_image =
+    cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/temp.dmg");
 
   std::string create_error;
   std::ostringstream temp_image_command;
@@ -522,12 +521,12 @@
   if (!cpack_license_file.empty() || !slaDirectory.empty()) {
     // Use old hardcoded style if sla_dir is not set
     bool oldStyle = slaDirectory.empty();
-    std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    sla_r += "/sla.r";
+    std::string sla_r =
+      cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/sla.r");
 
     std::vector<std::string> languages;
     if (!oldStyle) {
-      cmSystemTools::ExpandListArgument(cpack_dmg_languages, languages);
+      cmExpandList(cpack_dmg_languages, languages);
     }
 
     cmGeneratedFileStream ofs(sla_r);
@@ -649,8 +648,8 @@
     if (temp_image_format != "UDZO") {
       temp_image_format = "UDZO";
       // convert to UDZO to enable unflatten/flatten
-      std::string temp_udzo = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-      temp_udzo += "/temp-udzo.dmg";
+      std::string temp_udzo = cmStrCat(
+        this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/temp-udzo.dmg");
 
       std::ostringstream udco_image_command;
       udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h
index d8c5c83..f8c86c0 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.h
+++ b/Source/CPack/cmCPackDragNDropGenerator.h
@@ -6,10 +6,11 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <sstream>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
 #include "cmCPackGenerator.h"
 
 class cmGeneratedFileStream;
diff --git a/Source/CPack/cmCPackExternalGenerator.cxx b/Source/CPack/cmCPackExternalGenerator.cxx
index 9dc9853..142eb6f 100644
--- a/Source/CPack/cmCPackExternalGenerator.cxx
+++ b/Source/CPack/cmCPackExternalGenerator.cxx
@@ -2,20 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackExternalGenerator.h"
 
-#include "cmAlgorithms.h"
-#include "cmCPackComponentGroup.h"
-#include "cmCPackLog.h"
-#include "cmMakefile.h"
-#include "cmSystemTools.h"
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
 
 #include "cm_jsoncpp_value.h"
 #include "cm_jsoncpp_writer.h"
 
-#include "cmsys/FStream.hxx"
-
-#include <map>
-#include <utility>
-#include <vector>
+#include "cmCPackComponentGroup.h"
+#include "cmCPackLog.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 int cmCPackExternalGenerator::InitializeInternal()
 {
@@ -148,8 +150,7 @@
 
 bool cmCPackExternalGenerator::StagingEnabled() const
 {
-  return !cmSystemTools::IsOff(
-    this->GetOption("CPACK_EXTERNAL_ENABLE_STAGING"));
+  return !cmIsOff(this->GetOption("CPACK_EXTERNAL_ENABLE_STAGING"));
 }
 
 cmCPackExternalGenerator::cmCPackExternalVersionGenerator::
@@ -207,8 +208,7 @@
   if (defaultDirectoryPermissions && *defaultDirectoryPermissions) {
     root["defaultDirectoryPermissions"] = defaultDirectoryPermissions;
   }
-  if (cmSystemTools::IsInternallyOn(
-        this->Parent->GetOption("CPACK_SET_DESTDIR"))) {
+  if (cmIsInternallyOn(this->Parent->GetOption("CPACK_SET_DESTDIR"))) {
     root["setDestdir"] = true;
     root["packagingInstallPrefix"] =
       this->Parent->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
@@ -216,8 +216,7 @@
     root["setDestdir"] = false;
   }
 
-  root["stripFiles"] =
-    !cmSystemTools::IsOff(this->Parent->GetOption("CPACK_STRIP_FILES"));
+  root["stripFiles"] = !cmIsOff(this->Parent->GetOption("CPACK_STRIP_FILES"));
   root["warnOnAbsoluteInstallDestination"] =
     this->Parent->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION");
   root["errorOnAbsoluteInstallDestination"] =
diff --git a/Source/CPack/cmCPackExternalGenerator.h b/Source/CPack/cmCPackExternalGenerator.h
index 176d6a9..80011fd 100644
--- a/Source/CPack/cmCPackExternalGenerator.h
+++ b/Source/CPack/cmCPackExternalGenerator.h
@@ -3,12 +3,13 @@
 #ifndef cmCPackExternalGenerator_h
 #define cmCPackExternalGenerator_h
 
-#include "cmCPackGenerator.h"
-#include "cm_sys_stat.h"
-
 #include <memory>
 #include <string>
 
+#include "cm_sys_stat.h"
+
+#include "cmCPackGenerator.h"
+
 class cmGlobalGenerator;
 namespace Json {
 class Value;
diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx
index 9fdafa4..e3cc352 100644
--- a/Source/CPack/cmCPackFreeBSDGenerator.cxx
+++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx
@@ -6,21 +6,22 @@
 #include "cmCPackArchiveGenerator.h"
 #include "cmCPackLog.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 // Needed for ::open() and ::stat()
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
+#include <algorithm>
+#include <ostream>
+#include <utility>
+#include <vector>
 
+#include <fcntl.h>
 #include <pkg.h>
 
-#include <algorithm>
-#include <utility>
+#include <sys/stat.h>
 
 cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr")
+  : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr", ".txz")
 {
 }
 
@@ -130,7 +131,7 @@
 class ManifestKeyListValue : public ManifestKey
 {
 public:
-  typedef std::vector<std::string> VList;
+  using VList = std::vector<std::string>;
   VList value;
 
   ManifestKeyListValue(const std::string& k)
@@ -227,9 +228,8 @@
   manifest << ManifestKeyValue(
     "desc", var_lookup("CPACK_FREEBSD_PACKAGE_DESCRIPTION"));
   manifest << ManifestKeyValue("www", var_lookup("CPACK_FREEBSD_PACKAGE_WWW"));
-  std::vector<std::string> licenses;
-  cmSystemTools::ExpandListArgument(
-    var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE"), licenses);
+  std::vector<std::string> licenses =
+    cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE"));
   std::string licenselogic("single");
   if (licenses.empty()) {
     cmSystemTools::SetFatalErrorOccured();
@@ -238,14 +238,12 @@
   }
   manifest << ManifestKeyValue("licenselogic", licenselogic);
   manifest << (ManifestKeyListValue("licenses") << licenses);
-  std::vector<std::string> categories;
-  cmSystemTools::ExpandListArgument(
-    var_lookup("CPACK_FREEBSD_PACKAGE_CATEGORIES"), categories);
+  std::vector<std::string> categories =
+    cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_CATEGORIES"));
   manifest << (ManifestKeyListValue("categories") << categories);
   manifest << ManifestKeyValue("prefix", var_lookup("CMAKE_INSTALL_PREFIX"));
-  std::vector<std::string> deps;
-  cmSystemTools::ExpandListArgument(var_lookup("CPACK_FREEBSD_PACKAGE_DEPS"),
-                                    deps);
+  std::vector<std::string> deps =
+    cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_DEPS"));
   if (!deps.empty()) {
     manifest << (ManifestKeyDepsValue("deps") << deps);
   }
@@ -278,12 +276,6 @@
   s << "  },\n";
 }
 
-static bool has_suffix(const std::string& str, const std::string& suffix)
-{
-  return str.size() >= suffix.size() &&
-    str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
 int cmCPackFreeBSDGenerator::PackageFiles()
 {
   if (!this->ReadListFile("Internal/CPack/CPackFreeBSD.cmake")) {
@@ -329,13 +321,13 @@
   pkg_create_from_manifest(output_dir.c_str(), ::TXZ, toplevel.c_str(),
                            manifestname.c_str(), nullptr);
 
-  std::string broken_suffix = std::string("-") +
-    var_lookup("CPACK_TOPLEVEL_TAG") + std::string(GetOutputExtension());
+  std::string broken_suffix =
+    cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), ".txz");
   for (std::string& name : packageFileNames) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl);
-    if (has_suffix(name, broken_suffix)) {
+    if (cmHasSuffix(name, broken_suffix)) {
       name.replace(name.size() - broken_suffix.size(), std::string::npos,
-                   GetOutputExtension());
+                   ".txz");
       break;
     }
   }
diff --git a/Source/CPack/cmCPackFreeBSDGenerator.h b/Source/CPack/cmCPackFreeBSDGenerator.h
index 99d2e24..a18b72f 100644
--- a/Source/CPack/cmCPackFreeBSDGenerator.h
+++ b/Source/CPack/cmCPackFreeBSDGenerator.h
@@ -3,7 +3,9 @@
 #ifndef cmCPackFreeBSDGenerator_h
 #define cmCPackFreeBSDGenerator_h
 
-#include <cmConfigure.h>
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
 
 #include "cmCPackArchiveGenerator.h"
 #include "cmCPackGenerator.h"
@@ -28,8 +30,6 @@
   int PackageFiles() override;
 
 protected:
-  const char* GetOutputExtension() override { return ".txz"; }
-
   std::string var_lookup(const char* var_name);
   void write_manifest_fields(cmGeneratedFileStream&);
 };
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 7e07ff4..153f328 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackGenerator.h"
 
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <utility>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <cstring>
-#include <memory> // IWYU pragma: keep
-#include <utility>
 
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
@@ -21,6 +22,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmVersion.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLSafe.h"
@@ -72,8 +74,8 @@
     }
   }
 
-  std::string tempDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
-  tempDirectory += "/_CPack_Packages/";
+  std::string tempDirectory =
+    cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/_CPack_Packages/");
   const char* toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG");
   if (toplevelTag) {
     tempDirectory += toplevelTag;
@@ -179,8 +181,8 @@
   std::string bareTempInstallDirectory =
     this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
   std::string tempInstallDirectoryStr = bareTempInstallDirectory;
-  bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR")) |
-    cmSystemTools::IsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"));
+  bool setDestDir = cmIsOn(this->GetOption("CPACK_SET_DESTDIR")) |
+    cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"));
   if (!setDestDir) {
     tempInstallDirectoryStr += this->GetPackagingInstallPrefix();
   }
@@ -196,8 +198,7 @@
   }
 
   if (setDestDir) {
-    std::string destDir = "DESTDIR=";
-    destDir += tempInstallDirectory;
+    std::string destDir = cmStrCat("DESTDIR=", tempInstallDirectory);
     cmSystemTools::PutEnv(destDir);
   } else {
     // Make sure there is no destdir
@@ -210,8 +211,8 @@
   const char* default_dir_install_permissions =
     this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (default_dir_install_permissions && *default_dir_install_permissions) {
-    std::vector<std::string> items;
-    cmSystemTools::ExpandListArgument(default_dir_install_permissions, items);
+    std::vector<std::string> items =
+      cmExpandedList(default_dir_install_permissions);
     for (const auto& arg : items) {
       if (!cmFSPermissions::stringToModeT(arg, default_dir_mode_v)) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -235,7 +236,7 @@
     return 0;
   }
 
-  // If the CPackConfig file sets CPACK_INSTALL_SCRIPT then run them
+  // If the CPackConfig file sets CPACK_INSTALL_SCRIPT(S) then run them
   // as listed
   if (!this->InstallProjectViaInstallScript(setDestDir,
                                             tempInstallDirectory)) {
@@ -270,11 +271,11 @@
   (void)setDestDir;
   const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
   if (installCommands && *installCommands) {
-    std::string tempInstallDirectoryEnv = "CMAKE_INSTALL_PREFIX=";
-    tempInstallDirectoryEnv += tempInstallDirectory;
+    std::string tempInstallDirectoryEnv =
+      cmStrCat("CMAKE_INSTALL_PREFIX=", tempInstallDirectory);
     cmSystemTools::PutEnv(tempInstallDirectoryEnv);
-    std::vector<std::string> installCommandsVector;
-    cmSystemTools::ExpandListArgument(installCommands, installCommandsVector);
+    std::vector<std::string> installCommandsVector =
+      cmExpandedList(installCommands);
     for (std::string const& ic : installCommandsVector) {
       cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << ic << std::endl);
       std::string output;
@@ -283,8 +284,8 @@
         ic, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
         cmDuration::zero());
       if (!resB || retVal) {
-        std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-        tmpFile += "/InstallOutput.log";
+        std::string tmpFile = cmStrCat(
+          this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/InstallOutput.log");
         cmGeneratedFileStream ofs(tmpFile);
         ofs << "# Run command: " << ic << std::endl
             << "# Output:" << std::endl
@@ -310,9 +311,8 @@
   std::vector<cmsys::RegularExpression> ignoreFilesRegex;
   const char* cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES");
   if (cpackIgnoreFiles) {
-    std::vector<std::string> ignoreFilesRegexString;
-    cmSystemTools::ExpandListArgument(cpackIgnoreFiles,
-                                      ignoreFilesRegexString);
+    std::vector<std::string> ignoreFilesRegexString =
+      cmExpandedList(cpackIgnoreFiles);
     for (std::string const& ifr : ignoreFilesRegexString) {
       cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                     "Create ignore files regex for: " << ifr << std::endl);
@@ -322,9 +322,8 @@
   const char* installDirectories =
     this->GetOption("CPACK_INSTALLED_DIRECTORIES");
   if (installDirectories && *installDirectories) {
-    std::vector<std::string> installDirectoriesVector;
-    cmSystemTools::ExpandListArgument(installDirectories,
-                                      installDirectoriesVector);
+    std::vector<std::string> installDirectoriesVector =
+      cmExpandedList(installDirectories);
     if (installDirectoriesVector.size() % 2 != 0) {
       cmCPackLogger(
         cmCPackLog::LOG_ERROR,
@@ -344,8 +343,7 @@
       std::string top = *it;
       it++;
       std::string subdir = *it;
-      std::string findExpr = top;
-      findExpr += "/*";
+      std::string findExpr = cmStrCat(top, "/*");
       cmCPackLogger(cmCPackLog::LOG_OUTPUT,
                     "- Install directory: " << top << std::endl);
       gl.RecurseOn();
@@ -373,8 +371,8 @@
         if (skip) {
           continue;
         }
-        std::string filePath = tempDir;
-        filePath += "/" + subdir + "/" + cmSystemTools::RelativePath(top, gf);
+        std::string filePath = cmStrCat(tempDir, '/', subdir, '/',
+                                        cmSystemTools::RelativePath(top, gf));
         cmCPackLogger(cmCPackLog::LOG_DEBUG,
                       "Copy file: " << inFile << " -> " << filePath
                                     << std::endl);
@@ -399,8 +397,7 @@
       /* rebuild symlinks in the installed tree */
       if (!symlinkedFiles.empty()) {
         std::string curDir = cmSystemTools::GetCurrentWorkingDirectory();
-        std::string goToDir = tempDir;
-        goToDir += "/" + subdir;
+        std::string goToDir = cmStrCat(tempDir, '/', subdir);
         cmCPackLogger(cmCPackLog::LOG_DEBUG,
                       "Change dir to: " << goToDir << std::endl);
         cmWorkingDirectory workdir(goToDir);
@@ -448,12 +445,23 @@
 int cmCPackGenerator::InstallProjectViaInstallScript(
   bool setDestDir, const std::string& tempInstallDirectory)
 {
-  const char* cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPT");
+  const char* cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPTS");
+  {
+    const char* const cmakeScript = this->GetOption("CPACK_INSTALL_SCRIPT");
+    if (cmakeScript && cmakeScripts) {
+      cmCPackLogger(
+        cmCPackLog::LOG_WARNING,
+        "Both CPACK_INSTALL_SCRIPTS and CPACK_INSTALL_SCRIPT are set, "
+        "the latter will be ignored."
+          << std::endl);
+    } else if (cmakeScript && !cmakeScripts) {
+      cmakeScripts = cmakeScript;
+    }
+  }
   if (cmakeScripts && *cmakeScripts) {
     cmCPackLogger(cmCPackLog::LOG_OUTPUT,
                   "- Install scripts: " << cmakeScripts << std::endl);
-    std::vector<std::string> cmakeScriptsVector;
-    cmSystemTools::ExpandListArgument(cmakeScripts, cmakeScriptsVector);
+    std::vector<std::string> cmakeScriptsVector = cmExpandedList(cmakeScripts);
     for (std::string const& installScript : cmakeScriptsVector) {
 
       cmCPackLogger(cmCPackLog::LOG_OUTPUT,
@@ -517,8 +525,8 @@
                       << std::endl);
       return 0;
     }
-    std::vector<std::string> cmakeProjectsVector;
-    cmSystemTools::ExpandListArgument(cmakeProjects, cmakeProjectsVector);
+    std::vector<std::string> cmakeProjectsVector =
+      cmExpandedList(cmakeProjects);
     std::vector<std::string>::iterator it;
     for (it = cmakeProjectsVector.begin(); it != cmakeProjectsVector.end();
          ++it) {
@@ -562,8 +570,8 @@
           cmSystemTools::UpperCase(project.Component) + "_INSTALL_TYPES";
         const char* installTypes = this->GetOption(installTypesVar);
         if (installTypes && *installTypes) {
-          std::vector<std::string> installTypesVector;
-          cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
+          std::vector<std::string> installTypesVector =
+            cmExpandedList(installTypes);
           for (std::string const& installType : installTypesVector) {
             project.InstallationTypes.push_back(
               this->GetInstallationType(project.ProjectName, installType));
@@ -575,7 +583,7 @@
           "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(project.Component);
         const char* components = this->GetOption(componentsVar);
         if (components && *components) {
-          cmSystemTools::ExpandListArgument(components, componentsVector);
+          cmExpandList(components, componentsVector);
           for (std::string const& comp : componentsVector) {
             project.Components.push_back(
               this->GetComponent(project.ProjectName, comp));
@@ -587,11 +595,37 @@
         componentsVector.push_back(project.Component);
       }
 
-      const char* buildConfigCstr = this->GetOption("CPACK_BUILD_CONFIG");
-      std::string buildConfig = buildConfigCstr ? buildConfigCstr : "";
-      cmGlobalGenerator* globalGenerator =
+      std::vector<std::string> buildConfigs;
+
+      // Try get configuration names given via `-C` CLI option
+      {
+        const char* const buildConfigCstr =
+          this->GetOption("CPACK_BUILD_CONFIG");
+        auto buildConfig = buildConfigCstr ? buildConfigCstr : std::string{};
+        cmExpandList(buildConfig, buildConfigs);
+      }
+
+      // Try get configurations requested by the user explicitly
+      {
+        const char* const configsCstr =
+          this->GetOption("CPACK_INSTALL_CMAKE_CONFIGURATIONS");
+        auto configs = configsCstr ? configsCstr : std::string{};
+        cmExpandList(configs, buildConfigs);
+      }
+
+      // Remove duplicates
+      std::sort(buildConfigs.begin(), buildConfigs.end());
+      buildConfigs.erase(std::unique(buildConfigs.begin(), buildConfigs.end()),
+                         buildConfigs.end());
+
+      // Ensure we have at least one configuration.
+      if (buildConfigs.empty()) {
+        buildConfigs.emplace_back();
+      }
+
+      std::unique_ptr<cmGlobalGenerator> globalGenerator(
         this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator(
-          cmakeGenerator);
+          cmakeGenerator));
       if (!globalGenerator) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
                       "Specified package generator not found. "
@@ -604,27 +638,29 @@
       // on windows.
       cmSystemTools::SetForceUnixPaths(globalGenerator->GetForceUnixPaths());
 
-      if (!this->RunPreinstallTarget(project.ProjectName, project.Directory,
-                                     globalGenerator, buildConfig)) {
-        return 0;
-      }
-
-      delete globalGenerator;
-
-      cmCPackLogger(cmCPackLog::LOG_OUTPUT,
-                    "- Install project: " << project.ProjectName << std::endl);
-
-      // Run the installation for each component
-      for (std::string const& component : componentsVector) {
-        if (!this->InstallCMakeProject(
-              setDestDir, project.Directory, baseTempInstallDirectory,
-              default_dir_mode, component, componentInstall,
-              project.SubDirectory, buildConfig, absoluteDestFiles)) {
+      // Run the installation for the selected build configurations
+      for (auto const& buildConfig : buildConfigs) {
+        if (!this->RunPreinstallTarget(project.ProjectName, project.Directory,
+                                       globalGenerator.get(), buildConfig)) {
           return 0;
         }
+
+        cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+                      "- Install project: " << project.ProjectName << " ["
+                                            << buildConfig << ']'
+                                            << std::endl);
+        // Run the installation for each component
+        for (std::string const& component : componentsVector) {
+          if (!this->InstallCMakeProject(
+                setDestDir, project.Directory, baseTempInstallDirectory,
+                default_dir_mode, component, componentInstall,
+                project.SubDirectory, buildConfig, absoluteDestFiles)) {
+            return 0;
+          }
+        }
       }
 
-      this->CMakeProjects.push_back(project);
+      this->CMakeProjects.emplace_back(std::move(project));
     }
   }
   this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES",
@@ -651,8 +687,8 @@
       buildCommand, &output, &output, &retVal, installDirectory.c_str(),
       this->GeneratorVerbose, cmDuration::zero());
     if (!resB || retVal) {
-      std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-      tmpFile += "/PreinstallOutput.log";
+      std::string tmpFile = cmStrCat(
+        this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/PreinstallOutput.log");
       cmGeneratedFileStream ofs(tmpFile);
       ofs << "# Run command: " << buildCommand << std::endl
           << "# Directory: " << installDirectory << std::endl
@@ -739,7 +775,7 @@
     // CPACK_PACKAGING_INSTALL_PREFIX
     // I know this is tricky and awkward but it's the price for
     // CPACK_SET_DESTDIR backward compatibility.
-    if (cmSystemTools::IsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"))) {
+    if (cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR"))) {
       this->SetOption("CPACK_INSTALL_PREFIX",
                       this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"));
     }
@@ -747,7 +783,7 @@
     if (this->GetOption("CPACK_INSTALL_PREFIX")) {
       dir += this->GetOption("CPACK_INSTALL_PREFIX");
     }
-    mf.AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
+    mf.AddDefinition("CMAKE_INSTALL_PREFIX", dir);
 
     cmCPackLogger(
       cmCPackLog::LOG_DEBUG,
@@ -760,7 +796,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;
@@ -787,7 +823,7 @@
       return 0;
     }
   } else {
-    mf.AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str());
+    mf.AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
 
     if (!cmsys::SystemTools::MakeDirectory(tempInstallDirectory,
                                            default_dir_mode)) {
@@ -806,16 +842,16 @@
   }
 
   if (!buildConfig.empty()) {
-    mf.AddDefinition("BUILD_TYPE", buildConfig.c_str());
+    mf.AddDefinition("BUILD_TYPE", buildConfig);
   }
   std::string installComponentLowerCase = cmSystemTools::LowerCase(component);
   if (installComponentLowerCase != "all") {
-    mf.AddDefinition("CMAKE_INSTALL_COMPONENT", component.c_str());
+    mf.AddDefinition("CMAKE_INSTALL_COMPONENT", component);
   }
 
   // strip on TRUE, ON, 1, one or several file names, but not on
   // FALSE, OFF, 0 and an empty string
-  if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES"))) {
+  if (!cmIsOff(this->GetOption("CPACK_STRIP_FILES"))) {
     mf.AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
   }
   // Remember the list of files before installation
@@ -851,9 +887,8 @@
   // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES
   // to CPack (may be used by generators like CPack RPM or DEB)
   // in order to transparently handle ABSOLUTE PATH
-  if (mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) {
-    mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES",
-                     mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES"));
+  if (const char* def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) {
+    mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", def);
   }
 
   // Now rebuild the list of files after installation
@@ -901,10 +936,8 @@
         GetComponentInstallDirNameSuffix(component);
       if (nullptr != this->GetOption(absoluteDestFileComponent)) {
         std::string absoluteDestFilesListComponent =
-          this->GetOption(absoluteDestFileComponent);
-        absoluteDestFilesListComponent += ";";
-        absoluteDestFilesListComponent +=
-          mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES");
+          cmStrCat(this->GetOption(absoluteDestFileComponent), ';',
+                   mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"));
         this->SetOption(absoluteDestFileComponent,
                         absoluteDestFilesListComponent.c_str());
       } else {
@@ -967,8 +1000,7 @@
     return 0;
   }
 
-  if (cmSystemTools::IsOn(
-        this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) {
+  if (cmIsOn(this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) {
     const char* toplevelDirectory =
       this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
     if (cmSystemTools::FileExists(toplevelDirectory)) {
@@ -997,8 +1029,7 @@
 
   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
   cmsys::Glob gl;
-  std::string findExpr = tempDirectory;
-  findExpr += "/*";
+  std::string findExpr = cmStrCat(tempDirectory, "/*");
   gl.RecurseOn();
   gl.SetRecurseListDirs(true);
   gl.SetRecurseThroughSymlinks(false);
@@ -1018,8 +1049,7 @@
                   "Remove old package file" << std::endl);
     cmSystemTools::RemoveFile(tempPackageFileName);
   }
-  if (cmSystemTools::IsOn(
-        this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
+  if (cmIsOn(this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
     tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
   }
 
@@ -1143,14 +1173,14 @@
 
 bool cmCPackGenerator::IsOn(const std::string& name) const
 {
-  return cmSystemTools::IsOn(GetOption(name));
+  return cmIsOn(GetOption(name));
 }
 
 bool cmCPackGenerator::IsSetToOff(const std::string& op) const
 {
   const char* ret = this->MakefileMap->GetDefinition(op);
   if (ret && *ret) {
-    return cmSystemTools::IsOff(ret);
+    return cmIsOff(ret);
   }
   return false;
 }
@@ -1195,8 +1225,7 @@
   if (cmsys::SystemTools::GetEnv("ProgramFiles", prgfiles)) {
     this->InstallPath = prgfiles;
   } else if (cmsys::SystemTools::GetEnv("SystemDrive", sysDrive)) {
-    this->InstallPath = sysDrive;
-    this->InstallPath += "/Program Files";
+    this->InstallPath = cmStrCat(sysDrive, "/Program Files");
   } else {
     this->InstallPath = "c:/Program Files";
   }
@@ -1464,7 +1493,7 @@
     component->IsRequired = this->IsOn(macroPrefix + "_REQUIRED");
     component->IsDisabledByDefault = this->IsOn(macroPrefix + "_DISABLED");
     component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") ||
-      cmSystemTools::IsOn(this->GetOption("CPACK_DOWNLOAD_ALL"));
+      cmIsOn(this->GetOption("CPACK_DOWNLOAD_ALL"));
 
     const char* archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE");
     if (archiveFile && *archiveFile) {
@@ -1492,8 +1521,8 @@
     // Determine the installation types.
     const char* installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES");
     if (installTypes && *installTypes) {
-      std::vector<std::string> installTypesVector;
-      cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
+      std::vector<std::string> installTypesVector =
+        cmExpandedList(installTypes);
       for (std::string const& installType : installTypesVector) {
         component->InstallationTypes.push_back(
           this->GetInstallationType(projectName, installType));
@@ -1503,8 +1532,7 @@
     // Determine the component dependencies.
     const char* depends = this->GetOption(macroPrefix + "_DEPENDS");
     if (depends && *depends) {
-      std::vector<std::string> dependsVector;
-      cmSystemTools::ExpandListArgument(depends, dependsVector);
+      std::vector<std::string> dependsVector = cmExpandedList(depends);
       for (std::string const& depend : dependsVector) {
         cmCPackComponent* child = GetComponent(projectName, depend);
         component->Dependencies.push_back(child);
diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h
index 3c06d41..33026c1 100644
--- a/Source/CPack/cmCPackGenerator.h
+++ b/Source/CPack/cmCPackGenerator.h
@@ -10,9 +10,10 @@
 #include <string>
 #include <vector>
 
+#include "cm_sys_stat.h"
+
 #include "cmCPackComponentGroup.h"
 #include "cmSystemTools.h"
-#include "cm_sys_stat.h"
 
 class cmCPackLog;
 class cmGlobalGenerator;
@@ -326,7 +327,7 @@
 };
 
 #define cmCPackTypeMacro(klass, superclass)                                   \
-  typedef superclass Superclass;                                              \
+  using Superclass = superclass;                                              \
   const char* GetNameOfClass() override { return #klass; }                    \
   static cmCPackGenerator* CreateGenerator() { return new klass; }            \
   class cmCPackTypeMacro_UseTrailingSemicolon
diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx
index 2c5ab4d..79e344b 100644
--- a/Source/CPack/cmCPackGeneratorFactory.cxx
+++ b/Source/CPack/cmCPackGeneratorFactory.cxx
@@ -6,11 +6,10 @@
 #include <utility>
 
 #include "IFW/cmCPackIFWGenerator.h"
-#include "cmAlgorithms.h"
-#include "cmCPack7zGenerator.h"
 #ifdef HAVE_FREEBSD_PKG
 #  include "cmCPackFreeBSDGenerator.h"
 #endif
+#include "cmCPackArchiveGenerator.h"
 #include "cmCPackDebGenerator.h"
 #include "cmCPackExternalGenerator.h"
 #include "cmCPackGenerator.h"
@@ -18,11 +17,6 @@
 #include "cmCPackNSISGenerator.h"
 #include "cmCPackNuGetGenerator.h"
 #include "cmCPackSTGZGenerator.h"
-#include "cmCPackTGZGenerator.h"
-#include "cmCPackTXZGenerator.h"
-#include "cmCPackTarBZip2Generator.h"
-#include "cmCPackTarCompressGenerator.h"
-#include "cmCPackZIPGenerator.h"
 
 #ifdef __APPLE__
 #  include "cmCPackBundleGenerator.h"
@@ -48,13 +42,21 @@
 
 cmCPackGeneratorFactory::cmCPackGeneratorFactory()
 {
-  if (cmCPackTGZGenerator::CanGenerate()) {
+  if (cmCPackArchiveGenerator::CanGenerate()) {
+    this->RegisterGenerator("7Z", "7-Zip file format",
+                            cmCPackArchiveGenerator::Create7ZGenerator);
+    this->RegisterGenerator("TBZ2", "Tar BZip2 compression",
+                            cmCPackArchiveGenerator::CreateTBZ2Generator);
     this->RegisterGenerator("TGZ", "Tar GZip compression",
-                            cmCPackTGZGenerator::CreateGenerator);
-  }
-  if (cmCPackTXZGenerator::CanGenerate()) {
+                            cmCPackArchiveGenerator::CreateTGZGenerator);
     this->RegisterGenerator("TXZ", "Tar XZ compression",
-                            cmCPackTXZGenerator::CreateGenerator);
+                            cmCPackArchiveGenerator::CreateTXZGenerator);
+    this->RegisterGenerator("TZ", "Tar Compress compression",
+                            cmCPackArchiveGenerator::CreateTZGenerator);
+    this->RegisterGenerator("TZST", "Tar Zstandard compression",
+                            cmCPackArchiveGenerator::CreateTZSTGenerator);
+    this->RegisterGenerator("ZIP", "ZIP file format",
+                            cmCPackArchiveGenerator::CreateZIPGenerator);
   }
   if (cmCPackSTGZGenerator::CanGenerate()) {
     this->RegisterGenerator("STGZ", "Self extracting Tar GZip compression",
@@ -80,29 +82,12 @@
                             cmCPackCygwinSourceGenerator::CreateGenerator);
   }
 #endif
-
-  if (cmCPackZIPGenerator::CanGenerate()) {
-    this->RegisterGenerator("ZIP", "ZIP file format",
-                            cmCPackZIPGenerator::CreateGenerator);
-  }
-  if (cmCPack7zGenerator::CanGenerate()) {
-    this->RegisterGenerator("7Z", "7-Zip file format",
-                            cmCPack7zGenerator::CreateGenerator);
-  }
 #if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID))
   if (cmCPackWIXGenerator::CanGenerate()) {
     this->RegisterGenerator("WIX", "MSI file format via WiX tools",
                             cmCPackWIXGenerator::CreateGenerator);
   }
 #endif
-  if (cmCPackTarBZip2Generator::CanGenerate()) {
-    this->RegisterGenerator("TBZ2", "Tar BZip2 compression",
-                            cmCPackTarBZip2Generator::CreateGenerator);
-  }
-  if (cmCPackTarCompressGenerator::CanGenerate()) {
-    this->RegisterGenerator("TZ", "Tar Compress compression",
-                            cmCPackTarCompressGenerator::CreateGenerator);
-  }
   if (cmCPackDebGenerator::CanGenerate()) {
     this->RegisterGenerator("DEB", "Debian packages",
                             cmCPackDebGenerator::CreateGenerator);
@@ -152,32 +137,19 @@
 #endif
 }
 
-cmCPackGeneratorFactory::~cmCPackGeneratorFactory()
-{
-  cmDeleteAll(this->Generators);
-}
-
-cmCPackGenerator* cmCPackGeneratorFactory::NewGenerator(
+std::unique_ptr<cmCPackGenerator> cmCPackGeneratorFactory::NewGenerator(
   const std::string& name)
 {
-  cmCPackGenerator* gen = this->NewGeneratorInternal(name);
-  if (!gen) {
-    return nullptr;
-  }
-  this->Generators.push_back(gen);
-  gen->SetLogger(this->Logger);
-  return gen;
-}
-
-cmCPackGenerator* cmCPackGeneratorFactory::NewGeneratorInternal(
-  const std::string& name)
-{
-  cmCPackGeneratorFactory::t_GeneratorCreatorsMap::iterator it =
-    this->GeneratorCreators.find(name);
+  auto it = this->GeneratorCreators.find(name);
   if (it == this->GeneratorCreators.end()) {
     return nullptr;
   }
-  return (it->second)();
+  std::unique_ptr<cmCPackGenerator> gen(it->second());
+  if (!gen) {
+    return nullptr;
+  }
+  gen->SetLogger(this->Logger);
+  return gen;
 }
 
 void cmCPackGeneratorFactory::RegisterGenerator(
diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h
index 972f0f7..62b7484 100644
--- a/Source/CPack/cmCPackGeneratorFactory.h
+++ b/Source/CPack/cmCPackGeneratorFactory.h
@@ -6,8 +6,8 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
+#include <memory>
 #include <string>
-#include <vector>
 
 class cmCPackGenerator;
 class cmCPackLog;
@@ -20,16 +20,11 @@
 {
 public:
   cmCPackGeneratorFactory();
-  ~cmCPackGeneratorFactory();
-
-  cmCPackGeneratorFactory(const cmCPackGeneratorFactory&) = delete;
-  cmCPackGeneratorFactory& operator=(const cmCPackGeneratorFactory&) = delete;
 
   //! Get the generator
-  cmCPackGenerator* NewGenerator(const std::string& name);
-  void DeleteGenerator(cmCPackGenerator* gen);
+  std::unique_ptr<cmCPackGenerator> NewGenerator(const std::string& name);
 
-  typedef cmCPackGenerator* CreateGeneratorCall();
+  using CreateGeneratorCall = cmCPackGenerator*();
 
   void RegisterGenerator(const std::string& name,
                          const char* generatorDescription,
@@ -37,17 +32,14 @@
 
   void SetLogger(cmCPackLog* logger) { this->Logger = logger; }
 
-  typedef std::map<std::string, std::string> DescriptionsMap;
+  using DescriptionsMap = std::map<std::string, std::string>;
   const DescriptionsMap& GetGeneratorsList() const
   {
     return this->GeneratorDescriptions;
   }
 
 private:
-  cmCPackGenerator* NewGeneratorInternal(const std::string& name);
-  std::vector<cmCPackGenerator*> Generators;
-
-  typedef std::map<std::string, CreateGeneratorCall*> t_GeneratorCreatorsMap;
+  using t_GeneratorCreatorsMap = std::map<std::string, CreateGeneratorCall*>;
   t_GeneratorCreatorsMap GeneratorCreators;
   DescriptionsMap GeneratorDescriptions;
   cmCPackLog* Logger;
diff --git a/Source/CPack/cmCPackLog.cxx b/Source/CPack/cmCPackLog.cxx
index a3ca4b5..ca675fd 100644
--- a/Source/CPack/cmCPackLog.cxx
+++ b/Source/CPack/cmCPackLog.cxx
@@ -83,7 +83,7 @@
       if (!tagString.empty()) {
         tagString += ",";
       }
-      tagString = "VERBOSE";
+      tagString += "VERBOSE";
     }
   }
   if (tag & LOG_WARNING) {
@@ -93,7 +93,7 @@
       if (!tagString.empty()) {
         tagString += ",";
       }
-      tagString = "WARNING";
+      tagString += "WARNING";
     }
   }
   if (tag & LOG_ERROR) {
@@ -103,7 +103,7 @@
       if (!tagString.empty()) {
         tagString += ",";
       }
-      tagString = "ERROR";
+      tagString += "ERROR";
     }
   }
   if (tag & LOG_DEBUG && this->Debug) {
@@ -113,7 +113,7 @@
       if (!tagString.empty()) {
         tagString += ",";
       }
-      tagString = "DEBUG";
+      tagString += "DEBUG";
     }
     useFileAndLine = true;
   }
@@ -124,7 +124,7 @@
       if (!tagString.empty()) {
         tagString += ",";
       }
-      tagString = "VERBOSE";
+      tagString += "VERBOSE";
     }
   }
   if (this->Quiet) {
diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h
index 65281e3..1cb1643 100644
--- a/Source/CPack/cmCPackLog.h
+++ b/Source/CPack/cmCPackLog.h
@@ -6,9 +6,10 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <ostream>
-#include <string.h>
 #include <string>
 
+#include <string.h>
+
 #define cmCPack_Log(ctSelf, logType, msg)                                     \
   do {                                                                        \
     std::ostringstream cmCPackLog_msg;                                        \
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index e2020c5..adea8ec 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -2,22 +2,25 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackNSISGenerator.h"
 
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <sstream>
+#include <utility>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmAlgorithms.h"
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/Directory.hxx"
-#include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <map>
-#include <sstream>
-#include <stdlib.h>
-#include <string.h>
-#include <utility>
-
 /* NSIS uses different command line syntax on Windows and others */
 #ifdef _WIN32
 #  define NSIS_OPT "/"
@@ -36,7 +39,8 @@
 {
   // TODO: Fix nsis to force out file name
 
-  std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
+  std::string nsisInFileName =
+    this->FindTemplate("Internal/CPack/NSIS.template.in");
   if (nsisInFileName.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPack error: Could not find NSIS installer template file."
@@ -44,7 +48,7 @@
     return false;
   }
   std::string nsisInInstallOptions =
-    this->FindTemplate("NSIS.InstallOptions.ini.in");
+    this->FindTemplate("Internal/CPack/NSIS.InstallOptions.ini.in");
   if (nsisInInstallOptions.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPack error: Could not find NSIS installer options file."
@@ -53,8 +57,7 @@
   }
 
   std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  std::string tmpFile = nsisFileName;
-  tmpFile += "/NSISOutput.log";
+  std::string tmpFile = cmStrCat(nsisFileName, "/NSISOutput.log");
   std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini";
   nsisFileName += "/project.nsi";
   std::ostringstream str;
@@ -139,39 +142,34 @@
                             installerIconCode.c_str());
   }
   if (this->IsSet("CPACK_PACKAGE_ICON")) {
-    std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \"";
-    installerIconCode += this->GetOption("CPACK_PACKAGE_ICON");
-    installerIconCode += "\"\n";
+    std::string installerIconCode =
+      cmStrCat("!define MUI_HEADERIMAGE_BITMAP \"",
+               this->GetOption("CPACK_PACKAGE_ICON"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
                             installerIconCode.c_str());
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP")) {
-    std::string installerBitmapCode =
-      "!define MUI_WELCOMEFINISHPAGE_BITMAP \"";
-    installerBitmapCode +=
-      this->GetOption("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP");
-    installerBitmapCode += "\"\n";
+    std::string installerBitmapCode = cmStrCat(
+      "!define MUI_WELCOMEFINISHPAGE_BITMAP \"",
+      this->GetOption("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_WELCOMEFINISH_CODE",
                             installerBitmapCode.c_str());
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP")) {
-    std::string installerBitmapCode =
-      "!define MUI_UNWELCOMEFINISHPAGE_BITMAP \"";
-    installerBitmapCode +=
-      this->GetOption("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP");
-    installerBitmapCode += "\"\n";
+    std::string installerBitmapCode = cmStrCat(
+      "!define MUI_UNWELCOMEFINISHPAGE_BITMAP \"",
+      this->GetOption("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_UNWELCOMEFINISH_CODE",
                             installerBitmapCode.c_str());
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN")) {
-    std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\";
-    installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
-    installerRunCode += "\\";
-    installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN");
-    installerRunCode += "\"\n";
+    std::string installerRunCode =
+      cmStrCat("!define MUI_FINISHPAGE_RUN \"$INSTDIR\\",
+               this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"), '\\',
+               this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE",
                             installerRunCode.c_str());
   }
@@ -273,7 +271,7 @@
 
     if (anyDownloadedComponents) {
       defines += "!define CPACK_USES_DOWNLOAD\n";
-      if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE"))) {
+      if (cmIsOn(this->GetOption("CPACK_ADD_REMOVE"))) {
         defines += "!define CPACK_NSIS_ADD_REMOVE\n";
       }
     }
@@ -294,9 +292,9 @@
 
   this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions);
   this->ConfigureFile(nsisInFileName, nsisFileName);
-  std::string nsisCmd = "\"";
-  nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
-  nsisCmd += "\" \"" + nsisFileName + "\"";
+  std::string nsisCmd =
+    cmStrCat('"', this->GetOption("CPACK_INSTALLER_PROGRAM"), "\" \"",
+             nsisFileName, '"');
   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd << std::endl);
   std::string output;
   int retVal = 1;
@@ -320,8 +318,7 @@
 
 int cmCPackNSISGenerator::InitializeInternal()
 {
-  if (cmSystemTools::IsOn(
-        this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
+  if (cmIsOn(this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
     cmCPackLogger(
       cmCPackLog::LOG_WARNING,
       "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
@@ -413,8 +410,7 @@
   if (!resS || retVal ||
       (!versionRex.find(output) && !versionRexCVS.find(output))) {
     const char* topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    std::string tmpFile = topDir ? topDir : ".";
-    tmpFile += "/NSISOutput.log";
+    std::string tmpFile = cmStrCat(topDir ? topDir : ".", "/NSISOutput.log");
     cmGeneratedFileStream ofs(tmpFile);
     ofs << "# Run command: " << nsisCmd << std::endl
         << "# Output:" << std::endl
@@ -458,8 +454,7 @@
                   "CPACK_CREATE_DESKTOP_LINKS: " << cpackPackageDeskTopLinks
                                                  << std::endl);
 
-    cmSystemTools::ExpandListArgument(cpackPackageDeskTopLinks,
-                                      cpackPackageDesktopLinksVector);
+    cmExpandList(cpackPackageDeskTopLinks, cpackPackageDesktopLinksVector);
     for (std::string const& cpdl : cpackPackageDesktopLinksVector) {
       cmCPackLogger(cmCPackLog::LOG_DEBUG,
                     "CPACK_CREATE_DESKTOP_LINKS: " << cpdl << std::endl);
@@ -477,9 +472,8 @@
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "The cpackPackageExecutables: " << cpackPackageExecutables
                                                   << "." << std::endl);
-    std::vector<std::string> cpackPackageExecutablesVector;
-    cmSystemTools::ExpandListArgument(cpackPackageExecutables,
-                                      cpackPackageExecutablesVector);
+    std::vector<std::string> cpackPackageExecutablesVector =
+      cmExpandedList(cpackPackageExecutables);
     if (cpackPackageExecutablesVector.size() % 2 != 0) {
       cmCPackLogger(
         cmCPackLog::LOG_ERROR,
@@ -501,10 +495,7 @@
                 << ".lnk\"" << std::endl;
       // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
       // if so add a desktop link
-      if (!cpackPackageDesktopLinksVector.empty() &&
-          std::find(cpackPackageDesktopLinksVector.begin(),
-                    cpackPackageDesktopLinksVector.end(),
-                    execName) != cpackPackageDesktopLinksVector.end()) {
+      if (cmContains(cpackPackageDesktopLinksVector, execName)) {
         str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
         str << "    CreateShortCut \"$DESKTOP\\" << linkName
             << R"(.lnk" "$INSTDIR\)" << cpackNsisExecutablesDirectory << "\\"
@@ -534,8 +525,8 @@
   }
   cmCPackLogger(cmCPackLog::LOG_DEBUG,
                 "The cpackMenuLinks: " << cpackMenuLinks << "." << std::endl);
-  std::vector<std::string> cpackMenuLinksVector;
-  cmSystemTools::ExpandListArgument(cpackMenuLinks, cpackMenuLinksVector);
+  std::vector<std::string> cpackMenuLinksVector =
+    cmExpandedList(cpackMenuLinks);
   if (cpackMenuLinksVector.size() % 2 != 0) {
     cmCPackLogger(
       cmCPackLog::LOG_ERROR,
@@ -576,8 +567,7 @@
     }
     // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
     // if so add a desktop link
-    std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
-    desktop += linkName;
+    std::string desktop = cmStrCat("CPACK_CREATE_DESKTOP_LINK_", linkName);
     if (this->IsSet(desktop)) {
       str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
       str << "    CreateShortCut \"$DESKTOP\\" << linkName
@@ -659,8 +649,8 @@
   if (component->IsDownloaded) {
     if (component->ArchiveFile.empty()) {
       // Compute the name of the archive.
-      std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-      packagesDir += ".dummy";
+      std::string packagesDir =
+        cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
       std::ostringstream out;
       out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-"
           << component->Name << ".zip";
@@ -674,8 +664,8 @@
     if (userUploadDirectory && *userUploadDirectory) {
       uploadDirectory = userUploadDirectory;
     } else {
-      uploadDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
-      uploadDirectory += "/CPackUploads";
+      uploadDirectory =
+        cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
     }
     if (!cmSystemTools::FileExists(uploadDirectory)) {
       if (!cmSystemTools::MakeDirectory(uploadDirectory)) {
@@ -712,17 +702,14 @@
     }
 
     // The directory where this component's files reside
-    std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-    dirName += '/';
-    dirName += component->Name;
-    dirName += '/';
+    std::string dirName = cmStrCat(
+      this->GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component->Name, '/');
 
     // Build the list of files to go into this archive, and determine the
     // size of the installed component.
-    std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-    zipListFileName += "/winZip.filelist";
-    bool needQuotesInFile =
-      cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
+    std::string zipListFileName = cmStrCat(
+      this->GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist");
+    bool needQuotesInFile = cmIsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
     unsigned long totalSize = 0;
     { // the scope is needed for cmGeneratedFileStream
       cmGeneratedFileStream out(zipListFileName);
@@ -751,8 +738,8 @@
       cmd, &output, &output, &retVal, dirName.c_str(),
       cmSystemTools::OUTPUT_NONE, cmDuration::zero());
     if (!res || retVal) {
-      std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-      tmpFile += "/CompressZip.log";
+      std::string tmpFile = cmStrCat(
+        this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/CompressZip.log");
       cmGeneratedFileStream ofs(tmpFile);
       ofs << "# Run command: " << cmd << std::endl
           << "# Output:" << std::endl
diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h
index fc9ad9a..0af37af 100644
--- a/Source/CPack/cmCPackNSISGenerator.h
+++ b/Source/CPack/cmCPackNSISGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackGenerator.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmCPackGenerator.h"
+
 class cmCPackComponent;
 class cmCPackComponentGroup;
 
diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx
index 76f0699..60faecd 100644
--- a/Source/CPack/cmCPackNuGetGenerator.cxx
+++ b/Source/CPack/cmCPackNuGetGenerator.cxx
@@ -2,11 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackNuGetGenerator.h"
 
-#include "cmAlgorithms.h"
-#include "cmCPackComponentGroup.h"
-#include "cmCPackLog.h"
-#include "cmSystemTools.h"
-
 #include <algorithm>
 #include <iterator>
 #include <map>
@@ -15,6 +10,11 @@
 #include <utility>
 #include <vector>
 
+#include "cmCPackComponentGroup.h"
+#include "cmCPackLog.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 bool cmCPackNuGetGenerator::SupportsComponentInstallation() const
 {
   return IsOn("CPACK_NUGET_COMPONENT_INSTALL");
diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx
index 90e0afe..cd65694 100644
--- a/Source/CPack/cmCPackOSXX11Generator.cxx
+++ b/Source/CPack/cmCPackOSXX11Generator.cxx
@@ -4,12 +4,14 @@
 
 #include <sstream>
 
+#include "cm_sys_stat.h"
+
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cm_sys_stat.h"
 
 cmCPackOSXX11Generator::cmCPackOSXX11Generator() = default;
 
@@ -28,9 +30,8 @@
                                                   << "." << std::endl);
     std::ostringstream str;
     std::ostringstream deleteStr;
-    std::vector<std::string> cpackPackageExecutablesVector;
-    cmSystemTools::ExpandListArgument(cpackPackageExecutables,
-                                      cpackPackageExecutablesVector);
+    std::vector<std::string> cpackPackageExecutablesVector =
+      cmExpandedList(cpackPackageExecutables);
     if (cpackPackageExecutablesVector.size() % 2 != 0) {
       cmCPackLogger(
         cmCPackLog::LOG_ERROR,
@@ -55,16 +56,14 @@
     diskImageDirectory + "/.background";
 
   // App bundle directories
-  std::string packageDirFileName = toplevel;
-  packageDirFileName += "/";
-  packageDirFileName += this->GetOption("CPACK_PACKAGE_FILE_NAME");
-  packageDirFileName += ".app";
+  std::string packageDirFileName = cmStrCat(
+    toplevel, '/', this->GetOption("CPACK_PACKAGE_FILE_NAME"), ".app");
   std::string contentsDirectory = packageDirFileName + "/Contents";
   std::string resourcesDirectory = contentsDirectory + "/Resources";
   std::string appDirectory = contentsDirectory + "/MacOS";
   std::string scriptDirectory = resourcesDirectory + "/Scripts";
-  std::string resourceFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
-  resourceFileName += ".rsrc";
+  std::string resourceFileName =
+    cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), ".rsrc");
 
   const char* dir = resourcesDirectory.c_str();
   const char* appdir = appDirectory.c_str();
@@ -113,13 +112,10 @@
   }
 
   // Two of the files need to have execute permission, so ensure they do:
-  std::string runTimeScript = dir;
-  runTimeScript += "/";
-  runTimeScript += "RuntimeScript";
+  std::string runTimeScript = cmStrCat(dir, "/RuntimeScript");
 
-  std::string appScriptName = appdir;
-  appScriptName += "/";
-  appScriptName += this->GetOption("CPACK_PACKAGE_FILE_NAME");
+  std::string appScriptName =
+    cmStrCat(appdir, '/', this->GetOption("CPACK_PACKAGE_FILE_NAME"));
 
   mode_t mode;
   if (cmsys::SystemTools::GetPermissions(runTimeScript.c_str(), mode)) {
@@ -139,8 +135,8 @@
   }
 
   std::string output;
-  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  tmpFile += "/hdiutilOutput.log";
+  std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
+                                 "/hdiutilOutput.log");
   std::ostringstream dmgCmd;
   dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
          << "\" create -ov -fs HFS+ -format UDZO -srcfolder \""
@@ -228,9 +224,8 @@
     return false;
     }
 
-  std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  destFileName += "/Resources/";
-  destFileName += name + ext;
+  std::string destFileName = cmStrCat(
+this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/Resources/", name, ext );
 
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
@@ -245,9 +240,7 @@
   const std::string& name, const std::string& dir,
   const char* outputFileName /* = 0 */, bool copyOnly /* = false */)
 {
-  std::string inFName = "CPack.";
-  inFName += name;
-  inFName += ".in";
+  std::string inFName = cmStrCat("Internal/CPack/CPack.", name, ".in");
   std::string inFileName = this->FindTemplate(inFName.c_str());
   if (inFileName.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -259,9 +252,7 @@
     outputFileName = name.c_str();
   }
 
-  std::string destFileName = dir;
-  destFileName += "/";
-  destFileName += outputFileName;
+  std::string destFileName = cmStrCat(dir, '/', outputFileName);
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Configure file: " << inFileName << " to " << destFileName
@@ -272,8 +263,8 @@
 
 const char* cmCPackOSXX11Generator::GetPackagingInstallPrefix()
 {
-  this->InstallPrefix = "/";
-  this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME");
-  this->InstallPrefix += ".app/Contents/Resources";
+  this->InstallPrefix =
+    cmStrCat('/', this->GetOption("CPACK_PACKAGE_FILE_NAME"),
+             ".app/Contents/Resources");
   return this->InstallPrefix.c_str();
 }
diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx
index 8c22c65..3e1a51b 100644
--- a/Source/CPack/cmCPackPKGGenerator.cxx
+++ b/Source/CPack/cmCPackPKGGenerator.cxx
@@ -7,6 +7,7 @@
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 
@@ -34,8 +35,8 @@
   const cmCPackComponent& component)
 {
   if (component.ArchiveFile.empty()) {
-    std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-    packagesDir += ".dummy";
+    std::string packagesDir =
+      cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
     std::ostringstream out;
     out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-"
         << component.Name << ".pkg";
@@ -48,7 +49,7 @@
 void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile)
 {
   std::string distributionTemplate =
-    this->FindTemplate("CPack.distribution.dist.in");
+    this->FindTemplate("Internal/CPack/CPack.distribution.dist.in");
   if (distributionTemplate.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Cannot find input file: " << distributionTemplate
@@ -56,8 +57,8 @@
     return;
   }
 
-  std::string distributionFile = metapackageFile;
-  distributionFile += "/Contents/distribution.dist";
+  std::string distributionFile =
+    cmStrCat(metapackageFile, "/Contents/distribution.dist");
 
   // Create the choice outline, which provides a tree-based view of
   // the components in their groups.
@@ -144,12 +145,9 @@
 void cmCPackPKGGenerator::CreateChoice(const cmCPackComponent& component,
                                        cmXMLWriter& xout)
 {
-  std::string packageId = "com.";
-  packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
-  packageId += '.';
-  packageId += this->GetOption("CPACK_PACKAGE_NAME");
-  packageId += '.';
-  packageId += component.Name;
+  std::string packageId =
+    cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
+             this->GetOption("CPACK_PACKAGE_NAME"), '.', component.Name);
 
   xout.StartElement("choice");
   xout.Attribute("id", component.Name + "Choice");
@@ -192,14 +190,13 @@
 
   // Create a description of the package associated with this
   // component.
-  std::string relativePackageLocation = "Contents/Packages/";
-  relativePackageLocation += this->GetPackageName(component);
+  std::string relativePackageLocation =
+    cmStrCat("Contents/Packages/", this->GetPackageName(component));
 
   // Determine the installed size of the package.
-  std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-  dirName += '/';
-  dirName += component.Name;
-  dirName += this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
+  std::string dirName =
+    cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component.Name,
+             this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"));
   unsigned long installedSize = component.GetInstalledSizeInKbytes(dirName);
 
   xout.StartElement("pkg-ref");
@@ -282,9 +279,7 @@
     return false;
   }
 
-  std::string destFileName = dirName;
-  destFileName += '/';
-  destFileName += name + ext;
+  std::string destFileName = cmStrCat(dirName, '/', name, ext);
 
   // Set this so that distribution.dist gets the right name (without
   // the path).
@@ -305,9 +300,7 @@
     outName = name.c_str();
   }
 
-  std::string inFName = "CPack.";
-  inFName += name;
-  inFName += ".in";
+  std::string inFName = cmStrCat("Internal/CPack/CPack.", name, ".in");
   std::string inFileName = this->FindTemplate(inFName.c_str());
   if (inFileName.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -315,9 +308,8 @@
     return false;
   }
 
-  std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  destFileName += "/";
-  destFileName += outName;
+  std::string destFileName =
+    cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/', outName);
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Configure file: " << inFileName << " to " << destFileName
@@ -330,9 +322,7 @@
                                            const std::string& script,
                                            const std::string& name)
 {
-  std::string dst = resdir;
-  dst += "/";
-  dst += name;
+  std::string dst = cmStrCat(resdir, '/', name);
   cmSystemTools::CopyFileAlways(script, dst);
   cmSystemTools::SetPermissions(dst.c_str(), 0777);
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
index 3d93c48..c5ba726 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.cxx
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -2,19 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackPackageMakerGenerator.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/RegularExpression.hxx"
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
 #include <map>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string>
 
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 
@@ -47,8 +49,8 @@
     this->GetOption("CPACK_TEMPORARY_DIRECTORY");
   if (this->Components.empty()) {
     packageDirFileName += ".pkg";
-    resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    resDir += "/Resources";
+    resDir =
+      cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/Resources");
   } else {
     packageDirFileName += ".mpkg";
     if (!cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str())) {
@@ -58,8 +60,7 @@
       return 0;
     }
 
-    resDir = packageDirFileName;
-    resDir += "/Contents";
+    resDir = cmStrCat(packageDirFileName, "/Contents");
     if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "unable to create package subdirectory " << resDir
@@ -155,8 +156,8 @@
 
   if (!this->Components.empty()) {
     // Create the directory where component packages will be built.
-    std::string basePackageDir = packageDirFileName;
-    basePackageDir += "/Contents/Packages";
+    std::string basePackageDir =
+      cmStrCat(packageDirFileName, "/Contents/Packages");
     if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "Problem creating component packages directory: "
@@ -172,8 +173,8 @@
     if (userUploadDirectory && *userUploadDirectory) {
       uploadDirectory = userUploadDirectory;
     } else {
-      uploadDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
-      uploadDirectory += "/CPackUploads";
+      uploadDirectory =
+        cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
     }
 
     // Create packages for each component
@@ -232,9 +233,7 @@
       packageFile += '/';
       packageFile += GetPackageName(compIt->second);
 
-      std::string packageDir = toplevel;
-      packageDir += '/';
-      packageDir += compIt->first;
+      std::string packageDir = cmStrCat(toplevel, '/', compIt->first);
       if (!this->GenerateComponentPackage(
             packageFile.c_str(), packageDir.c_str(), compIt->second)) {
         return 0;
@@ -283,8 +282,8 @@
     WriteDistributionFile(packageDirFileName.c_str());
   }
 
-  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  tmpFile += "/hdiutilOutput.log";
+  std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
+                                 "/hdiutilOutput.log");
   std::ostringstream dmgCmd;
   dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
          << "\" create -ov -fs HFS+ -format UDZO -srcfolder \""
@@ -461,8 +460,8 @@
 bool cmCPackPackageMakerGenerator::RunPackageMaker(const char* command,
                                                    const char* packageFile)
 {
-  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  tmpFile += "/PackageMakerOutput.log";
+  std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
+                                 "/PackageMakerOutput.log");
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
   std::string output;
@@ -517,8 +516,9 @@
       this->PackageMakerVersion < 3.0) {
     // Create Description.plist and Info.plist files for normal Mac OS
     // X packages, which work on Mac OS X 10.3 and newer.
-    std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    descriptionFile += '/' + component.Name + "-Description.plist";
+    std::string descriptionFile =
+      cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
+               component.Name, "-Description.plist");
     cmsys::ofstream out(descriptionFile.c_str());
     cmXMLWriter xout(out);
     xout.StartDocument();
@@ -539,12 +539,10 @@
     out.close();
 
     // Create the Info.plist file for this component
-    std::string moduleVersionSuffix = ".";
-    moduleVersionSuffix += component.Name;
+    std::string moduleVersionSuffix = cmStrCat('.', component.Name);
     this->SetOption("CPACK_MODULE_VERSION_SUFFIX",
                     moduleVersionSuffix.c_str());
-    std::string infoFileName = component.Name;
-    infoFileName += "-Info.plist";
+    std::string infoFileName = cmStrCat(component.Name, "-Info.plist");
     if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str())) {
       return false;
     }
@@ -561,12 +559,9 @@
     // like normal packages, and can be downloaded by the installer
     // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
     // flat packages when the packages will be downloaded on the fly.
-    std::string pkgId = "com.";
-    pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
-    pkgId += '.';
-    pkgId += this->GetOption("CPACK_PACKAGE_NAME");
-    pkgId += '.';
-    pkgId += component.Name;
+    std::string pkgId =
+      cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
+               this->GetOption("CPACK_PACKAGE_NAME"), '.', component.Name);
 
     pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
            << "\" --root \"" << packageDir << "\""
diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx
index 94b5b5f..dae268c 100644
--- a/Source/CPack/cmCPackProductBuildGenerator.cxx
+++ b/Source/CPack/cmCPackProductBuildGenerator.cxx
@@ -2,14 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackProductBuildGenerator.h"
 
+#include <cstddef>
 #include <map>
 #include <sstream>
-#include <stddef.h>
 
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmCPackProductBuildGenerator::cmCPackProductBuildGenerator()
@@ -28,8 +29,8 @@
     this->GetOption("CPACK_TEMPORARY_DIRECTORY");
 
   // Create the directory where component packages will be built.
-  std::string basePackageDir = packageDirFileName;
-  basePackageDir += "/Contents/Packages";
+  std::string basePackageDir =
+    cmStrCat(packageDirFileName, "/Contents/Packages");
   if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Problem creating component packages directory: "
@@ -41,9 +42,7 @@
     std::map<std::string, cmCPackComponent>::iterator compIt;
     for (compIt = this->Components.begin(); compIt != this->Components.end();
          ++compIt) {
-      std::string packageDir = toplevel;
-      packageDir += '/';
-      packageDir += compIt->first;
+      std::string packageDir = cmStrCat(toplevel, '/', compIt->first);
       if (!this->GenerateComponentPackage(basePackageDir,
                                           GetPackageName(compIt->second),
                                           packageDir, &compIt->second)) {
@@ -138,8 +137,8 @@
 
 bool cmCPackProductBuildGenerator::RunProductBuild(const std::string& command)
 {
-  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-  tmpFile += "/ProductBuildOutput.log";
+  std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
+                                 "/ProductBuildOutput.log");
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
   std::string output;
@@ -166,9 +165,7 @@
   const std::string& packageFileDir, const std::string& packageFileName,
   const std::string& packageDir, const cmCPackComponent* component)
 {
-  std::string packageFile = packageFileDir;
-  packageFile += '/';
-  packageFile += packageFileName;
+  std::string packageFile = cmStrCat(packageFileDir, '/', packageFileName);
 
   cmCPackLogger(cmCPackLog::LOG_OUTPUT,
                 "-   Building component package: " << packageFile
@@ -206,10 +203,8 @@
   // The command that will be used to run ProductBuild
   std::ostringstream pkgCmd;
 
-  std::string pkgId = "com.";
-  pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
-  pkgId += '.';
-  pkgId += this->GetOption("CPACK_PACKAGE_NAME");
+  std::string pkgId = cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"),
+                               '.', this->GetOption("CPACK_PACKAGE_NAME"));
   if (component) {
     pkgId += '.';
     pkgId += component->Name;
diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx
index 33ab62b..0c1cecf 100644
--- a/Source/CPack/cmCPackRPMGenerator.cxx
+++ b/Source/CPack/cmCPackRPMGenerator.cxx
@@ -3,7 +3,7 @@
 #include "cmCPackRPMGenerator.h"
 
 #include <algorithm>
-#include <ctype.h>
+#include <cctype>
 #include <map>
 #include <ostream>
 #include <utility>
@@ -12,6 +12,7 @@
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmCPackRPMGenerator::cmCPackRPMGenerator() = default;
@@ -21,7 +22,7 @@
 int cmCPackRPMGenerator::InitializeInternal()
 {
   this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
-  if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
+  if (cmIsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
     this->SetOption("CPACK_SET_DESTDIR", "I_ON");
   }
   /* Replace space in CPACK_PACKAGE_NAME in order to avoid
@@ -81,8 +82,7 @@
   // Tell CPackRPM.cmake the name of the component NAME.
   this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName.c_str());
   // Tell CPackRPM.cmake the path where the component is.
-  std::string component_path = "/";
-  component_path += packageName;
+  std::string component_path = cmStrCat('/', packageName);
   this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH",
                   component_path.c_str());
   if (!this->ReadListFile("Internal/CPack/CPackRPM.cmake")) {
@@ -184,8 +184,7 @@
     // The default behavior is to have one package by component group
     // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
     if (!ignoreGroup) {
-      std::map<std::string, cmCPackComponentGroup>::iterator mainCompGIt =
-        this->ComponentGroups.end();
+      auto mainCompGIt = this->ComponentGroups.end();
 
       std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
       for (compGIt = this->ComponentGroups.begin();
@@ -206,8 +205,7 @@
         retval &= PackageOnePack(initialTopLevel, compGIt->first);
       }
       // Handle Orphan components (components not belonging to any groups)
-      std::map<std::string, cmCPackComponent>::iterator mainCompIt =
-        this->Components.end();
+      auto mainCompIt = this->Components.end();
       std::map<std::string, cmCPackComponent>::iterator compIt;
       for (compIt = this->Components.begin(); compIt != this->Components.end();
            ++compIt) {
@@ -251,8 +249,7 @@
     // CPACK_COMPONENTS_IGNORE_GROUPS is set
     // We build 1 package per component
     else {
-      std::map<std::string, cmCPackComponent>::iterator mainCompIt =
-        this->Components.end();
+      auto mainCompIt = this->Components.end();
 
       std::map<std::string, cmCPackComponent>::iterator compIt;
       for (compIt = this->Components.begin(); compIt != this->Components.end();
@@ -375,8 +372,7 @@
 
   if (!compInstDirName.empty()) {
     // Tell CPackRPM.cmake the path where the component is.
-    std::string component_path = "/";
-    component_path += compInstDirName;
+    std::string component_path = cmStrCat('/', compInstDirName);
     this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH",
                     component_path.c_str());
   }
diff --git a/Source/CPack/cmCPackRPMGenerator.h b/Source/CPack/cmCPackRPMGenerator.h
index 27d3b63..075ce84 100644
--- a/Source/CPack/cmCPackRPMGenerator.h
+++ b/Source/CPack/cmCPackRPMGenerator.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackGenerator.h"
-
 #include <string>
 
+#include "cmCPackGenerator.h"
+
 /** \class cmCPackRPMGenerator
  * \brief A generator for RPM packages
  * The idea of the CPack RPM generator is to use
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx
index aba15d2..bb0ed4f 100644
--- a/Source/CPack/cmCPackSTGZGenerator.cxx
+++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -2,18 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackSTGZGenerator.h"
 
-#include "cmsys/FStream.hxx"
+#include <cstdio>
 #include <sstream>
-#include <stdio.h>
 #include <string>
 #include <vector>
 
+#include "cmsys/FStream.hxx"
+
+#include "cm_sys_stat.h"
+
+#include "cmArchiveWrite.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmSystemTools.h"
-#include "cm_sys_stat.h"
 
-cmCPackSTGZGenerator::cmCPackSTGZGenerator() = default;
+cmCPackSTGZGenerator::cmCPackSTGZGenerator()
+  : cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr", ".sh")
+{
+}
 
 cmCPackSTGZGenerator::~cmCPackSTGZGenerator() = default;
 
@@ -21,7 +27,8 @@
 {
   this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0");
 
-  std::string inFile = this->FindTemplate("CPack.STGZ_Header.sh.in");
+  std::string inFile =
+    this->FindTemplate("Internal/CPack/CPack.STGZ_Header.sh.in");
   if (inFile.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Cannot find template file: " << inFile << std::endl);
diff --git a/Source/CPack/cmCPackSTGZGenerator.h b/Source/CPack/cmCPackSTGZGenerator.h
index 9cf184b..79d7035 100644
--- a/Source/CPack/cmCPackSTGZGenerator.h
+++ b/Source/CPack/cmCPackSTGZGenerator.h
@@ -5,19 +5,19 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCPackGenerator.h"
-#include "cmCPackTGZGenerator.h"
-
 #include <iosfwd>
 
+#include "cmCPackArchiveGenerator.h"
+#include "cmCPackGenerator.h"
+
 /** \class cmCPackSTGZGenerator
  * \brief A generator for Self extractable TGZ files
  *
  */
-class cmCPackSTGZGenerator : public cmCPackTGZGenerator
+class cmCPackSTGZGenerator : public cmCPackArchiveGenerator
 {
 public:
-  cmCPackTypeMacro(cmCPackSTGZGenerator, cmCPackTGZGenerator);
+  cmCPackTypeMacro(cmCPackSTGZGenerator, cmCPackArchiveGenerator);
 
   /**
    * Construct generator
@@ -29,7 +29,6 @@
   int PackageFiles() override;
   int InitializeInternal() override;
   int GenerateHeader(std::ostream* os) override;
-  const char* GetOutputExtension() override { return ".sh"; }
 };
 
 #endif
diff --git a/Source/CPack/cmCPackTGZGenerator.cxx b/Source/CPack/cmCPackTGZGenerator.cxx
deleted file mode 100644
index 6f4676e..0000000
--- a/Source/CPack/cmCPackTGZGenerator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPackTGZGenerator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPackTGZGenerator::cmCPackTGZGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr")
-{
-}
-
-cmCPackTGZGenerator::~cmCPackTGZGenerator() = default;
diff --git a/Source/CPack/cmCPackTGZGenerator.h b/Source/CPack/cmCPackTGZGenerator.h
deleted file mode 100644
index 7be3d9d..0000000
--- a/Source/CPack/cmCPackTGZGenerator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPackTGZGenerator_h
-#define cmCPackTGZGenerator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPackTGZGenerator
- * \brief A generator for TGZ files
- *
- */
-class cmCPackTGZGenerator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPackTGZGenerator, cmCPackArchiveGenerator);
-  /**
-   * Construct generator
-   */
-  cmCPackTGZGenerator();
-  ~cmCPackTGZGenerator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".tar.gz"; }
-};
-
-#endif
diff --git a/Source/CPack/cmCPackTXZGenerator.cxx b/Source/CPack/cmCPackTXZGenerator.cxx
deleted file mode 100644
index ccbccde..0000000
--- a/Source/CPack/cmCPackTXZGenerator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPackTXZGenerator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPackTXZGenerator::cmCPackTXZGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr")
-{
-}
-
-cmCPackTXZGenerator::~cmCPackTXZGenerator() = default;
diff --git a/Source/CPack/cmCPackTXZGenerator.h b/Source/CPack/cmCPackTXZGenerator.h
deleted file mode 100644
index 4aa5973..0000000
--- a/Source/CPack/cmCPackTXZGenerator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPackTXZGenerator_h
-#define cmCPackTXZGenerator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPackTXZGenerator
- * \brief A generator for TXZ files
- *
- */
-class cmCPackTXZGenerator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPackTXZGenerator, cmCPackArchiveGenerator);
-  /**
-   * Construct generator
-   */
-  cmCPackTXZGenerator();
-  ~cmCPackTXZGenerator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".tar.xz"; }
-};
-
-#endif
diff --git a/Source/CPack/cmCPackTarBZip2Generator.cxx b/Source/CPack/cmCPackTarBZip2Generator.cxx
deleted file mode 100644
index 85abeb1..0000000
--- a/Source/CPack/cmCPackTarBZip2Generator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPackTarBZip2Generator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPackTarBZip2Generator::cmCPackTarBZip2Generator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr")
-{
-}
-
-cmCPackTarBZip2Generator::~cmCPackTarBZip2Generator() = default;
diff --git a/Source/CPack/cmCPackTarBZip2Generator.h b/Source/CPack/cmCPackTarBZip2Generator.h
deleted file mode 100644
index 7975dda..0000000
--- a/Source/CPack/cmCPackTarBZip2Generator.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPackTarBZip2Generator_h
-#define cmCPackTarBZip2Generator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPackTarBZip2Generator
- * \brief A generator for TarBZip2 files
- */
-class cmCPackTarBZip2Generator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPackTarBZip2Generator, cmCPackArchiveGenerator);
-  /**
-   * Construct generator
-   */
-  cmCPackTarBZip2Generator();
-  ~cmCPackTarBZip2Generator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".tar.bz2"; }
-};
-
-#endif
diff --git a/Source/CPack/cmCPackTarCompressGenerator.cxx b/Source/CPack/cmCPackTarCompressGenerator.cxx
deleted file mode 100644
index 55a6de5..0000000
--- a/Source/CPack/cmCPackTarCompressGenerator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPackTarCompressGenerator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPackTarCompressGenerator::cmCPackTarCompressGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, "paxr")
-{
-}
-
-cmCPackTarCompressGenerator::~cmCPackTarCompressGenerator() = default;
diff --git a/Source/CPack/cmCPackTarCompressGenerator.h b/Source/CPack/cmCPackTarCompressGenerator.h
deleted file mode 100644
index 37c7f48..0000000
--- a/Source/CPack/cmCPackTarCompressGenerator.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPackTarCompressGenerator_h
-#define cmCPackTarCompressGenerator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPackTarCompressGenerator
- * \brief A generator for TarCompress files
- */
-class cmCPackTarCompressGenerator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPackTarCompressGenerator, cmCPackArchiveGenerator);
-  /**
-   * Construct generator
-   */
-  cmCPackTarCompressGenerator();
-  ~cmCPackTarCompressGenerator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".tar.Z"; }
-};
-
-#endif
diff --git a/Source/CPack/cmCPackZIPGenerator.cxx b/Source/CPack/cmCPackZIPGenerator.cxx
deleted file mode 100644
index f06494c..0000000
--- a/Source/CPack/cmCPackZIPGenerator.cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmCPackZIPGenerator.h"
-
-#include "cmArchiveWrite.h"
-#include "cmCPackArchiveGenerator.h"
-
-cmCPackZIPGenerator::cmCPackZIPGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "zip")
-{
-}
-
-cmCPackZIPGenerator::~cmCPackZIPGenerator() = default;
diff --git a/Source/CPack/cmCPackZIPGenerator.h b/Source/CPack/cmCPackZIPGenerator.h
deleted file mode 100644
index 58ec79e..0000000
--- a/Source/CPack/cmCPackZIPGenerator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmCPackZIPGenerator_h
-#define cmCPackZIPGenerator_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmCPackArchiveGenerator.h"
-#include "cmCPackGenerator.h"
-
-/** \class cmCPackZIPGenerator
- * \brief A generator for ZIP files
- */
-class cmCPackZIPGenerator : public cmCPackArchiveGenerator
-{
-public:
-  cmCPackTypeMacro(cmCPackZIPGenerator, cmCPackArchiveGenerator);
-
-  /**
-   * Construct generator
-   */
-  cmCPackZIPGenerator();
-  ~cmCPackZIPGenerator() override;
-
-protected:
-  const char* GetOutputExtension() override { return ".zip"; }
-};
-
-#endif
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index 58b9e70..5895652 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -3,18 +3,6 @@
 
 #include "cmsys/CommandLineArguments.hxx"
 #include "cmsys/Encoding.hxx"
-#include <iostream>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stddef.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
-#  include "cmsys/ConsoleBuf.hxx"
-#endif
 
 #include "cmCPackGenerator.h"
 #include "cmCPackGeneratorFactory.h"
@@ -26,22 +14,37 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-static const char* cmDocumentationName[][2] = {
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
+#  include "cmsys/ConsoleBuf.hxx"
+#endif
+
+#include <cstddef>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace {
+const char* cmDocumentationName[][2] = {
   { nullptr, "  cpack - Packaging driver provided by CMake." },
   { nullptr, nullptr }
 };
 
-static const char* cmDocumentationUsage[][2] = {
+const char* cmDocumentationUsage[][2] = {
   // clang-format off
   { nullptr, "  cpack [options]" },
   { nullptr, nullptr }
   // clang-format on
 };
 
-static const char* cmDocumentationOptions[][2] = {
+const char* cmDocumentationOptions[][2] = {
   { "-G <generators>", "Override/define CPACK_GENERATOR" },
   { "-C <Configuration>", "Specify the project configuration" },
   { "-D <var>=<value>", "Set a CPack variable." },
@@ -64,7 +67,7 @@
 
 struct cpackDefinitions
 {
-  typedef std::map<std::string, std::string> MapType;
+  using MapType = std::map<std::string, std::string>;
   MapType Map;
   cmCPackLog* Log;
 };
@@ -90,16 +93,17 @@
   return 1;
 }
 
-static void cpackProgressCallback(const std::string& message, float /*unused*/)
+void cpackProgressCallback(const std::string& message, float /*unused*/)
 {
   std::cout << "-- " << message << std::endl;
 }
+} // namespace
 
 // this is CPack.
 int main(int argc, char const* const* argv)
 {
   cmSystemTools::EnsureStdPipes();
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
   // Replace streambuf so we can output Unicode to console
   cmsys::ConsoleBuf::Manager consoleOut(std::cout);
   consoleOut.SetUTF8Pipes();
@@ -154,7 +158,7 @@
 
   cmsys::CommandLineArguments arg;
   arg.Initialize(argc, argv);
-  typedef cmsys::CommandLineArguments argT;
+  using argT = cmsys::CommandLineArguments;
   // Help arguments
   arg.AddArgument("--help", argT::NO_ARGUMENT, &help, "CPack help");
   arg.AddArgument("--help-full", argT::SPACE_ARGUMENT, &helpFull,
@@ -227,14 +231,13 @@
 
   bool cpackConfigFileSpecified = true;
   if (cpackConfigFile.empty()) {
-    cpackConfigFile = cmSystemTools::GetCurrentWorkingDirectory();
-    cpackConfigFile += "/CPackConfig.cmake";
+    cpackConfigFile = cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(),
+                               "/CPackConfig.cmake");
     cpackConfigFileSpecified = false;
   }
 
   cmCPackGeneratorFactory generators;
   generators.SetLogger(&log);
-  cmCPackGenerator* cpackGenerator = nullptr;
 
   cmDocumentation doc;
   doc.addCPackStandardDocSections();
@@ -269,7 +272,7 @@
     }
 
     if (!cpackBuildConfig.empty()) {
-      globalMF.AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig.c_str());
+      globalMF.AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig);
     }
 
     if (cmSystemTools::FileExists(cpackConfigFile)) {
@@ -291,24 +294,21 @@
     }
 
     if (!generator.empty()) {
-      globalMF.AddDefinition("CPACK_GENERATOR", generator.c_str());
+      globalMF.AddDefinition("CPACK_GENERATOR", generator);
     }
     if (!cpackProjectName.empty()) {
-      globalMF.AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName.c_str());
+      globalMF.AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName);
     }
     if (!cpackProjectVersion.empty()) {
-      globalMF.AddDefinition("CPACK_PACKAGE_VERSION",
-                             cpackProjectVersion.c_str());
+      globalMF.AddDefinition("CPACK_PACKAGE_VERSION", cpackProjectVersion);
     }
     if (!cpackProjectVendor.empty()) {
-      globalMF.AddDefinition("CPACK_PACKAGE_VENDOR",
-                             cpackProjectVendor.c_str());
+      globalMF.AddDefinition("CPACK_PACKAGE_VENDOR", cpackProjectVendor);
     }
     // if this is not empty it has been set on the command line
     // go for it. Command line override values set in config file.
     if (!cpackProjectDirectory.empty()) {
-      globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY",
-                             cpackProjectDirectory.c_str());
+      globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
     }
     // The value has not been set on the command line
     else {
@@ -317,11 +317,11 @@
       // use default value iff no value has been provided by the config file
       if (!globalMF.IsSet("CPACK_PACKAGE_DIRECTORY")) {
         globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY",
-                               cpackProjectDirectory.c_str());
+                               cpackProjectDirectory);
       }
     }
     for (auto const& cd : definitions.Map) {
-      globalMF.AddDefinition(cd.first, cd.second.c_str());
+      globalMF.AddDefinition(cd.first, cd.second);
     }
 
     const char* cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH");
@@ -333,8 +333,7 @@
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "CPack generator not specified" << std::endl);
     } else {
-      std::vector<std::string> generatorsVector;
-      cmSystemTools::ExpandListArgument(genList, generatorsVector);
+      std::vector<std::string> generatorsVector = cmExpandedList(genList);
       for (std::string const& gen : generatorsVector) {
         cmMakefile::ScopePushPop raii(&globalMF);
         cmMakefile* mf = &globalMF;
@@ -361,7 +360,8 @@
           parsed = 0;
         }
         if (parsed) {
-          cpackGenerator = generators.NewGenerator(gen);
+          std::unique_ptr<cmCPackGenerator> cpackGenerator =
+            generators.NewGenerator(gen);
           if (cpackGenerator) {
             cpackGenerator->SetTrace(trace);
             cpackGenerator->SetTraceExpand(traceExpand);
@@ -425,7 +425,7 @@
               std::ostringstream ostr;
               ostr << projVersionMajor << "." << projVersionMinor << "."
                    << projVersionPatch;
-              mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str().c_str());
+              mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str());
             }
 
             int res = cpackGenerator->DoPackage();
diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx
index b957856..8640c46 100644
--- a/Source/CTest/cmCTestBZR.cxx
+++ b/Source/CTest/cmCTestBZR.cxx
@@ -2,6 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestBZR.h"
 
+#include <cstdlib>
+#include <list>
+#include <map>
+#include <ostream>
+#include <vector>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_expat.h"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestVC.h"
@@ -9,14 +19,6 @@
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 
-#include "cm_expat.h"
-#include "cmsys/RegularExpression.hxx"
-#include <list>
-#include <map>
-#include <ostream>
-#include <stdlib.h>
-#include <vector>
-
 extern "C" int cmBZRXMLParserUnknownEncodingHandler(void* /*unused*/,
                                                     const XML_Char* name,
                                                     XML_Encoding* info)
@@ -200,8 +202,8 @@
 private:
   cmCTestBZR* BZR;
 
-  typedef cmCTestBZR::Revision Revision;
-  typedef cmCTestBZR::Change Change;
+  using Revision = cmCTestBZR::Revision;
+  using Change = cmCTestBZR::Change;
   Revision Rev;
   std::vector<Change> Changes;
   Change CurChange;
diff --git a/Source/CTest/cmCTestBZR.h b/Source/CTest/cmCTestBZR.h
index d5c78c7..d7c6321 100644
--- a/Source/CTest/cmCTestBZR.h
+++ b/Source/CTest/cmCTestBZR.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGlobalVC.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmCTestGlobalVC.h"
+
 class cmCTest;
 
 /** \class cmCTestBZR
diff --git a/Source/CTest/cmCTestBinPacker.cxx b/Source/CTest/cmCTestBinPacker.cxx
new file mode 100644
index 0000000..e9e3bee
--- /dev/null
+++ b/Source/CTest/cmCTestBinPacker.cxx
@@ -0,0 +1,201 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCTestBinPacker.h"
+
+#include <algorithm>
+#include <utility>
+
+bool cmCTestBinPackerAllocation::operator==(
+  const cmCTestBinPackerAllocation& other) const
+{
+  return this->ProcessIndex == other.ProcessIndex &&
+    this->SlotsNeeded == other.SlotsNeeded && this->Id == other.Id;
+}
+
+bool cmCTestBinPackerAllocation::operator!=(
+  const cmCTestBinPackerAllocation& other) const
+{
+  return !(*this == other);
+}
+
+namespace {
+
+/*
+ * The following algorithm is used to do two things:
+ *
+ * 1) Determine if a test's hardware requirements can fit within the hardware
+ *    present on the system, and
+ * 2) Do the actual allocation
+ *
+ * This algorithm performs a recursive search, looking for a bin pack that will
+ * fit the specified requirements. It has a template to specify different
+ * optimization strategies. If it ever runs out of room, it backtracks as far
+ * down the stack as it needs to and tries a different combination until no
+ * more combinations can be tried.
+ */
+template <typename AllocationStrategy>
+static bool AllocateCTestHardware(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  const std::vector<std::string>& hardwareSorted, std::size_t currentIndex,
+  std::vector<cmCTestBinPackerAllocation*>& allocations)
+{
+  // Iterate through all large enough resources until we find a solution
+  std::size_t hardwareIndex = 0;
+  while (hardwareIndex < hardwareSorted.size()) {
+    auto const& resource = hardware.at(hardwareSorted[hardwareIndex]);
+    if (resource.Free() >=
+        static_cast<unsigned int>(allocations[currentIndex]->SlotsNeeded)) {
+      // Preemptively allocate the resource
+      allocations[currentIndex]->Id = hardwareSorted[hardwareIndex];
+      if (currentIndex + 1 >= allocations.size()) {
+        // We have a solution
+        return true;
+      }
+
+      // Move the resource up the list until it is sorted again
+      auto hardware2 = hardware;
+      auto hardwareSorted2 = hardwareSorted;
+      hardware2[hardwareSorted2[hardwareIndex]].Locked +=
+        allocations[currentIndex]->SlotsNeeded;
+      AllocationStrategy::IncrementalSort(hardware2, hardwareSorted2,
+                                          hardwareIndex);
+
+      // Recurse one level deeper
+      if (AllocateCTestHardware<AllocationStrategy>(
+            hardware2, hardwareSorted2, currentIndex + 1, allocations)) {
+        return true;
+      }
+    }
+
+    // No solution found here, deallocate the resource and try the next one
+    allocations[currentIndex]->Id.clear();
+    auto freeSlots = hardware.at(hardwareSorted.at(hardwareIndex)).Free();
+    do {
+      ++hardwareIndex;
+    } while (hardwareIndex < hardwareSorted.size() &&
+             hardware.at(hardwareSorted.at(hardwareIndex)).Free() ==
+               freeSlots);
+  }
+
+  // No solution was found
+  return false;
+}
+
+template <typename AllocationStrategy>
+static bool AllocateCTestHardware(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+  // Sort the resource requirements in descending order by slots needed
+  std::vector<cmCTestBinPackerAllocation*> allocationsPtr;
+  allocationsPtr.reserve(allocations.size());
+  for (auto& allocation : allocations) {
+    allocationsPtr.push_back(&allocation);
+  }
+  std::stable_sort(
+    allocationsPtr.rbegin(), allocationsPtr.rend(),
+    [](cmCTestBinPackerAllocation* a1, cmCTestBinPackerAllocation* a2) {
+      return a1->SlotsNeeded < a2->SlotsNeeded;
+    });
+
+  // Sort the resources according to sort strategy
+  std::vector<std::string> hardwareSorted;
+  hardwareSorted.reserve(hardware.size());
+  for (auto const& hw : hardware) {
+    hardwareSorted.push_back(hw.first);
+  }
+  AllocationStrategy::InitialSort(hardware, hardwareSorted);
+
+  // Do the actual allocation
+  return AllocateCTestHardware<AllocationStrategy>(
+    hardware, hardwareSorted, std::size_t(0), allocationsPtr);
+}
+
+class RoundRobinAllocationStrategy
+{
+public:
+  static void InitialSort(
+    const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+    std::vector<std::string>& hardwareSorted);
+
+  static void IncrementalSort(
+    const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+    std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex);
+};
+
+void RoundRobinAllocationStrategy::InitialSort(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<std::string>& hardwareSorted)
+{
+  std::stable_sort(
+    hardwareSorted.rbegin(), hardwareSorted.rend(),
+    [&hardware](const std::string& id1, const std::string& id2) {
+      return hardware.at(id1).Free() < hardware.at(id2).Free();
+    });
+}
+
+void RoundRobinAllocationStrategy::IncrementalSort(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex)
+{
+  auto tmp = hardwareSorted[lastAllocatedIndex];
+  std::size_t i = lastAllocatedIndex;
+  while (i < hardwareSorted.size() - 1 &&
+         hardware.at(hardwareSorted[i + 1]).Free() > hardware.at(tmp).Free()) {
+    hardwareSorted[i] = hardwareSorted[i + 1];
+    ++i;
+  }
+  hardwareSorted[i] = tmp;
+}
+
+class BlockAllocationStrategy
+{
+public:
+  static void InitialSort(
+    const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+    std::vector<std::string>& hardwareSorted);
+
+  static void IncrementalSort(
+    const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+    std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex);
+};
+
+void BlockAllocationStrategy::InitialSort(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<std::string>& hardwareSorted)
+{
+  std::stable_sort(
+    hardwareSorted.rbegin(), hardwareSorted.rend(),
+    [&hardware](const std::string& id1, const std::string& id2) {
+      return hardware.at(id1).Free() < hardware.at(id2).Free();
+    });
+}
+
+void BlockAllocationStrategy::IncrementalSort(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>&,
+  std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex)
+{
+  auto tmp = hardwareSorted[lastAllocatedIndex];
+  std::size_t i = lastAllocatedIndex;
+  while (i > 0) {
+    hardwareSorted[i] = hardwareSorted[i - 1];
+    --i;
+  }
+  hardwareSorted[i] = tmp;
+}
+}
+
+bool cmAllocateCTestHardwareRoundRobin(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+  return AllocateCTestHardware<RoundRobinAllocationStrategy>(hardware,
+                                                             allocations);
+}
+
+bool cmAllocateCTestHardwareBlock(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+  return AllocateCTestHardware<BlockAllocationStrategy>(hardware, allocations);
+}
diff --git a/Source/CTest/cmCTestBinPacker.h b/Source/CTest/cmCTestBinPacker.h
new file mode 100644
index 0000000..54f03d7
--- /dev/null
+++ b/Source/CTest/cmCTestBinPacker.h
@@ -0,0 +1,31 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCTestBinPacker_h
+#define cmCTestBinPacker_h
+
+#include <cstddef>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareAllocator.h"
+
+struct cmCTestBinPackerAllocation
+{
+  std::size_t ProcessIndex;
+  int SlotsNeeded;
+  std::string Id;
+
+  bool operator==(const cmCTestBinPackerAllocation& other) const;
+  bool operator!=(const cmCTestBinPackerAllocation& other) const;
+};
+
+bool cmAllocateCTestHardwareRoundRobin(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<cmCTestBinPackerAllocation>& allocations);
+
+bool cmAllocateCTestHardwareBlock(
+  const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+  std::vector<cmCTestBinPackerAllocation>& allocations);
+
+#endif
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index 9ad9669..2ad661c 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -2,21 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestBuildAndTestHandler.h"
 
+#include <chrono>
+#include <cstdlib>
+#include <cstring>
+#include <ratio>
+
+#include "cmsys/Process.h"
+
 #include "cmCTest.h"
 #include "cmCTestTestHandler.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkingDirectory.h"
 #include "cmake.h"
 
-#include "cmsys/Process.h"
-#include <chrono>
-#include <cstring>
-#include <ratio>
-#include <stdlib.h>
-
 cmCTestBuildAndTestHandler::cmCTestBuildAndTestHandler()
 {
   this->BuildTwoConfig = false;
@@ -76,6 +78,11 @@
   if (config) {
     args.push_back("-DCMAKE_BUILD_TYPE:STRING=" + std::string(config));
   }
+  if (!this->BuildMakeProgram.empty() &&
+      (this->BuildGenerator.find("Make") != std::string::npos ||
+       this->BuildGenerator.find("Ninja") != std::string::npos)) {
+    args.push_back("-DCMAKE_MAKE_PROGRAM:FILEPATH=" + this->BuildMakeProgram);
+  }
 
   for (std::string const& opt : this->BuildOptions) {
     args.push_back(opt);
@@ -284,9 +291,8 @@
   std::vector<std::string> extraPaths;
   // if this->ExecutableDirectory is set try that as well
   if (!this->ExecutableDirectory.empty()) {
-    std::string tempPath = this->ExecutableDirectory;
-    tempPath += "/";
-    tempPath += this->TestCommand;
+    std::string tempPath =
+      cmStrCat(this->ExecutableDirectory, '/', this->TestCommand);
     extraPaths.push_back(tempPath);
   }
   std::vector<std::string> failed;
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.h b/Source/CTest/cmCTestBuildAndTestHandler.h
index 2d47b15..0c8a040 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.h
+++ b/Source/CTest/cmCTestBuildAndTestHandler.h
@@ -5,14 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-#include "cmDuration.h"
-
 #include <sstream>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
+#include "cmCTestGenericHandler.h"
+#include "cmDuration.h"
+
 class cmake;
 
 /** \class cmCTestBuildAndTestHandler
@@ -22,7 +23,7 @@
 class cmCTestBuildAndTestHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /*
    * The main entry point for this class
diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx
index 2eacaf1..18df214 100644
--- a/Source/CTest/cmCTestBuildCommand.cxx
+++ b/Source/CTest/cmCTestBuildCommand.cxx
@@ -2,30 +2,31 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestBuildCommand.h"
 
+#include <cstring>
+#include <sstream>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmCTest.h"
 #include "cmCTestBuildHandler.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <sstream>
-#include <string.h>
-
 class cmExecutionStatus;
 
-cmCTestBuildCommand::cmCTestBuildCommand()
+void cmCTestBuildCommand::BindArguments()
 {
-  this->GlobalGenerator = nullptr;
-  this->Arguments[ctb_NUMBER_ERRORS] = "NUMBER_ERRORS";
-  this->Arguments[ctb_NUMBER_WARNINGS] = "NUMBER_WARNINGS";
-  this->Arguments[ctb_TARGET] = "TARGET";
-  this->Arguments[ctb_CONFIGURATION] = "CONFIGURATION";
-  this->Arguments[ctb_FLAGS] = "FLAGS";
-  this->Arguments[ctb_PROJECT_NAME] = "PROJECT_NAME";
-  this->Arguments[ctb_LAST] = nullptr;
-  this->Last = ctb_LAST;
+  this->cmCTestHandlerCommand::BindArguments();
+  this->Bind("NUMBER_ERRORS"_s, this->NumberErrors);
+  this->Bind("NUMBER_WARNINGS"_s, this->NumberWarnings);
+  this->Bind("TARGET"_s, this->Target);
+  this->Bind("CONFIGURATION"_s, this->Configuration);
+  this->Bind("FLAGS"_s, this->Flags);
+  this->Bind("PROJECT_NAME"_s, this->ProjectName);
 }
 
 cmCTestBuildCommand::~cmCTestBuildCommand()
@@ -59,20 +60,17 @@
     //
     const char* ctestBuildConfiguration =
       this->Makefile->GetDefinition("CTEST_BUILD_CONFIGURATION");
-    const char* cmakeBuildConfiguration =
-      (this->Values[ctb_CONFIGURATION] && *this->Values[ctb_CONFIGURATION])
-      ? this->Values[ctb_CONFIGURATION]
+    const char* cmakeBuildConfiguration = !this->Configuration.empty()
+      ? this->Configuration.c_str()
       : ((ctestBuildConfiguration && *ctestBuildConfiguration)
            ? ctestBuildConfiguration
            : this->CTest->GetConfigType().c_str());
 
-    const char* cmakeBuildAdditionalFlags =
-      (this->Values[ctb_FLAGS] && *this->Values[ctb_FLAGS])
-      ? this->Values[ctb_FLAGS]
+    const char* cmakeBuildAdditionalFlags = !this->Flags.empty()
+      ? this->Flags.c_str()
       : this->Makefile->GetDefinition("CTEST_BUILD_FLAGS");
-    const char* cmakeBuildTarget =
-      (this->Values[ctb_TARGET] && *this->Values[ctb_TARGET])
-      ? this->Values[ctb_TARGET]
+    const char* cmakeBuildTarget = !this->Target.empty()
+      ? this->Target.c_str()
       : this->Makefile->GetDefinition("CTEST_BUILD_TARGET");
 
     if (cmakeGeneratorName && *cmakeGeneratorName) {
@@ -90,9 +88,8 @@
           this->Makefile->GetCMakeInstance()->CreateGlobalGenerator(
             cmakeGeneratorName);
         if (!this->GlobalGenerator) {
-          std::string e = "could not create generator named \"";
-          e += cmakeGeneratorName;
-          e += "\"";
+          std::string e = cmStrCat("could not create generator named \"",
+                                   cmakeGeneratorName, '"');
           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
           cmSystemTools::SetFatalErrorOccured();
           return nullptr;
@@ -153,18 +150,13 @@
                                       cmExecutionStatus& status)
 {
   bool ret = cmCTestHandlerCommand::InitialPass(args, status);
-  if (this->Values[ctb_NUMBER_ERRORS] && *this->Values[ctb_NUMBER_ERRORS]) {
-    std::ostringstream str;
-    str << this->Handler->GetTotalErrors();
-    this->Makefile->AddDefinition(this->Values[ctb_NUMBER_ERRORS],
-                                  str.str().c_str());
+  if (!this->NumberErrors.empty()) {
+    this->Makefile->AddDefinition(
+      this->NumberErrors, std::to_string(this->Handler->GetTotalErrors()));
   }
-  if (this->Values[ctb_NUMBER_WARNINGS] &&
-      *this->Values[ctb_NUMBER_WARNINGS]) {
-    std::ostringstream str;
-    str << this->Handler->GetTotalWarnings();
-    this->Makefile->AddDefinition(this->Values[ctb_NUMBER_WARNINGS],
-                                  str.str().c_str());
+  if (!this->NumberWarnings.empty()) {
+    this->Makefile->AddDefinition(
+      this->NumberWarnings, std::to_string(this->Handler->GetTotalWarnings()));
   }
   return ret;
 }
diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h
index 77b0549..da00a43 100644
--- a/Source/CTest/cmCTestBuildCommand.h
+++ b/Source/CTest/cmCTestBuildCommand.h
@@ -5,14 +5,17 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
 #include <string>
+#include <utility>
 #include <vector>
 
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
+
 class cmCTestBuildHandler;
 class cmCTestGenericHandler;
-class cmCommand;
 class cmExecutionStatus;
 class cmGlobalGenerator;
 
@@ -24,18 +27,17 @@
 class cmCTestBuildCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestBuildCommand();
   ~cmCTestBuildCommand() override;
 
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestBuildCommand* ni = new cmCTestBuildCommand;
+    auto ni = cm::make_unique<cmCTestBuildCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
@@ -46,23 +48,19 @@
   bool InitialPass(std::vector<std::string> const& args,
                    cmExecutionStatus& status) override;
 
-  cmGlobalGenerator* GlobalGenerator;
+  cmGlobalGenerator* GlobalGenerator = nullptr;
 
 protected:
   cmCTestBuildHandler* Handler;
-  enum
-  {
-    ctb_BUILD = ct_LAST,
-    ctb_NUMBER_ERRORS,
-    ctb_NUMBER_WARNINGS,
-    ctb_TARGET,
-    ctb_CONFIGURATION,
-    ctb_FLAGS,
-    ctb_PROJECT_NAME,
-    ctb_LAST
-  };
-
+  void BindArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
+
+  std::string NumberErrors;
+  std::string NumberWarnings;
+  std::string Target;
+  std::string Configuration;
+  std::string Flags;
+  std::string ProjectName;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index c8e4fa1..9cb5449 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -2,6 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestBuildHandler.h"
 
+#include <cstdlib>
+#include <cstring>
+#include <set>
+#include <utility>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmDuration.h"
@@ -9,17 +18,11 @@
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
+#include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 
-#include "cmsys/Directory.hxx"
-#include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
-#include <set>
-#include <stdlib.h>
-#include <string.h>
-#include <utility>
-
 static const char* cmCTestErrorMatches[] = {
   "^[Bb]us [Ee]rror",
   "^[Ss]egmentation [Vv]iolation",
@@ -246,13 +249,11 @@
   // Record the user-specified custom warning rules.
   if (const char* customWarningMatchers =
         mf->GetDefinition("CTEST_CUSTOM_WARNING_MATCH")) {
-    cmSystemTools::ExpandListArgument(customWarningMatchers,
-                                      this->ReallyCustomWarningMatches);
+    cmExpandList(customWarningMatchers, this->ReallyCustomWarningMatches);
   }
   if (const char* customWarningExceptions =
         mf->GetDefinition("CTEST_CUSTOM_WARNING_EXCEPTION")) {
-    cmSystemTools::ExpandListArgument(customWarningExceptions,
-                                      this->ReallyCustomWarningExceptions);
+    cmExpandList(customWarningExceptions, this->ReallyCustomWarningExceptions);
   }
 }
 
@@ -327,7 +328,7 @@
 
   std::string const& useLaunchers =
     this->CTest->GetCTestConfiguration("UseLaunchers");
-  this->UseCTestLaunch = cmSystemTools::IsOn(useLaunchers);
+  this->UseCTestLaunch = cmIsOn(useLaunchers);
 
   // Create a last build log
   cmGeneratedFileStream ofs;
@@ -384,11 +385,8 @@
   if (this->CTest->GetCTestConfiguration("SourceDirectory").size() > 20) {
     std::string srcdir =
       this->CTest->GetCTestConfiguration("SourceDirectory") + "/";
-    std::string srcdirrep;
     for (cc = srcdir.size() - 2; cc > 0; cc--) {
       if (srcdir[cc] == '/') {
-        srcdirrep = srcdir.substr(cc);
-        srcdirrep = "/..." + srcdirrep;
         srcdir = srcdir.substr(0, cc + 1);
         break;
       }
@@ -398,11 +396,8 @@
   if (this->CTest->GetCTestConfiguration("BuildDirectory").size() > 20) {
     std::string bindir =
       this->CTest->GetCTestConfiguration("BuildDirectory") + "/";
-    std::string bindirrep;
     for (cc = bindir.size() - 2; cc > 0; cc--) {
       if (bindir[cc] == '/') {
-        bindirrep = bindir.substr(cc);
-        bindirrep = "/..." + bindirrep;
         bindir = bindir.substr(0, cc + 1);
         break;
       }
@@ -415,6 +410,9 @@
   // Remember start build time
   this->StartBuild = this->CTest->CurrentTime();
   this->StartBuildTime = std::chrono::system_clock::now();
+
+  cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
+  this->ColorRemover = &colorRemover;
   int retVal = 0;
   int res = cmsysProcess_State_Exited;
   if (!this->CTest->GetShowOnly()) {
@@ -532,7 +530,7 @@
   // Sort XML fragments in chronological order.
   cmFileTimeCache ftc;
   FragmentCompare fragmentCompare(&ftc);
-  typedef std::set<std::string, FragmentCompare> Fragments;
+  using Fragments = std::set<std::string, FragmentCompare>;
   Fragments fragments(fragmentCompare);
 
   // only report the first 50 warnings and first 50 errors
@@ -572,8 +570,7 @@
   std::string srcdir = this->CTest->GetCTestConfiguration("SourceDirectory");
   // make sure the source dir is in the correct case on windows
   // via a call to collapse full path.
-  srcdir = cmSystemTools::CollapseFullPath(srcdir);
-  srcdir += "/";
+  srcdir = cmStrCat(cmSystemTools::CollapseFullPath(srcdir), '/');
   for (it = ew.begin();
        it != ew.end() && (numErrorsAllowed || numWarningsAllowed); it++) {
     cmCTestBuildErrorWarning* cm = &(*it);
@@ -704,10 +701,8 @@
   } else {
     // Compute a directory in which to store launcher fragments.
     std::string& launchDir = this->Handler->CTestLaunchDir;
-    launchDir = this->CTest->GetBinaryDir();
-    launchDir += "/Testing/";
-    launchDir += tag;
-    launchDir += "/Build";
+    launchDir =
+      cmStrCat(this->CTest->GetBinaryDir(), "/Testing/", tag, "/Build");
 
     // Clean out any existing launcher fragments.
     cmSystemTools::RemoveADirectory(launchDir);
@@ -716,8 +711,7 @@
       // Enable launcher fragments.
       cmSystemTools::MakeDirectory(launchDir);
       this->WriteLauncherConfig();
-      std::string launchEnv = "CTEST_LAUNCH_LOGS=";
-      launchEnv += launchDir;
+      std::string launchEnv = cmStrCat("CTEST_LAUNCH_LOGS=", launchDir);
       cmSystemTools::PutEnv(launchEnv);
     }
   }
@@ -743,8 +737,8 @@
                             this->Handler->ReallyCustomWarningExceptions);
 
   // Give some testing configuration information to the launcher.
-  std::string fname = this->Handler->CTestLaunchDir;
-  fname += "/CTestLaunchConfig.cmake";
+  std::string fname =
+    cmStrCat(this->Handler->CTestLaunchDir, "/CTestLaunchConfig.cmake");
   cmGeneratedFileStream fout(fname);
   std::string srcdir = this->CTest->GetCTestConfiguration("SourceDirectory");
   fout << "set(CTEST_SOURCE_DIRECTORY \"" << srcdir << "\")\n";
@@ -756,10 +750,8 @@
   if (matchers.empty()) {
     return;
   }
-  std::string fname = this->Handler->CTestLaunchDir;
-  fname += "/Custom";
-  fname += purpose;
-  fname += ".txt";
+  std::string fname =
+    cmStrCat(this->Handler->CTestLaunchDir, "/Custom", purpose, ".txt");
   cmGeneratedFileStream fout(fname);
   for (std::string const& m : matchers) {
     fout << m << "\n";
@@ -898,9 +890,8 @@
         // dashboard.
         cmCTestBuildErrorWarning errorwarning;
         errorwarning.LogLine = 1;
-        errorwarning.Text =
-          "*** WARNING non-zero return value in ctest from: ";
-        errorwarning.Text += argv[0];
+        errorwarning.Text = cmStrCat(
+          "*** WARNING non-zero return value in ctest from: ", argv[0]);
         errorwarning.PreContext.clear();
         errorwarning.PostContext.clear();
         errorwarning.Error = false;
@@ -922,8 +913,8 @@
     // If there was an error running command, report that on the dashboard.
     cmCTestBuildErrorWarning errorwarning;
     errorwarning.LogLine = 1;
-    errorwarning.Text = "*** ERROR executing: ";
-    errorwarning.Text += cmsysProcess_GetErrorString(cp);
+    errorwarning.Text =
+      cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
     errorwarning.PreContext.clear();
     errorwarning.PostContext.clear();
     errorwarning.Error = true;
@@ -1084,7 +1075,12 @@
     return b_REGULAR_LINE;
   }
 
-  cmCTestOptionalLog(this->CTest, DEBUG, "Line: [" << data << "]" << std::endl,
+  // Ignore ANSI color codes when checking for errors and warnings.
+  std::string input(data);
+  std::string line;
+  this->ColorRemover->Replace(input, line);
+
+  cmCTestOptionalLog(this->CTest, DEBUG, "Line: [" << line << "]" << std::endl,
                      this->Quiet);
 
   int warningLine = 0;
@@ -1096,10 +1092,10 @@
     // Errors
     int wrxCnt = 0;
     for (cmsys::RegularExpression& rx : this->ErrorMatchRegex) {
-      if (rx.find(data)) {
+      if (rx.find(line.c_str())) {
         errorLine = 1;
         cmCTestOptionalLog(this->CTest, DEBUG,
-                           "  Error Line: " << data << " (matches: "
+                           "  Error Line: " << line << " (matches: "
                                             << this->CustomErrorMatches[wrxCnt]
                                             << ")" << std::endl,
                            this->Quiet);
@@ -1110,11 +1106,11 @@
     // Error exceptions
     wrxCnt = 0;
     for (cmsys::RegularExpression& rx : this->ErrorExceptionRegex) {
-      if (rx.find(data)) {
+      if (rx.find(line.c_str())) {
         errorLine = 0;
         cmCTestOptionalLog(this->CTest, DEBUG,
                            "  Not an error Line: "
-                             << data << " (matches: "
+                             << line << " (matches: "
                              << this->CustomErrorExceptions[wrxCnt] << ")"
                              << std::endl,
                            this->Quiet);
@@ -1127,11 +1123,11 @@
     // Warnings
     int wrxCnt = 0;
     for (cmsys::RegularExpression& rx : this->WarningMatchRegex) {
-      if (rx.find(data)) {
+      if (rx.find(line.c_str())) {
         warningLine = 1;
         cmCTestOptionalLog(this->CTest, DEBUG,
                            "  Warning Line: "
-                             << data << " (matches: "
+                             << line << " (matches: "
                              << this->CustomWarningMatches[wrxCnt] << ")"
                              << std::endl,
                            this->Quiet);
@@ -1143,11 +1139,11 @@
     wrxCnt = 0;
     // Warning exceptions
     for (cmsys::RegularExpression& rx : this->WarningExceptionRegex) {
-      if (rx.find(data)) {
+      if (rx.find(line.c_str())) {
         warningLine = 0;
         cmCTestOptionalLog(this->CTest, DEBUG,
                            "  Not a warning Line: "
-                             << data << " (matches: "
+                             << line << " (matches: "
                              << this->CustomWarningExceptions[wrxCnt] << ")"
                              << std::endl,
                            this->Quiet);
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h
index 722c590..a5193f6 100644
--- a/Source/CTest/cmCTestBuildHandler.h
+++ b/Source/CTest/cmCTestBuildHandler.h
@@ -5,19 +5,22 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-
-#include "cmDuration.h"
-#include "cmProcessOutput.h"
-#include "cmsys/RegularExpression.hxx"
 #include <chrono>
 #include <deque>
 #include <iosfwd>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmCTestGenericHandler.h"
+#include "cmDuration.h"
+#include "cmProcessOutput.h"
+
 class cmMakefile;
+class cmStringReplaceHelper;
 class cmXMLWriter;
 
 /** \class cmCTestBuildHandler
@@ -27,8 +30,8 @@
 class cmCTestBuildHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
-  typedef cmProcessOutput::Encoding Encoding;
+  using Superclass = cmCTestGenericHandler;
+  using Encoding = cmProcessOutput::Encoding;
 
   /*
    * The main entry point for this class
@@ -110,7 +113,7 @@
   std::vector<cmsys::RegularExpression> WarningMatchRegex;
   std::vector<cmsys::RegularExpression> WarningExceptionRegex;
 
-  typedef std::deque<char> t_BuildProcessingQueueType;
+  using t_BuildProcessingQueueType = std::deque<char>;
 
   void ProcessBuffer(const char* data, size_t length, size_t& tick,
                      size_t tick_len, std::ostream& ofs,
@@ -125,7 +128,7 @@
   std::string SimplifySourceDir;
   std::string SimplifyBuildDir;
   size_t OutputLineCounter;
-  typedef std::vector<cmCTestBuildErrorWarning> t_ErrorsAndWarningsVector;
+  using t_ErrorsAndWarningsVector = std::vector<cmCTestBuildErrorWarning>;
   t_ErrorsAndWarningsVector ErrorsAndWarnings;
   t_ErrorsAndWarningsVector::iterator LastErrorOrWarning;
   size_t PostContextCount;
@@ -143,6 +146,9 @@
   int MaxErrors;
   int MaxWarnings;
 
+  // Used to remove ANSI color codes before checking for errors and warnings.
+  cmStringReplaceHelper* ColorRemover;
+
   bool UseCTestLaunch;
   std::string CTestLaunchDir;
   class LaunchHelper;
diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx
index 9c03839..45ec390 100644
--- a/Source/CTest/cmCTestCVS.cxx
+++ b/Source/CTest/cmCTestCVS.cxx
@@ -2,14 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestCVS.h"
 
-#include "cmCTest.h"
-#include "cmProcessTools.h"
-#include "cmSystemTools.h"
-#include "cmXMLWriter.h"
+#include <utility>
+
+#include <cm/string_view>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
-#include <utility>
+
+#include "cmCTest.h"
+#include "cmProcessTools.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmXMLWriter.h"
 
 cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log)
   : cmCTestVC(ct, log)
@@ -103,7 +107,7 @@
 class cmCTestCVS::LogParser : public cmCTestVC::LineParser
 {
 public:
-  typedef cmCTestCVS::Revision Revision;
+  using Revision = cmCTestCVS::Revision;
   LogParser(cmCTestCVS* cvs, const char* prefix, std::vector<Revision>& revs)
     : CVS(cvs)
     , Revisions(revs)
@@ -204,8 +208,7 @@
   if (tagStream && cmSystemTools::GetLineFromStream(tagStream, tagLine) &&
       tagLine.size() > 1 && tagLine[0] == 'T') {
     // Use the branch specified in the tag file.
-    std::string flag = "-r";
-    flag += tagLine.substr(1);
+    std::string flag = cmStrCat("-r", cm::string_view(tagLine).substr(1));
     return flag;
   }
   // Use the default branch.
diff --git a/Source/CTest/cmCTestCVS.h b/Source/CTest/cmCTestCVS.h
index 77fc3cc..7d33d8f 100644
--- a/Source/CTest/cmCTestCVS.h
+++ b/Source/CTest/cmCTestCVS.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestVC.h"
-
 #include <iosfwd>
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmCTestVC.h"
+
 class cmCTest;
 class cmXMLWriter;
 
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx
index 74a932a..f2f42b4 100644
--- a/Source/CTest/cmCTestConfigureCommand.cxx
+++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -2,30 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestConfigureCommand.h"
 
+#include <cstring>
+#include <sstream>
+#include <vector>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmCTest.h"
 #include "cmCTestConfigureHandler.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <sstream>
-#include <string.h>
-#include <vector>
-
-cmCTestConfigureCommand::cmCTestConfigureCommand()
+void cmCTestConfigureCommand::BindArguments()
 {
-  this->Arguments[ctc_OPTIONS] = "OPTIONS";
-  this->Arguments[ctc_LAST] = nullptr;
-  this->Last = ctc_LAST;
+  this->cmCTestHandlerCommand::BindArguments();
+  this->Bind("OPTIONS"_s, this->Options);
 }
 
 cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler()
 {
   std::vector<std::string> options;
 
-  if (this->Values[ctc_OPTIONS]) {
-    cmSystemTools::ExpandListArgument(this->Values[ctc_OPTIONS], options);
+  if (!this->Options.empty()) {
+    cmExpandList(this->Options, options);
   }
 
   if (this->CTest->GetCTestConfiguration("BuildDirectory").empty()) {
@@ -75,9 +77,8 @@
         delete gg;
       }
 
-      std::string cmakeConfigureCommand = "\"";
-      cmakeConfigureCommand += cmSystemTools::GetCMakeCommand();
-      cmakeConfigureCommand += "\"";
+      std::string cmakeConfigureCommand =
+        cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"');
 
       for (std::string const& option : options) {
         cmakeConfigureCommand += " \"";
diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h
index 0cbcbfa..3f5944a 100644
--- a/Source/CTest/cmCTestConfigureCommand.h
+++ b/Source/CTest/cmCTestConfigureCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
 #include <string>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestConfigure
  * \brief Run a ctest script
@@ -20,17 +23,15 @@
 class cmCTestConfigureCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestConfigureCommand();
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestConfigureCommand* ni = new cmCTestConfigureCommand;
+    auto ni = cm::make_unique<cmCTestConfigureCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
@@ -39,14 +40,10 @@
   std::string GetName() const override { return "ctest_configure"; }
 
 protected:
+  void BindArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  enum
-  {
-    ctc_FIRST = ct_LAST,
-    ctc_OPTIONS,
-    ctc_LAST
-  };
+  std::string Options;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx
index 7e93189..914930e 100644
--- a/Source/CTest/cmCTestConfigureHandler.cxx
+++ b/Source/CTest/cmCTestConfigureHandler.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestConfigureHandler.h"
 
+#include <chrono>
+#include <ostream>
+#include <string>
+
 #include "cmCTest.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
 #include "cmXMLWriter.h"
 
-#include <chrono>
-#include <ostream>
-#include <string>
-
 cmCTestConfigureHandler::cmCTestConfigureHandler() = default;
 
 void cmCTestConfigureHandler::Initialize()
diff --git a/Source/CTest/cmCTestConfigureHandler.h b/Source/CTest/cmCTestConfigureHandler.h
index 680401c..01fe801 100644
--- a/Source/CTest/cmCTestConfigureHandler.h
+++ b/Source/CTest/cmCTestConfigureHandler.h
@@ -14,7 +14,7 @@
 class cmCTestConfigureHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /*
    * The main entry point for this class
diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx
index 07aae76..d6e6be3 100644
--- a/Source/CTest/cmCTestCoverageCommand.cxx
+++ b/Source/CTest/cmCTestCoverageCommand.cxx
@@ -2,14 +2,27 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestCoverageCommand.h"
 
+#include <set>
+
+#include "cm_static_string_view.hxx"
+
+#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestCoverageHandler.h"
 
 class cmCTestGenericHandler;
 
-cmCTestCoverageCommand::cmCTestCoverageCommand()
+void cmCTestCoverageCommand::BindArguments()
 {
-  this->LabelsMentioned = false;
+  this->cmCTestHandlerCommand::BindArguments();
+  this->Bind("LABELS"_s, this->Labels);
+}
+
+void cmCTestCoverageCommand::CheckArguments(
+  std::vector<std::string> const& keywords)
+{
+  this->LabelsMentioned =
+    !this->Labels.empty() || cmContains(keywords, "LABELS");
 }
 
 cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler()
@@ -24,34 +37,10 @@
 
   // If a LABELS option was given, select only files with the labels.
   if (this->LabelsMentioned) {
-    handler->SetLabelFilter(this->Labels);
+    handler->SetLabelFilter(
+      std::set<std::string>(this->Labels.begin(), this->Labels.end()));
   }
 
   handler->SetQuiet(this->Quiet);
   return handler;
 }
-
-bool cmCTestCoverageCommand::CheckArgumentKeyword(std::string const& arg)
-{
-  // Look for arguments specific to this command.
-  if (arg == "LABELS") {
-    this->ArgumentDoing = ArgumentDoingLabels;
-    this->LabelsMentioned = true;
-    return true;
-  }
-
-  // Look for other arguments.
-  return this->Superclass::CheckArgumentKeyword(arg);
-}
-
-bool cmCTestCoverageCommand::CheckArgumentValue(std::string const& arg)
-{
-  // Handle states specific to this command.
-  if (this->ArgumentDoing == ArgumentDoingLabels) {
-    this->Labels.insert(arg);
-    return true;
-  }
-
-  // Look for other arguments.
-  return this->Superclass::CheckArgumentValue(arg);
-}
diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h
index 1ae2d86..76aaf46 100644
--- a/Source/CTest/cmCTestCoverageCommand.h
+++ b/Source/CTest/cmCTestCoverageCommand.h
@@ -5,13 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
-#include <set>
 #include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestCoverage
  * \brief Run a ctest script
@@ -21,17 +24,15 @@
 class cmCTestCoverageCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestCoverageCommand();
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestCoverageCommand* ni = new cmCTestCoverageCommand;
+    auto ni = cm::make_unique<cmCTestCoverageCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
@@ -39,22 +40,13 @@
    */
   std::string GetName() const override { return "ctest_coverage"; }
 
-  typedef cmCTestHandlerCommand Superclass;
-
 protected:
+  void BindArguments() override;
+  void CheckArguments(std::vector<std::string> const& keywords) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  bool CheckArgumentKeyword(std::string const& arg) override;
-  bool CheckArgumentValue(std::string const& arg) override;
-
-  enum
-  {
-    ArgumentDoingLabels = Superclass::ArgumentDoingLast1,
-    ArgumentDoingLast2
-  };
-
   bool LabelsMentioned;
-  std::set<std::string> Labels;
+  std::vector<std::string> Labels;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index f6028c4..4cd783f 100644
--- a/Source/CTest/cmCTestCoverageHandler.cxx
+++ b/Source/CTest/cmCTestCoverageHandler.cxx
@@ -2,6 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestCoverageHandler.h"
 
+#include <algorithm>
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iomanip>
+#include <iterator>
+#include <sstream>
+#include <utility>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+#include "cmsys/Process.h"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmDuration.h"
@@ -13,24 +28,11 @@
 #include "cmParseGTMCoverage.h"
 #include "cmParseJacocoCoverage.h"
 #include "cmParsePHPCoverage.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include "cmsys/Process.h"
-#include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <chrono>
-#include <cstring>
-#include <iomanip>
-#include <iterator>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <utility>
-
 class cmMakefile;
 
 #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
@@ -129,10 +131,9 @@
 
 void cmCTestCoverageHandler::CleanCoverageLogFiles(std::ostream& log)
 {
-  std::string logGlob = this->CTest->GetCTestConfiguration("BuildDirectory");
-  logGlob += "/Testing/";
-  logGlob += this->CTest->GetCurrentTag();
-  logGlob += "/CoverageLog*";
+  std::string logGlob =
+    cmStrCat(this->CTest->GetCTestConfiguration("BuildDirectory"), "/Testing/",
+             this->CTest->GetCurrentTag(), "/CoverageLog*");
   cmsys::Glob gl;
   gl.FindFiles(logGlob);
   std::vector<std::string> const& files = gl.GetFiles();
@@ -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);
@@ -1455,8 +1456,7 @@
       std::vector<std::string> lcovFiles;
       dir = this->CTest->GetBinaryDir();
       std::string daGlob;
-      daGlob = dir;
-      daGlob += "/*.LCOV";
+      daGlob = cmStrCat(dir, "/*.LCOV");
       cmCTestOptionalLog(
         this->CTest, HANDLER_VERBOSE_OUTPUT,
         "   looking for LCOV files in: " << daGlob << std::endl, this->Quiet);
@@ -1597,12 +1597,10 @@
     cmCTestOptionalLog(
       this->CTest, HANDLER_VERBOSE_OUTPUT,
       "   globbing for coverage in: " << lm.first << std::endl, this->Quiet);
-    std::string daGlob = lm.first;
-    daGlob += "/*.da";
+    std::string daGlob = cmStrCat(lm.first, "/*.da");
     gl.FindFiles(daGlob);
     cmAppend(files, gl.GetFiles());
-    daGlob = lm.first;
-    daGlob += "/*.gcda";
+    daGlob = cmStrCat(lm.first, "/*.gcda");
     gl.FindFiles(daGlob);
     cmAppend(files, gl.GetFiles());
   }
@@ -1631,8 +1629,7 @@
 
   // DPI file should appear in build directory
   std::string daGlob;
-  daGlob = buildDir;
-  daGlob += "/*.dpi";
+  daGlob = cmStrCat(buildDir, "/*.dpi");
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "   looking for dpi files in: " << daGlob << std::endl,
                      this->Quiet);
@@ -1834,9 +1831,8 @@
     return 0;
   }
   std::map<std::string, std::string> fileMap;
-  std::vector<std::string>::iterator fp = filesFullPath.begin();
-  for (std::vector<std::string>::iterator f = files.begin(); f != files.end();
-       ++f, ++fp) {
+  auto fp = filesFullPath.begin();
+  for (auto f = files.begin(); f != files.end(); ++f, ++fp) {
     fileMap[*f] = *fp;
   }
 
@@ -1874,7 +1870,7 @@
         this->StartCoverageLogXML(covLogXML);
         count++; // move on one
       }
-      std::map<std::string, std::string>::iterator i = fileMap.find(file);
+      auto i = fileMap.find(file);
       // if the file should be covered write out the header for that file
       if (i != fileMap.end()) {
         // we have a new file so count it in the output
@@ -1945,10 +1941,9 @@
   cmCTestRunProcess runCoverageSrc;
   runCoverageSrc.SetCommand(program.c_str());
   runCoverageSrc.AddArgument(arg);
-  std::string stdoutFile = cont->BinaryDir + "/Testing/Temporary/";
-  stdoutFile += this->GetCTestInstance()->GetCurrentTag();
-  stdoutFile += "-";
-  stdoutFile += cmd;
+  std::string stdoutFile =
+    cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
+             this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
   std::string stderrFile = stdoutFile;
   stdoutFile += ".stdout";
   stderrFile += ".stderr";
@@ -2037,9 +2032,7 @@
       coveredFileNames.insert(file);
       if (!cmSystemTools::FileIsFullPath(sourceFile)) {
         // file will be relative to the binary dir
-        file = cont->BinaryDir;
-        file += "/";
-        file += sourceFile;
+        file = cmStrCat(cont->BinaryDir, '/', sourceFile);
       }
       file = cmSystemTools::CollapseFullPath(file);
       bool shouldIDoCoverage =
@@ -2209,7 +2202,7 @@
 
 int cmCTestCoverageHandler::GetLabelId(std::string const& label)
 {
-  LabelIdMapType::iterator i = this->LabelIdMap.find(label);
+  auto i = this->LabelIdMap.find(label);
   if (i == this->LabelIdMap.end()) {
     int n = int(this->Labels.size());
     this->Labels.push_back(label);
@@ -2221,9 +2214,8 @@
 
 void cmCTestCoverageHandler::LoadLabels()
 {
-  std::string fileList = this->CTest->GetBinaryDir();
-  fileList += "/CMakeFiles";
-  fileList += "/TargetDirectories.txt";
+  std::string fileList =
+    cmStrCat(this->CTest->GetBinaryDir(), "/CMakeFiles/TargetDirectories.txt");
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      " target directory list [" << fileList << "]\n",
                      this->Quiet);
@@ -2237,8 +2229,7 @@
 void cmCTestCoverageHandler::LoadLabels(const char* dir)
 {
   LabelSet& dirLabels = this->TargetDirs[dir];
-  std::string fname = dir;
-  fname += "/Labels.txt";
+  std::string fname = cmStrCat(dir, "/Labels.txt");
   cmsys::ifstream fin(fname.c_str());
   if (!fin) {
     return;
@@ -2282,7 +2273,7 @@
 void cmCTestCoverageHandler::WriteXMLLabels(cmXMLWriter& xml,
                                             std::string const& source)
 {
-  LabelMapType::const_iterator li = this->SourceLabels.find(source);
+  auto li = this->SourceLabels.find(source);
   if (li != this->SourceLabels.end() && !li->second.empty()) {
     xml.StartElement("Labels");
     for (auto const& ls : li->second) {
@@ -2325,7 +2316,7 @@
   // The source is filtered out if it does not have any labels in
   // common with the filter set.
   std::string shortSrc = this->CTest->GetShortPathToFile(source.c_str());
-  LabelMapType::const_iterator li = this->SourceLabels.find(shortSrc);
+  auto li = this->SourceLabels.find(shortSrc);
   if (li != this->SourceLabels.end()) {
     return !this->IntersectsFilter(li->second);
   }
diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h
index 6492fe9..991b89d 100644
--- a/Source/CTest/cmCTestCoverageHandler.h
+++ b/Source/CTest/cmCTestCoverageHandler.h
@@ -5,15 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-
-#include "cmsys/RegularExpression.hxx"
 #include <iosfwd>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmCTestGenericHandler.h"
+
 class cmGeneratedFileStream;
 class cmMakefile;
 class cmXMLWriter;
@@ -24,8 +25,8 @@
   int Error;
   std::string SourceDir;
   std::string BinaryDir;
-  typedef std::vector<int> SingleFileCoverageVector;
-  typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap;
+  using SingleFileCoverageVector = std::vector<int>;
+  using TotalCoverageMap = std::map<std::string, SingleFileCoverageVector>;
   TotalCoverageMap TotalCoverage;
   std::ostream* OFS;
   bool Quiet;
@@ -37,7 +38,7 @@
 class cmCTestCoverageHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /*
    * The main entry point for this class
@@ -128,12 +129,12 @@
   class LabelSet : public std::set<int>
   {
   };
-  typedef std::map<std::string, LabelSet> LabelMapType;
+  using LabelMapType = std::map<std::string, LabelSet>;
   LabelMapType SourceLabels;
   LabelMapType TargetDirs;
 
   // Map from label name to label id.
-  typedef std::map<std::string, int> LabelIdMapType;
+  using LabelIdMapType = std::map<std::string, int>;
   LabelIdMapType LabelIdMap;
   std::vector<std::string> Labels;
   int GetLabelId(std::string const& label);
diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx
index cc63e45..ccac832 100644
--- a/Source/CTest/cmCTestCurl.cxx
+++ b/Source/CTest/cmCTestCurl.cxx
@@ -2,14 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestCurl.h"
 
+#include <cstdio>
+#include <ostream>
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCurl.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <ostream>
-#include <stdio.h>
-
 cmCTestCurl::cmCTestCurl(cmCTest* ctest)
 {
   this->CTest = ctest;
@@ -127,9 +128,7 @@
     return false;
   }
   // set the url
-  std::string upload_url = url;
-  upload_url += "?";
-  upload_url += fields;
+  std::string upload_url = cmStrCat(url, '?', fields);
   ::curl_easy_setopt(this->Curl, CURLOPT_URL, upload_url.c_str());
   // now specify which file to upload
   ::curl_easy_setopt(this->Curl, CURLOPT_INFILE, ftpfile);
diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h
index 6186af8..9c5ba66 100644
--- a/Source/CTest/cmCTestCurl.h
+++ b/Source/CTest/cmCTestCurl.h
@@ -5,10 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_curl.h"
 #include <string>
 #include <vector>
 
+#include "cm_curl.h"
+
 class cmCTest;
 
 class cmCTestCurl
diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
index f4531a1..051c117 100644
--- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
+++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestEmptyBinaryDirectoryCommand.h"
 
-#include "cmCTestScriptHandler.h"
-
 #include <sstream>
 
+#include "cmCTestScriptHandler.h"
+
 class cmExecutionStatus;
 
 bool cmCTestEmptyBinaryDirectoryCommand::InitialPass(
diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
index 9425ece..ac96a4e 100644
--- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
+++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
 #include <string>
+#include <utility>
 #include <vector>
 
-class cmCommand;
+#include <cm/memory>
+
+#include "cmCTestCommand.h"
+#include "cmCommand.h"
+
 class cmExecutionStatus;
 
 /** \class cmCTestEmptyBinaryDirectory
@@ -27,13 +30,12 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestEmptyBinaryDirectoryCommand* ni =
-      new cmCTestEmptyBinaryDirectoryCommand;
+    auto ni = cm::make_unique<cmCTestEmptyBinaryDirectoryCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index 9d9761c..3f3c107 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -2,19 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestGIT.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
 #include <vector>
 
-#include "cmAlgorithms.h"
+#include "cmsys/FStream.hxx"
+#include "cmsys/Process.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,
@@ -111,8 +112,8 @@
   else if (git_dir[0] == '/') {
     // Cygwin Git reports a full path that Cygwin understands, but we
     // are a Windows application.  Run "cygpath" to get Windows path.
-    std::string cygpath_exe = cmSystemTools::GetFilenamePath(git);
-    cygpath_exe += "/cygpath.exe";
+    std::string cygpath_exe =
+      cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
     if (cmSystemTools::FileExists(cygpath_exe)) {
       char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
                                 0 };
@@ -211,8 +212,7 @@
 
 bool cmCTestGIT::UpdateByCustom(std::string const& custom)
 {
-  std::vector<std::string> git_custom_command;
-  cmSystemTools::ExpandListArgument(custom, git_custom_command, true);
+  std::vector<std::string> git_custom_command = cmExpandedList(custom, true);
   std::vector<char const*> git_custom;
   git_custom.reserve(git_custom_command.size() + 1);
   for (std::string const& i : git_custom_command) {
@@ -270,7 +270,7 @@
 
   std::string init_submodules =
     this->CTest->GetCTestConfiguration("GITInitSubmodules");
-  if (cmSystemTools::IsOn(init_submodules)) {
+  if (cmIsOn(init_submodules)) {
     char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
     ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
                          top_dir.c_str());
@@ -334,7 +334,7 @@
     this->SetLog(&git->Log, prefix);
   }
 
-  typedef cmCTestGIT::Change Change;
+  using Change = cmCTestGIT::Change;
   std::vector<Change> Changes;
 
 protected:
@@ -457,7 +457,7 @@
   }
 
 private:
-  typedef cmCTestGIT::Revision Revision;
+  using Revision = cmCTestGIT::Revision;
   enum SectionType
   {
     SectionHeader,
diff --git a/Source/CTest/cmCTestGIT.h b/Source/CTest/cmCTestGIT.h
index ade430f..3103d84 100644
--- a/Source/CTest/cmCTestGIT.h
+++ b/Source/CTest/cmCTestGIT.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGlobalVC.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmCTestGlobalVC.h"
+
 class cmCTest;
 
 /** \class cmCTestGIT
diff --git a/Source/CTest/cmCTestGenericHandler.cxx b/Source/CTest/cmCTestGenericHandler.cxx
index d3020b5..cc0b4ed 100644
--- a/Source/CTest/cmCTestGenericHandler.cxx
+++ b/Source/CTest/cmCTestGenericHandler.cxx
@@ -23,8 +23,7 @@
 void cmCTestGenericHandler::SetOption(const std::string& op, const char* value)
 {
   if (!value) {
-    cmCTestGenericHandler::t_StringToString::iterator remit =
-      this->Options.find(op);
+    auto remit = this->Options.find(op);
     if (remit != this->Options.end()) {
       this->Options.erase(remit);
     }
@@ -39,8 +38,7 @@
 {
   this->SetOption(op, value);
   if (!value) {
-    cmCTestGenericHandler::t_StringToString::iterator remit =
-      this->PersistentOptions.find(op);
+    auto remit = this->PersistentOptions.find(op);
     if (remit != this->PersistentOptions.end()) {
       this->PersistentOptions.erase(remit);
     }
@@ -62,8 +60,7 @@
 
 const char* cmCTestGenericHandler::GetOption(const std::string& op)
 {
-  cmCTestGenericHandler::t_StringToString::iterator remit =
-    this->Options.find(op);
+  auto remit = this->Options.find(op);
   if (remit == this->Options.end()) {
     return nullptr;
   }
diff --git a/Source/CTest/cmCTestGenericHandler.h b/Source/CTest/cmCTestGenericHandler.h
index cf91b4f..94e5418 100644
--- a/Source/CTest/cmCTestGenericHandler.h
+++ b/Source/CTest/cmCTestGenericHandler.h
@@ -6,10 +6,11 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
 #include "cmCTest.h"
 #include "cmSystemTools.h"
 
@@ -71,7 +72,7 @@
   cmCTestGenericHandler();
   virtual ~cmCTestGenericHandler();
 
-  typedef std::map<std::string, std::string> t_StringToString;
+  using t_StringToString = std::map<std::string, std::string>;
 
   void SetPersistentOption(const std::string& op, const char* value);
   void SetOption(const std::string& op, const char* value);
diff --git a/Source/CTest/cmCTestGlobalVC.cxx b/Source/CTest/cmCTestGlobalVC.cxx
index 54ebd4f..5f05efb 100644
--- a/Source/CTest/cmCTestGlobalVC.cxx
+++ b/Source/CTest/cmCTestGlobalVC.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestGlobalVC.h"
 
+#include <ostream>
+#include <utility>
+
 #include "cmCTest.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 
-#include <ostream>
-#include <utility>
-
 cmCTestGlobalVC::cmCTestGlobalVC(cmCTest* ct, std::ostream& log)
   : cmCTestVC(ct, log)
 {
diff --git a/Source/CTest/cmCTestGlobalVC.h b/Source/CTest/cmCTestGlobalVC.h
index 9c57215..ff86591 100644
--- a/Source/CTest/cmCTestGlobalVC.h
+++ b/Source/CTest/cmCTestGlobalVC.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestVC.h"
-
 #include <iosfwd>
 #include <list>
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmCTestVC.h"
+
 class cmCTest;
 class cmXMLWriter;
 
diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx
index ba2252a..3265498 100644
--- a/Source/CTest/cmCTestHG.cxx
+++ b/Source/CTest/cmCTestHG.cxx
@@ -2,6 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestHG.h"
 
+#include <ostream>
+#include <vector>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestVC.h"
@@ -9,10 +14,6 @@
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <ostream>
-#include <vector>
-
 cmCTestHG::cmCTestHG(cmCTest* ct, std::ostream& log)
   : cmCTestGlobalVC(ct, log)
 {
@@ -174,8 +175,8 @@
 private:
   cmCTestHG* HG;
 
-  typedef cmCTestHG::Revision Revision;
-  typedef cmCTestHG::Change Change;
+  using Revision = cmCTestHG::Revision;
+  using Change = cmCTestHG::Change;
   Revision Rev;
   std::vector<Change> Changes;
   Change CurChange;
diff --git a/Source/CTest/cmCTestHG.h b/Source/CTest/cmCTestHG.h
index c12d618..2900139 100644
--- a/Source/CTest/cmCTestHG.h
+++ b/Source/CTest/cmCTestHG.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGlobalVC.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmCTestGlobalVC.h"
+
 class cmCTest;
 
 /** \class cmCTestHG
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx
index adf9553..b1034c9 100644
--- a/Source/CTest/cmCTestHandlerCommand.cxx
+++ b/Source/CTest/cmCTestHandlerCommand.cxx
@@ -2,36 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestHandlerCommand.h"
 
-#include "cmCTest.h"
-#include "cmCTestGenericHandler.h"
-#include "cmMakefile.h"
-#include "cmMessageType.h"
-#include "cmSystemTools.h"
-#include "cmWorkingDirectory.h"
-
+#include <algorithm>
+#include <cstdlib>
 #include <cstring>
 #include <sstream>
-#include <stdlib.h>
 
-class cmExecutionStatus;
+#include "cm_static_string_view.hxx"
 
-cmCTestHandlerCommand::cmCTestHandlerCommand()
-{
-  const size_t INIT_SIZE = 100;
-  size_t cc;
-  this->Arguments.reserve(INIT_SIZE);
-  for (cc = 0; cc < INIT_SIZE; ++cc) {
-    this->Arguments.push_back(nullptr);
-  }
-  this->Arguments[ct_RETURN_VALUE] = "RETURN_VALUE";
-  this->Arguments[ct_CAPTURE_CMAKE_ERROR] = "CAPTURE_CMAKE_ERROR";
-  this->Arguments[ct_SOURCE] = "SOURCE";
-  this->Arguments[ct_BUILD] = "BUILD";
-  this->Arguments[ct_SUBMIT_INDEX] = "SUBMIT_INDEX";
-  this->Last = ct_LAST;
-  this->AppendXML = false;
-  this->Quiet = false;
-}
+#include "cmCTest.h"
+#include "cmCTestGenericHandler.h"
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmWorkingDirectory.h"
 
 namespace {
 // class to save and restore the error state for ctest_* commands
@@ -86,47 +71,46 @@
 }
 
 bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
-                                        cmExecutionStatus& /*unused*/)
+                                        cmExecutionStatus& status)
 {
   // save error state and restore it if needed
   SaveRestoreErrorState errorState;
   // Allocate space for argument values.
-  this->Values.clear();
-  this->Values.resize(this->Last, nullptr);
+  this->BindArguments();
 
   // Process input arguments.
-  this->ArgumentDoing = ArgumentDoingNone;
-  // look at all arguments and do not short circuit on the first
-  // bad one so that CAPTURE_CMAKE_ERROR can override setting the
-  // global error state
-  bool foundBadArgument = false;
-  for (std::string const& arg : args) {
-    // Check this argument.
-    if (!this->CheckArgumentKeyword(arg) && !this->CheckArgumentValue(arg)) {
-      std::ostringstream e;
-      e << "called with unknown argument \"" << arg << "\".";
-      this->SetError(e.str());
-      foundBadArgument = true;
-    }
-    // note bad argument
-    if (this->ArgumentDoing == ArgumentDoingError) {
-      foundBadArgument = true;
-    }
+  std::vector<std::string> unparsedArguments;
+  std::vector<std::string> keywordsMissingValue;
+  std::vector<std::string> parsedKeywords;
+  this->Parse(args, &unparsedArguments, &keywordsMissingValue,
+              &parsedKeywords);
+  this->CheckArguments(keywordsMissingValue);
+
+  std::sort(parsedKeywords.begin(), parsedKeywords.end());
+  auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end());
+  if (it != parsedKeywords.end()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Called with more than one value for ", *it));
   }
-  bool capureCMakeError = (this->Values[ct_CAPTURE_CMAKE_ERROR] &&
-                           *this->Values[ct_CAPTURE_CMAKE_ERROR]);
+
+  bool const foundBadArgument = !unparsedArguments.empty();
+  if (foundBadArgument) {
+    this->SetError(cmStrCat("called with unknown argument \"",
+                            unparsedArguments.front(), "\"."));
+  }
+  bool const captureCMakeError = !this->CaptureCMakeError.empty();
   // now that arguments are parsed check to see if there is a
   // CAPTURE_CMAKE_ERROR specified let the errorState object know.
-  if (capureCMakeError) {
+  if (captureCMakeError) {
     errorState.CaptureCMakeError();
   }
   // if we found a bad argument then exit before running command
   if (foundBadArgument) {
     // store the cmake error
-    if (capureCMakeError) {
-      this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
-                                    "-1");
-      std::string const err = this->GetName() + " " + this->GetError();
+    if (captureCMakeError) {
+      this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
+      std::string const err = this->GetName() + " " + status.GetError();
       if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
         cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
       }
@@ -147,10 +131,9 @@
     this->CTest->SetConfigType(ctestConfigType);
   }
 
-  if (this->Values[ct_BUILD]) {
+  if (!this->Build.empty()) {
     this->CTest->SetCTestConfiguration(
-      "BuildDirectory",
-      cmSystemTools::CollapseFullPath(this->Values[ct_BUILD]).c_str(),
+      "BuildDirectory", cmSystemTools::CollapseFullPath(this->Build).c_str(),
       this->Quiet);
   } else {
     std::string const& bdir =
@@ -164,13 +147,11 @@
                  "CTEST_BINARY_DIRECTORY not set" << std::endl;);
     }
   }
-  if (this->Values[ct_SOURCE]) {
+  if (!this->Source.empty()) {
     cmCTestLog(this->CTest, DEBUG,
-               "Set source directory to: " << this->Values[ct_SOURCE]
-                                           << std::endl);
+               "Set source directory to: " << this->Source << std::endl);
     this->CTest->SetCTestConfiguration(
-      "SourceDirectory",
-      cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(),
+      "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(),
       this->Quiet);
   } else {
     this->CTest->SetCTestConfiguration(
@@ -192,11 +173,10 @@
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Cannot instantiate test handler " << this->GetName()
                                                   << std::endl);
-    if (capureCMakeError) {
-      this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
-                                    "-1");
-      const char* err = this->GetError();
-      if (err && !cmSystemTools::FindLastString(err, "unknown error.")) {
+    if (captureCMakeError) {
+      this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
+      std::string const& err = status.GetError();
+      if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
         cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
       }
       return true;
@@ -204,11 +184,11 @@
     return false;
   }
 
-  handler->SetAppendXML(this->AppendXML);
+  handler->SetAppendXML(this->Append);
 
   handler->PopulateCustomVectors(this->Makefile);
-  if (this->Values[ct_SUBMIT_INDEX]) {
-    handler->SetSubmitIndex(atoi(this->Values[ct_SUBMIT_INDEX]));
+  if (!this->SubmitIndex.empty()) {
+    handler->SetSubmitIndex(atoi(this->SubmitIndex.c_str()));
   }
   cmWorkingDirectory workdir(
     this->CTest->GetCTestConfiguration("BuildDirectory"));
@@ -216,11 +196,10 @@
     this->SetError("failed to change directory to " +
                    this->CTest->GetCTestConfiguration("BuildDirectory") +
                    " : " + std::strerror(workdir.GetLastResult()));
-    if (capureCMakeError) {
-      this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
-                                    "-1");
+    if (captureCMakeError) {
+      this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
       cmCTestLog(this->CTest, ERROR_MESSAGE,
-                 this->GetName() << " " << this->GetError() << "\n");
+                 this->GetName() << " " << status.GetError() << "\n");
       // return success because failure is recorded in CAPTURE_CMAKE_ERROR
       return true;
     }
@@ -228,28 +207,24 @@
   }
 
   int res = handler->ProcessHandler();
-  if (this->Values[ct_RETURN_VALUE] && *this->Values[ct_RETURN_VALUE]) {
-    std::ostringstream str;
-    str << res;
-    this->Makefile->AddDefinition(this->Values[ct_RETURN_VALUE],
-                                  str.str().c_str());
+  if (!this->ReturnValue.empty()) {
+    this->Makefile->AddDefinition(this->ReturnValue, std::to_string(res));
   }
   this->ProcessAdditionalValues(handler);
   // log the error message if there was an error
-  if (capureCMakeError) {
+  if (captureCMakeError) {
     const char* returnString = "0";
     if (cmSystemTools::GetErrorOccuredFlag()) {
       returnString = "-1";
-      const char* err = this->GetError();
+      std::string const& err = status.GetError();
       // print out the error if it is not "unknown error" which means
       // there was no message
-      if (err && !cmSystemTools::FindLastString(err, "unknown error.")) {
+      if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
         cmCTestLog(this->CTest, ERROR_MESSAGE, err);
       }
     }
     // store the captured cmake error state 0 or -1
-    this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
-                                  returnString);
+    this->Makefile->AddDefinition(this->CaptureCMakeError, returnString);
   }
   return true;
 }
@@ -258,47 +233,17 @@
 {
 }
 
-bool cmCTestHandlerCommand::CheckArgumentKeyword(std::string const& arg)
+void cmCTestHandlerCommand::BindArguments()
 {
-  // Look for non-value arguments common to all commands.
-  if (arg == "APPEND") {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->AppendXML = true;
-    return true;
-  }
-  if (arg == "QUIET") {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->Quiet = true;
-    return true;
-  }
-
-  // Check for a keyword in our argument/value table.
-  for (unsigned int k = 0; k < this->Arguments.size(); ++k) {
-    if (this->Arguments[k] && arg == this->Arguments[k]) {
-      this->ArgumentDoing = ArgumentDoingKeyword;
-      this->ArgumentIndex = k;
-      return true;
-    }
-  }
-  return false;
+  this->Bind("APPEND"_s, this->Append);
+  this->Bind("QUIET"_s, this->Quiet);
+  this->Bind("RETURN_VALUE"_s, this->ReturnValue);
+  this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
+  this->Bind("SOURCE"_s, this->Source);
+  this->Bind("BUILD"_s, this->Build);
+  this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex);
 }
 
-bool cmCTestHandlerCommand::CheckArgumentValue(std::string const& arg)
+void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&)
 {
-  if (this->ArgumentDoing == ArgumentDoingKeyword) {
-    this->ArgumentDoing = ArgumentDoingNone;
-    unsigned int k = this->ArgumentIndex;
-    if (this->Values[k]) {
-      std::ostringstream e;
-      e << "Called with more than one value for " << this->Arguments[k];
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      this->ArgumentDoing = ArgumentDoingError;
-      return true;
-    }
-    this->Values[k] = arg.c_str();
-    cmCTestLog(this->CTest, DEBUG,
-               "Set " << this->Arguments[k] << " to " << arg << "\n");
-    return true;
-  }
-  return false;
 }
diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h
index 79d61f3..a20d607 100644
--- a/Source/CTest/cmCTestHandlerCommand.h
+++ b/Source/CTest/cmCTestHandlerCommand.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include "cmArgumentParser.h"
+#include "cmCTestCommand.h"
+
 class cmCTestGenericHandler;
 class cmExecutionStatus;
 
@@ -19,11 +19,11 @@
  *
  * cmCTestHandlerCommand defineds the command to test the project.
  */
-class cmCTestHandlerCommand : public cmCTestCommand
+class cmCTestHandlerCommand
+  : public cmCTestCommand
+  , public cmArgumentParser<void>
 {
 public:
-  cmCTestHandlerCommand();
-
   /**
    * The name of the command as specified in CMakeList.txt.
    */
@@ -36,42 +36,22 @@
   bool InitialPass(std::vector<std::string> const& args,
                    cmExecutionStatus& status) override;
 
-  enum
-  {
-    ct_NONE,
-    ct_RETURN_VALUE,
-    ct_CAPTURE_CMAKE_ERROR,
-    ct_BUILD,
-    ct_SOURCE,
-    ct_SUBMIT_INDEX,
-    ct_LAST
-  };
-
 protected:
   virtual cmCTestGenericHandler* InitializeHandler() = 0;
 
   virtual void ProcessAdditionalValues(cmCTestGenericHandler* handler);
 
   // Command argument handling.
-  virtual bool CheckArgumentKeyword(std::string const& arg);
-  virtual bool CheckArgumentValue(std::string const& arg);
-  enum
-  {
-    ArgumentDoingNone,
-    ArgumentDoingError,
-    ArgumentDoingKeyword,
-    ArgumentDoingLast1
-  };
-  int ArgumentDoing;
-  unsigned int ArgumentIndex;
+  virtual void BindArguments();
+  virtual void CheckArguments(std::vector<std::string> const& keywords);
 
-  bool AppendXML;
-  bool Quiet;
-
-  std::string ReturnVariable;
-  std::vector<const char*> Arguments;
-  std::vector<const char*> Values;
-  size_t Last;
+  bool Append = false;
+  bool Quiet = false;
+  std::string CaptureCMakeError;
+  std::string ReturnValue;
+  std::string Build;
+  std::string Source;
+  std::string SubmitIndex;
 };
 
 #define CTEST_COMMAND_APPEND_OPTION_DOCS                                      \
diff --git a/Source/CTest/cmCTestHardwareAllocator.cxx b/Source/CTest/cmCTestHardwareAllocator.cxx
new file mode 100644
index 0000000..2d1833d
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareAllocator.cxx
@@ -0,0 +1,86 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmCTestHardwareAllocator.h"
+
+#include <utility>
+#include <vector>
+
+#include "cmCTestHardwareSpec.h"
+
+void cmCTestHardwareAllocator::InitializeFromHardwareSpec(
+  const cmCTestHardwareSpec& spec)
+{
+  this->Resources.clear();
+
+  for (auto const& it : spec.LocalSocket.Resources) {
+    auto& res = this->Resources[it.first];
+    for (auto const& specRes : it.second) {
+      res[specRes.Id].Total = specRes.Capacity;
+      res[specRes.Id].Locked = 0;
+    }
+  }
+}
+
+const std::map<std::string,
+               std::map<std::string, cmCTestHardwareAllocator::Resource>>&
+cmCTestHardwareAllocator::GetResources() const
+{
+  return this->Resources;
+}
+
+bool cmCTestHardwareAllocator::AllocateResource(const std::string& name,
+                                                const std::string& id,
+                                                unsigned int slots)
+{
+  auto it = this->Resources.find(name);
+  if (it == this->Resources.end()) {
+    return false;
+  }
+
+  auto resIt = it->second.find(id);
+  if (resIt == it->second.end()) {
+    return false;
+  }
+
+  if (resIt->second.Total < resIt->second.Locked + slots) {
+    return false;
+  }
+
+  resIt->second.Locked += slots;
+  return true;
+}
+
+bool cmCTestHardwareAllocator::DeallocateResource(const std::string& name,
+                                                  const std::string& id,
+                                                  unsigned int slots)
+{
+  auto it = this->Resources.find(name);
+  if (it == this->Resources.end()) {
+    return false;
+  }
+
+  auto resIt = it->second.find(id);
+  if (resIt == it->second.end()) {
+    return false;
+  }
+
+  if (resIt->second.Locked < slots) {
+    return false;
+  }
+
+  resIt->second.Locked -= slots;
+  return true;
+}
+
+bool cmCTestHardwareAllocator::Resource::operator==(
+  const Resource& other) const
+{
+  return this->Total == other.Total && this->Locked == other.Locked;
+}
+
+bool cmCTestHardwareAllocator::Resource::operator!=(
+  const Resource& other) const
+{
+  return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestHardwareAllocator.h b/Source/CTest/cmCTestHardwareAllocator.h
new file mode 100644
index 0000000..441f84d
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareAllocator.h
@@ -0,0 +1,39 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCTestHardwareAllocator_h
+#define cmCTestHardwareAllocator_h
+
+#include <map>
+#include <string>
+
+class cmCTestHardwareSpec;
+
+class cmCTestHardwareAllocator
+{
+public:
+  struct Resource
+  {
+    unsigned int Total;
+    unsigned int Locked;
+
+    unsigned int Free() const { return this->Total - this->Locked; }
+
+    bool operator==(const Resource& other) const;
+    bool operator!=(const Resource& other) const;
+  };
+
+  void InitializeFromHardwareSpec(const cmCTestHardwareSpec& spec);
+
+  const std::map<std::string, std::map<std::string, Resource>>& GetResources()
+    const;
+
+  bool AllocateResource(const std::string& name, const std::string& id,
+                        unsigned int slots);
+  bool DeallocateResource(const std::string& name, const std::string& id,
+                          unsigned int slots);
+
+private:
+  std::map<std::string, std::map<std::string, Resource>> Resources;
+};
+
+#endif
diff --git a/Source/CTest/cmCTestHardwareSpec.cxx b/Source/CTest/cmCTestHardwareSpec.cxx
new file mode 100644
index 0000000..137398a
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareSpec.cxx
@@ -0,0 +1,133 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCTestHardwareSpec.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_jsoncpp_reader.h"
+#include "cm_jsoncpp_value.h"
+
+static const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
+static const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
+
+bool cmCTestHardwareSpec::ReadFromJSONFile(const std::string& filename)
+{
+  cmsys::ifstream fin(filename.c_str());
+  if (!fin) {
+    return false;
+  }
+
+  Json::Value root;
+  Json::CharReaderBuilder builder;
+  if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
+    return false;
+  }
+
+  if (!root.isObject()) {
+    return false;
+  }
+
+  auto const& local = root["local"];
+  if (!local.isArray()) {
+    return false;
+  }
+  if (local.size() > 1) {
+    return false;
+  }
+
+  if (local.empty()) {
+    this->LocalSocket.Resources.clear();
+    return true;
+  }
+
+  auto const& localSocket = local[0];
+  if (!localSocket.isObject()) {
+    return false;
+  }
+  std::map<std::string, std::vector<cmCTestHardwareSpec::Resource>> resources;
+  cmsys::RegularExpressionMatch match;
+  for (auto const& key : localSocket.getMemberNames()) {
+    if (IdentifierRegex.find(key.c_str(), match)) {
+      auto const& value = localSocket[key];
+      auto& r = resources[key];
+      if (value.isArray()) {
+        for (auto const& item : value) {
+          if (item.isObject()) {
+            cmCTestHardwareSpec::Resource resource;
+
+            if (!item.isMember("id")) {
+              return false;
+            }
+            auto const& id = item["id"];
+            if (!id.isString()) {
+              return false;
+            }
+            resource.Id = id.asString();
+            if (!IdRegex.find(resource.Id.c_str(), match)) {
+              return false;
+            }
+
+            if (item.isMember("slots")) {
+              auto const& capacity = item["slots"];
+              if (!capacity.isConvertibleTo(Json::uintValue)) {
+                return false;
+              }
+              resource.Capacity = capacity.asUInt();
+            } else {
+              resource.Capacity = 1;
+            }
+
+            r.push_back(resource);
+          } else {
+            return false;
+          }
+        }
+      } else {
+        return false;
+      }
+    }
+  }
+
+  this->LocalSocket.Resources = std::move(resources);
+  return true;
+}
+
+bool cmCTestHardwareSpec::operator==(const cmCTestHardwareSpec& other) const
+{
+  return this->LocalSocket == other.LocalSocket;
+}
+
+bool cmCTestHardwareSpec::operator!=(const cmCTestHardwareSpec& other) const
+{
+  return !(*this == other);
+}
+
+bool cmCTestHardwareSpec::Socket::operator==(
+  const cmCTestHardwareSpec::Socket& other) const
+{
+  return this->Resources == other.Resources;
+}
+
+bool cmCTestHardwareSpec::Socket::operator!=(
+  const cmCTestHardwareSpec::Socket& other) const
+{
+  return !(*this == other);
+}
+
+bool cmCTestHardwareSpec::Resource::operator==(
+  const cmCTestHardwareSpec::Resource& other) const
+{
+  return this->Id == other.Id && this->Capacity == other.Capacity;
+}
+
+bool cmCTestHardwareSpec::Resource::operator!=(
+  const cmCTestHardwareSpec::Resource& other) const
+{
+  return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestHardwareSpec.h b/Source/CTest/cmCTestHardwareSpec.h
new file mode 100644
index 0000000..a0b4cae
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareSpec.h
@@ -0,0 +1,40 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCTestHardwareSpec_h
+#define cmCTestHardwareSpec_h
+
+#include <map>
+#include <string>
+#include <vector>
+
+class cmCTestHardwareSpec
+{
+public:
+  class Resource
+  {
+  public:
+    std::string Id;
+    unsigned int Capacity;
+
+    bool operator==(const Resource& other) const;
+    bool operator!=(const Resource& other) const;
+  };
+
+  class Socket
+  {
+  public:
+    std::map<std::string, std::vector<Resource>> Resources;
+
+    bool operator==(const Socket& other) const;
+    bool operator!=(const Socket& other) const;
+  };
+
+  Socket LocalSocket;
+
+  bool ReadFromJSONFile(const std::string& filename);
+
+  bool operator==(const cmCTestHardwareSpec& other) const;
+  bool operator!=(const cmCTestHardwareSpec& other) const;
+};
+
+#endif
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index a96513e..647f5ff 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunch.h"
 
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
-#include <iostream>
-#include <memory> // IWYU pragma: keep
-#include <stdlib.h>
-#include <string.h>
 
 #include "cmCryptoHash.h"
 #include "cmGeneratedFileStream.h"
@@ -17,6 +17,7 @@
 #include "cmProcessOutput.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
@@ -176,14 +177,8 @@
   this->LogHash = md5.FinalizeHex();
 
   // We store stdout and stderr in temporary log files.
-  this->LogOut = this->LogDir;
-  this->LogOut += "launch-";
-  this->LogOut += this->LogHash;
-  this->LogOut += "-out.txt";
-  this->LogErr = this->LogDir;
-  this->LogErr += "launch-";
-  this->LogErr += this->LogHash;
-  this->LogErr += "-err.txt";
+  this->LogOut = cmStrCat(this->LogDir, "launch-", this->LogHash, "-out.txt");
+  this->LogErr = cmStrCat(this->LogDir, "launch-", this->LogHash, "-err.txt");
 }
 
 void cmCTestLaunch::RunChild()
@@ -282,11 +277,8 @@
   }
 
   // Labels are listed in per-target files.
-  std::string fname = this->OptionBuildDir;
-  fname += "/CMakeFiles";
-  fname += "/";
-  fname += this->OptionTargetName;
-  fname += ".dir/Labels.txt";
+  std::string fname = cmStrCat(this->OptionBuildDir, "/CMakeFiles/",
+                               this->OptionTargetName, ".dir/Labels.txt");
 
   // We are interested in per-target labels for this source file.
   std::string source = this->OptionSource;
@@ -340,10 +332,9 @@
 void cmCTestLaunch::WriteXML()
 {
   // Name the xml file.
-  std::string logXML = this->LogDir;
-  logXML += this->IsError() ? "error-" : "warning-";
-  logXML += this->LogHash;
-  logXML += ".xml";
+  std::string logXML =
+    cmStrCat(this->LogDir, this->IsError() ? "error-" : "warning-",
+             this->LogHash, ".xml");
 
   // Use cmGeneratedFileStream to atomically create the report file.
   cmGeneratedFileStream fxml(logXML);
@@ -494,9 +485,9 @@
       continue;
     }
     if (this->Match(line, this->RegexWarningSuppress)) {
-      line = "[CTest: warning suppressed] " + line;
+      line = cmStrCat("[CTest: warning suppressed] ", line);
     } else if (this->Match(line, this->RegexWarning)) {
-      line = "[CTest: warning matched] " + line;
+      line = cmStrCat("[CTest: warning matched] ", line);
     }
     e4.Content(sep);
     e4.Content(line);
@@ -546,10 +537,7 @@
 void cmCTestLaunch::LoadScrapeRules(
   const char* purpose, std::vector<cmsys::RegularExpression>& regexps)
 {
-  std::string fname = this->LogDir;
-  fname += "Custom";
-  fname += purpose;
-  fname += ".txt";
+  std::string fname = cmStrCat(this->LogDir, "Custom", purpose, ".txt");
   cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary);
   std::string line;
   cmsys::RegularExpression rex;
@@ -595,7 +583,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[])
@@ -617,8 +605,7 @@
   cm.GetCurrentSnapshot().SetDefaultDefinitions();
   cmGlobalGenerator gg(&cm);
   cmMakefile mf(&gg, cm.GetCurrentSnapshot());
-  std::string fname = this->LogDir;
-  fname += "CTestLaunchConfig.cmake";
+  std::string fname = cmStrCat(this->LogDir, "CTestLaunchConfig.cmake");
   if (cmSystemTools::FileExists(fname) && mf.ReadListFile(fname)) {
     this->SourceDir = mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
     cmSystemTools::ConvertToUnixSlashes(this->SourceDir);
diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h
index 107fd61..79a7712 100644
--- a/Source/CTest/cmCTestLaunch.h
+++ b/Source/CTest/cmCTestLaunch.h
@@ -5,11 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 class cmXMLElement;
 
 /** \class cmCTestLaunch
diff --git a/Source/CTest/cmCTestMemCheckCommand.cxx b/Source/CTest/cmCTestMemCheckCommand.cxx
index 7dad1ce..39dec6d 100644
--- a/Source/CTest/cmCTestMemCheckCommand.cxx
+++ b/Source/CTest/cmCTestMemCheckCommand.cxx
@@ -2,19 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestMemCheckCommand.h"
 
-#include <sstream>
-#include <string>
-#include <vector>
+#include "cm_static_string_view.hxx"
 
 #include "cmCTest.h"
 #include "cmCTestMemCheckHandler.h"
 #include "cmMakefile.h"
 
-cmCTestMemCheckCommand::cmCTestMemCheckCommand()
+void cmCTestMemCheckCommand::BindArguments()
 {
-  this->Arguments[ctm_DEFECT_COUNT] = "DEFECT_COUNT";
-  this->Arguments[ctm_LAST] = nullptr;
-  this->Last = ctm_LAST;
+  this->cmCTestTestCommand::BindArguments();
+  this->Bind("DEFECT_COUNT"_s, this->DefectCount);
 }
 
 cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler()
@@ -44,10 +41,10 @@
 void cmCTestMemCheckCommand::ProcessAdditionalValues(
   cmCTestGenericHandler* handler)
 {
-  if (this->Values[ctm_DEFECT_COUNT] && *this->Values[ctm_DEFECT_COUNT]) {
-    std::ostringstream str;
-    str << static_cast<cmCTestMemCheckHandler*>(handler)->GetDefectCount();
-    this->Makefile->AddDefinition(this->Values[ctm_DEFECT_COUNT],
-                                  str.str().c_str());
+  if (!this->DefectCount.empty()) {
+    this->Makefile->AddDefinition(
+      this->DefectCount,
+      std::to_string(
+        static_cast<cmCTestMemCheckHandler*>(handler)->GetDefectCount()));
   }
 }
diff --git a/Source/CTest/cmCTestMemCheckCommand.h b/Source/CTest/cmCTestMemCheckCommand.h
index b6b3c40..8f4ffb8 100644
--- a/Source/CTest/cmCTestMemCheckCommand.h
+++ b/Source/CTest/cmCTestMemCheckCommand.h
@@ -5,10 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmCTestTestCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestMemCheck
  * \brief Run a ctest script
@@ -18,29 +23,25 @@
 class cmCTestMemCheckCommand : public cmCTestTestCommand
 {
 public:
-  cmCTestMemCheckCommand();
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestMemCheckCommand* ni = new cmCTestMemCheckCommand;
+    auto ni = cm::make_unique<cmCTestMemCheckCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
 protected:
+  void BindArguments() override;
+
   cmCTestGenericHandler* InitializeActualHandler() override;
 
   void ProcessAdditionalValues(cmCTestGenericHandler* handler) override;
 
-  enum
-  {
-    ctm_DEFECT_COUNT = ctt_LAST,
-    ctm_LAST
-  };
+  std::string DefectCount;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index b09e7bb..a5ec1ae 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -2,21 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestMemCheckHandler.h"
 
+#include <chrono>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <utility>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmCTest.h"
 #include "cmDuration.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include "cmsys/RegularExpression.hxx"
-#include <chrono>
-#include <iostream>
-#include <sstream>
-#include <string.h>
-#include <utility>
-
 struct CatToErrorType
 {
   const char* ErrorCategory;
@@ -1074,10 +1075,7 @@
 void cmCTestMemCheckHandler::TestOutputFileNames(
   int test, std::vector<std::string>& files)
 {
-  std::string index;
-  std::ostringstream stream;
-  stream << test;
-  index = stream.str();
+  std::string index = std::to_string(test);
   std::string ofile = this->MemoryTesterOutputFile;
   std::string::size_type pos = ofile.find("??");
   ofile.replace(pos, 2, index);
diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h
index 746d72c..eda65f7 100644
--- a/Source/CTest/cmCTestMemCheckHandler.h
+++ b/Source/CTest/cmCTestMemCheckHandler.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestTestHandler.h"
-
 #include <string>
 #include <vector>
 
+#include "cmCTestTestHandler.h"
+
 class cmMakefile;
 class cmXMLWriter;
 
@@ -22,7 +22,7 @@
   friend class cmCTestRunTest;
 
 public:
-  typedef cmCTestTestHandler Superclass;
+  using Superclass = cmCTestTestHandler;
 
   void PopulateCustomVectors(cmMakefile* mf) override;
 
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 1b71f2a..7e8d548 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -2,38 +2,43 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestMultiProcessHandler.h"
 
-#include "cmAffinity.h"
-#include "cmAlgorithms.h"
-#include "cmCTest.h"
-#include "cmCTestRunTest.h"
-#include "cmCTestTestHandler.h"
-#include "cmDuration.h"
-#include "cmListFileCache.h"
-#include "cmRange.h"
-#include "cmSystemTools.h"
-#include "cmWorkingDirectory.h"
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cmath>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <memory>
+#include <sstream>
+#include <stack>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/SystemInformation.hxx"
 
 #include "cm_jsoncpp_value.h"
 #include "cm_jsoncpp_writer.h"
 #include "cm_uv.h"
 
+#include "cmAffinity.h"
+#include "cmAlgorithms.h"
+#include "cmCTest.h"
+#include "cmCTestBinPacker.h"
+#include "cmCTestRunTest.h"
+#include "cmCTestTestHandler.h"
+#include "cmDuration.h"
+#include "cmListFileCache.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmUVSignalHackRAII.h" // IWYU pragma: keep
-
-#include "cmsys/FStream.hxx"
-#include "cmsys/SystemInformation.hxx"
-
-#include <algorithm>
-#include <chrono>
-#include <cstring>
-#include <iomanip>
-#include <iostream>
-#include <list>
-#include <math.h>
-#include <sstream>
-#include <stack>
-#include <stdlib.h>
-#include <unordered_map>
-#include <utility>
+#include "cmWorkingDirectory.h"
 
 namespace cmsys {
 class RegularExpression;
@@ -108,8 +113,7 @@
   std::string fake_load_value;
   if (cmSystemTools::GetEnv("__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING",
                             fake_load_value)) {
-    if (!cmSystemTools::StringToULong(fake_load_value.c_str(),
-                                      &this->FakeLoadForTesting)) {
+    if (!cmStrToULong(fake_load_value, &this->FakeLoadForTesting)) {
       cmSystemTools::Error("Failed to parse fake load value: " +
                            fake_load_value);
     }
@@ -132,6 +136,12 @@
   uv_run(&this->Loop, UV_RUN_DEFAULT);
   uv_loop_close(&this->Loop);
 
+  if (!this->StopTimePassed) {
+    assert(this->Completed == this->Total);
+    assert(this->Tests.empty());
+  }
+  assert(this->AllHardwareAvailable());
+
   this->MarkFinished();
   this->UpdateCostData();
 }
@@ -167,19 +177,28 @@
   }
   testRun->SetIndex(test);
   testRun->SetTestProperties(this->Properties[test]);
+  if (this->TestHandler->UseHardwareSpec) {
+    testRun->SetUseAllocatedHardware(true);
+    testRun->SetAllocatedHardware(this->AllocatedHardware[test]);
+  }
 
   // Find any failed dependencies for this test. We assume the more common
   // scenario has no failed tests, so make it the outer loop.
   for (std::string const& f : *this->Failed) {
-    if (this->Properties[test]->RequireSuccessDepends.find(f) !=
-        this->Properties[test]->RequireSuccessDepends.end()) {
+    if (cmContains(this->Properties[test]->RequireSuccessDepends, f)) {
       testRun->AddFailedDependency(f);
     }
   }
 
   // Always lock the resources we'll be using, even if we fail to set the
   // working directory because FinishTestProcess() will try to unlock them
-  this->LockResources(test);
+  this->AllocateResources(test);
+
+  if (!this->TestsHaveSufficientHardware[test]) {
+    testRun->StartFailure("Insufficient hardware");
+    this->FinishTestProcess(testRun, false);
+    return false;
+  }
 
   cmWorkingDirectory workdir(this->Properties[test]->Directory);
   if (workdir.Failed()) {
@@ -188,14 +207,121 @@
                           std::strerror(workdir.GetLastResult()));
   } else {
     if (testRun->StartTest(this->Completed, this->Total)) {
+      // Ownership of 'testRun' has moved to another structure.
+      // When the test finishes, FinishTestProcess will be called.
       return true;
     }
   }
 
+  // Pass ownership of 'testRun'.
   this->FinishTestProcess(testRun, false);
   return false;
 }
 
+bool cmCTestMultiProcessHandler::AllocateHardware(int index)
+{
+  if (!this->TestHandler->UseHardwareSpec) {
+    return true;
+  }
+
+  std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
+  if (!this->TryAllocateHardware(index, allocations)) {
+    return false;
+  }
+
+  auto& allocatedHardware = this->AllocatedHardware[index];
+  allocatedHardware.resize(this->Properties[index]->Processes.size());
+  for (auto const& it : allocations) {
+    for (auto const& alloc : it.second) {
+      bool result = this->HardwareAllocator.AllocateResource(
+        it.first, alloc.Id, alloc.SlotsNeeded);
+      (void)result;
+      assert(result);
+      allocatedHardware[alloc.ProcessIndex][it.first].push_back(
+        { alloc.Id, static_cast<unsigned int>(alloc.SlotsNeeded) });
+    }
+  }
+
+  return true;
+}
+
+bool cmCTestMultiProcessHandler::TryAllocateHardware(
+  int index,
+  std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations)
+{
+  allocations.clear();
+
+  std::size_t processIndex = 0;
+  for (auto const& process : this->Properties[index]->Processes) {
+    for (auto const& requirement : process) {
+      for (int i = 0; i < requirement.UnitsNeeded; ++i) {
+        allocations[requirement.ResourceType].push_back(
+          { processIndex, requirement.SlotsNeeded, "" });
+      }
+    }
+    ++processIndex;
+  }
+
+  auto const& availableHardware = this->HardwareAllocator.GetResources();
+  for (auto& it : allocations) {
+    if (!availableHardware.count(it.first)) {
+      return false;
+    }
+    if (!cmAllocateCTestHardwareRoundRobin(availableHardware.at(it.first),
+                                           it.second)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void cmCTestMultiProcessHandler::DeallocateHardware(int index)
+{
+  if (!this->TestHandler->UseHardwareSpec) {
+    return;
+  }
+
+  {
+    auto& allocatedHardware = this->AllocatedHardware[index];
+    for (auto const& processAlloc : allocatedHardware) {
+      for (auto const& it : processAlloc) {
+        auto resourceType = it.first;
+        for (auto const& it2 : it.second) {
+          bool success = this->HardwareAllocator.DeallocateResource(
+            resourceType, it2.Id, it2.Slots);
+          (void)success;
+          assert(success);
+        }
+      }
+    }
+  }
+  this->AllocatedHardware.erase(index);
+}
+
+bool cmCTestMultiProcessHandler::AllHardwareAvailable()
+{
+  for (auto const& it : this->HardwareAllocator.GetResources()) {
+    for (auto const& it2 : it.second) {
+      if (it2.second.Locked != 0) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+void cmCTestMultiProcessHandler::CheckHardwareAvailable()
+{
+  for (auto test : this->SortedTests) {
+    std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
+    this->TestsHaveSufficientHardware[test] =
+      !this->TestHandler->UseHardwareSpec ||
+      this->TryAllocateHardware(test, allocations);
+  }
+}
+
 bool cmCTestMultiProcessHandler::CheckStopTimePassed()
 {
   if (!this->StopTimePassed) {
@@ -220,7 +346,7 @@
   }
 }
 
-void cmCTestMultiProcessHandler::LockResources(int index)
+void cmCTestMultiProcessHandler::AllocateResources(int index)
 {
   this->LockedResources.insert(
     this->Properties[index]->LockedResources.begin(),
@@ -231,7 +357,7 @@
   }
 }
 
-void cmCTestMultiProcessHandler::UnlockResources(int index)
+void cmCTestMultiProcessHandler::DeallocateResources(int index)
 {
   for (std::string const& i : this->Properties[index]->LockedResources) {
     this->LockedResources.erase(i);
@@ -273,17 +399,25 @@
 {
   // Check for locked resources
   for (std::string const& i : this->Properties[test]->LockedResources) {
-    if (this->LockedResources.find(i) != this->LockedResources.end()) {
+    if (cmContains(this->LockedResources, i)) {
       return false;
     }
   }
 
+  // Allocate hardware
+  if (this->TestsHaveSufficientHardware[test] &&
+      !this->AllocateHardware(test)) {
+    this->DeallocateHardware(test);
+    return false;
+  }
+
   // if there are no depends left then run this test
   if (this->Tests[test].empty()) {
     return this->StartTestProcess(test);
   }
   // This test was not able to start because it is waiting
   // on depends to run
+  this->DeallocateHardware(test);
   return false;
 }
 
@@ -468,7 +602,8 @@
   this->TestFinishMap[test] = true;
   this->TestRunningMap[test] = false;
   this->WriteCheckpoint(test);
-  this->UnlockResources(test);
+  this->DeallocateHardware(test);
+  this->DeallocateResources(test);
   this->RunningCount -= GetProcessorsUsed(test);
 
   for (auto p : properties->Affinity) {
@@ -619,9 +754,7 @@
   // In parallel test runs add previously failed tests to the front
   // of the cost list and queue other tests for further sorting
   for (auto const& t : this->Tests) {
-    if (std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(),
-                  this->Properties[t.first]->Name) !=
-        this->LastTestsFailed.end()) {
+    if (cmContains(this->LastTestsFailed, this->Properties[t.first]->Name)) {
       // If the test failed last time, it should be run first.
       this->SortedTests.push_back(t.first);
       alreadySortedTests.insert(t.first);
@@ -660,7 +793,7 @@
                      TestComparator(this));
 
     for (auto const& j : sortedCopy) {
-      if (alreadySortedTests.find(j) == alreadySortedTests.end()) {
+      if (!cmContains(alreadySortedTests, j)) {
         this->SortedTests.push_back(j);
         alreadySortedTests.insert(j);
       }
@@ -692,7 +825,7 @@
   TestSet alreadySortedTests;
 
   for (int test : presortedList) {
-    if (alreadySortedTests.find(test) != alreadySortedTests.end()) {
+    if (cmContains(alreadySortedTests, test)) {
       continue;
     }
 
@@ -700,8 +833,7 @@
     GetAllTestDependencies(test, dependencies);
 
     for (int testDependency : dependencies) {
-      if (alreadySortedTests.find(testDependency) ==
-          alreadySortedTests.end()) {
+      if (!cmContains(alreadySortedTests, testDependency)) {
         alreadySortedTests.insert(testDependency);
         this->SortedTests.push_back(testDependency);
       }
@@ -780,6 +912,28 @@
   return timeoutAfterMatch;
 }
 
+static Json::Value DumpProcessesToJsonArray(
+  const std::vector<
+    std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+    processes)
+{
+  Json::Value jsonProcesses = Json::arrayValue;
+  for (auto const& it : processes) {
+    Json::Value jsonProcess = Json::objectValue;
+    Json::Value requirements = Json::arrayValue;
+    for (auto const& it2 : it) {
+      Json::Value res = Json::objectValue;
+      res[".type"] = it2.ResourceType;
+      // res[".units"] = it2.UnitsNeeded; // Intentionally commented out
+      res["slots"] = it2.SlotsNeeded;
+      requirements.append(res);
+    }
+    jsonProcess["requirements"] = requirements;
+    jsonProcesses.append(jsonProcess);
+  }
+  return jsonProcesses;
+}
+
 static Json::Value DumpCTestProperty(std::string const& name,
                                      Json::Value value)
 {
@@ -821,6 +975,11 @@
       "FAIL_REGULAR_EXPRESSION",
       DumpRegExToJsonArray(testProperties.ErrorRegularExpressions)));
   }
+  if (!testProperties.SkipRegularExpressions.empty()) {
+    properties.append(DumpCTestProperty(
+      "SKIP_REGULAR_EXPRESSION",
+      DumpRegExToJsonArray(testProperties.SkipRegularExpressions)));
+  }
   if (!testProperties.FixturesCleanup.empty()) {
     properties.append(DumpCTestProperty(
       "FIXTURES_CLEANUP", DumpToJsonArray(testProperties.FixturesCleanup)));
@@ -846,6 +1005,10 @@
       "PASS_REGULAR_EXPRESSION",
       DumpRegExToJsonArray(testProperties.RequiredRegularExpressions)));
   }
+  if (!testProperties.Processes.empty()) {
+    properties.append(DumpCTestProperty(
+      "PROCESSES", DumpProcessesToJsonArray(testProperties.Processes)));
+  }
   if (testProperties.WantAffinity) {
     properties.append(
       DumpCTestProperty("PROCESSOR_AFFINITY", testProperties.WantAffinity));
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index 93bb880..da716f0 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -5,17 +5,22 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestTestHandler.h"
 #include <map>
 #include <set>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
-#include "cmUVHandlePtr.h"
+#include <stddef.h>
+
 #include "cm_uv.h"
 
+#include "cmCTestHardwareAllocator.h"
+#include "cmCTestTestHandler.h"
+#include "cmUVHandlePtr.h"
+
 class cmCTest;
+struct cmCTestBinPackerAllocation;
+class cmCTestHardwareSpec;
 class cmCTestRunTest;
 
 /** \class cmCTestMultiProcessHandler
@@ -42,6 +47,11 @@
     : public std::map<int, cmCTestTestHandler::cmCTestTestProperties*>
   {
   };
+  struct HardwareAllocation
+  {
+    std::string Id;
+    unsigned int Slots;
+  };
 
   cmCTestMultiProcessHandler();
   virtual ~cmCTestMultiProcessHandler();
@@ -77,6 +87,13 @@
 
   void SetQuiet(bool b) { this->Quiet = b; }
 
+  void InitHardwareAllocator(const cmCTestHardwareSpec& spec)
+  {
+    this->HardwareAllocator.InitializeFromHardwareSpec(spec);
+  }
+
+  void CheckHardwareAvailable();
+
 protected:
   // Start the next test or tests as many as are allowed by
   // ParallelLevel
@@ -117,8 +134,17 @@
   bool CheckStopTimePassed();
   void SetStopTimePassed();
 
-  void LockResources(int index);
-  void UnlockResources(int index);
+  void AllocateResources(int index);
+  void DeallocateResources(int index);
+
+  bool AllocateHardware(int index);
+  bool TryAllocateHardware(
+    int index,
+    std::map<std::string, std::vector<cmCTestBinPackerAllocation>>&
+      allocations);
+  void DeallocateHardware(int index);
+  bool AllHardwareAvailable();
+
   // map from test number to set of depend tests
   TestMap Tests;
   TestList SortedTests;
@@ -139,6 +165,11 @@
   std::vector<std::string>* Failed;
   std::vector<std::string> LastTestsFailed;
   std::set<std::string> LockedResources;
+  std::map<int,
+           std::vector<std::map<std::string, std::vector<HardwareAllocation>>>>
+    AllocatedHardware;
+  std::map<int, bool> TestsHaveSufficientHardware;
+  cmCTestHardwareAllocator HardwareAllocator;
   std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
   size_t ParallelLevel; // max number of process that can be run at once
   unsigned long TestLoad;
diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx
index 2eb8dba..e2063e1 100644
--- a/Source/CTest/cmCTestP4.cxx
+++ b/Source/CTest/cmCTestP4.cxx
@@ -2,19 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestP4.h"
 
+#include <algorithm>
+#include <ctime>
+#include <ostream>
+#include <utility>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmProcessTools.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <ostream>
-#include <time.h>
-#include <utility>
-
 cmCTestP4::cmCTestP4(cmCTest* ct, std::ostream& log)
   : cmCTestGlobalVC(ct, log)
 {
@@ -145,8 +147,7 @@
 
 cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
 {
-  std::map<std::string, cmCTestP4::User>::const_iterator it =
-    Users.find(username);
+  auto it = Users.find(username);
 
   if (it == Users.end()) {
     std::vector<char const*> p4_users;
@@ -203,8 +204,8 @@
   cmsys::RegularExpression RegexDiff;
   cmCTestP4* P4;
 
-  typedef cmCTestP4::Revision Revision;
-  typedef cmCTestP4::Change Change;
+  using Revision = cmCTestP4::Revision;
+  using Change = cmCTestP4::Change;
   std::vector<Change> Changes;
   enum SectionType
   {
@@ -459,8 +460,7 @@
 
 bool cmCTestP4::UpdateCustom(const std::string& custom)
 {
-  std::vector<std::string> p4_custom_command;
-  cmSystemTools::ExpandListArgument(custom, p4_custom_command, true);
+  std::vector<std::string> p4_custom_command = cmExpandedList(custom, true);
 
   std::vector<char const*> p4_custom;
   p4_custom.reserve(p4_custom_command.size() + 1);
diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h
index b14edf7..e19472e 100644
--- a/Source/CTest/cmCTestP4.h
+++ b/Source/CTest/cmCTestP4.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGlobalVC.h"
-
 #include <iosfwd>
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmCTestGlobalVC.h"
+
 class cmCTest;
 
 /** \class cmCTestP4
diff --git a/Source/CTest/cmCTestProcessesLexerHelper.cxx b/Source/CTest/cmCTestProcessesLexerHelper.cxx
new file mode 100644
index 0000000..797164b
--- /dev/null
+++ b/Source/CTest/cmCTestProcessesLexerHelper.cxx
@@ -0,0 +1,55 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCTestProcessesLexerHelper.h"
+
+#include "cmCTestProcessesLexer.h"
+#include "cmCTestTestHandler.h"
+
+cmCTestProcessesLexerHelper::cmCTestProcessesLexerHelper(
+  std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+    output)
+  : Output(output)
+{
+}
+
+bool cmCTestProcessesLexerHelper::ParseString(const std::string& value)
+{
+  yyscan_t lexer;
+  cmCTestProcesses_yylex_init_extra(this, &lexer);
+
+  auto state = cmCTestProcesses_yy_scan_string(value.c_str(), lexer);
+  int retval = cmCTestProcesses_yylex(lexer);
+  cmCTestProcesses_yy_delete_buffer(state, lexer);
+
+  cmCTestProcesses_yylex_destroy(lexer);
+  return retval == 0;
+}
+
+void cmCTestProcessesLexerHelper::SetProcessCount(unsigned int count)
+{
+  this->ProcessCount = count;
+}
+
+void cmCTestProcessesLexerHelper::SetResourceType(const std::string& type)
+{
+  this->ResourceType = type;
+}
+
+void cmCTestProcessesLexerHelper::SetNeededSlots(int count)
+{
+  this->NeededSlots = count;
+}
+
+void cmCTestProcessesLexerHelper::WriteRequirement()
+{
+  this->Process.push_back({ this->ResourceType, this->NeededSlots, 1 });
+}
+
+void cmCTestProcessesLexerHelper::WriteProcess()
+{
+  for (unsigned int i = 0; i < this->ProcessCount; ++i) {
+    this->Output.push_back(this->Process);
+  }
+  this->Process.clear();
+  this->ProcessCount = 1;
+}
diff --git a/Source/CTest/cmCTestProcessesLexerHelper.h b/Source/CTest/cmCTestProcessesLexerHelper.h
new file mode 100644
index 0000000..6c9289f
--- /dev/null
+++ b/Source/CTest/cmCTestProcessesLexerHelper.h
@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCTestProcessesLexerHelper_h
+#define cmCTestProcessesLexerHelper_h
+
+#include <string>
+#include <vector>
+
+#include "cmCTestTestHandler.h"
+
+class cmCTestProcessesLexerHelper
+{
+public:
+  struct ParserType
+  {
+  };
+
+  cmCTestProcessesLexerHelper(
+    std::vector<
+      std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+      output);
+  ~cmCTestProcessesLexerHelper() = default;
+
+  bool ParseString(const std::string& value);
+
+  void SetProcessCount(unsigned int count);
+  void SetResourceType(const std::string& type);
+  void SetNeededSlots(int count);
+  void WriteRequirement();
+  void WriteProcess();
+
+private:
+  std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+    Output;
+
+  unsigned int ProcessCount = 1;
+  std::string ResourceType;
+  int NeededSlots;
+  std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement> Process;
+};
+
+#define YY_EXTRA_TYPE cmCTestProcessesLexerHelper*
+
+#endif
diff --git a/Source/CTest/cmCTestReadCustomFilesCommand.h b/Source/CTest/cmCTestReadCustomFilesCommand.h
index ba25c51..cbb9390 100644
--- a/Source/CTest/cmCTestReadCustomFilesCommand.h
+++ b/Source/CTest/cmCTestReadCustomFilesCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
 #include <string>
+#include <utility>
 #include <vector>
 
-class cmCommand;
+#include <cm/memory>
+
+#include "cmCTestCommand.h"
+#include "cmCommand.h"
+
 class cmExecutionStatus;
 
 /** \class cmCTestReadCustomFiles
@@ -27,11 +30,11 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestReadCustomFilesCommand* ni = new cmCTestReadCustomFilesCommand;
+    auto ni = cm::make_unique<cmCTestReadCustomFilesCommand>();
     ni->CTest = this->CTest;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestRunScriptCommand.cxx b/Source/CTest/cmCTestRunScriptCommand.cxx
index a7e47d3..f59ca57 100644
--- a/Source/CTest/cmCTestRunScriptCommand.cxx
+++ b/Source/CTest/cmCTestRunScriptCommand.cxx
@@ -5,8 +5,6 @@
 #include "cmCTestScriptHandler.h"
 #include "cmMakefile.h"
 
-#include <sstream>
-
 class cmExecutionStatus;
 
 bool cmCTestRunScriptCommand::InitialPass(std::vector<std::string> const& args,
@@ -41,9 +39,7 @@
       int ret;
       cmCTestScriptHandler::RunScript(this->CTest, this->Makefile,
                                       args[i].c_str(), !np, &ret);
-      std::ostringstream str;
-      str << ret;
-      this->Makefile->AddDefinition(returnVariable, str.str().c_str());
+      this->Makefile->AddDefinition(returnVariable, std::to_string(ret));
     }
   }
   return true;
diff --git a/Source/CTest/cmCTestRunScriptCommand.h b/Source/CTest/cmCTestRunScriptCommand.h
index 9d8b4b5..2d8bde1 100644
--- a/Source/CTest/cmCTestRunScriptCommand.h
+++ b/Source/CTest/cmCTestRunScriptCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
 #include <string>
+#include <utility>
 #include <vector>
 
-class cmCommand;
+#include <cm/memory>
+
+#include "cmCTestCommand.h"
+#include "cmCommand.h"
+
 class cmExecutionStatus;
 
 /** \class cmCTestRunScript
@@ -27,12 +30,12 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestRunScriptCommand* ni = new cmCTestRunScriptCommand;
+    auto ni = cm::make_unique<cmCTestRunScriptCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 31976b9..7f7f736 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -2,24 +2,28 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestRunTest.h"
 
-#include "cmCTest.h"
-#include "cmCTestMemCheckHandler.h"
-#include "cmCTestMultiProcessHandler.h"
-#include "cmProcess.h"
-#include "cmSystemTools.h"
-#include "cmWorkingDirectory.h"
-
-#include "cmsys/RegularExpression.hxx"
 #include <chrono>
-#include <cmAlgorithms.h>
+#include <cstddef>
 #include <cstdint>
+#include <cstdio>
 #include <cstring>
 #include <iomanip>
 #include <ratio>
 #include <sstream>
-#include <stdio.h>
 #include <utility>
 
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmCTest.h"
+#include "cmCTestMemCheckHandler.h"
+#include "cmCTestMultiProcessHandler.h"
+#include "cmProcess.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmWorkingDirectory.h"
+
 cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
   : MultiTestHandler(multiHandler)
 {
@@ -76,6 +80,7 @@
   }
   std::int64_t retVal = this->TestProcess->GetExitValue();
   bool forceFail = false;
+  bool forceSkip = false;
   bool skipped = false;
   bool outputTestErrorsToConsole = false;
   if (!this->TestProperties->RequiredRegularExpressions.empty() &&
@@ -84,49 +89,62 @@
     for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
       if (pass.first.find(this->ProcessOutput)) {
         found = true;
-        reason = "Required regular expression found.";
+        reason = cmStrCat("Required regular expression found. Regex=[",
+                          pass.second, ']');
         break;
       }
     }
     if (!found) {
-      reason = "Required regular expression not found.";
+      reason = "Required regular expression not found. Regex=[";
+      for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
+        reason += pass.second;
+        reason += "\n";
+      }
+      reason += "]";
       forceFail = true;
     }
-    reason += "Regex=[";
-    for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
-      reason += pass.second;
-      reason += "\n";
-    }
-    reason += "]";
   }
   if (!this->TestProperties->ErrorRegularExpressions.empty() &&
       this->FailedDependencies.empty()) {
-    for (auto& pass : this->TestProperties->ErrorRegularExpressions) {
-      if (pass.first.find(this->ProcessOutput)) {
-        reason = "Error regular expression found in output.";
-        reason += " Regex=[";
-        reason += pass.second;
-        reason += "]";
+    for (auto& fail : this->TestProperties->ErrorRegularExpressions) {
+      if (fail.first.find(this->ProcessOutput)) {
+        reason = cmStrCat("Error regular expression found in output. Regex=[",
+                          fail.second, ']');
         forceFail = true;
         break;
       }
     }
   }
+  if (!this->TestProperties->SkipRegularExpressions.empty() &&
+      this->FailedDependencies.empty()) {
+    for (auto& skip : this->TestProperties->SkipRegularExpressions) {
+      if (skip.first.find(this->ProcessOutput)) {
+        reason = cmStrCat("Skip regular expression found in output. Regex=[",
+                          skip.second, ']');
+        forceSkip = true;
+        break;
+      }
+    }
+  }
   std::ostringstream outputStream;
   if (res == cmProcess::State::Exited) {
     bool success = !forceFail &&
       (retVal == 0 ||
        !this->TestProperties->RequiredRegularExpressions.empty());
-    if (this->TestProperties->SkipReturnCode >= 0 &&
-        this->TestProperties->SkipReturnCode == retVal) {
+    if ((this->TestProperties->SkipReturnCode >= 0 &&
+         this->TestProperties->SkipReturnCode == retVal) ||
+        forceSkip) {
       this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
       std::ostringstream s;
-      s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
+      if (forceSkip) {
+        s << "SKIP_REGULAR_EXPRESSION_MATCHED";
+      } else {
+        s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
+      }
       this->TestResult.CompletionStatus = s.str();
       cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped ");
       skipped = true;
-    } else if ((success && !this->TestProperties->WillFail) ||
-               (!success && this->TestProperties->WillFail)) {
+    } else if (success != this->TestProperties->WillFail) {
       this->TestResult.Status = cmCTestTestHandler::COMPLETED;
       outputStream << "   Passed  ";
     } else {
@@ -481,12 +499,11 @@
     this->TestProcess = cm::make_unique<cmProcess>(*this);
     std::string msg;
     if (this->CTest->GetConfigType().empty()) {
-      msg = "Test not available without configuration.";
-      msg += "  (Missing \"-C <config>\"?)";
+      msg = "Test not available without configuration.  (Missing \"-C "
+            "<config>\"?)";
     } else {
-      msg = "Test not available in configuration \"";
-      msg += this->CTest->GetConfigType();
-      msg += "\".";
+      msg = cmStrCat("Test not available in configuration \"",
+                     this->CTest->GetConfigType(), "\".");
     }
     *this->TestHandler->LogFile << msg << std::endl;
     cmCTestLog(this->CTest, ERROR_MESSAGE, msg << std::endl);
@@ -556,8 +573,7 @@
 void cmCTestRunTest::ComputeArguments()
 {
   this->Arguments.clear(); // reset because this might be a rerun
-  std::vector<std::string>::const_iterator j =
-    this->TestProperties->Args.begin();
+  auto j = this->TestProperties->Args.begin();
   ++j; // skip test name
   // find the test executable
   if (this->TestHandler->MemCheck) {
@@ -666,7 +682,7 @@
 
   this->TestProcess->SetTimeout(timeout);
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmSystemTools::SaveRestoreEnvironment sre;
 #endif
 
@@ -674,10 +690,52 @@
     cmSystemTools::AppendEnv(*environment);
   }
 
+  if (this->UseAllocatedHardware) {
+    this->SetupHardwareEnvironment();
+  } else {
+    cmSystemTools::UnsetEnv("CTEST_PROCESS_COUNT");
+  }
+
   return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
                                          affinity);
 }
 
+void cmCTestRunTest::SetupHardwareEnvironment()
+{
+  std::string processCount = "CTEST_PROCESS_COUNT=";
+  processCount += std::to_string(this->AllocatedHardware.size());
+  cmSystemTools::PutEnv(processCount);
+
+  std::size_t i = 0;
+  for (auto const& process : this->AllocatedHardware) {
+    std::string prefix = "CTEST_PROCESS_";
+    prefix += std::to_string(i);
+    std::string resourceList = prefix + '=';
+    prefix += '_';
+    bool firstType = true;
+    for (auto const& it : process) {
+      if (!firstType) {
+        resourceList += ',';
+      }
+      firstType = false;
+      auto resourceType = it.first;
+      resourceList += resourceType;
+      std::string var = prefix + cmSystemTools::UpperCase(resourceType) + '=';
+      bool firstName = true;
+      for (auto const& it2 : it.second) {
+        if (!firstName) {
+          var += ';';
+        }
+        firstName = false;
+        var += "id:" + it2.Id + ",slots:" + std::to_string(it2.Slots);
+      }
+      cmSystemTools::PutEnv(var);
+    }
+    cmSystemTools::PutEnv(resourceList);
+    ++i;
+  }
+}
+
 void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
 {
   std::ostringstream outputStream;
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index 38cc417..085a6b8 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -5,17 +5,20 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
+#include <memory>
 #include <set>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
+#include "cmCTestMultiProcessHandler.h"
 #include "cmCTestTestHandler.h"
 #include "cmDuration.h"
-#include "cmProcess.h" // IWYU pragma: keep (for unique_ptr)
+#include "cmProcess.h"
 
 class cmCTest;
-class cmCTestMultiProcessHandler;
 
 /** \class cmRunTest
  * \brief represents a single test to be run
@@ -81,6 +84,16 @@
 
   bool TimedOutForStopTime() const { return this->TimeoutIsForStopTime; }
 
+  void SetUseAllocatedHardware(bool use) { this->UseAllocatedHardware = use; }
+  void SetAllocatedHardware(
+    const std::vector<
+      std::map<std::string,
+               std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>&
+      hardware)
+  {
+    this->AllocatedHardware = hardware;
+  }
+
 private:
   bool NeedsToRerun();
   void DartProcessing();
@@ -92,6 +105,8 @@
   // Run post processing of the process output for MemCheck
   void MemCheckPostProcess();
 
+  void SetupHardwareEnvironment();
+
   // Returns "completed/total Test #Index: "
   std::string GetTestPrefix(size_t completed, size_t total) const;
 
@@ -110,6 +125,10 @@
   std::string StartTime;
   std::string ActualCommand;
   std::vector<std::string> Arguments;
+  bool UseAllocatedHardware = false;
+  std::vector<std::map<
+    std::string, std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>
+    AllocatedHardware;
   bool RunUntilFail;
   int NumberOfRunsLeft;
   bool RunAgain;
diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx
index c834686..34395c9 100644
--- a/Source/CTest/cmCTestSVN.cxx
+++ b/Source/CTest/cmCTestSVN.cxx
@@ -2,20 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestSVN.h"
 
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <ostream>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmProcessTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <map>
-#include <ostream>
-#include <stdlib.h>
-#include <string.h>
-
 struct cmCTestSVN::Revision : public cmCTestVC::Revision
 {
   cmCTestSVN::SVNInfo* SVNInfo;
@@ -143,9 +145,8 @@
     // the repository root.
     if (!svninfo.Root.empty() &&
         cmCTestSVNPathStarts(svninfo.URL, svninfo.Root)) {
-      svninfo.Base =
-        cmCTest::DecodeURL(svninfo.URL.substr(svninfo.Root.size()));
-      svninfo.Base += "/";
+      svninfo.Base = cmStrCat(
+        cmCTest::DecodeURL(svninfo.URL.substr(svninfo.Root.size())), '/');
     }
     this->Log << "Repository '" << svninfo.LocalPath
               << "' Base = " << svninfo.Base << "\n";
@@ -169,7 +170,7 @@
        slash = svninfo.URL.find('/', slash + 1)) {
     // If the URL suffix is a prefix of at least one path then it is the base.
     std::string base = cmCTest::DecodeURL(svninfo.URL.substr(slash));
-    for (std::vector<Change>::const_iterator ci = changes.begin();
+    for (auto ci = changes.begin();
          svninfo.Base.empty() && ci != changes.end(); ++ci) {
       if (cmCTestSVNPathStarts(ci->Path, base)) {
         svninfo.Base = base;
@@ -307,8 +308,8 @@
   cmCTestSVN* SVN;
   cmCTestSVN::SVNInfo& SVNRepo;
 
-  typedef cmCTestSVN::Revision Revision;
-  typedef cmCTestSVN::Change Change;
+  using Revision = cmCTestSVN::Revision;
+  using Change = cmCTestSVN::Change;
   Revision Rev;
   std::vector<Change> Changes;
   Change CurChange;
diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h
index 5c8505d..b74dc12 100644
--- a/Source/CTest/cmCTestSVN.h
+++ b/Source/CTest/cmCTestSVN.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGlobalVC.h"
-
 #include <iosfwd>
 #include <list>
 #include <string>
 #include <vector>
 
+#include "cmCTestGlobalVC.h"
+
 class cmCTest;
 class cmXMLWriter;
 
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index a739f44..60facbd 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -2,16 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestScriptHandler.h"
 
-#include "cmsys/Directory.hxx"
-#include "cmsys/Process.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <map>
+#include <memory>
 #include <ratio>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <utility>
 
+#include <cm/memory>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/Process.h"
+
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
 #include "cmCTestCommand.h"
@@ -27,14 +31,15 @@
 #include "cmCTestTestCommand.h"
 #include "cmCTestUpdateCommand.h"
 #include "cmCTestUploadCommand.h"
+#include "cmCommand.h"
 #include "cmDuration.h"
-#include "cmFunctionBlocker.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -44,32 +49,8 @@
 #  include <unistd.h>
 #endif
 
-class cmExecutionStatus;
-struct cmListFileFunction;
-
 #define CTEST_INITIAL_CMAKE_OUTPUT_FILE_NAME "CTestInitialCMakeOutput.log"
 
-// used to keep elapsed time up to date
-class cmCTestScriptFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
-                         cmExecutionStatus& /*status*/) override;
-  // virtual bool ShouldRemove(const cmListFileFunction& lff, cmMakefile &mf);
-  // virtual void ScopeEnded(cmMakefile &mf);
-
-  cmCTestScriptHandler* CTestScriptHandler;
-};
-
-// simply update the time and don't block anything
-bool cmCTestScriptFunctionBlocker::IsFunctionBlocked(
-  const cmListFileFunction& /*lff*/, cmMakefile& /*mf*/,
-  cmExecutionStatus& /*status*/)
-{
-  this->CTestScriptHandler->UpdateElapsedTime();
-  return false;
-}
-
 cmCTestScriptHandler::cmCTestScriptHandler()
 {
   this->Backup = false;
@@ -163,16 +144,16 @@
     auto itime = cmDurationTo<unsigned int>(std::chrono::steady_clock::now() -
                                             this->ScriptStartTime);
     auto timeString = std::to_string(itime);
-    this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString.c_str());
+    this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString);
   }
 }
 
-void cmCTestScriptHandler::AddCTestCommand(std::string const& name,
-                                           cmCTestCommand* command)
+void cmCTestScriptHandler::AddCTestCommand(
+  std::string const& name, std::unique_ptr<cmCTestCommand> command)
 {
   command->CTest = this->CTest;
   command->CTestScriptHandler = this;
-  this->CMake->GetState()->AddBuiltinCommand(name, command);
+  this->CMake->GetState()->AddBuiltinCommand(name, std::move(command));
 }
 
 int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
@@ -295,21 +276,28 @@
       }
     });
 
-  this->AddCTestCommand("ctest_build", new cmCTestBuildCommand);
-  this->AddCTestCommand("ctest_configure", new cmCTestConfigureCommand);
-  this->AddCTestCommand("ctest_coverage", new cmCTestCoverageCommand);
+  this->AddCTestCommand("ctest_build", cm::make_unique<cmCTestBuildCommand>());
+  this->AddCTestCommand("ctest_configure",
+                        cm::make_unique<cmCTestConfigureCommand>());
+  this->AddCTestCommand("ctest_coverage",
+                        cm::make_unique<cmCTestCoverageCommand>());
   this->AddCTestCommand("ctest_empty_binary_directory",
-                        new cmCTestEmptyBinaryDirectoryCommand);
-  this->AddCTestCommand("ctest_memcheck", new cmCTestMemCheckCommand);
+                        cm::make_unique<cmCTestEmptyBinaryDirectoryCommand>());
+  this->AddCTestCommand("ctest_memcheck",
+                        cm::make_unique<cmCTestMemCheckCommand>());
   this->AddCTestCommand("ctest_read_custom_files",
-                        new cmCTestReadCustomFilesCommand);
-  this->AddCTestCommand("ctest_run_script", new cmCTestRunScriptCommand);
-  this->AddCTestCommand("ctest_sleep", new cmCTestSleepCommand);
-  this->AddCTestCommand("ctest_start", new cmCTestStartCommand);
-  this->AddCTestCommand("ctest_submit", new cmCTestSubmitCommand);
-  this->AddCTestCommand("ctest_test", new cmCTestTestCommand);
-  this->AddCTestCommand("ctest_update", new cmCTestUpdateCommand);
-  this->AddCTestCommand("ctest_upload", new cmCTestUploadCommand);
+                        cm::make_unique<cmCTestReadCustomFilesCommand>());
+  this->AddCTestCommand("ctest_run_script",
+                        cm::make_unique<cmCTestRunScriptCommand>());
+  this->AddCTestCommand("ctest_sleep", cm::make_unique<cmCTestSleepCommand>());
+  this->AddCTestCommand("ctest_start", cm::make_unique<cmCTestStartCommand>());
+  this->AddCTestCommand("ctest_submit",
+                        cm::make_unique<cmCTestSubmitCommand>());
+  this->AddCTestCommand("ctest_test", cm::make_unique<cmCTestTestCommand>());
+  this->AddCTestCommand("ctest_update",
+                        cm::make_unique<cmCTestUpdateCommand>());
+  this->AddCTestCommand("ctest_upload",
+                        cm::make_unique<cmCTestUploadCommand>());
 }
 
 // this sets up some variables for the script to use, creates the required
@@ -340,31 +328,29 @@
   this->CreateCMake();
 
   // set a variable with the path to the current script
-  this->Makefile->AddDefinition(
-    "CTEST_SCRIPT_DIRECTORY", cmSystemTools::GetFilenamePath(script).c_str());
-  this->Makefile->AddDefinition(
-    "CTEST_SCRIPT_NAME", cmSystemTools::GetFilenameName(script).c_str());
+  this->Makefile->AddDefinition("CTEST_SCRIPT_DIRECTORY",
+                                cmSystemTools::GetFilenamePath(script));
+  this->Makefile->AddDefinition("CTEST_SCRIPT_NAME",
+                                cmSystemTools::GetFilenameName(script));
   this->Makefile->AddDefinition("CTEST_EXECUTABLE_NAME",
-                                cmSystemTools::GetCTestCommand().c_str());
+                                cmSystemTools::GetCTestCommand());
   this->Makefile->AddDefinition("CMAKE_EXECUTABLE_NAME",
-                                cmSystemTools::GetCMakeCommand().c_str());
-  this->Makefile->AddDefinition("CTEST_RUN_CURRENT_SCRIPT", true);
+                                cmSystemTools::GetCMakeCommand());
+  this->Makefile->AddDefinitionBool("CTEST_RUN_CURRENT_SCRIPT", true);
   this->SetRunCurrentScript(true);
   this->UpdateElapsedTime();
 
   // add the script arg if defined
   if (!script_arg.empty()) {
-    this->Makefile->AddDefinition("CTEST_SCRIPT_ARG", script_arg.c_str());
+    this->Makefile->AddDefinition("CTEST_SCRIPT_ARG", script_arg);
   }
 
 #if defined(__CYGWIN__)
   this->Makefile->AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0");
 #endif
 
-  // always add a function blocker to update the elapsed time
-  cmCTestScriptFunctionBlocker* f = new cmCTestScriptFunctionBlocker();
-  f->CTestScriptHandler = this;
-  this->Makefile->AddFunctionBlocker(f);
+  // set a callback function to update the elapsed time
+  this->Makefile->OnExecuteCommand([this] { this->UpdateElapsedTime(); });
 
   /* Execute CTestScriptMode.cmake, which loads CMakeDetermineSystem and
   CMakeSystemSpecificInformation, so
@@ -384,7 +370,7 @@
   const std::map<std::string, std::string>& defs =
     this->CTest->GetDefinitions();
   for (auto const& d : defs) {
-    this->Makefile->AddDefinition(d.first, d.second.c_str());
+    this->Makefile->AddDefinition(d.first, d.second);
   }
 
   // finally read in the script
@@ -465,12 +451,13 @@
   // make sure the required info is here
   if (this->SourceDir.empty() || this->BinaryDir.empty() ||
       this->CTestCmd.empty()) {
-    std::string msg = "CTEST_SOURCE_DIRECTORY = ";
-    msg += (!this->SourceDir.empty()) ? this->SourceDir.c_str() : "(Null)";
-    msg += "\nCTEST_BINARY_DIRECTORY = ";
-    msg += (!this->BinaryDir.empty()) ? this->BinaryDir.c_str() : "(Null)";
-    msg += "\nCTEST_COMMAND = ";
-    msg += (!this->CTestCmd.empty()) ? this->CTestCmd.c_str() : "(Null)";
+    std::string msg =
+      cmStrCat("CTEST_SOURCE_DIRECTORY = ",
+               (!this->SourceDir.empty()) ? this->SourceDir.c_str() : "(Null)",
+               "\nCTEST_BINARY_DIRECTORY = ",
+               (!this->BinaryDir.empty()) ? this->BinaryDir.c_str() : "(Null)",
+               "\nCTEST_COMMAND = ",
+               (!this->CTestCmd.empty()) ? this->CTestCmd.c_str() : "(Null)");
     cmSystemTools::Error(
       "Some required settings in the configuration file were missing:\n" +
       msg);
@@ -509,7 +496,7 @@
 int cmCTestScriptHandler::RunConfigurationScript(
   const std::string& total_script_arg, bool pscope)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmSystemTools::SaveRestoreEnvironment sre;
 #endif
 
@@ -557,8 +544,7 @@
 
   // set any environment variables
   if (!this->CTestEnv.empty()) {
-    std::vector<std::string> envArgs;
-    cmSystemTools::ExpandListArgument(this->CTestEnv, envArgs);
+    std::vector<std::string> envArgs = cmExpandedList(this->CTestEnv);
     cmSystemTools::AppendEnv(envArgs);
   }
 
@@ -624,10 +610,8 @@
   int retVal;
 
   // compute the backup names
-  this->BackupSourceDir = this->SourceDir;
-  this->BackupSourceDir += "_CMakeBackup";
-  this->BackupBinaryDir = this->BinaryDir;
-  this->BackupBinaryDir += "_CMakeBackup";
+  this->BackupSourceDir = cmStrCat(this->SourceDir, "_CMakeBackup");
+  this->BackupBinaryDir = cmStrCat(this->BinaryDir, "_CMakeBackup");
 
   // backup the binary and src directories if requested
   if (this->Backup) {
@@ -664,12 +648,9 @@
   // do an initial cvs update as required
   command = this->UpdateCmd;
   for (std::string const& eu : this->ExtraUpdates) {
-    std::vector<std::string> cvsArgs;
-    cmSystemTools::ExpandListArgument(eu, cvsArgs);
+    std::vector<std::string> cvsArgs = cmExpandedList(eu);
     if (cvsArgs.size() == 2) {
-      std::string fullCommand = command;
-      fullCommand += " update ";
-      fullCommand += cvsArgs[1];
+      std::string fullCommand = cmStrCat(command, " update ", cvsArgs[1]);
       output.clear();
       retVal = 0;
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
@@ -678,8 +659,8 @@
         fullCommand, &output, &output, &retVal, cvsArgs[0].c_str(),
         this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/);
       if (!res || retVal != 0) {
-        cmSystemTools::Error("Unable to perform extra updates:\n" + eu +
-                             "\nWith output:\n" + output);
+        cmSystemTools::Error(cmStrCat("Unable to perform extra updates:\n", eu,
+                                      "\nWith output:\n", output));
         return 0;
       }
     }
@@ -770,9 +751,7 @@
   int cmakeFailed = 0;
   std::string cmakeFailedOuput;
   if (!this->CMakeCmd.empty()) {
-    command = this->CMakeCmd;
-    command += " \"";
-    command += this->SourceDir;
+    command = cmStrCat(this->CMakeCmd, " \"", this->SourceDir);
     output.clear();
     command += "\"";
     retVal = 0;
@@ -808,8 +787,7 @@
   }
 
   // run ctest, it may be more than one command in here
-  std::vector<std::string> ctestCommands;
-  cmSystemTools::ExpandListArgument(this->CTestCmd, ctestCommands);
+  std::vector<std::string> ctestCommands = cmExpandedList(this->CTestCmd);
   // for each variable/argument do a putenv
   for (std::string const& ctestCommand : ctestCommands) {
     command = ctestCommand;
@@ -853,8 +831,7 @@
 bool cmCTestScriptHandler::WriteInitialCache(const char* directory,
                                              const char* text)
 {
-  std::string cacheFile = directory;
-  cacheFile += "/CMakeCache.txt";
+  std::string cacheFile = cmStrCat(directory, "/CMakeCache.txt");
   cmGeneratedFileStream fout(cacheFile);
   if (!fout) {
     return false;
@@ -919,8 +896,7 @@
   }
 
   // try to avoid deleting directories that we shouldn't
-  std::string check = sname;
-  check += "/CMakeCache.txt";
+  std::string check = cmStrCat(sname, "/CMakeCache.txt");
 
   if (!cmSystemTools::FileExists(check)) {
     return false;
@@ -949,7 +925,7 @@
       continue;
     }
 
-    std::string fullPath = directoryPath + std::string("/") + path;
+    std::string fullPath = cmStrCat(directoryPath, "/", path);
 
     bool isDirectory = cmSystemTools::FileIsDirectory(fullPath) &&
       !cmSystemTools::FileIsSymlink(fullPath);
diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h
index d93b5f8..d003199 100644
--- a/Source/CTest/cmCTestScriptHandler.h
+++ b/Source/CTest/cmCTestScriptHandler.h
@@ -5,13 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-#include "cmDuration.h"
-
 #include <chrono>
+#include <memory>
 #include <string>
 #include <vector>
 
+#include "cmCTestGenericHandler.h"
+#include "cmDuration.h"
+
 class cmCTest;
 class cmCTestCommand;
 class cmGlobalGenerator;
@@ -57,7 +58,7 @@
 class cmCTestScriptHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /**
    * Add a script to run, and if is should run in the current process
@@ -131,7 +132,8 @@
   int RunConfigurationDashboard();
 
   // Add ctest command
-  void AddCTestCommand(std::string const& name, cmCTestCommand* command);
+  void AddCTestCommand(std::string const& name,
+                       std::unique_ptr<cmCTestCommand> command);
 
   // Try to remove the binary directory once
   static bool TryToRemoveBinaryDirectoryOnce(const std::string& directoryPath);
diff --git a/Source/CTest/cmCTestSleepCommand.cxx b/Source/CTest/cmCTestSleepCommand.cxx
index 2752cd3..623d3b6 100644
--- a/Source/CTest/cmCTestSleepCommand.cxx
+++ b/Source/CTest/cmCTestSleepCommand.cxx
@@ -2,9 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestSleepCommand.h"
 
-#include "cmCTestScriptHandler.h"
+#include <cstdlib>
 
-#include <stdlib.h>
+#include "cmCTestScriptHandler.h"
 
 class cmExecutionStatus;
 
diff --git a/Source/CTest/cmCTestSleepCommand.h b/Source/CTest/cmCTestSleepCommand.h
index 5cd185a..1c3b8a1 100644
--- a/Source/CTest/cmCTestSleepCommand.h
+++ b/Source/CTest/cmCTestSleepCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
 #include <string>
+#include <utility>
 #include <vector>
 
-class cmCommand;
+#include <cm/memory>
+
+#include "cmCTestCommand.h"
+#include "cmCommand.h"
+
 class cmExecutionStatus;
 
 /** \class cmCTestSleep
@@ -27,12 +30,12 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestSleepCommand* ni = new cmCTestSleepCommand;
+    auto ni = cm::make_unique<cmCTestSleepCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestStartCommand.cxx b/Source/CTest/cmCTestStartCommand.cxx
index 67ff2db..fe68406 100644
--- a/Source/CTest/cmCTestStartCommand.cxx
+++ b/Source/CTest/cmCTestStartCommand.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestStartCommand.h"
 
+#include <cstddef>
+#include <sstream>
+
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
-#include <sstream>
-#include <stddef.h>
-
 class cmExecutionStatus;
 
 cmCTestStartCommand::cmCTestStartCommand()
@@ -33,14 +33,16 @@
   const char* bld_dir = nullptr;
 
   while (cnt < args.size()) {
-    if (args[cnt] == "TRACK") {
+    if (args[cnt] == "GROUP" || args[cnt] == "TRACK") {
       cnt++;
       if (cnt >= args.size() || args[cnt] == "APPEND" ||
           args[cnt] == "QUIET") {
-        this->SetError("TRACK argument missing track name");
+        std::ostringstream e;
+        e << args[cnt - 1] << " argument missing group name";
+        this->SetError(e.str());
         return false;
       }
-      this->CTest->SetSpecificTrack(args[cnt].c_str());
+      this->CTest->SetSpecificGroup(args[cnt].c_str());
       cnt++;
     } else if (args[cnt] == "APPEND") {
       cnt++;
@@ -113,10 +115,10 @@
                          << "   Build directory: " << bld_dir << std::endl,
                        this->Quiet);
   }
-  const char* track = this->CTest->GetSpecificTrack();
-  if (track) {
+  const char* group = this->CTest->GetSpecificGroup();
+  if (group) {
     cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
-                       "   Track: " << track << std::endl, this->Quiet);
+                       "   Group: " << group << std::endl, this->Quiet);
   }
 
   // Log startup actions.
diff --git a/Source/CTest/cmCTestStartCommand.h b/Source/CTest/cmCTestStartCommand.h
index 542f27c..b30b1bb 100644
--- a/Source/CTest/cmCTestStartCommand.h
+++ b/Source/CTest/cmCTestStartCommand.h
@@ -5,13 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestCommand.h"
-
 #include <iosfwd>
 #include <string>
+#include <utility>
 #include <vector>
 
-class cmCommand;
+#include <cm/memory>
+
+#include "cmCTestCommand.h"
+#include "cmCommand.h"
+
 class cmExecutionStatus;
 
 /** \class cmCTestStart
@@ -27,14 +30,14 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestStartCommand* ni = new cmCTestStartCommand;
+    auto ni = cm::make_unique<cmCTestStartCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
     ni->CreateNewTag = this->CreateNewTag;
     ni->Quiet = this->Quiet;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index afc3e67..46b00b1 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -2,37 +2,35 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestSubmitCommand.h"
 
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cm_static_string_view.hxx"
+
+#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestSubmitHandler.h"
+#include "cmCommand.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <sstream>
-
 class cmExecutionStatus;
 
-cmCTestSubmitCommand::cmCTestSubmitCommand()
-{
-  this->PartsMentioned = false;
-  this->FilesMentioned = false;
-  this->InternalTest = false;
-  this->RetryCount = "";
-  this->RetryDelay = "";
-  this->CDashUpload = false;
-  this->Arguments[cts_BUILD_ID] = "BUILD_ID";
-  this->Last = cts_LAST;
-}
-
 /**
  * This is a virtual constructor for the command.
  */
-cmCommand* cmCTestSubmitCommand::Clone()
+std::unique_ptr<cmCommand> cmCTestSubmitCommand::Clone()
 {
-  cmCTestSubmitCommand* ni = new cmCTestSubmitCommand;
+  auto ni = cm::make_unique<cmCTestSubmitCommand>();
   ni->CTest = this->CTest;
   ni->CTestScriptHandler = this->CTestScriptHandler;
-  return ni;
+  return std::unique_ptr<cmCommand>(std::move(ni));
 }
 
 cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
@@ -63,16 +61,14 @@
   const char* notesFilesVariable =
     this->Makefile->GetDefinition("CTEST_NOTES_FILES");
   if (notesFilesVariable) {
-    std::vector<std::string> notesFiles;
-    cmSystemTools::ExpandListArgument(notesFilesVariable, notesFiles);
+    std::vector<std::string> notesFiles = cmExpandedList(notesFilesVariable);
     this->CTest->GenerateNotesFile(notesFiles);
   }
 
   const char* extraFilesVariable =
     this->Makefile->GetDefinition("CTEST_EXTRA_SUBMIT_FILES");
   if (extraFilesVariable) {
-    std::vector<std::string> extraFiles;
-    cmSystemTools::ExpandListArgument(extraFilesVariable, extraFiles);
+    std::vector<std::string> extraFiles = cmExpandedList(extraFilesVariable);
     if (!this->CTest->SubmitExtraFiles(extraFiles)) {
       this->SetError("problem submitting extra files.");
       return nullptr;
@@ -103,13 +99,18 @@
     // without any of the default parts.
     //
     handler->SelectParts(std::set<cmCTest::Part>());
-    handler->SelectFiles(this->Files);
+    handler->SelectFiles(
+      std::set<std::string>(this->Files.begin(), this->Files.end()));
   }
 
   // If a PARTS option was given, select only the named parts for submission.
   //
   if (this->PartsMentioned) {
-    handler->SelectParts(this->Parts);
+    auto parts =
+      cmMakeRange(this->Parts).transform([this](std::string const& arg) {
+        return this->CTest->GetPartFromName(arg.c_str());
+      });
+    handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end()));
   }
 
   // Pass along any HTTPHEADER to the handler if this option was given.
@@ -137,133 +138,61 @@
 
   bool ret = this->cmCTestHandlerCommand::InitialPass(args, status);
 
-  if (this->Values[cts_BUILD_ID] && *this->Values[cts_BUILD_ID]) {
-    this->Makefile->AddDefinition(this->Values[cts_BUILD_ID],
-                                  this->CTest->GetBuildID().c_str());
+  if (!this->BuildID.empty()) {
+    this->Makefile->AddDefinition(this->BuildID, this->CTest->GetBuildID());
   }
 
   return ret;
 }
 
-bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg)
+void cmCTestSubmitCommand::BindArguments()
 {
   if (this->CDashUpload) {
     // Arguments specific to the CDASH_UPLOAD signature.
-    if (arg == "CDASH_UPLOAD") {
-      this->ArgumentDoing = ArgumentDoingCDashUpload;
-      return true;
-    }
-
-    if (arg == "CDASH_UPLOAD_TYPE") {
-      this->ArgumentDoing = ArgumentDoingCDashUploadType;
-      return true;
-    }
+    this->Bind("CDASH_UPLOAD", this->CDashUploadFile);
+    this->Bind("CDASH_UPLOAD_TYPE", this->CDashUploadType);
   } else {
     // Arguments that cannot be used with CDASH_UPLOAD.
-    if (arg == "PARTS") {
-      this->ArgumentDoing = ArgumentDoingParts;
-      this->PartsMentioned = true;
-      return true;
-    }
-
-    if (arg == "FILES") {
-      this->ArgumentDoing = ArgumentDoingFiles;
-      this->FilesMentioned = true;
-      return true;
-    }
+    this->Bind("PARTS"_s, this->Parts);
+    this->Bind("FILES"_s, this->Files);
   }
   // Arguments used by both modes.
-  if (arg == "HTTPHEADER") {
-    this->ArgumentDoing = ArgumentDoingHttpHeader;
-    return true;
-  }
-
-  if (arg == "RETRY_COUNT") {
-    this->ArgumentDoing = ArgumentDoingRetryCount;
-    return true;
-  }
-
-  if (arg == "RETRY_DELAY") {
-    this->ArgumentDoing = ArgumentDoingRetryDelay;
-    return true;
-  }
-
-  if (arg == "SUBMIT_URL") {
-    this->ArgumentDoing = ArgumentDoingSubmitURL;
-    return true;
-  }
-
-  if (arg == "INTERNAL_TEST_CHECKSUM") {
-    this->InternalTest = true;
-    return true;
-  }
+  this->Bind("BUILD_ID"_s, this->BuildID);
+  this->Bind("HTTPHEADER"_s, this->HttpHeaders);
+  this->Bind("RETRY_COUNT"_s, this->RetryCount);
+  this->Bind("RETRY_DELAY"_s, this->RetryDelay);
+  this->Bind("SUBMIT_URL"_s, this->SubmitURL);
+  this->Bind("INTERNAL_TEST_CHECKSUM", this->InternalTest);
 
   // Look for other arguments.
-  return this->Superclass::CheckArgumentKeyword(arg);
+  this->cmCTestHandlerCommand::BindArguments();
 }
 
-bool cmCTestSubmitCommand::CheckArgumentValue(std::string const& arg)
+void cmCTestSubmitCommand::CheckArguments(
+  std::vector<std::string> const& keywords)
 {
-  // Handle states specific to this command.
-  if (this->ArgumentDoing == ArgumentDoingParts) {
+  this->PartsMentioned = !this->Parts.empty() || cmContains(keywords, "PARTS");
+  this->FilesMentioned = !this->Files.empty() || cmContains(keywords, "FILES");
+
+  cmEraseIf(this->Parts, [this](std::string const& arg) -> bool {
     cmCTest::Part p = this->CTest->GetPartFromName(arg.c_str());
-    if (p != cmCTest::PartCount) {
-      this->Parts.insert(p);
-    } else {
+    if (p == cmCTest::PartCount) {
       std::ostringstream e;
       e << "Part name \"" << arg << "\" is invalid.";
       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      this->ArgumentDoing = ArgumentDoingError;
+      return true;
     }
-    return true;
-  }
+    return false;
+  });
 
-  if (this->ArgumentDoing == ArgumentDoingFiles) {
-    if (cmSystemTools::FileExists(arg)) {
-      this->Files.insert(arg);
-    } else {
+  cmEraseIf(this->Files, [this](std::string const& arg) -> bool {
+    if (!cmSystemTools::FileExists(arg)) {
       std::ostringstream e;
       e << "File \"" << arg << "\" does not exist. Cannot submit "
         << "a non-existent file.";
       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      this->ArgumentDoing = ArgumentDoingError;
+      return true;
     }
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingHttpHeader) {
-    this->HttpHeaders.push_back(arg);
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingRetryCount) {
-    this->RetryCount = arg;
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingRetryDelay) {
-    this->RetryDelay = arg;
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingCDashUpload) {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->CDashUploadFile = arg;
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingCDashUploadType) {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->CDashUploadType = arg;
-    return true;
-  }
-
-  if (this->ArgumentDoing == ArgumentDoingSubmitURL) {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->SubmitURL = arg;
-    return true;
-  }
-
-  // Look for other arguments.
-  return this->Superclass::CheckArgumentValue(arg);
+    return false;
+  });
 }
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 1e27046..9060771 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -5,15 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTest.h"
-#include "cmCTestHandlerCommand.h"
-
-#include <set>
+#include <memory>
 #include <string>
 #include <vector>
 
-class cmCTestGenericHandler;
+#include "cmCTestHandlerCommand.h"
+
 class cmCommand;
+class cmCTestGenericHandler;
 class cmExecutionStatus;
 
 /** \class cmCTestSubmit
@@ -25,8 +24,7 @@
 class cmCTestSubmitCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestSubmitCommand();
-  cmCommand* Clone() override;
+  std::unique_ptr<cmCommand> Clone() override;
 
   bool InitialPass(std::vector<std::string> const& args,
                    cmExecutionStatus& status) override;
@@ -36,45 +34,26 @@
    */
   std::string GetName() const override { return "ctest_submit"; }
 
-  typedef cmCTestHandlerCommand Superclass;
-
 protected:
+  void BindArguments() override;
+  void CheckArguments(std::vector<std::string> const& keywords) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  bool CheckArgumentKeyword(std::string const& arg) override;
-  bool CheckArgumentValue(std::string const& arg) override;
+  bool CDashUpload = false;
+  bool FilesMentioned = false;
+  bool InternalTest = false;
+  bool PartsMentioned = false;
 
-  enum
-  {
-    ArgumentDoingParts = Superclass::ArgumentDoingLast1,
-    ArgumentDoingFiles,
-    ArgumentDoingRetryDelay,
-    ArgumentDoingRetryCount,
-    ArgumentDoingCDashUpload,
-    ArgumentDoingCDashUploadType,
-    ArgumentDoingHttpHeader,
-    ArgumentDoingSubmitURL,
-    ArgumentDoingLast2
-  };
-
-  enum
-  {
-    cts_BUILD_ID = ct_LAST,
-    cts_LAST
-  };
-
-  bool PartsMentioned;
-  std::set<cmCTest::Part> Parts;
-  bool FilesMentioned;
-  bool InternalTest;
-  std::set<std::string> Files;
-  std::string RetryCount;
-  std::string RetryDelay;
-  bool CDashUpload;
+  std::string BuildID;
   std::string CDashUploadFile;
   std::string CDashUploadType;
-  std::vector<std::string> HttpHeaders;
+  std::string RetryCount;
+  std::string RetryDelay;
   std::string SubmitURL;
+
+  std::vector<std::string> Files;
+  std::vector<std::string> HttpHeaders;
+  std::vector<std::string> Parts;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 54c4bae..2ac5af6 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestSubmitHandler.h"
 
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
+
 #include "cm_curl.h"
 #include "cm_jsoncpp_reader.h"
 #include "cm_jsoncpp_value.h"
-#include <chrono>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
 
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
@@ -19,13 +20,14 @@
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 #include "cmake.h"
 
 #define SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT 120
 
-typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
+using cmCTestSubmitHandlerVectorOfChar = std::vector<char>;
 
 class cmCTestSubmitHandler::ResponseParser : public cmXMLParser
 {
@@ -154,8 +156,7 @@
   /* In windows, this will init the winsock stuff */
   ::curl_global_init(CURL_GLOBAL_ALL);
   std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
-  std::vector<std::string> args;
-  cmSystemTools::ExpandListArgument(curlopt, args);
+  std::vector<std::string> args = cmExpandedList(curlopt);
   bool verifyPeerOff = false;
   bool verifyHostOff = false;
   for (std::string const& arg : args) {
@@ -224,7 +225,7 @@
       std::string local_file = file;
       bool initialize_cdash_buildid = false;
       if (!cmSystemTools::FileExists(local_file)) {
-        local_file = localprefix + "/" + file;
+        local_file = cmStrCat(localprefix, "/", file);
         // If this file exists within the local Testing directory we assume
         // that it will be associated with the current build in CDash.
         initialize_cdash_buildid = true;
@@ -236,9 +237,9 @@
                      << remote_file << std::endl;
 
       std::string ofile = cmSystemTools::EncodeURL(remote_file);
-      std::string upload_as = url +
-        ((url.find('?') == std::string::npos) ? '?' : '&') +
-        "FileName=" + ofile;
+      std::string upload_as =
+        cmStrCat(url, ((url.find('?') == std::string::npos) ? '?' : '&'),
+                 "FileName=", ofile);
 
       if (initialize_cdash_buildid) {
         // Provide extra arguments to CDash so that it can initialize and
@@ -279,7 +280,7 @@
 
       upload_as += "&MD5=";
 
-      if (cmSystemTools::IsOn(this->GetOption("InternalTest"))) {
+      if (cmIsOn(this->GetOption("InternalTest"))) {
         upload_as += "bad_md5sum";
       } else {
         upload_as +=
@@ -498,8 +499,7 @@
   cmCTestCurl curl(this->CTest);
   curl.SetQuiet(this->Quiet);
   std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
-  std::vector<std::string> args;
-  cmSystemTools::ExpandListArgument(curlopt, args);
+  std::vector<std::string> args = cmExpandedList(curlopt);
   curl.SetCurlOptions(args);
   curl.SetTimeOutSeconds(SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
   curl.SetHttpHeaders(this->HttpHeaders);
@@ -516,7 +516,7 @@
                "Only http and https are supported for CDASH_UPLOAD\n");
     return -1;
   }
-  bool internalTest = cmSystemTools::IsOn(this->GetOption("InternalTest"));
+  bool internalTest = cmIsOn(this->GetOption("InternalTest"));
 
   // Get RETRY_COUNT and RETRY_DELAY values if they were set.
   std::string retryDelayString = this->GetOption("RetryDelay") == nullptr
@@ -528,8 +528,7 @@
   auto retryDelay = std::chrono::seconds(0);
   if (!retryDelayString.empty()) {
     unsigned long retryDelayValue = 0;
-    if (!cmSystemTools::StringToULong(retryDelayString.c_str(),
-                                      &retryDelayValue)) {
+    if (!cmStrToULong(retryDelayString, &retryDelayValue)) {
       cmCTestLog(this->CTest, WARNING,
                  "Invalid value for 'RETRY_DELAY' : " << retryDelayString
                                                       << std::endl);
@@ -539,7 +538,7 @@
   }
   unsigned long retryCount = 0;
   if (!retryCountString.empty()) {
-    if (!cmSystemTools::StringToULong(retryCountString.c_str(), &retryCount)) {
+    if (!cmStrToULong(retryCountString, &retryCount)) {
       cmCTestLog(this->CTest, WARNING,
                  "Invalid value for 'RETRY_DELAY' : " << retryCountString
                                                       << std::endl);
@@ -569,6 +568,11 @@
       << curl.Escape(this->CTest->GetCTestConfiguration("BuildName")) << "&"
       << "site=" << curl.Escape(this->CTest->GetCTestConfiguration("Site"))
       << "&"
+      << "group=" << curl.Escape(this->CTest->GetTestModelString())
+      << "&"
+      // For now, we send both "track" and "group" to CDash in case we're
+      // submitting to an older instance that still expects the prior
+      // terminology.
       << "track=" << curl.Escape(this->CTest->GetTestModelString()) << "&"
       << "starttime=" << timeNow << "&"
       << "endtime=" << timeNow << "&"
@@ -837,10 +841,10 @@
   }
   cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Submit files\n",
                      this->Quiet);
-  const char* specificTrack = this->CTest->GetSpecificTrack();
-  if (specificTrack) {
+  const char* specificGroup = this->CTest->GetSpecificGroup();
+  if (specificGroup) {
     cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
-                       "   Send to track: " << specificTrack << std::endl,
+                       "   Send to group: " << specificGroup << std::endl,
                        this->Quiet);
   }
   this->SetLogFile(&ofs);
diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h
index e0fed10..304daaa 100644
--- a/Source/CTest/cmCTestSubmitHandler.h
+++ b/Source/CTest/cmCTestSubmitHandler.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTest.h"
-#include "cmCTestGenericHandler.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmCTest.h"
+#include "cmCTestGenericHandler.h"
+
 /** \class cmCTestSubmitHandler
  * \brief Helper class for CTest
  *
@@ -22,7 +22,7 @@
 class cmCTestSubmitHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   cmCTestSubmitHandler();
   ~cmCTestSubmitHandler() override { this->LogFile = nullptr; }
@@ -59,7 +59,7 @@
                        const std::string& remoteprefix,
                        const std::string& url);
 
-  typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
+  using cmCTestSubmitHandlerVectorOfChar = std::vector<char>;
 
   void ParseResponse(cmCTestSubmitHandlerVectorOfChar chunk);
 
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index cfd5e3d..5496353 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -2,36 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestTestCommand.h"
 
+#include <chrono>
+#include <cstdlib>
+#include <sstream>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmCTest.h"
 #include "cmCTestGenericHandler.h"
 #include "cmCTestTestHandler.h"
 #include "cmDuration.h"
 #include "cmMakefile.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 
-#include <chrono>
-#include <sstream>
-#include <stdlib.h>
-#include <vector>
-
-cmCTestTestCommand::cmCTestTestCommand()
+void cmCTestTestCommand::BindArguments()
 {
-  this->Arguments[ctt_START] = "START";
-  this->Arguments[ctt_END] = "END";
-  this->Arguments[ctt_STRIDE] = "STRIDE";
-  this->Arguments[ctt_EXCLUDE] = "EXCLUDE";
-  this->Arguments[ctt_INCLUDE] = "INCLUDE";
-  this->Arguments[ctt_EXCLUDE_LABEL] = "EXCLUDE_LABEL";
-  this->Arguments[ctt_INCLUDE_LABEL] = "INCLUDE_LABEL";
-  this->Arguments[ctt_EXCLUDE_FIXTURE] = "EXCLUDE_FIXTURE";
-  this->Arguments[ctt_EXCLUDE_FIXTURE_SETUP] = "EXCLUDE_FIXTURE_SETUP";
-  this->Arguments[ctt_EXCLUDE_FIXTURE_CLEANUP] = "EXCLUDE_FIXTURE_CLEANUP";
-  this->Arguments[ctt_PARALLEL_LEVEL] = "PARALLEL_LEVEL";
-  this->Arguments[ctt_SCHEDULE_RANDOM] = "SCHEDULE_RANDOM";
-  this->Arguments[ctt_STOP_TIME] = "STOP_TIME";
-  this->Arguments[ctt_TEST_LOAD] = "TEST_LOAD";
-  this->Arguments[ctt_LAST] = nullptr;
-  this->Last = ctt_LAST;
+  this->cmCTestHandlerCommand::BindArguments();
+  this->Bind("START"_s, this->Start);
+  this->Bind("END"_s, this->End);
+  this->Bind("STRIDE"_s, this->Stride);
+  this->Bind("EXCLUDE"_s, this->Exclude);
+  this->Bind("INCLUDE"_s, this->Include);
+  this->Bind("EXCLUDE_LABEL"_s, this->ExcludeLabel);
+  this->Bind("INCLUDE_LABEL"_s, this->IncludeLabel);
+  this->Bind("EXCLUDE_FIXTURE"_s, this->ExcludeFixture);
+  this->Bind("EXCLUDE_FIXTURE_SETUP"_s, this->ExcludeFixtureSetup);
+  this->Bind("EXCLUDE_FIXTURE_CLEANUP"_s, this->ExcludeFixtureCleanup);
+  this->Bind("PARALLEL_LEVEL"_s, this->ParallelLevel);
+  this->Bind("SCHEDULE_RANDOM"_s, this->ScheduleRandom);
+  this->Bind("STOP_TIME"_s, this->StopTime);
+  this->Bind("TEST_LOAD"_s, this->TestLoad);
+  this->Bind("HARDWARE_SPEC_FILE"_s, this->HardwareSpecFile);
 }
 
 cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
@@ -51,57 +52,47 @@
   }
   this->CTest->SetTimeOut(timeout);
   cmCTestGenericHandler* handler = this->InitializeActualHandler();
-  if (this->Values[ctt_START] || this->Values[ctt_END] ||
-      this->Values[ctt_STRIDE]) {
-    std::ostringstream testsToRunString;
-    if (this->Values[ctt_START]) {
-      testsToRunString << this->Values[ctt_START];
-    }
-    testsToRunString << ",";
-    if (this->Values[ctt_END]) {
-      testsToRunString << this->Values[ctt_END];
-    }
-    testsToRunString << ",";
-    if (this->Values[ctt_STRIDE]) {
-      testsToRunString << this->Values[ctt_STRIDE];
-    }
-    handler->SetOption("TestsToRunInformation",
-                       testsToRunString.str().c_str());
+  if (!this->Start.empty() || !this->End.empty() || !this->Stride.empty()) {
+    handler->SetOption(
+      "TestsToRunInformation",
+      cmStrCat(this->Start, ',', this->End, ',', this->Stride).c_str());
   }
-  if (this->Values[ctt_EXCLUDE]) {
-    handler->SetOption("ExcludeRegularExpression", this->Values[ctt_EXCLUDE]);
+  if (!this->Exclude.empty()) {
+    handler->SetOption("ExcludeRegularExpression", this->Exclude.c_str());
   }
-  if (this->Values[ctt_INCLUDE]) {
-    handler->SetOption("IncludeRegularExpression", this->Values[ctt_INCLUDE]);
+  if (!this->Include.empty()) {
+    handler->SetOption("IncludeRegularExpression", this->Include.c_str());
   }
-  if (this->Values[ctt_EXCLUDE_LABEL]) {
+  if (!this->ExcludeLabel.empty()) {
     handler->SetOption("ExcludeLabelRegularExpression",
-                       this->Values[ctt_EXCLUDE_LABEL]);
+                       this->ExcludeLabel.c_str());
   }
-  if (this->Values[ctt_INCLUDE_LABEL]) {
-    handler->SetOption("LabelRegularExpression",
-                       this->Values[ctt_INCLUDE_LABEL]);
+  if (!this->IncludeLabel.empty()) {
+    handler->SetOption("LabelRegularExpression", this->IncludeLabel.c_str());
   }
-  if (this->Values[ctt_EXCLUDE_FIXTURE]) {
+  if (!this->ExcludeFixture.empty()) {
     handler->SetOption("ExcludeFixtureRegularExpression",
-                       this->Values[ctt_EXCLUDE_FIXTURE]);
+                       this->ExcludeFixture.c_str());
   }
-  if (this->Values[ctt_EXCLUDE_FIXTURE_SETUP]) {
+  if (!this->ExcludeFixtureSetup.empty()) {
     handler->SetOption("ExcludeFixtureSetupRegularExpression",
-                       this->Values[ctt_EXCLUDE_FIXTURE_SETUP]);
+                       this->ExcludeFixtureSetup.c_str());
   }
-  if (this->Values[ctt_EXCLUDE_FIXTURE_CLEANUP]) {
+  if (!this->ExcludeFixtureCleanup.empty()) {
     handler->SetOption("ExcludeFixtureCleanupRegularExpression",
-                       this->Values[ctt_EXCLUDE_FIXTURE_CLEANUP]);
+                       this->ExcludeFixtureCleanup.c_str());
   }
-  if (this->Values[ctt_PARALLEL_LEVEL]) {
-    handler->SetOption("ParallelLevel", this->Values[ctt_PARALLEL_LEVEL]);
+  if (!this->ParallelLevel.empty()) {
+    handler->SetOption("ParallelLevel", this->ParallelLevel.c_str());
   }
-  if (this->Values[ctt_SCHEDULE_RANDOM]) {
-    handler->SetOption("ScheduleRandom", this->Values[ctt_SCHEDULE_RANDOM]);
+  if (!this->ScheduleRandom.empty()) {
+    handler->SetOption("ScheduleRandom", this->ScheduleRandom.c_str());
   }
-  if (this->Values[ctt_STOP_TIME]) {
-    this->CTest->SetStopTime(this->Values[ctt_STOP_TIME]);
+  if (!this->HardwareSpecFile.empty()) {
+    handler->SetOption("HardwareSpecFile", this->HardwareSpecFile.c_str());
+  }
+  if (!this->StopTime.empty()) {
+    this->CTest->SetStopTime(this->StopTime);
   }
 
   // Test load is determined by: TEST_LOAD argument,
@@ -109,16 +100,15 @@
   // command line argument... in that order.
   unsigned long testLoad;
   const char* ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD");
-  if (this->Values[ctt_TEST_LOAD] && *this->Values[ctt_TEST_LOAD]) {
-    if (!cmSystemTools::StringToULong(this->Values[ctt_TEST_LOAD],
-                                      &testLoad)) {
+  if (!this->TestLoad.empty()) {
+    if (!cmStrToULong(this->TestLoad.c_str(), &testLoad)) {
       testLoad = 0;
       cmCTestLog(this->CTest, WARNING,
-                 "Invalid value for 'TEST_LOAD' : "
-                   << this->Values[ctt_TEST_LOAD] << std::endl);
+                 "Invalid value for 'TEST_LOAD' : " << this->TestLoad
+                                                    << std::endl);
     }
   } else if (ctestTestLoad && *ctestTestLoad) {
-    if (!cmSystemTools::StringToULong(ctestTestLoad, &testLoad)) {
+    if (!cmStrToULong(ctestTestLoad, &testLoad)) {
       testLoad = 0;
       cmCTestLog(this->CTest, WARNING,
                  "Invalid value for 'CTEST_TEST_LOAD' : " << ctestTestLoad
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index 11c0db9..dc15279 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
 #include <string>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestTest
  * \brief Run a ctest script
@@ -20,17 +23,15 @@
 class cmCTestTestCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestTestCommand();
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestTestCommand* ni = new cmCTestTestCommand;
+    auto ni = cm::make_unique<cmCTestTestCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
@@ -39,29 +40,25 @@
   std::string GetName() const override { return "ctest_test"; }
 
 protected:
+  void BindArguments() override;
   virtual cmCTestGenericHandler* InitializeActualHandler();
   cmCTestGenericHandler* InitializeHandler() override;
 
-  enum
-  {
-    ctt_BUILD = ct_LAST,
-    ctt_RETURN_VALUE,
-    ctt_START,
-    ctt_END,
-    ctt_STRIDE,
-    ctt_EXCLUDE,
-    ctt_INCLUDE,
-    ctt_EXCLUDE_LABEL,
-    ctt_INCLUDE_LABEL,
-    ctt_EXCLUDE_FIXTURE,
-    ctt_EXCLUDE_FIXTURE_SETUP,
-    ctt_EXCLUDE_FIXTURE_CLEANUP,
-    ctt_PARALLEL_LEVEL,
-    ctt_SCHEDULE_RANDOM,
-    ctt_STOP_TIME,
-    ctt_TEST_LOAD,
-    ctt_LAST
-  };
+  std::string Start;
+  std::string End;
+  std::string Stride;
+  std::string Exclude;
+  std::string Include;
+  std::string ExcludeLabel;
+  std::string IncludeLabel;
+  std::string ExcludeFixture;
+  std::string ExcludeFixtureSetup;
+  std::string ExcludeFixtureCleanup;
+  std::string ParallelLevel;
+  std::string ScheduleRandom;
+  std::string StopTime;
+  std::string TestLoad;
+  std::string HardwareSpecFile;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 0ed56c8..2be62ae 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -1,70 +1,84 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestTestHandler.h"
+
 #include <algorithm>
 #include <chrono>
 #include <cmath>
-#include <cmsys/Base64.h>
-#include <cmsys/Directory.hxx>
-#include <cmsys/RegularExpression.hxx>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
 #include <cstring>
+#include <ctime>
 #include <functional>
 #include <iomanip>
 #include <iterator>
-#include <memory> // IWYU pragma: keep
 #include <set>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+#include <cmsys/Base64.h>
+#include <cmsys/Directory.hxx>
+#include <cmsys/RegularExpression.hxx>
+
+#include "cm_utf8.h"
 
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestMultiProcessHandler.h"
-#include "cmCommand.h"
+#include "cmCTestProcessesLexerHelper.h"
 #include "cmDuration.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
-#include "cm_utf8.h"
 #include "cmake.h"
-#include "cmsys/FStream.hxx"
 
-class cmExecutionStatus;
+namespace {
 
-class cmCTestSubdirCommand : public cmCommand
+class cmCTestCommand
 {
 public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
+  cmCTestCommand(cmCTestTestHandler* testHandler)
+    : TestHandler(testHandler)
   {
-    cmCTestSubdirCommand* c = new cmCTestSubdirCommand;
-    c->TestHandler = this->TestHandler;
-    return c;
   }
 
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& /*unused*/) override;
+  virtual ~cmCTestCommand() = default;
+
+  bool operator()(std::vector<cmListFileArgument> const& args,
+                  cmExecutionStatus& status)
+  {
+    cmMakefile& mf = status.GetMakefile();
+    std::vector<std::string> expandedArguments;
+    if (!mf.ExpandArguments(args, expandedArguments)) {
+      // There was an error expanding arguments.  It was already
+      // reported, so we can skip this command without error.
+      return true;
+    }
+    return this->InitialPass(expandedArguments, status);
+  }
+
+  virtual bool InitialPass(std::vector<std::string> const& args,
+                           cmExecutionStatus& status) = 0;
 
   cmCTestTestHandler* TestHandler;
 };
 
-bool cmCTestSubdirCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus& /*unused*/)
+bool cmCTestSubdirCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
@@ -74,9 +88,7 @@
     if (cmSystemTools::FileIsFullPath(arg)) {
       fname = arg;
     } else {
-      fname = cwd;
-      fname += "/";
-      fname += arg;
+      fname = cmStrCat(cwd, '/', arg);
     }
 
     if (!cmSystemTools::FileIsDirectory(fname)) {
@@ -87,8 +99,8 @@
     {
       cmWorkingDirectory workdir(fname);
       if (workdir.Failed()) {
-        this->SetError("Failed to change directory to " + fname + " : " +
-                       std::strerror(workdir.GetLastResult()));
+        status.SetError("Failed to change directory to " + fname + " : " +
+                        std::strerror(workdir.GetLastResult()));
         return false;
       }
       const char* testFilename;
@@ -104,52 +116,26 @@
       }
       fname += "/";
       fname += testFilename;
-      readit = this->Makefile->ReadDependentFile(fname);
+      readit = status.GetMakefile().ReadDependentFile(fname);
     }
     if (!readit) {
-      std::string m = "Could not find include file: ";
-      m += fname;
-      this->SetError(m);
+      status.SetError(cmStrCat("Could not find include file: ", fname));
       return false;
     }
   }
   return true;
 }
 
-class cmCTestAddSubdirectoryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmCTestAddSubdirectoryCommand* c = new cmCTestAddSubdirectoryCommand;
-    c->TestHandler = this->TestHandler;
-    return c;
-  }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& /*unused*/) override;
-
-  cmCTestTestHandler* TestHandler;
-};
-
-bool cmCTestAddSubdirectoryCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus& /*unused*/)
+bool cmCTestAddSubdirectoryCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  std::string fname = cmSystemTools::GetCurrentWorkingDirectory();
-  fname += "/";
-  fname += args[0];
+  std::string fname =
+    cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(), '/', args[0]);
 
   if (!cmSystemTools::FileExists(fname)) {
     // No subdirectory? So what...
@@ -170,29 +156,19 @@
     }
     fname += "/";
     fname += testFilename;
-    readit = this->Makefile->ReadDependentFile(fname);
+    readit = status.GetMakefile().ReadDependentFile(fname);
   }
   if (!readit) {
-    std::string m = "Could not find include file: ";
-    m += fname;
-    this->SetError(m);
+    status.SetError(cmStrCat("Could not find include file: ", fname));
     return false;
   }
   return true;
 }
 
-class cmCTestAddTestCommand : public cmCommand
+class cmCTestAddTestCommand : public cmCTestCommand
 {
 public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmCTestAddTestCommand* c = new cmCTestAddTestCommand;
-    c->TestHandler = this->TestHandler;
-    return c;
-  }
+  using cmCTestCommand::cmCTestCommand;
 
   /**
    * This is called when the command is first encountered in
@@ -200,32 +176,22 @@
    */
   bool InitialPass(std::vector<std::string> const& /*args*/,
                    cmExecutionStatus& /*unused*/) override;
-
-  cmCTestTestHandler* TestHandler;
 };
 
 bool cmCTestAddTestCommand::InitialPass(std::vector<std::string> const& args,
-                                        cmExecutionStatus& /*unused*/)
+                                        cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   return this->TestHandler->AddTest(args);
 }
 
-class cmCTestSetTestsPropertiesCommand : public cmCommand
+class cmCTestSetTestsPropertiesCommand : public cmCTestCommand
 {
 public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmCTestSetTestsPropertiesCommand* c = new cmCTestSetTestsPropertiesCommand;
-    c->TestHandler = this->TestHandler;
-    return c;
-  }
+  using cmCTestCommand::cmCTestCommand;
 
   /**
    * This is called when the command is first encountered in
@@ -233,8 +199,6 @@
    */
   bool InitialPass(std::vector<std::string> const& /*args*/,
                    cmExecutionStatus& /*unused*/) override;
-
-  cmCTestTestHandler* TestHandler;
 };
 
 bool cmCTestSetTestsPropertiesCommand::InitialPass(
@@ -243,19 +207,10 @@
   return this->TestHandler->SetTestsProperties(args);
 }
 
-class cmCTestSetDirectoryPropertiesCommand : public cmCommand
+class cmCTestSetDirectoryPropertiesCommand : public cmCTestCommand
 {
 public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmCTestSetDirectoryPropertiesCommand* c =
-      new cmCTestSetDirectoryPropertiesCommand;
-    c->TestHandler = this->TestHandler;
-    return c;
-  }
+  using cmCTestCommand::cmCTestCommand;
 
   /**
    * This is called when the command is first encountered in
@@ -263,8 +218,6 @@
    */
   bool InitialPass(std::vector<std::string> const& /*unused*/,
                    cmExecutionStatus& /*unused*/) override;
-
-  cmCTestTestHandler* TestHandler;
 };
 
 bool cmCTestSetDirectoryPropertiesCommand::InitialPass(
@@ -325,6 +278,8 @@
   return 0;
 }
 
+} // namespace
+
 cmCTestTestHandler::cmCTestTestHandler()
 {
   this->UseUnion = false;
@@ -334,6 +289,7 @@
   this->UseIncludeRegExpFlag = false;
   this->UseExcludeRegExpFlag = false;
   this->UseExcludeRegExpFirst = false;
+  this->UseHardwareSpec = false;
 
   this->CustomMaximumPassedTestOutputSize = 1 * 1024;
   this->CustomMaximumFailedTestOutputSize = 300 * 1024;
@@ -422,14 +378,97 @@
   return 1;
 }
 
-// clearly it would be nice if this were broken up into a few smaller
-// functions and commented...
 int cmCTestTestHandler::ProcessHandler()
 {
+  if (!this->ProcessOptions()) {
+    return -1;
+  }
+
+  this->TestResults.clear();
+
+  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
+                     (this->MemCheck ? "Memory check" : "Test")
+                       << " project "
+                       << cmSystemTools::GetCurrentWorkingDirectory()
+                       << std::endl,
+                     this->Quiet);
+  if (!this->PreProcessHandler()) {
+    return -1;
+  }
+
+  cmGeneratedFileStream mLogFile;
+  this->StartLogFile((this->MemCheck ? "DynamicAnalysis" : "Test"), mLogFile);
+  this->LogFile = &mLogFile;
+
+  std::vector<std::string> passed;
+  std::vector<std::string> failed;
+
+  // start the real time clock
+  auto clock_start = std::chrono::steady_clock::now();
+
+  this->ProcessDirectory(passed, failed);
+
+  auto clock_finish = std::chrono::steady_clock::now();
+
+  if (passed.size() + failed.size() == 0) {
+    if (!this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels()) {
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "No tests were found!!!" << std::endl);
+    }
+  } else {
+    if (this->HandlerVerbose && !passed.empty() &&
+        (this->UseIncludeRegExpFlag || this->UseExcludeRegExpFlag)) {
+      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                         std::endl
+                           << "The following tests passed:" << std::endl,
+                         this->Quiet);
+      for (std::string const& j : passed) {
+        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                           "\t" << j << std::endl, this->Quiet);
+      }
+    }
+
+    SetOfTests resultsSet(this->TestResults.begin(), this->TestResults.end());
+    std::vector<cmCTestTestHandler::cmCTestTestResult> disabledTests;
+
+    for (cmCTestTestResult const& ft : resultsSet) {
+      if (cmHasLiteralPrefix(ft.CompletionStatus, "SKIP_") ||
+          ft.CompletionStatus == "Disabled") {
+        disabledTests.push_back(ft);
+      }
+    }
+
+    cmDuration durationInSecs = clock_finish - clock_start;
+    this->LogTestSummary(passed, failed, durationInSecs);
+
+    this->LogDisabledTests(disabledTests);
+
+    this->LogFailedTests(failed, resultsSet);
+  }
+
+  if (!this->GenerateXML()) {
+    return 1;
+  }
+
+  if (!this->PostProcessHandler()) {
+    this->LogFile = nullptr;
+    return -1;
+  }
+
+  if (!failed.empty()) {
+    this->LogFile = nullptr;
+    return -1;
+  }
+  this->LogFile = nullptr;
+  return 0;
+}
+
+bool cmCTestTestHandler::ProcessOptions()
+{
   // Update internal data structure from generic one
   this->SetTestsToRunInformation(this->GetOption("TestsToRunInformation"));
-  this->SetUseUnion(cmSystemTools::IsOn(this->GetOption("UseUnion")));
-  if (cmSystemTools::IsOn(this->GetOption("ScheduleRandom"))) {
+  this->SetUseUnion(cmIsOn(this->GetOption("UseUnion")));
+  if (cmIsOn(this->GetOption("ScheduleRandom"))) {
     this->CTest->SetScheduleType("Random");
   }
   if (this->GetOption("ParallelLevel")) {
@@ -469,154 +508,122 @@
   if (val) {
     this->ExcludeFixtureCleanupRegExp = val;
   }
-  this->SetRerunFailed(cmSystemTools::IsOn(this->GetOption("RerunFailed")));
+  this->SetRerunFailed(cmIsOn(this->GetOption("RerunFailed")));
 
-  this->TestResults.clear();
-
-  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
-                     (this->MemCheck ? "Memory check" : "Test")
-                       << " project "
-                       << cmSystemTools::GetCurrentWorkingDirectory()
-                       << std::endl,
-                     this->Quiet);
-  if (!this->PreProcessHandler()) {
-    return -1;
+  val = this->GetOption("HardwareSpecFile");
+  if (val) {
+    this->UseHardwareSpec = true;
+    if (!this->HardwareSpec.ReadFromJSONFile(val)) {
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "Could not read hardware spec file: " << val << std::endl);
+      return false;
+    }
   }
 
-  cmGeneratedFileStream mLogFile;
-  this->StartLogFile((this->MemCheck ? "DynamicAnalysis" : "Test"), mLogFile);
-  this->LogFile = &mLogFile;
+  return true;
+}
 
-  std::vector<std::string> passed;
-  std::vector<std::string> failed;
-  int total;
+void cmCTestTestHandler::LogTestSummary(const std::vector<std::string>& passed,
+                                        const std::vector<std::string>& failed,
+                                        const cmDuration& durationInSecs)
+{
+  std::size_t total = passed.size() + failed.size();
 
-  // start the real time clock
-  auto clock_start = std::chrono::steady_clock::now();
+  float percent = float(passed.size()) * 100.0f / float(total);
+  if (!failed.empty() && percent > 99) {
+    percent = 99;
+  }
 
-  this->ProcessDirectory(passed, failed);
-
-  auto clock_finish = std::chrono::steady_clock::now();
-
-  total = int(passed.size()) + int(failed.size());
-
-  if (total == 0) {
-    if (!this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels()) {
-      cmCTestLog(this->CTest, ERROR_MESSAGE,
-                 "No tests were found!!!" << std::endl);
-    }
+  std::string passColorCode;
+  std::string failedColorCode;
+  if (failed.empty()) {
+    passColorCode = this->CTest->GetColorCode(cmCTest::Color::GREEN);
   } else {
-    if (this->HandlerVerbose && !passed.empty() &&
-        (this->UseIncludeRegExpFlag || this->UseExcludeRegExpFlag)) {
-      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-                         std::endl
-                           << "The following tests passed:" << std::endl,
-                         this->Quiet);
-      for (std::string const& j : passed) {
-        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-                           "\t" << j << std::endl, this->Quiet);
-      }
-    }
+    failedColorCode = this->CTest->GetColorCode(cmCTest::Color::RED);
+  }
+  cmCTestLog(this->CTest, HANDLER_OUTPUT,
+             std::endl
+               << passColorCode << std::lround(percent) << "% tests passed"
+               << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
+               << ", " << failedColorCode << failed.size() << " tests failed"
+               << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
+               << " out of " << total << std::endl);
+  if ((!this->CTest->GetLabelsForSubprojects().empty() &&
+       this->CTest->GetSubprojectSummary())) {
+    this->PrintLabelOrSubprojectSummary(true);
+  }
+  if (this->CTest->GetLabelSummary()) {
+    this->PrintLabelOrSubprojectSummary(false);
+  }
+  char realBuf[1024];
+  sprintf(realBuf, "%6.2f sec", durationInSecs.count());
+  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
+                     "\nTotal Test time (real) = " << realBuf << "\n",
+                     this->Quiet);
+}
 
-    typedef std::set<cmCTestTestHandler::cmCTestTestResult,
-                     cmCTestTestResultLess>
-      SetOfTests;
-    SetOfTests resultsSet(this->TestResults.begin(), this->TestResults.end());
-    std::vector<cmCTestTestHandler::cmCTestTestResult> disabledTests;
-
-    for (cmCTestTestResult const& ft : resultsSet) {
-      if (cmHasLiteralPrefix(ft.CompletionStatus, "SKIP_RETURN_CODE=") ||
-          ft.CompletionStatus == "Disabled") {
-        disabledTests.push_back(ft);
-      }
-    }
-
-    float percent = float(passed.size()) * 100.0f / float(total);
-    if (!failed.empty() && percent > 99) {
-      percent = 99;
-    }
-
-    std::string passColorCode;
-    std::string failedColorCode;
-    if (failed.empty()) {
-      passColorCode = this->CTest->GetColorCode(cmCTest::Color::GREEN);
-    } else {
-      failedColorCode = this->CTest->GetColorCode(cmCTest::Color::RED);
-    }
+void cmCTestTestHandler::LogDisabledTests(
+  const std::vector<cmCTestTestResult>& disabledTests)
+{
+  if (!disabledTests.empty()) {
+    cmGeneratedFileStream ofs;
     cmCTestLog(this->CTest, HANDLER_OUTPUT,
                std::endl
-                 << passColorCode << std::lround(percent) << "% tests passed"
-                 << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
-                 << ", " << failedColorCode << failed.size() << " tests failed"
-                 << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
-                 << " out of " << total << std::endl);
-    if ((!this->CTest->GetLabelsForSubprojects().empty() &&
-         this->CTest->GetSubprojectSummary())) {
-      this->PrintLabelOrSubprojectSummary(true);
-    }
-    if (this->CTest->GetLabelSummary()) {
-      this->PrintLabelOrSubprojectSummary(false);
-    }
-    char realBuf[1024];
-    cmDuration durationInSecs = clock_finish - clock_start;
-    sprintf(realBuf, "%6.2f sec", durationInSecs.count());
-    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
-                       "\nTotal Test time (real) = " << realBuf << "\n",
-                       this->Quiet);
+                 << "The following tests did not run:" << std::endl);
+    this->StartLogFile("TestsDisabled", ofs);
 
-    if (!disabledTests.empty()) {
-      cmGeneratedFileStream ofs;
-      cmCTestLog(this->CTest, HANDLER_OUTPUT,
-                 std::endl
-                   << "The following tests did not run:" << std::endl);
-      this->StartLogFile("TestsDisabled", ofs);
-
-      const char* disabled_reason;
-      cmCTestLog(this->CTest, HANDLER_OUTPUT,
-                 this->CTest->GetColorCode(cmCTest::Color::BLUE));
-      for (cmCTestTestResult const& dt : disabledTests) {
-        ofs << dt.TestCount << ":" << dt.Name << std::endl;
-        if (dt.CompletionStatus == "Disabled") {
-          disabled_reason = "Disabled";
-        } else {
-          disabled_reason = "Skipped";
-        }
-        cmCTestLog(this->CTest, HANDLER_OUTPUT,
-                   "\t" << std::setw(3) << dt.TestCount << " - " << dt.Name
-                        << " (" << disabled_reason << ")" << std::endl);
+    const char* disabled_reason;
+    cmCTestLog(this->CTest, HANDLER_OUTPUT,
+               this->CTest->GetColorCode(cmCTest::Color::BLUE));
+    for (cmCTestTestResult const& dt : disabledTests) {
+      ofs << dt.TestCount << ":" << dt.Name << std::endl;
+      if (dt.CompletionStatus == "Disabled") {
+        disabled_reason = "Disabled";
+      } else {
+        disabled_reason = "Skipped";
       }
       cmCTestLog(this->CTest, HANDLER_OUTPUT,
-                 this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR));
+                 "\t" << std::setw(3) << dt.TestCount << " - " << dt.Name
+                      << " (" << disabled_reason << ")" << std::endl);
     }
+    cmCTestLog(this->CTest, HANDLER_OUTPUT,
+               this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR));
+  }
+}
 
-    if (!failed.empty()) {
-      cmGeneratedFileStream ofs;
-      cmCTestLog(this->CTest, HANDLER_OUTPUT,
-                 std::endl
-                   << "The following tests FAILED:" << std::endl);
-      this->StartLogFile("TestsFailed", ofs);
+void cmCTestTestHandler::LogFailedTests(const std::vector<std::string>& failed,
+                                        const SetOfTests& resultsSet)
+{
+  if (!failed.empty()) {
+    cmGeneratedFileStream ofs;
+    cmCTestLog(this->CTest, HANDLER_OUTPUT,
+               std::endl
+                 << "The following tests FAILED:" << std::endl);
+    this->StartLogFile("TestsFailed", ofs);
 
-      for (cmCTestTestResult const& ft : resultsSet) {
-        if (ft.Status != cmCTestTestHandler::COMPLETED &&
-            !cmHasLiteralPrefix(ft.CompletionStatus, "SKIP_RETURN_CODE=") &&
-            ft.CompletionStatus != "Disabled") {
-          ofs << ft.TestCount << ":" << ft.Name << std::endl;
-          auto testColor = cmCTest::Color::RED;
-          if (this->GetTestStatus(ft) == "Not Run") {
-            testColor = cmCTest::Color::YELLOW;
-          }
-          cmCTestLog(
-            this->CTest, HANDLER_OUTPUT,
-            "\t" << this->CTest->GetColorCode(testColor) << std::setw(3)
-                 << ft.TestCount << " - " << ft.Name << " ("
-                 << this->GetTestStatus(ft) << ")"
-                 << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
-                 << std::endl);
+    for (cmCTestTestResult const& ft : resultsSet) {
+      if (ft.Status != cmCTestTestHandler::COMPLETED &&
+          !cmHasLiteralPrefix(ft.CompletionStatus, "SKIP_") &&
+          ft.CompletionStatus != "Disabled") {
+        ofs << ft.TestCount << ":" << ft.Name << std::endl;
+        auto testColor = cmCTest::Color::RED;
+        if (this->GetTestStatus(ft) == "Not Run") {
+          testColor = cmCTest::Color::YELLOW;
         }
+        cmCTestLog(
+          this->CTest, HANDLER_OUTPUT,
+          "\t" << this->CTest->GetColorCode(testColor) << std::setw(3)
+               << ft.TestCount << " - " << ft.Name << " ("
+               << this->GetTestStatus(ft) << ")"
+               << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR)
+               << std::endl);
       }
     }
   }
+}
 
+bool cmCTestTestHandler::GenerateXML()
+{
   if (this->CTest->GetProduceXML()) {
     cmGeneratedFileStream xmlfile;
     if (!this->StartResultingXML(
@@ -627,23 +634,13 @@
                    << (this->MemCheck ? "memory check" : "testing")
                    << " XML file" << std::endl);
       this->LogFile = nullptr;
-      return 1;
+      return false;
     }
     cmXMLWriter xml(xmlfile);
     this->GenerateDartOutput(xml);
   }
 
-  if (!this->PostProcessHandler()) {
-    this->LogFile = nullptr;
-    return -1;
-  }
-
-  if (!failed.empty()) {
-    this->LogFile = nullptr;
-    return -1;
-  }
-  this->LogFile = nullptr;
-  return 0;
+  return true;
 }
 
 void cmCTestTestHandler::PrintLabelOrSubprojectSummary(bool doSubProject)
@@ -660,15 +657,13 @@
     for (std::string const& l : p.Labels) {
       // first check to see if the current label is a subproject label
       bool isSubprojectLabel = false;
-      std::vector<std::string>::iterator subproject =
-        std::find(subprojects.begin(), subprojects.end(), l);
+      auto subproject = std::find(subprojects.begin(), subprojects.end(), l);
       if (subproject != subprojects.end()) {
         isSubprojectLabel = true;
       }
       // if we are doing sub projects and this label is one, then use it
       // if we are not doing sub projects and the label is not one use it
-      if ((doSubProject && isSubprojectLabel) ||
-          (!doSubProject && !isSubprojectLabel)) {
+      if (doSubProject == isSubprojectLabel) {
         if (l.size() > maxlen) {
           maxlen = l.size();
         }
@@ -683,7 +678,7 @@
     cmCTestTestProperties& p = *result.Properties;
     for (std::string const& l : p.Labels) {
       // only use labels found in labels
-      if (labels.find(l) != labels.end()) {
+      if (cmContains(labels, l)) {
         labelTimes[l] +=
           result.ExecutionTime.count() * result.Properties->Processors;
         ++labelCounts[l];
@@ -825,17 +820,14 @@
 
     if (this->UseUnion) {
       // if it is not in the list and not in the regexp then skip
-      if ((!this->TestsToRun.empty() &&
-           std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt) ==
-             this->TestsToRun.end()) &&
+      if ((!this->TestsToRun.empty() && !cmContains(this->TestsToRun, cnt)) &&
           !tp.IsInBasedOnREOptions) {
         continue;
       }
     } else {
       // is this test in the list of tests to run? If not then skip it
       if ((!this->TestsToRun.empty() &&
-           std::find(this->TestsToRun.begin(), this->TestsToRun.end(),
-                     inREcnt) == this->TestsToRun.end()) ||
+           !cmContains(this->TestsToRun, inREcnt)) ||
           !tp.IsInBasedOnREOptions) {
         continue;
       }
@@ -864,9 +856,7 @@
     cnt++;
 
     // if this test is not in our list of tests to run, then skip it.
-    if ((!this->TestsToRun.empty() &&
-         std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt) ==
-           this->TestsToRun.end())) {
+    if (!this->TestsToRun.empty() && !cmContains(this->TestsToRun, cnt)) {
       continue;
     }
 
@@ -915,14 +905,13 @@
 
   // Prepare some maps to help us find setup and cleanup tests for
   // any given fixture
-  typedef ListOfTests::const_iterator TestIterator;
-  typedef std::multimap<std::string, TestIterator> FixtureDependencies;
-  typedef FixtureDependencies::const_iterator FixtureDepsIterator;
+  using TestIterator = ListOfTests::const_iterator;
+  using FixtureDependencies = std::multimap<std::string, TestIterator>;
+  using FixtureDepsIterator = FixtureDependencies::const_iterator;
   FixtureDependencies fixtureSetups;
   FixtureDependencies fixtureCleanups;
 
-  for (ListOfTests::const_iterator it = this->TestList.begin();
-       it != this->TestList.end(); ++it) {
+  for (auto it = this->TestList.begin(); it != this->TestList.end(); ++it) {
     const cmCTestTestProperties& p = *it;
 
     for (std::string const& deps : p.FixturesSetup) {
@@ -983,12 +972,10 @@
       // cleanup tests depend on this test case later.
       std::pair<FixtureDepsIterator, FixtureDepsIterator> setupRange =
         fixtureSetups.equal_range(requiredFixtureName);
-      for (FixtureDepsIterator sIt = setupRange.first;
-           sIt != setupRange.second; ++sIt) {
+      for (auto sIt = setupRange.first; sIt != setupRange.second; ++sIt) {
         const std::string& setupTestName = sIt->second->Name;
         tests[i].RequireSuccessDepends.insert(setupTestName);
-        if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(),
-                      setupTestName) == tests[i].Depends.end()) {
+        if (!cmContains(tests[i].Depends, setupTestName)) {
           tests[i].Depends.push_back(setupTestName);
         }
       }
@@ -1008,8 +995,7 @@
           !excludeSetupRegex.find(requiredFixtureName)) {
         std::pair<FixtureDepsIterator, FixtureDepsIterator> fixtureRange =
           fixtureSetups.equal_range(requiredFixtureName);
-        for (FixtureDepsIterator it = fixtureRange.first;
-             it != fixtureRange.second; ++it) {
+        for (auto it = fixtureRange.first; it != fixtureRange.second; ++it) {
           ListOfTests::const_iterator lotIt = it->second;
           const cmCTestTestProperties& p = *lotIt;
 
@@ -1040,8 +1026,7 @@
           !excludeCleanupRegex.find(requiredFixtureName)) {
         std::pair<FixtureDepsIterator, FixtureDepsIterator> fixtureRange =
           fixtureCleanups.equal_range(requiredFixtureName);
-        for (FixtureDepsIterator it = fixtureRange.first;
-             it != fixtureRange.second; ++it) {
+        for (auto it = fixtureRange.first; it != fixtureRange.second; ++it) {
           ListOfTests::const_iterator lotIt = it->second;
           const cmCTestTestProperties& p = *lotIt;
 
@@ -1089,14 +1074,12 @@
       // This cleanup test could be part of the original test list that was
       // passed in. It is then possible that no other test requires the
       // fIt fixture, so we have to check for this.
-      std::map<std::string, std::vector<size_t>>::const_iterator cIt =
-        fixtureRequirements.find(fixture);
+      auto cIt = fixtureRequirements.find(fixture);
       if (cIt != fixtureRequirements.end()) {
         const std::vector<size_t>& indices = cIt->second;
         for (size_t index : indices) {
           const std::string& reqTestName = tests[index].Name;
-          if (std::find(p.Depends.begin(), p.Depends.end(), reqTestName) ==
-              p.Depends.end()) {
+          if (!cmContains(p.Depends, reqTestName)) {
             p.Depends.push_back(reqTestName);
           }
         }
@@ -1109,8 +1092,7 @@
         const std::vector<size_t>& indices = cIt->second;
         for (size_t index : indices) {
           const std::string& setupTestName = tests[index].Name;
-          if (std::find(p.Depends.begin(), p.Depends.end(), setupTestName) ==
-              p.Depends.end()) {
+          if (!cmContains(p.Depends, setupTestName)) {
             p.Depends.push_back(setupTestName);
           }
         }
@@ -1255,6 +1237,9 @@
   } else {
     parallel->SetTestLoad(this->CTest->GetTestLoad());
   }
+  if (this->UseHardwareSpec) {
+    parallel->InitHardwareAllocator(this->HardwareSpec);
+  }
 
   *this->LogFile
     << "Start testing: " << this->CTest->CurrentTime() << std::endl
@@ -1298,6 +1283,7 @@
   parallel->SetPassFailVectors(&passed, &failed);
   this->TestResults.clear();
   parallel->SetTestResults(&this->TestResults);
+  parallel->CheckHardwareAvailable();
 
   if (this->CTest->ShouldPrintLabels()) {
     parallel->PrintLabels();
@@ -1527,50 +1513,32 @@
   attemptedConfigs.emplace_back();
 
   if (!ctest->GetConfigType().empty()) {
-    tempPath = filepath;
-    tempPath += ctest->GetConfigType();
-    tempPath += "/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, ctest->GetConfigType(), '/', filename);
     attempted.push_back(tempPath);
     attemptedConfigs.push_back(ctest->GetConfigType());
     // If the file is an OSX bundle then the configtype
     // will be at the start of the path
-    tempPath = ctest->GetConfigType();
-    tempPath += "/";
-    tempPath += filepath;
-    tempPath += filename;
+    tempPath = cmStrCat(ctest->GetConfigType(), '/', filepath, filename);
     attempted.push_back(tempPath);
     attemptedConfigs.push_back(ctest->GetConfigType());
   } else {
     // no config specified - try some options...
-    tempPath = filepath;
-    tempPath += "Release/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "Release/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("Release");
-    tempPath = filepath;
-    tempPath += "Debug/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "Debug/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("Debug");
-    tempPath = filepath;
-    tempPath += "MinSizeRel/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "MinSizeRel/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("MinSizeRel");
-    tempPath = filepath;
-    tempPath += "RelWithDebInfo/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "RelWithDebInfo/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("RelWithDebInfo");
-    tempPath = filepath;
-    tempPath += "Deployment/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "Deployment/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("Deployment");
-    tempPath = filepath;
-    tempPath += "Development/";
-    tempPath += filename;
+    tempPath = cmStrCat(filepath, "Development/", filename);
     attempted.push_back(tempPath);
     attemptedConfigs.emplace_back("Deployment");
   }
@@ -1624,8 +1592,8 @@
     // then try with the exe extension
     else {
       failed.push_back(attempted[ai]);
-      tempPath = attempted[ai];
-      tempPath += cmSystemTools::GetExecutableExtension();
+      tempPath =
+        cmStrCat(attempted[ai], cmSystemTools::GetExecutableExtension());
       if (cmSystemTools::FileExists(tempPath) &&
           !cmSystemTools::FileIsDirectory(tempPath)) {
         fullPath = cmSystemTools::CollapseFullPath(tempPath);
@@ -1658,6 +1626,14 @@
   return fullPath;
 }
 
+bool cmCTestTestHandler::ParseProcessesProperty(
+  const std::string& val,
+  std::vector<std::vector<cmCTestTestResourceRequirement>>& processes)
+{
+  cmCTestProcessesLexerHelper lexer(processes);
+  return lexer.ParseString(val);
+}
+
 void cmCTestTestHandler::GetListOfTests()
 {
   if (!this->IncludeLabelRegExp.empty()) {
@@ -1682,36 +1658,26 @@
   cm.GetCurrentSnapshot().SetDefaultDefinitions();
   cmGlobalGenerator gg(&cm);
   cmMakefile mf(&gg, cm.GetCurrentSnapshot());
-  mf.AddDefinition("CTEST_CONFIGURATION_TYPE",
-                   this->CTest->GetConfigType().c_str());
+  mf.AddDefinition("CTEST_CONFIGURATION_TYPE", this->CTest->GetConfigType());
 
   // Add handler for ADD_TEST
-  cmCTestAddTestCommand* newCom1 = new cmCTestAddTestCommand;
-  newCom1->TestHandler = this;
-  cm.GetState()->AddBuiltinCommand("add_test", newCom1);
+  cm.GetState()->AddBuiltinCommand("add_test", cmCTestAddTestCommand(this));
 
   // Add handler for SUBDIRS
-  cmCTestSubdirCommand* newCom2 = new cmCTestSubdirCommand;
-  newCom2->TestHandler = this;
-  cm.GetState()->AddBuiltinCommand("subdirs", newCom2);
+  cm.GetState()->AddBuiltinCommand("subdirs", cmCTestSubdirCommand);
 
   // Add handler for ADD_SUBDIRECTORY
-  cmCTestAddSubdirectoryCommand* newCom3 = new cmCTestAddSubdirectoryCommand;
-  newCom3->TestHandler = this;
-  cm.GetState()->AddBuiltinCommand("add_subdirectory", newCom3);
+  cm.GetState()->AddBuiltinCommand("add_subdirectory",
+                                   cmCTestAddSubdirectoryCommand);
 
   // Add handler for SET_TESTS_PROPERTIES
-  cmCTestSetTestsPropertiesCommand* newCom4 =
-    new cmCTestSetTestsPropertiesCommand;
-  newCom4->TestHandler = this;
-  cm.GetState()->AddBuiltinCommand("set_tests_properties", newCom4);
+  cm.GetState()->AddBuiltinCommand("set_tests_properties",
+                                   cmCTestSetTestsPropertiesCommand(this));
 
   // Add handler for SET_DIRECTORY_PROPERTIES
   cm.GetState()->RemoveBuiltinCommand("set_directory_properties");
-  cmCTestSetDirectoryPropertiesCommand* newCom5 =
-    new cmCTestSetDirectoryPropertiesCommand;
-  newCom5->TestHandler = this;
-  cm.GetState()->AddBuiltinCommand("set_directory_properties", newCom5);
+  cm.GetState()->AddBuiltinCommand("set_directory_properties",
+                                   cmCTestSetDirectoryPropertiesCommand(this));
 
   const char* testFilename;
   if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
@@ -1818,8 +1784,7 @@
   std::sort(this->TestsToRun.begin(), this->TestsToRun.end(),
             std::less<int>());
   // remove duplicates
-  std::vector<int>::iterator new_end =
-    std::unique(this->TestsToRun.begin(), this->TestsToRun.end());
+  auto new_end = std::unique(this->TestsToRun.begin(), this->TestsToRun.end());
   this->TestsToRun.erase(new_end, this->TestsToRun.end());
 }
 
@@ -2150,7 +2115,7 @@
           if (key == "_BACKTRACE_TRIPLES") {
             std::vector<std::string> triples;
             // allow empty args in the triples
-            cmSystemTools::ExpandListArgument(val, triples, true);
+            cmExpandList(val, triples, true);
 
             // Ensure we have complete triples otherwise the data is corrupt.
             if (triples.size() % 3 == 0) {
@@ -2163,8 +2128,7 @@
                 cmListFileContext fc;
                 fc.FilePath = triples[i - 3];
                 long line = 0;
-                if (!cmSystemTools::StringToLong(triples[i - 2].c_str(),
-                                                 &line)) {
+                if (!cmStrToLong(triples[i - 2], &line)) {
                   line = 0;
                 }
                 fc.Line = line;
@@ -2174,38 +2138,34 @@
             }
           }
           if (key == "WILL_FAIL") {
-            rt.WillFail = cmSystemTools::IsOn(val);
+            rt.WillFail = cmIsOn(val);
           }
           if (key == "DISABLED") {
-            rt.Disabled = cmSystemTools::IsOn(val);
+            rt.Disabled = cmIsOn(val);
           }
           if (key == "ATTACHED_FILES") {
-            cmSystemTools::ExpandListArgument(val, rt.AttachedFiles);
+            cmExpandList(val, rt.AttachedFiles);
           }
           if (key == "ATTACHED_FILES_ON_FAIL") {
-            cmSystemTools::ExpandListArgument(val, rt.AttachOnFail);
+            cmExpandList(val, rt.AttachOnFail);
           }
           if (key == "RESOURCE_LOCK") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
 
             rt.LockedResources.insert(lval.begin(), lval.end());
           }
           if (key == "FIXTURES_SETUP") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
 
             rt.FixturesSetup.insert(lval.begin(), lval.end());
           }
           if (key == "FIXTURES_CLEANUP") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
 
             rt.FixturesCleanup.insert(lval.begin(), lval.end());
           }
           if (key == "FIXTURES_REQUIRED") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
 
             rt.FixturesRequired.insert(lval.begin(), lval.end());
           }
@@ -2217,18 +2177,23 @@
             rt.Cost = static_cast<float>(atof(val.c_str()));
           }
           if (key == "REQUIRED_FILES") {
-            cmSystemTools::ExpandListArgument(val, rt.RequiredFiles);
+            cmExpandList(val, rt.RequiredFiles);
           }
           if (key == "RUN_SERIAL") {
-            rt.RunSerial = cmSystemTools::IsOn(val);
+            rt.RunSerial = cmIsOn(val);
           }
           if (key == "FAIL_REGULAR_EXPRESSION") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
             for (std::string const& cr : lval) {
               rt.ErrorRegularExpressions.emplace_back(cr, cr);
             }
           }
+          if (key == "SKIP_REGULAR_EXPRESSION") {
+            std::vector<std::string> lval = cmExpandedList(val);
+            for (std::string const& cr : lval) {
+              rt.SkipRegularExpressions.emplace_back(cr, cr);
+            }
+          }
           if (key == "PROCESSORS") {
             rt.Processors = atoi(val.c_str());
             if (rt.Processors < 1) {
@@ -2236,7 +2201,12 @@
             }
           }
           if (key == "PROCESSOR_AFFINITY") {
-            rt.WantAffinity = cmSystemTools::IsOn(val);
+            rt.WantAffinity = cmIsOn(val);
+          }
+          if (key == "PROCESSES") {
+            if (!ParseProcessesProperty(val, rt.Processes)) {
+              return false;
+            }
           }
           if (key == "SKIP_RETURN_CODE") {
             rt.SkipReturnCode = atoi(val.c_str());
@@ -2245,20 +2215,18 @@
             }
           }
           if (key == "DEPENDS") {
-            cmSystemTools::ExpandListArgument(val, rt.Depends);
+            cmExpandList(val, rt.Depends);
           }
           if (key == "ENVIRONMENT") {
-            cmSystemTools::ExpandListArgument(val, rt.Environment);
+            cmExpandList(val, rt.Environment);
           }
           if (key == "LABELS") {
-            std::vector<std::string> Labels;
-            cmSystemTools::ExpandListArgument(val, Labels);
+            std::vector<std::string> Labels = cmExpandedList(val);
             rt.Labels.insert(rt.Labels.end(), Labels.begin(), Labels.end());
             // sort the array
             std::sort(rt.Labels.begin(), rt.Labels.end());
             // remove duplicates
-            std::vector<std::string>::iterator new_end =
-              std::unique(rt.Labels.begin(), rt.Labels.end());
+            auto new_end = std::unique(rt.Labels.begin(), rt.Labels.end());
             rt.Labels.erase(new_end, rt.Labels.end());
           }
           if (key == "MEASUREMENT") {
@@ -2272,8 +2240,7 @@
             }
           }
           if (key == "PASS_REGULAR_EXPRESSION") {
-            std::vector<std::string> lval;
-            cmSystemTools::ExpandListArgument(val, lval);
+            std::vector<std::string> lval = cmExpandedList(val);
             for (std::string const& cr : lval) {
               rt.RequiredRegularExpressions.emplace_back(cr, cr);
             }
@@ -2282,16 +2249,14 @@
             rt.Directory = val;
           }
           if (key == "TIMEOUT_AFTER_MATCH") {
-            std::vector<std::string> propArgs;
-            cmSystemTools::ExpandListArgument(val, propArgs);
+            std::vector<std::string> propArgs = cmExpandedList(val);
             if (propArgs.size() != 2) {
               cmCTestLog(this->CTest, WARNING,
                          "TIMEOUT_AFTER_MATCH expects two arguments, found "
                            << propArgs.size() << std::endl);
             } else {
               rt.AlternateTimeout = cmDuration(atof(propArgs[0].c_str()));
-              std::vector<std::string> lval;
-              cmSystemTools::ExpandListArgument(propArgs[1], lval);
+              std::vector<std::string> lval = cmExpandedList(propArgs[1]);
               for (std::string const& cr : lval) {
                 rt.TimeoutRegularExpressions.emplace_back(cr, cr);
               }
@@ -2333,16 +2298,14 @@
       std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
       if (cwd == rt.Directory) {
         if (key == "LABELS") {
-          std::vector<std::string> DirectoryLabels;
-          cmSystemTools::ExpandListArgument(val, DirectoryLabels);
+          std::vector<std::string> DirectoryLabels = cmExpandedList(val);
           rt.Labels.insert(rt.Labels.end(), DirectoryLabels.begin(),
                            DirectoryLabels.end());
 
           // sort the array
           std::sort(rt.Labels.begin(), rt.Labels.end());
           // remove duplicates
-          std::vector<std::string>::iterator new_end =
-            std::unique(rt.Labels.begin(), rt.Labels.end());
+          auto new_end = std::unique(rt.Labels.begin(), rt.Labels.end());
           rt.Labels.erase(new_end, rt.Labels.end());
         }
       }
@@ -2422,3 +2385,17 @@
   this->TestList.push_back(test);
   return true;
 }
+
+bool cmCTestTestHandler::cmCTestTestResourceRequirement::operator==(
+  const cmCTestTestResourceRequirement& other) const
+{
+  return this->ResourceType == other.ResourceType &&
+    this->SlotsNeeded == other.SlotsNeeded &&
+    this->UnitsNeeded == other.UnitsNeeded;
+}
+
+bool cmCTestTestHandler::cmCTestTestResourceRequirement::operator!=(
+  const cmCTestTestResourceRequirement& other) const
+{
+  return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 7f3f5e4..525215c 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -5,21 +5,24 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-#include "cmDuration.h"
-#include "cmListFileCache.h"
-
-#include "cmsys/RegularExpression.hxx"
 #include <chrono>
 #include <cstdint>
 #include <iosfwd>
 #include <map>
 #include <set>
-#include <stddef.h>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <stddef.h>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmCTestGenericHandler.h"
+#include "cmCTestHardwareSpec.h"
+#include "cmDuration.h"
+#include "cmListFileCache.h"
+
 class cmCTest;
 class cmMakefile;
 class cmXMLWriter;
@@ -34,7 +37,7 @@
   friend class cmCTestMultiProcessHandler;
 
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /**
    * The main entry point for this class
@@ -100,6 +103,16 @@
 
   void Initialize() override;
 
+  struct cmCTestTestResourceRequirement
+  {
+    std::string ResourceType;
+    int SlotsNeeded;
+    int UnitsNeeded;
+
+    bool operator==(const cmCTestTestResourceRequirement& other) const;
+    bool operator!=(const cmCTestTestResourceRequirement& other) const;
+  };
+
   // NOTE: This struct is Saved/Restored
   // in cmCTestTestHandler, if you add to this class
   // then you must add the new members to that code or
@@ -118,6 +131,8 @@
     std::vector<std::pair<cmsys::RegularExpression, std::string>>
       RequiredRegularExpressions;
     std::vector<std::pair<cmsys::RegularExpression, std::string>>
+      SkipRegularExpressions;
+    std::vector<std::pair<cmsys::RegularExpression, std::string>>
       TimeoutRegularExpressions;
     std::map<std::string, std::string> Measurements;
     bool IsInBasedOnREOptions;
@@ -143,6 +158,7 @@
     std::set<std::string> FixturesCleanup;
     std::set<std::string> FixturesRequired;
     std::set<std::string> RequireSuccessDepends;
+    std::vector<std::vector<cmCTestTestResourceRequirement>> Processes;
     // Private test generator properties used to track backtraces
     cmListFileBacktrace Backtrace;
   };
@@ -186,15 +202,31 @@
                                     std::vector<std::string>& extraPaths,
                                     std::vector<std::string>& failed);
 
-  typedef std::vector<cmCTestTestProperties> ListOfTests;
+  static bool ParseProcessesProperty(
+    const std::string& val,
+    std::vector<std::vector<cmCTestTestResourceRequirement>>& processes);
+
+  using ListOfTests = std::vector<cmCTestTestProperties>;
 
 protected:
+  using SetOfTests =
+    std::set<cmCTestTestHandler::cmCTestTestResult, cmCTestTestResultLess>;
+
   // compute a final test list
   virtual int PreProcessHandler();
   virtual int PostProcessHandler();
   virtual void GenerateTestCommand(std::vector<std::string>& args, int test);
   int ExecuteCommands(std::vector<std::string>& vec);
 
+  bool ProcessOptions();
+  void LogTestSummary(const std::vector<std::string>& passed,
+                      const std::vector<std::string>& failed,
+                      const cmDuration& durationInSecs);
+  void LogDisabledTests(const std::vector<cmCTestTestResult>& disabledTests);
+  void LogFailedTests(const std::vector<std::string>& failed,
+                      const SetOfTests& resultsSet);
+  bool GenerateXML();
+
   void WriteTestResultHeader(cmXMLWriter& xml,
                              cmCTestTestResult const& result);
   void WriteTestResultFooter(cmXMLWriter& xml,
@@ -207,7 +239,7 @@
 
   cmDuration ElapsedTestingTime;
 
-  typedef std::vector<cmCTestTestResult> TestResultsVector;
+  using TestResultsVector = std::vector<cmCTestTestResult>;
   TestResultsVector TestResults;
 
   std::vector<std::string> CustomTestsIgnore;
@@ -304,6 +336,9 @@
   cmsys::RegularExpression IncludeTestsRegularExpression;
   cmsys::RegularExpression ExcludeTestsRegularExpression;
 
+  bool UseHardwareSpec;
+  cmCTestHardwareSpec HardwareSpec;
+
   void GenerateRegressionImages(cmXMLWriter& xml, const std::string& dart);
   cmsys::RegularExpression DartStuff1;
   void CheckLabelFilter(cmCTestTestProperties& it);
diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx
index 65dc921..673eb9a 100644
--- a/Source/CTest/cmCTestUpdateCommand.cxx
+++ b/Source/CTest/cmCTestUpdateCommand.cxx
@@ -7,14 +7,11 @@
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
-#include <vector>
-
 cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler()
 {
-  if (this->Values[ct_SOURCE]) {
+  if (!this->Source.empty()) {
     this->CTest->SetCTestConfiguration(
-      "SourceDirectory",
-      cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(),
+      "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(),
       this->Quiet);
   } else {
     this->CTest->SetCTestConfiguration(
diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h
index 3b2f3e1..5555c16 100644
--- a/Source/CTest/cmCTestUpdateCommand.h
+++ b/Source/CTest/cmCTestUpdateCommand.h
@@ -5,12 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
 #include <string>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestUpdate
  * \brief Run a ctest script
@@ -20,17 +23,15 @@
 class cmCTestUpdateCommand : public cmCTestHandlerCommand
 {
 public:
-  cmCTestUpdateCommand() {}
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestUpdateCommand* ni = new cmCTestUpdateCommand;
+    auto ni = cm::make_unique<cmCTestUpdateCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx
index 5cfc4a7..f30ba2b 100644
--- a/Source/CTest/cmCTestUpdateHandler.cxx
+++ b/Source/CTest/cmCTestUpdateHandler.cxx
@@ -2,7 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestUpdateHandler.h"
 
-#include "cmAlgorithms.h"
+#include <chrono>
+#include <sstream>
+
+#include <cm/memory>
+
 #include "cmCLocaleEnvironmentScope.h"
 #include "cmCTest.h"
 #include "cmCTestBZR.h"
@@ -13,14 +17,11 @@
 #include "cmCTestSVN.h"
 #include "cmCTestVC.h"
 #include "cmGeneratedFileStream.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmXMLWriter.h"
 
-#include <chrono>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-
 static const char* cmCTestUpdateHandlerUpdateStrings[] = {
   "Unknown", "CVS", "SVN", "BZR", "GIT", "HG", "P4"
 };
@@ -266,33 +267,27 @@
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_SVN;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/CVS";
+  sourceDirectory = cmStrCat(dir, "/CVS");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_CVS;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/.bzr";
+  sourceDirectory = cmStrCat(dir, "/.bzr");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_BZR;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/.git";
+  sourceDirectory = cmStrCat(dir, "/.git");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_GIT;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/.hg";
+  sourceDirectory = cmStrCat(dir, "/.hg");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_HG;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/.p4";
+  sourceDirectory = cmStrCat(dir, "/.p4");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_P4;
   }
-  sourceDirectory = dir;
-  sourceDirectory += "/.p4config";
+  sourceDirectory = cmStrCat(dir, "/.p4config");
   if (cmSystemTools::FileExists(sourceDirectory)) {
     return cmCTestUpdateHandler::e_P4;
   }
diff --git a/Source/CTest/cmCTestUpdateHandler.h b/Source/CTest/cmCTestUpdateHandler.h
index 0f51d3f..afc0e3d 100644
--- a/Source/CTest/cmCTestUpdateHandler.h
+++ b/Source/CTest/cmCTestUpdateHandler.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmCTestGenericHandler.h"
+
 /** \class cmCTestUpdateHandler
  * \brief A class that handles ctest -S invocations
  *
@@ -18,7 +18,7 @@
 class cmCTestUpdateHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   /*
    * The main entry point for this class
diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx
index 59fbf37..d0e3848 100644
--- a/Source/CTest/cmCTestUploadCommand.cxx
+++ b/Source/CTest/cmCTestUploadCommand.cxx
@@ -2,61 +2,46 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestUploadCommand.h"
 
+#include <set>
 #include <sstream>
 #include <vector>
 
+#include "cm_static_string_view.hxx"
+
+#include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestUploadHandler.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmSystemTools.h"
 
+void cmCTestUploadCommand::BindArguments()
+{
+  this->Bind("FILES"_s, this->Files);
+  this->Bind("QUIET"_s, this->Quiet);
+  this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
+}
+
+void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&)
+{
+  cmEraseIf(this->Files, [this](std::string const& arg) -> bool {
+    if (!cmSystemTools::FileExists(arg)) {
+      std::ostringstream e;
+      e << "File \"" << arg << "\" does not exist. Cannot submit "
+        << "a non-existent file.";
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return true;
+    }
+    return false;
+  });
+}
+
 cmCTestGenericHandler* cmCTestUploadCommand::InitializeHandler()
 {
   cmCTestUploadHandler* handler = this->CTest->GetUploadHandler();
   handler->Initialize();
-  handler->SetFiles(this->Files);
+  handler->SetFiles(
+    std::set<std::string>(this->Files.begin(), this->Files.end()));
   handler->SetQuiet(this->Quiet);
   return handler;
 }
-
-bool cmCTestUploadCommand::CheckArgumentKeyword(std::string const& arg)
-{
-  if (arg == "FILES") {
-    this->ArgumentDoing = ArgumentDoingFiles;
-    return true;
-  }
-  if (arg == "QUIET") {
-    this->ArgumentDoing = ArgumentDoingNone;
-    this->Quiet = true;
-    return true;
-  }
-  if (arg == "CAPTURE_CMAKE_ERROR") {
-    this->ArgumentDoing = ArgumentDoingCaptureCMakeError;
-    return true;
-  }
-  return false;
-}
-
-bool cmCTestUploadCommand::CheckArgumentValue(std::string const& arg)
-{
-  if (this->ArgumentDoing == ArgumentDoingCaptureCMakeError) {
-    this->Values[ct_CAPTURE_CMAKE_ERROR] = arg.c_str();
-    return true;
-  }
-  if (this->ArgumentDoing == ArgumentDoingFiles) {
-    if (cmSystemTools::FileExists(arg)) {
-      this->Files.insert(arg);
-      return true;
-    }
-    std::ostringstream e;
-    e << "File \"" << arg << "\" does not exist. Cannot submit "
-      << "a non-existent file.";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    this->ArgumentDoing = ArgumentDoingError;
-    return false;
-  }
-
-  // Look for other arguments.
-  return this->Superclass::CheckArgumentValue(arg);
-}
diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h
index 0d3b06e..8334a9e 100644
--- a/Source/CTest/cmCTestUploadCommand.h
+++ b/Source/CTest/cmCTestUploadCommand.h
@@ -5,13 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestHandlerCommand.h"
-
-#include <set>
 #include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmCTestHandlerCommand.h"
+#include "cmCommand.h"
 
 class cmCTestGenericHandler;
-class cmCommand;
 
 /** \class cmCTestUpload
  * \brief Run a ctest script
@@ -25,12 +28,12 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmCTestUploadCommand* ni = new cmCTestUploadCommand;
+    auto ni = cm::make_unique<cmCTestUploadCommand>();
     ni->CTest = this->CTest;
     ni->CTestScriptHandler = this->CTestScriptHandler;
-    return ni;
+    return std::unique_ptr<cmCommand>(std::move(ni));
   }
 
   /**
@@ -38,22 +41,12 @@
    */
   std::string GetName() const override { return "ctest_upload"; }
 
-  typedef cmCTestHandlerCommand Superclass;
-
 protected:
+  void BindArguments() override;
+  void CheckArguments(std::vector<std::string> const&) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  bool CheckArgumentKeyword(std::string const& arg) override;
-  bool CheckArgumentValue(std::string const& arg) override;
-
-  enum
-  {
-    ArgumentDoingFiles = Superclass::ArgumentDoingLast1,
-    ArgumentDoingCaptureCMakeError,
-    ArgumentDoingLast2
-  };
-
-  std::set<std::string> Files;
+  std::vector<std::string> Files;
 };
 
 #endif
diff --git a/Source/CTest/cmCTestUploadHandler.cxx b/Source/CTest/cmCTestUploadHandler.cxx
index 9efdf70..9d92aad 100644
--- a/Source/CTest/cmCTestUploadHandler.cxx
+++ b/Source/CTest/cmCTestUploadHandler.cxx
@@ -2,14 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestUploadHandler.h"
 
+#include <ostream>
+#include <string>
+
 #include "cmCTest.h"
 #include "cmGeneratedFileStream.h"
 #include "cmVersion.h"
 #include "cmXMLWriter.h"
 
-#include <ostream>
-#include <string>
-
 cmCTestUploadHandler::cmCTestUploadHandler()
 {
   this->Initialize();
diff --git a/Source/CTest/cmCTestUploadHandler.h b/Source/CTest/cmCTestUploadHandler.h
index 4d8fab4..dde14df 100644
--- a/Source/CTest/cmCTestUploadHandler.h
+++ b/Source/CTest/cmCTestUploadHandler.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCTestGenericHandler.h"
-
 #include <set>
 #include <string>
 
+#include "cmCTestGenericHandler.h"
+
 /** \class cmCTestUploadHandler
  * \brief Helper class for CTest
  *
@@ -19,7 +19,7 @@
 class cmCTestUploadHandler : public cmCTestGenericHandler
 {
 public:
-  typedef cmCTestGenericHandler Superclass;
+  using Superclass = cmCTestGenericHandler;
 
   cmCTestUploadHandler();
 
diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx
index eea41cf..6026c69 100644
--- a/Source/CTest/cmCTestVC.cxx
+++ b/Source/CTest/cmCTestVC.cxx
@@ -2,15 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestVC.h"
 
-#include "cmCTest.h"
-#include "cmSystemTools.h"
-#include "cmXMLWriter.h"
+#include <cstdio>
+#include <ctime>
+#include <sstream>
+#include <vector>
 
 #include "cmsys/Process.h"
-#include <sstream>
-#include <stdio.h>
-#include <time.h>
-#include <vector>
+
+#include "cmCTest.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmXMLWriter.h"
 
 cmCTestVC::cmCTestVC(cmCTest* ct, std::ostream& log)
   : CTest(ct)
@@ -152,8 +154,7 @@
 
   // if update version only is on then do not actually update,
   // just note the current version and finish
-  if (!cmSystemTools::IsOn(
-        this->CTest->GetCTestConfiguration("UpdateVersionOnly"))) {
+  if (!cmIsOn(this->CTest->GetCTestConfiguration("UpdateVersionOnly"))) {
     result = this->NoteOldRevision() && result;
     this->Log << "--- Begin Update ---\n";
     result = this->UpdateImpl() && result;
diff --git a/Source/CTest/cmParseBlanketJSCoverage.cxx b/Source/CTest/cmParseBlanketJSCoverage.cxx
index 63d6a15..409025f 100644
--- a/Source/CTest/cmParseBlanketJSCoverage.cxx
+++ b/Source/CTest/cmParseBlanketJSCoverage.cxx
@@ -2,19 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmParseBlanketJSCoverage.h"
 
+#include <cstdio>
+#include <cstdlib>
+
+#include "cmsys/FStream.hxx"
+
 #include "cmCTest.h"
 #include "cmCTestCoverageHandler.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/FStream.hxx"
-#include <stdio.h>
-#include <stdlib.h>
-
 class cmParseBlanketJSCoverage::JSONParser
 {
 public:
-  typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
-    FileLinesType;
+  using FileLinesType =
+    cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
   JSONParser(cmCTestCoverageHandlerContainer& cont)
     : Coverage(cont)
   {
@@ -110,7 +111,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/cmParseCacheCoverage.cxx b/Source/CTest/cmParseCacheCoverage.cxx
index ca1fe70..8c4da75 100644
--- a/Source/CTest/cmParseCacheCoverage.cxx
+++ b/Source/CTest/cmParseCacheCoverage.cxx
@@ -1,15 +1,17 @@
 #include "cmParseCacheCoverage.h"
 
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <utility>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include <map>
-#include <stdio.h>
-#include <stdlib.h>
-#include <utility>
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 cmParseCacheCoverage::cmParseCacheCoverage(
   cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
@@ -30,9 +32,7 @@
   for (i = 0; i < numf; i++) {
     std::string file = dir.GetFile(i);
     if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) {
-      std::string path = d;
-      path += "/";
-      path += file;
+      std::string path = cmStrCat(d, '/', file);
       if (cmSystemTools::GetFilenameLastExtension(path) == ".cmcov") {
         if (!this->ReadCMCovFile(path.c_str())) {
           return false;
@@ -48,8 +48,7 @@
 {
   // loop over the coverage data computed and remove all files
   // that only have -1 or 0 for the lines.
-  cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator ci =
-    this->Coverage.TotalCoverage.begin();
+  auto ci = this->Coverage.TotalCoverage.begin();
   while (ci != this->Coverage.TotalCoverage.end()) {
     cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v = ci->second;
     bool nothing = true;
diff --git a/Source/CTest/cmParseCacheCoverage.h b/Source/CTest/cmParseCacheCoverage.h
index 081f5fa..e89b9e4 100644
--- a/Source/CTest/cmParseCacheCoverage.h
+++ b/Source/CTest/cmParseCacheCoverage.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmParseMumpsCoverage.h"
-
 #include <string>
 #include <vector>
 
+#include "cmParseMumpsCoverage.h"
+
 class cmCTest;
 class cmCTestCoverageHandlerContainer;
 
diff --git a/Source/CTest/cmParseCoberturaCoverage.cxx b/Source/CTest/cmParseCoberturaCoverage.cxx
index 848a034..05da84e 100644
--- a/Source/CTest/cmParseCoberturaCoverage.cxx
+++ b/Source/CTest/cmParseCoberturaCoverage.cxx
@@ -1,13 +1,15 @@
 #include "cmParseCoberturaCoverage.h"
 
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
-#include "cmXMLParser.h"
+#include <cstdlib>
+#include <cstring>
 
 #include "cmsys/FStream.hxx"
-#include <stdlib.h>
-#include <string.h>
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmXMLParser.h"
 
 class cmParseCoberturaCoverage::XMLParser : public cmXMLParser
 {
@@ -75,7 +77,7 @@
             // Check if this is a path that is relative to our source or
             // binary directories.
             for (std::string const& filePath : FilePaths) {
-              finalpath = filePath + "/" + filename;
+              finalpath = cmStrCat(filePath, "/", filename);
               if (cmSystemTools::FileExists(finalpath)) {
                 this->CurFileName = finalpath;
                 break;
@@ -86,7 +88,7 @@
           cmsys::ifstream fin(this->CurFileName.c_str());
           if (this->CurFileName.empty() || !fin) {
             this->CurFileName =
-              this->Coverage.BinaryDir + "/" + atts[tagCount + 1];
+              cmStrCat(this->Coverage.BinaryDir, "/", atts[tagCount + 1]);
             fin.open(this->CurFileName.c_str());
             if (!fin) {
               cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
@@ -141,8 +143,8 @@
   bool InSource = false;
   bool SkipThisClass = false;
   std::vector<std::string> FilePaths;
-  typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
-    FileLinesType;
+  using FileLinesType =
+    cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
   cmCTest* CTest;
   cmCTestCoverageHandlerContainer& Coverage;
   std::string CurFileName;
diff --git a/Source/CTest/cmParseDelphiCoverage.cxx b/Source/CTest/cmParseDelphiCoverage.cxx
index 9eda6f8..016e90c 100644
--- a/Source/CTest/cmParseDelphiCoverage.cxx
+++ b/Source/CTest/cmParseDelphiCoverage.cxx
@@ -1,19 +1,20 @@
 #include "cmParseDelphiCoverage.h"
 
+#include <cstdio>
+#include <cstdlib>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+
 #include "cmCTest.h"
 #include "cmCTestCoverageHandler.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include <stdio.h>
-#include <stdlib.h>
-
 class cmParseDelphiCoverage::HTMLParser
 {
 public:
-  typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
-    FileLinesType;
+  using FileLinesType =
+    cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
   HTMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
     : CTest(ctest)
     , Coverage(cont)
diff --git a/Source/CTest/cmParseGTMCoverage.cxx b/Source/CTest/cmParseGTMCoverage.cxx
index 0722753..1dc5b70 100644
--- a/Source/CTest/cmParseGTMCoverage.cxx
+++ b/Source/CTest/cmParseGTMCoverage.cxx
@@ -1,16 +1,17 @@
 #include "cmParseGTMCoverage.h"
 
-#include "cmAlgorithms.h"
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <vector>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include <map>
-#include <stdio.h>
-#include <stdlib.h>
-#include <vector>
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 cmParseGTMCoverage::cmParseGTMCoverage(cmCTestCoverageHandlerContainer& cont,
                                        cmCTest* ctest)
@@ -31,9 +32,7 @@
   for (i = 0; i < numf; i++) {
     std::string file = dir.GetFile(i);
     if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) {
-      std::string path = d;
-      path += "/";
-      path += file;
+      std::string path = cmStrCat(d, '/', file);
       if (cmSystemTools::GetFilenameLastExtension(path) == ".mcov") {
         if (!this->ReadMCovFile(path.c_str())) {
           return false;
diff --git a/Source/CTest/cmParseGTMCoverage.h b/Source/CTest/cmParseGTMCoverage.h
index 13afbbc..fe0ae0b 100644
--- a/Source/CTest/cmParseGTMCoverage.h
+++ b/Source/CTest/cmParseGTMCoverage.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmParseMumpsCoverage.h"
-
 #include <string>
 
+#include "cmParseMumpsCoverage.h"
+
 class cmCTest;
 class cmCTestCoverageHandlerContainer;
 
diff --git a/Source/CTest/cmParseJacocoCoverage.cxx b/Source/CTest/cmParseJacocoCoverage.cxx
index b78142a..9cf30df 100644
--- a/Source/CTest/cmParseJacocoCoverage.cxx
+++ b/Source/CTest/cmParseJacocoCoverage.cxx
@@ -1,15 +1,17 @@
 #include "cmParseJacocoCoverage.h"
 
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
-#include "cmXMLParser.h"
+#include <cstdlib>
+#include <cstring>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include <stdlib.h>
-#include <string.h>
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmXMLParser.h"
 
 class cmParseJacocoCoverage::XMLParser : public cmXMLParser
 {
@@ -103,9 +105,7 @@
                                 std::string const& baseDir)
   {
     // Search for the file in the baseDir and its subdirectories.
-    std::string packageGlob = baseDir;
-    packageGlob += "/";
-    packageGlob += fileName;
+    std::string packageGlob = cmStrCat(baseDir, '/', fileName);
     cmsys::Glob gl;
     gl.RecurseOn();
     gl.RecurseThroughSymlinksOn();
@@ -118,7 +118,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,
@@ -134,8 +134,8 @@
   std::string FilePath;
   std::string PackagePath;
   std::string PackageName;
-  typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
-    FileLinesType;
+  using FileLinesType =
+    cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
   cmCTest* CTest;
   cmCTestCoverageHandlerContainer& Coverage;
 };
diff --git a/Source/CTest/cmParseMumpsCoverage.cxx b/Source/CTest/cmParseMumpsCoverage.cxx
index 4a81ee4..b16f101 100644
--- a/Source/CTest/cmParseMumpsCoverage.cxx
+++ b/Source/CTest/cmParseMumpsCoverage.cxx
@@ -1,16 +1,18 @@
 #include "cmParseMumpsCoverage.h"
 
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
-
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
 #include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 cmParseMumpsCoverage::cmParseMumpsCoverage(
   cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
   : Coverage(cont)
@@ -107,8 +109,7 @@
 {
   cmsys::Glob glob;
   glob.RecurseOn();
-  std::string pat = d;
-  pat += "/*.m";
+  std::string pat = cmStrCat(d, "/*.m");
   glob.FindFiles(pat);
   for (std::string& file : glob.GetFiles()) {
     std::string name = cmSystemTools::GetFilenameName(file);
@@ -123,8 +124,7 @@
 bool cmParseMumpsCoverage::FindMumpsFile(std::string const& routine,
                                          std::string& filepath)
 {
-  std::map<std::string, std::string>::iterator i =
-    this->RoutineToDirectory.find(routine);
+  auto i = this->RoutineToDirectory.find(routine);
   if (i != this->RoutineToDirectory.end()) {
     filepath = i->second;
     return true;
diff --git a/Source/CTest/cmParsePHPCoverage.cxx b/Source/CTest/cmParsePHPCoverage.cxx
index a6e65c9..a494b92 100644
--- a/Source/CTest/cmParsePHPCoverage.cxx
+++ b/Source/CTest/cmParsePHPCoverage.cxx
@@ -1,13 +1,15 @@
 #include "cmParsePHPCoverage.h"
 
-#include "cmCTest.h"
-#include "cmCTestCoverageHandler.h"
-#include "cmSystemTools.h"
+#include <cstdlib>
+#include <cstring>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include <stdlib.h>
-#include <string.h>
+
+#include "cmCTest.h"
+#include "cmCTestCoverageHandler.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 /*
   To setup coverage for php.
@@ -210,9 +212,7 @@
   for (i = 0; i < numf; i++) {
     std::string file = dir.GetFile(i);
     if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) {
-      std::string path = d;
-      path += "/";
-      path += file;
+      std::string path = cmStrCat(d, '/', file);
       if (!this->ReadPHPData(path.c_str())) {
         return false;
       }
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx
index 7a3b82e..87f7147 100644
--- a/Source/CTest/cmProcess.cxx
+++ b/Source/CTest/cmProcess.cxx
@@ -2,16 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcess.h"
 
+#include <csignal>
+#include <iostream>
+#include <string>
+
+#include "cmsys/Process.h"
+
 #include "cmAlgorithms.h"
 #include "cmCTest.h"
 #include "cmCTestRunTest.h"
 #include "cmCTestTestHandler.h"
 #include "cmGetPipes.h"
-#include "cmsys/Process.h"
-
-#include <iostream>
-#include <signal.h>
-#include <string>
+#include "cmStringAlgorithms.h"
 #if defined(_WIN32)
 #  include "cm_kwiml.h"
 #endif
@@ -694,8 +696,7 @@
 #    endif
 #  endif
     default:
-      exception_str = "Signal ";
-      exception_str += std::to_string(this->Signal);
+      exception_str = cmStrCat("Signal ", this->Signal);
   }
 #endif
   return exception_str;
diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h
index a0a4b6b..2c24f2d 100644
--- a/Source/CTest/cmProcess.h
+++ b/Source/CTest/cmProcess.h
@@ -4,18 +4,20 @@
 #define cmProcess_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmDuration.h"
-
-#include "cmProcessOutput.h"
-#include "cmUVHandlePtr.h"
-#include "cm_uv.h"
 
 #include <chrono>
-#include <stddef.h>
-#include <stdint.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include "cm_uv.h"
+
+#include "cmDuration.h"
+#include "cmProcessOutput.h"
+#include "cmUVHandlePtr.h"
+
 class cmCTestRunTest;
 
 /** \class cmProcess
diff --git a/Source/Checks/cm_cxx17_check.cpp b/Source/Checks/cm_cxx17_check.cpp
index 593d9b2..29863b1 100644
--- a/Source/Checks/cm_cxx17_check.cpp
+++ b/Source/Checks/cm_cxx17_check.cpp
@@ -1,6 +1,7 @@
 #include <cstdio>
 #include <iterator>
 #include <memory>
+#include <optional>
 #include <unordered_map>
 
 #ifdef _MSC_VER
@@ -27,5 +28,7 @@
   IDispatchPtr disp(ptr);
 #endif
 
-  return *u + *ai + *(bi - 1) + (3 - static_cast<int>(ci));
+  std::optional<int> oi = 0;
+
+  return *u + *ai + *(bi - 1) + (3 - static_cast<int>(ci)) + oi.value();
 }
diff --git a/Source/CursesDialog/CMakeLists.txt b/Source/CursesDialog/CMakeLists.txt
index c51b0dd..a9e46fd5 100644
--- a/Source/CursesDialog/CMakeLists.txt
+++ b/Source/CursesDialog/CMakeLists.txt
@@ -1,28 +1,28 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-set( CURSES_SRCS
-              CursesDialog/cmCursesOptionsWidget.cxx
-              CursesDialog/cmCursesBoolWidget.cxx
-              CursesDialog/cmCursesCacheEntryComposite.cxx
-              CursesDialog/cmCursesDummyWidget.cxx
-              CursesDialog/cmCursesFilePathWidget.cxx
-              CursesDialog/cmCursesForm.cxx
-              CursesDialog/cmCursesLabelWidget.cxx
-              CursesDialog/cmCursesLongMessageForm.cxx
-              CursesDialog/cmCursesMainForm.cxx
-              CursesDialog/cmCursesPathWidget.cxx
-              CursesDialog/cmCursesStringWidget.cxx
-              CursesDialog/cmCursesWidget.cxx
-              CursesDialog/ccmake.cxx
-   )
-
-include_directories(${CURSES_INCLUDE_PATH})
-
-
-add_executable(ccmake ${CURSES_SRCS} )
+add_executable(ccmake
+  ccmake.cxx
+  cmCursesBoolWidget.cxx
+  cmCursesCacheEntryComposite.cxx
+  cmCursesDummyWidget.cxx
+  cmCursesFilePathWidget.cxx
+  cmCursesForm.cxx
+  cmCursesLabelWidget.cxx
+  cmCursesLongMessageForm.cxx
+  cmCursesMainForm.cxx
+  cmCursesOptionsWidget.cxx
+  cmCursesPathWidget.cxx
+  cmCursesStringWidget.cxx
+  cmCursesWidget.cxx
+  )
+target_include_directories(ccmake PRIVATE ${CURSES_INCLUDE_PATH})
 target_link_libraries(ccmake CMakeLib)
 if(CMAKE_USE_SYSTEM_FORM)
+  find_path(CURSES_FORM_INCLUDE_DIR NAMES form.h HINTS ${CURSES_INCLUDE_PATH} ${CURSES_INCLUDE_PATH}/ncurses)
+  if(CURSES_FORM_INCLUDE_DIR)
+    target_include_directories(ccmake PRIVATE ${CURSES_FORM_INCLUDE_DIR})
+  endif()
   target_link_libraries(ccmake
     ${CURSES_FORM_LIBRARY}
     ${CURSES_LIBRARY}
@@ -34,5 +34,9 @@
   target_link_libraries(ccmake cmForm)
 endif()
 
+if(CMake_JOB_POOL_LINK_BIN)
+  set_property(TARGET ccmake PROPERTY JOB_POOL_LINK "link-bin")
+endif()
+
 CMake_OPTIONAL_COMPONENT(ccmake)
 install(TARGETS ccmake DESTINATION ${CMAKE_BIN_DIR} ${COMPONENT})
diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx
index 7caed0c..9e9dfbd 100644
--- a/Source/CursesDialog/ccmake.cxx
+++ b/Source/CursesDialog/ccmake.cxx
@@ -1,6 +1,14 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
+#include <csignal>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "cmsys/Encoding.hxx"
+
 #include "cmCursesForm.h"
 #include "cmCursesMainForm.h"
 #include "cmCursesStandardIncludes.h"
@@ -10,13 +18,6 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include "cmsys/Encoding.hxx"
-#include <iostream>
-#include <signal.h>
-#include <string.h>
-#include <string>
-#include <vector>
-
 static const char* cmDocumentationName[][2] = {
   { nullptr, "  ccmake - Curses Interface for CMake." },
   { nullptr, nullptr }
@@ -56,7 +57,8 @@
     cbreak();             /* nl- or cr not needed */
     keypad(stdscr, true); /* Use key symbols as KEY_DOWN */
     refresh();
-    int x, y;
+    int x;
+    int y;
     getmaxyx(stdscr, y, x);
     cmCursesForm::CurrentForm->Render(1, 1, x, y);
     cmCursesForm::CurrentForm->UpdateStatusBar();
@@ -127,7 +129,8 @@
 
   signal(SIGWINCH, onsig);
 
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
   if (x < cmCursesMainForm::MIN_WIDTH || y < cmCursesMainForm::MIN_HEIGHT) {
     endwin();
diff --git a/Source/CursesDialog/cmCursesBoolWidget.cxx b/Source/CursesDialog/cmCursesBoolWidget.cxx
index 80a5b5b..97b0811 100644
--- a/Source/CursesDialog/cmCursesBoolWidget.cxx
+++ b/Source/CursesDialog/cmCursesBoolWidget.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesBoolWidget.h"
 
+#include <string>
+
 #include "cmCursesWidget.h"
 #include "cmStateTypes.h"
 
-#include <string>
-
 cmCursesBoolWidget::cmCursesBoolWidget(int width, int height, int left,
                                        int top)
   : cmCursesWidget(width, height, left, top)
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
index c1dd591..4d3131b 100644
--- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
+++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
@@ -2,6 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesCacheEntryComposite.h"
 
+#include <cassert>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
 #include "cmCursesBoolWidget.h"
 #include "cmCursesFilePathWidget.h"
 #include "cmCursesLabelWidget.h"
@@ -11,11 +17,8 @@
 #include "cmCursesWidget.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmake.h"
-
-#include <assert.h>
-#include <vector>
 
 cmCursesCacheEntryComposite::cmCursesCacheEntryComposite(
   const std::string& key, int labelwidth, int entrywidth)
@@ -23,62 +26,65 @@
   , LabelWidth(labelwidth)
   , EntryWidth(entrywidth)
 {
-  this->Label = new cmCursesLabelWidget(this->LabelWidth, 1, 1, 1, key);
-  this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, " ");
-  this->Entry = nullptr;
-  this->Entry = new cmCursesStringWidget(this->EntryWidth, 1, 1, 1);
+  this->Label =
+    cm::make_unique<cmCursesLabelWidget>(this->LabelWidth, 1, 1, 1, key);
+  this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, " ");
+  this->Entry =
+    cm::make_unique<cmCursesStringWidget>(this->EntryWidth, 1, 1, 1);
 }
 
 cmCursesCacheEntryComposite::cmCursesCacheEntryComposite(
-  const std::string& key, cmake* cm, bool isNew, int labelwidth,
+  const std::string& key, cmState* state, bool isNew, int labelwidth,
   int entrywidth)
   : Key(key)
   , LabelWidth(labelwidth)
   , EntryWidth(entrywidth)
 {
-  this->Label = new cmCursesLabelWidget(this->LabelWidth, 1, 1, 1, key);
+  this->Label =
+    cm::make_unique<cmCursesLabelWidget>(this->LabelWidth, 1, 1, 1, key);
   if (isNew) {
-    this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, "*");
+    this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, "*");
   } else {
-    this->IsNewLabel = new cmCursesLabelWidget(1, 1, 1, 1, " ");
+    this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, " ");
   }
 
-  this->Entry = nullptr;
-  const char* value = cm->GetState()->GetCacheEntryValue(key);
+  const char* value = state->GetCacheEntryValue(key);
   assert(value);
-  switch (cm->GetState()->GetCacheEntryType(key)) {
-    case cmStateEnums::BOOL:
-      this->Entry = new cmCursesBoolWidget(this->EntryWidth, 1, 1, 1);
-      if (cmSystemTools::IsOn(value)) {
-        static_cast<cmCursesBoolWidget*>(this->Entry)->SetValueAsBool(true);
-      } else {
-        static_cast<cmCursesBoolWidget*>(this->Entry)->SetValueAsBool(false);
-      }
+  switch (state->GetCacheEntryType(key)) {
+    case cmStateEnums::BOOL: {
+      auto bw = cm::make_unique<cmCursesBoolWidget>(this->EntryWidth, 1, 1, 1);
+      bw->SetValueAsBool(cmIsOn(value));
+      this->Entry = std::move(bw);
       break;
-    case cmStateEnums::PATH:
-      this->Entry = new cmCursesPathWidget(this->EntryWidth, 1, 1, 1);
-      static_cast<cmCursesPathWidget*>(this->Entry)->SetString(value);
+    }
+    case cmStateEnums::PATH: {
+      auto pw = cm::make_unique<cmCursesPathWidget>(this->EntryWidth, 1, 1, 1);
+      pw->SetString(value);
+      this->Entry = std::move(pw);
       break;
-    case cmStateEnums::FILEPATH:
-      this->Entry = new cmCursesFilePathWidget(this->EntryWidth, 1, 1, 1);
-      static_cast<cmCursesFilePathWidget*>(this->Entry)->SetString(value);
+    }
+    case cmStateEnums::FILEPATH: {
+      auto fpw =
+        cm::make_unique<cmCursesFilePathWidget>(this->EntryWidth, 1, 1, 1);
+      fpw->SetString(value);
+      this->Entry = std::move(fpw);
       break;
+    }
     case cmStateEnums::STRING: {
-      const char* stringsProp =
-        cm->GetState()->GetCacheEntryProperty(key, "STRINGS");
+      const char* stringsProp = state->GetCacheEntryProperty(key, "STRINGS");
       if (stringsProp) {
-        cmCursesOptionsWidget* ow =
-          new cmCursesOptionsWidget(this->EntryWidth, 1, 1, 1);
-        this->Entry = ow;
-        std::vector<std::string> options;
-        cmSystemTools::ExpandListArgument(stringsProp, options);
-        for (auto const& opt : options) {
+        auto ow =
+          cm::make_unique<cmCursesOptionsWidget>(this->EntryWidth, 1, 1, 1);
+        for (std::string const& opt : cmExpandedList(stringsProp)) {
           ow->AddOption(opt);
         }
         ow->SetOption(value);
+        this->Entry = std::move(ow);
       } else {
-        this->Entry = new cmCursesStringWidget(this->EntryWidth, 1, 1, 1);
-        static_cast<cmCursesStringWidget*>(this->Entry)->SetString(value);
+        auto sw =
+          cm::make_unique<cmCursesStringWidget>(this->EntryWidth, 1, 1, 1);
+        sw->SetString(value);
+        this->Entry = std::move(sw);
       }
       break;
     }
@@ -91,12 +97,7 @@
   }
 }
 
-cmCursesCacheEntryComposite::~cmCursesCacheEntryComposite()
-{
-  delete this->Label;
-  delete this->IsNewLabel;
-  delete this->Entry;
-}
+cmCursesCacheEntryComposite::~cmCursesCacheEntryComposite() = default;
 
 const char* cmCursesCacheEntryComposite::GetValue()
 {
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.h b/Source/CursesDialog/cmCursesCacheEntryComposite.h
index 0a69d3a..a711363 100644
--- a/Source/CursesDialog/cmCursesCacheEntryComposite.h
+++ b/Source/CursesDialog/cmCursesCacheEntryComposite.h
@@ -5,33 +5,38 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <memory>
 #include <string>
 
 class cmCursesLabelWidget;
 class cmCursesWidget;
-class cmake;
+class cmState;
 
 class cmCursesCacheEntryComposite
 {
 public:
   cmCursesCacheEntryComposite(const std::string& key, int labelwidth,
                               int entrywidth);
-  cmCursesCacheEntryComposite(const std::string& key, cmake* cm, bool isNew,
-                              int labelwidth, int entrywidth);
+  cmCursesCacheEntryComposite(const std::string& key, cmState* state,
+                              bool isNew, int labelwidth, int entrywidth);
   ~cmCursesCacheEntryComposite();
 
   cmCursesCacheEntryComposite(cmCursesCacheEntryComposite const&) = delete;
   cmCursesCacheEntryComposite& operator=(cmCursesCacheEntryComposite const&) =
     delete;
 
+  cmCursesCacheEntryComposite(cmCursesCacheEntryComposite&&) = default;
+  cmCursesCacheEntryComposite& operator=(cmCursesCacheEntryComposite&&) =
+    default;
+
   const char* GetValue();
 
   friend class cmCursesMainForm;
 
 protected:
-  cmCursesLabelWidget* Label;
-  cmCursesLabelWidget* IsNewLabel;
-  cmCursesWidget* Entry;
+  std::unique_ptr<cmCursesLabelWidget> Label;
+  std::unique_ptr<cmCursesLabelWidget> IsNewLabel;
+  std::unique_ptr<cmCursesWidget> Entry;
   std::string Key;
   int LabelWidth;
   int EntryWidth;
diff --git a/Source/CursesDialog/cmCursesForm.h b/Source/CursesDialog/cmCursesForm.h
index 7842905..e3626e6 100644
--- a/Source/CursesDialog/cmCursesForm.h
+++ b/Source/CursesDialog/cmCursesForm.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCursesStandardIncludes.h"
+#include <string>
 
 #include "cmsys/FStream.hxx"
 
-#include <string>
+#include "cmCursesStandardIncludes.h"
 
 class cmCursesForm
 {
diff --git a/Source/CursesDialog/cmCursesLabelWidget.h b/Source/CursesDialog/cmCursesLabelWidget.h
index 2ee9cfc..9e75681 100644
--- a/Source/CursesDialog/cmCursesLabelWidget.h
+++ b/Source/CursesDialog/cmCursesLabelWidget.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+
 #include "cmCursesStandardIncludes.h"
 #include "cmCursesWidget.h"
 
-#include <string>
-
 class cmCursesMainForm;
 
 class cmCursesLabelWidget : public cmCursesWidget
diff --git a/Source/CursesDialog/cmCursesLongMessageForm.cxx b/Source/CursesDialog/cmCursesLongMessageForm.cxx
index 4e887d6..e2d8d06 100644
--- a/Source/CursesDialog/cmCursesLongMessageForm.cxx
+++ b/Source/CursesDialog/cmCursesLongMessageForm.cxx
@@ -2,14 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesLongMessageForm.h"
 
+#include <cstdio>
+#include <cstring>
+
 #include "cmCursesForm.h"
 #include "cmCursesMainForm.h"
 #include "cmCursesStandardIncludes.h"
 #include "cmVersion.h"
 
-#include <stdio.h>
-#include <string.h>
-
 inline int ctrl(int z)
 {
   return (z & 037);
@@ -38,7 +38,8 @@
 
 void cmCursesLongMessageForm::UpdateStatusBar()
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
 
   char bar[cmCursesMainForm::MAX_WIDTH];
@@ -81,7 +82,8 @@
 
 void cmCursesLongMessageForm::PrintKeys()
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
   if (x < cmCursesMainForm::MIN_WIDTH || y < cmCursesMainForm::MIN_HEIGHT) {
     return;
@@ -98,7 +100,8 @@
 void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/,
                                      int /*height*/)
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
 
   if (this->Form) {
diff --git a/Source/CursesDialog/cmCursesLongMessageForm.h b/Source/CursesDialog/cmCursesLongMessageForm.h
index 466b4e1..42f9c71 100644
--- a/Source/CursesDialog/cmCursesLongMessageForm.h
+++ b/Source/CursesDialog/cmCursesLongMessageForm.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCursesForm.h"
-#include "cmCursesStandardIncludes.h"
-
 #include <string>
 #include <vector>
 
+#include "cmCursesForm.h"
+#include "cmCursesStandardIncludes.h"
+
 class cmCursesLongMessageForm : public cmCursesForm
 {
 public:
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index 028e852..ad3debb 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -2,7 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesMainForm.h"
 
-#include "cmAlgorithms.h"
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmCursesCacheEntryComposite.h"
 #include "cmCursesDummyWidget.h"
 #include "cmCursesForm.h"
@@ -13,15 +19,11 @@
 #include "cmCursesWidget.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <stdio.h>
-#include <string.h>
-#include <utility>
-
 inline int ctrl(int z)
 {
   return (z & 037);
@@ -33,8 +35,6 @@
   , InitialWidth(initWidth)
 {
   this->NumberOfPages = 0;
-  this->Fields = nullptr;
-  this->Entries = nullptr;
   this->AdvancedMode = false;
   this->NumberOfVisibleEntries = 0;
   this->OkToGenerate = false;
@@ -42,17 +42,16 @@
     "Welcome to ccmake, curses based user interface for CMake.");
   this->HelpMessage.emplace_back();
   this->HelpMessage.emplace_back(s_ConstHelpMessage);
-  this->CMakeInstance = new cmake(cmake::RoleProject, cmState::Project);
+  this->CMakeInstance =
+    cm::make_unique<cmake>(cmake::RoleProject, cmState::Project);
   this->CMakeInstance->SetCMakeEditCommand(
     cmSystemTools::GetCMakeCursesCommand());
 
   // create the arguments for the cmake object
-  std::string whereCMake = cmSystemTools::GetProgramPath(this->Args[0]);
-  whereCMake += "/cmake";
+  std::string whereCMake =
+    cmStrCat(cmSystemTools::GetProgramPath(this->Args[0]), "/cmake");
   this->Args[0] = whereCMake;
   this->CMakeInstance->SetArgs(this->Args);
-  this->SearchString = "";
-  this->OldSearchString = "";
   this->SearchMode = false;
 }
 
@@ -63,27 +62,15 @@
     free_form(this->Form);
     this->Form = nullptr;
   }
-  delete[] this->Fields;
-
-  // Clean-up composites
-  if (this->Entries) {
-    cmDeleteAll(*this->Entries);
-  }
-  delete this->Entries;
-  if (this->CMakeInstance) {
-    delete this->CMakeInstance;
-    this->CMakeInstance = nullptr;
-  }
 }
 
 // See if a cache entry is in the list of entries in the ui.
 bool cmCursesMainForm::LookForCacheEntry(const std::string& key)
 {
-  return this->Entries &&
-    std::any_of(this->Entries->begin(), this->Entries->end(),
-                [&key](cmCursesCacheEntryComposite* entry) {
-                  return key == entry->Key;
-                });
+  return std::any_of(this->Entries.begin(), this->Entries.end(),
+                     [&key](cmCursesCacheEntryComposite const& entry) {
+                       return key == entry.Key;
+                     });
 }
 
 // Create new cmCursesCacheEntryComposite entries from the cache
@@ -91,11 +78,10 @@
 {
   // Create a vector of cmCursesCacheEntryComposite's
   // which contain labels, entries and new entry markers
-  std::vector<cmCursesCacheEntryComposite*>* newEntries =
-    new std::vector<cmCursesCacheEntryComposite*>;
+  std::vector<cmCursesCacheEntryComposite> newEntries;
   std::vector<std::string> cacheKeys =
     this->CMakeInstance->GetState()->GetCacheEntryKeys();
-  newEntries->reserve(cacheKeys.size());
+  newEntries.reserve(cacheKeys.size());
 
   // Count non-internal and non-static entries
   int count = 0;
@@ -111,13 +97,12 @@
 
   int entrywidth = this->InitialWidth - 35;
 
-  cmCursesCacheEntryComposite* comp;
   if (count == 0) {
     // If cache is empty, display a label saying so and a
     // dummy entry widget (does not respond to input)
-    comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
-    comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1);
-    newEntries->push_back(comp);
+    cmCursesCacheEntryComposite comp("EMPTY CACHE", 30, 30);
+    comp.Entry = cm::make_unique<cmCursesDummyWidget>(1, 1, 1, 1);
+    newEntries.emplace_back(std::move(comp));
   } else {
     // Create the composites.
 
@@ -131,8 +116,8 @@
       }
 
       if (!this->LookForCacheEntry(key)) {
-        newEntries->push_back(new cmCursesCacheEntryComposite(
-          key, this->CMakeInstance, true, 30, entrywidth));
+        newEntries.emplace_back(key, this->CMakeInstance->GetState(), true, 30,
+                                entrywidth);
         this->OkToGenerate = false;
       }
     }
@@ -147,18 +132,14 @@
       }
 
       if (this->LookForCacheEntry(key)) {
-        newEntries->push_back(new cmCursesCacheEntryComposite(
-          key, this->CMakeInstance, false, 30, entrywidth));
+        newEntries.emplace_back(key, this->CMakeInstance->GetState(), false,
+                                30, entrywidth);
       }
     }
   }
 
-  // Clean old entries
-  if (this->Entries) {
-    cmDeleteAll(*this->Entries);
-  }
-  delete this->Entries;
-  this->Entries = newEntries;
+  // Replace old entries
+  this->Entries = std::move(newEntries);
 
   // Compute fields from composites
   this->RePost();
@@ -172,18 +153,18 @@
     free_form(this->Form);
     this->Form = nullptr;
   }
-  delete[] this->Fields;
+  this->Fields.clear();
   if (this->AdvancedMode) {
-    this->NumberOfVisibleEntries = this->Entries->size();
+    this->NumberOfVisibleEntries = this->Entries.size();
   } else {
     // If normal mode, count only non-advanced entries
     this->NumberOfVisibleEntries = 0;
-    for (cmCursesCacheEntryComposite* entry : *this->Entries) {
+    for (cmCursesCacheEntryComposite& entry : this->Entries) {
       const char* existingValue =
-        this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue());
+        this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
-          entry->GetValue(), "ADVANCED");
+          entry.GetValue(), "ADVANCED");
       if (!existingValue || (!this->AdvancedMode && advanced)) {
         continue;
       }
@@ -196,38 +177,32 @@
   }
   // Assign the fields: 3 for each entry: label, new entry marker
   // ('*' or ' ') and entry widget
-  this->Fields = new FIELD*[3 * this->NumberOfVisibleEntries + 1];
-  size_t cc;
-  for (cc = 0; cc < 3 * this->NumberOfVisibleEntries + 1; cc++) {
-    this->Fields[cc] = nullptr;
-  }
+  this->Fields.reserve(3 * this->NumberOfVisibleEntries + 1);
 
   // Assign fields
-  int j = 0;
-  for (cmCursesCacheEntryComposite* entry : *this->Entries) {
+  for (cmCursesCacheEntryComposite& entry : this->Entries) {
     const char* existingValue =
-      this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue());
+      this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
     bool advanced =
       this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
-        entry->GetValue(), "ADVANCED");
+        entry.GetValue(), "ADVANCED");
     if (!existingValue || (!this->AdvancedMode && advanced)) {
       continue;
     }
-    this->Fields[3 * j] = entry->Label->Field;
-    this->Fields[3 * j + 1] = entry->IsNewLabel->Field;
-    this->Fields[3 * j + 2] = entry->Entry->Field;
-    j++;
+    this->Fields.push_back(entry.Label->Field);
+    this->Fields.push_back(entry.IsNewLabel->Field);
+    this->Fields.push_back(entry.Entry->Field);
   }
   // if no cache entries there should still be one dummy field
-  if (j == 0) {
-    const auto& front = *this->Entries->front();
-    this->Fields[0] = front.Label->Field;
-    this->Fields[1] = front.IsNewLabel->Field;
-    this->Fields[2] = front.Entry->Field;
+  if (this->Fields.empty()) {
+    const auto& front = this->Entries.front();
+    this->Fields.push_back(front.Label->Field);
+    this->Fields.push_back(front.IsNewLabel->Field);
+    this->Fields.push_back(front.Entry->Field);
     this->NumberOfVisibleEntries = 1;
   }
   // Has to be null terminated.
-  this->Fields[3 * this->NumberOfVisibleEntries] = nullptr;
+  this->Fields.push_back(nullptr);
 }
 
 void cmCursesMainForm::Render(int left, int top, int width, int height)
@@ -260,16 +235,16 @@
   height -= 7;
 
   if (this->AdvancedMode) {
-    this->NumberOfVisibleEntries = this->Entries->size();
+    this->NumberOfVisibleEntries = this->Entries.size();
   } else {
     // If normal, display only non-advanced entries
     this->NumberOfVisibleEntries = 0;
-    for (cmCursesCacheEntryComposite* entry : *this->Entries) {
+    for (cmCursesCacheEntryComposite& entry : this->Entries) {
       const char* existingValue =
-        this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue());
+        this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
-          entry->GetValue(), "ADVANCED");
+          entry.GetValue(), "ADVANCED");
       if (!existingValue || (!this->AdvancedMode && advanced)) {
         continue;
       }
@@ -282,12 +257,12 @@
   if (height > 0) {
     bool isNewPage;
     int i = 0;
-    for (cmCursesCacheEntryComposite* entry : *this->Entries) {
+    for (cmCursesCacheEntryComposite& entry : this->Entries) {
       const char* existingValue =
-        this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue());
+        this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
-          entry->GetValue(), "ADVANCED");
+          entry.GetValue(), "ADVANCED");
       if (!existingValue || (!this->AdvancedMode && advanced)) {
         continue;
       }
@@ -298,16 +273,16 @@
       if (isNewPage) {
         this->NumberOfPages++;
       }
-      entry->Label->Move(left, top + row - 1, isNewPage);
-      entry->IsNewLabel->Move(left + 32, top + row - 1, false);
-      entry->Entry->Move(left + 33, top + row - 1, false);
-      entry->Entry->SetPage(this->NumberOfPages);
+      entry.Label->Move(left, top + row - 1, isNewPage);
+      entry.IsNewLabel->Move(left + 32, top + row - 1, false);
+      entry.Entry->Move(left + 33, top + row - 1, false);
+      entry.Entry->SetPage(this->NumberOfPages);
       i++;
     }
   }
 
   // Post the form
-  this->Form = new_form(this->Fields);
+  this->Form = new_form(this->Fields.data());
   post_form(this->Form);
   // Update toolbar
   this->UpdateStatusBar();
@@ -319,7 +294,8 @@
 
 void cmCursesMainForm::PrintKeys(int process /* = 0 */)
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
   if (x < cmCursesMainForm::MIN_WIDTH || x < this->InitialWidth ||
       y < cmCursesMainForm::MIN_HEIGHT) {
@@ -390,7 +366,8 @@
 // on the status bar. Designed for a width of 80 chars.
 void cmCursesMainForm::UpdateStatusBar(const char* message)
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
   // If window size is too small, display error and return
   if (x < cmCursesMainForm::MIN_WIDTH || x < this->InitialWidth ||
@@ -508,7 +485,8 @@
 
 int cmCursesMainForm::Configure(int noconfigure)
 {
-  int xi, yi;
+  int xi;
+  int yi;
   getmaxyx(stdscr, yi, xi);
 
   curses_move(1, 1);
@@ -551,7 +529,8 @@
     if (cmSystemTools::GetErrorOccuredFlag()) {
       this->OkToGenerate = false;
     }
-    int xx, yy;
+    int xx;
+    int yy;
     getmaxyx(stdscr, yy, xx);
     cmCursesLongMessageForm* msgs =
       new cmCursesLongMessageForm(this->Errors,
@@ -580,7 +559,8 @@
 
 int cmCursesMainForm::Generate()
 {
-  int xi, yi;
+  int xi;
+  int yi;
   getmaxyx(stdscr, yi, xi);
 
   curses_move(1, 1);
@@ -609,7 +589,8 @@
     }
     // reset error condition
     cmSystemTools::ResetErrorOccuredFlag();
-    int xx, yy;
+    int xx;
+    int yy;
     getmaxyx(stdscr, yy, xx);
     const char* title = "Messages during last pass.";
     if (cmSystemTools::GetErrorOccuredFlag()) {
@@ -648,28 +629,28 @@
   }
 
   auto removeIt =
-    std::find_if(this->Entries->begin(), this->Entries->end(),
-                 [value](cmCursesCacheEntryComposite* entry) -> bool {
-                   const char* val = entry->GetValue();
+    std::find_if(this->Entries.begin(), this->Entries.end(),
+                 [value](cmCursesCacheEntryComposite& entry) -> bool {
+                   const char* val = entry.GetValue();
                    return val != nullptr && !strcmp(value, val);
                  });
 
-  if (removeIt != this->Entries->end()) {
+  if (removeIt != this->Entries.end()) {
     this->CMakeInstance->UnwatchUnusedCli(value);
-    this->Entries->erase(removeIt);
+    this->Entries.erase(removeIt);
   }
 }
 
 // copy from the list box to the cache manager
 void cmCursesMainForm::FillCacheManagerFromUI()
 {
-  for (cmCursesCacheEntryComposite* entry : *this->Entries) {
-    const std::string& cacheKey = entry->Key;
+  for (cmCursesCacheEntryComposite& entry : this->Entries) {
+    const std::string& cacheKey = entry.Key;
     const char* existingValue =
       this->CMakeInstance->GetState()->GetCacheEntryValue(cacheKey);
     if (existingValue) {
       std::string oldValue = existingValue;
-      std::string newValue = entry->Entry->GetValue();
+      std::string newValue = entry.Entry->GetValue();
       std::string fixedOldValue;
       std::string fixedNewValue;
       cmStateEnums::CacheEntryType t =
@@ -696,7 +677,7 @@
     cmSystemTools::ConvertToUnixSlashes(out);
   }
   if (type == cmStateEnums::BOOL) {
-    if (cmSystemTools::IsOff(out)) {
+    if (cmIsOff(out)) {
       out = "OFF";
     } else {
       out = "ON";
@@ -706,7 +687,8 @@
 
 void cmCursesMainForm::HandleInput()
 {
-  int x = 0, y = 0;
+  int x = 0;
+  int y = 0;
 
   if (!this->Form) {
     return;
@@ -754,7 +736,7 @@
           this->JumpToCacheEntry(this->SearchString.c_str());
           this->OldSearchString = this->SearchString;
         }
-        this->SearchString = "";
+        this->SearchString.clear();
       }
       /*
       else if ( key == KEY_ESCAPE )
@@ -770,7 +752,7 @@
         }
       } else if (key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC) {
         if (!this->SearchString.empty()) {
-          this->SearchString.resize(this->SearchString.size() - 1);
+          this->SearchString.pop_back();
         }
       }
     } else if (currentWidget && !this->SearchMode) {
@@ -859,17 +841,9 @@
             curField, "HELPSTRING");
         }
         if (helpString) {
-          char* message = new char
-            [strlen(curField) + strlen(helpString) +
-             strlen(
-               "Current option is: \n Help string for this option is: \n") +
-             10];
-          sprintf(
-            message,
-            "Current option is: %s\nHelp string for this option is: %s\n",
-            curField, helpString);
-          this->HelpMessage[1] = message;
-          delete[] message;
+          this->HelpMessage[1] =
+            cmStrCat("Current option is: ", curField, '\n',
+                     "Help string for this option is: ", helpString, '\n');
         } else {
           this->HelpMessage[1] = "";
         }
@@ -965,14 +939,14 @@
 
           if (nextCur) {
             // make the next or prev. current field after deletion
-            auto nextEntryIt =
-              std::find_if(this->Entries->begin(), this->Entries->end(),
-                           [&nextVal](cmCursesCacheEntryComposite* entry) {
-                             return nextVal == entry->Key;
-                           });
+            auto nextEntryIt = std::find_if(
+              this->Entries.begin(), this->Entries.end(),
+              [&nextVal](cmCursesCacheEntryComposite const& entry) {
+                return nextVal == entry.Key;
+              });
 
-            if (nextEntryIt != this->Entries->end()) {
-              set_current_field(this->Form, (*nextEntryIt)->Entry->Field);
+            if (nextEntryIt != this->Entries.end()) {
+              set_current_field(this->Form, nextEntryIt->Entry->Field);
             }
           }
         }
diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h
index d379975..b8769b7 100644
--- a/Source/CursesDialog/cmCursesMainForm.h
+++ b/Source/CursesDialog/cmCursesMainForm.h
@@ -5,15 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmCursesCacheEntryComposite.h"
 #include "cmCursesForm.h"
 #include "cmCursesStandardIncludes.h"
 #include "cmStateTypes.h"
 
-#include <stddef.h>
-#include <string>
-#include <vector>
-
-class cmCursesCacheEntryComposite;
 class cmake;
 
 /** \class cmCursesMainForm
@@ -122,7 +123,7 @@
   void JumpToCacheEntry(const char* str);
 
   // Copies of cache entries stored in the user interface
-  std::vector<cmCursesCacheEntryComposite*>* Entries;
+  std::vector<cmCursesCacheEntryComposite> Entries;
   // Errors produced during last run of cmake
   std::vector<std::string> Errors;
   // Command line arguments to be passed to cmake each time
@@ -136,11 +137,7 @@
   static const char* s_ConstHelpMessage;
 
   // Fields displayed. Includes labels, new entry markers, entries
-  FIELD** Fields;
-  // Where is source of current project
-  std::string WhereSource;
-  // Where is cmake executable
-  std::string WhereCMake;
+  std::vector<FIELD*> Fields;
   // Number of entries shown (depends on mode -normal or advanced-)
   size_t NumberOfVisibleEntries;
   bool AdvancedMode;
@@ -150,7 +147,7 @@
   int NumberOfPages;
 
   int InitialWidth;
-  cmake* CMakeInstance;
+  std::unique_ptr<cmake> CMakeInstance;
 
   std::string SearchString;
   std::string OldSearchString;
diff --git a/Source/CursesDialog/cmCursesOptionsWidget.h b/Source/CursesDialog/cmCursesOptionsWidget.h
index 0128d6a..0de8e64 100644
--- a/Source/CursesDialog/cmCursesOptionsWidget.h
+++ b/Source/CursesDialog/cmCursesOptionsWidget.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCursesStandardIncludes.h"
-#include "cmCursesWidget.h"
-
 #include <string>
 #include <vector>
 
+#include "cmCursesStandardIncludes.h"
+#include "cmCursesWidget.h"
+
 class cmCursesMainForm;
 
 class cmCursesOptionsWidget : public cmCursesWidget
diff --git a/Source/CursesDialog/cmCursesPathWidget.cxx b/Source/CursesDialog/cmCursesPathWidget.cxx
index 05c3279..bb3808e 100644
--- a/Source/CursesDialog/cmCursesPathWidget.cxx
+++ b/Source/CursesDialog/cmCursesPathWidget.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesPathWidget.h"
 
+#include <vector>
+
 #include "cmCursesMainForm.h"
 #include "cmCursesStringWidget.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 
-#include <vector>
-
 cmCursesPathWidget::cmCursesPathWidget(int width, int height, int left,
                                        int top)
   : cmCursesStringWidget(width, height, left, top)
diff --git a/Source/CursesDialog/cmCursesPathWidget.h b/Source/CursesDialog/cmCursesPathWidget.h
index 1eace03..fb365e9 100644
--- a/Source/CursesDialog/cmCursesPathWidget.h
+++ b/Source/CursesDialog/cmCursesPathWidget.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+
 #include "cmCursesStandardIncludes.h"
 #include "cmCursesStringWidget.h"
 
-#include <string>
-
 class cmCursesMainForm;
 
 class cmCursesPathWidget : public cmCursesStringWidget
diff --git a/Source/CursesDialog/cmCursesStringWidget.cxx b/Source/CursesDialog/cmCursesStringWidget.cxx
index 5e2a329..6296af2 100644
--- a/Source/CursesDialog/cmCursesStringWidget.cxx
+++ b/Source/CursesDialog/cmCursesStringWidget.cxx
@@ -2,15 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCursesStringWidget.h"
 
+#include <cstdio>
+
 #include "cmCursesForm.h"
 #include "cmCursesMainForm.h"
 #include "cmCursesStandardIncludes.h"
 #include "cmCursesWidget.h"
 #include "cmStateTypes.h"
 
-#include <stdio.h>
-#include <string.h>
-
 inline int ctrl(int z)
 {
   return (z & 037);
@@ -35,13 +34,13 @@
 
 void cmCursesStringWidget::OnReturn(cmCursesMainForm* fm, WINDOW* /*unused*/)
 {
-  FORM* form = fm->GetForm();
   if (this->InEdit) {
     cmCursesForm::LogMessage("String widget leaving edit.");
     this->InEdit = false;
     fm->PrintKeys();
-    delete[] this->OriginalString;
+    this->OriginalString.clear();
     // trick to force forms to update the field buffer
+    FORM* form = fm->GetForm();
     form_driver(form, REQ_NEXT_FIELD);
     form_driver(form, REQ_PREV_FIELD);
     this->Done = true;
@@ -49,9 +48,7 @@
     cmCursesForm::LogMessage("String widget entering edit.");
     this->InEdit = true;
     fm->PrintKeys();
-    char* buf = field_buffer(this->Field, 0);
-    this->OriginalString = new char[strlen(buf) + 1];
-    strcpy(this->OriginalString, buf);
+    this->OriginalString = field_buffer(this->Field, 0);
   }
 }
 
@@ -64,7 +61,8 @@
 bool cmCursesStringWidget::HandleInput(int& key, cmCursesMainForm* fm,
                                        WINDOW* w)
 {
-  int x, y;
+  int x;
+  int y;
 
   FORM* form = fm->GetForm();
   // when not in edit mode, edit mode is entered by pressing enter or i (vim
@@ -74,7 +72,7 @@
     return false;
   }
 
-  this->OriginalString = nullptr;
+  this->OriginalString.clear();
   this->Done = false;
 
   char debugMessage[128];
@@ -112,7 +110,7 @@
                key == ctrl('p') || key == KEY_NPAGE || key == ctrl('d') ||
                key == KEY_PPAGE || key == ctrl('u')) {
       this->InEdit = false;
-      delete[] this->OriginalString;
+      this->OriginalString.clear();
       // trick to force forms to update the field buffer
       form_driver(form, REQ_NEXT_FIELD);
       form_driver(form, REQ_PREV_FIELD);
@@ -124,7 +122,7 @@
         this->InEdit = false;
         fm->PrintKeys();
         this->SetString(this->OriginalString);
-        delete[] this->OriginalString;
+        this->OriginalString.clear();
         touchwin(w);
         wrefresh(w);
         return true;
@@ -179,30 +177,26 @@
 
 bool cmCursesStringWidget::PrintKeys()
 {
-  int x, y;
+  int x;
+  int y;
   getmaxyx(stdscr, y, x);
   if (x < cmCursesMainForm::MIN_WIDTH || y < cmCursesMainForm::MIN_HEIGHT) {
     return false;
   }
   if (this->InEdit) {
     char fmt_s[] = "%s";
-    char firstLine[512];
     // Clean the toolbar
-    memset(firstLine, ' ', sizeof(firstLine));
-    firstLine[511] = '\0';
     curses_move(y - 4, 0);
-    printw(fmt_s, firstLine);
-    curses_move(y - 3, 0);
-    printw(fmt_s, firstLine);
-    curses_move(y - 2, 0);
-    printw(fmt_s, firstLine);
-    curses_move(y - 1, 0);
-    printw(fmt_s, firstLine);
-
+    clrtoeol();
     curses_move(y - 3, 0);
     printw(fmt_s, "Editing option, press [enter] to confirm");
+    clrtoeol();
     curses_move(y - 2, 0);
     printw(fmt_s, "                press [esc] to cancel");
+    clrtoeol();
+    curses_move(y - 1, 0);
+    clrtoeol();
+
     return true;
   }
   return false;
diff --git a/Source/CursesDialog/cmCursesStringWidget.h b/Source/CursesDialog/cmCursesStringWidget.h
index 021515b..ce06c6d 100644
--- a/Source/CursesDialog/cmCursesStringWidget.h
+++ b/Source/CursesDialog/cmCursesStringWidget.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+
 #include "cmCursesStandardIncludes.h"
 #include "cmCursesWidget.h"
 
-#include <string>
-
 class cmCursesMainForm;
 
 /** \class cmCursesStringWidget
@@ -23,9 +23,6 @@
 public:
   cmCursesStringWidget(int width, int height, int left, int top);
 
-  cmCursesStringWidget(cmCursesStringWidget const&) = delete;
-  cmCursesStringWidget& operator=(cmCursesStringWidget const&) = delete;
-
   /**
    * Handle user input. Called by the container of this widget
    * when this widget has focus. Returns true if the input was
@@ -65,7 +62,7 @@
 protected:
   // true if the widget is in edit mode
   bool InEdit;
-  char* OriginalString;
+  std::string OriginalString;
   bool Done;
 };
 
diff --git a/Source/CursesDialog/cmCursesWidget.h b/Source/CursesDialog/cmCursesWidget.h
index f761f6d..9d03c6e 100644
--- a/Source/CursesDialog/cmCursesWidget.h
+++ b/Source/CursesDialog/cmCursesWidget.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+
 #include "cmCursesStandardIncludes.h"
 #include "cmStateTypes.h"
 
-#include <string>
-
 class cmCursesMainForm;
 
 class cmCursesWidget
diff --git a/Source/LexerParser/.gitattributes b/Source/LexerParser/.gitattributes
index 47eedfb..e5a1d8b 100644
--- a/Source/LexerParser/.gitattributes
+++ b/Source/LexerParser/.gitattributes
@@ -2,6 +2,8 @@
 /cmCommandArgumentLexer.h          generated
 /cmCommandArgumentParser.cxx       generated
 /cmCommandArgumentParserTokens.h   generated
+/cmCTestProcessesLexer.cxx         generated
+/cmCTestProcessesLexer.h           generated
 /cmDependsJavaLexer.cxx            generated
 /cmDependsJavaLexer.h              generated
 /cmDependsJavaParser.cxx           generated
diff --git a/Source/LexerParser/cmCTestProcessesLexer.cxx b/Source/LexerParser/cmCTestProcessesLexer.cxx
new file mode 100644
index 0000000..2a5dab3
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.cxx
@@ -0,0 +1,2224 @@
+#include "cmStandardLexer.h"
+
+#define FLEXINT_H 1
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmCTestProcesses_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmCTestProcesses_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmCTestProcesses_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmCTestProcesses_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmCTestProcesses_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmCTestProcesses_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmCTestProcesses_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmCTestProcesses_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmCTestProcesses_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmCTestProcesses_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmCTestProcesses_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmCTestProcesses_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmCTestProcesses_yylex_ALREADY_DEFINED
+#else
+#define yylex cmCTestProcesses_yylex
+#endif
+
+#ifdef yyrestart
+#define cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmCTestProcesses_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmCTestProcesses_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmCTestProcesses_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmCTestProcesses_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmCTestProcesses_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmCTestProcesses_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmCTestProcesses_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmCTestProcesses_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmCTestProcesses_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmCTestProcesses_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmCTestProcesses_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmCTestProcesses_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmCTestProcesses_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmCTestProcesses_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmCTestProcesses_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmCTestProcesses_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmCTestProcesses_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmCTestProcesses_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmCTestProcesses_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmCTestProcesses_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmCTestProcesses_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmCTestProcesses_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmCTestProcesses_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmCTestProcesses_yyfree
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX               (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ *   integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin , yyscanner )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    #define YY_LINENO_REWIND_TO(ptr)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	int yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+static void yyensure_buffer_stack ( yyscan_t yyscanner );
+static void yy_load_buffer_state ( yyscan_t yyscanner );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define cmCTestProcesses_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+typedef flex_uint8_t YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state  , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+#define YY_NUM_RULES 8
+#define YY_END_OF_BUFFER 9
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static const flex_int16_t yy_accept[29] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    9,    7,    2,    5,    7,    4,    6,    3,
+        2,    5,    0,    1,    4,    6,    3,    0
+    } ;
+
+static const YY_CHAR yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    3,    1,    1,    1,    4,    4,    4,
+        4,    4,    4,    4,    4,    4,    4,    5,    6,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    7,    1,    7,    7,    7,    7,
+
+        7,    7,    7,    7,    7,    7,    7,    7,    7,    7,
+        7,    7,    7,    7,    7,    7,    7,    7,    7,    7,
+        7,    7,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static const YY_CHAR yy_meta[8] =
+    {   0,
+        1,    1,    1,    2,    2,    1,    2
+    } ;
+
+static const flex_int16_t yy_base[30] =
+    {   0,
+        0,    0,    0,    0,    7,    0,   31,   12,   17,    0,
+        0,    0,   34,   36,   29,   26,   26,   27,   23,   24,
+       23,   20,   20,   36,   21,   16,   13,   36,   14
+    } ;
+
+static const flex_int16_t yy_def[30] =
+    {   0,
+       28,    1,    1,    1,   28,    5,    1,    5,    5,    9,
+        5,    5,   28,   28,   28,   28,   29,   28,   28,   28,
+       28,   28,   29,   28,   28,   28,   28,    0,   28
+    } ;
+
+static const flex_int16_t yy_nxt[44] =
+    {   0,
+       14,   14,   14,   15,   14,   16,   17,   14,   14,   18,
+       14,   14,   19,   14,   14,   23,   27,   16,   17,   14,
+       20,   26,   14,   25,   24,   22,   21,   27,   26,   25,
+       24,   22,   21,   28,   14,   13,   28,   28,   28,   28,
+       28,   28,   28
+    } ;
+
+static const flex_int16_t yy_chk[44] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    5,    5,    5,
+        5,    5,    5,    5,    8,   29,   27,    8,    8,    9,
+        9,   26,    9,   25,   23,   22,   21,   20,   19,   18,
+       17,   16,   15,   13,    7,   28,   28,   28,   28,   28,
+       28,   28,   28
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+/*
+
+This file must be translated to C++ and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+  flex --nounistd -DFLEXINT_H --noline --header-file=cmCTestProcessesLexer.h -ocmCTestProcessesLexer.cxx cmCTestProcessesLexer.in.l
+
+Modify cmCTestProcessesLexer.cxx:
+  - remove trailing whitespace:              sed -i 's/\s*$//' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+  - remove blank lines at end of file:       sed -i '${/^$/d;}' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+  - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmCTestProcessesLexer.cxx
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+#ifndef __clang_analyzer__ /* Suppress clang scan-build warnings */
+
+#include "cmCTestProcessesLexerHelper.h"
+
+#include <string>
+
+#include <cstddef>
+
+/*--------------------------------------------------------------------------*/
+
+#define INITIAL 0
+#define PROCESSES_START 1
+#define PROCESSES_END 2
+#define RESOURCE_START 3
+#define RESOURCE_COUNT 4
+#define RESOURCE_END 5
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    int yy_n_chars;
+    int yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals ( yyscan_t yyscanner );
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in  ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out  ( FILE * _out_str , yyscan_t yyscanner );
+
+			int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column  ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+
+    static void yyunput ( int c, char *buf_ptr  , yyscan_t yyscanner);
+
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( yyscan_t yyscanner );
+#else
+static int input ( yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		int n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+		}
+
+		yy_load_buffer_state( yyscanner );
+		}
+
+	{
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 29 )
+					yy_c = yy_meta[yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 36 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+{
+  BEGIN(RESOURCE_COUNT);
+  yyextra->SetResourceType(std::string(yytext, yyleng - 1));
+}
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+{
+  BEGIN(PROCESSES_END);
+  std::size_t len = yyleng;
+  yyextra->SetProcessCount(std::stoll(yytext, &len, 10));
+}
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+{
+  BEGIN(RESOURCE_END);
+  std::size_t len = yyleng;
+  yyextra->SetNeededSlots(std::stoll(yytext, &len, 10));
+  yyextra->WriteRequirement();
+}
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+  BEGIN(RESOURCE_START);
+}
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+  BEGIN(PROCESSES_START);
+}
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+{
+  BEGIN(PROCESSES_START);
+  yyextra->WriteProcess();
+}
+	YY_BREAK
+case YY_STATE_EOF(RESOURCE_START):
+case YY_STATE_EOF(PROCESSES_END):
+case YY_STATE_EOF(RESOURCE_END):
+{
+  yyextra->WriteProcess();
+  return 0;
+}
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(PROCESSES_START):
+{
+  return 0;
+}
+	YY_BREAK
+case YY_STATE_EOF(RESOURCE_COUNT):
+{
+  return 1;
+}
+	YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+{
+  return 1;
+}
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap( yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = yyg->yytext_ptr;
+	int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc( (void *) b->yy_ch_buf,
+							 (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = NULL;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin  , yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+			(void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+		/* "- 2" to take care of EOB's */
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	yy_state_type yy_current_state;
+	char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 29 )
+				yy_c = yy_meta[yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	char *yy_cp = yyg->yy_c_buf_p;
+
+	YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 29 )
+			yy_c = yy_meta[yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+	yy_is_jam = (yy_current_state == 28);
+
+	(void)yyg;
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+{
+	char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    yy_cp = yyg->yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yyg->yy_hold_char;
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		int number_to_move = yyg->yy_n_chars + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+	yyg->yytext_ptr = yy_bp;
+	yyg->yy_hold_char = *yy_cp;
+	yyg->yy_c_buf_p = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin , yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap( yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+	}
+
+	yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner);
+	yy_load_buffer_state( yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state( yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file , yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree( (void *) b->yy_ch_buf , yyscanner );
+
+	yyfree( (void *) b , yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_flush_buffer( b , yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state( yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state( yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state( yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return NULL;
+
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = (int) (size - 2);	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = NULL;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b , yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner)
+{
+
+	return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = (yy_size_t) (_yybytes_len + 2);
+	buf = (char *) yyalloc( n , yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n , yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int  _line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+    yylineno = _line_number;
+}
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int  _column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+    yycolumn = _column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  _in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = _in_str ;
+}
+
+void yyset_out (FILE *  _out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = _out_str ;
+}
+
+int yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void yyset_debug (int  _bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = _bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+int yylex_init(yyscan_t* ptr_yy_globals)
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+{
+    struct yyguts_t dummy_yyguts;
+
+    yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = NULL;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = NULL;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = NULL;
+    yyout = NULL;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	yyfree(yyg->yy_buffer_stack , yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        yyfree( yyg->yy_start_stack , yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
+{
+	int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	return malloc(size);
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return realloc(ptr, size);
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+/*--------------------------------------------------------------------------*/
+
+#endif /* __clang_analyzer__ */
diff --git a/Source/LexerParser/cmCTestProcessesLexer.h b/Source/LexerParser/cmCTestProcessesLexer.h
new file mode 100644
index 0000000..b6437b4
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.h
@@ -0,0 +1,692 @@
+#ifndef cmCTestProcesses_yyHEADER_H
+#define cmCTestProcesses_yyHEADER_H 1
+#define cmCTestProcesses_yyIN_HEADER 1
+
+#define FLEXINT_H 1
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmCTestProcesses_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmCTestProcesses_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmCTestProcesses_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmCTestProcesses_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmCTestProcesses_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmCTestProcesses_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmCTestProcesses_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmCTestProcesses_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmCTestProcesses_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmCTestProcesses_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmCTestProcesses_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmCTestProcesses_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmCTestProcesses_yylex_ALREADY_DEFINED
+#else
+#define yylex cmCTestProcesses_yylex
+#endif
+
+#ifdef yyrestart
+#define cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmCTestProcesses_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmCTestProcesses_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmCTestProcesses_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmCTestProcesses_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmCTestProcesses_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmCTestProcesses_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmCTestProcesses_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmCTestProcesses_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmCTestProcesses_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmCTestProcesses_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmCTestProcesses_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmCTestProcesses_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmCTestProcesses_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmCTestProcesses_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmCTestProcesses_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmCTestProcesses_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmCTestProcesses_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmCTestProcesses_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmCTestProcesses_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmCTestProcesses_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmCTestProcesses_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmCTestProcesses_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmCTestProcesses_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmCTestProcesses_yyfree
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX               (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	int yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+/* Begin user sect3 */
+
+#define cmCTestProcesses_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define PROCESSES_START 1
+#define PROCESSES_END 2
+#define RESOURCE_START 3
+#define RESOURCE_COUNT 4
+#define RESOURCE_END 5
+
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in  ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out  ( FILE * _out_str , yyscan_t yyscanner );
+
+			int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column  ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#ifndef cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#undef yy_create_buffer
+#endif
+#ifndef cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#undef yy_delete_buffer
+#endif
+#ifndef cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#undef yy_scan_buffer
+#endif
+#ifndef cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#undef yy_scan_string
+#endif
+#ifndef cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#undef yy_scan_bytes
+#endif
+#ifndef cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#undef yy_init_buffer
+#endif
+#ifndef cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#undef yy_flush_buffer
+#endif
+#ifndef cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#undef yy_load_buffer_state
+#endif
+#ifndef cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#undef yy_switch_to_buffer
+#endif
+#ifndef cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#undef yypush_buffer_state
+#endif
+#ifndef cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#undef yypop_buffer_state
+#endif
+#ifndef cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#undef yyensure_buffer_stack
+#endif
+#ifndef cmCTestProcesses_yylex_ALREADY_DEFINED
+#undef yylex
+#endif
+#ifndef cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#undef yyrestart
+#endif
+#ifndef cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#undef yylex_init
+#endif
+#ifndef cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#undef yylex_init_extra
+#endif
+#ifndef cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#undef yylex_destroy
+#endif
+#ifndef cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#undef yyget_debug
+#endif
+#ifndef cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#undef yyset_debug
+#endif
+#ifndef cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#undef yyget_extra
+#endif
+#ifndef cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#undef yyset_extra
+#endif
+#ifndef cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#undef yyget_in
+#endif
+#ifndef cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#undef yyset_in
+#endif
+#ifndef cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#undef yyget_out
+#endif
+#ifndef cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#undef yyset_out
+#endif
+#ifndef cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#undef yyget_leng
+#endif
+#ifndef cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#undef yyget_text
+#endif
+#ifndef cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#undef yyget_lineno
+#endif
+#ifndef cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#undef yyset_lineno
+#endif
+#ifndef cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#undef yyget_column
+#endif
+#ifndef cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#undef yyset_column
+#endif
+#ifndef cmCTestProcesses_yywrap_ALREADY_DEFINED
+#undef yywrap
+#endif
+#ifndef cmCTestProcesses_yyget_lval_ALREADY_DEFINED
+#undef yyget_lval
+#endif
+#ifndef cmCTestProcesses_yyset_lval_ALREADY_DEFINED
+#undef yyset_lval
+#endif
+#ifndef cmCTestProcesses_yyget_lloc_ALREADY_DEFINED
+#undef yyget_lloc
+#endif
+#ifndef cmCTestProcesses_yyset_lloc_ALREADY_DEFINED
+#undef yyset_lloc
+#endif
+#ifndef cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#undef yyalloc
+#endif
+#ifndef cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#undef yyrealloc
+#endif
+#ifndef cmCTestProcesses_yyfree_ALREADY_DEFINED
+#undef yyfree
+#endif
+#ifndef cmCTestProcesses_yytext_ALREADY_DEFINED
+#undef yytext
+#endif
+#ifndef cmCTestProcesses_yyleng_ALREADY_DEFINED
+#undef yyleng
+#endif
+#ifndef cmCTestProcesses_yyin_ALREADY_DEFINED
+#undef yyin
+#endif
+#ifndef cmCTestProcesses_yyout_ALREADY_DEFINED
+#undef yyout
+#endif
+#ifndef cmCTestProcesses_yy_flex_debug_ALREADY_DEFINED
+#undef yy_flex_debug
+#endif
+#ifndef cmCTestProcesses_yylineno_ALREADY_DEFINED
+#undef yylineno
+#endif
+#ifndef cmCTestProcesses_yytables_fload_ALREADY_DEFINED
+#undef yytables_fload
+#endif
+#ifndef cmCTestProcesses_yytables_destroy_ALREADY_DEFINED
+#undef yytables_destroy
+#endif
+#ifndef cmCTestProcesses_yyTABLES_NAME_ALREADY_DEFINED
+#undef yyTABLES_NAME
+#endif
+
+#undef cmCTestProcesses_yyIN_HEADER
+#endif /* cmCTestProcesses_yyHEADER_H */
diff --git a/Source/LexerParser/cmCTestProcessesLexer.in.l b/Source/LexerParser/cmCTestProcessesLexer.in.l
new file mode 100644
index 0000000..ef494d5
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.in.l
@@ -0,0 +1,102 @@
+%{
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+/*
+
+This file must be translated to C++ and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+  flex --nounistd -DFLEXINT_H --noline --header-file=cmCTestProcessesLexer.h -ocmCTestProcessesLexer.cxx cmCTestProcessesLexer.in.l
+
+Modify cmCTestProcessesLexer.cxx:
+  - remove trailing whitespace:              sed -i 's/\s*$//' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+  - remove blank lines at end of file:       sed -i '${/^$/d;}' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+  - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmCTestProcessesLexer.cxx
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+#ifndef __clang_analyzer__ /* Suppress clang scan-build warnings */
+
+#include "cmCTestProcessesLexerHelper.h"
+
+#include <string>
+
+#include <cstddef>
+
+/*--------------------------------------------------------------------------*/
+%}
+
+%option prefix="cmCTestProcesses_yy"
+
+%option reentrant
+%option noyywrap
+%option nodefault
+%pointer
+
+%s PROCESSES_START
+%s PROCESSES_END
+%s RESOURCE_START
+%s RESOURCE_COUNT
+%s RESOURCE_END
+
+NUMBER [0-9]+
+IDENTIFIER [a-z_][a-z0-9_]*
+
+%%
+
+<INITIAL,PROCESSES_START,RESOURCE_START>{IDENTIFIER}: {
+  BEGIN(RESOURCE_COUNT);
+  yyextra->SetResourceType(std::string(yytext, yyleng - 1));
+}
+
+<INITIAL,PROCESSES_START>{NUMBER} {
+  BEGIN(PROCESSES_END);
+  std::size_t len = yyleng;
+  yyextra->SetProcessCount(std::stoll(yytext, &len, 10));
+}
+
+<RESOURCE_COUNT>{NUMBER} {
+  BEGIN(RESOURCE_END);
+  std::size_t len = yyleng;
+  yyextra->SetNeededSlots(std::stoll(yytext, &len, 10));
+  yyextra->WriteRequirement();
+}
+
+<PROCESSES_END,RESOURCE_END>,+ {
+  BEGIN(RESOURCE_START);
+}
+
+<INITIAL,PROCESSES_START,RESOURCE_START>;+ {
+  BEGIN(PROCESSES_START);
+}
+
+<PROCESSES_END,RESOURCE_END>;+ {
+  BEGIN(PROCESSES_START);
+  yyextra->WriteProcess();
+}
+
+<RESOURCE_START,PROCESSES_END,RESOURCE_END><<EOF>> {
+  yyextra->WriteProcess();
+  return 0;
+}
+
+<INITIAL,PROCESSES_START><<EOF>> {
+  return 0;
+}
+
+<<EOF>> {
+  return 1;
+}
+
+.|\n {
+  return 1;
+}
+
+%%
+
+/*--------------------------------------------------------------------------*/
+
+#endif /* __clang_analyzer__ */
diff --git a/Source/LexerParser/cmCommandArgumentParser.cxx b/Source/LexerParser/cmCommandArgumentParser.cxx
index b965b32..ae7fb42 100644
--- a/Source/LexerParser/cmCommandArgumentParser.cxx
+++ b/Source/LexerParser/cmCommandArgumentParser.cxx
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -40,11 +41,14 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 /* Identify Bison output.  */
 #define YYBISON 1
 
 /* Bison version.  */
-#define YYBISON_VERSION "3.0.4"
+#define YYBISON_VERSION "3.3.2"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -67,8 +71,8 @@
 #define yynerrs         cmCommandArgument_yynerrs
 
 
-/* Copy the first part of user declarations.  */
-#line 1 "cmCommandArgumentParser.y" /* yacc.c:339  */
+/* First part of user prologue.  */
+#line 1 "cmCommandArgumentParser.y" /* yacc.c:337  */
 
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
@@ -130,13 +134,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 #endif
 
-#line 134 "cmCommandArgumentParser.cxx" /* yacc.c:339  */
-
+#line 138 "cmCommandArgumentParser.cxx" /* yacc.c:337  */
 # ifndef YY_NULLPTR
-#  if defined __cplusplus && 201103L <= __cplusplus
-#   define YY_NULLPTR nullptr
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
 #  else
-#   define YY_NULLPTR 0
+#   define YY_NULLPTR ((void*)0)
 #  endif
 # endif
 
@@ -201,9 +208,7 @@
 
 #endif /* !YY_CMCOMMANDARGUMENT_YY_CMCOMMANDARGUMENTPARSERTOKENS_H_INCLUDED  */
 
-/* Copy the second part of user declarations.  */
 
-#line 207 "cmCommandArgumentParser.cxx" /* yacc.c:358  */
 
 #ifdef short
 # undef short
@@ -224,13 +229,13 @@
 #ifdef YYTYPE_UINT16
 typedef YYTYPE_UINT16 yytype_uint16;
 #else
-typedef unsigned short int yytype_uint16;
+typedef unsigned short yytype_uint16;
 #endif
 
 #ifdef YYTYPE_INT16
 typedef YYTYPE_INT16 yytype_int16;
 #else
-typedef short int yytype_int16;
+typedef short yytype_int16;
 #endif
 
 #ifndef YYSIZE_T
@@ -242,7 +247,7 @@
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
-#  define YYSIZE_T unsigned int
+#  define YYSIZE_T unsigned
 # endif
 #endif
 
@@ -278,15 +283,6 @@
 # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
 #endif
 
-#if !defined _Noreturn \
-     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
-# if defined _MSC_VER && 1200 <= _MSC_VER
-#  define _Noreturn __declspec (noreturn)
-# else
-#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
-# endif
-#endif
-
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
 # define YYUSE(E) ((void) (E))
@@ -294,7 +290,7 @@
 # define YYUSE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
     _Pragma ("GCC diagnostic push") \
@@ -456,16 +452,16 @@
 /* YYNSTATES -- Number of states.  */
 #define YYNSTATES  33
 
-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
-   by yylex, with out-of-bounds checking.  */
 #define YYUNDEFTOK  2
 #define YYMAXUTOK   269
 
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
 #define YYTRANSLATE(YYX)                                                \
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+  ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
-   as returned by yylex, without out-of-bounds checking.  */
+   as returned by yylex.  */
 static const yytype_uint8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -513,7 +509,7 @@
 static const char *const yytname[] =
 {
   "$end", "error", "$undefined", "cal_ENVCURLY", "cal_NCURLY",
-  "cal_DCURLY", "\"$\"", "\"{\"", "\"}\"", "cal_NAME", R"("\\")",
+  "cal_DCURLY", "\"$\"", "\"{\"", "\"}\"", "cal_NAME", "\"\\\\\"",
   "cal_SYMBOL", "\"@\"", "cal_ERROR", "cal_ATNAME", "$accept", "Start",
   "GoalWithOptionalBackSlash", "Goal", "String", "OuterText", "Variable",
   "EnvVarName", "MultipleIds", "ID", YY_NULLPTR
@@ -633,22 +629,22 @@
 
 #define YYRECOVERING()  (!!yyerrstatus)
 
-#define YYBACKUP(Token, Value)                                  \
-do                                                              \
-  if (yychar == YYEMPTY)                                        \
-    {                                                           \
-      yychar = (Token);                                         \
-      yylval = (Value);                                         \
-      YYPOPSTACK (yylen);                                       \
-      yystate = *yyssp;                                         \
-      goto yybackup;                                            \
-    }                                                           \
-  else                                                          \
-    {                                                           \
-      yyerror (yyscanner, YY_("syntax error: cannot back up")); \
-      YYERROR;                                                  \
-    }                                                           \
-while (0)
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (yyscanner, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
 
 /* Error token number */
 #define YYTERROR        1
@@ -688,38 +684,38 @@
 } while (0)
 
 
-/*----------------------------------------.
-| Print this symbol's value on YYOUTPUT.  |
-`----------------------------------------*/
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  FILE *yyo = yyoutput;
-  YYUSE (yyo);
+  FILE *yyoutput = yyo;
+  YYUSE (yyoutput);
   YYUSE (yyscanner);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
   if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+    YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
 # endif
   YYUSE (yytype);
 }
 
 
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  YYFPRINTF (yyoutput, "%s %s (",
+  YYFPRINTF (yyo, "%s %s (",
              yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
 
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yyscanner);
-  YYFPRINTF (yyoutput, ")");
+  yy_symbol_value_print (yyo, yytype, yyvaluep, yyscanner);
+  YYFPRINTF (yyo, ")");
 }
 
 /*------------------------------------------------------------------.
@@ -753,7 +749,7 @@
 static void
 yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, yyscan_t yyscanner)
 {
-  unsigned long int yylno = yyrline[yyrule];
+  unsigned long yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
   YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
@@ -764,7 +760,7 @@
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
                        yystos[yyssp[yyi + 1 - yynrhs]],
-                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                       &yyvsp[(yyi + 1) - (yynrhs)]
                                               , yyscanner);
       YYFPRINTF (stderr, "\n");
     }
@@ -868,7 +864,10 @@
           case '\\':
             if (*++yyp != '\\')
               goto do_not_strip_quotes;
-            /* Fall through.  */
+            else
+              goto append;
+
+          append:
           default:
             if (yyres)
               yyres[yyn] = *yyp;
@@ -886,7 +885,7 @@
   if (! yyres)
     return yystrlen (yystr);
 
-  return yystpcpy (yyres, yystr) - yyres;
+  return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres);
 }
 # endif
 
@@ -964,10 +963,10 @@
                 yyarg[yycount++] = yytname[yyx];
                 {
                   YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (! (yysize <= yysize1
-                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+                    yysize = yysize1;
+                  else
                     return 2;
-                  yysize = yysize1;
                 }
               }
         }
@@ -979,6 +978,7 @@
       case N:                               \
         yyformat = S;                       \
       break
+    default: /* Avoid compiler warnings. */
       YYCASE_(0, YY_("syntax error"));
       YYCASE_(1, YY_("syntax error, unexpected %s"));
       YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
@@ -990,9 +990,10 @@
 
   {
     YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+      yysize = yysize1;
+    else
       return 2;
-    yysize = yysize1;
   }
 
   if (*yymsg_alloc < yysize)
@@ -1123,23 +1124,31 @@
   yychar = YYEMPTY; /* Cause a token to be read.  */
   goto yysetstate;
 
+
 /*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
+| yynewstate -- push a new state, which is found in yystate.  |
 `------------------------------------------------------------*/
- yynewstate:
+yynewstate:
   /* In all cases, when you get here, the value and location stacks
      have just been pushed.  So pushing a state here evens the stacks.  */
   yyssp++;
 
- yysetstate:
-  *yyssp = yystate;
+
+/*--------------------------------------------------------------------.
+| yynewstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  *yyssp = (yytype_int16) yystate;
 
   if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
+      YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1);
 
-#ifdef yyoverflow
+# if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
@@ -1155,14 +1164,10 @@
                     &yyss1, yysize * sizeof (*yyssp),
                     &yyvs1, yysize * sizeof (*yyvsp),
                     &yystacksize);
-
         yyss = yyss1;
         yyvs = yyvs1;
       }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
+# else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
         goto yyexhaustedlab;
@@ -1178,22 +1183,22 @@
           goto yyexhaustedlab;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
         YYSTACK_RELOCATE (yyvs_alloc, yyvs);
-#  undef YYSTACK_RELOCATE
+# undef YYSTACK_RELOCATE
         if (yyss1 != yyssa)
           YYSTACK_FREE (yyss1);
       }
 # endif
-#endif /* no yyoverflow */
 
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
       YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long int) yystacksize));
+                  (unsigned long) yystacksize));
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
   YYDPRINTF ((stderr, "Entering state %d\n", yystate));
 
@@ -1202,11 +1207,11 @@
 
   goto yybackup;
 
+
 /*-----------.
 | yybackup.  |
 `-----------*/
 yybackup:
-
   /* Do appropriate processing given the current state.  Read a
      lookahead token if we need one and don't already have one.  */
 
@@ -1279,7 +1284,7 @@
 
 
 /*-----------------------------.
-| yyreduce -- Do a reduction.  |
+| yyreduce -- do a reduction.  |
 `-----------------------------*/
 yyreduce:
   /* yyn is the number of a rule to reduce with.  */
@@ -1300,192 +1305,192 @@
   switch (yyn)
     {
         case 2:
-#line 99 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 99 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = 0;
     yyGetParser->SetResult((yyvsp[0].str));
   }
-#line 1309 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1314 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 3:
-#line 105 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 105 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1317 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1322 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 4:
-#line 108 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 108 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1325 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1330 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 5:
-#line 113 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 113 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = 0;
   }
-#line 1333 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1338 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 6:
-#line 116 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 116 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1341 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1346 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 7:
-#line 121 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 121 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1349 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1354 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 8:
-#line 124 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 124 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1357 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1362 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 9:
-#line 129 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 129 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1365 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1370 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 10:
-#line 132 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 132 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1373 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1378 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 11:
-#line 135 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 135 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1381 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1386 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 12:
-#line 138 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 138 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1389 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1394 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 13:
-#line 141 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 141 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1397 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1402 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 14:
-#line 144 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 144 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1405 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1410 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 15:
-#line 149 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 149 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->ExpandSpecialVariable((yyvsp[-2].str), (yyvsp[-1].str));
   }
-#line 1413 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1418 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 16:
-#line 152 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 152 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->ExpandSpecialVariable((yyvsp[-2].str), (yyvsp[-1].str));
   }
-#line 1421 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1426 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 17:
-#line 155 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 155 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->ExpandVariable((yyvsp[-1].str));
   }
-#line 1429 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1434 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 18:
-#line 158 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 158 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->ExpandVariableForAt((yyvsp[0].str));
   }
-#line 1437 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1442 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 19:
-#line 163 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 163 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1445 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1450 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 20:
-#line 166 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 166 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[-1].str);
   }
-#line 1453 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1458 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 21:
-#line 171 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 171 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = 0;
   }
-#line 1461 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1466 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 22:
-#line 174 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 174 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1469 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1474 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 23:
-#line 179 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 179 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1477 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1482 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
   case 24:
-#line 182 "cmCommandArgumentParser.y" /* yacc.c:1646  */
+#line 182 "cmCommandArgumentParser.y" /* yacc.c:1652  */
     {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1485 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1490 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
     break;
 
 
-#line 1489 "cmCommandArgumentParser.cxx" /* yacc.c:1646  */
+#line 1494 "cmCommandArgumentParser.cxx" /* yacc.c:1652  */
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1510,14 +1515,13 @@
   /* Now 'shift' the result of the reduction.  Determine what state
      that goes to, based on the state we popped back to and the rule
      number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
 
   goto yynewstate;
 
@@ -1601,12 +1605,10 @@
 | yyerrorlab -- error raised explicitly by YYERROR.  |
 `---------------------------------------------------*/
 yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -1669,6 +1671,7 @@
   yyresult = 0;
   goto yyreturn;
 
+
 /*-----------------------------------.
 | yyabortlab -- YYABORT comes here.  |
 `-----------------------------------*/
@@ -1676,6 +1679,7 @@
   yyresult = 1;
   goto yyreturn;
 
+
 #if !defined yyoverflow || YYERROR_VERBOSE
 /*-------------------------------------------------.
 | yyexhaustedlab -- memory exhaustion comes here.  |
@@ -1686,6 +1690,10 @@
   /* Fall through.  */
 #endif
 
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result.  |
+`-----------------------------------------------------*/
 yyreturn:
   if (yychar != YYEMPTY)
     {
@@ -1715,7 +1723,7 @@
 #endif
   return yyresult;
 }
-#line 187 "cmCommandArgumentParser.y" /* yacc.c:1906  */
+#line 187 "cmCommandArgumentParser.y" /* yacc.c:1918  */
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmCommandArgumentParserTokens.h b/Source/LexerParser/cmCommandArgumentParserTokens.h
index 3172182..56c9794 100644
--- a/Source/LexerParser/cmCommandArgumentParserTokens.h
+++ b/Source/LexerParser/cmCommandArgumentParserTokens.h
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +31,9 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 #ifndef YY_CMCOMMANDARGUMENT_YY_CMCOMMANDARGUMENTPARSERTOKENS_H_INCLUDED
 # define YY_CMCOMMANDARGUMENT_YY_CMCOMMANDARGUMENTPARSERTOKENS_H_INCLUDED
 /* Debug traces.  */
diff --git a/Source/LexerParser/cmDependsJavaParser.cxx b/Source/LexerParser/cmDependsJavaParser.cxx
index e83afa9..6c1fb2c 100644
--- a/Source/LexerParser/cmDependsJavaParser.cxx
+++ b/Source/LexerParser/cmDependsJavaParser.cxx
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -40,11 +41,14 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 /* Identify Bison output.  */
 #define YYBISON 1
 
 /* Bison version.  */
-#define YYBISON_VERSION "3.0.4"
+#define YYBISON_VERSION "3.3.2"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -67,8 +71,8 @@
 #define yynerrs         cmDependsJava_yynerrs
 
 
-/* Copy the first part of user declarations.  */
-#line 1 "cmDependsJavaParser.y" /* yacc.c:339  */
+/* First part of user prologue.  */
+#line 1 "cmDependsJavaParser.y" /* yacc.c:337  */
 
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
@@ -119,13 +123,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 #endif
 
-#line 123 "cmDependsJavaParser.cxx" /* yacc.c:339  */
-
+#line 127 "cmDependsJavaParser.cxx" /* yacc.c:337  */
 # ifndef YY_NULLPTR
-#  if defined __cplusplus && 201103L <= __cplusplus
-#   define YY_NULLPTR nullptr
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
 #  else
-#   define YY_NULLPTR 0
+#   define YY_NULLPTR ((void*)0)
 #  endif
 # endif
 
@@ -372,9 +379,7 @@
 
 #endif /* !YY_CMDEPENDSJAVA_YY_CMDEPENDSJAVAPARSERTOKENS_H_INCLUDED  */
 
-/* Copy the second part of user declarations.  */
 
-#line 378 "cmDependsJavaParser.cxx" /* yacc.c:358  */
 
 #ifdef short
 # undef short
@@ -395,13 +400,13 @@
 #ifdef YYTYPE_UINT16
 typedef YYTYPE_UINT16 yytype_uint16;
 #else
-typedef unsigned short int yytype_uint16;
+typedef unsigned short yytype_uint16;
 #endif
 
 #ifdef YYTYPE_INT16
 typedef YYTYPE_INT16 yytype_int16;
 #else
-typedef short int yytype_int16;
+typedef short yytype_int16;
 #endif
 
 #ifndef YYSIZE_T
@@ -413,7 +418,7 @@
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
-#  define YYSIZE_T unsigned int
+#  define YYSIZE_T unsigned
 # endif
 #endif
 
@@ -449,15 +454,6 @@
 # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
 #endif
 
-#if !defined _Noreturn \
-     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
-# if defined _MSC_VER && 1200 <= _MSC_VER
-#  define _Noreturn __declspec (noreturn)
-# else
-#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
-# endif
-#endif
-
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
 # define YYUSE(E) ((void) (E))
@@ -465,7 +461,7 @@
 # define YYUSE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
     _Pragma ("GCC diagnostic push") \
@@ -627,16 +623,16 @@
 /* YYNSTATES -- Number of states.  */
 #define YYNSTATES  575
 
-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
-   by yylex, with out-of-bounds checking.  */
 #define YYUNDEFTOK  2
 #define YYMAXUTOK   360
 
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
 #define YYTRANSLATE(YYX)                                                \
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+  ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
-   as returned by yylex, without out-of-bounds checking.  */
+   as returned by yylex.  */
 static const yytype_uint8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -1618,22 +1614,22 @@
 
 #define YYRECOVERING()  (!!yyerrstatus)
 
-#define YYBACKUP(Token, Value)                                  \
-do                                                              \
-  if (yychar == YYEMPTY)                                        \
-    {                                                           \
-      yychar = (Token);                                         \
-      yylval = (Value);                                         \
-      YYPOPSTACK (yylen);                                       \
-      yystate = *yyssp;                                         \
-      goto yybackup;                                            \
-    }                                                           \
-  else                                                          \
-    {                                                           \
-      yyerror (yyscanner, YY_("syntax error: cannot back up")); \
-      YYERROR;                                                  \
-    }                                                           \
-while (0)
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (yyscanner, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
 
 /* Error token number */
 #define YYTERROR        1
@@ -1673,38 +1669,38 @@
 } while (0)
 
 
-/*----------------------------------------.
-| Print this symbol's value on YYOUTPUT.  |
-`----------------------------------------*/
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  FILE *yyo = yyoutput;
-  YYUSE (yyo);
+  FILE *yyoutput = yyo;
+  YYUSE (yyoutput);
   YYUSE (yyscanner);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
   if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+    YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
 # endif
   YYUSE (yytype);
 }
 
 
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  YYFPRINTF (yyoutput, "%s %s (",
+  YYFPRINTF (yyo, "%s %s (",
              yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
 
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yyscanner);
-  YYFPRINTF (yyoutput, ")");
+  yy_symbol_value_print (yyo, yytype, yyvaluep, yyscanner);
+  YYFPRINTF (yyo, ")");
 }
 
 /*------------------------------------------------------------------.
@@ -1738,7 +1734,7 @@
 static void
 yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, yyscan_t yyscanner)
 {
-  unsigned long int yylno = yyrline[yyrule];
+  unsigned long yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
   YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
@@ -1749,7 +1745,7 @@
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
                        yystos[yyssp[yyi + 1 - yynrhs]],
-                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                       &yyvsp[(yyi + 1) - (yynrhs)]
                                               , yyscanner);
       YYFPRINTF (stderr, "\n");
     }
@@ -1853,7 +1849,10 @@
           case '\\':
             if (*++yyp != '\\')
               goto do_not_strip_quotes;
-            /* Fall through.  */
+            else
+              goto append;
+
+          append:
           default:
             if (yyres)
               yyres[yyn] = *yyp;
@@ -1871,7 +1870,7 @@
   if (! yyres)
     return yystrlen (yystr);
 
-  return yystpcpy (yyres, yystr) - yyres;
+  return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres);
 }
 # endif
 
@@ -1949,10 +1948,10 @@
                 yyarg[yycount++] = yytname[yyx];
                 {
                   YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (! (yysize <= yysize1
-                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+                    yysize = yysize1;
+                  else
                     return 2;
-                  yysize = yysize1;
                 }
               }
         }
@@ -1964,6 +1963,7 @@
       case N:                               \
         yyformat = S;                       \
       break
+    default: /* Avoid compiler warnings. */
       YYCASE_(0, YY_("syntax error"));
       YYCASE_(1, YY_("syntax error, unexpected %s"));
       YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
@@ -1975,9 +1975,10 @@
 
   {
     YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+      yysize = yysize1;
+    else
       return 2;
-    yysize = yysize1;
   }
 
   if (*yymsg_alloc < yysize)
@@ -2108,23 +2109,31 @@
   yychar = YYEMPTY; /* Cause a token to be read.  */
   goto yysetstate;
 
+
 /*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
+| yynewstate -- push a new state, which is found in yystate.  |
 `------------------------------------------------------------*/
- yynewstate:
+yynewstate:
   /* In all cases, when you get here, the value and location stacks
      have just been pushed.  So pushing a state here evens the stacks.  */
   yyssp++;
 
- yysetstate:
-  *yyssp = yystate;
+
+/*--------------------------------------------------------------------.
+| yynewstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  *yyssp = (yytype_int16) yystate;
 
   if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
+      YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1);
 
-#ifdef yyoverflow
+# if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
@@ -2140,14 +2149,10 @@
                     &yyss1, yysize * sizeof (*yyssp),
                     &yyvs1, yysize * sizeof (*yyvsp),
                     &yystacksize);
-
         yyss = yyss1;
         yyvs = yyvs1;
       }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
+# else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
         goto yyexhaustedlab;
@@ -2163,22 +2168,22 @@
           goto yyexhaustedlab;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
         YYSTACK_RELOCATE (yyvs_alloc, yyvs);
-#  undef YYSTACK_RELOCATE
+# undef YYSTACK_RELOCATE
         if (yyss1 != yyssa)
           YYSTACK_FREE (yyss1);
       }
 # endif
-#endif /* no yyoverflow */
 
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
       YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long int) yystacksize));
+                  (unsigned long) yystacksize));
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
   YYDPRINTF ((stderr, "Entering state %d\n", yystate));
 
@@ -2187,11 +2192,11 @@
 
   goto yybackup;
 
+
 /*-----------.
 | yybackup.  |
 `-----------*/
 yybackup:
-
   /* Do appropriate processing given the current state.  Read a
      lookahead token if we need one and don't already have one.  */
 
@@ -2264,7 +2269,7 @@
 
 
 /*-----------------------------.
-| yyreduce -- Do a reduction.  |
+| yyreduce -- do a reduction.  |
 `-----------------------------*/
 yyreduce:
   /* yyn is the number of a rule to reduce with.  */
@@ -2285,214 +2290,214 @@
   switch (yyn)
     {
         case 2:
-#line 183 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 183 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2296 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2301 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 3:
-#line 192 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 192 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2307 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2312 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 4:
-#line 200 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 200 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2318 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2323 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 5:
-#line 208 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 208 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2329 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2334 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 6:
-#line 216 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 216 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2340 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2345 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 7:
-#line 224 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 224 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2351 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2356 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 8:
-#line 232 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 232 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2362 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2367 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 9:
-#line 241 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 241 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2373 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2378 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 10:
-#line 249 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 249 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2384 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2389 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 11:
-#line 258 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 258 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2395 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2400 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 12:
-#line 266 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 266 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2406 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2411 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 13:
-#line 275 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 275 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2414 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2419 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 14:
-#line 280 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 280 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2422 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2427 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 15:
-#line 285 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 285 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2430 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2435 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 16:
-#line 290 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 290 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2438 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2443 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 17:
-#line 295 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 295 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2446 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2451 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 18:
-#line 300 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 300 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2454 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2459 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 19:
-#line 305 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 305 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2462 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2467 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 20:
-#line 310 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 310 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
 }
-#line 2470 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2475 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 21:
-#line 316 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 316 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2481 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2486 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 22:
-#line 324 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 324 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2492 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2497 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 23:
-#line 333 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 333 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpStoreClass((yyvsp[0].str));
@@ -2500,44 +2505,44 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2504 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2509 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 24:
-#line 343 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 343 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2515 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2520 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 25:
-#line 352 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 352 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2526 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2531 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 26:
-#line 361 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 361 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2537 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2542 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 27:
-#line 369 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 369 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpStoreClass((yyvsp[-1].str));
@@ -2545,56 +2550,56 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2549 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2554 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 28:
-#line 379 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 379 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2558 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2563 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 29:
-#line 385 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 385 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2567 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2572 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 30:
-#line 392 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 392 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2576 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2581 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 31:
-#line 399 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 399 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2585 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2590 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 32:
-#line 405 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 405 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2594 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2599 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 33:
-#line 412 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 412 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->AddClassFound((yyvsp[-2].str));
@@ -2602,11 +2607,11 @@
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
   (yyval.str) = const_cast<char*>(yyGetParser->GetCurrentCombine());
 }
-#line 2606 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2611 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 34:
-#line 421 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 421 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2615,11 +2620,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2619 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2624 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 35:
-#line 431 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 431 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2628,118 +2633,118 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2632 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2637 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 36:
-#line 441 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 441 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2643 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2648 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 37:
-#line 450 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 450 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2654 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2659 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 38:
-#line 458 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 458 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2665 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2670 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 39:
-#line 467 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 467 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2676 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2681 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 40:
-#line 475 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 475 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2686 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2691 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 41:
-#line 482 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 482 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2697 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2702 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 42:
-#line 490 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 490 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2707 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2712 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 43:
-#line 497 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 497 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2718 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2723 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 44:
-#line 505 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 505 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2728 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2733 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 45:
-#line 512 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 512 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2739 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2744 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 46:
-#line 521 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 521 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->SetCurrentPackage((yyvsp[-1].str));
@@ -2749,33 +2754,33 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2753 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2758 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 47:
-#line 533 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 533 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2764 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2769 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 48:
-#line 541 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 541 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2775 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2780 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 49:
-#line 550 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 550 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->AddPackagesImport((yyvsp[-1].str));
@@ -2785,11 +2790,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2789 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2794 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 50:
-#line 562 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 562 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   std::string str = (yyvsp[-3].str);
@@ -2800,77 +2805,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2804 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2809 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 51:
-#line 575 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 575 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2815 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2820 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 52:
-#line 583 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 583 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2826 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2831 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 53:
-#line 591 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 591 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2837 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2842 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 54:
-#line 600 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 600 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2848 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2853 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 55:
-#line 608 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 608 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2859 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2864 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 67:
-#line 623 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 623 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 2870 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2875 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 68:
-#line 633 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 633 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -2878,11 +2883,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 2882 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2887 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 69:
-#line 642 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 642 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(2);
@@ -2890,11 +2895,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 2894 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2899 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 70:
-#line 651 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 651 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -2902,11 +2907,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 2906 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2911 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 71:
-#line 660 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 660 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -2914,226 +2919,226 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 2918 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2923 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 72:
-#line 669 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 669 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2928 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2933 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 73:
-#line 676 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 676 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2939 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2944 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 74:
-#line 685 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 685 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2950 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2955 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 75:
-#line 694 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 694 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2961 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2966 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 76:
-#line 703 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 703 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2972 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2977 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 77:
-#line 711 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 711 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2983 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2988 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 78:
-#line 720 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 720 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2994 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 2999 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 79:
-#line 728 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 728 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3004 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3009 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 80:
-#line 735 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 735 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3015 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3020 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 81:
-#line 744 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 744 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3026 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3031 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 82:
-#line 752 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 752 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3037 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3042 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 83:
-#line 760 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 760 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3048 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3053 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 84:
-#line 768 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 768 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3059 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3064 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 85:
-#line 777 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 777 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3070 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3075 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 86:
-#line 785 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 785 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3081 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3086 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 87:
-#line 794 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 794 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
 }
-#line 3089 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3094 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 88:
-#line 800 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 800 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3100 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3105 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 89:
-#line 808 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 808 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3111 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3116 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 90:
-#line 817 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 817 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3122 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3127 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 91:
-#line 825 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 825 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3133 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3138 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 92:
-#line 834 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 834 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -3141,77 +3146,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3145 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3150 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 93:
-#line 843 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 843 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3156 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3161 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 94:
-#line 852 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 852 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3167 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3172 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 95:
-#line 860 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 860 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3178 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3183 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 96:
-#line 869 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 869 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3189 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3194 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 97:
-#line 877 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 877 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3200 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3205 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 98:
-#line 885 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 885 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3211 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3216 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 99:
-#line 894 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 894 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3219,11 +3224,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3223 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3228 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 100:
-#line 903 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 903 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3231,22 +3236,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3235 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3240 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 101:
-#line 912 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 912 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3246 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3251 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 102:
-#line 920 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 920 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3254,11 +3259,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3258 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3263 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 103:
-#line 930 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 930 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3267,40 +3272,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3271 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3276 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 104:
-#line 940 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 940 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
 
 }
-#line 3280 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3285 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 105:
-#line 946 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 946 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3291 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3296 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 107:
-#line 957 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 957 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
 
 }
-#line 3300 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3305 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 108:
-#line 963 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 963 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3308,11 +3313,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3312 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3317 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 109:
-#line 973 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 973 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3320,11 +3325,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3324 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3329 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 110:
-#line 983 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 983 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3332,20 +3337,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3336 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3341 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 111:
-#line 993 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 993 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
 
 }
-#line 3345 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3350 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 112:
-#line 999 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 999 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3353,11 +3358,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3357 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3362 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 113:
-#line 1009 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1009 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3365,11 +3370,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3369 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3374 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 114:
-#line 1019 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1019 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3377,11 +3382,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3381 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3386 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 115:
-#line 1029 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1029 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3389,11 +3394,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3393 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3398 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 116:
-#line 1038 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1038 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3401,11 +3406,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3405 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3410 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 117:
-#line 1048 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1048 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3414,11 +3419,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3418 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3423 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 118:
-#line 1059 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1059 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3426,22 +3431,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3430 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3435 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 119:
-#line 1068 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1068 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3441 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3446 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 120:
-#line 1076 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1076 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3449,11 +3454,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3453 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3458 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 121:
-#line 1086 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1086 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3461,11 +3466,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3465 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3470 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 122:
-#line 1095 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1095 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3473,22 +3478,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3477 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3482 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 123:
-#line 1105 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1105 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 3488 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3493 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 124:
-#line 1114 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1114 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3496,21 +3501,21 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3500 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3505 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 125:
-#line 1123 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1123 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3510 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3515 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 126:
-#line 1130 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1130 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3518,11 +3523,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3522 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3527 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 127:
-#line 1140 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1140 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3530,11 +3535,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3534 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3539 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 128:
-#line 1149 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1149 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3542,11 +3547,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3546 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3551 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 129:
-#line 1159 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1159 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3554,33 +3559,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3558 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3563 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 130:
-#line 1168 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1168 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3569 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3574 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 131:
-#line 1176 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1176 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3580 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3585 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 132:
-#line 1185 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1185 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3588,11 +3593,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3592 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3597 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 133:
-#line 1194 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1194 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3600,11 +3605,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3604 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3609 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 134:
-#line 1203 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1203 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3612,22 +3617,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3616 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3621 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 135:
-#line 1212 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1212 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3627 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3632 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 136:
-#line 1220 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1220 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3635,22 +3640,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3639 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3644 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 137:
-#line 1229 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1229 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3650 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3655 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 138:
-#line 1238 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1238 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3658,11 +3663,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3662 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3667 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 139:
-#line 1248 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1248 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3670,11 +3675,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3674 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3679 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 140:
-#line 1258 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1258 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3682,11 +3687,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3686 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3691 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 141:
-#line 1267 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1267 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3694,11 +3699,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3698 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3703 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 142:
-#line 1277 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1277 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3706,22 +3711,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3710 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3715 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 143:
-#line 1286 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1286 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3721 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3726 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 144:
-#line 1294 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1294 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3729,11 +3734,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3733 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3738 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 145:
-#line 1303 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1303 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3741,11 +3746,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3745 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3750 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 146:
-#line 1313 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1313 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3753,11 +3758,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3757 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3762 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 147:
-#line 1322 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1322 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3765,33 +3770,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3769 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3774 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 148:
-#line 1332 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1332 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3780 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3785 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 149:
-#line 1340 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1340 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3791 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3796 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 150:
-#line 1348 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1348 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3799,11 +3804,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3803 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3808 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 151:
-#line 1358 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1358 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3811,11 +3816,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3815 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3820 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 152:
-#line 1367 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1367 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -3823,11 +3828,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3827 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3832 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 153:
-#line 1377 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1377 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3835,11 +3840,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3839 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3844 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 154:
-#line 1386 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1386 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3847,11 +3852,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3851 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3856 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 155:
-#line 1395 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1395 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3859,11 +3864,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3863 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3868 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 156:
-#line 1405 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1405 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -3871,11 +3876,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3875 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3880 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 157:
-#line 1415 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1415 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(3);
@@ -3883,11 +3888,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3887 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3892 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 158:
-#line 1424 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1424 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -3895,11 +3900,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3899 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3904 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 159:
-#line 1434 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1434 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3907,11 +3912,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3911 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3916 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 160:
-#line 1443 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1443 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3919,11 +3924,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3923 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3928 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 161:
-#line 1452 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1452 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3931,11 +3936,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3935 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3940 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 162:
-#line 1461 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1461 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3943,11 +3948,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3947 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3952 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 163:
-#line 1470 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1470 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3955,11 +3960,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3959 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3964 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 164:
-#line 1479 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1479 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3967,11 +3972,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3971 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3976 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 165:
-#line 1489 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1489 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3979,11 +3984,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3983 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 3988 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 166:
-#line 1498 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1498 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3991,11 +3996,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3995 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4000 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 167:
-#line 1507 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1507 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4003,11 +4008,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4007 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4012 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 168:
-#line 1516 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1516 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4015,11 +4020,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4019 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4024 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 169:
-#line 1525 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1525 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4027,11 +4032,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4031 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4036 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 170:
-#line 1535 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1535 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4039,11 +4044,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4043 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4048 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 171:
-#line 1544 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1544 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4051,11 +4056,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4055 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4060 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 172:
-#line 1553 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1553 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4063,11 +4068,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4067 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4072 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 173:
-#line 1562 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1562 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4075,11 +4080,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4079 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4084 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 174:
-#line 1571 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1571 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4087,11 +4092,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4091 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4096 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 175:
-#line 1580 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1580 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4099,11 +4104,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4103 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4108 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 176:
-#line 1589 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1589 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4111,11 +4116,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4115 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4120 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 177:
-#line 1598 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1598 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4123,11 +4128,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4127 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4132 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 178:
-#line 1607 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1607 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4135,11 +4140,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4139 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4144 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 179:
-#line 1616 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1616 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4147,11 +4152,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4151 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4156 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 180:
-#line 1625 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1625 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4159,11 +4164,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4163 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4168 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 181:
-#line 1634 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1634 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4171,11 +4176,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4175 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4180 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 182:
-#line 1644 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1644 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4183,11 +4188,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4187 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4192 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 183:
-#line 1654 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1654 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
@@ -4196,11 +4201,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4200 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4205 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 184:
-#line 1665 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1665 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4208,11 +4213,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4212 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4217 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 185:
-#line 1675 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1675 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4220,11 +4225,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4224 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4229 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 186:
-#line 1685 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1685 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4232,11 +4237,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4236 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4241 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 187:
-#line 1694 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1694 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4244,11 +4249,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4248 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4253 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 188:
-#line 1703 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1703 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4256,11 +4261,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4260 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4265 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 189:
-#line 1712 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1712 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4268,11 +4273,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4272 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4277 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 190:
-#line 1721 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1721 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4280,11 +4285,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4284 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4289 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 191:
-#line 1730 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1730 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4292,11 +4297,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4296 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4301 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 192:
-#line 1739 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1739 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4304,11 +4309,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4308 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4313 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 193:
-#line 1749 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1749 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4316,11 +4321,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4320 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4325 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 194:
-#line 1759 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1759 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4328,11 +4333,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4332 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4337 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 195:
-#line 1769 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1769 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4340,40 +4345,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4344 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4349 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 196:
-#line 1779 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1779 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
 
 }
-#line 4353 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4358 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 197:
-#line 1786 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1786 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
 
 }
-#line 4362 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4367 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 198:
-#line 1792 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1792 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4373 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4378 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 199:
-#line 1800 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1800 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4381,22 +4386,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4385 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4390 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 200:
-#line 1809 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1809 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4396 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4401 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 201:
-#line 1817 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1817 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4404,11 +4409,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4408 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4413 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 202:
-#line 1827 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1827 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4416,11 +4421,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4420 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4425 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 203:
-#line 1837 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1837 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4428,11 +4433,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4432 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4437 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 204:
-#line 1846 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1846 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4440,11 +4445,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4444 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4449 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 205:
-#line 1856 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1856 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4452,11 +4457,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4456 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4461 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 206:
-#line 1865 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1865 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4464,58 +4469,58 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4468 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4473 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 207:
-#line 1875 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1875 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
 
 }
-#line 4477 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4482 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 208:
-#line 1882 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1882 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
 
 }
-#line 4486 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4491 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 209:
-#line 1889 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1889 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(7);
 
 }
-#line 4495 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4500 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 210:
-#line 1897 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1897 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(9);
 
 }
-#line 4504 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4509 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 211:
-#line 1903 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1903 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4515 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4520 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 212:
-#line 1911 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1911 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4523,22 +4528,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4527 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4532 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 213:
-#line 1920 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1920 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4538 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4543 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 214:
-#line 1928 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1928 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4546,33 +4551,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4550 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4555 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 215:
-#line 1939 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1939 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(9);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4561 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4566 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 216:
-#line 1947 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1947 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4572 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4577 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 217:
-#line 1955 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1955 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4580,11 +4585,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4584 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4589 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 218:
-#line 1965 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1965 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4592,11 +4597,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4596 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4601 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 219:
-#line 1974 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1974 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4604,11 +4609,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4608 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4613 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 220:
-#line 1984 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1984 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4616,11 +4621,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4620 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4625 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 221:
-#line 1994 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 1994 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4628,11 +4633,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4632 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4637 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 222:
-#line 2003 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2003 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4640,11 +4645,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4644 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4649 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 223:
-#line 2013 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2013 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4652,11 +4657,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4656 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4661 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 224:
-#line 2022 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2022 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4664,11 +4669,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4668 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4673 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 225:
-#line 2032 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2032 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4677,31 +4682,31 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4681 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4686 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 226:
-#line 2042 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2042 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4692 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4697 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 227:
-#line 2050 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2050 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
 
 }
-#line 4701 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4706 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 228:
-#line 2057 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2057 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4710,11 +4715,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4714 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4719 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 229:
-#line 2068 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2068 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4722,11 +4727,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4726 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4731 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 230:
-#line 2078 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2078 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4734,11 +4739,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4738 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4743 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 231:
-#line 2088 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2088 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4746,11 +4751,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4750 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4755 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 232:
-#line 2098 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2098 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4758,11 +4763,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4762 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4767 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 233:
-#line 2107 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2107 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -4770,22 +4775,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4774 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4779 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 234:
-#line 2116 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2116 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4785 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4790 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 235:
-#line 2124 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2124 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4793,11 +4798,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4797 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4802 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 236:
-#line 2134 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2134 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4805,11 +4810,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4809 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4814 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 237:
-#line 2143 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2143 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4817,20 +4822,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4821 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4826 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 238:
-#line 2153 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2153 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
 
 }
-#line 4830 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4835 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 239:
-#line 2160 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2160 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4838,11 +4843,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4842 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4847 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 240:
-#line 2170 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2170 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4850,11 +4855,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4854 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4859 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 241:
-#line 2179 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2179 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4862,11 +4867,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4866 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4871 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 242:
-#line 2189 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2189 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4874,20 +4879,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4878 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4883 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 243:
-#line 2198 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2198 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
 
 }
-#line 4887 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4892 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 244:
-#line 2204 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2204 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4895,11 +4900,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4899 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4904 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 245:
-#line 2213 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2213 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4907,11 +4912,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4911 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4916 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 246:
-#line 2222 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2222 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4919,11 +4924,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4923 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4928 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 247:
-#line 2231 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2231 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4931,11 +4936,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4935 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4940 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 248:
-#line 2240 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2240 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4943,11 +4948,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4947 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4952 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 249:
-#line 2250 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2250 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(6);
   jpCheckEmpty(6);
@@ -4955,22 +4960,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4959 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4964 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 250:
-#line 2259 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2259 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4970 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4975 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 251:
-#line 2267 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2267 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4978,22 +4983,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4982 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4987 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 252:
-#line 2276 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2276 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4993 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 4998 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 253:
-#line 2284 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2284 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5001,11 +5006,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5005 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5010 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 254:
-#line 2294 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2294 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5013,11 +5018,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5017 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5022 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 255:
-#line 2303 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2303 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5025,11 +5030,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5029 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5034 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 256:
-#line 2313 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2313 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5037,11 +5042,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5041 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5046 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 257:
-#line 2322 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2322 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5049,11 +5054,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5053 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5058 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 258:
-#line 2331 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2331 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5061,11 +5066,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5065 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5070 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 259:
-#line 2340 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2340 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5073,22 +5078,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5077 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5082 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 260:
-#line 2349 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2349 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5088 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5093 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 261:
-#line 2357 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2357 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5096,11 +5101,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5100 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5105 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 262:
-#line 2367 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2367 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5108,11 +5113,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5112 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5117 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 263:
-#line 2376 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2376 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5120,11 +5125,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5124 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5129 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 264:
-#line 2386 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2386 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5132,29 +5137,29 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5136 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5141 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 265:
-#line 2396 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2396 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
 
 }
-#line 5145 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5150 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 266:
-#line 2402 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2402 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
 
 }
-#line 5154 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5159 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 267:
-#line 2409 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2409 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5163,11 +5168,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5167 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5172 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 268:
-#line 2419 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2419 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5176,11 +5181,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5180 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5185 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 269:
-#line 2429 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2429 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5189,11 +5194,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5193 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5198 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 270:
-#line 2439 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2439 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5202,11 +5207,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5206 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5211 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 271:
-#line 2450 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2450 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5215,11 +5220,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5219 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5224 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 272:
-#line 2460 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2460 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-5].str)));
@@ -5229,11 +5234,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5233 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5238 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 273:
-#line 2471 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2471 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5242,11 +5247,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5246 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5251 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 274:
-#line 2481 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2481 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5255,11 +5260,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5259 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5264 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 275:
-#line 2492 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2492 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5268,11 +5273,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5272 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5277 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 276:
-#line 2502 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2502 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5280,11 +5285,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5284 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5289 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 277:
-#line 2512 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2512 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5292,11 +5297,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5296 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5301 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 278:
-#line 2521 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2521 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5304,11 +5309,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5308 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5313 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 279:
-#line 2530 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2530 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5316,11 +5321,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5320 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5325 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 280:
-#line 2539 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2539 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5328,11 +5333,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5332 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5337 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 281:
-#line 2548 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2548 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5340,11 +5345,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5344 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5349 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 282:
-#line 2558 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2558 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5352,11 +5357,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5356 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5361 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 283:
-#line 2568 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2568 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5364,11 +5369,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5368 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5373 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 284:
-#line 2578 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2578 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5376,11 +5381,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5380 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5385 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 285:
-#line 2587 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2587 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5388,11 +5393,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5392 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5397 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 286:
-#line 2596 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2596 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5400,11 +5405,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5404 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5409 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 287:
-#line 2605 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2605 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5412,11 +5417,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5416 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5421 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 288:
-#line 2614 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2614 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5424,11 +5429,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5428 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5433 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 289:
-#line 2624 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2624 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5436,11 +5441,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5440 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5445 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 290:
-#line 2634 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2634 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5448,11 +5453,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5452 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5457 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 291:
-#line 2644 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2644 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5460,11 +5465,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5464 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5469 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 292:
-#line 2653 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2653 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5472,11 +5477,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5476 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5481 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 293:
-#line 2662 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2662 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5484,11 +5489,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5488 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5493 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 294:
-#line 2671 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2671 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5496,11 +5501,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5500 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5505 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 295:
-#line 2681 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2681 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -5508,11 +5513,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5512 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5517 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 296:
-#line 2690 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2690 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5520,20 +5525,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5524 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5529 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 297:
-#line 2699 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2699 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
 
 }
-#line 5533 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5538 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 298:
-#line 2706 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2706 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5541,11 +5546,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5545 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5550 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 299:
-#line 2715 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2715 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5553,11 +5558,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5557 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5562 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 300:
-#line 2724 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2724 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5565,11 +5570,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5569 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5574 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 301:
-#line 2733 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2733 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5577,11 +5582,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5581 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5586 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 302:
-#line 2743 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2743 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5589,11 +5594,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5593 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5598 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 303:
-#line 2752 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2752 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5601,11 +5606,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5605 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5610 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 304:
-#line 2761 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2761 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5613,11 +5618,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5617 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5622 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 305:
-#line 2771 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2771 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5625,11 +5630,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5629 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5634 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 306:
-#line 2780 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2780 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5637,11 +5642,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5641 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5646 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 307:
-#line 2789 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2789 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5649,11 +5654,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5653 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5658 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 308:
-#line 2798 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2798 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5661,11 +5666,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5665 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5670 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 309:
-#line 2808 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2808 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5673,11 +5678,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5677 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5682 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 310:
-#line 2817 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2817 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5685,11 +5690,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5689 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5694 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 311:
-#line 2826 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2826 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5697,11 +5702,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5701 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5706 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 312:
-#line 2835 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2835 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5709,11 +5714,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5713 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5718 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 313:
-#line 2844 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2844 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5721,11 +5726,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5725 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5730 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 314:
-#line 2853 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2853 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5733,11 +5738,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5737 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5742 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 315:
-#line 2863 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2863 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5745,11 +5750,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5749 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5754 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 316:
-#line 2872 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2872 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5757,11 +5762,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5761 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5766 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 317:
-#line 2881 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2881 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5769,11 +5774,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5773 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5778 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 318:
-#line 2891 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2891 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5781,11 +5786,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5785 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5790 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 319:
-#line 2900 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2900 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5793,11 +5798,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5797 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5802 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 320:
-#line 2910 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2910 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5805,11 +5810,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5809 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5814 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 321:
-#line 2919 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2919 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5817,11 +5822,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5821 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5826 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 322:
-#line 2929 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2929 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5829,11 +5834,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5833 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5838 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 323:
-#line 2938 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2938 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5841,11 +5846,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5845 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5850 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 324:
-#line 2948 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2948 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5853,11 +5858,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5857 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5862 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 325:
-#line 2957 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2957 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5865,11 +5870,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5869 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5874 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 326:
-#line 2967 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2967 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5877,11 +5882,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5881 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5886 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 327:
-#line 2976 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2976 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5889,11 +5894,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5893 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5898 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 328:
-#line 2986 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2986 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5901,11 +5906,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5905 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5910 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 329:
-#line 2995 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 2995 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -5913,11 +5918,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5917 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5922 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 330:
-#line 3005 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3005 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5925,11 +5930,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5929 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5934 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 331:
-#line 3014 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3014 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5937,11 +5942,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5941 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5946 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 332:
-#line 3024 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3024 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5949,11 +5954,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5953 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5958 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 333:
-#line 3034 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3034 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5962,11 +5967,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5966 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5971 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 334:
-#line 3044 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3044 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5974,11 +5979,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5978 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5983 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 335:
-#line 3053 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3053 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5986,11 +5991,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5990 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 5995 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 336:
-#line 3063 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3063 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5998,11 +6003,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6002 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6007 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 337:
-#line 3072 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3072 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6010,11 +6015,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6014 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6019 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 338:
-#line 3081 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3081 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6022,11 +6027,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6026 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6031 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 339:
-#line 3090 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3090 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6034,11 +6039,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6038 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6043 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 340:
-#line 3099 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3099 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6046,11 +6051,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6050 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6055 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 341:
-#line 3108 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3108 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6058,11 +6063,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6062 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6067 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 342:
-#line 3117 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3117 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6070,11 +6075,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6074 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6079 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 343:
-#line 3126 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3126 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6082,11 +6087,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6086 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6091 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 344:
-#line 3135 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3135 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6094,11 +6099,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6098 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6103 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 345:
-#line 3144 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3144 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6106,11 +6111,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6110 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6115 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 346:
-#line 3153 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3153 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6118,11 +6123,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6122 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6127 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 347:
-#line 3162 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3162 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6130,11 +6135,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6134 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6139 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 348:
-#line 3172 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3172 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6142,11 +6147,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6146 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6151 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 349:
-#line 3182 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3182 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6154,11 +6159,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6158 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6163 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 350:
-#line 3192 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3192 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6166,11 +6171,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6170 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6175 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
   case 351:
-#line 3201 "cmDependsJavaParser.y" /* yacc.c:1646  */
+#line 3201 "cmDependsJavaParser.y" /* yacc.c:1652  */
     {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -6179,11 +6184,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6183 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6188 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
     break;
 
 
-#line 6187 "cmDependsJavaParser.cxx" /* yacc.c:1646  */
+#line 6192 "cmDependsJavaParser.cxx" /* yacc.c:1652  */
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -6208,14 +6213,13 @@
   /* Now 'shift' the result of the reduction.  Determine what state
      that goes to, based on the state we popped back to and the rule
      number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
 
   goto yynewstate;
 
@@ -6299,12 +6303,10 @@
 | yyerrorlab -- error raised explicitly by YYERROR.  |
 `---------------------------------------------------*/
 yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -6367,6 +6369,7 @@
   yyresult = 0;
   goto yyreturn;
 
+
 /*-----------------------------------.
 | yyabortlab -- YYABORT comes here.  |
 `-----------------------------------*/
@@ -6374,6 +6377,7 @@
   yyresult = 1;
   goto yyreturn;
 
+
 #if !defined yyoverflow || YYERROR_VERBOSE
 /*-------------------------------------------------.
 | yyexhaustedlab -- memory exhaustion comes here.  |
@@ -6384,6 +6388,10 @@
   /* Fall through.  */
 #endif
 
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result.  |
+`-----------------------------------------------------*/
 yyreturn:
   if (yychar != YYEMPTY)
     {
@@ -6413,7 +6421,7 @@
 #endif
   return yyresult;
 }
-#line 3210 "cmDependsJavaParser.y" /* yacc.c:1906  */
+#line 3210 "cmDependsJavaParser.y" /* yacc.c:1918  */
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmDependsJavaParserTokens.h b/Source/LexerParser/cmDependsJavaParserTokens.h
index 7f18f1d..6bbc084 100644
--- a/Source/LexerParser/cmDependsJavaParserTokens.h
+++ b/Source/LexerParser/cmDependsJavaParserTokens.h
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +31,9 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 #ifndef YY_CMDEPENDSJAVA_YY_CMDEPENDSJAVAPARSERTOKENS_H_INCLUDED
 # define YY_CMDEPENDSJAVA_YY_CMDEPENDSJAVAPARSERTOKENS_H_INCLUDED
 /* Debug traces.  */
diff --git a/Source/LexerParser/cmExprParser.cxx b/Source/LexerParser/cmExprParser.cxx
index 73ece2b..8416e72 100644
--- a/Source/LexerParser/cmExprParser.cxx
+++ b/Source/LexerParser/cmExprParser.cxx
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -40,11 +41,14 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 /* Identify Bison output.  */
 #define YYBISON 1
 
 /* Bison version.  */
-#define YYBISON_VERSION "3.0.4"
+#define YYBISON_VERSION "3.3.2"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -67,8 +71,8 @@
 #define yynerrs         cmExpr_yynerrs
 
 
-/* Copy the first part of user declarations.  */
-#line 1 "cmExprParser.y" /* yacc.c:339  */
+/* First part of user prologue.  */
+#line 1 "cmExprParser.y" /* yacc.c:337  */
 
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
@@ -112,13 +116,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 #endif
 
-#line 116 "cmExprParser.cxx" /* yacc.c:339  */
-
+#line 120 "cmExprParser.cxx" /* yacc.c:337  */
 # ifndef YY_NULLPTR
-#  if defined __cplusplus && 201103L <= __cplusplus
-#   define YY_NULLPTR nullptr
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
 #  else
-#   define YY_NULLPTR 0
+#   define YY_NULLPTR ((void*)0)
 #  endif
 # endif
 
@@ -187,9 +194,7 @@
 
 #endif /* !YY_CMEXPR_YY_CMEXPRPARSERTOKENS_H_INCLUDED  */
 
-/* Copy the second part of user declarations.  */
 
-#line 193 "cmExprParser.cxx" /* yacc.c:358  */
 
 #ifdef short
 # undef short
@@ -210,13 +215,13 @@
 #ifdef YYTYPE_UINT16
 typedef YYTYPE_UINT16 yytype_uint16;
 #else
-typedef unsigned short int yytype_uint16;
+typedef unsigned short yytype_uint16;
 #endif
 
 #ifdef YYTYPE_INT16
 typedef YYTYPE_INT16 yytype_int16;
 #else
-typedef short int yytype_int16;
+typedef short yytype_int16;
 #endif
 
 #ifndef YYSIZE_T
@@ -228,7 +233,7 @@
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
-#  define YYSIZE_T unsigned int
+#  define YYSIZE_T unsigned
 # endif
 #endif
 
@@ -264,15 +269,6 @@
 # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
 #endif
 
-#if !defined _Noreturn \
-     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
-# if defined _MSC_VER && 1200 <= _MSC_VER
-#  define _Noreturn __declspec (noreturn)
-# else
-#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
-# endif
-#endif
-
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
 # define YYUSE(E) ((void) (E))
@@ -280,7 +276,7 @@
 # define YYUSE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
     _Pragma ("GCC diagnostic push") \
@@ -429,29 +425,29 @@
 #endif /* !YYCOPY_NEEDED */
 
 /* YYFINAL -- State number of the termination state.  */
-#define YYFINAL  17
+#define YYFINAL  19
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   30
+#define YYLAST   34
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  17
 /* YYNNTS -- Number of nonterminals.  */
 #define YYNNTS  10
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  23
+#define YYNRULES  24
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  39
+#define YYNSTATES  41
 
-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
-   by yylex, with out-of-bounds checking.  */
 #define YYUNDEFTOK  2
 #define YYMAXUTOK   271
 
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
 #define YYTRANSLATE(YYX)                                                \
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+  ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
-   as returned by yylex, without out-of-bounds checking.  */
+   as returned by yylex.  */
 static const yytype_uint8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -490,7 +486,7 @@
 {
        0,    77,    77,    82,    85,    90,    93,    98,   101,   106,
      109,   112,   117,   120,   123,   128,   131,   134,   140,   145,
-     148,   151,   156,   159
+     148,   151,   154,   159,   162
 };
 #endif
 
@@ -517,10 +513,10 @@
 };
 # endif
 
-#define YYPACT_NINF -8
+#define YYPACT_NINF -11
 
 #define yypact_value_is_default(Yystate) \
-  (!!((Yystate) == (-8)))
+  (!!((Yystate) == (-11)))
 
 #define YYTABLE_NINF -1
 
@@ -531,10 +527,11 @@
      STATE-NUM.  */
 static const yytype_int8 yypact[] =
 {
-       0,     0,     0,     0,    -8,     2,    -7,    -5,     8,     3,
-      10,     1,    -8,    -8,    -8,    -8,     6,    -8,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,    -8,    -5,
-       8,     3,    10,    10,     1,     1,    -8,    -8,    -8
+       1,     1,     1,     1,     1,   -11,     6,   -10,    -4,     9,
+       4,    11,     2,   -11,   -11,   -11,   -11,     7,   -11,   -11,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+     -11,    -4,     9,     4,    11,    11,     2,     2,   -11,   -11,
+     -11
 };
 
   /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -542,22 +539,23 @@
      means the default is an error.  */
 static const yytype_uint8 yydefact[] =
 {
-       0,     0,     0,     0,    22,     0,     2,     3,     5,     7,
-       9,    12,    15,    19,    20,    21,     0,     1,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,    23,     4,
-       6,     8,    10,    11,    13,    14,    16,    17,    18
+       0,     0,     0,     0,     0,    23,     0,     2,     3,     5,
+       7,     9,    12,    15,    19,    20,    21,     0,    22,     1,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      24,     4,     6,     8,    10,    11,    13,    14,    16,    17,
+      18
 };
 
   /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int8 yypgoto[] =
 {
-      -8,    -8,    12,     5,    11,     9,    -2,     4,    -1,    -8
+     -11,   -11,    22,    10,     8,    12,    -3,    -2,    -1,   -11
 };
 
   /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int8 yydefgoto[] =
 {
-      -1,     5,     6,     7,     8,     9,    10,    11,    12,    13
+      -1,     6,     7,     8,     9,    10,    11,    12,    13,    14
 };
 
   /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
@@ -565,28 +563,29 @@
      number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_uint8 yytable[] =
 {
-      14,    15,    17,     1,     2,    18,    25,    26,    27,    19,
-       3,    21,    22,    23,    24,    16,     4,    28,    18,    32,
-      33,    20,     0,    29,    36,    37,    38,    34,    35,    31,
-      30
+      15,    16,    20,    18,     1,     2,    19,    27,    28,    29,
+      21,     3,    23,    24,    25,    26,     4,     5,    30,    20,
+      34,    35,    22,    36,    37,    17,    38,    39,    40,    32,
+      31,     0,     0,     0,    33
 };
 
 static const yytype_int8 yycheck[] =
 {
-       1,     2,     0,     3,     4,    12,     5,     6,     7,    14,
-      10,     8,     9,     3,     4,     3,    16,    11,    12,    21,
-      22,    13,    -1,    18,    25,    26,    27,    23,    24,    20,
-      19
+       1,     2,    12,     4,     3,     4,     0,     5,     6,     7,
+      14,    10,     8,     9,     3,     4,    15,    16,    11,    12,
+      23,    24,    13,    25,    26,     3,    27,    28,    29,    21,
+      20,    -1,    -1,    -1,    22
 };
 
   /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
      symbol of state STATE-NUM.  */
 static const yytype_uint8 yystos[] =
 {
-       0,     3,     4,    10,    16,    18,    19,    20,    21,    22,
-      23,    24,    25,    26,    25,    25,    19,     0,    12,    14,
-      13,     8,     9,     3,     4,     5,     6,     7,    11,    20,
-      21,    22,    23,    23,    24,    24,    25,    25,    25
+       0,     3,     4,    10,    15,    16,    18,    19,    20,    21,
+      22,    23,    24,    25,    26,    25,    25,    19,    25,     0,
+      12,    14,    13,     8,     9,     3,     4,     5,     6,     7,
+      11,    20,    21,    22,    23,    23,    24,    24,    25,    25,
+      25
 };
 
   /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
@@ -594,7 +593,7 @@
 {
        0,    17,    18,    19,    19,    20,    20,    21,    21,    22,
       22,    22,    23,    23,    23,    24,    24,    24,    24,    25,
-      25,    25,    26,    26
+      25,    25,    25,    26,    26
 };
 
   /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
@@ -602,7 +601,7 @@
 {
        0,     2,     1,     1,     3,     1,     3,     1,     3,     1,
        3,     3,     1,     3,     3,     1,     3,     3,     3,     1,
-       2,     2,     1,     3
+       2,     2,     2,     1,     3
 };
 
 
@@ -618,22 +617,22 @@
 
 #define YYRECOVERING()  (!!yyerrstatus)
 
-#define YYBACKUP(Token, Value)                                  \
-do                                                              \
-  if (yychar == YYEMPTY)                                        \
-    {                                                           \
-      yychar = (Token);                                         \
-      yylval = (Value);                                         \
-      YYPOPSTACK (yylen);                                       \
-      yystate = *yyssp;                                         \
-      goto yybackup;                                            \
-    }                                                           \
-  else                                                          \
-    {                                                           \
-      yyerror (yyscanner, YY_("syntax error: cannot back up")); \
-      YYERROR;                                                  \
-    }                                                           \
-while (0)
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (yyscanner, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
 
 /* Error token number */
 #define YYTERROR        1
@@ -673,38 +672,38 @@
 } while (0)
 
 
-/*----------------------------------------.
-| Print this symbol's value on YYOUTPUT.  |
-`----------------------------------------*/
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  FILE *yyo = yyoutput;
-  YYUSE (yyo);
+  FILE *yyoutput = yyo;
+  YYUSE (yyoutput);
   YYUSE (yyscanner);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
   if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+    YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
 # endif
   YYUSE (yytype);
 }
 
 
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  YYFPRINTF (yyoutput, "%s %s (",
+  YYFPRINTF (yyo, "%s %s (",
              yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
 
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yyscanner);
-  YYFPRINTF (yyoutput, ")");
+  yy_symbol_value_print (yyo, yytype, yyvaluep, yyscanner);
+  YYFPRINTF (yyo, ")");
 }
 
 /*------------------------------------------------------------------.
@@ -738,7 +737,7 @@
 static void
 yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, yyscan_t yyscanner)
 {
-  unsigned long int yylno = yyrline[yyrule];
+  unsigned long yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
   YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
@@ -749,7 +748,7 @@
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
                        yystos[yyssp[yyi + 1 - yynrhs]],
-                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                       &yyvsp[(yyi + 1) - (yynrhs)]
                                               , yyscanner);
       YYFPRINTF (stderr, "\n");
     }
@@ -853,7 +852,10 @@
           case '\\':
             if (*++yyp != '\\')
               goto do_not_strip_quotes;
-            /* Fall through.  */
+            else
+              goto append;
+
+          append:
           default:
             if (yyres)
               yyres[yyn] = *yyp;
@@ -871,7 +873,7 @@
   if (! yyres)
     return yystrlen (yystr);
 
-  return yystpcpy (yyres, yystr) - yyres;
+  return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres);
 }
 # endif
 
@@ -949,10 +951,10 @@
                 yyarg[yycount++] = yytname[yyx];
                 {
                   YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (! (yysize <= yysize1
-                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+                    yysize = yysize1;
+                  else
                     return 2;
-                  yysize = yysize1;
                 }
               }
         }
@@ -964,6 +966,7 @@
       case N:                               \
         yyformat = S;                       \
       break
+    default: /* Avoid compiler warnings. */
       YYCASE_(0, YY_("syntax error"));
       YYCASE_(1, YY_("syntax error, unexpected %s"));
       YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
@@ -975,9 +978,10 @@
 
   {
     YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+      yysize = yysize1;
+    else
       return 2;
-    yysize = yysize1;
   }
 
   if (*yymsg_alloc < yysize)
@@ -1108,23 +1112,31 @@
   yychar = YYEMPTY; /* Cause a token to be read.  */
   goto yysetstate;
 
+
 /*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
+| yynewstate -- push a new state, which is found in yystate.  |
 `------------------------------------------------------------*/
- yynewstate:
+yynewstate:
   /* In all cases, when you get here, the value and location stacks
      have just been pushed.  So pushing a state here evens the stacks.  */
   yyssp++;
 
- yysetstate:
-  *yyssp = yystate;
+
+/*--------------------------------------------------------------------.
+| yynewstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  *yyssp = (yytype_int16) yystate;
 
   if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
+      YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1);
 
-#ifdef yyoverflow
+# if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
@@ -1140,14 +1152,10 @@
                     &yyss1, yysize * sizeof (*yyssp),
                     &yyvs1, yysize * sizeof (*yyvsp),
                     &yystacksize);
-
         yyss = yyss1;
         yyvs = yyvs1;
       }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
+# else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
         goto yyexhaustedlab;
@@ -1163,22 +1171,22 @@
           goto yyexhaustedlab;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
         YYSTACK_RELOCATE (yyvs_alloc, yyvs);
-#  undef YYSTACK_RELOCATE
+# undef YYSTACK_RELOCATE
         if (yyss1 != yyssa)
           YYSTACK_FREE (yyss1);
       }
 # endif
-#endif /* no yyoverflow */
 
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
       YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long int) yystacksize));
+                  (unsigned long) yystacksize));
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
   YYDPRINTF ((stderr, "Entering state %d\n", yystate));
 
@@ -1187,11 +1195,11 @@
 
   goto yybackup;
 
+
 /*-----------.
 | yybackup.  |
 `-----------*/
 yybackup:
-
   /* Do appropriate processing given the current state.  Read a
      lookahead token if we need one and don't already have one.  */
 
@@ -1264,7 +1272,7 @@
 
 
 /*-----------------------------.
-| yyreduce -- Do a reduction.  |
+| yyreduce -- do a reduction.  |
 `-----------------------------*/
 yyreduce:
   /* yyn is the number of a rule to reduce with.  */
@@ -1285,186 +1293,194 @@
   switch (yyn)
     {
         case 2:
-#line 77 "cmExprParser.y" /* yacc.c:1646  */
+#line 77 "cmExprParser.y" /* yacc.c:1652  */
     {
     cmExpr_yyget_extra(yyscanner)->SetResult((yyvsp[0].Number));
   }
-#line 1293 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1301 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 3:
-#line 82 "cmExprParser.y" /* yacc.c:1646  */
+#line 82 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1301 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1309 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 4:
-#line 85 "cmExprParser.y" /* yacc.c:1646  */
+#line 85 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) | (yyvsp[0].Number);
   }
-#line 1309 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1317 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 5:
-#line 90 "cmExprParser.y" /* yacc.c:1646  */
+#line 90 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1317 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1325 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 6:
-#line 93 "cmExprParser.y" /* yacc.c:1646  */
+#line 93 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) ^ (yyvsp[0].Number);
   }
-#line 1325 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1333 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 7:
-#line 98 "cmExprParser.y" /* yacc.c:1646  */
+#line 98 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1333 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1341 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 8:
-#line 101 "cmExprParser.y" /* yacc.c:1646  */
+#line 101 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) & (yyvsp[0].Number);
   }
-#line 1341 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1349 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 9:
-#line 106 "cmExprParser.y" /* yacc.c:1646  */
+#line 106 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1349 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1357 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 10:
-#line 109 "cmExprParser.y" /* yacc.c:1646  */
+#line 109 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) << (yyvsp[0].Number);
   }
-#line 1357 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1365 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 11:
-#line 112 "cmExprParser.y" /* yacc.c:1646  */
+#line 112 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) >> (yyvsp[0].Number);
   }
-#line 1365 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1373 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 12:
-#line 117 "cmExprParser.y" /* yacc.c:1646  */
+#line 117 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1373 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1381 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 13:
-#line 120 "cmExprParser.y" /* yacc.c:1646  */
+#line 120 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) + (yyvsp[0].Number);
   }
-#line 1381 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1389 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 14:
-#line 123 "cmExprParser.y" /* yacc.c:1646  */
+#line 123 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) - (yyvsp[0].Number);
   }
-#line 1389 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1397 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 15:
-#line 128 "cmExprParser.y" /* yacc.c:1646  */
+#line 128 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1397 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1405 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 16:
-#line 131 "cmExprParser.y" /* yacc.c:1646  */
+#line 131 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) * (yyvsp[0].Number);
   }
-#line 1405 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1413 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 17:
-#line 134 "cmExprParser.y" /* yacc.c:1646  */
+#line 134 "cmExprParser.y" /* yacc.c:1652  */
     {
     if (yyvsp[0].Number == 0) {
       throw std::overflow_error("divide by zero");
     }
     (yyval.Number) = (yyvsp[-2].Number) / (yyvsp[0].Number);
   }
-#line 1416 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1424 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 18:
-#line 140 "cmExprParser.y" /* yacc.c:1646  */
+#line 140 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-2].Number) % (yyvsp[0].Number);
   }
-#line 1424 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1432 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 19:
-#line 145 "cmExprParser.y" /* yacc.c:1646  */
+#line 145 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1432 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1440 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 20:
-#line 148 "cmExprParser.y" /* yacc.c:1646  */
+#line 148 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = + (yyvsp[0].Number);
   }
-#line 1440 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1448 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 21:
-#line 151 "cmExprParser.y" /* yacc.c:1646  */
+#line 151 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = - (yyvsp[0].Number);
   }
-#line 1448 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1456 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 22:
-#line 156 "cmExprParser.y" /* yacc.c:1646  */
+#line 154 "cmExprParser.y" /* yacc.c:1652  */
     {
-    (yyval.Number) = (yyvsp[0].Number);
+    (yyval.Number) = ~ (yyvsp[0].Number);
   }
-#line 1456 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1464 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
   case 23:
-#line 159 "cmExprParser.y" /* yacc.c:1646  */
+#line 159 "cmExprParser.y" /* yacc.c:1652  */
+    {
+    (yyval.Number) = (yyvsp[0].Number);
+  }
+#line 1472 "cmExprParser.cxx" /* yacc.c:1652  */
+    break;
+
+  case 24:
+#line 162 "cmExprParser.y" /* yacc.c:1652  */
     {
     (yyval.Number) = (yyvsp[-1].Number);
   }
-#line 1464 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1480 "cmExprParser.cxx" /* yacc.c:1652  */
     break;
 
 
-#line 1468 "cmExprParser.cxx" /* yacc.c:1646  */
+#line 1484 "cmExprParser.cxx" /* yacc.c:1652  */
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1489,14 +1505,13 @@
   /* Now 'shift' the result of the reduction.  Determine what state
      that goes to, based on the state we popped back to and the rule
      number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
 
   goto yynewstate;
 
@@ -1580,12 +1595,10 @@
 | yyerrorlab -- error raised explicitly by YYERROR.  |
 `---------------------------------------------------*/
 yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -1648,6 +1661,7 @@
   yyresult = 0;
   goto yyreturn;
 
+
 /*-----------------------------------.
 | yyabortlab -- YYABORT comes here.  |
 `-----------------------------------*/
@@ -1655,6 +1669,7 @@
   yyresult = 1;
   goto yyreturn;
 
+
 #if !defined yyoverflow || YYERROR_VERBOSE
 /*-------------------------------------------------.
 | yyexhaustedlab -- memory exhaustion comes here.  |
@@ -1665,6 +1680,10 @@
   /* Fall through.  */
 #endif
 
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result.  |
+`-----------------------------------------------------*/
 yyreturn:
   if (yychar != YYEMPTY)
     {
@@ -1694,7 +1713,7 @@
 #endif
   return yyresult;
 }
-#line 164 "cmExprParser.y" /* yacc.c:1906  */
+#line 167 "cmExprParser.y" /* yacc.c:1918  */
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmExprParser.y b/Source/LexerParser/cmExprParser.y
index 2137473..7ae2118 100644
--- a/Source/LexerParser/cmExprParser.y
+++ b/Source/LexerParser/cmExprParser.y
@@ -151,6 +151,9 @@
 | exp_MINUS unary {
     $<Number>$ = - $<Number>2;
   }
+| exp_NOT unary {
+    $<Number>$ = ~ $<Number>2;
+  }
 
 factor:
   exp_NUMBER {
diff --git a/Source/LexerParser/cmExprParserTokens.h b/Source/LexerParser/cmExprParserTokens.h
index 84b2bbd..5ffd7c5 100644
--- a/Source/LexerParser/cmExprParserTokens.h
+++ b/Source/LexerParser/cmExprParserTokens.h
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +31,9 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 #ifndef YY_CMEXPR_YY_CMEXPRPARSERTOKENS_H_INCLUDED
 # define YY_CMEXPR_YY_CMEXPRPARSERTOKENS_H_INCLUDED
 /* Debug traces.  */
diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx
index 015cab9..2ca7927 100644
--- a/Source/LexerParser/cmFortranParser.cxx
+++ b/Source/LexerParser/cmFortranParser.cxx
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -40,11 +41,14 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 /* Identify Bison output.  */
 #define YYBISON 1
 
 /* Bison version.  */
-#define YYBISON_VERSION "3.0.4"
+#define YYBISON_VERSION "3.3.2"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -67,8 +71,8 @@
 #define yynerrs         cmFortran_yynerrs
 
 
-/* Copy the first part of user declarations.  */
-#line 1 "cmFortranParser.y" /* yacc.c:339  */
+/* First part of user prologue.  */
+#line 1 "cmFortranParser.y" /* yacc.c:337  */
 
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
@@ -131,13 +135,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 #endif
 
-#line 135 "cmFortranParser.cxx" /* yacc.c:339  */
-
+#line 139 "cmFortranParser.cxx" /* yacc.c:337  */
 # ifndef YY_NULLPTR
-#  if defined __cplusplus && 201103L <= __cplusplus
-#   define YY_NULLPTR nullptr
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
 #  else
-#   define YY_NULLPTR 0
+#   define YY_NULLPTR ((void*)0)
 #  endif
 # endif
 
@@ -251,11 +258,11 @@
 
 union YYSTYPE
 {
-#line 73 "cmFortranParser.y" /* yacc.c:355  */
+#line 73 "cmFortranParser.y" /* yacc.c:352  */
 
   char* string;
 
-#line 259 "cmFortranParser.cxx" /* yacc.c:355  */
+#line 266 "cmFortranParser.cxx" /* yacc.c:352  */
 };
 
 typedef union YYSTYPE YYSTYPE;
@@ -269,9 +276,7 @@
 
 #endif /* !YY_CMFORTRAN_YY_CMFORTRANPARSERTOKENS_H_INCLUDED  */
 
-/* Copy the second part of user declarations.  */
 
-#line 275 "cmFortranParser.cxx" /* yacc.c:358  */
 
 #ifdef short
 # undef short
@@ -292,13 +297,13 @@
 #ifdef YYTYPE_UINT16
 typedef YYTYPE_UINT16 yytype_uint16;
 #else
-typedef unsigned short int yytype_uint16;
+typedef unsigned short yytype_uint16;
 #endif
 
 #ifdef YYTYPE_INT16
 typedef YYTYPE_INT16 yytype_int16;
 #else
-typedef short int yytype_int16;
+typedef short yytype_int16;
 #endif
 
 #ifndef YYSIZE_T
@@ -310,7 +315,7 @@
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
-#  define YYSIZE_T unsigned int
+#  define YYSIZE_T unsigned
 # endif
 #endif
 
@@ -346,15 +351,6 @@
 # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
 #endif
 
-#if !defined _Noreturn \
-     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
-# if defined _MSC_VER && 1200 <= _MSC_VER
-#  define _Noreturn __declspec (noreturn)
-# else
-#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
-# endif
-#endif
-
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
 # define YYUSE(E) ((void) (E))
@@ -362,7 +358,7 @@
 # define YYUSE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
 # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
     _Pragma ("GCC diagnostic push") \
@@ -524,16 +520,16 @@
 /* YYNSTATES -- Number of states.  */
 #define YYNSTATES  126
 
-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
-   by yylex, with out-of-bounds checking.  */
 #define YYUNDEFTOK  2
 #define YYMAXUTOK   295
 
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
 #define YYTRANSLATE(YYX)                                                \
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+  ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
-   as returned by yylex, without out-of-bounds checking.  */
+   as returned by yylex.  */
 static const yytype_uint8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -863,22 +859,22 @@
 
 #define YYRECOVERING()  (!!yyerrstatus)
 
-#define YYBACKUP(Token, Value)                                  \
-do                                                              \
-  if (yychar == YYEMPTY)                                        \
-    {                                                           \
-      yychar = (Token);                                         \
-      yylval = (Value);                                         \
-      YYPOPSTACK (yylen);                                       \
-      yystate = *yyssp;                                         \
-      goto yybackup;                                            \
-    }                                                           \
-  else                                                          \
-    {                                                           \
-      yyerror (yyscanner, YY_("syntax error: cannot back up")); \
-      YYERROR;                                                  \
-    }                                                           \
-while (0)
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (yyscanner, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
 
 /* Error token number */
 #define YYTERROR        1
@@ -918,38 +914,38 @@
 } while (0)
 
 
-/*----------------------------------------.
-| Print this symbol's value on YYOUTPUT.  |
-`----------------------------------------*/
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  FILE *yyo = yyoutput;
-  YYUSE (yyo);
+  FILE *yyoutput = yyo;
+  YYUSE (yyoutput);
   YYUSE (yyscanner);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
   if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+    YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
 # endif
   YYUSE (yytype);
 }
 
 
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, yyscan_t yyscanner)
 {
-  YYFPRINTF (yyoutput, "%s %s (",
+  YYFPRINTF (yyo, "%s %s (",
              yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
 
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yyscanner);
-  YYFPRINTF (yyoutput, ")");
+  yy_symbol_value_print (yyo, yytype, yyvaluep, yyscanner);
+  YYFPRINTF (yyo, ")");
 }
 
 /*------------------------------------------------------------------.
@@ -983,7 +979,7 @@
 static void
 yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, yyscan_t yyscanner)
 {
-  unsigned long int yylno = yyrline[yyrule];
+  unsigned long yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
   YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
@@ -994,7 +990,7 @@
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
                        yystos[yyssp[yyi + 1 - yynrhs]],
-                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                       &yyvsp[(yyi + 1) - (yynrhs)]
                                               , yyscanner);
       YYFPRINTF (stderr, "\n");
     }
@@ -1098,7 +1094,10 @@
           case '\\':
             if (*++yyp != '\\')
               goto do_not_strip_quotes;
-            /* Fall through.  */
+            else
+              goto append;
+
+          append:
           default:
             if (yyres)
               yyres[yyn] = *yyp;
@@ -1116,7 +1115,7 @@
   if (! yyres)
     return yystrlen (yystr);
 
-  return yystpcpy (yyres, yystr) - yyres;
+  return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres);
 }
 # endif
 
@@ -1194,10 +1193,10 @@
                 yyarg[yycount++] = yytname[yyx];
                 {
                   YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (! (yysize <= yysize1
-                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+                    yysize = yysize1;
+                  else
                     return 2;
-                  yysize = yysize1;
                 }
               }
         }
@@ -1209,6 +1208,7 @@
       case N:                               \
         yyformat = S;                       \
       break
+    default: /* Avoid compiler warnings. */
       YYCASE_(0, YY_("syntax error"));
       YYCASE_(1, YY_("syntax error, unexpected %s"));
       YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
@@ -1220,9 +1220,10 @@
 
   {
     YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+      yysize = yysize1;
+    else
       return 2;
-    yysize = yysize1;
   }
 
   if (*yymsg_alloc < yysize)
@@ -1353,23 +1354,31 @@
   yychar = YYEMPTY; /* Cause a token to be read.  */
   goto yysetstate;
 
+
 /*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
+| yynewstate -- push a new state, which is found in yystate.  |
 `------------------------------------------------------------*/
- yynewstate:
+yynewstate:
   /* In all cases, when you get here, the value and location stacks
      have just been pushed.  So pushing a state here evens the stacks.  */
   yyssp++;
 
- yysetstate:
-  *yyssp = yystate;
+
+/*--------------------------------------------------------------------.
+| yynewstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  *yyssp = (yytype_int16) yystate;
 
   if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
+      YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1);
 
-#ifdef yyoverflow
+# if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
@@ -1385,14 +1394,10 @@
                     &yyss1, yysize * sizeof (*yyssp),
                     &yyvs1, yysize * sizeof (*yyvsp),
                     &yystacksize);
-
         yyss = yyss1;
         yyvs = yyvs1;
       }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
+# else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
         goto yyexhaustedlab;
@@ -1408,22 +1413,22 @@
           goto yyexhaustedlab;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
         YYSTACK_RELOCATE (yyvs_alloc, yyvs);
-#  undef YYSTACK_RELOCATE
+# undef YYSTACK_RELOCATE
         if (yyss1 != yyssa)
           YYSTACK_FREE (yyss1);
       }
 # endif
-#endif /* no yyoverflow */
 
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
       YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long int) yystacksize));
+                  (unsigned long) yystacksize));
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
   YYDPRINTF ((stderr, "Entering state %d\n", yystate));
 
@@ -1432,11 +1437,11 @@
 
   goto yybackup;
 
+
 /*-----------.
 | yybackup.  |
 `-----------*/
 yybackup:
-
   /* Do appropriate processing given the current state.  Read a
      lookahead token if we need one and don't already have one.  */
 
@@ -1509,7 +1514,7 @@
 
 
 /*-----------------------------.
-| yyreduce -- Do a reduction.  |
+| yyreduce -- do a reduction.  |
 `-----------------------------*/
 yyreduce:
   /* yyn is the number of a rule to reduce with.  */
@@ -1530,26 +1535,26 @@
   switch (yyn)
     {
         case 4:
-#line 104 "cmFortranParser.y" /* yacc.c:1646  */
+#line 104 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
   }
-#line 1539 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1544 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 5:
-#line 108 "cmFortranParser.y" /* yacc.c:1646  */
+#line 108 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1549 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1554 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 6:
-#line 113 "cmFortranParser.y" /* yacc.c:1646  */
+#line 113 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     if (cmsysString_strcasecmp((yyvsp[-2].string), "function") != 0 &&
@@ -1559,22 +1564,22 @@
     }
     free((yyvsp[-2].string));
   }
-#line 1563 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1568 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 7:
-#line 122 "cmFortranParser.y" /* yacc.c:1646  */
+#line 122 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string));
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1574 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1579 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 8:
-#line 128 "cmFortranParser.y" /* yacc.c:1646  */
+#line 128 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string));
@@ -1582,40 +1587,40 @@
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1586 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1591 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 9:
-#line 135 "cmFortranParser.y" /* yacc.c:1646  */
+#line 135 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
     free((yyvsp[-2].string));
   }
-#line 1596 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1601 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 10:
-#line 140 "cmFortranParser.y" /* yacc.c:1646  */
+#line 140 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
   }
-#line 1605 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1610 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 11:
-#line 144 "cmFortranParser.y" /* yacc.c:1646  */
+#line 144 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1615 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1620 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 12:
-#line 149 "cmFortranParser.y" /* yacc.c:1646  */
+#line 149 "cmFortranParser.y" /* yacc.c:1652  */
     {
     if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) {
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
@@ -1624,139 +1629,139 @@
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1628 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1633 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 13:
-#line 157 "cmFortranParser.y" /* yacc.c:1646  */
+#line 157 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1638 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1643 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 14:
-#line 162 "cmFortranParser.y" /* yacc.c:1646  */
+#line 162 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1648 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1653 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 15:
-#line 167 "cmFortranParser.y" /* yacc.c:1646  */
+#line 167 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1658 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1663 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 16:
-#line 172 "cmFortranParser.y" /* yacc.c:1646  */
+#line 172 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1668 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1673 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 17:
-#line 177 "cmFortranParser.y" /* yacc.c:1646  */
+#line 177 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1678 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1683 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 18:
-#line 182 "cmFortranParser.y" /* yacc.c:1646  */
+#line 182 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1688 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1693 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 19:
-#line 187 "cmFortranParser.y" /* yacc.c:1646  */
+#line 187 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1698 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1703 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 20:
-#line 192 "cmFortranParser.y" /* yacc.c:1646  */
+#line 192 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1708 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1713 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 21:
-#line 197 "cmFortranParser.y" /* yacc.c:1646  */
+#line 197 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIf(parser);
   }
-#line 1717 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1722 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 22:
-#line 201 "cmFortranParser.y" /* yacc.c:1646  */
+#line 201 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElif(parser);
   }
-#line 1726 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1731 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 23:
-#line 205 "cmFortranParser.y" /* yacc.c:1646  */
+#line 205 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElse(parser);
   }
-#line 1735 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1740 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 24:
-#line 209 "cmFortranParser.y" /* yacc.c:1646  */
+#line 209 "cmFortranParser.y" /* yacc.c:1652  */
     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleEndif(parser);
   }
-#line 1744 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1749 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 48:
-#line 231 "cmFortranParser.y" /* yacc.c:1646  */
+#line 231 "cmFortranParser.y" /* yacc.c:1652  */
     { free ((yyvsp[0].string)); }
-#line 1750 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1755 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
   case 55:
-#line 238 "cmFortranParser.y" /* yacc.c:1646  */
+#line 238 "cmFortranParser.y" /* yacc.c:1652  */
     { free ((yyvsp[0].string)); }
-#line 1756 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1761 "cmFortranParser.cxx" /* yacc.c:1652  */
     break;
 
 
-#line 1760 "cmFortranParser.cxx" /* yacc.c:1646  */
+#line 1765 "cmFortranParser.cxx" /* yacc.c:1652  */
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1781,14 +1786,13 @@
   /* Now 'shift' the result of the reduction.  Determine what state
      that goes to, based on the state we popped back to and the rule
      number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
 
   goto yynewstate;
 
@@ -1872,12 +1876,10 @@
 | yyerrorlab -- error raised explicitly by YYERROR.  |
 `---------------------------------------------------*/
 yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -1940,6 +1942,7 @@
   yyresult = 0;
   goto yyreturn;
 
+
 /*-----------------------------------.
 | yyabortlab -- YYABORT comes here.  |
 `-----------------------------------*/
@@ -1947,6 +1950,7 @@
   yyresult = 1;
   goto yyreturn;
 
+
 #if !defined yyoverflow || YYERROR_VERBOSE
 /*-------------------------------------------------.
 | yyexhaustedlab -- memory exhaustion comes here.  |
@@ -1957,6 +1961,10 @@
   /* Fall through.  */
 #endif
 
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result.  |
+`-----------------------------------------------------*/
 yyreturn:
   if (yychar != YYEMPTY)
     {
@@ -1986,6 +1994,6 @@
 #endif
   return yyresult;
 }
-#line 249 "cmFortranParser.y" /* yacc.c:1906  */
+#line 249 "cmFortranParser.y" /* yacc.c:1918  */
 
 /* End of grammar */
diff --git a/Source/LexerParser/cmFortranParserTokens.h b/Source/LexerParser/cmFortranParserTokens.h
index 29c6d60..0da4c1c 100644
--- a/Source/LexerParser/cmFortranParserTokens.h
+++ b/Source/LexerParser/cmFortranParserTokens.h
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.3.2.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +31,9 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 #ifndef YY_CMFORTRAN_YY_CMFORTRANPARSERTOKENS_H_INCLUDED
 # define YY_CMFORTRAN_YY_CMFORTRANPARSERTOKENS_H_INCLUDED
 /* Debug traces.  */
@@ -130,11 +134,11 @@
 
 union YYSTYPE
 {
-#line 73 "cmFortranParser.y" /* yacc.c:1909  */
+#line 73 "cmFortranParser.y" /* yacc.c:1921  */
 
   char* string;
 
-#line 138 "cmFortranParserTokens.h" /* yacc.c:1909  */
+#line 142 "cmFortranParserTokens.h" /* yacc.c:1921  */
 };
 
 typedef union YYSTYPE YYSTYPE;
diff --git a/Source/QtDialog/AddCacheEntry.h b/Source/QtDialog/AddCacheEntry.h
index 65e11c0..e7a60dd 100644
--- a/Source/QtDialog/AddCacheEntry.h
+++ b/Source/QtDialog/AddCacheEntry.h
@@ -4,7 +4,6 @@
 #define AddCacheEntry_h
 
 #include "QCMake.h"
-
 #include <QCheckBox>
 #include <QStringList>
 #include <QWidget>
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
index cb89d19..98dd0e2 100644
--- a/Source/QtDialog/CMakeLists.txt
+++ b/Source/QtDialog/CMakeLists.txt
@@ -178,6 +178,10 @@
   target_sources(cmake-gui PRIVATE $<TARGET_OBJECTS:CMakeVersion>)
 endif()
 
+if(CMake_JOB_POOL_LINK_BIN)
+  set_property(TARGET cmake-gui PROPERTY JOB_POOL_LINK "link-bin")
+endif()
+
 # cmake-gui has not been updated for `include-what-you-use`.
 # Block the tool until this is done.
 set_target_properties(cmake-gui PROPERTIES
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index c9ebba8..ee81a7d 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -1,16 +1,8 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "QCMake.h" // include to disable MS warnings
+#include <iostream>
 
-#include "CMakeSetupDialog.h"
-#include "cmAlgorithms.h"
-#include "cmDocumentation.h"
-#include "cmDocumentationEntry.h"
-#include "cmVersion.h"
-#include "cmake.h"
-#include "cmsys/CommandLineArguments.hxx"
-#include "cmsys/Encoding.hxx"
-#include "cmsys/SystemTools.hxx"
+#include "QCMake.h" // include to disable MS warnings
 #include <QApplication>
 #include <QDir>
 #include <QLocale>
@@ -18,9 +10,19 @@
 #include <QTextCodec>
 #include <QTranslator>
 #include <QtPlugin>
-#include <iostream>
 
+#include "cmsys/CommandLineArguments.hxx"
+#include "cmsys/Encoding.hxx"
+#include "cmsys/SystemTools.hxx"
+
+#include "CMakeSetupDialog.h"
+#include "cmAlgorithms.h"
+#include "cmDocumentation.h"
+#include "cmDocumentationEntry.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h" // IWYU pragma: keep
+#include "cmVersion.h"
+#include "cmake.h"
 
 static const char* cmDocumentationName[][2] = { { nullptr,
                                                   "  cmake-gui - CMake GUI." },
@@ -227,10 +229,12 @@
 }
 
 #if defined(Q_OS_MAC)
-#  include "cm_sys_stat.h"
-#  include <errno.h>
-#  include <string.h>
+#  include <cerrno>
+#  include <cstring>
+
 #  include <unistd.h>
+
+#  include "cm_sys_stat.h"
 static bool cmOSXInstall(std::string const& dir, std::string const& tool)
 {
   if (tool.empty()) {
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index e98cdcf..436a904 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -26,15 +26,17 @@
 #  include <QWinTaskbarProgress>
 #endif
 
-#include "AddCacheEntry.h"
-#include "FirstConfigure.h"
 #include "QCMake.h"
 #include "QCMakeCacheView.h"
-#include "RegexExplorer.h"
-#include "WarningMessagesDialog.h"
+
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
+#include "AddCacheEntry.h"
+#include "FirstConfigure.h"
+#include "RegexExplorer.h"
+#include "WarningMessagesDialog.h"
+
 QCMakeThread::QCMakeThread(QObject* p)
   : QThread(p)
   , CMakeInstance(nullptr)
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h
index 39c1053..f23aee6 100644
--- a/Source/QtDialog/CMakeSetupDialog.h
+++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -4,12 +4,12 @@
 #define CMakeSetupDialog_h
 
 #include "QCMake.h"
-
-#include "ui_CMakeSetupDialog.h"
 #include <QEventLoop>
 #include <QMainWindow>
 #include <QThread>
 
+#include "ui_CMakeSetupDialog.h"
+
 class QCMakeThread;
 class CMakeCacheModel;
 class QProgressBar;
diff --git a/Source/QtDialog/Compilers.h b/Source/QtDialog/Compilers.h
index 96770e3..931c935 100644
--- a/Source/QtDialog/Compilers.h
+++ b/Source/QtDialog/Compilers.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <QWidget>
-
 #include <ui_Compilers.h>
 
+#include <QWidget>
+
 class Compilers
   : public QWidget
   , public Ui::Compilers
diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx
index 364a378..ca28b19 100644
--- a/Source/QtDialog/FirstConfigure.cxx
+++ b/Source/QtDialog/FirstConfigure.cxx
@@ -1,13 +1,15 @@
 
 #include "FirstConfigure.h"
 
-#include "Compilers.h"
-
 #include <QComboBox>
 #include <QRadioButton>
 #include <QSettings>
 #include <QVBoxLayout>
 
+#include "cmStringAlgorithms.h"
+
+#include "Compilers.h"
+
 StartCompilerSetup::StartCompilerSetup(QWidget* p)
   : QWizardPage(p)
 {
@@ -106,8 +108,7 @@
         ->GeneratorDefaultPlatform[QString::fromLocal8Bit(gen.name.c_str())] =
         QString::fromLocal8Bit(gen.defaultPlatform.c_str());
 
-      std::vector<std::string>::const_iterator platformIt =
-        gen.supportedPlatforms.cbegin();
+      auto platformIt = gen.supportedPlatforms.cbegin();
       while (platformIt != gen.supportedPlatforms.cend()) {
 
         this->GeneratorSupportedPlatforms.insert(
@@ -183,10 +184,9 @@
   if (GeneratorsSupportingPlatform.contains(name)) {
 
     // Change the label title to include the default platform
-    std::string label = "Optional platform for generator";
-    label += "(if empty, generator uses: ";
-    label += this->GeneratorDefaultPlatform[name].toStdString();
-    label += ")";
+    std::string label =
+      cmStrCat("Optional platform for generator(if empty, generator uses: ",
+               this->GeneratorDefaultPlatform[name].toStdString(), ')');
     this->PlatformLabel->setText(tr(label.c_str()));
 
     // Regenerate the list of supported platform
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index f357f90..b608fcb 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -7,6 +7,7 @@
 
 #include "cmExternalMakefileProjectGenerator.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #ifdef Q_OS_WIN
@@ -256,14 +257,14 @@
     }
   }
 
-  // remove some properites
+  // remove some properties
   foreach (QString const& s, toremove) {
     this->CMakeInstance->UnwatchUnusedCli(s.toLocal8Bit().data());
 
     state->RemoveCacheEntry(s.toLocal8Bit().data());
   }
 
-  // add some new properites
+  // add some new properties
   foreach (QCMakeProperty const& s, props) {
     this->CMakeInstance->WatchUnusedCli(s.Key.toLocal8Bit().data());
 
@@ -312,7 +313,7 @@
     prop.Advanced = state->GetCacheEntryPropertyAsBool(key, "ADVANCED");
     if (t == cmStateEnums::BOOL) {
       prop.Type = QCMakeProperty::BOOL;
-      prop.Value = cmSystemTools::IsOn(cachedValue);
+      prop.Value = cmIsOn(cachedValue);
     } else if (t == cmStateEnums::PATH) {
       prop.Type = QCMakeProperty::PATH;
     } else if (t == cmStateEnums::FILEPATH) {
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
index f2fd6d9..fa4451b 100644
--- a/Source/QtDialog/QCMake.h
+++ b/Source/QtDialog/QCMake.h
@@ -50,7 +50,7 @@
 };
 
 // list of properties
-typedef QList<QCMakeProperty> QCMakePropertyList;
+using QCMakePropertyList = QList<QCMakeProperty>;
 
 // allow QVariant to be a property or list of properties
 Q_DECLARE_METATYPE(QCMakeProperty)
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx
index 78a2710..3e6a49e 100644
--- a/Source/QtDialog/QCMakeCacheView.cxx
+++ b/Source/QtDialog/QCMakeCacheView.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "QCMakeCacheView.h"
 
+#include "QCMakeWidgets.h"
 #include <QApplication>
 #include <QEvent>
 #include <QHBoxLayout>
@@ -11,8 +12,6 @@
 #include <QSortFilterProxyModel>
 #include <QStyle>
 
-#include "QCMakeWidgets.h"
-
 // filter for searches
 class QCMakeSearchFilter : public QSortFilterProxyModel
 {
@@ -210,7 +209,8 @@
 
 void QCMakeCacheModel::setProperties(const QCMakePropertyList& props)
 {
-  QSet<QCMakeProperty> newProps, newProps2;
+  QSet<QCMakeProperty> newProps;
+  QSet<QCMakeProperty> newProps2;
 
   if (this->ShowNewProperties) {
     newProps = props.toSet();
diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h
index c1debf5..bea1965 100644
--- a/Source/QtDialog/QCMakeCacheView.h
+++ b/Source/QtDialog/QCMakeCacheView.h
@@ -4,7 +4,6 @@
 #define QCMakeCacheView_h
 
 #include "QCMake.h"
-
 #include <QItemDelegate>
 #include <QSet>
 #include <QStandardItemModel>
diff --git a/Source/QtDialog/QCMakeWidgets.cxx b/Source/QtDialog/QCMakeWidgets.cxx
index eab418f..332a770 100644
--- a/Source/QtDialog/QCMakeWidgets.cxx
+++ b/Source/QtDialog/QCMakeWidgets.cxx
@@ -2,12 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "QCMakeWidgets.h"
 
+#include <utility>
+
 #include <QDirModel>
 #include <QFileDialog>
 #include <QFileInfo>
 #include <QResizeEvent>
 #include <QToolButton>
-#include <utility>
 
 QCMakeFileEditor::QCMakeFileEditor(QWidget* p, QString var)
   : QLineEdit(p)
diff --git a/Source/QtDialog/RegexExplorer.h b/Source/QtDialog/RegexExplorer.h
index c7dbb76..1a1d770 100644
--- a/Source/QtDialog/RegexExplorer.h
+++ b/Source/QtDialog/RegexExplorer.h
@@ -3,10 +3,12 @@
 #ifndef RegexExplorer_h
 #define RegexExplorer_h
 
-#include "cmsys/RegularExpression.hxx"
-#include <QDialog>
 #include <string>
 
+#include <QDialog>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "ui_RegexExplorer.h"
 
 class QString;
diff --git a/Source/QtDialog/WarningMessagesDialog.h b/Source/QtDialog/WarningMessagesDialog.h
index 9b29ad6..f209dbd 100644
--- a/Source/QtDialog/WarningMessagesDialog.h
+++ b/Source/QtDialog/WarningMessagesDialog.h
@@ -3,10 +3,10 @@
 #ifndef WarningMessagesDialog_h
 #define WarningMessagesDialog_h
 
+#include "QCMake.h"
 #include <QDialog>
 #include <QWidget>
 
-#include "QCMake.h"
 #include "ui_WarningMessagesDialog.h"
 
 /**
diff --git a/Source/bindexplib.cxx b/Source/bindexplib.cxx
index a107294..b85cc33 100644
--- a/Source/bindexplib.cxx
+++ b/Source/bindexplib.cxx
@@ -64,10 +64,16 @@
  */
 #include "bindexplib.h"
 
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include <windows.h>
+
 #include "cmsys/Encoding.hxx"
 #include "cmsys/FStream.hxx"
-#include <iostream>
-#include <windows.h>
+
+#include "cmSystemTools.h"
 
 #ifndef IMAGE_FILE_MACHINE_ARM
 #  define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
@@ -301,7 +307,63 @@
   bool IsI386;
 };
 
-bool DumpFile(const char* filename, std::set<std::string>& symbols,
+bool DumpFileWithLlvmNm(std::string const& nmPath, const char* filename,
+                        std::set<std::string>& symbols,
+                        std::set<std::string>& dataSymbols)
+{
+  std::string output;
+  // break up command line into a vector
+  std::vector<std::string> command;
+  command.push_back(nmPath);
+  command.push_back("--no-weak");
+  command.push_back("--defined-only");
+  command.push_back("--format=posix");
+  command.push_back(filename);
+
+  // run the command
+  int exit_code = 0;
+  cmSystemTools::RunSingleCommand(command, &output, &output, &exit_code, "",
+                                  cmSystemTools::OUTPUT_NONE);
+
+  if (exit_code != 0) {
+    fprintf(stderr, "llvm-nm returned an error: %s\n", output.c_str());
+    return false;
+  }
+
+  std::istringstream ss(output);
+  std::string line;
+  while (std::getline(ss, line)) {
+    if (line.empty()) { // last line
+      continue;
+    }
+    size_t sym_end = line.find(" ");
+    if (sym_end == std::string::npos) {
+      fprintf(stderr, "Couldn't parse llvm-nm output line: %s\n",
+              line.c_str());
+      return false;
+    }
+    if (line.size() < sym_end + 1) {
+      fprintf(stderr, "Couldn't parse llvm-nm output line: %s\n",
+              line.c_str());
+      return false;
+    }
+    const std::string sym = line.substr(0, sym_end);
+    const char sym_type = line[sym_end + 1];
+    switch (sym_type) {
+      case 'D':
+        dataSymbols.insert(sym);
+        break;
+      case 'T':
+        symbols.insert(sym);
+        break;
+    }
+  }
+
+  return true;
+}
+
+bool DumpFile(std::string const& nmPath, const char* filename,
+              std::set<std::string>& symbols,
               std::set<std::string>& dataSymbols)
 {
   HANDLE hFile;
@@ -356,16 +418,26 @@
         (imageHeader->Machine == IMAGE_FILE_MACHINE_I386));
       symbolDumper.DumpObjFile();
     } else {
-      // check for /bigobj format
+      // check for /bigobj and llvm LTO format
       cmANON_OBJECT_HEADER_BIGOBJ* h =
         (cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase;
       if (h->Sig1 == 0x0 && h->Sig2 == 0xffff) {
+        // bigobj
         DumpSymbols<cmANON_OBJECT_HEADER_BIGOBJ, cmIMAGE_SYMBOL_EX>
           symbolDumper((cmANON_OBJECT_HEADER_BIGOBJ*)lpFileBase, symbols,
                        dataSymbols, (h->Machine == IMAGE_FILE_MACHINE_I386));
         symbolDumper.DumpObjFile();
+      } else if (
+        // BCexCODE - llvm bitcode
+        (h->Sig1 == 0x4342 && h->Sig2 == 0xDEC0) ||
+        // 0x0B17C0DE - llvm bitcode BC wrapper
+        (h->Sig1 == 0x0B17 && h->Sig2 == 0xC0DE)) {
+
+        return DumpFileWithLlvmNm(nmPath, filename, symbols, dataSymbols);
+
       } else {
-        printf("unrecognized file format in '%s'\n", filename);
+        printf("unrecognized file format in '%s, %u'\n", filename,
+               imageHeader->Machine);
         return false;
       }
     }
@@ -378,7 +450,7 @@
 
 bool bindexplib::AddObjectFile(const char* filename)
 {
-  return DumpFile(filename, this->Symbols, this->DataSymbols);
+  return DumpFile(NmPath, filename, this->Symbols, this->DataSymbols);
 }
 
 bool bindexplib::AddDefinitionFile(const char* filename)
@@ -419,3 +491,8 @@
     fprintf(file, "\t%s\n", s.c_str());
   }
 }
+
+void bindexplib::SetNmPath(std::string const& nm)
+{
+  NmPath = nm;
+}
diff --git a/Source/bindexplib.h b/Source/bindexplib.h
index 3e22ac7..538177d 100644
--- a/Source/bindexplib.h
+++ b/Source/bindexplib.h
@@ -6,19 +6,23 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <set>
-#include <stdio.h>
 #include <string>
 
+#include <stdio.h>
+
 class bindexplib
 {
 public:
-  bindexplib() {}
+  bindexplib() { NmPath = "nm"; }
   bool AddDefinitionFile(const char* filename);
   bool AddObjectFile(const char* filename);
   void WriteFile(FILE* file);
 
+  void SetNmPath(std::string const& nm);
+
 private:
   std::set<std::string> Symbols;
   std::set<std::string> DataSymbols;
+  std::string NmPath;
 };
 #endif
diff --git a/Source/cmAddCompileDefinitionsCommand.cxx b/Source/cmAddCompileDefinitionsCommand.cxx
index 0474819..b00a4a7 100644
--- a/Source/cmAddCompileDefinitionsCommand.cxx
+++ b/Source/cmAddCompileDefinitionsCommand.cxx
@@ -2,19 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddCompileDefinitionsCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-bool cmAddCompileDefinitionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddCompileDefinitionsCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status)
 {
-  if (args.empty()) {
-    return true;
-  }
-
+  cmMakefile& mf = status.GetMakefile();
   for (std::string const& i : args) {
-    this->Makefile->AddCompileDefinition(i);
+    mf.AddCompileDefinition(i);
   }
   return true;
 }
diff --git a/Source/cmAddCompileDefinitionsCommand.h b/Source/cmAddCompileDefinitionsCommand.h
index e985dca..4bd621c 100644
--- a/Source/cmAddCompileDefinitionsCommand.h
+++ b/Source/cmAddCompileDefinitionsCommand.h
@@ -8,24 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmAddCompileDefinitionsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddCompileDefinitionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddCompileDefinitionsCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddCompileOptionsCommand.cxx b/Source/cmAddCompileOptionsCommand.cxx
index 412fb38..8ccb512 100644
--- a/Source/cmAddCompileOptionsCommand.cxx
+++ b/Source/cmAddCompileOptionsCommand.cxx
@@ -2,19 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddCompileOptionsCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-bool cmAddCompileOptionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddCompileOptionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
-  if (args.empty()) {
-    return true;
-  }
-
+  cmMakefile& mf = status.GetMakefile();
   for (std::string const& i : args) {
-    this->Makefile->AddCompileOption(i);
+    mf.AddCompileOption(i);
   }
   return true;
 }
diff --git a/Source/cmAddCompileOptionsCommand.h b/Source/cmAddCompileOptionsCommand.h
index 3d53d09..b172412 100644
--- a/Source/cmAddCompileOptionsCommand.h
+++ b/Source/cmAddCompileOptionsCommand.h
@@ -8,24 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmAddCompileOptionsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddCompileOptionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddCompileOptionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index 0be3c85..6e04ce5 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -4,43 +4,50 @@
 
 #include <sstream>
 #include <unordered_set>
-#include <utility>
 
+#include "cmCheckCustomOutputs.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmAddCustomCommandCommand
-bool cmAddCustomCommandCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
   /* Let's complain at the end of this function about the lack of a particular
      arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE
      are required.
   */
   if (args.size() < 4) {
-    this->SetError("called with wrong number of arguments.");
+    status.SetError("called with wrong number of arguments.");
     return false;
   }
 
-  std::string source, target, main_dependency, working, depfile, job_pool;
+  cmMakefile& mf = status.GetMakefile();
+  std::string source;
+  std::string target;
+  std::string main_dependency;
+  std::string working;
+  std::string depfile;
+  std::string job_pool;
   std::string comment_buffer;
   const char* comment = nullptr;
-  std::vector<std::string> depends, outputs, output, byproducts;
+  std::vector<std::string> depends;
+  std::vector<std::string> outputs;
+  std::vector<std::string> output;
+  std::vector<std::string> byproducts;
   bool verbatim = false;
   bool append = false;
   bool uses_terminal = false;
   bool command_expand_lists = false;
   std::string implicit_depends_lang;
-  cmCustomCommand::ImplicitDependsList implicit_depends;
+  cmImplicitDependsList implicit_depends;
 
   // Accumulate one command line at a time.
   cmCustomCommandLine currentLine;
@@ -48,7 +55,7 @@
   // Save all command lines.
   cmCustomCommandLines commandLines;
 
-  cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
+  cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
 
   enum tdoing
   {
@@ -95,30 +102,29 @@
   MAKE_STATIC_KEYWORD(VERBATIM);
   MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
 #undef MAKE_STATIC_KEYWORD
-  static std::unordered_set<std::string> keywords;
-  if (keywords.empty()) {
-    keywords.insert(keyAPPEND);
-    keywords.insert(keyARGS);
-    keywords.insert(keyBYPRODUCTS);
-    keywords.insert(keyCOMMAND);
-    keywords.insert(keyCOMMAND_EXPAND_LISTS);
-    keywords.insert(keyCOMMENT);
-    keywords.insert(keyDEPENDS);
-    keywords.insert(keyDEPFILE);
-    keywords.insert(keyIMPLICIT_DEPENDS);
-    keywords.insert(keyJOB_POOL);
-    keywords.insert(keyMAIN_DEPENDENCY);
-    keywords.insert(keyOUTPUT);
-    keywords.insert(keyOUTPUTS);
-    keywords.insert(keyPOST_BUILD);
-    keywords.insert(keyPRE_BUILD);
-    keywords.insert(keyPRE_LINK);
-    keywords.insert(keySOURCE);
-    keywords.insert(keyTARGET);
-    keywords.insert(keyUSES_TERMINAL);
-    keywords.insert(keyVERBATIM);
-    keywords.insert(keyWORKING_DIRECTORY);
-  }
+  static std::unordered_set<std::string> const keywords{
+    keyAPPEND,
+    keyARGS,
+    keyBYPRODUCTS,
+    keyCOMMAND,
+    keyCOMMAND_EXPAND_LISTS,
+    keyCOMMENT,
+    keyDEPENDS,
+    keyDEPFILE,
+    keyIMPLICIT_DEPENDS,
+    keyJOB_POOL,
+    keyMAIN_DEPENDENCY,
+    keyOUTPUT,
+    keyOUTPUTS,
+    keyPOST_BUILD,
+    keyPRE_BUILD,
+    keyPRE_LINK,
+    keySOURCE,
+    keyTARGET,
+    keyUSES_TERMINAL,
+    keyVERBATIM,
+    keyWORKING_DIRECTORY
+  };
 
   for (std::string const& copy : args) {
     if (keywords.count(copy)) {
@@ -133,11 +139,11 @@
           currentLine.clear();
         }
       } else if (copy == keyPRE_BUILD) {
-        cctype = cmTarget::PRE_BUILD;
+        cctype = cmCustomCommandType::PRE_BUILD;
       } else if (copy == keyPRE_LINK) {
-        cctype = cmTarget::PRE_LINK;
+        cctype = cmCustomCommandType::PRE_LINK;
       } else if (copy == keyPOST_BUILD) {
-        cctype = cmTarget::POST_BUILD;
+        cctype = cmCustomCommandType::POST_BUILD;
       } else if (copy == keyVERBATIM) {
         verbatim = true;
       } else if (copy == keyAPPEND) {
@@ -168,9 +174,9 @@
         doing = doing_comment;
       } else if (copy == keyDEPFILE) {
         doing = doing_depfile;
-        if (this->Makefile->GetGlobalGenerator()->GetName() != "Ninja") {
-          this->SetError("Option DEPFILE not supported by " +
-                         this->Makefile->GetGlobalGenerator()->GetName());
+        if (mf.GetGlobalGenerator()->GetName() != "Ninja") {
+          status.SetError("Option DEPFILE not supported by " +
+                          mf.GetGlobalGenerator()->GetName());
           return false;
         }
       } else if (copy == keyJOB_POOL) {
@@ -193,8 +199,7 @@
             // and later references "${CMAKE_CURRENT_SOURCE_DIR}/out.txt".
             // This is fairly obscure so we can wait for someone to
             // complain.
-            filename = this->Makefile->GetCurrentBinaryDirectory();
-            filename += "/";
+            filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/');
           }
           filename += copy;
           cmSystemTools::ConvertToUnixSlashes(filename);
@@ -239,6 +244,8 @@
           // An implicit dependency starting point is also an
           // explicit dependency.
           std::string dep = copy;
+          // Upfront path conversion is correct because Genex
+          // are not supported.
           cmSystemTools::ConvertToUnixSlashes(dep);
           depends.push_back(dep);
 
@@ -255,9 +262,7 @@
           target = copy;
           break;
         case doing_depends: {
-          std::string dep = copy;
-          cmSystemTools::ConvertToUnixSlashes(dep);
-          depends.push_back(std::move(dep));
+          depends.push_back(copy);
         } break;
         case doing_outputs:
           outputs.push_back(filename);
@@ -270,7 +275,7 @@
           comment = comment_buffer.c_str();
           break;
         default:
-          this->SetError("Wrong syntax. Unknown type of argument.");
+          status.SetError("Wrong syntax. Unknown type of argument.");
           return false;
       }
     }
@@ -285,49 +290,43 @@
   // At this point we could complain about the lack of arguments.  For
   // the moment, let's say that COMMAND, TARGET are always required.
   if (output.empty() && target.empty()) {
-    this->SetError("Wrong syntax. A TARGET or OUTPUT must be specified.");
+    status.SetError("Wrong syntax. A TARGET or OUTPUT must be specified.");
     return false;
   }
 
   if (source.empty() && !target.empty() && !output.empty()) {
-    this->SetError(
+    status.SetError(
       "Wrong syntax. A TARGET and OUTPUT can not both be specified.");
     return false;
   }
   if (append && output.empty()) {
-    this->SetError("given APPEND option with no OUTPUT.");
+    status.SetError("given APPEND option with no OUTPUT.");
     return false;
   }
 
   // Make sure the output names and locations are safe.
-  if (!this->CheckOutputs(output) || !this->CheckOutputs(outputs) ||
-      !this->CheckOutputs(byproducts)) {
+  if (!cmCheckCustomOutputs(output, "OUTPUT", status) ||
+      !cmCheckCustomOutputs(outputs, "OUTPUTS", status) ||
+      !cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) {
     return false;
   }
 
   // Check for an append request.
   if (append) {
-    // Lookup an existing command.
-    if (cmSourceFile* sf =
-          this->Makefile->GetSourceFileWithOutput(output[0])) {
-      if (cmCustomCommand* cc = sf->GetCustomCommand()) {
-        cc->AppendCommands(commandLines);
-        cc->AppendDepends(depends);
-        cc->AppendImplicitDepends(implicit_depends);
-        return true;
-      }
+    if (mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends,
+                                       commandLines)) {
+      return true;
     }
 
     // No command for this output exists.
-    std::ostringstream e;
-    e << "given APPEND option with output\n\"" << output[0]
-      << "\"\nwhich is not already a custom command output.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("given APPEND option with output\n  ", output[0],
+               "\nwhich is not already a custom command output."));
     return false;
   }
 
   if (uses_terminal && !job_pool.empty()) {
-    this->SetError("JOB_POOL is shadowed by USES_TERMINAL.");
+    status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
     return false;
   }
 
@@ -336,46 +335,27 @@
   if (source.empty() && output.empty()) {
     // Source is empty, use the target.
     std::vector<std::string> no_depends;
-    this->Makefile->AddCustomCommandToTarget(
-      target, byproducts, no_depends, commandLines, cctype, comment,
-      working.c_str(), escapeOldStyle, uses_terminal, depfile, job_pool,
-      command_expand_lists);
+    mf.AddCustomCommandToTarget(target, byproducts, no_depends, commandLines,
+                                cctype, comment, working.c_str(),
+                                escapeOldStyle, uses_terminal, depfile,
+                                job_pool, command_expand_lists);
   } else if (target.empty()) {
     // Target is empty, use the output.
-    this->Makefile->AddCustomCommandToOutput(
-      output, byproducts, depends, main_dependency, commandLines, comment,
-      working.c_str(), false, escapeOldStyle, uses_terminal,
-      command_expand_lists, depfile, job_pool);
-
-    // Add implicit dependency scanning requests if any were given.
-    if (!implicit_depends.empty()) {
-      bool okay = false;
-      if (cmSourceFile* sf =
-            this->Makefile->GetSourceFileWithOutput(output[0])) {
-        if (cmCustomCommand* cc = sf->GetCustomCommand()) {
-          okay = true;
-          cc->SetImplicitDepends(implicit_depends);
-        }
-      }
-      if (!okay) {
-        std::ostringstream e;
-        e << "could not locate source file with a custom command producing \""
-          << output[0] << "\" even though this command tried to create it!";
-        this->SetError(e.str());
-        return false;
-      }
-    }
+    mf.AddCustomCommandToOutput(
+      output, byproducts, depends, main_dependency, implicit_depends,
+      commandLines, comment, working.c_str(), false, escapeOldStyle,
+      uses_terminal, command_expand_lists, depfile, job_pool);
   } else if (!byproducts.empty()) {
-    this->SetError("BYPRODUCTS may not be specified with SOURCE signatures");
+    status.SetError("BYPRODUCTS may not be specified with SOURCE signatures");
     return false;
   } else if (uses_terminal) {
-    this->SetError("USES_TERMINAL may not be used with SOURCE signatures");
+    status.SetError("USES_TERMINAL may not be used with SOURCE signatures");
     return false;
   } else {
     bool issueMessage = true;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0050)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0050)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0050) << "\n";
         break;
@@ -392,43 +372,16 @@
     if (issueMessage) {
       e << "The SOURCE signatures of add_custom_command are no longer "
            "supported.";
-      this->Makefile->IssueMessage(messageType, e.str());
+      mf.IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
     }
 
     // Use the old-style mode for backward compatibility.
-    this->Makefile->AddCustomCommandOldStyle(target, outputs, depends, source,
-                                             commandLines, comment);
+    mf.AddCustomCommandOldStyle(target, outputs, depends, source, commandLines,
+                                comment);
   }
 
   return true;
 }
-
-bool cmAddCustomCommandCommand::CheckOutputs(
-  const std::vector<std::string>& outputs)
-{
-  for (std::string const& o : outputs) {
-    // Make sure the file will not be generated into the source
-    // directory during an out of source build.
-    if (!this->Makefile->CanIWriteThisFile(o)) {
-      std::string e = "attempted to have a file \"" + o +
-        "\" in a source directory as an output of custom command.";
-      this->SetError(e);
-      cmSystemTools::SetFatalErrorOccured();
-      return false;
-    }
-
-    // Make sure the output file name has no invalid characters.
-    std::string::size_type pos = o.find_first_of("#<>");
-    if (pos != std::string::npos) {
-      std::ostringstream msg;
-      msg << "called with OUTPUT containing a \"" << o[pos]
-          << "\".  This character is not allowed.";
-      this->SetError(msg.str());
-      return false;
-    }
-  }
-  return true;
-}
diff --git a/Source/cmAddCustomCommandCommand.h b/Source/cmAddCustomCommandCommand.h
index 6af4f10..4f8c58f 100644
--- a/Source/cmAddCustomCommandCommand.h
+++ b/Source/cmAddCustomCommandCommand.h
@@ -8,33 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddCustomCommandCommand
- * \brief cmAddCustomCommandCommand defines a new command (rule) that can
- *  be executed within the build process
- *
- */
-
-class cmAddCustomCommandCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddCustomCommandCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  bool CheckOutputs(const std::vector<std::string>& outputs);
-};
+bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx
index 0ecd5f5..e27b126 100644
--- a/Source/cmAddCustomTargetCommand.cxx
+++ b/Source/cmAddCustomTargetCommand.cxx
@@ -2,38 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddCustomTargetCommand.h"
 
-#include <sstream>
 #include <utility>
 
+#include "cmCheckCustomOutputs.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmAddCustomTargetCommand
-bool cmAddCustomTargetCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
   std::string const& targetName = args[0];
 
   // Check the target name.
   if (targetName.find_first_of("/\\") != std::string::npos) {
-    std::ostringstream e;
-    e << "called with invalid target name \"" << targetName
-      << "\".  Target names may not contain a slash.  "
-      << "Use ADD_CUSTOM_COMMAND to generate files.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat("called with invalid target name \"", targetName,
+                             "\".  Target names may not contain a slash.  "
+                             "Use ADD_CUSTOM_COMMAND to generate files."));
     return false;
   }
 
@@ -44,7 +43,8 @@
   cmCustomCommandLines commandLines;
 
   // Accumulate dependencies.
-  std::vector<std::string> depends, byproducts;
+  std::vector<std::string> depends;
+  std::vector<std::string> byproducts;
   std::string working_directory;
   bool verbatim = false;
   bool uses_terminal = false;
@@ -122,12 +122,11 @@
         case doing_byproducts: {
           std::string filename;
           if (!cmSystemTools::FileIsFullPath(copy)) {
-            filename = this->Makefile->GetCurrentBinaryDirectory();
-            filename += "/";
+            filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/');
           }
           filename += copy;
           cmSystemTools::ConvertToUnixSlashes(filename);
-          byproducts.push_back(filename);
+          byproducts.push_back(cmSystemTools::CollapseFullPath(filename));
         } break;
         case doing_depends: {
           std::string dep = copy;
@@ -145,7 +144,7 @@
           job_pool = copy;
           break;
         default:
-          this->SetError("Wrong syntax. Unknown type of argument.");
+          status.SetError("Wrong syntax. Unknown type of argument.");
           return false;
       }
     }
@@ -153,10 +152,9 @@
 
   std::string::size_type pos = targetName.find_first_of("#<>");
   if (pos != std::string::npos) {
-    std::ostringstream msg;
-    msg << "called with target name containing a \"" << targetName[pos]
-        << "\".  This character is not allowed.";
-    this->SetError(msg.str());
+    status.SetError(cmStrCat("called with target name containing a \"",
+                             targetName[pos],
+                             "\".  This character is not allowed."));
     return false;
   }
 
@@ -168,8 +166,7 @@
   if (nameOk) {
     nameOk = targetName.find(':') == std::string::npos;
   }
-  if (!nameOk &&
-      !this->Makefile->CheckCMP0037(targetName, cmStateEnums::UTILITY)) {
+  if (!nameOk && !mf.CheckCMP0037(targetName, cmStateEnums::UTILITY)) {
     return false;
   }
 
@@ -182,40 +179,43 @@
   // Enforce name uniqueness.
   {
     std::string msg;
-    if (!this->Makefile->EnforceUniqueName(targetName, msg, true)) {
-      this->SetError(msg);
+    if (!mf.EnforceUniqueName(targetName, msg, true)) {
+      status.SetError(msg);
       return false;
     }
   }
 
   if (commandLines.empty() && !byproducts.empty()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "BYPRODUCTS may not be specified without any COMMAND");
+    mf.IssueMessage(MessageType::FATAL_ERROR,
+                    "BYPRODUCTS may not be specified without any COMMAND");
     return true;
   }
   if (commandLines.empty() && uses_terminal) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "USES_TERMINAL may not be specified without any COMMAND");
+    mf.IssueMessage(MessageType::FATAL_ERROR,
+                    "USES_TERMINAL may not be specified without any COMMAND");
     return true;
   }
   if (commandLines.empty() && command_expand_lists) {
-    this->Makefile->IssueMessage(
+    mf.IssueMessage(
       MessageType::FATAL_ERROR,
       "COMMAND_EXPAND_LISTS may not be specified without any COMMAND");
     return true;
   }
 
   if (uses_terminal && !job_pool.empty()) {
-    this->SetError("JOB_POOL is shadowed by USES_TERMINAL.");
+    status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
+    return false;
+  }
+
+  // Make sure the byproduct names and locations are safe.
+  if (!cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) {
     return false;
   }
 
   // Add the utility target to the makefile.
   bool escapeOldStyle = !verbatim;
-  cmTarget* target = this->Makefile->AddUtilityCommand(
-    targetName, cmMakefile::TargetOrigin::Project, excludeFromAll,
+  cmTarget* target = mf.AddUtilityCommand(
+    targetName, cmCommandOrigin::Project, excludeFromAll,
     working_directory.c_str(), byproducts, depends, commandLines,
     escapeOldStyle, comment, uses_terminal, command_expand_lists, job_pool);
 
diff --git a/Source/cmAddCustomTargetCommand.h b/Source/cmAddCustomTargetCommand.h
index 1a55116..e23ef9f 100644
--- a/Source/cmAddCustomTargetCommand.h
+++ b/Source/cmAddCustomTargetCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddCustomTargetCommand
- * \brief Command that adds a target to the build system.
- *
- * cmAddCustomTargetCommand adds an extra target to the build system.
- * This is useful when you would like to add special
- * targets like "install,", "clean," and so on.
- */
-class cmAddCustomTargetCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddCustomTargetCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddDefinitionsCommand.cxx b/Source/cmAddDefinitionsCommand.cxx
index 62e57a3..0b10aee 100644
--- a/Source/cmAddDefinitionsCommand.cxx
+++ b/Source/cmAddDefinitionsCommand.cxx
@@ -2,21 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddDefinitionsCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// cmAddDefinitionsCommand
-bool cmAddDefinitionsCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmAddDefinitionsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
-  // it is OK to have no arguments
-  if (args.empty()) {
-    return true;
-  }
-
+  cmMakefile& mf = status.GetMakefile();
   for (std::string const& i : args) {
-    this->Makefile->AddDefineFlag(i);
+    mf.AddDefineFlag(i);
   }
   return true;
 }
diff --git a/Source/cmAddDefinitionsCommand.h b/Source/cmAddDefinitionsCommand.h
index 7b75638..a67f095 100644
--- a/Source/cmAddDefinitionsCommand.h
+++ b/Source/cmAddDefinitionsCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddDefinitionsCommand
- * \brief Specify a list of compiler defines
- *
- * cmAddDefinitionsCommand specifies a list of compiler defines. These defines
- * will be added to the compile command.
- */
-class cmAddDefinitionsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddDefinitionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddDefinitionsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddDependenciesCommand.cxx b/Source/cmAddDependenciesCommand.cxx
index 4956a47..b1fc893 100644
--- a/Source/cmAddDependenciesCommand.cxx
+++ b/Source/cmAddDependenciesCommand.cxx
@@ -2,46 +2,47 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddDependenciesCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmDependenciesCommand
-bool cmAddDependenciesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddDependenciesCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
   std::string const& target_name = args[0];
-  if (this->Makefile->IsAlias(target_name)) {
-    std::ostringstream e;
-    e << "Cannot add target-level dependencies to alias target \""
-      << target_name << "\".\n";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  if (mf.IsAlias(target_name)) {
+    mf.IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot add target-level dependencies to alias target \"",
+               target_name, "\".\n"));
   }
-  if (cmTarget* target = this->Makefile->FindTargetToUse(target_name)) {
+  if (cmTarget* target = mf.FindTargetToUse(target_name)) {
 
     // skip over target_name
     for (std::string const& arg : cmMakeRange(args).advance(1)) {
-      target->AddUtility(arg, this->Makefile);
+      target->AddUtility(arg, &mf);
     }
   } else {
-    std::ostringstream e;
-    e << "Cannot add target-level dependencies to non-existent target \""
-      << target_name << "\".\n"
-      << "The add_dependencies works for top-level logical targets created "
-      << "by the add_executable, add_library, or add_custom_target commands.  "
-      << "If you want to add file-level dependencies see the DEPENDS option "
-      << "of the add_custom_target and add_custom_command commands.";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf.IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "Cannot add target-level dependencies to non-existent "
+        "target \"",
+        target_name,
+        "\".\nThe add_dependencies works for "
+        "top-level logical targets created by the add_executable, "
+        "add_library, or add_custom_target commands.  If you want to add "
+        "file-level dependencies see the DEPENDS option of the "
+        "add_custom_target and add_custom_command commands."));
   }
 
   return true;
diff --git a/Source/cmAddDependenciesCommand.h b/Source/cmAddDependenciesCommand.h
index e10df71..0c60e3a 100644
--- a/Source/cmAddDependenciesCommand.h
+++ b/Source/cmAddDependenciesCommand.h
@@ -8,29 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddDependenciesCommand
- * \brief Add a dependency to a target
- *
- * cmAddDependenciesCommand adds a dependency to a target
- */
-class cmAddDependenciesCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddDependenciesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddDependenciesCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddExecutableCommand.cxx b/Source/cmAddExecutableCommand.cxx
index 5685fdf..e738bc4 100644
--- a/Source/cmAddExecutableCommand.cxx
+++ b/Source/cmAddExecutableCommand.cxx
@@ -2,25 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddExecutableCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmExecutableCommand
-bool cmAddExecutableCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmAddExecutableCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
-  std::vector<std::string>::const_iterator s = args.begin();
+
+  cmMakefile& mf = status.GetMakefile();
+  auto s = args.begin();
 
   std::string const& exename = *s;
 
@@ -61,110 +60,100 @@
   if (nameOk && !importTarget && !isAlias) {
     nameOk = exename.find(':') == std::string::npos;
   }
-  if (!nameOk &&
-      !this->Makefile->CheckCMP0037(exename, cmStateEnums::EXECUTABLE)) {
+  if (!nameOk && !mf.CheckCMP0037(exename, cmStateEnums::EXECUTABLE)) {
     return false;
   }
 
   // Special modifiers are not allowed with IMPORTED signature.
   if (importTarget && (use_win32 || use_macbundle || excludeFromAll)) {
     if (use_win32) {
-      this->SetError("may not be given WIN32 for an IMPORTED target.");
+      status.SetError("may not be given WIN32 for an IMPORTED target.");
     } else if (use_macbundle) {
-      this->SetError("may not be given MACOSX_BUNDLE for an IMPORTED target.");
+      status.SetError(
+        "may not be given MACOSX_BUNDLE for an IMPORTED target.");
     } else // if(excludeFromAll)
     {
-      this->SetError(
+      status.SetError(
         "may not be given EXCLUDE_FROM_ALL for an IMPORTED target.");
     }
     return false;
   }
   if (isAlias) {
     if (!cmGeneratorExpression::IsValidTargetName(exename)) {
-      this->SetError("Invalid name for ALIAS: " + exename);
+      status.SetError("Invalid name for ALIAS: " + exename);
       return false;
     }
     if (excludeFromAll) {
-      this->SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
+      status.SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
       return false;
     }
     if (importTarget || importGlobal) {
-      this->SetError("IMPORTED with ALIAS is not allowed.");
+      status.SetError("IMPORTED with ALIAS is not allowed.");
       return false;
     }
     if (args.size() != 3) {
-      std::ostringstream e;
-      e << "ALIAS requires exactly one target argument.";
-      this->SetError(e.str());
+      status.SetError("ALIAS requires exactly one target argument.");
       return false;
     }
 
     std::string const& aliasedName = *s;
-    if (this->Makefile->IsAlias(aliasedName)) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << exename << "\" because target \""
-        << aliasedName << "\" is itself an ALIAS.";
-      this->SetError(e.str());
+    if (mf.IsAlias(aliasedName)) {
+      status.SetError(cmStrCat("cannot create ALIAS target \"", exename,
+                               "\" because target \"", aliasedName,
+                               "\" is itself an ALIAS."));
       return false;
     }
-    cmTarget* aliasedTarget =
-      this->Makefile->FindTargetToUse(aliasedName, true);
+    cmTarget* aliasedTarget = mf.FindTargetToUse(aliasedName, true);
     if (!aliasedTarget) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << exename << "\" because target \""
-        << aliasedName << "\" does not already exist.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", exename,
+                               "\" because target \"", aliasedName,
+                               "\" does not already exist."));
       return false;
     }
     cmStateEnums::TargetType type = aliasedTarget->GetType();
     if (type != cmStateEnums::EXECUTABLE) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << exename << "\" because target \""
-        << aliasedName << "\" is not an executable.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", exename,
+                               "\" because target \"", aliasedName,
+                               "\" is not an executable."));
       return false;
     }
     if (aliasedTarget->IsImported() &&
         !aliasedTarget->IsImportedGloballyVisible()) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << exename << "\" because target \""
-        << aliasedName << "\" is imported but not globally visible.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", exename,
+                               "\" because target \"", aliasedName,
+                               "\" is imported but not globally visible."));
       return false;
     }
-    this->Makefile->AddAlias(exename, aliasedName);
+    mf.AddAlias(exename, aliasedName);
     return true;
   }
 
   // Handle imported target creation.
   if (importTarget) {
     // Make sure the target does not already exist.
-    if (this->Makefile->FindTargetToUse(exename)) {
-      std::ostringstream e;
-      e << "cannot create imported target \"" << exename
-        << "\" because another target with the same name already exists.";
-      this->SetError(e.str());
+    if (mf.FindTargetToUse(exename)) {
+      status.SetError(cmStrCat(
+        "cannot create imported target \"", exename,
+        "\" because another target with the same name already exists."));
       return false;
     }
 
     // Create the imported target.
-    this->Makefile->AddImportedTarget(exename, cmStateEnums::EXECUTABLE,
-                                      importGlobal);
+    mf.AddImportedTarget(exename, cmStateEnums::EXECUTABLE, importGlobal);
     return true;
   }
 
   // Enforce name uniqueness.
   {
     std::string msg;
-    if (!this->Makefile->EnforceUniqueName(exename, msg)) {
-      this->SetError(msg);
+    if (!mf.EnforceUniqueName(exename, msg)) {
+      status.SetError(msg);
       return false;
     }
   }
 
   std::vector<std::string> srclists(s, args.end());
-  cmTarget* tgt =
-    this->Makefile->AddExecutable(exename, srclists, excludeFromAll);
+  cmTarget* tgt = mf.AddExecutable(exename, srclists, excludeFromAll);
   if (use_win32) {
     tgt->SetProperty("WIN32_EXECUTABLE", "ON");
   }
diff --git a/Source/cmAddExecutableCommand.h b/Source/cmAddExecutableCommand.h
index bdf607d..f7bc273 100644
--- a/Source/cmAddExecutableCommand.h
+++ b/Source/cmAddExecutableCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmExecutablesCommand
- * \brief Defines a list of executables to build.
- *
- * cmExecutablesCommand defines a list of executable (i.e., test)
- * programs to create.
- */
-class cmAddExecutableCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddExecutableCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddExecutableCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx
index ad12e89..0439c51 100644
--- a/Source/cmAddLibraryCommand.cxx
+++ b/Source/cmAddLibraryCommand.cxx
@@ -2,40 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddLibraryCommand.h"
 
-#include <sstream>
-
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmLibraryCommand
-bool cmAddLibraryCommand::InitialPass(std::vector<std::string> const& args,
-                                      cmExecutionStatus&)
+bool cmAddLibraryCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
+
+  cmMakefile& mf = status.GetMakefile();
   // Library type defaults to value of BUILD_SHARED_LIBS, if it exists,
   // otherwise it defaults to static library.
   cmStateEnums::TargetType type = cmStateEnums::SHARED_LIBRARY;
-  if (cmSystemTools::IsOff(
-        this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) {
+  if (cmIsOff(mf.GetDefinition("BUILD_SHARED_LIBS"))) {
     type = cmStateEnums::STATIC_LIBRARY;
   }
   bool excludeFromAll = false;
   bool importTarget = false;
   bool importGlobal = false;
 
-  std::vector<std::string>::const_iterator s = args.begin();
+  auto s = args.begin();
 
   std::string const& libName = *s;
 
@@ -50,9 +47,8 @@
     std::string libType = *s;
     if (libType == "STATIC") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting STATIC type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting STATIC type.");
         return false;
       }
       ++s;
@@ -60,9 +56,8 @@
       haveSpecifiedType = true;
     } else if (libType == "SHARED") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting SHARED type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting SHARED type.");
         return false;
       }
       ++s;
@@ -70,9 +65,8 @@
       haveSpecifiedType = true;
     } else if (libType == "MODULE") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting MODULE type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting MODULE type.");
         return false;
       }
       ++s;
@@ -80,9 +74,8 @@
       haveSpecifiedType = true;
     } else if (libType == "OBJECT") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting OBJECT type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting OBJECT type.");
         return false;
       }
       ++s;
@@ -90,9 +83,8 @@
       haveSpecifiedType = true;
     } else if (libType == "UNKNOWN") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting UNKNOWN type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting UNKNOWN type.");
         return false;
       }
       ++s;
@@ -100,30 +92,26 @@
       haveSpecifiedType = true;
     } else if (libType == "ALIAS") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting ALIAS type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting ALIAS type.");
         return false;
       }
       ++s;
       isAlias = true;
     } else if (libType == "INTERFACE") {
       if (haveSpecifiedType) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting/multiple types.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting/multiple types.");
         return false;
       }
       if (isAlias) {
-        std::ostringstream e;
-        e << "INTERFACE library specified with conflicting ALIAS type.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library specified with conflicting ALIAS type.");
         return false;
       }
       if (excludeFromAll) {
-        std::ostringstream e;
-        e << "INTERFACE library may not be used with EXCLUDE_FROM_ALL.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library may not be used with EXCLUDE_FROM_ALL.");
         return false;
       }
       ++s;
@@ -131,9 +119,8 @@
       haveSpecifiedType = true;
     } else if (*s == "EXCLUDE_FROM_ALL") {
       if (type == cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "INTERFACE library may not be used with EXCLUDE_FROM_ALL.";
-        this->SetError(e.str());
+        status.SetError(
+          "INTERFACE library may not be used with EXCLUDE_FROM_ALL.");
         return false;
       }
       ++s;
@@ -145,9 +132,8 @@
       ++s;
       importGlobal = true;
     } else if (type == cmStateEnums::INTERFACE_LIBRARY && *s == "GLOBAL") {
-      std::ostringstream e;
-      e << "GLOBAL option may only be used with IMPORTED libraries.";
-      this->SetError(e.str());
+      status.SetError(
+        "GLOBAL option may only be used with IMPORTED libraries.");
       return false;
     } else {
       break;
@@ -156,15 +142,12 @@
 
   if (type == cmStateEnums::INTERFACE_LIBRARY) {
     if (s != args.end()) {
-      std::ostringstream e;
-      e << "INTERFACE library requires no source arguments.";
-      this->SetError(e.str());
+      status.SetError("INTERFACE library requires no source arguments.");
       return false;
     }
     if (importGlobal && !importTarget) {
-      std::ostringstream e;
-      e << "INTERFACE library specified as GLOBAL, but not as IMPORTED.";
-      this->SetError(e.str());
+      status.SetError(
+        "INTERFACE library specified as GLOBAL, but not as IMPORTED.");
       return false;
     }
   }
@@ -175,47 +158,40 @@
   if (nameOk && !importTarget && !isAlias) {
     nameOk = libName.find(':') == std::string::npos;
   }
-  if (!nameOk && !this->Makefile->CheckCMP0037(libName, type)) {
+  if (!nameOk && !mf.CheckCMP0037(libName, type)) {
     return false;
   }
 
   if (isAlias) {
     if (!cmGeneratorExpression::IsValidTargetName(libName)) {
-      this->SetError("Invalid name for ALIAS: " + libName);
+      status.SetError("Invalid name for ALIAS: " + libName);
       return false;
     }
     if (excludeFromAll) {
-      this->SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
+      status.SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
       return false;
     }
     if (importTarget || importGlobal) {
-      this->SetError("IMPORTED with ALIAS is not allowed.");
+      status.SetError("IMPORTED with ALIAS is not allowed.");
       return false;
     }
     if (args.size() != 3) {
-      std::ostringstream e;
-      e << "ALIAS requires exactly one target argument.";
-      this->SetError(e.str());
+      status.SetError("ALIAS requires exactly one target argument.");
       return false;
     }
 
     std::string const& aliasedName = *s;
-    if (this->Makefile->IsAlias(aliasedName)) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << libName << "\" because target \""
-        << aliasedName << "\" is itself an ALIAS.";
-      this->SetError(e.str());
+    if (mf.IsAlias(aliasedName)) {
+      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
+                               "\" because target \"", aliasedName,
+                               "\" is itself an ALIAS."));
       return false;
     }
-    cmTarget* aliasedTarget =
-      this->Makefile->FindTargetToUse(aliasedName, true);
+    cmTarget* aliasedTarget = mf.FindTargetToUse(aliasedName, true);
     if (!aliasedTarget) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << libName << "\" because target \""
-        << aliasedName
-        << "\" does not already "
-           "exist.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
+                               "\" because target \"", aliasedName,
+                               "\" does not already exist."));
       return false;
     }
     cmStateEnums::TargetType aliasedType = aliasedTarget->GetType();
@@ -226,26 +202,24 @@
         aliasedType != cmStateEnums::INTERFACE_LIBRARY &&
         !(aliasedType == cmStateEnums::UNKNOWN_LIBRARY &&
           aliasedTarget->IsImported())) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << libName << "\" because target \""
-        << aliasedName << "\" is not a library.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
+                               "\" because target \"", aliasedName,
+                               "\" is not a library."));
       return false;
     }
     if (aliasedTarget->IsImported() &&
         !aliasedTarget->IsImportedGloballyVisible()) {
-      std::ostringstream e;
-      e << "cannot create ALIAS target \"" << libName << "\" because target \""
-        << aliasedName << "\" is imported but not globally visible.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
+                               "\" because target \"", aliasedName,
+                               "\" is imported but not globally visible."));
       return false;
     }
-    this->Makefile->AddAlias(libName, aliasedName);
+    mf.AddAlias(libName, aliasedName);
     return true;
   }
 
   if (importTarget && excludeFromAll) {
-    this->SetError("excludeFromAll with IMPORTED target makes no sense.");
+    status.SetError("excludeFromAll with IMPORTED target makes no sense.");
     return false;
   }
 
@@ -255,14 +229,14 @@
     yet its linker language. */
   if ((type == cmStateEnums::SHARED_LIBRARY ||
        type == cmStateEnums::MODULE_LIBRARY) &&
-      !this->Makefile->GetState()->GetGlobalPropertyAsBool(
-        "TARGET_SUPPORTS_SHARED_LIBS")) {
-    std::ostringstream w;
-    w << "ADD_LIBRARY called with "
-      << (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE")
-      << " option but the target platform does not support dynamic linking. "
-         "Building a STATIC library instead. This may lead to problems.";
-    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+      !mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) {
+    mf.IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      cmStrCat(
+        "ADD_LIBRARY called with ",
+        (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"),
+        " option but the target platform does not support dynamic linking. ",
+        "Building a STATIC library instead. This may lead to problems."));
     type = cmStateEnums::STATIC_LIBRARY;
   }
 
@@ -270,14 +244,13 @@
   if (importTarget) {
     // The IMPORTED signature requires a type to be specified explicitly.
     if (!haveSpecifiedType) {
-      this->SetError("called with IMPORTED argument but no library type.");
+      status.SetError("called with IMPORTED argument but no library type.");
       return false;
     }
     if (type == cmStateEnums::OBJECT_LIBRARY) {
       std::string reason;
-      if (!this->Makefile->GetGlobalGenerator()->HasKnownObjectFileLocation(
-            &reason)) {
-        this->Makefile->IssueMessage(
+      if (!mf.GetGlobalGenerator()->HasKnownObjectFileLocation(&reason)) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The OBJECT library type may not be used for IMPORTED libraries" +
             reason + ".");
@@ -286,30 +259,28 @@
     }
     if (type == cmStateEnums::INTERFACE_LIBRARY) {
       if (!cmGeneratorExpression::IsValidTargetName(libName)) {
-        std::ostringstream e;
-        e << "Invalid name for IMPORTED INTERFACE library target: " << libName;
-        this->SetError(e.str());
+        status.SetError(cmStrCat(
+          "Invalid name for IMPORTED INTERFACE library target: ", libName));
         return false;
       }
     }
 
     // Make sure the target does not already exist.
-    if (this->Makefile->FindTargetToUse(libName)) {
-      std::ostringstream e;
-      e << "cannot create imported target \"" << libName
-        << "\" because another target with the same name already exists.";
-      this->SetError(e.str());
+    if (mf.FindTargetToUse(libName)) {
+      status.SetError(cmStrCat(
+        "cannot create imported target \"", libName,
+        "\" because another target with the same name already exists."));
       return false;
     }
 
     // Create the imported target.
-    this->Makefile->AddImportedTarget(libName, type, importGlobal);
+    mf.AddImportedTarget(libName, type, importGlobal);
     return true;
   }
 
   // A non-imported target may not have UNKNOWN type.
   if (type == cmStateEnums::UNKNOWN_LIBRARY) {
-    this->Makefile->IssueMessage(
+    mf.IssueMessage(
       MessageType::FATAL_ERROR,
       "The UNKNOWN library type may be used only for IMPORTED libraries.");
     return true;
@@ -318,8 +289,8 @@
   // Enforce name uniqueness.
   {
     std::string msg;
-    if (!this->Makefile->EnforceUniqueName(libName, msg)) {
-      this->SetError(msg);
+    if (!mf.EnforceUniqueName(libName, msg)) {
+      status.SetError(msg);
       return false;
     }
   }
@@ -329,19 +300,18 @@
   if (type == cmStateEnums::INTERFACE_LIBRARY) {
     if (!cmGeneratorExpression::IsValidTargetName(libName) ||
         libName.find("::") != std::string::npos) {
-      std::ostringstream e;
-      e << "Invalid name for INTERFACE library target: " << libName;
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("Invalid name for INTERFACE library target: ", libName));
       return false;
     }
 
-    this->Makefile->AddLibrary(libName, type, srclists, excludeFromAll);
+    mf.AddLibrary(libName, type, srclists, excludeFromAll);
     return true;
   }
 
   cmAppend(srclists, s, args.end());
 
-  this->Makefile->AddLibrary(libName, type, srclists, excludeFromAll);
+  mf.AddLibrary(libName, type, srclists, excludeFromAll);
 
   return true;
 }
diff --git a/Source/cmAddLibraryCommand.h b/Source/cmAddLibraryCommand.h
index aa21261..609449c 100644
--- a/Source/cmAddLibraryCommand.h
+++ b/Source/cmAddLibraryCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmLibrarysCommand
- * \brief Defines a list of executables to build.
- *
- * cmLibrarysCommand defines a list of executable (i.e., test)
- * programs to create.
- */
-class cmAddLibraryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddLibraryCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddLibraryCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddLinkOptionsCommand.cxx b/Source/cmAddLinkOptionsCommand.cxx
index 10ebd12..7ed31bd 100644
--- a/Source/cmAddLinkOptionsCommand.cxx
+++ b/Source/cmAddLinkOptionsCommand.cxx
@@ -2,19 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddLinkOptionsCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-bool cmAddLinkOptionsCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmAddLinkOptionsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
-  if (args.empty()) {
-    return true;
-  }
-
+  cmMakefile& mf = status.GetMakefile();
   for (std::string const& i : args) {
-    this->Makefile->AddLinkOption(i);
+    mf.AddLinkOption(i);
   }
   return true;
 }
diff --git a/Source/cmAddLinkOptionsCommand.h b/Source/cmAddLinkOptionsCommand.h
index 30fff00..466fc32 100644
--- a/Source/cmAddLinkOptionsCommand.h
+++ b/Source/cmAddLinkOptionsCommand.h
@@ -8,24 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmAddLinkOptionsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddLinkOptionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddLinkOptionsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddSubDirectoryCommand.cxx b/Source/cmAddSubDirectoryCommand.cxx
index 7947188..6a1a514 100644
--- a/Source/cmAddSubDirectoryCommand.cxx
+++ b/Source/cmAddSubDirectoryCommand.cxx
@@ -2,24 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddSubDirectoryCommand.h"
 
-#include <sstream>
-#include <string.h>
+#include <cstring>
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
-// cmAddSubDirectoryCommand
-bool cmAddSubDirectoryCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
   // store the binpath
   std::string const& srcArg = args.front();
   std::string binArg;
@@ -35,7 +34,7 @@
     if (binArg.empty()) {
       binArg = arg;
     } else {
-      this->SetError("called with incorrect number of arguments");
+      status.SetError("called with incorrect number of arguments");
       return false;
     }
   }
@@ -46,15 +45,12 @@
   if (cmSystemTools::FileIsFullPath(srcArg)) {
     srcPath = srcArg;
   } else {
-    srcPath = this->Makefile->GetCurrentSourceDirectory();
-    srcPath += "/";
-    srcPath += srcArg;
+    srcPath = cmStrCat(mf.GetCurrentSourceDirectory(), '/', srcArg);
   }
   if (!cmSystemTools::FileIsDirectory(srcPath)) {
-    std::string error = "given source \"";
-    error += srcArg;
-    error += "\" which is not an existing directory.";
-    this->SetError(error);
+    std::string error = cmStrCat("given source \"", srcArg,
+                                 "\" which is not an existing directory.");
+    status.SetError(error);
     return false;
   }
   srcPath = cmSystemTools::CollapseFullPath(srcPath);
@@ -65,22 +61,22 @@
     // No binary directory was specified.  If the source directory is
     // not a subdirectory of the current directory then it is an
     // error.
-    if (!cmSystemTools::IsSubDirectory(
-          srcPath, this->Makefile->GetCurrentSourceDirectory())) {
-      std::ostringstream e;
-      e << "not given a binary directory but the given source directory "
-        << "\"" << srcPath << "\" is not a subdirectory of \""
-        << this->Makefile->GetCurrentSourceDirectory() << "\".  "
-        << "When specifying an out-of-tree source a binary directory "
-        << "must be explicitly specified.";
-      this->SetError(e.str());
+    if (!cmSystemTools::IsSubDirectory(srcPath,
+                                       mf.GetCurrentSourceDirectory())) {
+      status.SetError(
+        cmStrCat("not given a binary directory but the given source ",
+                 "directory \"", srcPath, "\" is not a subdirectory of \"",
+                 mf.GetCurrentSourceDirectory(),
+                 "\".  When specifying an "
+                 "out-of-tree source a binary directory must be explicitly "
+                 "specified."));
       return false;
     }
 
     // Remove the CurrentDirectory from the srcPath and replace it
     // with the CurrentOutputDirectory.
-    const std::string& src = this->Makefile->GetCurrentSourceDirectory();
-    const std::string& bin = this->Makefile->GetCurrentBinaryDirectory();
+    const std::string& src = mf.GetCurrentSourceDirectory();
+    const std::string& bin = mf.GetCurrentBinaryDirectory();
     size_t srcLen = src.length();
     size_t binLen = bin.length();
     if (srcLen > 0 && src.back() == '/') {
@@ -96,15 +92,13 @@
     if (cmSystemTools::FileIsFullPath(binArg)) {
       binPath = binArg;
     } else {
-      binPath = this->Makefile->GetCurrentBinaryDirectory();
-      binPath += "/";
-      binPath += binArg;
+      binPath = cmStrCat(mf.GetCurrentBinaryDirectory(), '/', binArg);
     }
   }
   binPath = cmSystemTools::CollapseFullPath(binPath);
 
   // Add the subdirectory using the computed full paths.
-  this->Makefile->AddSubDirectory(srcPath, binPath, excludeFromAll, true);
+  mf.AddSubDirectory(srcPath, binPath, excludeFromAll, true);
 
   return true;
 }
diff --git a/Source/cmAddSubDirectoryCommand.h b/Source/cmAddSubDirectoryCommand.h
index 0ea4423..87da840 100644
--- a/Source/cmAddSubDirectoryCommand.h
+++ b/Source/cmAddSubDirectoryCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddSubDirectoryCommand
- * \brief Specify a subdirectory to build
- *
- * cmAddSubDirectoryCommand specifies a subdirectory to process
- * by CMake. CMake will descend
- * into the specified source directory and process any CMakeLists.txt found.
- */
-class cmAddSubDirectoryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddSubDirectoryCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx
index bf28702..8942113 100644
--- a/Source/cmAddTestCommand.cxx
+++ b/Source/cmAddTestCommand.cxx
@@ -2,62 +2,63 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAddTestCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmTest.h"
 #include "cmTestGenerator.h"
 
-class cmExecutionStatus;
+static bool cmAddTestCommandHandleNameMode(
+  std::vector<std::string> const& args, cmExecutionStatus& status);
 
-// cmExecutableCommand
-bool cmAddTestCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool cmAddTestCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (!args.empty() && args[0] == "NAME") {
-    return this->HandleNameMode(args);
+    return cmAddTestCommandHandleNameMode(args, status);
   }
 
   // First argument is the name of the test Second argument is the name of
   // the executable to run (a target or external program) Remaining arguments
   // are the arguments to pass to the executable
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
   // Collect the command with arguments.
   std::vector<std::string> command(args.begin() + 1, args.end());
 
   // Create the test but add a generator only the first time it is
   // seen.  This preserves behavior from before test generators.
-  cmTest* test = this->Makefile->GetTest(args[0]);
+  cmTest* test = mf.GetTest(args[0]);
   if (test) {
     // If the test was already added by a new-style signature do not
     // allow it to be duplicated.
     if (!test->GetOldStyle()) {
-      std::ostringstream e;
-      e << " given test name \"" << args[0]
-        << "\" which already exists in this directory.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat(" given test name \"", args[0],
+                               "\" which already exists in this directory."));
       return false;
     }
   } else {
-    test = this->Makefile->CreateTest(args[0]);
+    test = mf.CreateTest(args[0]);
     test->SetOldStyle(true);
-    this->Makefile->AddTestGenerator(new cmTestGenerator(test));
+    mf.AddTestGenerator(new cmTestGenerator(test));
   }
   test->SetCommand(command);
 
   return true;
 }
 
-bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args)
+bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status)
 {
   std::string name;
   std::vector<std::string> configurations;
   std::string working_directory;
   std::vector<std::string> command;
+  bool command_expand_lists = false;
 
   // Read the arguments.
   enum Doing
@@ -72,22 +73,29 @@
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (args[i] == "COMMAND") {
       if (!command.empty()) {
-        this->SetError(" may be given at most one COMMAND.");
+        status.SetError(" may be given at most one COMMAND.");
         return false;
       }
       doing = DoingCommand;
     } else if (args[i] == "CONFIGURATIONS") {
       if (!configurations.empty()) {
-        this->SetError(" may be given at most one set of CONFIGURATIONS.");
+        status.SetError(" may be given at most one set of CONFIGURATIONS.");
         return false;
       }
       doing = DoingConfigs;
     } else if (args[i] == "WORKING_DIRECTORY") {
       if (!working_directory.empty()) {
-        this->SetError(" may be given at most one WORKING_DIRECTORY.");
+        status.SetError(" may be given at most one WORKING_DIRECTORY.");
         return false;
       }
       doing = DoingWorkingDirectory;
+    } else if (args[i] == "COMMAND_EXPAND_LISTS") {
+      if (command_expand_lists) {
+        status.SetError(" may be given at most one COMMAND_EXPAND_LISTS.");
+        return false;
+      }
+      command_expand_lists = true;
+      doing = DoingNone;
     } else if (doing == DoingName) {
       name = args[i];
       doing = DoingNone;
@@ -99,42 +107,41 @@
       working_directory = args[i];
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << " given unknown argument:\n  " << args[i] << "\n";
-      this->SetError(e.str());
+      status.SetError(cmStrCat(" given unknown argument:\n  ", args[i], "\n"));
       return false;
     }
   }
 
   // Require a test name.
   if (name.empty()) {
-    this->SetError(" must be given non-empty NAME.");
+    status.SetError(" must be given non-empty NAME.");
     return false;
   }
 
   // Require a command.
   if (command.empty()) {
-    this->SetError(" must be given non-empty COMMAND.");
+    status.SetError(" must be given non-empty COMMAND.");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Require a unique test name within the directory.
-  if (this->Makefile->GetTest(name)) {
-    std::ostringstream e;
-    e << " given test NAME \"" << name
-      << "\" which already exists in this directory.";
-    this->SetError(e.str());
+  if (mf.GetTest(name)) {
+    status.SetError(cmStrCat(" given test NAME \"", name,
+                             "\" which already exists in this directory."));
     return false;
   }
 
   // Add the test.
-  cmTest* test = this->Makefile->CreateTest(name);
+  cmTest* test = mf.CreateTest(name);
   test->SetOldStyle(false);
   test->SetCommand(command);
   if (!working_directory.empty()) {
     test->SetProperty("WORKING_DIRECTORY", working_directory.c_str());
   }
-  this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations));
+  test->SetCommandExpandLists(command_expand_lists);
+  mf.AddTestGenerator(new cmTestGenerator(test, configurations));
 
   return true;
 }
diff --git a/Source/cmAddTestCommand.h b/Source/cmAddTestCommand.h
index bea3f3d..5877547 100644
--- a/Source/cmAddTestCommand.h
+++ b/Source/cmAddTestCommand.h
@@ -8,32 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAddTestCommand
- * \brief Add a test to the lists of tests to run.
- *
- * cmAddTestCommand adds a test to the list of tests to run .
- */
-class cmAddTestCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAddTestCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool HandleNameMode(std::vector<std::string> const& args);
-};
+bool cmAddTestCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmAffinity.cxx b/Source/cmAffinity.cxx
index 09b0298..8f9fe2a 100644
--- a/Source/cmAffinity.cxx
+++ b/Source/cmAffinity.cxx
@@ -13,16 +13,17 @@
 #    include <pthread.h>
 #    include <sched.h>
 // On some platforms CPU_ZERO needs memset but sched.h forgets string.h
-#    include <string.h> // IWYU pragma: keep
+#    include <cstring> // IWYU pragma: keep
 #    if defined(__FreeBSD__)
 #      include <pthread_np.h>
+
 #      include <sys/cpuset.h>
 #      include <sys/param.h>
 #    endif
 #    if defined(__linux__)
-typedef cpu_set_t cm_cpuset_t;
+using cm_cpuset_t = cpu_set_t;
 #    else
-typedef cpuset_t cm_cpuset_t;
+using cm_cpuset_t = cpuset_t;
 #    endif
 #  endif
 #endif
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index d1e32b0..e0d27ee 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -5,78 +5,25 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmRange.h"
-
-#include "cm_kwiml.h"
 #include <algorithm>
 #include <functional>
 #include <iterator>
-#include <memory>
-#include <sstream>
-#include <string.h>
-#include <string>
 #include <unordered_set>
 #include <utility>
 #include <vector>
 
-inline bool cmHasLiteralPrefixImpl(const std::string& str1, const char* str2,
-                                   size_t N)
+#include "cm_kwiml.h"
+
+#include "cmRange.h"
+
+template <std::size_t N>
+struct cmOverloadPriority : cmOverloadPriority<N - 1>
 {
-  return strncmp(str1.c_str(), str2, N) == 0;
-}
+};
 
-inline bool cmHasLiteralPrefixImpl(const char* str1, const char* str2,
-                                   size_t N)
+template <>
+struct cmOverloadPriority<0>
 {
-  return strncmp(str1, str2, N) == 0;
-}
-
-inline bool cmHasLiteralSuffixImpl(const std::string& str1, const char* str2,
-                                   size_t N)
-{
-  size_t len = str1.size();
-  return len >= N && strcmp(str1.c_str() + len - N, str2) == 0;
-}
-
-inline bool cmHasLiteralSuffixImpl(const char* str1, const char* str2,
-                                   size_t N)
-{
-  size_t len = strlen(str1);
-  return len >= N && strcmp(str1 + len - N, str2) == 0;
-}
-
-template <typename T, size_t N>
-bool cmHasLiteralPrefix(const T& str1, const char (&str2)[N])
-{
-  return cmHasLiteralPrefixImpl(str1, str2, N - 1);
-}
-
-template <typename T, size_t N>
-bool cmHasLiteralSuffix(const T& str1, const char (&str2)[N])
-{
-  return cmHasLiteralSuffixImpl(str1, str2, N - 1);
-}
-
-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>
@@ -95,6 +42,34 @@
   cont.erase(std::remove_if(cont.begin(), cont.end(), pred), cont.end());
 }
 
+template <typename Range, typename Key>
+auto cmContainsImpl(Range const& range, Key const& key, cmOverloadPriority<2>)
+  -> decltype(range.exists(key))
+{
+  return range.exists(key);
+}
+
+template <typename Range, typename Key>
+auto cmContainsImpl(Range const& range, Key const& key, cmOverloadPriority<1>)
+  -> decltype(range.find(key) != range.end())
+{
+  return range.find(key) != range.end();
+}
+
+template <typename Range, typename Key>
+bool cmContainsImpl(Range const& range, Key const& key, cmOverloadPriority<0>)
+{
+  using std::begin;
+  using std::end;
+  return std::find(begin(range), end(range), key) != end(range);
+}
+
+template <typename Range, typename Key>
+bool cmContains(Range const& range, Key const& key)
+{
+  return cmContainsImpl(range, key, cmOverloadPriority<2>{});
+}
+
 namespace ContainerAlgorithms {
 
 template <typename T>
@@ -142,7 +117,7 @@
 template <typename Range>
 struct BinarySearcher
 {
-  typedef typename Range::value_type argument_type;
+  using argument_type = typename Range::value_type;
   BinarySearcher(Range const& r)
     : m_range(r)
   {
@@ -158,11 +133,9 @@
 };
 }
 
-typedef cmRange<std::vector<std::string>::const_iterator> cmStringRange;
-
 class cmListFileBacktrace;
-typedef cmRange<std::vector<cmListFileBacktrace>::const_iterator>
-  cmBacktraceRange;
+using cmBacktraceRange =
+  cmRange<std::vector<cmListFileBacktrace>::const_iterator>;
 
 template <typename Range>
 void cmDeleteAll(Range const& r)
@@ -184,31 +157,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);
@@ -219,14 +167,14 @@
 {
   typename InputRange::const_iterator remIt = rem.begin();
   typename InputRange::const_iterator remEnd = rem.end();
-  const typename Range::iterator rangeEnd = r.end();
+  const auto rangeEnd = r.end();
   if (remIt == remEnd) {
     return rangeEnd;
   }
 
-  typename Range::iterator writer = r.begin();
+  auto writer = r.begin();
   std::advance(writer, *remIt);
-  typename Range::iterator pivot = writer;
+  auto pivot = writer;
   typename InputRange::value_type prevRem = *remIt;
   ++remIt;
   size_t count = 1;
@@ -268,7 +216,7 @@
 
   ForwardIterator result = first;
   while (first != last) {
-    if (uniq.find(first) == uniq.end()) {
+    if (!cmContains(uniq, first)) {
       if (result != first) {
         *result = std::move(*first);
       }
@@ -286,141 +234,10 @@
   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)
 {
   return std::find_if(r.begin(), r.end(), [&t](T const& i) { return i != t; });
 }
 
-template <class Iter>
-std::reverse_iterator<Iter> cmMakeReverseIterator(Iter it)
-{
-  return std::reverse_iterator<Iter>(it);
-}
-
-inline bool cmHasPrefix(std::string const& str, std::string const& prefix)
-{
-  if (str.size() < prefix.size()) {
-    return false;
-  }
-  return str.compare(0, prefix.size(), prefix) == 0;
-}
-
-inline bool cmHasSuffix(const std::string& str, const std::string& suffix)
-{
-  if (str.size() < suffix.size()) {
-    return false;
-  }
-  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
-inline void cmStripSuffixIfExists(std::string& str, const std::string& suffix)
-{
-  if (cmHasSuffix(str, suffix)) {
-    str.resize(str.size() - suffix.size());
-  }
-}
-
-namespace cm {
-
-#if defined(CMake_HAVE_CXX_MAKE_UNIQUE)
-
-using std::make_unique;
-
-#else
-
-template <typename T, typename... Args>
-std::unique_ptr<T> make_unique(Args&&... args)
-{
-  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-#endif
-
-#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
-
-using std::size;
-
-#else
-
-// std::size backport from C++17.
-template <class C>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  auto
-  size(C const& c) -> decltype(c.size())
-{
-  return c.size();
-}
-
-template <typename T, size_t N>
-#  if !defined(_MSC_VER) || _MSC_VER >= 1900
-constexpr
-#  endif
-  std::size_t
-  size(const T (&)[N]) throw()
-{
-  return N;
-}
-
-#endif
-
-template <typename T>
-int isize(const T& t)
-{
-  return static_cast<int>(cm::size(t));
-}
-
-#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
-
-using std::cbegin;
-using std::cend;
-
-#else
-
-// std::c{begin,end} backport from C++14
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto cbegin(C const& c)
-#  else
-constexpr auto cbegin(C const& c) noexcept(noexcept(std::begin(c)))
-#  endif
-  -> decltype(std::begin(c))
-{
-  return std::begin(c);
-}
-
-template <class C>
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-auto cend(C const& c)
-#  else
-constexpr auto cend(C const& c) noexcept(noexcept(std::end(c)))
-#  endif
-  -> decltype(std::end(c))
-{
-  return std::end(c);
-}
-
-#endif
-
-} // namespace cm
-
 #endif
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index 359d57a..e5eea79 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -2,17 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmArchiveWrite.h"
 
-#include "cmLocale.h"
-#include "cmSystemTools.h"
-#include "cm_get_date.h"
-#include "cm_libarchive.h"
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <sstream>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/Encoding.hxx"
 #include "cmsys/FStream.hxx"
-#include <iostream>
-#include <sstream>
-#include <string.h>
-#include <time.h>
+
+#include "cm_get_date.h"
+#include "cm_libarchive.h"
+
+#include "cmLocale.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 #ifndef __LA_SSIZE_T
 #  define __LA_SSIZE_T la_ssize_t
@@ -85,22 +89,22 @@
   switch (c) {
     case CompressNone:
       if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_none: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_none: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
     case CompressCompress:
       if (archive_write_add_filter_compress(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_compress: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_compress: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
     case CompressGZip: {
       if (archive_write_add_filter_gzip(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_gzip: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_gzip: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       std::string source_date_epoch;
@@ -110,60 +114,60 @@
         // The next best thing is to omit the timestamp entirely.
         if (archive_write_set_filter_option(this->Archive, "gzip", "timestamp",
                                             nullptr) != ARCHIVE_OK) {
-          this->Error = "archive_write_set_filter_option: ";
-          this->Error += cm_archive_error_string(this->Archive);
+          this->Error = cmStrCat("archive_write_set_filter_option: ",
+                                 cm_archive_error_string(this->Archive));
           return;
         }
       }
     } break;
     case CompressBZip2:
       if (archive_write_add_filter_bzip2(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_bzip2: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_bzip2: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
     case CompressLZMA:
       if (archive_write_add_filter_lzma(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_lzma: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_lzma: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
     case CompressXZ:
       if (archive_write_add_filter_xz(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_xz: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_xz: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
     case CompressZstd:
       if (archive_write_add_filter_zstd(this->Archive) != ARCHIVE_OK) {
-        this->Error = "archive_write_add_filter_zstd: ";
-        this->Error += cm_archive_error_string(this->Archive);
+        this->Error = cmStrCat("archive_write_add_filter_zstd: ",
+                               cm_archive_error_string(this->Archive));
         return;
       }
       break;
   }
 #if !defined(_WIN32) || defined(__CYGWIN__)
   if (archive_read_disk_set_standard_lookup(this->Disk) != ARCHIVE_OK) {
-    this->Error = "archive_read_disk_set_standard_lookup: ";
-    this->Error += cm_archive_error_string(this->Archive);
+    this->Error = cmStrCat("archive_read_disk_set_standard_lookup: ",
+                           cm_archive_error_string(this->Archive));
     return;
   }
 #endif
 
   if (archive_write_set_format_by_name(this->Archive, format.c_str()) !=
       ARCHIVE_OK) {
-    this->Error = "archive_write_set_format_by_name: ";
-    this->Error += cm_archive_error_string(this->Archive);
+    this->Error = cmStrCat("archive_write_set_format_by_name: ",
+                           cm_archive_error_string(this->Archive));
     return;
   }
 
   // do not pad the last block!!
   if (archive_write_set_bytes_in_last_block(this->Archive, 1)) {
-    this->Error = "archive_write_set_bytes_in_last_block: ";
-    this->Error += cm_archive_error_string(this->Archive);
+    this->Error = cmStrCat("archive_write_set_bytes_in_last_block: ",
+                           cm_archive_error_string(this->Archive));
     return;
   }
 
@@ -171,8 +175,8 @@
         this->Archive, this, nullptr,
         reinterpret_cast<archive_write_callback*>(&Callback::Write),
         nullptr) != ARCHIVE_OK) {
-    this->Error = "archive_write_open: ";
-    this->Error += cm_archive_error_string(this->Archive);
+    this->Error =
+      cmStrCat("archive_write_open: ", cm_archive_error_string(this->Archive));
     return;
   }
 }
@@ -205,8 +209,7 @@
   }
   cmsys::Directory d;
   if (d.Load(path)) {
-    std::string next = path;
-    next += "/";
+    std::string next = cmStrCat(path, '/');
     std::string::size_type end = next.size();
     unsigned long n = d.GetNumberOfFiles();
     for (unsigned long i = 0; i < n; ++i) {
@@ -237,8 +240,7 @@
   static_cast<void>(localeRAII);
 
   // Meta-data.
-  std::string dest = prefix ? prefix : "";
-  dest += out;
+  std::string dest = cmStrCat(prefix ? prefix : "", out);
   if (this->Verbose) {
     std::cout << dest << "\n";
   }
@@ -247,10 +249,8 @@
   cm_archive_entry_copy_pathname(e, dest);
   if (archive_read_disk_entry_from_file(this->Disk, e, -1, nullptr) !=
       ARCHIVE_OK) {
-    this->Error = "Unable to read from file '";
-    this->Error += file;
-    this->Error += "': ";
-    this->Error += cm_archive_error_string(this->Disk);
+    this->Error = cmStrCat("Unable to read from file '", file,
+                           "': ", cm_archive_error_string(this->Disk));
     return false;
   }
   if (!this->MTime.empty()) {
@@ -258,9 +258,7 @@
     time(&now);
     time_t t = cm_get_date(now, this->MTime.c_str());
     if (t == -1) {
-      this->Error = "unable to parse mtime '";
-      this->Error += this->MTime;
-      this->Error += "'";
+      this->Error = cmStrCat("unable to parse mtime '", this->MTime, '\'');
       return false;
     }
     archive_entry_set_mtime(e, t, 0);
@@ -310,8 +308,8 @@
   }
 
   if (archive_write_header(this->Archive, e) != ARCHIVE_OK) {
-    this->Error = "archive_write_header: ";
-    this->Error += cm_archive_error_string(this->Archive);
+    this->Error = cmStrCat("archive_write_header: ",
+                           cm_archive_error_string(this->Archive));
     return false;
   }
 
@@ -329,17 +327,15 @@
 {
   cmsys::ifstream fin(file, std::ios::in | std::ios::binary);
   if (!fin) {
-    this->Error = "Error opening \"";
-    this->Error += file;
-    this->Error += "\": ";
-    this->Error += cmSystemTools::GetLastSystemError();
+    this->Error = cmStrCat("Error opening \"", file,
+                           "\": ", cmSystemTools::GetLastSystemError());
     return false;
   }
 
   char buffer[16384];
   size_t nleft = size;
   while (nleft > 0) {
-    typedef std::streamsize ssize_type;
+    using ssize_type = std::streamsize;
     size_t const nnext = nleft > sizeof(buffer) ? sizeof(buffer) : nleft;
     ssize_type const nnext_s = static_cast<ssize_type>(nnext);
     fin.read(buffer, nnext_s);
@@ -350,17 +346,15 @@
       break;
     }
     if (archive_write_data(this->Archive, buffer, nnext) != nnext_s) {
-      this->Error = "archive_write_data: ";
-      this->Error += cm_archive_error_string(this->Archive);
+      this->Error = cmStrCat("archive_write_data: ",
+                             cm_archive_error_string(this->Archive));
       return false;
     }
     nleft -= nnext;
   }
   if (nleft > 0) {
-    this->Error = "Error reading \"";
-    this->Error += file;
-    this->Error += "\": ";
-    this->Error += cmSystemTools::GetLastSystemError();
+    this->Error = cmStrCat("Error reading \"", file,
+                           "\": ", cmSystemTools::GetLastSystemError());
     return false;
   }
   return true;
diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h
index 9ea88d3..e791761 100644
--- a/Source/cmArchiveWrite.h
+++ b/Source/cmArchiveWrite.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <iosfwd>
-#include <stddef.h>
 #include <string>
 
-#if !defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(CMAKE_BOOTSTRAP)
 #  error "cmArchiveWrite not allowed during bootstrap build!"
 #endif
 
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx
index 751d117..4c87177 100644
--- a/Source/cmArgumentParser.cxx
+++ b/Source/cmArgumentParser.cxx
@@ -61,10 +61,14 @@
 
 void Instance::Consume(cm::string_view arg, void* result,
                        std::vector<std::string>* unparsedArguments,
-                       std::vector<std::string>* keywordsMissingValue)
+                       std::vector<std::string>* keywordsMissingValue,
+                       std::vector<std::string>* parsedKeywords)
 {
   auto const it = this->Bindings.Find(arg);
   if (it != this->Bindings.end()) {
+    if (parsedKeywords != nullptr) {
+      parsedKeywords->emplace_back(arg);
+    }
     it->second(*this, result);
     if (this->ExpectValue && keywordsMissingValue != nullptr) {
       keywordsMissingValue->emplace_back(arg);
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
index 6cfe946..9426537 100644
--- a/Source/cmArgumentParser.h
+++ b/Source/cmArgumentParser.h
@@ -5,15 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_static_string_view.hxx"
-#include "cm_string_view.hxx"
-
 #include <cassert>
 #include <functional>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 namespace ArgumentParser {
 
 using StringList = std::vector<std::string>;
@@ -45,7 +46,8 @@
 
   void Consume(cm::string_view arg, void* result,
                std::vector<std::string>* unparsedArguments,
-               std::vector<std::string>* keywordsMissingValue);
+               std::vector<std::string>* keywordsMissingValue,
+               std::vector<std::string>* parsedKeywords);
 
 private:
   ActionMap const& Bindings;
@@ -79,21 +81,25 @@
   template <typename Range>
   void Parse(Result& result, Range const& args,
              std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr) const
+             std::vector<std::string>* keywordsMissingValue = nullptr,
+             std::vector<std::string>* parsedKeywords = nullptr) const
   {
     ArgumentParser::Instance instance(this->Bindings);
     for (cm::string_view arg : args) {
-      instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue);
+      instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue,
+                       parsedKeywords);
     }
   }
 
   template <typename Range>
   Result Parse(Range const& args,
                std::vector<std::string>* unparsedArguments = nullptr,
-               std::vector<std::string>* keywordsMissingValue = nullptr) const
+               std::vector<std::string>* keywordsMissingValue = nullptr,
+               std::vector<std::string>* parsedKeywords = nullptr) const
   {
     Result result;
-    this->Parse(result, args, unparsedArguments, keywordsMissingValue);
+    this->Parse(result, args, unparsedArguments, keywordsMissingValue,
+                parsedKeywords);
     return result;
   }
 
@@ -116,11 +122,13 @@
   template <typename Range>
   void Parse(Range const& args,
              std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr) const
+             std::vector<std::string>* keywordsMissingValue = nullptr,
+             std::vector<std::string>* parsedKeywords = nullptr) const
   {
     ArgumentParser::Instance instance(this->Bindings);
     for (cm::string_view arg : args) {
-      instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue);
+      instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue,
+                       parsedKeywords);
     }
   }
 
diff --git a/Source/cmAuxSourceDirectoryCommand.cxx b/Source/cmAuxSourceDirectoryCommand.cxx
index 106e7a7..289bb72 100644
--- a/Source/cmAuxSourceDirectoryCommand.cxx
+++ b/Source/cmAuxSourceDirectoryCommand.cxx
@@ -2,41 +2,39 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmAuxSourceDirectoryCommand.h"
 
-#include "cmsys/Directory.hxx"
 #include <algorithm>
-#include <stddef.h>
+#include <cstddef>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include "cmsys/Directory.hxx"
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
-// cmAuxSourceDirectoryCommand
-bool cmAuxSourceDirectoryCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmAuxSourceDirectoryCommand(std::vector<std::string> const& args,
+                                 cmExecutionStatus& status)
 {
   if (args.size() != 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
   std::string sourceListValue;
   std::string const& templateDirectory = args[0];
   std::string tdir;
   if (!cmSystemTools::FileIsFullPath(templateDirectory)) {
-    tdir = this->Makefile->GetCurrentSourceDirectory();
-    tdir += "/";
-    tdir += templateDirectory;
+    tdir = cmStrCat(mf.GetCurrentSourceDirectory(), '/', templateDirectory);
   } else {
     tdir = templateDirectory;
   }
 
   // was the list already populated
-  const char* def = this->Makefile->GetDefinition(args[1]);
+  const char* def = mf.GetDefinition(args[1]);
   if (def) {
     sourceListValue = def;
   }
@@ -55,14 +53,12 @@
         std::string ext = file.substr(dotpos + 1);
         std::string base = file.substr(0, dotpos);
         // Process only source files
-        auto cm = this->Makefile->GetCMakeInstance();
+        auto cm = mf.GetCMakeInstance();
         if (!base.empty() && cm->IsSourceExtension(ext)) {
-          std::string fullname = templateDirectory;
-          fullname += "/";
-          fullname += file;
+          std::string fullname = cmStrCat(templateDirectory, '/', file);
           // add the file as a class file so
           // depends can be done
-          cmSourceFile* sf = this->Makefile->GetOrCreateSource(fullname);
+          cmSourceFile* sf = mf.GetOrCreateSource(fullname);
           sf->SetProperty("ABSTRACT", "0");
           files.push_back(std::move(fullname));
         }
@@ -74,6 +70,6 @@
     sourceListValue += ";";
   }
   sourceListValue += cmJoin(files, ";");
-  this->Makefile->AddDefinition(args[1], sourceListValue.c_str());
+  mf.AddDefinition(args[1], sourceListValue);
   return true;
 }
diff --git a/Source/cmAuxSourceDirectoryCommand.h b/Source/cmAuxSourceDirectoryCommand.h
index 3742e3e..ae26092 100644
--- a/Source/cmAuxSourceDirectoryCommand.h
+++ b/Source/cmAuxSourceDirectoryCommand.h
@@ -8,33 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmAuxSourceDirectoryCommand
- * \brief Specify auxiliary source code directories.
- *
- * cmAuxSourceDirectoryCommand specifies source code directories
- * that must be built as part of this build process. This directories
- * are not recursively processed like the SUBDIR command (cmSubdirCommand).
- * A side effect of this command is to create a subdirectory in the build
- * directory structure.
- */
-class cmAuxSourceDirectoryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmAuxSourceDirectoryCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmAuxSourceDirectoryCommand(std::vector<std::string> const& args,
+                                 cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmBase32.h b/Source/cmBase32.h
index c6758d4..d85198d 100644
--- a/Source/cmBase32.h
+++ b/Source/cmBase32.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 
 /** \class cmBase32Encoder
diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx
new file mode 100644
index 0000000..a9f4d91
--- /dev/null
+++ b/Source/cmBinUtilsLinker.cxx
@@ -0,0 +1,16 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinker.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsLinker::SetError(const std::string& e)
+{
+  this->Archive->SetError(e);
+}
diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h
new file mode 100644
index 0000000..78d517b
--- /dev/null
+++ b/Source/cmBinUtilsLinker.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinker_h
+#define cmBinUtilsLinker_h
+
+#include <string>
+
+#include "cmStateTypes.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinker
+{
+public:
+  cmBinUtilsLinker(cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsLinker() = default;
+
+  virtual bool Prepare() { return true; }
+
+  virtual bool ScanDependencies(std::string const& file,
+                                cmStateEnums::TargetType type) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinker_h
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..abd1209
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinuxELFGetRuntimeDependenciesTool::
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsLinuxELFGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..d514e7f
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsLinuxELFGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(std::string const& file,
+                           std::vector<std::string>& needed,
+                           std::vector<std::string>& rpaths,
+                           std::vector<std::string>& runpaths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx
new file mode 100644
index 0000000..0dea825
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.cxx
@@ -0,0 +1,179 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFLinker.h"
+
+#include <sstream>
+
+#include <cm/memory>
+
+#include <cmsys/RegularExpression.hxx>
+
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmLDConfigLDConfigTool.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+static std::string ReplaceOrigin(const std::string& rpath,
+                                 const std::string& origin)
+{
+  static const cmsys::RegularExpression originRegex(
+    "(\\$ORIGIN)([^a-zA-Z0-9_]|$)");
+  static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}");
+
+  cmsys::RegularExpressionMatch match;
+  if (originRegex.find(rpath.c_str(), match)) {
+    std::string begin = rpath.substr(0, match.start(1));
+    std::string end = rpath.substr(match.end(1));
+    return begin + origin + end;
+  }
+  if (originCurlyRegex.find(rpath.c_str(), match)) {
+    std::string begin = rpath.substr(0, match.start());
+    std::string end = rpath.substr(match.end());
+    return begin + origin + end;
+  }
+  return rpath;
+}
+
+cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFLinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    tool = "objdump";
+  }
+  if (tool == "objdump") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string ldConfigTool =
+    this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL");
+  if (ldConfigTool.empty()) {
+    ldConfigTool = "ldconfig";
+  }
+  if (ldConfigTool == "ldconfig") {
+    this->LDConfigTool =
+      cm::make_unique<cmLDConfigLDConfigTool>(this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+  std::vector<std::string> parentRpaths;
+  return this->ScanDependencies(file, parentRpaths);
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+  std::string const& file, std::vector<std::string> const& parentRpaths)
+{
+  std::string origin = cmSystemTools::GetFilenamePath(file);
+  std::vector<std::string> needed;
+  std::vector<std::string> rpaths;
+  std::vector<std::string> runpaths;
+  if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) {
+    return false;
+  }
+  for (auto& runpath : runpaths) {
+    runpath = ReplaceOrigin(runpath, origin);
+  }
+  for (auto& rpath : rpaths) {
+    rpath = ReplaceOrigin(rpath, origin);
+  }
+
+  std::vector<std::string> searchPaths;
+  if (!runpaths.empty()) {
+    searchPaths = runpaths;
+  } else {
+    searchPaths = rpaths;
+    searchPaths.insert(searchPaths.end(), parentRpaths.begin(),
+                       parentRpaths.end());
+  }
+
+  std::vector<std::string> ldConfigPaths;
+  if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) {
+    return false;
+  }
+  searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(),
+                     ldConfigPaths.end());
+
+  for (auto const& dep : needed) {
+    if (!this->Archive->IsPreExcluded(dep)) {
+      std::string path;
+      bool resolved = false;
+      if (dep.find('/') != std::string::npos) {
+        this->SetError("Paths to dependencies are not supported");
+        return false;
+      }
+      if (!this->ResolveDependency(dep, searchPaths, path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          bool unique;
+          this->Archive->AddResolvedPath(dep, path, unique);
+          if (unique && !this->ScanDependencies(path, rpaths)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(dep);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ResolveDependency(
+  std::string const& name, std::vector<std::string> const& searchPaths,
+  std::string& path, bool& resolved)
+{
+  for (auto const& searchPath : searchPaths) {
+    path = cmStrCat(searchPath, '/', name);
+    if (cmSystemTools::PathExists(path)) {
+      resolved = true;
+      return true;
+    }
+  }
+
+  for (auto const& searchPath : this->Archive->GetSearchDirectories()) {
+    path = cmStrCat(searchPath, '/', name);
+    if (cmSystemTools::PathExists(path)) {
+      std::ostringstream warning;
+      warning << "Dependency " << name << " found in search directory:\n  "
+              << searchPath
+              << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for "
+              << "more information.";
+      this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING,
+                                                 warning.str());
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h
new file mode 100644
index 0000000..b17df11
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.h
@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFLinker_h
+#define cmBinUtilsLinuxELFLinker_h
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+#include "cmLDConfigTool.h"
+#include "cmStateTypes.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFLinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsLinuxELFLinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsLinuxELFGetRuntimeDependenciesTool> Tool;
+  std::unique_ptr<cmLDConfigTool> LDConfigTool;
+  bool HaveLDConfigPaths = false;
+  std::vector<std::string> LDConfigPaths;
+
+  bool ScanDependencies(std::string const& file,
+                        std::vector<std::string> const& parentRpaths);
+
+  bool ResolveDependency(std::string const& name,
+                         std::vector<std::string> const& searchPaths,
+                         std::string& path, bool& resolved);
+
+  bool GetLDConfigPaths();
+};
+
+#endif // cmBinUtilsLinuxELFLinker_h
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..566e4a4
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,85 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+
+#include <sstream>
+
+#include <cmsys/RegularExpression.hxx>
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinuxELFGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+  std::string const& file, std::vector<std::string>& needed,
+  std::vector<std::string>& rpaths, std::vector<std::string>& runpaths)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+    this->SetError("Could not find objdump");
+    return false;
+  }
+  command.emplace_back("-p");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$");
+  static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$");
+  static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (neededRegex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    } else if (rpathRegex.find(line.c_str(), match)) {
+      std::vector<std::string> rpathSplit =
+        cmSystemTools::SplitString(match.match(1), ':');
+      rpaths.reserve(rpaths.size() + rpathSplit.size());
+      for (auto const& rpath : rpathSplit) {
+        rpaths.push_back(rpath);
+      }
+    } else if (runpathRegex.find(line.c_str(), match)) {
+      std::vector<std::string> runpathSplit =
+        cmSystemTools::SplitString(match.match(1), ':');
+      runpaths.reserve(runpaths.size() + runpathSplit.size());
+      for (auto const& runpath : runpathSplit) {
+        runpaths.push_back(runpath);
+      }
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run objdump on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..969e4d4
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,26 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool
+  : public cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(std::string const& file, std::vector<std::string>& needed,
+                   std::vector<std::string>& rpaths,
+                   std::vector<std::string>& runpaths) override;
+};
+
+#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..a296a47
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..dbb2882
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
@@ -0,0 +1,29 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsMacOSMachOGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(std::string const& file,
+                           std::vector<std::string>& libs,
+                           std::vector<std::string>& rpaths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
new file mode 100644
index 0000000..98250b1
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -0,0 +1,231 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOLinker.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+cmBinUtilsMacOSMachOLinker::cmBinUtilsMacOSMachOLinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOLinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    tool = "otool";
+  }
+  if (tool == "otool") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType type)
+{
+  std::string executableFile;
+  if (type == cmStateEnums::EXECUTABLE) {
+    executableFile = file;
+  } else {
+    executableFile = this->Archive->GetBundleExecutable();
+  }
+  std::string executablePath;
+  if (!executableFile.empty()) {
+    executablePath = cmSystemTools::GetFilenamePath(executableFile);
+  }
+  return this->ScanDependencies(file, executablePath);
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+  std::string const& file, std::string const& executablePath)
+{
+  std::vector<std::string> libs;
+  std::vector<std::string> rpaths;
+  if (!this->Tool->GetFileInfo(file, libs, rpaths)) {
+    return false;
+  }
+
+  std::string loaderPath = cmSystemTools::GetFilenamePath(file);
+  return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths);
+}
+
+bool cmBinUtilsMacOSMachOLinker::GetFileDependencies(
+  std::vector<std::string> const& names, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths)
+{
+  for (std::string const& name : names) {
+    if (!this->Archive->IsPreExcluded(name)) {
+      std::string path;
+      bool resolved;
+      if (!this->ResolveDependency(name, executablePath, loaderPath, rpaths,
+                                   path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          auto filename = cmSystemTools::GetFilenameName(path);
+          bool unique;
+          this->Archive->AddResolvedPath(filename, path, unique);
+          if (unique && !this->ScanDependencies(path, executablePath)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(name);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths,
+  std::string& path, bool& resolved)
+{
+  resolved = false;
+  if (cmHasLiteralPrefix(name, "@rpath/")) {
+    if (!this->ResolveRPathDependency(name, executablePath, loaderPath, rpaths,
+                                      path, resolved)) {
+      return false;
+    }
+  } else if (cmHasLiteralPrefix(name, "@loader_path/")) {
+    if (!this->ResolveLoaderPathDependency(name, loaderPath, path, resolved)) {
+      return false;
+    }
+  } else if (cmHasLiteralPrefix(name, "@executable_path/")) {
+    if (!this->ResolveExecutablePathDependency(name, executablePath, path,
+                                               resolved)) {
+      return false;
+    }
+  } else {
+    resolved = true;
+    path = name;
+  }
+
+  if (resolved && !cmSystemTools::FileIsFullPath(path)) {
+    this->SetError("Resolved path is not absolute");
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string& path, bool& resolved)
+{
+  if (executablePath.empty()) {
+    resolved = false;
+    return true;
+  }
+
+  // 16 is == "@executable_path".length()
+  path = name;
+  path.replace(0, 16, executablePath);
+
+  if (!cmSystemTools::PathExists(path)) {
+    resolved = false;
+    return true;
+  }
+
+  resolved = true;
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency(
+  std::string const& name, std::string const& loaderPath, std::string& path,
+  bool& resolved)
+{
+  if (loaderPath.empty()) {
+    resolved = false;
+    return true;
+  }
+
+  // 12 is "@loader_path".length();
+  path = name;
+  path.replace(0, 12, loaderPath);
+
+  if (!cmSystemTools::PathExists(path)) {
+    resolved = false;
+    return true;
+  }
+
+  resolved = true;
+  return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency(
+  std::string const& name, std::string const& executablePath,
+  std::string const& loaderPath, std::vector<std::string> const& rpaths,
+  std::string& path, bool& resolved)
+{
+  for (std::string const& rpath : rpaths) {
+    std::string searchFile = name;
+    searchFile.replace(0, 6, rpath);
+    if (cmHasLiteralPrefix(searchFile, "@loader_path/")) {
+      if (!this->ResolveLoaderPathDependency(searchFile, loaderPath, path,
+                                             resolved)) {
+        return false;
+      }
+      if (resolved) {
+        return true;
+      }
+    } else if (cmHasLiteralPrefix(searchFile, "@executable_path/")) {
+      if (!this->ResolveExecutablePathDependency(searchFile, executablePath,
+                                                 path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        return true;
+      }
+    } else if (cmSystemTools::PathExists(searchFile)) {
+      /*
+       * paraphrasing @ben.boeckel:
+       *  if /b/libB.dylib is supposed to be used,
+       *  /a/libbB.dylib will be found first if it exists. CMake tries to
+       *  sort rpath directories to avoid this, but sometimes there is no
+       *  right answer.
+       *
+       *  I believe it is possible to resolve this using otools -l
+       *  then checking the LC_LOAD_DYLIB command whose name is
+       *  equal to the value of search_file, UNLESS the build
+       *  specifically sets the RPath to paths that will match
+       *  duplicate libs; at this point can we just point to
+       *  user error, or is there a reason why the advantages
+       *  to this scenario outweigh its disadvantages?
+       *
+       *  Also priority seems to be the order as passed in when compiled
+       *  so as long as this method's resolution guarantees priority
+       *  in that manner further checking should not be necessary?
+       */
+      path = searchFile;
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h
new file mode 100644
index 0000000..4a24ea3
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.h
@@ -0,0 +1,59 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOLinker_h
+#define cmBinUtilsMacOSMachOLinker_h
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOLinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsMacOSMachOLinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool;
+
+  bool ScanDependencies(std::string const& file,
+                        std::string const& executablePath);
+
+  bool GetFileDependencies(std::vector<std::string> const& names,
+                           std::string const& executablePath,
+                           std::string const& loaderPath,
+                           std::vector<std::string> const& rpaths);
+
+  bool ResolveDependency(std::string const& name,
+                         std::string const& executablePath,
+                         std::string const& loaderPath,
+                         std::vector<std::string> const& rpaths,
+                         std::string& path, bool& resolved);
+
+  bool ResolveExecutablePathDependency(std::string const& name,
+                                       std::string const& executablePath,
+                                       std::string& path, bool& resolved);
+
+  bool ResolveLoaderPathDependency(std::string const& name,
+                                   std::string const& loaderPath,
+                                   std::string& path, bool& resolved);
+
+  bool ResolveRPathDependency(std::string const& name,
+                              std::string const& executablePath,
+                              std::string const& loaderPath,
+                              std::vector<std::string> const& rpaths,
+                              std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsMacOSMachOLinker_h
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..351d92a
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,100 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+
+#include <sstream>
+
+#include <cmsys/RegularExpression.hxx>
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo(
+  std::string const& file, std::vector<std::string>& libs,
+  std::vector<std::string>& rpaths)
+{
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("otool", command)) {
+    this->SetError("Could not find otool");
+    return false;
+  }
+  command.emplace_back("-l");
+  command.emplace_back(file);
+
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start otool process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$");
+  static const cmsys::RegularExpression loadDylibRegex(
+    "^ *cmd LC_LOAD_DYLIB$");
+  static const cmsys::RegularExpression pathRegex(
+    "^ *path (.*) \\(offset [0-9]+\\)$");
+  static const cmsys::RegularExpression nameRegex(
+    "^ *name (.*) \\(offset [0-9]+\\)$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch cmdMatch;
+    if (rpathRegex.find(line.c_str(), cmdMatch)) {
+      if (!std::getline(*process.OutputStream(), line) ||
+          !std::getline(*process.OutputStream(), line)) {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+
+      cmsys::RegularExpressionMatch pathMatch;
+      if (pathRegex.find(line.c_str(), pathMatch)) {
+        rpaths.push_back(pathMatch.match(1));
+      } else {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+    } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) {
+      if (!std::getline(*process.OutputStream(), line) ||
+          !std::getline(*process.OutputStream(), line)) {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+
+      cmsys::RegularExpressionMatch nameMatch;
+      if (nameRegex.find(line.c_str(), nameMatch)) {
+        libs.push_back(nameMatch.match(1));
+      } else {
+        this->SetError("Invalid output from otool");
+        return false;
+      }
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on otool process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run otool on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..8ac7e18
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool
+  : public cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(std::string const& file, std::vector<std::string>& libs,
+                   std::vector<std::string>& rpaths) override;
+};
+
+#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..f342884
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,68 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+
+#include <sstream>
+
+#include <cmsys/RegularExpression.hxx>
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo(
+  const std::string& file, std::vector<std::string>& needed)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+    this->SetError("Could not find dumpbin");
+    return false;
+  }
+  command.emplace_back("/dependents");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start dumpbin process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex(
+    "^    ([^\n]*\\.[Dd][Ll][Ll])\r$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on dumpbin process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run dumpbin on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..eae22ea
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool
+  : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(const std::string& file,
+                   std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..468a40c
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsWindowsPEGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
+
+void cmBinUtilsWindowsPEGetRuntimeDependenciesTool::SetError(
+  const std::string& error)
+{
+  this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..e9e402b
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
@@ -0,0 +1,28 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+  virtual ~cmBinUtilsWindowsPEGetRuntimeDependenciesTool() = default;
+
+  virtual bool GetFileInfo(const std::string& file,
+                           std::vector<std::string>& needed) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+
+  void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx
new file mode 100644
index 0000000..79e39e9
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.cxx
@@ -0,0 +1,123 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPELinker.h"
+
+#include <sstream>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+#ifdef _WIN32
+#  include <windows.h>
+#endif
+
+cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
+  cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsWindowsPELinker::Prepare()
+{
+  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+  if (tool.empty()) {
+    std::vector<std::string> command;
+    if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+      tool = "dumpbin";
+    } else {
+      tool = "objdump";
+    }
+  }
+  if (tool == "dumpbin") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else if (tool == "objdump") {
+    this->Tool =
+      cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>(
+        this->Archive);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ScanDependencies(
+  std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+  std::vector<std::string> needed;
+  if (!this->Tool->GetFileInfo(file, needed)) {
+    return false;
+  }
+  for (auto& n : needed) {
+    n = cmSystemTools::LowerCase(n);
+  }
+  std::string origin = cmSystemTools::GetFilenamePath(file);
+
+  for (auto const& lib : needed) {
+    if (!this->Archive->IsPreExcluded(lib)) {
+      std::string path;
+      bool resolved = false;
+      if (!this->ResolveDependency(lib, origin, path, resolved)) {
+        return false;
+      }
+      if (resolved) {
+        if (!this->Archive->IsPostExcluded(path)) {
+          bool unique;
+          this->Archive->AddResolvedPath(lib, path, unique);
+          if (unique &&
+              !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
+            return false;
+          }
+        }
+      } else {
+        this->Archive->AddUnresolvedPath(lib);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name,
+                                                  std::string const& origin,
+                                                  std::string& path,
+                                                  bool& resolved)
+{
+  auto dirs = this->Archive->GetSearchDirectories();
+
+#ifdef _WIN32
+  char buf[MAX_PATH];
+  unsigned int len;
+  if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) {
+    dirs.insert(dirs.begin(), std::string(buf, len));
+  }
+  if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) {
+    dirs.insert(dirs.begin(), std::string(buf, len));
+  }
+#endif
+
+  dirs.insert(dirs.begin(), origin);
+
+  for (auto const& searchPath : dirs) {
+    path = cmStrCat(searchPath, '/', name);
+    if (cmSystemTools::PathExists(path)) {
+      resolved = true;
+      return true;
+    }
+  }
+
+  resolved = false;
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h
new file mode 100644
index 0000000..a8bb596
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.h
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPELinker_h
+#define cmBinUtilsWindowsPELinker_h
+
+#include <memory>
+#include <string>
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPELinker : public cmBinUtilsLinker
+{
+public:
+  cmBinUtilsWindowsPELinker(cmRuntimeDependencyArchive* archive);
+
+  bool Prepare() override;
+
+  bool ScanDependencies(std::string const& file,
+                        cmStateEnums::TargetType type) override;
+
+private:
+  std::unique_ptr<cmBinUtilsWindowsPEGetRuntimeDependenciesTool> Tool;
+
+  bool ResolveDependency(std::string const& name, std::string const& origin,
+                         std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsWindowsPELinker_h
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..1effda0
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,68 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+
+#include <sstream>
+
+#include <cmsys/RegularExpression.hxx>
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive)
+  : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+  const std::string& file, std::vector<std::string>& needed)
+{
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  std::vector<std::string> command;
+  if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+    this->SetError("Could not find objdump");
+    return false;
+  }
+  command.emplace_back("-p");
+  command.push_back(file);
+  builder.AddCommand(command);
+
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    std::ostringstream e;
+    e << "Failed to start objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex(
+    "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])\r$");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      needed.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    std::ostringstream e;
+    e << "Failed to wait on objdump process for:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    std::ostringstream e;
+    e << "Failed to run objdump on:\n  " << file;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..a67cb0c
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool
+  : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+    cmRuntimeDependencyArchive* archive);
+
+  bool GetFileInfo(const std::string& file,
+                   std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmBreakCommand.cxx b/Source/cmBreakCommand.cxx
index d07898f..95db689 100644
--- a/Source/cmBreakCommand.cxx
+++ b/Source/cmBreakCommand.cxx
@@ -10,14 +10,14 @@
 #include "cmPolicies.h"
 
 // cmBreakCommand
-bool cmBreakCommand::InitialPass(std::vector<std::string> const& args,
-                                 cmExecutionStatus& status)
+bool cmBreakCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status)
 {
-  if (!this->Makefile->IsLoopBlock()) {
+  if (!status.GetMakefile().IsLoopBlock()) {
     bool issueMessage = true;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0055)) {
+    switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0055)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0055) << "\n";
         break;
@@ -34,7 +34,7 @@
     if (issueMessage) {
       e << "A BREAK command was found outside of a proper "
            "FOREACH or WHILE loop scope.";
-      this->Makefile->IssueMessage(messageType, e.str());
+      status.GetMakefile().IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
@@ -47,7 +47,7 @@
     bool issueMessage = true;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0055)) {
+    switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0055)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0055) << "\n";
         break;
@@ -63,7 +63,7 @@
 
     if (issueMessage) {
       e << "The BREAK command does not accept any arguments.";
-      this->Makefile->IssueMessage(messageType, e.str());
+      status.GetMakefile().IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
diff --git a/Source/cmBreakCommand.h b/Source/cmBreakCommand.h
index 3b18567..e6ce6fe 100644
--- a/Source/cmBreakCommand.h
+++ b/Source/cmBreakCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmBreakCommand
+/**
  * \brief Break from an enclosing foreach or while loop
  *
  * cmBreakCommand returns from an enclosing foreach or while loop
  */
-class cmBreakCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmBreakCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmBreakCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx
index 428a0b2..49c9439 100644
--- a/Source/cmBuildCommand.cxx
+++ b/Source/cmBuildCommand.cxx
@@ -2,32 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmBuildCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmBuildCommand::InitialPass(std::vector<std::string> const& args,
-                                 cmExecutionStatus&)
-{
-  // Support the legacy signature of the command:
-  //
-  if (2 == args.size()) {
-    return this->TwoArgsSignature(args);
-  }
-
-  return this->MainSignature(args);
-}
-
-bool cmBuildCommand::MainSignature(std::vector<std::string> const& args)
+bool MainSignature(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("requires at least one argument naming a CMake variable");
+    status.SetError("requires at least one argument naming a CMake variable");
     return false;
   }
 
@@ -63,9 +52,7 @@
       doing = DoingNone;
       target = args[i];
     } else {
-      std::ostringstream e;
-      e << "unknown argument \"" << args[i] << "\"";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("unknown argument \"", args[i], "\""));
       return false;
     }
   }
@@ -82,30 +69,32 @@
     configuration = "Release";
   }
 
+  cmMakefile& mf = status.GetMakefile();
   if (!project_name.empty()) {
-    this->Makefile->IssueMessage(
-      MessageType::AUTHOR_WARNING,
-      "Ignoring PROJECT_NAME option because it has no effect.");
+    mf.IssueMessage(MessageType::AUTHOR_WARNING,
+                    "Ignoring PROJECT_NAME option because it has no effect.");
   }
 
-  std::string makecommand =
-    this->Makefile->GetGlobalGenerator()->GenerateCMakeBuildCommand(
-      target, configuration, "", this->Makefile->IgnoreErrorsCMP0061());
+  std::string makecommand = mf.GetGlobalGenerator()->GenerateCMakeBuildCommand(
+    target, configuration, "", mf.IgnoreErrorsCMP0061());
 
-  this->Makefile->AddDefinition(variable, makecommand.c_str());
+  mf.AddDefinition(variable, makecommand);
 
   return true;
 }
 
-bool cmBuildCommand::TwoArgsSignature(std::vector<std::string> const& args)
+bool TwoArgsSignature(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with less than two arguments");
+    status.SetError("called with less than two arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   std::string const& define = args[0];
-  const char* cacheValue = this->Makefile->GetDefinition(define);
+  const char* cacheValue = mf.GetDefinition(define);
 
   std::string configType;
   if (!cmSystemTools::GetEnv("CMAKE_CONFIG_TYPE", configType) ||
@@ -113,16 +102,28 @@
     configType = "Release";
   }
 
-  std::string makecommand =
-    this->Makefile->GetGlobalGenerator()->GenerateCMakeBuildCommand(
-      "", configType, "", this->Makefile->IgnoreErrorsCMP0061());
+  std::string makecommand = mf.GetGlobalGenerator()->GenerateCMakeBuildCommand(
+    "", configType, "", mf.IgnoreErrorsCMP0061());
 
   if (cacheValue) {
     return true;
   }
-  this->Makefile->AddCacheDefinition(define, makecommand.c_str(),
-                                     "Command used to build entire project "
-                                     "from the command line.",
-                                     cmStateEnums::STRING);
+  mf.AddCacheDefinition(define, makecommand.c_str(),
+                        "Command used to build entire project "
+                        "from the command line.",
+                        cmStateEnums::STRING);
   return true;
 }
+
+} // namespace
+
+bool cmBuildCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status)
+{
+  // Support the legacy signature of the command:
+  if (args.size() == 2) {
+    return TwoArgsSignature(args, status);
+  }
+
+  return MainSignature(args, status);
+}
diff --git a/Source/cmBuildCommand.h b/Source/cmBuildCommand.h
index e0529a4..45aa71d 100644
--- a/Source/cmBuildCommand.h
+++ b/Source/cmBuildCommand.h
@@ -8,42 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmBuildCommand
- * \brief build_command command
- *
- * cmBuildCommand implements the build_command CMake command
- */
-class cmBuildCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmBuildCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   * The primary command signature with optional, KEYWORD-based args.
-   */
-  virtual bool MainSignature(std::vector<std::string> const& args);
-
-  /**
-   * Legacy "exactly 2 args required" signature.
-   */
-  virtual bool TwoArgsSignature(std::vector<std::string> const& args);
-
-private:
-  bool IgnoreErrors() const;
-};
+bool cmBuildCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmBuildNameCommand.cxx b/Source/cmBuildNameCommand.cxx
index ddff686..3e517dc 100644
--- a/Source/cmBuildNameCommand.cxx
+++ b/Source/cmBuildNameCommand.cxx
@@ -2,24 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmBuildNameCommand.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
 
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
-// cmBuildNameCommand
-bool cmBuildNameCommand::InitialPass(std::vector<std::string> const& args,
-                                     cmExecutionStatus&)
+bool cmBuildNameCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
-  const char* cacheValue = this->Makefile->GetDefinition(args[0]);
+  cmMakefile& mf = status.GetMakefile();
+  const char* cacheValue = mf.GetDefinition(args[0]);
   if (cacheValue) {
     // do we need to correct the value?
     cmsys::RegularExpression reg("[()/]");
@@ -28,14 +28,14 @@
       std::replace(cv.begin(), cv.end(), '/', '_');
       std::replace(cv.begin(), cv.end(), '(', '_');
       std::replace(cv.begin(), cv.end(), ')', '_');
-      this->Makefile->AddCacheDefinition(args[0], cv.c_str(), "Name of build.",
-                                         cmStateEnums::STRING);
+      mf.AddCacheDefinition(args[0], cv.c_str(), "Name of build.",
+                            cmStateEnums::STRING);
     }
     return true;
   }
 
   std::string buildname = "WinNT";
-  if (this->Makefile->GetDefinition("UNIX")) {
+  if (mf.GetDefinition("UNIX")) {
     buildname.clear();
     cmSystemTools::RunSingleCommand("uname -a", &buildname, &buildname);
     if (!buildname.empty()) {
@@ -47,14 +47,14 @@
     }
   }
   std::string compiler = "${CMAKE_CXX_COMPILER}";
-  this->Makefile->ExpandVariablesInString(compiler);
+  mf.ExpandVariablesInString(compiler);
   buildname += "-";
   buildname += cmSystemTools::GetFilenameName(compiler);
   std::replace(buildname.begin(), buildname.end(), '/', '_');
   std::replace(buildname.begin(), buildname.end(), '(', '_');
   std::replace(buildname.begin(), buildname.end(), ')', '_');
 
-  this->Makefile->AddCacheDefinition(args[0], buildname.c_str(),
-                                     "Name of build.", cmStateEnums::STRING);
+  mf.AddCacheDefinition(args[0], buildname.c_str(), "Name of build.",
+                        cmStateEnums::STRING);
   return true;
 }
diff --git a/Source/cmBuildNameCommand.h b/Source/cmBuildNameCommand.h
index 4bb72d1..37a7268 100644
--- a/Source/cmBuildNameCommand.h
+++ b/Source/cmBuildNameCommand.h
@@ -8,16 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmBuildNameCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmBuildNameCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmBuildNameCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCLocaleEnvironmentScope.cxx b/Source/cmCLocaleEnvironmentScope.cxx
index 737e3ea..c6c38f8 100644
--- a/Source/cmCLocaleEnvironmentScope.cxx
+++ b/Source/cmCLocaleEnvironmentScope.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCLocaleEnvironmentScope.h"
 
-#include "cmSystemTools.h"
-
 #include <sstream>
 #include <utility>
 
+#include "cmSystemTools.h"
+
 cmCLocaleEnvironmentScope::cmCLocaleEnvironmentScope()
 {
   this->SetEnv("LANGUAGE", "");
diff --git a/Source/cmCLocaleEnvironmentScope.h b/Source/cmCLocaleEnvironmentScope.h
index 93032c1..aa2827e 100644
--- a/Source/cmCLocaleEnvironmentScope.h
+++ b/Source/cmCLocaleEnvironmentScope.h
@@ -22,7 +22,7 @@
   std::string GetEnv(std::string const& key);
   void SetEnv(std::string const& key, std::string const& value);
 
-  typedef std::map<std::string, std::string> backup_map_t;
+  using backup_map_t = std::map<std::string, std::string>;
   backup_map_t EnvironmentBackup;
 };
 
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 54f08bb..26e9af0 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -2,11 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakeHostSystemInformationCommand.h"
 
-#include <sstream>
+#include <cstddef>
 
-#include "cmMakefile.h"
 #include "cmsys/SystemInformation.hxx"
 
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+
 #if defined(_WIN32)
 #  include "cmAlgorithms.h"
 #  include "cmGlobalGenerator.h"
@@ -16,16 +18,22 @@
 #  define HAVE_VS_SETUP_HELPER
 #endif
 
-class cmExecutionStatus;
+namespace {
+bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info,
+              std::string const& key, std::string& value);
+std::string ValueToString(size_t value);
+std::string ValueToString(const char* value);
+std::string ValueToString(std::string const& value);
+}
 
 // cmCMakeHostSystemInformation
-bool cmCMakeHostSystemInformationCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
+                                         cmExecutionStatus& status)
 {
   size_t current_index = 0;
 
   if (args.size() < (current_index + 2) || args[current_index] != "RESULT") {
-    this->SetError("missing RESULT specification.");
+    status.SetError("missing RESULT specification.");
     return false;
   }
 
@@ -33,7 +41,7 @@
   current_index += 2;
 
   if (args.size() < (current_index + 2) || args[current_index] != "QUERY") {
-    this->SetError("missing QUERY specification");
+    status.SetError("missing QUERY specification");
     return false;
   }
 
@@ -49,89 +57,91 @@
       result_list += ";";
     }
     std::string value;
-    if (!this->GetValue(info, key, value)) {
+    if (!GetValue(status, info, key, value)) {
       return false;
     }
     result_list += value;
   }
 
-  this->Makefile->AddDefinition(variable, result_list.c_str());
+  status.GetMakefile().AddDefinition(variable, result_list);
 
   return true;
 }
 
-bool cmCMakeHostSystemInformationCommand::GetValue(
-  cmsys::SystemInformation& info, std::string const& key, std::string& value)
+namespace {
+
+bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info,
+              std::string const& key, std::string& value)
 {
   if (key == "NUMBER_OF_LOGICAL_CORES") {
-    value = this->ValueToString(info.GetNumberOfLogicalCPU());
+    value = ValueToString(info.GetNumberOfLogicalCPU());
   } else if (key == "NUMBER_OF_PHYSICAL_CORES") {
-    value = this->ValueToString(info.GetNumberOfPhysicalCPU());
+    value = ValueToString(info.GetNumberOfPhysicalCPU());
   } else if (key == "HOSTNAME") {
-    value = this->ValueToString(info.GetHostname());
+    value = ValueToString(info.GetHostname());
   } else if (key == "FQDN") {
-    value = this->ValueToString(info.GetFullyQualifiedDomainName());
+    value = ValueToString(info.GetFullyQualifiedDomainName());
   } else if (key == "TOTAL_VIRTUAL_MEMORY") {
-    value = this->ValueToString(info.GetTotalVirtualMemory());
+    value = ValueToString(info.GetTotalVirtualMemory());
   } else if (key == "AVAILABLE_VIRTUAL_MEMORY") {
-    value = this->ValueToString(info.GetAvailableVirtualMemory());
+    value = ValueToString(info.GetAvailableVirtualMemory());
   } else if (key == "TOTAL_PHYSICAL_MEMORY") {
-    value = this->ValueToString(info.GetTotalPhysicalMemory());
+    value = ValueToString(info.GetTotalPhysicalMemory());
   } else if (key == "AVAILABLE_PHYSICAL_MEMORY") {
-    value = this->ValueToString(info.GetAvailablePhysicalMemory());
+    value = ValueToString(info.GetAvailablePhysicalMemory());
   } else if (key == "IS_64BIT") {
-    value = this->ValueToString(info.Is64Bits());
+    value = ValueToString(info.Is64Bits());
   } else if (key == "HAS_FPU") {
-    value = this->ValueToString(
+    value = ValueToString(
       info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_FPU));
   } else if (key == "HAS_MMX") {
-    value = this->ValueToString(
+    value = ValueToString(
       info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX));
   } else if (key == "HAS_MMX_PLUS") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS));
   } else if (key == "HAS_SSE") {
-    value = this->ValueToString(
+    value = ValueToString(
       info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE));
   } else if (key == "HAS_SSE2") {
-    value = this->ValueToString(
+    value = ValueToString(
       info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2));
   } else if (key == "HAS_SSE_FP") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_SSE_FP));
   } else if (key == "HAS_SSE_MMX") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_SSE_MMX));
   } else if (key == "HAS_AMD_3DNOW") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW));
   } else if (key == "HAS_AMD_3DNOW_PLUS") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS));
   } else if (key == "HAS_IA64") {
-    value = this->ValueToString(
+    value = ValueToString(
       info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64));
   } else if (key == "HAS_SERIAL_NUMBER") {
-    value = this->ValueToString(info.DoesCPUSupportFeature(
+    value = ValueToString(info.DoesCPUSupportFeature(
       cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER));
   } else if (key == "PROCESSOR_NAME") {
-    value = this->ValueToString(info.GetExtendedProcessorName());
+    value = ValueToString(info.GetExtendedProcessorName());
   } else if (key == "PROCESSOR_DESCRIPTION") {
     value = info.GetCPUDescription();
   } else if (key == "PROCESSOR_SERIAL_NUMBER") {
-    value = this->ValueToString(info.GetProcessorSerialNumber());
+    value = ValueToString(info.GetProcessorSerialNumber());
   } else if (key == "OS_NAME") {
-    value = this->ValueToString(info.GetOSName());
+    value = ValueToString(info.GetOSName());
   } else if (key == "OS_RELEASE") {
-    value = this->ValueToString(info.GetOSRelease());
+    value = ValueToString(info.GetOSRelease());
   } else if (key == "OS_VERSION") {
-    value = this->ValueToString(info.GetOSVersion());
+    value = ValueToString(info.GetOSVersion());
   } else if (key == "OS_PLATFORM") {
-    value = this->ValueToString(info.GetOSPlatform());
+    value = ValueToString(info.GetOSPlatform());
 #ifdef HAVE_VS_SETUP_HELPER
   } else if (key == "VS_15_DIR") {
     // If generating for the VS 15 IDE, use the same instance.
-    cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
+    cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
     if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 15 ")) {
       cmGlobalVisualStudioVersionedGenerator* vs15gen =
         static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
@@ -147,7 +157,7 @@
     }
   } else if (key == "VS_16_DIR") {
     // If generating for the VS 16 IDE, use the same instance.
-    cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
+    cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
     if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 16 ")) {
       cmGlobalVisualStudioVersionedGenerator* vs16gen =
         static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
@@ -164,30 +174,26 @@
 #endif
   } else {
     std::string e = "does not recognize <key> " + key;
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
   return true;
 }
 
-std::string cmCMakeHostSystemInformationCommand::ValueToString(
-  size_t value) const
+std::string ValueToString(size_t value)
 {
-  std::ostringstream tmp;
-  tmp << value;
-  return tmp.str();
+  return std::to_string(value);
 }
 
-std::string cmCMakeHostSystemInformationCommand::ValueToString(
-  const char* value) const
+std::string ValueToString(const char* value)
 {
   std::string safe_string = value ? value : "";
   return safe_string;
 }
 
-std::string cmCMakeHostSystemInformationCommand::ValueToString(
-  std::string const& value) const
+std::string ValueToString(std::string const& value)
 {
   return value;
 }
+}
diff --git a/Source/cmCMakeHostSystemInformationCommand.h b/Source/cmCMakeHostSystemInformationCommand.h
index b871641..79e3f27 100644
--- a/Source/cmCMakeHostSystemInformationCommand.h
+++ b/Source/cmCMakeHostSystemInformationCommand.h
@@ -5,48 +5,18 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stddef.h>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-namespace cmsys {
-class SystemInformation;
-} // namespace cmsys
 
-/** \class cmCMakeHostSystemInformationCommand
+/**
  * \brief Query host system specific information
  *
  * cmCMakeHostSystemInformationCommand queries system information of
  * the system on which CMake runs.
  */
-class cmCMakeHostSystemInformationCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    return new cmCMakeHostSystemInformationCommand;
-  }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool GetValue(cmsys::SystemInformation& info, std::string const& key,
-                std::string& value);
-
-  std::string ValueToString(size_t value) const;
-  std::string ValueToString(const char* value) const;
-  std::string ValueToString(std::string const& value) const;
-};
+bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
+                                         cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCMakeMinimumRequired.cxx b/Source/cmCMakeMinimumRequired.cxx
index 4b4bca2..1b03873 100644
--- a/Source/cmCMakeMinimumRequired.cxx
+++ b/Source/cmCMakeMinimumRequired.cxx
@@ -2,29 +2,35 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakeMinimumRequired.h"
 
+#include <cstdio>
 #include <sstream>
-#include <stdio.h>
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
-class cmExecutionStatus;
+namespace {
+bool EnforceUnknownArguments(std::string const& version_max,
+                             std::vector<std::string> const& unknown_arguments,
+                             cmExecutionStatus& status);
+}
 
 // cmCMakeMinimumRequired
-bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmCMakeMinimumRequired(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   // Process arguments.
   std::string version_string;
   bool doing_version = false;
+  std::vector<std::string> unknown_arguments;
   for (std::string const& arg : args) {
     if (arg == "VERSION") {
       doing_version = true;
     } else if (arg == "FATAL_ERROR") {
       if (doing_version) {
-        this->SetError("called with no value for VERSION.");
+        status.SetError("called with no value for VERSION.");
         return false;
       }
       doing_version = false;
@@ -32,17 +38,17 @@
       doing_version = false;
       version_string = arg;
     } else {
-      this->UnknownArguments.push_back(arg);
+      unknown_arguments.push_back(arg);
     }
   }
   if (doing_version) {
-    this->SetError("called with no value for VERSION.");
+    status.SetError("called with no value for VERSION.");
     return false;
   }
 
   // Make sure there was a version to check.
   if (version_string.empty()) {
-    return this->EnforceUnknownArguments(std::string());
+    return EnforceUnknownArguments(std::string(), unknown_arguments, status);
   }
 
   // Separate the <min> version and any trailing ...<max> component.
@@ -56,13 +62,13 @@
     std::ostringstream e;
     e << "VERSION \"" << version_string
       << R"(" does not have a version on both sides of "...".)";
-    this->SetError(e.str());
+    status.SetError(e.str());
     return false;
   }
 
   // Save the required version string.
-  this->Makefile->AddDefinition("CMAKE_MINIMUM_REQUIRED_VERSION",
-                                version_min.c_str());
+  status.GetMakefile().AddDefinition("CMAKE_MINIMUM_REQUIRED_VERSION",
+                                     version_min);
 
   // Get the current version number.
   unsigned int current_major = cmVersion::GetMajorVersion();
@@ -80,7 +86,7 @@
              &required_minor, &required_patch, &required_tweak) < 2) {
     std::ostringstream e;
     e << "could not parse VERSION \"" << version_min << "\".";
-    this->SetError(e.str());
+    status.SetError(e.str());
     return false;
   }
 
@@ -96,32 +102,34 @@
     e << "CMake " << version_min
       << " or higher is required.  You are running version "
       << cmVersion::GetCMakeVersion();
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, e.str());
     cmSystemTools::SetFatalErrorOccured();
     return true;
   }
 
   // The version is not from the future, so enforce unknown arguments.
-  if (!this->EnforceUnknownArguments(version_max)) {
+  if (!EnforceUnknownArguments(version_max, unknown_arguments, status)) {
     return false;
   }
 
   if (required_major < 2 || (required_major == 2 && required_minor < 4)) {
-    this->Makefile->IssueMessage(
+    status.GetMakefile().IssueMessage(
       MessageType::AUTHOR_WARNING,
       "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0.");
-    this->Makefile->SetPolicyVersion("2.4", version_max);
+    status.GetMakefile().SetPolicyVersion("2.4", version_max);
   } else {
-    this->Makefile->SetPolicyVersion(version_min, version_max);
+    status.GetMakefile().SetPolicyVersion(version_min, version_max);
   }
 
   return true;
 }
 
-bool cmCMakeMinimumRequired::EnforceUnknownArguments(
-  std::string const& version_max)
+namespace {
+bool EnforceUnknownArguments(std::string const& version_max,
+                             std::vector<std::string> const& unknown_arguments,
+                             cmExecutionStatus& status)
 {
-  if (this->UnknownArguments.empty()) {
+  if (unknown_arguments.empty()) {
     return true;
   }
 
@@ -150,7 +158,8 @@
   }
 
   std::ostringstream e;
-  e << "called with unknown argument \"" << this->UnknownArguments[0] << "\".";
-  this->SetError(e.str());
+  e << "called with unknown argument \"" << unknown_arguments[0] << "\".";
+  status.SetError(e.str());
   return false;
 }
+}
diff --git a/Source/cmCMakeMinimumRequired.h b/Source/cmCMakeMinimumRequired.h
index f9b61e1..53f78f6 100644
--- a/Source/cmCMakeMinimumRequired.h
+++ b/Source/cmCMakeMinimumRequired.h
@@ -8,33 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmCMakeMinimumRequired
+/**
  * \brief cmake_minimum_required command
  *
  * cmCMakeMinimumRequired implements the cmake_minimum_required CMake command
  */
-class cmCMakeMinimumRequired : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmCMakeMinimumRequired; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  std::vector<std::string> UnknownArguments;
-  bool EnforceUnknownArguments(std::string const& version_max);
-};
+bool cmCMakeMinimumRequired(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx
index 8da5ef7..b7f08d2 100644
--- a/Source/cmCMakePolicyCommand.cxx
+++ b/Source/cmCMakePolicyCommand.cxx
@@ -2,90 +2,99 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakePolicyCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 
-class cmExecutionStatus;
+namespace {
+bool HandleSetMode(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+bool HandleGetMode(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+bool HandleVersionMode(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
+bool HandleGetWarningMode(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
+}
 
 // cmCMakePolicyCommand
-bool cmCMakePolicyCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmCMakePolicyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("requires at least one argument.");
+    status.SetError("requires at least one argument.");
     return false;
   }
 
   if (args[0] == "SET") {
-    return this->HandleSetMode(args);
+    return HandleSetMode(args, status);
   }
   if (args[0] == "GET") {
-    return this->HandleGetMode(args);
+    return HandleGetMode(args, status);
   }
   if (args[0] == "PUSH") {
     if (args.size() > 1) {
-      this->SetError("PUSH may not be given additional arguments.");
+      status.SetError("PUSH may not be given additional arguments.");
       return false;
     }
-    this->Makefile->PushPolicy();
+    status.GetMakefile().PushPolicy();
     return true;
   }
   if (args[0] == "POP") {
     if (args.size() > 1) {
-      this->SetError("POP may not be given additional arguments.");
+      status.SetError("POP may not be given additional arguments.");
       return false;
     }
-    this->Makefile->PopPolicy();
+    status.GetMakefile().PopPolicy();
     return true;
   }
   if (args[0] == "VERSION") {
-    return this->HandleVersionMode(args);
+    return HandleVersionMode(args, status);
   }
   if (args[0] == "GET_WARNING") {
-    return this->HandleGetWarningMode(args);
+    return HandleGetWarningMode(args, status);
   }
 
-  std::ostringstream e;
-  e << "given unknown first argument \"" << args[0] << "\"";
-  this->SetError(e.str());
+  status.SetError(cmStrCat("given unknown first argument \"", args[0], "\""));
   return false;
 }
 
-bool cmCMakePolicyCommand::HandleSetMode(std::vector<std::string> const& args)
+namespace {
+
+bool HandleSetMode(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("SET must be given exactly 2 additional arguments.");
+    status.SetError("SET must be given exactly 2 additional arguments.");
     return false;
   }
 
-  cmPolicies::PolicyStatus status;
+  cmPolicies::PolicyStatus policyStatus;
   if (args[2] == "OLD") {
-    status = cmPolicies::OLD;
+    policyStatus = cmPolicies::OLD;
   } else if (args[2] == "NEW") {
-    status = cmPolicies::NEW;
+    policyStatus = cmPolicies::NEW;
   } else {
-    std::ostringstream e;
-    e << "SET given unrecognized policy status \"" << args[2] << "\"";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("SET given unrecognized policy status \"", args[2], "\""));
     return false;
   }
 
-  if (!this->Makefile->SetPolicy(args[1].c_str(), status)) {
-    this->SetError("SET failed to set policy.");
+  if (!status.GetMakefile().SetPolicy(args[1].c_str(), policyStatus)) {
+    status.SetError("SET failed to set policy.");
     return false;
   }
   if (args[1] == "CMP0001" &&
-      (status == cmPolicies::WARN || status == cmPolicies::OLD)) {
-    if (!(this->Makefile->GetState()->GetInitializedCacheValue(
+      (policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)) {
+    if (!(status.GetMakefile().GetState()->GetInitializedCacheValue(
           "CMAKE_BACKWARDS_COMPATIBILITY"))) {
       // Set it to 2.4 because that is the last version where the
       // variable had meaning.
-      this->Makefile->AddCacheDefinition(
+      status.GetMakefile().AddCacheDefinition(
         "CMAKE_BACKWARDS_COMPATIBILITY", "2.4",
         "For backwards compatibility, what version of CMake "
         "commands and "
@@ -96,14 +105,15 @@
   return true;
 }
 
-bool cmCMakePolicyCommand::HandleGetMode(std::vector<std::string> const& args)
+bool HandleGetMode(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
 {
   bool parent_scope = false;
   if (args.size() == 4 && args[3] == "PARENT_SCOPE") {
     // Undocumented PARENT_SCOPE option for use within CMake.
     parent_scope = true;
   } else if (args.size() != 3) {
-    this->SetError("GET must be given exactly 2 additional arguments.");
+    status.SetError("GET must be given exactly 2 additional arguments.");
     return false;
   }
 
@@ -114,54 +124,55 @@
   // Lookup the policy number.
   cmPolicies::PolicyID pid;
   if (!cmPolicies::GetPolicyID(id.c_str(), pid)) {
-    std::ostringstream e;
-    e << "GET given policy \"" << id << "\" which is not known to this "
-      << "version of CMake.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("GET given policy \"", id,
+               "\" which is not known to this version of CMake."));
     return false;
   }
 
   // Lookup the policy setting.
-  cmPolicies::PolicyStatus status =
-    this->Makefile->GetPolicyStatus(pid, parent_scope);
-  switch (status) {
+  cmPolicies::PolicyStatus policyStatus =
+    status.GetMakefile().GetPolicyStatus(pid, parent_scope);
+  switch (policyStatus) {
     case cmPolicies::OLD:
       // Report that the policy is set to OLD.
-      this->Makefile->AddDefinition(var, "OLD");
+      status.GetMakefile().AddDefinition(var, "OLD");
       break;
     case cmPolicies::WARN:
       // Report that the policy is not set.
-      this->Makefile->AddDefinition(var, "");
+      status.GetMakefile().AddDefinition(var, "");
       break;
     case cmPolicies::NEW:
       // Report that the policy is set to NEW.
-      this->Makefile->AddDefinition(var, "NEW");
+      status.GetMakefile().AddDefinition(var, "NEW");
       break;
     case cmPolicies::REQUIRED_IF_USED:
     case cmPolicies::REQUIRED_ALWAYS:
       // The policy is required to be set before anything needs it.
       {
-        std::ostringstream e;
-        e << cmPolicies::GetRequiredPolicyError(pid) << "\n"
-          << "The call to cmake_policy(GET " << id << " ...) at which this "
-          << "error appears requests the policy, and this version of CMake "
-          << "requires that the policy be set to NEW before it is checked.";
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        status.GetMakefile().IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            cmPolicies::GetRequiredPolicyError(pid), "\n",
+            "The call to cmake_policy(GET ", id,
+            " ...) at which this "
+            "error appears requests the policy, and this version of CMake ",
+            "requires that the policy be set to NEW before it is checked."));
       }
   }
 
   return true;
 }
 
-bool cmCMakePolicyCommand::HandleVersionMode(
-  std::vector<std::string> const& args)
+bool HandleVersionMode(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() <= 1) {
-    this->SetError("VERSION not given an argument");
+    status.SetError("VERSION not given an argument");
     return false;
   }
   if (args.size() >= 3) {
-    this->SetError("VERSION given too many arguments");
+    status.SetError("VERSION given too many arguments");
     return false;
   }
   std::string const& version_string = args[1];
@@ -174,22 +185,21 @@
     : std::string();
   if (dd != std::string::npos &&
       (version_min.empty() || version_max.empty())) {
-    std::ostringstream e;
-    e << "VERSION \"" << version_string
-      << R"(" does not have a version on both sides of "...".)";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("VERSION \"", version_string,
+               R"(" does not have a version on both sides of "...".)"));
     return false;
   }
 
-  this->Makefile->SetPolicyVersion(version_min, version_max);
+  status.GetMakefile().SetPolicyVersion(version_min, version_max);
   return true;
 }
 
-bool cmCMakePolicyCommand::HandleGetWarningMode(
-  std::vector<std::string> const& args)
+bool HandleGetWarningMode(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError(
+    status.SetError(
       "GET_WARNING must be given exactly 2 additional arguments.");
     return false;
   }
@@ -201,16 +211,15 @@
   // Lookup the policy number.
   cmPolicies::PolicyID pid;
   if (!cmPolicies::GetPolicyID(id.c_str(), pid)) {
-    std::ostringstream e;
-    e << "GET_WARNING given policy \"" << id
-      << "\" which is not known to this version of CMake.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("GET_WARNING given policy \"", id,
+               "\" which is not known to this version of CMake."));
     return false;
   }
 
   // Lookup the policy warning.
-  this->Makefile->AddDefinition(var,
-                                cmPolicies::GetPolicyWarning(pid).c_str());
+  status.GetMakefile().AddDefinition(var, cmPolicies::GetPolicyWarning(pid));
 
   return true;
 }
+}
diff --git a/Source/cmCMakePolicyCommand.h b/Source/cmCMakePolicyCommand.h
index cca1406..ba9397d 100644
--- a/Source/cmCMakePolicyCommand.h
+++ b/Source/cmCMakePolicyCommand.h
@@ -8,36 +8,15 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmCMakePolicyCommand
+/**
  * \brief Set how CMake should handle policies
  *
  * cmCMakePolicyCommand sets how CMake should deal with backwards
  * compatibility policies.
  */
-class cmCMakePolicyCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmCMakePolicyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool HandleSetMode(std::vector<std::string> const& args);
-  bool HandleGetMode(std::vector<std::string> const& args);
-  bool HandleVersionMode(std::vector<std::string> const& args);
-  bool HandleGetWarningMode(std::vector<std::string> const& args);
-};
+bool cmCMakePolicyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCPackPropertiesGenerator.cxx b/Source/cmCPackPropertiesGenerator.cxx
index a33b824..cc9ad01 100644
--- a/Source/cmCPackPropertiesGenerator.cxx
+++ b/Source/cmCPackPropertiesGenerator.cxx
@@ -1,12 +1,12 @@
 #include "cmCPackPropertiesGenerator.h"
 
+#include <map>
+#include <ostream>
+
 #include "cmGeneratorExpression.h"
 #include "cmInstalledFile.h"
 #include "cmOutputConverter.h"
 
-#include <map>
-#include <ostream>
-
 cmCPackPropertiesGenerator::cmCPackPropertiesGenerator(
   cmLocalGenerator* lg, cmInstalledFile const& installedFile,
   std::vector<std::string> const& configurations)
diff --git a/Source/cmCPackPropertiesGenerator.h b/Source/cmCPackPropertiesGenerator.h
index ad943c5..8339238 100644
--- a/Source/cmCPackPropertiesGenerator.h
+++ b/Source/cmCPackPropertiesGenerator.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmScriptGenerator.h"
+
 class cmInstalledFile;
 class cmLocalGenerator;
 
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index 255a8e6..177bca8 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -7,6 +7,8 @@
 
 #include "cmCPluginAPI.h"
 
+#include <cstdlib>
+
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -14,8 +16,6 @@
 #include "cmState.h"
 #include "cmVersion.h"
 
-#include <stdlib.h>
-
 #ifdef __QNX__
 #  include <malloc.h> /* for malloc/free on QNX */
 #endif
@@ -65,8 +65,10 @@
 
 void CCONV cmAddDefinition(void* arg, const char* name, const char* value)
 {
-  cmMakefile* mf = static_cast<cmMakefile*>(arg);
-  mf->AddDefinition(name, value);
+  if (value) {
+    cmMakefile* mf = static_cast<cmMakefile*>(arg);
+    mf->AddDefinition(name, value);
+  }
 }
 
 /* Add a definition to this makefile and the global cmake cache. */
@@ -218,8 +220,10 @@
   }
 
   // Pass the call to the makefile instance.
-  mf->AddUtilityCommand(utilityName, cmMakefile::TargetOrigin::Project,
-                        (all ? false : true), nullptr, depends2, commandLines);
+  std::vector<std::string> no_byproducts;
+  mf->AddUtilityCommand(utilityName, cmCommandOrigin::Project,
+                        (all ? false : true), nullptr, no_byproducts, depends2,
+                        commandLines);
 }
 void CCONV cmAddCustomCommand(void* arg, const char* source,
                               const char* command, int numArgs,
@@ -317,16 +321,16 @@
   commandLines.push_back(commandLine);
 
   // Select the command type.
-  cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
+  cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
   switch (commandType) {
     case CM_PRE_BUILD:
-      cctype = cmTarget::PRE_BUILD;
+      cctype = cmCustomCommandType::PRE_BUILD;
       break;
     case CM_PRE_LINK:
-      cctype = cmTarget::PRE_LINK;
+      cctype = cmCustomCommandType::PRE_LINK;
       break;
     case CM_POST_BUILD:
-      cctype = cmTarget::POST_BUILD;
+      cctype = cmCustomCommandType::POST_BUILD;
       break;
   }
 
@@ -421,7 +425,7 @@
     // Assume all arguments are quoted.
     lff.Arguments.emplace_back(args[i], cmListFileArgument::Quoted, 0);
   }
-  cmExecutionStatus status;
+  cmExecutionStatus status(*mf);
   return mf->ExecuteCommand(lff, status);
 }
 
@@ -488,9 +492,9 @@
   : public std::map<cmSourceFile*, cmCPluginAPISourceFile*>
 {
 public:
-  typedef std::map<cmSourceFile*, cmCPluginAPISourceFile*> derived;
-  typedef derived::iterator iterator;
-  typedef derived::value_type value_type;
+  using derived = std::map<cmSourceFile*, cmCPluginAPISourceFile*>;
+  using iterator = derived::iterator;
+  using value_type = derived::value_type;
   cmCPluginAPISourceFileMap() = default;
   ~cmCPluginAPISourceFileMap()
   {
@@ -529,12 +533,12 @@
   cmMakefile* mf = static_cast<cmMakefile*>(arg);
   if (cmSourceFile* rsf = mf->GetSource(name)) {
     // Lookup the proxy source file object for this source.
-    cmCPluginAPISourceFileMap::iterator i = cmCPluginAPISourceFiles.find(rsf);
+    auto i = cmCPluginAPISourceFiles.find(rsf);
     if (i == cmCPluginAPISourceFiles.end()) {
       // Create a proxy source file object for this source.
       cmCPluginAPISourceFile* sf = new cmCPluginAPISourceFile;
       sf->RealSourceFile = rsf;
-      sf->FullPath = rsf->GetFullPath();
+      sf->FullPath = rsf->ResolveFullPath();
       sf->SourceName =
         cmSystemTools::GetFilenameWithoutLastExtension(sf->FullPath);
       sf->SourceExtension =
@@ -559,7 +563,7 @@
 
   // Create the real cmSourceFile instance and copy over saved information.
   cmSourceFile* rsf = mf->GetOrCreateSource(osf->FullPath);
-  rsf->GetProperties() = osf->Properties;
+  rsf->SetProperties(osf->Properties);
   for (std::string const& d : osf->Depends) {
     rsf->AddDepend(d);
   }
@@ -606,7 +610,7 @@
   if (cmSourceFile* rsf = sf->RealSourceFile) {
     return rsf->GetPropertyAsBool(prop) ? 1 : 0;
   }
-  return cmSystemTools::IsOn(cmSourceFileGetProperty(arg, prop)) ? 1 : 0;
+  return cmIsOn(cmSourceFileGetProperty(arg, prop)) ? 1 : 0;
 }
 
 void CCONV cmSourceFileSetProperty(void* arg, const char* prop,
@@ -688,9 +692,7 @@
 
   // Next, try the various source extensions
   for (std::string const& ext : sourceExts) {
-    hname = pathname;
-    hname += ".";
-    hname += ext;
+    hname = cmStrCat(pathname, '.', ext);
     if (cmSystemTools::FileExists(hname)) {
       sf->SourceExtension = ext;
       sf->FullPath = hname;
@@ -700,9 +702,7 @@
 
   // Finally, try the various header extensions
   for (std::string const& ext : headerExts) {
-    hname = pathname;
-    hname += ".";
-    hname += ext;
+    hname = cmStrCat(pathname, '.', ext);
     if (cmSystemTools::FileExists(hname)) {
       sf->SourceExtension = ext;
       sf->FullPath = hname;
diff --git a/Source/cmCPluginAPI.h b/Source/cmCPluginAPI.h
index adc57a2..6a95148 100644
--- a/Source/cmCPluginAPI.h
+++ b/Source/cmCPluginAPI.h
@@ -28,6 +28,7 @@
 structure must be kept in sync with the static decaled at the bottom of
 cmCPLuginAPI.cxx
 =========================================================================*/
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef struct
 {
   /*=========================================================================
@@ -194,12 +195,21 @@
 /*=========================================================================
 Finally we define the key data structures and function prototypes
 =========================================================================*/
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef const char*(CCONV* CM_DOC_FUNCTION)();
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef int(CCONV* CM_INITIAL_PASS_FUNCTION)(void* info, void* mf, int argc,
                                              char* []);
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef void(CCONV* CM_FINAL_PASS_FUNCTION)(void* info, void* mf);
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef void(CCONV* CM_DESTRUCTOR_FUNCTION)(void* info);
 
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef struct
 {
   unsigned long reserved1; /* Reserved for future use.  DO NOT USE.  */
@@ -216,6 +226,7 @@
   void* ClientData;
 } cmLoadedCommandInfo;
 
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef void(CCONV* CM_INIT_FUNCTION)(cmLoadedCommandInfo*);
 
 #ifdef __cplusplus
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index d1226c3..10b7646 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -2,34 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTest.h"
 
-#include "cm_curl.h"
-#include "cm_zlib.h"
+#include <algorithm>
+#include <cctype>
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "cmsys/Base64.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/Process.h"
 #include "cmsys/SystemInformation.hxx"
-#include <algorithm>
-#include <chrono>
-#include <ctype.h>
-#include <iostream>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-#include <time.h>
-#include <utility>
-#include <vector>
+
+#include "cm_curl.h"
+#include "cm_zlib.h"
 #if defined(_WIN32)
 #  include <windows.h> // IWYU pragma: keep
 #else
 #  include <unistd.h> // IWYU pragma: keep
 #endif
 
+#include <cm/memory>
+
 #include "cmAlgorithms.h"
 #include "cmCTestBuildAndTestHandler.h"
 #include "cmCTestBuildHandler.h"
@@ -51,6 +54,7 @@
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
@@ -153,7 +157,7 @@
   bool TomorrowTag = false;
 
   int TestModel = cmCTest::EXPERIMENTAL;
-  std::string SpecificTrack;
+  std::string SpecificGroup;
 
   cmDuration TimeOut = cmDuration::zero();
 
@@ -321,12 +325,11 @@
 {
   std::string envValue;
   if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", envValue)) {
-    this->Impl->OutputTestOutputOnTestFailure =
-      !cmSystemTools::IsOff(envValue);
+    this->Impl->OutputTestOutputOnTestFailure = !cmIsOff(envValue);
   }
   envValue.clear();
   if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) {
-    this->Impl->TestProgressOutput = !cmSystemTools::IsOff(envValue);
+    this->Impl->TestProgressOutput = !cmIsOff(envValue);
   }
 
   this->Impl->Parts[PartStart].SetName("Start");
@@ -507,10 +510,10 @@
             day != lctime->tm_mday) {
           tag.clear();
         }
-        std::string track;
-        if (cmSystemTools::GetLineFromStream(tfin, track) &&
+        std::string group;
+        if (cmSystemTools::GetLineFromStream(tfin, group) &&
             !this->Impl->Parts[PartStart] && !command) {
-          this->Impl->SpecificTrack = track;
+          this->Impl->SpecificGroup = group;
         }
         std::string model;
         if (cmSystemTools::GetLineFromStream(tfin, model) &&
@@ -563,13 +566,13 @@
         }
       }
     } else {
-      std::string track;
+      std::string group;
       std::string modelStr;
       int model = cmCTest::UNKNOWN;
 
       if (tfin) {
         cmSystemTools::GetLineFromStream(tfin, tag);
-        cmSystemTools::GetLineFromStream(tfin, track);
+        cmSystemTools::GetLineFromStream(tfin, group);
         if (cmSystemTools::GetLineFromStream(tfin, modelStr)) {
           model = GetTestModelFromString(modelStr.c_str());
         }
@@ -604,15 +607,15 @@
                            quiet);
       }
 
-      if (!this->Impl->SpecificTrack.empty() &&
-          track != this->Impl->SpecificTrack) {
+      if (!this->Impl->SpecificGroup.empty() &&
+          group != this->Impl->SpecificGroup) {
         cmCTestOptionalLog(this, WARNING,
-                           "Track given in TAG does not match "
-                           "track given in ctest_start()"
+                           "Group given in TAG does not match "
+                           "group given in ctest_start()"
                              << std::endl,
                            quiet);
       } else {
-        this->Impl->SpecificTrack = track;
+        this->Impl->SpecificGroup = group;
       }
 
       cmCTestOptionalLog(this, OUTPUT,
@@ -640,12 +643,10 @@
   cmMakefile* mf = command->GetMakefile();
   std::string fname;
 
-  std::string src_dir_fname = src_dir;
-  src_dir_fname += "/CTestConfig.cmake";
+  std::string src_dir_fname = cmStrCat(src_dir, "/CTestConfig.cmake");
   cmSystemTools::ConvertToUnixSlashes(src_dir_fname);
 
-  std::string bld_dir_fname = bld_dir;
-  bld_dir_fname += "/CTestConfig.cmake";
+  std::string bld_dir_fname = cmStrCat(bld_dir, "/CTestConfig.cmake");
   cmSystemTools::ConvertToUnixSlashes(bld_dir_fname);
 
   if (cmSystemTools::FileExists(bld_dir_fname)) {
@@ -661,8 +662,7 @@
                        command->ShouldBeQuiet());
     bool readit = mf->ReadDependentFile(fname);
     if (!readit) {
-      std::string m = "Could not find include file: ";
-      m += fname;
+      std::string m = cmStrCat("Could not find include file: ", fname);
       command->SetError(m);
       return false;
     }
@@ -752,7 +752,7 @@
   std::string const& testLoad = this->GetCTestConfiguration("TestLoad");
   if (!testLoad.empty()) {
     unsigned long load;
-    if (cmSystemTools::StringToULong(testLoad.c_str(), &load)) {
+    if (cmStrToULong(testLoad, &load)) {
       this->SetTestLoad(load);
     } else {
       cmCTestLog(this, WARNING,
@@ -761,7 +761,7 @@
   }
   if (this->Impl->ProduceXML) {
     this->Impl->CompressXMLFiles =
-      cmSystemTools::IsOn(this->GetCTestConfiguration("CompressSubmission"));
+      cmIsOn(this->GetCTestConfiguration("CompressSubmission"));
   }
   return true;
 }
@@ -855,8 +855,7 @@
   if (this->CTestFileExists(file)) {
     this->AddSubmitFile(part, file);
   } else {
-    std::string name = file;
-    name += ".gz";
+    std::string name = cmStrCat(file, ".gz");
     if (this->CTestFileExists(name)) {
       this->AddSubmitFile(part, file);
     } else {
@@ -1020,8 +1019,8 @@
 
 std::string cmCTest::GetTestModelString()
 {
-  if (!this->Impl->SpecificTrack.empty()) {
-    return this->Impl->SpecificTrack;
+  if (!this->Impl->SpecificGroup.empty()) {
+    return this->Impl->SpecificGroup;
   }
   switch (this->Impl->TestModel) {
     case cmCTest::NIGHTLY:
@@ -1098,9 +1097,10 @@
   cmProcessOutput processOutput(encoding);
   std::string strdata;
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
-             "   Each . represents " << tick_len << " bytes of output"
-                                     << std::endl
-                                     << "    " << std::flush);
+             "   Each . represents " << tick_len
+                                     << " bytes of output\n"
+                                        "    "
+                                     << std::flush);
   while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
     processOutput.DecodeText(data, length, strdata);
     for (char& cc : strdata) {
@@ -1115,8 +1115,7 @@
       if (tick % tick_line_len == 0 && tick > 0) {
         cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
                    "  Size: " << int((double(output.size()) / 1024.0) + 1)
-                              << "K" << std::endl
-                              << "    " << std::flush);
+                              << "K\n    " << std::flush);
       }
     }
     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
@@ -1221,9 +1220,7 @@
             timeout != cmCTest::MaxDuration() &&
             timeout > cmDuration::zero()) {
           args.emplace_back("--test-timeout");
-          std::ostringstream msg;
-          msg << cmDurationTo<unsigned int>(timeout);
-          args.push_back(msg.str());
+          args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
         }
         args.emplace_back(i);
       }
@@ -1319,23 +1316,19 @@
       OutputTestErrors(tempOutput);
     }
     *retVal = cmsysProcess_GetExitException(cp);
-    std::string outerr = "\n*** Exception executing: ";
-    outerr += cmsysProcess_GetExceptionString(cp);
+    std::string outerr = cmStrCat("\n*** Exception executing: ",
+                                  cmsysProcess_GetExceptionString(cp));
     if (output) {
       *output += outerr;
     }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               outerr << std::endl
-                      << std::flush);
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
   } else if (result == cmsysProcess_State_Error) {
-    std::string outerr = "\n*** ERROR executing: ";
-    outerr += cmsysProcess_GetErrorString(cp);
+    std::string outerr =
+      cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
     if (output) {
       *output += outerr;
     }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               outerr << std::endl
-                      << std::flush);
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
   }
   cmsysProcess_Delete(cp);
 
@@ -1455,8 +1448,7 @@
     if (labels) {
       xml.StartElement("Labels");
       std::string l = labels;
-      std::vector<std::string> args;
-      cmSystemTools::ExpandListArgument(l, args);
+      std::vector<std::string> args = cmExpandedList(l);
       for (std::string const& i : args) {
         xml.Element("Label", i);
       }
@@ -1488,14 +1480,12 @@
 {
   std::string labelsForSubprojects =
     this->GetCTestConfiguration("LabelsForSubprojects");
-  std::vector<std::string> subprojects;
-  cmSystemTools::ExpandListArgument(labelsForSubprojects, subprojects);
+  std::vector<std::string> subprojects = cmExpandedList(labelsForSubprojects);
 
   // sort the array
   std::sort(subprojects.begin(), subprojects.end());
   // remove duplicates
-  std::vector<std::string>::iterator new_end =
-    std::unique(subprojects.begin(), subprojects.end());
+  auto new_end = std::unique(subprojects.begin(), subprojects.end());
   subprojects.erase(new_end, subprojects.end());
 
   return subprojects;
@@ -1856,7 +1846,7 @@
     }
     i++;
     long repeat = 1;
-    if (!cmSystemTools::StringToLong(args[i].c_str(), &repeat)) {
+    if (!cmStrToLong(args[i], &repeat)) {
       errormsg =
         "'--repeat-until-fail' given non-integer value '" + args[i] + "'";
       return false;
@@ -1870,7 +1860,7 @@
   if (this->CheckArgument(arg, "--test-load") && i < args.size() - 1) {
     i++;
     unsigned long load;
-    if (cmSystemTools::StringToULong(args[i].c_str(), &load)) {
+    if (cmStrToULong(args[i], &load)) {
       this->SetTestLoad(load);
     } else {
       cmCTestLog(this, WARNING,
@@ -1911,9 +1901,15 @@
     this->Impl->Debug = true;
     this->Impl->ShowLineNumbers = true;
   }
+  if (this->CheckArgument(arg, "--group") && i < args.size() - 1) {
+    i++;
+    this->Impl->SpecificGroup = args[i];
+  }
+  // This is an undocumented / deprecated option.
+  // "Track" has been renamed to "Group".
   if (this->CheckArgument(arg, "--track") && i < args.size() - 1) {
     i++;
-    this->Impl->SpecificTrack = args[i];
+    this->Impl->SpecificGroup = args[i];
   }
   if (this->CheckArgument(arg, "--show-line-numbers")) {
     this->Impl->ShowLineNumbers = true;
@@ -1944,7 +1940,7 @@
       i < args.size() - 1) {
     i++;
     long outputSize;
-    if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) {
+    if (cmStrToLong(args[i], &outputSize)) {
       this->Impl->TestHandler.SetTestOutputSizePassed(int(outputSize));
     } else {
       cmCTestLog(this, WARNING,
@@ -1956,7 +1952,7 @@
       i < args.size() - 1) {
     i++;
     long outputSize;
-    if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) {
+    if (cmStrToLong(args[i], &outputSize)) {
       this->Impl->TestHandler.SetTestOutputSizeFailed(int(outputSize));
     } else {
       cmCTestLog(this, WARNING,
@@ -1967,7 +1963,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
@@ -2003,7 +1999,7 @@
   if (this->CheckArgument(arg, "--interactive-debug-mode") &&
       i < args.size() - 1) {
     i++;
-    this->Impl->InteractiveDebugMode = cmSystemTools::IsOn(args[i]);
+    this->Impl->InteractiveDebugMode = cmIsOn(args[i]);
   }
   if (this->CheckArgument(arg, "--submit-index") && i < args.size() - 1) {
     i++;
@@ -2094,6 +2090,15 @@
       "ExcludeFixtureCleanupRegularExpression", args[i].c_str());
   }
 
+  if (this->CheckArgument(arg, "--hardware-spec-file") &&
+      i < args.size() - 1) {
+    i++;
+    this->GetTestHandler()->SetPersistentOption("HardwareSpecFile",
+                                                args[i].c_str());
+    this->GetMemCheckHandler()->SetPersistentOption("HardwareSpecFile",
+                                                    args[i].c_str());
+  }
+
   if (this->CheckArgument(arg, "--rerun-failed")) {
     this->GetTestHandler()->SetPersistentOption("RerunFailed", "true");
     this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true");
@@ -2132,6 +2137,11 @@
   return false;
 #else
   // On UNIX we need a non-dumb tty.
+  std::string clicolor_force;
+  if (cmSystemTools::GetEnv("CLICOLOR_FORCE", clicolor_force) &&
+      !clicolor_force.empty() && clicolor_force != "0") {
+    return true;
+  }
   return ConsoleIsNotDumb();
 #endif
 }
@@ -2229,7 +2239,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);
     }
@@ -2425,7 +2435,7 @@
   cmCTestBuildAndTestHandler* handler = this->GetBuildAndTestHandler();
   int retv = handler->ProcessHandler();
   *output = handler->GetOutput();
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmDynamicLoader::FlushCache();
 #endif
   if (retv != 0) {
@@ -2502,8 +2512,7 @@
              "* Read custom CTest configuration directory: " << dir
                                                              << std::endl);
 
-  std::string fname = dir;
-  fname += "/CTestCustom.cmake";
+  std::string fname = cmStrCat(dir, "/CTestCustom.cmake");
   cmCTestLog(this, DEBUG, "* Check for file: " << fname << std::endl);
   if (cmSystemTools::FileExists(fname)) {
     cmCTestLog(this, DEBUG,
@@ -2523,8 +2532,7 @@
     }
   }
 
-  std::string rexpr = dir;
-  rexpr += "/CTestCustom.ctest";
+  std::string rexpr = cmStrCat(dir, "/CTestCustom.ctest");
   cmCTestLog(this, DEBUG, "* Check for file: " << rexpr << std::endl);
   if (!found && cmSystemTools::FileExists(rexpr)) {
     cmsys::Glob gl;
@@ -2567,7 +2575,7 @@
   cmCTestLog(this, DEBUG, "PopulateCustomVector: " << def << std::endl);
 
   vec.clear();
-  cmSystemTools::ExpandListArgument(dval, vec);
+  cmExpandList(dval, vec);
 
   for (std::string const& it : vec) {
     cmCTestLog(this, DEBUG, "  -- " << it << std::endl);
@@ -2676,8 +2684,7 @@
     std::string site = this->GetCTestConfiguration("DropSite");
     std::string location = this->GetCTestConfiguration("DropLocation");
 
-    url = method.empty() ? "http" : method;
-    url += "://";
+    url = cmStrCat(method.empty() ? "http" : method, "://");
     if (!user.empty()) {
       url += user;
       if (!password.empty()) {
@@ -2772,21 +2779,21 @@
   return this->Impl->InitialCommandLineArguments;
 }
 
-const char* cmCTest::GetSpecificTrack()
+const char* cmCTest::GetSpecificGroup()
 {
-  if (this->Impl->SpecificTrack.empty()) {
+  if (this->Impl->SpecificGroup.empty()) {
     return nullptr;
   }
-  return this->Impl->SpecificTrack.c_str();
+  return this->Impl->SpecificGroup.c_str();
 }
 
-void cmCTest::SetSpecificTrack(const char* track)
+void cmCTest::SetSpecificGroup(const char* group)
 {
-  if (!track) {
-    this->Impl->SpecificTrack.clear();
+  if (!group) {
+    this->Impl->SpecificGroup.clear();
     return;
   }
-  this->Impl->SpecificTrack = track;
+  this->Impl->SpecificGroup = group;
 }
 
 void cmCTest::SetFailover(bool failover)
@@ -3077,11 +3084,11 @@
         } else {
           *this->Impl->OutputLogFile << cmCTestStringLogType[logType];
         }
-        *this->Impl->OutputLogFile << "] " << std::endl << std::flush;
+        *this->Impl->OutputLogFile << "] " << std::endl;
       }
       *this->Impl->OutputLogFile << msg << std::flush;
       if (logType != this->Impl->OutputLogFileLastTag) {
-        *this->Impl->OutputLogFile << std::endl << std::flush;
+        *this->Impl->OutputLogFile << std::endl;
         this->Impl->OutputLogFileLastTag = logType;
       }
     }
@@ -3194,7 +3201,7 @@
   if (!process_output.empty()) {
     test_outputs.append(process_output.data(), process_output.size());
   }
-  cmCTestLog(this, HANDLER_OUTPUT, test_outputs << std::endl << std::flush);
+  cmCTestLog(this, HANDLER_OUTPUT, test_outputs << std::endl);
 }
 
 bool cmCTest::CompressString(std::string& str)
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index d300c33..82a6f4c 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -5,17 +5,17 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmDuration.h"
-#include "cmProcessOutput.h"
-
 #include <chrono>
+#include <ctime>
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <sstream>
 #include <string>
-#include <time.h>
 #include <vector>
 
+#include "cmDuration.h"
+#include "cmProcessOutput.h"
+
 class cmCTestBuildHandler;
 class cmCTestBuildAndTestHandler;
 class cmCTestCoverageHandler;
@@ -41,7 +41,7 @@
 class cmCTest
 {
 public:
-  typedef cmProcessOutput::Encoding Encoding;
+  using Encoding = cmProcessOutput::Encoding;
   /** Enumerate parts of the testing and submission process.  */
   enum Part
   {
@@ -404,9 +404,9 @@
 
   std::vector<std::string>& GetInitialCommandLineArguments();
 
-  /** Set the track to submit to */
-  void SetSpecificTrack(const char* track);
-  const char* GetSpecificTrack();
+  /** Set the group to submit to */
+  void SetSpecificGroup(const char* group);
+  const char* GetSpecificGroup();
 
   void SetFailover(bool failover);
   bool GetFailover() const;
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 358f095..d627465 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -2,18 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCacheManager.h"
 
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+#include <string>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include <algorithm>
-#include <sstream>
-#include <stdio.h>
-#include <string.h>
-#include <string>
 
 #include "cmGeneratedFileStream.h"
 #include "cmMessageType.h"
 #include "cmMessenger.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
@@ -25,9 +27,7 @@
 
 void cmCacheManager::CleanCMakeFiles(const std::string& path)
 {
-  std::string glob = path;
-  glob += "/CMakeFiles";
-  glob += "/*.cmake";
+  std::string glob = cmStrCat(path, "/CMakeFiles/*.cmake");
   cmsys::Glob globIt;
   globIt.FindFiles(glob);
   std::vector<std::string> files = globIt.GetFiles();
@@ -38,8 +38,7 @@
                                std::set<std::string>& excludes,
                                std::set<std::string>& includes)
 {
-  std::string cacheFile = path;
-  cacheFile += "/CMakeCache.txt";
+  std::string cacheFile = cmStrCat(path, "/CMakeCache.txt");
   // clear the old cache, if we are reading in internal values
   if (internal) {
     this->Cache.clear();
@@ -104,12 +103,10 @@
           // not visible in the gui
           if (!internal) {
             e.Type = cmStateEnums::INTERNAL;
-            helpString = "DO NOT EDIT, ";
-            helpString += entryKey;
-            helpString += " loaded from external file.  "
-                          "To change this value edit this file: ";
-            helpString += path;
-            helpString += "/CMakeCache.txt";
+            helpString = cmStrCat("DO NOT EDIT, ", entryKey,
+                                  " loaded from external file.  "
+                                  "To change this value edit this file: ",
+                                  path, "/CMakeCache.txt");
             e.SetProperty("HELPSTRING", helpString.c_str());
           }
           if (!this->ReadPropertyEntry(entryKey, e)) {
@@ -214,14 +211,11 @@
 {
   for (const char** p = cmCacheManager::PersistentProperties; *p; ++p) {
     if (const char* value = i.GetProperty(*p)) {
-      std::string helpstring = *p;
-      helpstring += " property for variable: ";
-      helpstring += i.GetName();
+      std::string helpstring =
+        cmStrCat(*p, " property for variable: ", i.GetName());
       cmCacheManager::OutputHelpString(os, helpstring);
 
-      std::string key = i.GetName();
-      key += "-";
-      key += *p;
+      std::string key = cmStrCat(i.GetName(), '-', *p);
       cmCacheManager::OutputKey(os, key);
       os << ":INTERNAL=";
       cmCacheManager::OutputValue(os, value);
@@ -234,8 +228,7 @@
 
 bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger)
 {
-  std::string cacheFile = path;
-  cacheFile += "/CMakeCache.txt";
+  std::string cacheFile = cmStrCat(path, "/CMakeCache.txt");
   cmGeneratedFileStream fout(cacheFile);
   fout.SetCopyIfDifferent(true);
   if (!fout) {
@@ -356,8 +349,7 @@
   }
   fout << "\n";
   fout.Close();
-  std::string checkCacheFile = path;
-  checkCacheFile += "/CMakeFiles";
+  std::string checkCacheFile = cmStrCat(path, "/CMakeFiles");
   cmSystemTools::MakeDirectory(checkCacheFile);
   checkCacheFile += "/cmake.check_cache";
   cmsys::ofstream checkCache(checkCacheFile.c_str());
@@ -473,15 +465,14 @@
 {
   if (value.find('\n') != std::string::npos) {
     if (messenger) {
-      std::string message = "Value of ";
-      message += key;
-      message += " contained a newline; truncating";
+      std::string message =
+        cmStrCat("Value of ", key, " contained a newline; truncating");
       messenger->IssueMessage(MessageType::WARNING, message);
     }
 
-    std::string comment = "WARNING: Value of ";
-    comment += key;
-    comment += " contained a newline and was truncated. Original value:";
+    std::string comment =
+      cmStrCat("WARNING: Value of ", key,
+               " contained a newline and was truncated. Original value:");
 
     OutputWarningComment(fout, comment, true);
     OutputWarningComment(fout, value, false);
@@ -490,7 +481,7 @@
 
 void cmCacheManager::RemoveCacheEntry(const std::string& key)
 {
-  CacheEntryMap::iterator i = this->Cache.find(key);
+  auto i = this->Cache.find(key);
   if (i != this->Cache.end()) {
     this->Cache.erase(i);
   }
@@ -499,7 +490,7 @@
 cmCacheManager::CacheEntry* cmCacheManager::GetCacheEntry(
   const std::string& key)
 {
-  CacheEntryMap::iterator i = this->Cache.find(key);
+  auto i = this->Cache.find(key);
   if (i != this->Cache.end()) {
     return &i->second;
   }
@@ -508,13 +499,13 @@
 
 cmCacheManager::CacheIterator cmCacheManager::GetCacheIterator(const char* key)
 {
-  return CacheIterator(*this, key);
+  return { *this, key };
 }
 
 const std::string* cmCacheManager::GetInitializedCacheValue(
   const std::string& key) const
 {
-  CacheEntryMap::const_iterator i = this->Cache.find(key);
+  auto i = this->Cache.find(key);
   if (i != this->Cache.end() && i->second.Initialized) {
     return &i->second.Value;
   }
@@ -551,8 +542,7 @@
   // make sure we only use unix style paths
   if (type == cmStateEnums::FILEPATH || type == cmStateEnums::PATH) {
     if (e.Value.find(';') != std::string::npos) {
-      std::vector<std::string> paths;
-      cmSystemTools::ExpandListArgument(e.Value, paths);
+      std::vector<std::string> paths = cmExpandedList(e.Value);
       const char* sep = "";
       e.Value = "";
       for (std::string& i : paths) {
@@ -615,12 +605,12 @@
 
 bool cmCacheManager::CacheIterator::GetValueAsBool() const
 {
-  return cmSystemTools::IsOn(this->GetEntry().Value);
+  return cmIsOn(this->GetEntry().Value);
 }
 
 std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const
 {
-  return this->Properties.GetPropertyList();
+  return this->Properties.GetKeys();
 }
 
 const char* cmCacheManager::CacheEntry::GetProperty(
@@ -695,7 +685,7 @@
   const std::string& prop) const
 {
   if (const char* value = this->GetProperty(prop)) {
-    return cmSystemTools::IsOn(value);
+    return cmIsOn(value);
   }
   return false;
 }
diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h
index 65f22f7..faa60c5 100644
--- a/Source/cmCacheManager.h
+++ b/Source/cmCacheManager.h
@@ -94,7 +94,7 @@
   };
 
   //! return an iterator to iterate through the cache map
-  cmCacheManager::CacheIterator NewIterator() { return CacheIterator(*this); }
+  cmCacheManager::CacheIterator NewIterator() { return { *this }; }
 
   //! Load a cache for given makefile.  Loads from path/CMakeCache.txt.
   bool LoadCache(const std::string& path, bool internal,
@@ -212,7 +212,7 @@
   unsigned int CacheMinorVersion;
 
 private:
-  typedef std::map<std::string, CacheEntry> CacheEntryMap;
+  using CacheEntryMap = std::map<std::string, CacheEntry>;
   static void OutputHelpString(std::ostream& fout,
                                const std::string& helpString);
   static void OutputWarningComment(std::ostream& fout,
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/cmCheckCustomOutputs.cxx b/Source/cmCheckCustomOutputs.cxx
new file mode 100644
index 0000000..7645c88
--- /dev/null
+++ b/Source/cmCheckCustomOutputs.cxx
@@ -0,0 +1,36 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCheckCustomOutputs.h"
+
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+bool cmCheckCustomOutputs(const std::vector<std::string>& outputs,
+                          cm::string_view keyword, cmExecutionStatus& status)
+{
+  cmMakefile& mf = status.GetMakefile();
+
+  for (std::string const& o : outputs) {
+    // Make sure the file will not be generated into the source
+    // directory during an out of source build.
+    if (!mf.CanIWriteThisFile(o)) {
+      status.SetError(
+        cmStrCat("attempted to have a file\n  ", o,
+                 "\nin a source directory as an output of custom command."));
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
+
+    // Make sure the output file name has no invalid characters.
+    std::string::size_type pos = o.find_first_of("#<>");
+    if (pos != std::string::npos) {
+      status.SetError(cmStrCat("called with ", keyword, " containing a \"",
+                               o[pos], "\".  This character is not allowed."));
+      return false;
+    }
+  }
+
+  return true;
+}
diff --git a/Source/cmCheckCustomOutputs.h b/Source/cmCheckCustomOutputs.h
new file mode 100644
index 0000000..9f33d16
--- /dev/null
+++ b/Source/cmCheckCustomOutputs.h
@@ -0,0 +1,18 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCheckCustomOutputs_h
+#define cmCheckCustomOutputs_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include <cm/string_view>
+
+class cmExecutionStatus;
+
+bool cmCheckCustomOutputs(const std::vector<std::string>& outputs,
+                          cm::string_view keyword, cmExecutionStatus& status);
+
+#endif
diff --git a/Source/cmCommand.cxx b/Source/cmCommand.cxx
index d349c91..0c2734e 100644
--- a/Source/cmCommand.cxx
+++ b/Source/cmCommand.cxx
@@ -2,11 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCommand.h"
 
+#include <utility>
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
 struct cmListFileArgument;
 
+void cmCommand::SetExecutionStatus(cmExecutionStatus* status)
+{
+  this->Status = status;
+  this->Makefile = &status->GetMakefile();
+}
+
 bool cmCommand::InvokeInitialPass(const std::vector<cmListFileArgument>& args,
                                   cmExecutionStatus& status)
 {
@@ -19,15 +27,33 @@
   return this->InitialPass(expandedArguments, status);
 }
 
-const char* cmCommand::GetError()
-{
-  if (this->Error.empty()) {
-    return "unknown error.";
-  }
-  return this->Error.c_str();
-}
-
 void cmCommand::SetError(const std::string& e)
 {
-  this->Error = e;
+  this->Status->SetError(e);
+}
+
+cmLegacyCommandWrapper::cmLegacyCommandWrapper(std::unique_ptr<cmCommand> cmd)
+  : Command(std::move(cmd))
+{
+}
+
+cmLegacyCommandWrapper::cmLegacyCommandWrapper(
+  cmLegacyCommandWrapper const& other)
+  : Command(other.Command->Clone())
+{
+}
+
+cmLegacyCommandWrapper& cmLegacyCommandWrapper::operator=(
+  cmLegacyCommandWrapper const& other)
+{
+  this->Command = other.Command->Clone();
+  return *this;
+}
+
+bool cmLegacyCommandWrapper::operator()(
+  std::vector<cmListFileArgument> const& args, cmExecutionStatus& status) const
+{
+  auto cmd = this->Command->Clone();
+  cmd->SetExecutionStatus(&status);
+  return cmd->InvokeInitialPass(args, status);
 }
diff --git a/Source/cmCommand.h b/Source/cmCommand.h
index 9ccd773..bcb178d 100644
--- a/Source/cmCommand.h
+++ b/Source/cmCommand.h
@@ -5,6 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -41,16 +42,18 @@
   /**
    * Specify the makefile.
    */
-  void SetMakefile(cmMakefile* m) { this->Makefile = m; }
   cmMakefile* GetMakefile() { return this->Makefile; }
 
+  void SetExecutionStatus(cmExecutionStatus* s);
+  cmExecutionStatus* GetExecutionStatus() { return this->Status; };
+
   /**
    * This is called by the cmMakefile when the command is first
    * encountered in the CMakeLists.txt file.  It expands the command's
    * arguments and then invokes the InitialPass.
    */
-  virtual bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
-                                 cmExecutionStatus& status);
+  bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
+                         cmExecutionStatus& status);
 
   /**
    * This is called when the command is first encountered in
@@ -60,27 +63,9 @@
                            cmExecutionStatus&) = 0;
 
   /**
-   * This is called at the end after all the information
-   * specified by the command is accumulated. Most commands do
-   * not implement this method.  At this point, reading and
-   * writing to the cache can be done.
-   */
-  virtual void FinalPass() {}
-
-  /**
-   * Does this command have a final pass?  Query after InitialPass.
-   */
-  virtual bool HasFinalPass() const { return false; }
-
-  /**
    * This is a virtual constructor for the command.
    */
-  virtual cmCommand* Clone() = 0;
-
-  /**
-   * Return the last error string.
-   */
-  const char* GetError();
+  virtual std::unique_ptr<cmCommand> Clone() = 0;
 
   /**
    * Set the error message
@@ -91,7 +76,25 @@
   cmMakefile* Makefile = nullptr;
 
 private:
-  std::string Error;
+  cmExecutionStatus* Status = nullptr;
+};
+
+class cmLegacyCommandWrapper
+{
+public:
+  explicit cmLegacyCommandWrapper(std::unique_ptr<cmCommand> cmd);
+
+  cmLegacyCommandWrapper(cmLegacyCommandWrapper const& other);
+  cmLegacyCommandWrapper& operator=(cmLegacyCommandWrapper const& other);
+
+  cmLegacyCommandWrapper(cmLegacyCommandWrapper&&) = default;
+  cmLegacyCommandWrapper& operator=(cmLegacyCommandWrapper&&) = default;
+
+  bool operator()(std::vector<cmListFileArgument> const& args,
+                  cmExecutionStatus& status) const;
+
+private:
+  std::unique_ptr<cmCommand> Command;
 };
 
 #endif
diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx
index ca29967..613ae06 100644
--- a/Source/cmCommandArgumentParserHelper.cxx
+++ b/Source/cmCommandArgumentParserHelper.cxx
@@ -2,15 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCommandArgumentParserHelper.h"
 
+#include <cstring>
+#include <iostream>
+#include <sstream>
+
 #include "cmCommandArgumentLexer.h"
 #include "cmMakefile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <iostream>
-#include <sstream>
-#include <string.h>
-
 int cmCommandArgument_yyparse(yyscan_t yyscanner);
 //
 cmCommandArgumentParserHelper::cmCommandArgumentParserHelper()
@@ -58,7 +59,7 @@
     std::string str;
     if (cmSystemTools::GetEnv(var, str)) {
       if (this->EscapeQuotes) {
-        return this->AddString(cmSystemTools::EscapeQuotes(str));
+        return this->AddString(cmEscapeQuotes(str));
       }
       return this->AddString(str);
     }
@@ -68,7 +69,7 @@
     if (const std::string* c =
           this->Makefile->GetState()->GetInitializedCacheValue(var)) {
       if (this->EscapeQuotes) {
-        return this->AddString(cmSystemTools::EscapeQuotes(*c));
+        return this->AddString(cmEscapeQuotes(*c));
       }
       return this->AddString(*c);
     }
@@ -87,9 +88,7 @@
     return nullptr;
   }
   if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) {
-    std::ostringstream ostr;
-    ostr << this->FileLine;
-    return this->AddString(ostr.str());
+    return this->AddString(std::to_string(this->FileLine));
   }
   const char* value = this->Makefile->GetDefinition(var);
   if (!value) {
@@ -99,7 +98,7 @@
     }
   }
   if (this->EscapeQuotes && value) {
-    return this->AddString(cmSystemTools::EscapeQuotes(value));
+    return this->AddString(cmEscapeQuotes(value));
   }
   return this->AddString(value ? value : "");
 }
@@ -123,9 +122,7 @@
   // - this->ReplaceAtSyntax is false
   // - this->ReplaceAtSyntax is true, but this->RemoveEmpty is false,
   //   and the variable was not defined
-  std::string ref = "@";
-  ref += var;
-  ref += "@";
+  std::string ref = cmStrCat('@', var, '@');
   return this->AddString(ref);
 }
 
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index 63c5397..ff73b27 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -1,8 +1,9 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+
 #include "cmCommands.h"
-#include "cmPolicies.h"
-#include "cmState.h"
+
+#include <cm/memory>
 
 #include "cmAddCompileDefinitionsCommand.h"
 #include "cmAddCustomCommandCommand.h"
@@ -17,6 +18,7 @@
 #include "cmBuildCommand.h"
 #include "cmCMakeMinimumRequired.h"
 #include "cmCMakePolicyCommand.h"
+#include "cmCommand.h"
 #include "cmConfigureFileCommand.h"
 #include "cmContinueCommand.h"
 #include "cmCreateTestSourceList.h"
@@ -57,6 +59,7 @@
 #include "cmMessageCommand.h"
 #include "cmOptionCommand.h"
 #include "cmParseArgumentsCommand.h"
+#include "cmPolicies.h"
 #include "cmProjectCommand.h"
 #include "cmReturnCommand.h"
 #include "cmSeparateArgumentsCommand.h"
@@ -67,6 +70,7 @@
 #include "cmSetTargetPropertiesCommand.h"
 #include "cmSetTestsPropertiesCommand.h"
 #include "cmSiteNameCommand.h"
+#include "cmState.h"
 #include "cmStringCommand.h"
 #include "cmSubdirCommand.h"
 #include "cmTargetCompileDefinitionsCommand.h"
@@ -74,13 +78,14 @@
 #include "cmTargetCompileOptionsCommand.h"
 #include "cmTargetIncludeDirectoriesCommand.h"
 #include "cmTargetLinkLibrariesCommand.h"
+#include "cmTargetPrecompileHeadersCommand.h"
 #include "cmTargetSourcesCommand.h"
 #include "cmTryCompileCommand.h"
 #include "cmTryRunCommand.h"
 #include "cmUnsetCommand.h"
 #include "cmWhileCommand.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmAddCompileOptionsCommand.h"
 #  include "cmAddLinkOptionsCommand.h"
 #  include "cmAuxSourceDirectoryCommand.h"
@@ -112,52 +117,48 @@
 
 void GetScriptingCommands(cmState* state)
 {
-  state->AddBuiltinCommand("break", new cmBreakCommand);
-  state->AddBuiltinCommand("cmake_minimum_required",
-                           new cmCMakeMinimumRequired);
-  state->AddBuiltinCommand("cmake_policy", new cmCMakePolicyCommand);
-  state->AddBuiltinCommand("configure_file", new cmConfigureFileCommand);
-  state->AddBuiltinCommand("continue", new cmContinueCommand);
-  state->AddBuiltinCommand("exec_program", new cmExecProgramCommand);
-  state->AddBuiltinCommand("execute_process", new cmExecuteProcessCommand);
-  state->AddBuiltinCommand("file", new cmFileCommand);
-  state->AddBuiltinCommand("find_file", new cmFindFileCommand);
-  state->AddBuiltinCommand("find_library", new cmFindLibraryCommand);
-  state->AddBuiltinCommand("find_package", new cmFindPackageCommand);
-  state->AddBuiltinCommand("find_path", new cmFindPathCommand);
-  state->AddBuiltinCommand("find_program", new cmFindProgramCommand);
-  state->AddBuiltinCommand("foreach", new cmForEachCommand);
-  state->AddBuiltinCommand("function", new cmFunctionCommand);
-  state->AddBuiltinCommand("get_cmake_property",
-                           new cmGetCMakePropertyCommand);
+  state->AddBuiltinCommand("break", cmBreakCommand);
+  state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired);
+  state->AddBuiltinCommand("cmake_policy", cmCMakePolicyCommand);
+  state->AddBuiltinCommand("configure_file", cmConfigureFileCommand);
+  state->AddBuiltinCommand("continue", cmContinueCommand);
+  state->AddBuiltinCommand("exec_program", cmExecProgramCommand);
+  state->AddBuiltinCommand("execute_process", cmExecuteProcessCommand);
+  state->AddBuiltinCommand("file", cmFileCommand);
+  state->AddBuiltinCommand("find_file", cmFindFile);
+  state->AddBuiltinCommand("find_library", cmFindLibrary);
+  state->AddBuiltinCommand("find_package", cmFindPackage);
+  state->AddBuiltinCommand("find_path", cmFindPath);
+  state->AddBuiltinCommand("find_program", cmFindProgram);
+  state->AddBuiltinCommand("foreach", cmForEachCommand);
+  state->AddBuiltinCommand("function", cmFunctionCommand);
+  state->AddBuiltinCommand("get_cmake_property", cmGetCMakePropertyCommand);
   state->AddBuiltinCommand("get_directory_property",
-                           new cmGetDirectoryPropertyCommand);
+                           cmGetDirectoryPropertyCommand);
   state->AddBuiltinCommand("get_filename_component",
-                           new cmGetFilenameComponentCommand);
-  state->AddBuiltinCommand("get_property", new cmGetPropertyCommand);
-  state->AddBuiltinCommand("if", new cmIfCommand);
-  state->AddBuiltinCommand("include", new cmIncludeCommand);
-  state->AddBuiltinCommand("include_guard", new cmIncludeGuardCommand);
-  state->AddBuiltinCommand("list", new cmListCommand);
-  state->AddBuiltinCommand("macro", new cmMacroCommand);
-  state->AddBuiltinCommand("make_directory", new cmMakeDirectoryCommand);
-  state->AddBuiltinCommand("mark_as_advanced", new cmMarkAsAdvancedCommand);
-  state->AddBuiltinCommand("math", new cmMathCommand);
-  state->AddBuiltinCommand("message", new cmMessageCommand);
-  state->AddBuiltinCommand("option", new cmOptionCommand);
-  state->AddBuiltinCommand("cmake_parse_arguments",
-                           new cmParseArgumentsCommand);
-  state->AddBuiltinCommand("return", new cmReturnCommand);
-  state->AddBuiltinCommand("separate_arguments",
-                           new cmSeparateArgumentsCommand);
-  state->AddBuiltinCommand("set", new cmSetCommand);
+                           cmGetFilenameComponentCommand);
+  state->AddBuiltinCommand("get_property", cmGetPropertyCommand);
+  state->AddBuiltinCommand("if", cmIfCommand);
+  state->AddBuiltinCommand("include", cmIncludeCommand);
+  state->AddBuiltinCommand("include_guard", cmIncludeGuardCommand);
+  state->AddBuiltinCommand("list", cmListCommand);
+  state->AddBuiltinCommand("macro", cmMacroCommand);
+  state->AddBuiltinCommand("make_directory", cmMakeDirectoryCommand);
+  state->AddBuiltinCommand("mark_as_advanced", cmMarkAsAdvancedCommand);
+  state->AddBuiltinCommand("math", cmMathCommand);
+  state->AddBuiltinCommand("message", cmMessageCommand);
+  state->AddBuiltinCommand("option", cmOptionCommand);
+  state->AddBuiltinCommand("cmake_parse_arguments", cmParseArgumentsCommand);
+  state->AddBuiltinCommand("return", cmReturnCommand);
+  state->AddBuiltinCommand("separate_arguments", cmSeparateArgumentsCommand);
+  state->AddBuiltinCommand("set", cmSetCommand);
   state->AddBuiltinCommand("set_directory_properties",
-                           new cmSetDirectoryPropertiesCommand);
-  state->AddBuiltinCommand("set_property", new cmSetPropertyCommand);
-  state->AddBuiltinCommand("site_name", new cmSiteNameCommand);
-  state->AddBuiltinCommand("string", new cmStringCommand);
-  state->AddBuiltinCommand("unset", new cmUnsetCommand);
-  state->AddBuiltinCommand("while", new cmWhileCommand);
+                           cmSetDirectoryPropertiesCommand);
+  state->AddBuiltinCommand("set_property", cmSetPropertyCommand);
+  state->AddBuiltinCommand("site_name", cmSiteNameCommand);
+  state->AddBuiltinCommand("string", cmStringCommand);
+  state->AddBuiltinCommand("unset", cmUnsetCommand);
+  state->AddBuiltinCommand("while", cmWhileCommand);
 
   state->AddUnexpectedCommand(
     "else",
@@ -194,18 +195,18 @@
     "WHILE ENDWHILE structure. Or its arguments did not "
     "match the opening WHILE command.");
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   state->AddBuiltinCommand("cmake_host_system_information",
-                           new cmCMakeHostSystemInformationCommand);
-  state->AddBuiltinCommand("remove", new cmRemoveCommand);
-  state->AddBuiltinCommand("variable_watch", new cmVariableWatchCommand);
-  state->AddBuiltinCommand("write_file", new cmWriteFileCommand);
+                           cmCMakeHostSystemInformationCommand);
+  state->AddBuiltinCommand("remove", cmRemoveCommand);
+  state->AddBuiltinCommand("variable_watch", cmVariableWatchCommand);
+  state->AddBuiltinCommand("write_file", cmWriteFileCommand);
 
   state->AddDisallowedCommand(
-    "build_name", new cmBuildNameCommand, cmPolicies::CMP0036,
+    "build_name", cmBuildNameCommand, cmPolicies::CMP0036,
     "The build_name command should not be called; see CMP0036.");
   state->AddDisallowedCommand(
-    "use_mangled_mesa", new cmUseMangledMesaCommand, cmPolicies::CMP0030,
+    "use_mangled_mesa", cmUseMangledMesaCommand, cmPolicies::CMP0030,
     "The use_mangled_mesa command should not be called; see CMP0030.");
 
 #endif
@@ -213,101 +214,96 @@
 
 void GetProjectCommands(cmState* state)
 {
-  state->AddBuiltinCommand("add_custom_command",
-                           new cmAddCustomCommandCommand);
-  state->AddBuiltinCommand("add_custom_target", new cmAddCustomTargetCommand);
-  state->AddBuiltinCommand("add_definitions", new cmAddDefinitionsCommand);
-  state->AddBuiltinCommand("add_dependencies", new cmAddDependenciesCommand);
-  state->AddBuiltinCommand("add_executable", new cmAddExecutableCommand);
-  state->AddBuiltinCommand("add_library", new cmAddLibraryCommand);
-  state->AddBuiltinCommand("add_subdirectory", new cmAddSubDirectoryCommand);
-  state->AddBuiltinCommand("add_test", new cmAddTestCommand);
-  state->AddBuiltinCommand("build_command", new cmBuildCommand);
-  state->AddBuiltinCommand("create_test_sourcelist",
-                           new cmCreateTestSourceList);
-  state->AddBuiltinCommand("define_property", new cmDefinePropertyCommand);
-  state->AddBuiltinCommand("enable_language", new cmEnableLanguageCommand);
-  state->AddBuiltinCommand("enable_testing", new cmEnableTestingCommand);
+  state->AddBuiltinCommand("add_custom_command", cmAddCustomCommandCommand);
+  state->AddBuiltinCommand("add_custom_target", cmAddCustomTargetCommand);
+  state->AddBuiltinCommand("add_definitions", cmAddDefinitionsCommand);
+  state->AddBuiltinCommand("add_dependencies", cmAddDependenciesCommand);
+  state->AddBuiltinCommand("add_executable", cmAddExecutableCommand);
+  state->AddBuiltinCommand("add_library", cmAddLibraryCommand);
+  state->AddBuiltinCommand("add_subdirectory", cmAddSubDirectoryCommand);
+  state->AddBuiltinCommand("add_test", cmAddTestCommand);
+  state->AddBuiltinCommand("build_command", cmBuildCommand);
+  state->AddBuiltinCommand("create_test_sourcelist", cmCreateTestSourceList);
+  state->AddBuiltinCommand("define_property", cmDefinePropertyCommand);
+  state->AddBuiltinCommand("enable_language", cmEnableLanguageCommand);
+  state->AddBuiltinCommand("enable_testing", cmEnableTestingCommand);
   state->AddBuiltinCommand("get_source_file_property",
-                           new cmGetSourceFilePropertyCommand);
-  state->AddBuiltinCommand("get_target_property",
-                           new cmGetTargetPropertyCommand);
-  state->AddBuiltinCommand("get_test_property", new cmGetTestPropertyCommand);
-  state->AddBuiltinCommand("include_directories",
-                           new cmIncludeDirectoryCommand);
+                           cmGetSourceFilePropertyCommand);
+  state->AddBuiltinCommand("get_target_property", cmGetTargetPropertyCommand);
+  state->AddBuiltinCommand("get_test_property", cmGetTestPropertyCommand);
+  state->AddBuiltinCommand("include_directories", cmIncludeDirectoryCommand);
   state->AddBuiltinCommand("include_regular_expression",
-                           new cmIncludeRegularExpressionCommand);
-  state->AddBuiltinCommand("install", new cmInstallCommand);
-  state->AddBuiltinCommand("install_files", new cmInstallFilesCommand);
-  state->AddBuiltinCommand("install_targets", new cmInstallTargetsCommand);
-  state->AddBuiltinCommand("link_directories", new cmLinkDirectoriesCommand);
-  state->AddBuiltinCommand("project", new cmProjectCommand);
+                           cmIncludeRegularExpressionCommand);
+  state->AddBuiltinCommand("install", cmInstallCommand);
+  state->AddBuiltinCommand("install_files", cmInstallFilesCommand);
+  state->AddBuiltinCommand("install_targets", cmInstallTargetsCommand);
+  state->AddBuiltinCommand("link_directories", cmLinkDirectoriesCommand);
+  state->AddBuiltinCommand("project", cmProjectCommand);
   state->AddBuiltinCommand("set_source_files_properties",
-                           new cmSetSourceFilesPropertiesCommand);
+                           cmSetSourceFilesPropertiesCommand);
   state->AddBuiltinCommand("set_target_properties",
-                           new cmSetTargetPropertiesCommand);
+                           cmSetTargetPropertiesCommand);
   state->AddBuiltinCommand("set_tests_properties",
-                           new cmSetTestsPropertiesCommand);
-  state->AddBuiltinCommand("subdirs", new cmSubdirCommand);
+                           cmSetTestsPropertiesCommand);
+  state->AddBuiltinCommand("subdirs", cmSubdirCommand);
   state->AddBuiltinCommand("target_compile_definitions",
-                           new cmTargetCompileDefinitionsCommand);
+                           cmTargetCompileDefinitionsCommand);
   state->AddBuiltinCommand("target_compile_features",
-                           new cmTargetCompileFeaturesCommand);
+                           cmTargetCompileFeaturesCommand);
   state->AddBuiltinCommand("target_compile_options",
-                           new cmTargetCompileOptionsCommand);
+                           cmTargetCompileOptionsCommand);
   state->AddBuiltinCommand("target_include_directories",
-                           new cmTargetIncludeDirectoriesCommand);
+                           cmTargetIncludeDirectoriesCommand);
   state->AddBuiltinCommand("target_link_libraries",
-                           new cmTargetLinkLibrariesCommand);
-  state->AddBuiltinCommand("target_sources", new cmTargetSourcesCommand);
-  state->AddBuiltinCommand("try_compile", new cmTryCompileCommand);
-  state->AddBuiltinCommand("try_run", new cmTryRunCommand);
+                           cmTargetLinkLibrariesCommand);
+  state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand);
+  state->AddBuiltinCommand("try_compile",
+                           cm::make_unique<cmTryCompileCommand>());
+  state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>());
+  state->AddBuiltinCommand("target_precompile_headers",
+                           cmTargetPrecompileHeadersCommand);
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   state->AddBuiltinCommand("add_compile_definitions",
-                           new cmAddCompileDefinitionsCommand);
-  state->AddBuiltinCommand("add_compile_options",
-                           new cmAddCompileOptionsCommand);
+                           cmAddCompileDefinitionsCommand);
+  state->AddBuiltinCommand("add_compile_options", cmAddCompileOptionsCommand);
   state->AddBuiltinCommand("aux_source_directory",
-                           new cmAuxSourceDirectoryCommand);
-  state->AddBuiltinCommand("export", new cmExportCommand);
-  state->AddBuiltinCommand("fltk_wrap_ui", new cmFLTKWrapUICommand);
+                           cmAuxSourceDirectoryCommand);
+  state->AddBuiltinCommand("export", cmExportCommand);
+  state->AddBuiltinCommand("fltk_wrap_ui", cmFLTKWrapUICommand);
   state->AddBuiltinCommand("include_external_msproject",
-                           new cmIncludeExternalMSProjectCommand);
-  state->AddBuiltinCommand("install_programs", new cmInstallProgramsCommand);
-  state->AddBuiltinCommand("add_link_options", new cmAddLinkOptionsCommand);
-  state->AddBuiltinCommand("link_libraries", new cmLinkLibrariesCommand);
-  state->AddBuiltinCommand("target_link_options",
-                           new cmTargetLinkOptionsCommand);
+                           cmIncludeExternalMSProjectCommand);
+  state->AddBuiltinCommand("install_programs", cmInstallProgramsCommand);
+  state->AddBuiltinCommand("add_link_options", cmAddLinkOptionsCommand);
+  state->AddBuiltinCommand("link_libraries", cmLinkLibrariesCommand);
+  state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand);
   state->AddBuiltinCommand("target_link_directories",
-                           new cmTargetLinkDirectoriesCommand);
-  state->AddBuiltinCommand("load_cache", new cmLoadCacheCommand);
-  state->AddBuiltinCommand("qt_wrap_cpp", new cmQTWrapCPPCommand);
-  state->AddBuiltinCommand("qt_wrap_ui", new cmQTWrapUICommand);
-  state->AddBuiltinCommand("remove_definitions",
-                           new cmRemoveDefinitionsCommand);
-  state->AddBuiltinCommand("source_group", new cmSourceGroupCommand);
+                           cmTargetLinkDirectoriesCommand);
+  state->AddBuiltinCommand("load_cache", cmLoadCacheCommand);
+  state->AddBuiltinCommand("qt_wrap_cpp", cmQTWrapCPPCommand);
+  state->AddBuiltinCommand("qt_wrap_ui", cmQTWrapUICommand);
+  state->AddBuiltinCommand("remove_definitions", cmRemoveDefinitionsCommand);
+  state->AddBuiltinCommand("source_group", cmSourceGroupCommand);
 
   state->AddDisallowedCommand(
-    "export_library_dependencies", new cmExportLibraryDependenciesCommand,
+    "export_library_dependencies", cmExportLibraryDependenciesCommand,
     cmPolicies::CMP0033,
     "The export_library_dependencies command should not be called; "
     "see CMP0033.");
   state->AddDisallowedCommand(
-    "load_command", new cmLoadCommandCommand, cmPolicies::CMP0031,
+    "load_command", cmLoadCommandCommand, cmPolicies::CMP0031,
     "The load_command command should not be called; see CMP0031.");
   state->AddDisallowedCommand(
-    "output_required_files", new cmOutputRequiredFilesCommand,
-    cmPolicies::CMP0032,
+    "output_required_files", cmOutputRequiredFilesCommand, cmPolicies::CMP0032,
     "The output_required_files command should not be called; see CMP0032.");
   state->AddDisallowedCommand(
-    "subdir_depends", new cmSubdirDependsCommand, cmPolicies::CMP0029,
+    "subdir_depends", cmSubdirDependsCommand, cmPolicies::CMP0029,
     "The subdir_depends command should not be called; see CMP0029.");
   state->AddDisallowedCommand(
-    "utility_source", new cmUtilitySourceCommand, cmPolicies::CMP0034,
+    "utility_source", cmUtilitySourceCommand, cmPolicies::CMP0034,
     "The utility_source command should not be called; see CMP0034.");
   state->AddDisallowedCommand(
-    "variable_requires", new cmVariableRequiresCommand, cmPolicies::CMP0035,
+    "variable_requires", cmVariableRequiresCommand, cmPolicies::CMP0035,
     "The variable_requires command should not be called; see CMP0035.");
 #endif
 }
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 66250f3..19a096b 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)
@@ -59,10 +59,11 @@
 
   // Append the flag and value.  Use ConvertToLinkReference to help
   // vs6's "cl -link" pass it to the linker.
-  std::string flag = defFileFlag;
-  flag += this->LocalCommonGenerator->ConvertToOutputFormat(
-    linkLineComputer->ConvertToLinkReference(mdi->DefFile),
-    cmOutputConverter::SHELL);
+  std::string flag =
+    cmStrCat(defFileFlag,
+             this->LocalCommonGenerator->ConvertToOutputFormat(
+               linkLineComputer->ConvertToLinkReference(mdi->DefFile),
+               cmOutputConverter::SHELL));
   this->LocalCommonGenerator->AppendFlags(flags, flag);
 }
 
@@ -89,13 +90,13 @@
   }
   if (var) {
     this->LocalCommonGenerator->AppendFlags(
-      flags, this->Makefile->GetDefinition(var));
+      flags, this->Makefile->GetSafeDefinition(var));
   }
 }
 
 std::string cmCommonTargetGenerator::GetFlags(const std::string& l)
 {
-  ByLanguageMap::iterator i = this->FlagsByLanguage.find(l);
+  auto i = this->FlagsByLanguage.find(l);
   if (i == this->FlagsByLanguage.end()) {
     std::string flags;
 
@@ -110,7 +111,7 @@
 
 std::string cmCommonTargetGenerator::GetDefines(const std::string& l)
 {
-  ByLanguageMap::iterator i = this->DefinesByLanguage.find(l);
+  auto i = this->DefinesByLanguage.find(l);
   if (i == this->DefinesByLanguage.end()) {
     std::set<std::string> defines;
     this->LocalCommonGenerator->GetTargetDefines(this->GeneratorTarget,
@@ -127,7 +128,7 @@
 
 std::string cmCommonTargetGenerator::GetIncludes(std::string const& l)
 {
-  ByLanguageMap::iterator i = this->IncludesByLanguage.find(l);
+  auto i = this->IncludesByLanguage.find(l);
   if (i == this->IncludesByLanguage.end()) {
     std::string includes;
     this->AddIncludeFlags(includes, l);
@@ -155,9 +156,8 @@
           && linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
           emitted.insert(linkee).second) {
         cmLocalGenerator* lg = linkee->GetLocalGenerator();
-        std::string di = lg->GetCurrentBinaryDirectory();
-        di += "/";
-        di += lg->GetTargetDirectory(linkee);
+        std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
+                                  lg->GetTargetDirectory(linkee));
         dirs.push_back(std::move(di));
       }
     }
@@ -171,6 +171,7 @@
   if (this->GeneratorTarget->GetType() > cmStateEnums::OBJECT_LIBRARY) {
     return compilePdbPath;
   }
+
   compilePdbPath =
     this->GeneratorTarget->GetCompilePDBPath(this->GetConfigName());
   if (compilePdbPath.empty()) {
@@ -210,11 +211,7 @@
                                                const char* name, bool so)
 {
   // Lookup the flag to specify the version.
-  std::string fvar = "CMAKE_";
-  fvar += lang;
-  fvar += "_OSX_";
-  fvar += name;
-  fvar += "_VERSION_FLAG";
+  std::string fvar = cmStrCat("CMAKE_", lang, "_OSX_", name, "_VERSION_FLAG");
   const char* flag = this->Makefile->GetDefinition(fvar);
 
   // Skip if no such flag.
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h
index 6b0f74e..17792d6 100644
--- a/Source/cmCommonTargetGenerator.h
+++ b/Source/cmCommonTargetGenerator.h
@@ -50,7 +50,7 @@
   void AppendOSXVerFlag(std::string& flags, const std::string& lang,
                         const char* name, bool so);
 
-  typedef std::map<std::string, std::string> ByLanguageMap;
+  using ByLanguageMap = std::map<std::string, std::string>;
   std::string GetFlags(const std::string& l);
   ByLanguageMap FlagsByLanguage;
   std::string GetDefines(const std::string& l);
diff --git a/Source/cmComputeComponentGraph.cxx b/Source/cmComputeComponentGraph.cxx
index 113463f..af257ee 100644
--- a/Source/cmComputeComponentGraph.cxx
+++ b/Source/cmComputeComponentGraph.cxx
@@ -3,8 +3,7 @@
 #include "cmComputeComponentGraph.h"
 
 #include <algorithm>
-
-#include <assert.h>
+#include <cassert>
 
 cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input)
   : InputGraph(input)
diff --git a/Source/cmComputeComponentGraph.h b/Source/cmComputeComponentGraph.h
index 8cd4fe7..202888c 100644
--- a/Source/cmComputeComponentGraph.h
+++ b/Source/cmComputeComponentGraph.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGraphAdjacencyList.h"
-
 #include <stack>
 #include <vector>
 
+#include "cmGraphAdjacencyList.h"
+
 /** \class cmComputeComponentGraph
  * \brief Analyze a graph to determine strongly connected components.
  *
@@ -24,9 +24,9 @@
 {
 public:
   // Represent the graph with an adjacency list.
-  typedef cmGraphNodeList NodeList;
-  typedef cmGraphEdgeList EdgeList;
-  typedef cmGraphAdjacencyList Graph;
+  using NodeList = cmGraphNodeList;
+  using EdgeList = cmGraphEdgeList;
+  using Graph = cmGraphAdjacencyList;
 
   cmComputeComponentGraph(Graph const& input);
   ~cmComputeComponentGraph();
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 186deb6..7a9e2b7 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -2,7 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmComputeLinkDepends.h"
 
-#include "cmAlgorithms.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmComputeComponentGraph.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -11,18 +20,10 @@
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <assert.h>
-#include <iterator>
-#include <sstream>
-#include <stdio.h>
-#include <string.h>
-#include <utility>
-
 /*
 
 This file computes an ordered list of link items to use when linking a
@@ -199,11 +200,7 @@
   this->CCG = nullptr;
 }
 
-cmComputeLinkDepends::~cmComputeLinkDepends()
-{
-  cmDeleteAll(this->InferredDependSets);
-  delete this->CCG;
-}
+cmComputeLinkDepends::~cmComputeLinkDepends() = default;
 
 void cmComputeLinkDepends::SetOldLinkDirMode(bool b)
 {
@@ -282,10 +279,9 @@
 {
   std::map<cmLinkItem, int>::value_type index_entry(
     item, static_cast<int>(this->EntryList.size()));
-  std::map<cmLinkItem, int>::iterator lei =
-    this->LinkEntryIndex.insert(index_entry).first;
+  auto lei = this->LinkEntryIndex.insert(index_entry).first;
   this->EntryList.emplace_back();
-  this->InferredDependSets.push_back(nullptr);
+  this->InferredDependSets.emplace_back();
   this->EntryConstraintGraph.emplace_back();
   return lei;
 }
@@ -293,7 +289,7 @@
 int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
 {
   // Check if the item entry has already been added.
-  std::map<cmLinkItem, int>::iterator lei = this->LinkEntryIndex.find(item);
+  auto lei = this->LinkEntryIndex.find(item);
   if (lei != this->LinkEntryIndex.end()) {
     // Yes.  We do not need to follow the item's dependencies again.
     return lei->second;
@@ -318,15 +314,14 @@
     this->BFSQueue.push(qe);
   } else {
     // Look for an old-style <item>_LIB_DEPENDS variable.
-    std::string var = entry.Item;
-    var += "_LIB_DEPENDS";
+    std::string var = cmStrCat(entry.Item, "_LIB_DEPENDS");
     if (const char* val = this->Makefile->GetDefinition(var)) {
       // The item dependencies are known.  Follow them.
       BFSEntry qe = { index, val };
       this->BFSQueue.push(qe);
     } else if (!entry.IsFlag) {
       // The item dependencies are not known.  We need to infer them.
-      this->InferredDependSets[index] = new DependSetList;
+      this->InferredDependSets[index].Initialized = true;
     }
   }
 
@@ -394,8 +389,7 @@
 void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
 {
   // Check if the target already has an entry.
-  std::map<cmLinkItem, int>::iterator lei =
-    this->LinkEntryIndex.find(dep.Item);
+  auto lei = this->LinkEntryIndex.find(dep.Item);
   if (lei == this->LinkEntryIndex.end()) {
     // Allocate a spot for the item entry.
     lei = this->AllocateLinkEntry(dep.Item);
@@ -436,8 +430,7 @@
   // This is called to add the dependencies named by
   // <item>_LIB_DEPENDS.  The variable contains a semicolon-separated
   // list.  The list contains link-type;item pairs and just items.
-  std::vector<std::string> deplist;
-  cmSystemTools::ExpandListArgument(value, deplist);
+  std::vector<std::string> deplist = cmExpandedList(value);
 
   // Look for entries meant for this configuration.
   std::vector<cmLinkItem> actual_libs;
@@ -460,8 +453,7 @@
       // the export_library_dependencies command from CMake 2.4 and
       // lower.
       if (!haveLLT) {
-        std::string var = d;
-        var += "_LINK_TYPE";
+        std::string var = cmStrCat(d, "_LINK_TYPE");
         if (const char* val = this->Makefile->GetDefinition(var)) {
           if (strcmp(val, "debug") == 0) {
             llt = DEBUG_LibraryType;
@@ -542,7 +534,7 @@
     }
 
     // If this item needs to have dependencies inferred, do so.
-    if (this->InferredDependSets[dependee_index]) {
+    if (this->InferredDependSets[dependee_index].Initialized) {
       // Make sure an entry exists to hold the set for the item.
       dependSets[dependee_index];
     }
@@ -550,7 +542,7 @@
 
   // Store the inferred dependency sets discovered for this list.
   for (auto const& dependSet : dependSets) {
-    this->InferredDependSets[dependSet.first]->push_back(dependSet.second);
+    this->InferredDependSets[dependSet.first].push_back(dependSet.second);
   }
 }
 
@@ -577,14 +569,14 @@
        depender_index < this->InferredDependSets.size(); ++depender_index) {
     // Skip items for which dependencies do not need to be inferred or
     // for which the inferred dependency sets are empty.
-    DependSetList* sets = this->InferredDependSets[depender_index];
-    if (!sets || sets->empty()) {
+    DependSetList& sets = this->InferredDependSets[depender_index];
+    if (!sets.Initialized || sets.empty()) {
       continue;
     }
 
     // Intersect the sets for this item.
-    DependSet common = sets->front();
-    for (DependSet const& i : cmMakeRange(*sets).advance(1)) {
+    DependSet common = sets.front();
+    for (DependSet const& i : cmMakeRange(sets).advance(1)) {
       DependSet intersection;
       std::set_intersection(common.begin(), common.end(), i.begin(), i.end(),
                             std::inserter(intersection, intersection.begin()));
@@ -632,7 +624,8 @@
   // the same order in which the items were originally discovered in
   // the BFS.  This should preserve the original order when no
   // constraints disallow it.
-  this->CCG = new cmComputeComponentGraph(this->EntryConstraintGraph);
+  this->CCG =
+    cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
 
   // The component graph is guaranteed to be acyclic.  Start a DFS
   // from every entry to compute a topological order for the
@@ -720,8 +713,7 @@
   // This entry has now been seen.  Update its component.
   bool completed = false;
   int component = this->CCG->GetComponentMap()[index];
-  std::map<int, PendingComponent>::iterator mi =
-    this->PendingComponents.find(this->ComponentOrder[component]);
+  auto mi = this->PendingComponents.find(this->ComponentOrder[component]);
   if (mi != this->PendingComponents.end()) {
     // The entry is in an already pending component.
     PendingComponent& pc = mi->second;
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index dfaaf8b..645189a 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -5,16 +5,18 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGraphAdjacencyList.h"
-#include "cmLinkItem.h"
-#include "cmTargetLinkLibraryType.h"
-
 #include <map>
-#include <queue>
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
+#include <queue>
+
+#include "cmGraphAdjacencyList.h"
+#include "cmLinkItem.h"
+#include "cmTargetLinkLibraryType.h"
+
 class cmComputeComponentGraph;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
@@ -43,7 +45,7 @@
     bool IsFlag = false;
   };
 
-  typedef std::vector<LinkEntry> EntryVector;
+  using EntryVector = std::vector<LinkEntry>;
   EntryVector const& Compute();
 
   void SetOldLinkDirMode(bool b);
@@ -105,14 +107,15 @@
   };
   struct DependSetList : public std::vector<DependSet>
   {
+    bool Initialized = false;
   };
-  std::vector<DependSetList*> InferredDependSets;
+  std::vector<DependSetList> InferredDependSets;
   void InferDependencies();
 
   // Ordering constraint graph adjacency list.
-  typedef cmGraphNodeList NodeList;
-  typedef cmGraphEdgeList EdgeList;
-  typedef cmGraphAdjacencyList Graph;
+  using NodeList = cmGraphNodeList;
+  using EdgeList = cmGraphEdgeList;
+  using Graph = cmGraphAdjacencyList;
   Graph EntryConstraintGraph;
   void CleanConstraintGraph();
   void DisplayConstraintGraph();
@@ -137,7 +140,7 @@
     std::set<int> Entries;
   };
   std::map<int, PendingComponent> PendingComponents;
-  cmComputeComponentGraph* CCG;
+  std::unique_ptr<cmComputeComponentGraph> CCG;
   std::vector<int> FinalLinkOrder;
   void DisplayComponents();
   void VisitComponent(unsigned int c);
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 44d8615..8773d10 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -2,10 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmComputeLinkInformation.h"
 
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <sstream>
+#include <utility>
+
 #include "cmAlgorithms.h"
 #include "cmComputeLinkDepends.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -14,16 +21,11 @@
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <ctype.h>
-#include <sstream>
-#include <string.h>
-#include <utility>
-
 //#define CM_COMPUTE_LINK_INFO_DEBUG
 
 /*
@@ -268,10 +270,6 @@
     return;
   }
 
-  // Check whether we should use an import library for linking a target.
-  this->UseImportLibrary =
-    this->Makefile->IsDefinitionSet("CMAKE_IMPORT_LIBRARY_SUFFIX");
-
   // Check whether we should skip dependencies on shared library files.
   this->LinkDependsNoShared =
     this->Target->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED");
@@ -280,21 +278,35 @@
   // to use when creating a plugin (module) that obtains symbols from
   // the program that will load it.
   this->LoaderFlag = nullptr;
-  if (!this->UseImportLibrary &&
+  if (!this->Target->IsDLLPlatform() &&
       this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
-    std::string loader_flag_var = "CMAKE_SHARED_MODULE_LOADER_";
-    loader_flag_var += this->LinkLanguage;
-    loader_flag_var += "_FLAG";
+    std::string loader_flag_var =
+      cmStrCat("CMAKE_SHARED_MODULE_LOADER_", this->LinkLanguage, "_FLAG");
     this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var);
   }
 
   // Get options needed to link libraries.
-  this->LibLinkFlag =
-    this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
-  this->LibLinkFileFlag =
-    this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG");
-  this->LibLinkSuffix =
-    this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
+  if (const char* flag = this->Makefile->GetDefinition(
+        "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) {
+    this->LibLinkFlag = flag;
+  } else {
+    this->LibLinkFlag =
+      this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
+  }
+  if (const char* flag = this->Makefile->GetDefinition(
+        "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) {
+    this->LibLinkFileFlag = flag;
+  } else {
+    this->LibLinkFileFlag =
+      this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG");
+  }
+  if (const char* suffix = this->Makefile->GetDefinition(
+        "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) {
+    this->LibLinkSuffix = suffix;
+  } else {
+    this->LibLinkSuffix =
+      this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
+  }
 
   // Get options needed to specify RPATHs.
   this->RuntimeUseChrpath = false;
@@ -302,11 +314,8 @@
     const char* tType = ((this->Target->GetType() == cmStateEnums::EXECUTABLE)
                            ? "EXECUTABLE"
                            : "SHARED_LIBRARY");
-    std::string rtVar = "CMAKE_";
-    rtVar += tType;
-    rtVar += "_RUNTIME_";
-    rtVar += this->LinkLanguage;
-    rtVar += "_FLAG";
+    std::string rtVar =
+      cmStrCat("CMAKE_", tType, "_RUNTIME_", this->LinkLanguage, "_FLAG");
     std::string rtSepVar = rtVar + "_SEP";
     this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar);
     this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar);
@@ -316,19 +325,15 @@
     this->RuntimeUseChrpath = this->Target->IsChrpathUsed(config);
 
     // Get options needed to help find dependent libraries.
-    std::string rlVar = "CMAKE_";
-    rlVar += tType;
-    rlVar += "_RPATH_LINK_";
-    rlVar += this->LinkLanguage;
-    rlVar += "_FLAG";
+    std::string rlVar =
+      cmStrCat("CMAKE_", tType, "_RPATH_LINK_", this->LinkLanguage, "_FLAG");
     this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar);
   }
 
   // Check if we need to include the runtime search path at link time.
   {
-    std::string var = "CMAKE_SHARED_LIBRARY_LINK_";
-    var += this->LinkLanguage;
-    var += "_WITH_RUNTIME_PATH";
+    std::string var = cmStrCat("CMAKE_SHARED_LIBRARY_LINK_",
+                               this->LinkLanguage, "_WITH_RUNTIME_PATH");
     this->LinkWithRuntimePath = this->Makefile->IsOn(var);
   }
 
@@ -402,6 +407,18 @@
   delete this->OrderDependentRPath;
 }
 
+void cmComputeLinkInformation::AppendValues(
+  std::string& result, std::vector<BT<std::string>>& values)
+{
+  for (BT<std::string>& p : values) {
+    if (result.empty()) {
+      result.append(" ");
+    }
+
+    result.append(p.Value);
+  }
+}
+
 cmComputeLinkInformation::ItemVector const&
 cmComputeLinkInformation::GetItems() const
 {
@@ -414,6 +431,28 @@
   return this->OrderLinkerSearchPath->GetOrderedDirectories();
 }
 
+std::vector<BT<std::string>>
+cmComputeLinkInformation::GetDirectoriesWithBacktraces()
+{
+  std::vector<BT<std::string>> directoriesWithBacktraces;
+
+  std::vector<BT<std::string>> targetLinkDirectores =
+    this->Target->GetLinkDirectories(this->Config, this->LinkLanguage);
+
+  const std::vector<std::string>& orderedDirectories = this->GetDirectories();
+  for (const std::string& dir : orderedDirectories) {
+    auto result =
+      std::find(targetLinkDirectores.begin(), targetLinkDirectores.end(), dir);
+    if (result != targetLinkDirectores.end()) {
+      directoriesWithBacktraces.emplace_back(std::move(*result));
+    } else {
+      directoriesWithBacktraces.emplace_back(dir);
+    }
+  }
+
+  return directoriesWithBacktraces;
+}
+
 std::string cmComputeLinkInformation::GetRPathLinkString() const
 {
   // If there is no separate linker runtime search flag (-rpath-link)
@@ -479,7 +518,7 @@
   // Restore the target link type so the correct system runtime
   // libraries are found.
   const char* lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
-  if (cmSystemTools::IsOn(lss)) {
+  if (cmIsOn(lss)) {
     this->SetCurrentLinkType(LinkStatic);
   } else {
     this->SetCurrentLinkType(this->StartLinkType);
@@ -493,9 +532,7 @@
     std::set<cmGeneratorTarget const*> const& wrongItems =
       cld.GetOldWrongConfigItems();
     for (cmGeneratorTarget const* tgt : wrongItems) {
-      bool implib = (this->UseImportLibrary &&
-                     (tgt->GetType() == cmStateEnums::SHARED_LIBRARY));
-      cmStateEnums::ArtifactType artifact = implib
+      cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config)
         ? cmStateEnums::ImportLibraryArtifact
         : cmStateEnums::RuntimeBinaryArtifact;
       this->OldLinkDirItems.push_back(
@@ -547,14 +584,11 @@
 {
   // Add libraries for this language that are not implied by the
   // linker language.
-  std::string libVar = "CMAKE_";
-  libVar += lang;
-  libVar += "_IMPLICIT_LINK_LIBRARIES";
+  std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES");
   if (const char* libs = this->Makefile->GetDefinition(libVar)) {
-    std::vector<std::string> libsVec;
-    cmSystemTools::ExpandListArgument(libs, libsVec);
+    std::vector<std::string> libsVec = cmExpandedList(libs);
     for (std::string const& i : libsVec) {
-      if (this->ImplicitLinkLibs.find(i) == this->ImplicitLinkLibs.end()) {
+      if (!cmContains(this->ImplicitLinkLibs, i)) {
         this->AddItem(i, nullptr);
       }
     }
@@ -562,12 +596,9 @@
 
   // Add linker search paths for this language that are not
   // implied by the linker language.
-  std::string dirVar = "CMAKE_";
-  dirVar += lang;
-  dirVar += "_IMPLICIT_LINK_DIRECTORIES";
+  std::string dirVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_DIRECTORIES");
   if (const char* dirs = this->Makefile->GetDefinition(dirVar)) {
-    std::vector<std::string> dirsVec;
-    cmSystemTools::ExpandListArgument(dirs, dirsVec);
+    std::vector<std::string> dirsVec = cmExpandedList(dirs);
     this->OrderLinkerSearchPath->AddLanguageDirectories(dirsVec);
   }
 }
@@ -578,7 +609,7 @@
   // Compute the proper name to use to link this library.
   const std::string& config = this->Config;
   bool impexe = (tgt && tgt->IsExecutableWithExports());
-  if (impexe && !this->UseImportLibrary && !this->LoaderFlag) {
+  if (impexe && !tgt->HasImportLibrary(config) && !this->LoaderFlag) {
     // Skip linking to executables on platforms with no import
     // libraries or loader flags.
     return;
@@ -592,7 +623,7 @@
       // platform.  Add it now.
       std::string linkItem;
       linkItem = this->LoaderFlag;
-      cmStateEnums::ArtifactType artifact = this->UseImportLibrary
+      cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
         ? cmStateEnums::ImportLibraryArtifact
         : cmStateEnums::RuntimeBinaryArtifact;
 
@@ -616,15 +647,21 @@
       // Its object-files should already have been extracted for linking.
     } else {
       // Decide whether to use an import library.
-      bool implib =
-        (this->UseImportLibrary &&
-         (impexe || tgt->GetType() == cmStateEnums::SHARED_LIBRARY));
-      cmStateEnums::ArtifactType artifact = implib
+      cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
         ? cmStateEnums::ImportLibraryArtifact
         : cmStateEnums::RuntimeBinaryArtifact;
 
       // Pass the full path to the target file.
       std::string lib = tgt->GetFullPath(config, artifact, true);
+      if (tgt->Target->IsAIX() && cmHasLiteralSuffix(lib, "-NOTFOUND") &&
+          artifact == cmStateEnums::ImportLibraryArtifact) {
+        // This is an imported executable on AIX that has ENABLE_EXPORTS
+        // but not IMPORTED_IMPLIB.  CMake used to produce and accept such
+        // imported executables on AIX before we taught it to use linker
+        // import files.  For compatibility, simply skip linking to this
+        // executable as we did before.  It works with runtime linking.
+        return;
+      }
       if (!this->LinkDependsNoShared ||
           tgt->GetType() != cmStateEnums::SHARED_LIBRARY) {
         this->Depends.push_back(lib);
@@ -694,7 +731,7 @@
   // linked will be able to find it.
   std::string lib;
   if (tgt) {
-    cmStateEnums::ArtifactType artifact = this->UseImportLibrary
+    cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config)
       ? cmStateEnums::ImportLibraryArtifact
       : cmStateEnums::RuntimeBinaryArtifact;
     lib = tgt->GetFullPath(this->Config, artifact);
@@ -754,19 +791,15 @@
       break;
   }
   if (target_type_str) {
-    std::string static_link_type_flag_var = "CMAKE_";
-    static_link_type_flag_var += target_type_str;
-    static_link_type_flag_var += "_LINK_STATIC_";
-    static_link_type_flag_var += this->LinkLanguage;
-    static_link_type_flag_var += "_FLAGS";
+    std::string static_link_type_flag_var =
+      cmStrCat("CMAKE_", target_type_str, "_LINK_STATIC_", this->LinkLanguage,
+               "_FLAGS");
     static_link_type_flag =
       this->Makefile->GetDefinition(static_link_type_flag_var);
 
-    std::string shared_link_type_flag_var = "CMAKE_";
-    shared_link_type_flag_var += target_type_str;
-    shared_link_type_flag_var += "_LINK_DYNAMIC_";
-    shared_link_type_flag_var += this->LinkLanguage;
-    shared_link_type_flag_var += "_FLAGS";
+    std::string shared_link_type_flag_var =
+      cmStrCat("CMAKE_", target_type_str, "_LINK_DYNAMIC_", this->LinkLanguage,
+               "_FLAGS");
     shared_link_type_flag =
       this->Makefile->GetDefinition(shared_link_type_flag_var);
   }
@@ -782,7 +815,7 @@
 
   // Lookup the starting link type from the target (linked statically?).
   const char* lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC");
-  this->StartLinkType = cmSystemTools::IsOn(lss) ? LinkStatic : LinkShared;
+  this->StartLinkType = cmIsOn(lss) ? LinkStatic : LinkShared;
   this->CurrentLinkType = this->StartLinkType;
 }
 
@@ -805,16 +838,14 @@
                          LinkUnknown);
   if (const char* linkSuffixes =
         mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) {
-    std::vector<std::string> linkSuffixVec;
-    cmSystemTools::ExpandListArgument(linkSuffixes, linkSuffixVec);
+    std::vector<std::string> linkSuffixVec = cmExpandedList(linkSuffixes);
     for (std::string const& i : linkSuffixVec) {
       this->AddLinkExtension(i.c_str(), LinkUnknown);
     }
   }
   if (const char* sharedSuffixes =
         mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) {
-    std::vector<std::string> sharedSuffixVec;
-    cmSystemTools::ExpandListArgument(sharedSuffixes, sharedSuffixVec);
+    std::vector<std::string> sharedSuffixVec = cmExpandedList(sharedSuffixes);
     for (std::string const& i : sharedSuffixVec) {
       this->AddLinkExtension(i.c_str(), LinkShared);
     }
@@ -842,8 +873,7 @@
   reg += "([^/:]*)";
 
   // Create a regex to match any library name.
-  std::string reg_any = reg;
-  reg_any += libext;
+  std::string reg_any = cmStrCat(reg, libext);
 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
   fprintf(stderr, "any regex [%s]\n", reg_any.c_str());
 #endif
@@ -851,9 +881,8 @@
 
   // Create a regex to match static library names.
   if (!this->StaticLinkExtensions.empty()) {
-    std::string reg_static = reg;
-    reg_static +=
-      this->CreateExtensionRegex(this->StaticLinkExtensions, LinkStatic);
+    std::string reg_static = cmStrCat(
+      reg, this->CreateExtensionRegex(this->StaticLinkExtensions, LinkStatic));
 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
     fprintf(stderr, "static regex [%s]\n", reg_static.c_str());
 #endif
@@ -990,16 +1019,11 @@
     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() &&
-      this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) ==
-        this->OldLinkDirMask.end()) {
+      !cmContains(this->OldLinkDirMask,
+                  cmSystemTools::GetFilenamePath(item))) {
     this->OldLinkDirItems.push_back(item);
   }
 
@@ -1052,16 +1076,11 @@
   // For compatibility with CMake 2.4 include the item's directory in
   // the linker search path.
   if (this->OldLinkDirMode &&
-      this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) ==
-        this->OldLinkDirMask.end()) {
+      !cmContains(this->OldLinkDirMask,
+                  cmSystemTools::GetFilenamePath(item))) {
     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);
 }
@@ -1077,7 +1096,7 @@
 
   // Check if this item is in an implicit link directory.
   std::string dir = cmSystemTools::GetFilenamePath(item);
-  if (this->ImplicitLinkDirs.find(dir) == this->ImplicitLinkDirs.end()) {
+  if (!cmContains(this->ImplicitLinkDirs, dir)) {
     // Only libraries in implicit link directories are converted to
     // pathless items.
     return false;
@@ -1219,9 +1238,7 @@
   }
 
   // Create an option to ask the linker to search for the library.
-  std::string out = this->LibLinkFlag;
-  out += lib;
-  out += this->LibLinkSuffix;
+  std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
   this->Items.emplace_back(out, false);
 
   // Here we could try to find the library the linker will find and
@@ -1243,12 +1260,7 @@
 
   std::string fw_path = this->SplitFramework.match(1);
   std::string fw = this->SplitFramework.match(2);
-  std::string full_fw = fw_path;
-  full_fw += "/";
-  full_fw += fw;
-  full_fw += ".framework";
-  full_fw += "/";
-  full_fw += fw;
+  std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw);
 
   // Add the directory portion to the framework search path.
   this->AddFrameworkPath(fw_path);
@@ -1293,16 +1305,15 @@
   // Get platform-wide implicit directories.
   if (const char* implicitLinks = this->Makefile->GetDefinition(
         "CMAKE_PLATFORM_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES")) {
-    cmSystemTools::ExpandListArgument(implicitLinks, implicitDirVec);
+    cmExpandList(implicitLinks, implicitDirVec);
   }
 
   // Get language-specific implicit directories.
-  std::string implicitDirVar = "CMAKE_";
-  implicitDirVar += this->LinkLanguage;
-  implicitDirVar += "_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES";
+  std::string implicitDirVar = cmStrCat(
+    "CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES");
   if (const char* implicitDirs =
         this->Makefile->GetDefinition(implicitDirVar)) {
-    cmSystemTools::ExpandListArgument(implicitDirs, implicitDirVec);
+    cmExpandList(implicitDirs, implicitDirVec);
   }
 
   this->FrameworkPathsEmmitted.insert(implicitDirVec.begin(),
@@ -1356,8 +1367,7 @@
                                                  std::string const& file)
 {
   // Do not depend on things that do not exist.
-  std::vector<std::string>::iterator i =
-    std::find(this->Depends.begin(), this->Depends.end(), item);
+  auto i = std::find(this->Depends.begin(), this->Depends.end(), item);
   if (i != this->Depends.end()) {
     this->Depends.erase(i);
   }
@@ -1372,8 +1382,7 @@
   switch (this->Target->GetPolicyStatusCMP0008()) {
     case cmPolicies::WARN: {
       // Print the warning at most once for this item.
-      std::string wid = "CMP0008-WARNING-GIVEN-";
-      wid += item;
+      std::string wid = cmStrCat("CMP0008-WARNING-GIVEN-", item);
       if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(wid)) {
         this->CMakeInstance->GetState()->SetGlobalProperty(wid, "1");
         std::ostringstream w;
@@ -1521,7 +1530,7 @@
   // Get platform-wide implicit directories.
   if (const char* implicitLinks = (this->Makefile->GetDefinition(
         "CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES"))) {
-    cmSystemTools::ExpandListArgument(implicitLinks, implicitDirVec);
+    cmExpandList(implicitLinks, implicitDirVec);
   }
 
   // Append library architecture to all implicit platform directories
@@ -1534,12 +1543,11 @@
   }
 
   // Get language-specific implicit directories.
-  std::string implicitDirVar = "CMAKE_";
-  implicitDirVar += this->LinkLanguage;
-  implicitDirVar += "_IMPLICIT_LINK_DIRECTORIES";
+  std::string implicitDirVar =
+    cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_DIRECTORIES");
   if (const char* implicitDirs =
         this->Makefile->GetDefinition(implicitDirVar)) {
-    cmSystemTools::ExpandListArgument(implicitDirs, implicitDirVec);
+    cmExpandList(implicitDirs, implicitDirVec);
   }
 
   // Store implicit link directories.
@@ -1547,12 +1555,11 @@
 
   // Get language-specific implicit libraries.
   std::vector<std::string> implicitLibVec;
-  std::string implicitLibVar = "CMAKE_";
-  implicitLibVar += this->LinkLanguage;
-  implicitLibVar += "_IMPLICIT_LINK_LIBRARIES";
+  std::string implicitLibVar =
+    cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_LIBRARIES");
   if (const char* implicitLibs =
         this->Makefile->GetDefinition(implicitLibVar)) {
-    cmSystemTools::ExpandListArgument(implicitLibs, implicitLibVec);
+    cmExpandList(implicitLibs, implicitLibVec);
   }
 
   // Store implicit link libraries.
@@ -1567,7 +1574,7 @@
   // Get platform specific rpath link directories
   if (const char* rpathDirs =
         (this->Makefile->GetDefinition("CMAKE_PLATFORM_RUNTIME_PATH"))) {
-    cmSystemTools::ExpandListArgument(rpathDirs, this->RuntimeLinkDirs);
+    cmExpandList(rpathDirs, this->RuntimeLinkDirs);
   }
 }
 
@@ -1673,8 +1680,7 @@
                                    std::vector<std::string>& out,
                                    std::set<std::string>& emitted)
 {
-  std::vector<std::string> tmp;
-  cmSystemTools::ExpandListArgument(str, tmp);
+  std::vector<std::string> tmp = cmExpandedList(str);
   for (std::string const& i : tmp) {
     if (emitted.insert(i).second) {
       out.push_back(i);
@@ -1695,7 +1701,7 @@
     (for_install ||
      this->Target->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"));
   bool use_install_rpath =
-    (outputRuntime && this->Target->HaveInstallTreeRPATH() &&
+    (outputRuntime && this->Target->HaveInstallTreeRPATH(this->Config) &&
      linking_for_install);
   bool use_build_rpath =
     (outputRuntime && this->Target->HaveBuildTreeRPATH(this->Config) &&
@@ -1715,15 +1721,17 @@
   // Construct the RPATH.
   std::set<std::string> emitted;
   if (use_install_rpath) {
-    const char* install_rpath = this->Target->GetProperty("INSTALL_RPATH");
-    cmCLI_ExpandListUnique(install_rpath, runtimeDirs, emitted);
+    std::string install_rpath;
+    this->Target->GetInstallRPATH(this->Config, install_rpath);
+    cmCLI_ExpandListUnique(install_rpath.c_str(), runtimeDirs, emitted);
   }
   if (use_build_rpath) {
     // Add directories explicitly specified by user
-    if (const char* build_rpath = this->Target->GetProperty("BUILD_RPATH")) {
+    std::string build_rpath;
+    if (this->Target->GetBuildRPATH(this->Config, build_rpath)) {
       // This will not resolve entries to use $ORIGIN, the user is expected to
       // do that if necessary.
-      cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
+      cmCLI_ExpandListUnique(build_rpath.c_str(), runtimeDirs, emitted);
     }
   }
   if (use_build_rpath || use_link_rpath) {
@@ -1751,9 +1759,7 @@
           d = d.substr(rootPath.size());
         } else if (stagePath && *stagePath && d.find(stagePath) == 0) {
           std::string suffix = d.substr(strlen(stagePath));
-          d = installPrefix;
-          d += "/";
-          d += suffix;
+          d = cmStrCat(installPrefix, '/', suffix);
           cmSystemTools::ConvertToUnixSlashes(d);
         } else if (use_relative_build_rpath) {
           // If expansion of the $ORIGIN token is supported and permitted per
@@ -1762,7 +1768,7 @@
               cmSystemTools::IsSubDirectory(d, topBinaryDir)) {
             d = cmSystemTools::RelativePath(targetOutputDir, d);
             if (!d.empty()) {
-              d = originToken + "/" + d;
+              d = cmStrCat(originToken, "/", d);
             } else {
               d = originToken;
             }
@@ -1784,9 +1790,7 @@
             d = d.substr(rootPath.size());
           } else if (stagePath && *stagePath && d.find(stagePath) == 0) {
             std::string suffix = d.substr(strlen(stagePath));
-            d = installPrefix;
-            d += "/";
-            d += suffix;
+            d = cmStrCat(installPrefix, '/', suffix);
             cmSystemTools::ConvertToUnixSlashes(d);
           }
           if (emitted.insert(d).second) {
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 863639c..92ab83b 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -5,18 +5,21 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmMakefile;
 class cmOrderDirectories;
 class cmake;
+template <typename T>
+class BT;
 
 /** \class cmComputeLinkInformation
  * \brief Compute link information for a target in one configuration.
@@ -42,9 +45,11 @@
     bool IsPath = true;
     cmGeneratorTarget const* Target = nullptr;
   };
-  typedef std::vector<Item> ItemVector;
+  using ItemVector = std::vector<Item>;
+  void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
   ItemVector const& GetItems() const;
   std::vector<std::string> const& GetDirectories() const;
+  std::vector<BT<std::string>> GetDirectoriesWithBacktraces();
   std::vector<std::string> const& GetDepends() const;
   std::vector<std::string> const& GetFrameworkPaths() const;
   std::string GetLinkLanguage() const { return this->LinkLanguage; }
@@ -56,11 +61,18 @@
   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;
 
   std::string GetConfig() const { return this->Config; }
 
+  const cmGeneratorTarget* GetTarget() { return this->Target; }
+
 private:
   void AddItem(std::string const& item, const cmGeneratorTarget* tgt);
   void AddSharedDepItem(std::string const& item, cmGeneratorTarget const* tgt);
@@ -179,7 +191,6 @@
   bool OldLinkDirMode;
   bool OpenBSD;
   bool LinkDependsNoShared;
-  bool UseImportLibrary;
   bool RuntimeUseChrpath;
   bool NoSONameUsesPath;
   bool LinkWithRuntimePath;
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx
index 01d4c07..162bab2 100644
--- a/Source/cmComputeTargetDepends.cxx
+++ b/Source/cmComputeTargetDepends.cxx
@@ -2,6 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmComputeTargetDepends.h"
 
+#include <cassert>
+#include <cstdio>
+#include <sstream>
+#include <utility>
+
 #include "cmComputeComponentGraph.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -20,11 +25,6 @@
 #include "cmTargetDepend.h"
 #include "cmake.h"
 
-#include <assert.h>
-#include <sstream>
-#include <stdio.h>
-#include <utility>
-
 /*
 
 This class is meant to analyze inter-target dependencies globally
@@ -140,8 +140,7 @@
 {
   // Lookup the index for this target.  All targets should be known by
   // this point.
-  std::map<cmGeneratorTarget const*, int>::const_iterator tii =
-    this->TargetIndex.find(t);
+  auto tii = this->TargetIndex.find(t);
   assert(tii != this->TargetIndex.end());
   int i = tii->second;
 
@@ -149,7 +148,7 @@
   EdgeList const& nl = this->FinalGraph[i];
   for (cmGraphEdge const& ni : nl) {
     cmGeneratorTarget const* dep = this->Targets[ni];
-    cmTargetDependSet::iterator di = deps.insert(dep).first;
+    auto di = deps.insert(dep).first;
     di->SetType(ni.IsStrong());
     di->SetBacktrace(ni.GetBacktrace());
   }
@@ -197,11 +196,8 @@
   {
     std::set<cmLinkItem> emitted;
 
-    std::vector<std::string> configs;
-    depender->Makefile->GetConfigurations(configs);
-    if (configs.empty()) {
-      configs.emplace_back();
-    }
+    std::vector<std::string> const& configs =
+      depender->Makefile->GetGeneratorConfigs();
     for (std::string const& it : configs) {
       std::vector<cmSourceFile const*> objectFiles;
       depender->GetExternalObjects(objectFiles, it);
@@ -371,8 +367,7 @@
   } else {
     // Lookup the index for this target.  All targets should be known by
     // this point.
-    std::map<cmGeneratorTarget const*, int>::const_iterator tii =
-      this->TargetIndex.find(dependee);
+    auto tii = this->TargetIndex.find(dependee);
     assert(tii != this->TargetIndex.end());
     int dependee_index = tii->second;
 
diff --git a/Source/cmComputeTargetDepends.h b/Source/cmComputeTargetDepends.h
index 3840bd7..d8060ae 100644
--- a/Source/cmComputeTargetDepends.h
+++ b/Source/cmComputeTargetDepends.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGraphAdjacencyList.h"
-#include "cmListFileCache.h"
-
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmGraphAdjacencyList.h"
+#include "cmListFileCache.h"
+
 class cmComputeComponentGraph;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
@@ -70,9 +70,9 @@
   // Represent the target dependency graph.  The entry at each
   // top-level index corresponds to a depender whose dependencies are
   // listed.
-  typedef cmGraphNodeList NodeList;
-  typedef cmGraphEdgeList EdgeList;
-  typedef cmGraphAdjacencyList Graph;
+  using NodeList = cmGraphNodeList;
+  using EdgeList = cmGraphEdgeList;
+  using Graph = cmGraphAdjacencyList;
   Graph InitialGraph;
   Graph FinalGraph;
   void DisplayGraph(Graph const& graph, const std::string& name);
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index e7e91c1..003e60d 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -2,22 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmConditionEvaluator.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <utility>
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-class cmCommand;
 class cmTest;
 
 static std::string const keyAND = "AND";
@@ -219,10 +220,10 @@
   }
 
   // Check named constants.
-  if (cmSystemTools::IsOn(arg.c_str())) {
+  if (cmIsOn(arg.GetValue())) {
     return true;
   }
-  if (cmSystemTools::IsOff(arg.c_str())) {
+  if (cmIsOff(arg.GetValue())) {
     return false;
   }
 
@@ -238,7 +239,7 @@
 
   // Check definition.
   const char* def = this->GetDefinitionIfUnquoted(arg);
-  return !cmSystemTools::IsOff(def);
+  return !cmIsOff(def);
 }
 
 //=========================================================================
@@ -255,14 +256,14 @@
       return true;
     }
     const char* def = this->GetDefinitionIfUnquoted(arg);
-    return !cmSystemTools::IsOff(def);
+    return !cmIsOff(def);
   }
   // Old GetVariableOrNumber behavior.
   const char* def = this->GetDefinitionIfUnquoted(arg);
   if (!def && atoi(arg.c_str())) {
     def = arg.c_str();
   }
-  return !cmSystemTools::IsOff(def);
+  return !cmIsOff(def);
 }
 
 //=========================================================================
@@ -367,7 +368,7 @@
   int reducible;
   do {
     reducible = 0;
-    cmArgumentList::iterator arg = newArgs.begin();
+    auto arg = newArgs.begin();
     while (arg != newArgs.end()) {
       if (IsKeyword(keyParenL, *arg)) {
         // search for the closing paren for this opening one
@@ -393,7 +394,7 @@
         std::vector<cmExpandedCommandArgument> newArgs2;
 
         // copy to the list structure
-        cmArgumentList::iterator argP1 = arg;
+        auto argP1 = arg;
         argP1++;
         cmAppend(newArgs2, argP1, argClose);
         newArgs2.pop_back();
@@ -424,7 +425,7 @@
   int reducible;
   do {
     reducible = 0;
-    cmArgumentList::iterator arg = newArgs.begin();
+    auto arg = newArgs.begin();
     cmArgumentList::iterator argP1;
     cmArgumentList::iterator argP2;
     while (arg != newArgs.end()) {
@@ -452,7 +453,7 @@
       }
       // does a command exist
       if (this->IsKeyword(keyCOMMAND, *arg) && argP1 != newArgs.end()) {
-        cmCommand* command =
+        cmState::Command command =
           this->Makefile.GetState()->GetCommand(argP1->c_str());
         this->HandlePredicate(command != nullptr, reducible, arg, newArgs,
                               argP1, argP2);
@@ -524,7 +525,7 @@
   const char* def2;
   do {
     reducible = 0;
-    cmArgumentList::iterator arg = newArgs.begin();
+    auto arg = newArgs.begin();
     cmArgumentList::iterator argP1;
     cmArgumentList::iterator argP2;
     while (arg != newArgs.end()) {
@@ -668,10 +669,9 @@
           def2 = this->Makefile.GetDefinition(argP2->GetValue());
 
           if (def2) {
-            std::vector<std::string> list;
-            cmSystemTools::ExpandListArgument(def2, list, true);
+            std::vector<std::string> list = cmExpandedList(def2, true);
 
-            result = std::find(list.begin(), list.end(), def) != list.end();
+            result = cmContains(list, def);
           }
 
           this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
@@ -701,7 +701,7 @@
   int reducible;
   do {
     reducible = 0;
-    cmArgumentList::iterator arg = newArgs.begin();
+    auto arg = newArgs.begin();
     cmArgumentList::iterator argP1;
     cmArgumentList::iterator argP2;
     while (arg != newArgs.end()) {
@@ -729,7 +729,7 @@
   bool rhs;
   do {
     reducible = 0;
-    cmArgumentList::iterator arg = newArgs.begin();
+    auto arg = newArgs.begin();
     cmArgumentList::iterator argP1;
     cmArgumentList::iterator argP2;
     while (arg != newArgs.end()) {
diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h
index 59e1396..082534c 100644
--- a/Source/cmConditionEvaluator.h
+++ b/Source/cmConditionEvaluator.h
@@ -19,7 +19,7 @@
 class cmConditionEvaluator
 {
 public:
-  typedef std::list<cmExpandedCommandArgument> cmArgumentList;
+  using cmArgumentList = std::list<cmExpandedCommandArgument>;
 
   cmConditionEvaluator(cmMakefile& makefile, cmListFileContext context,
                        cmListFileBacktrace bt);
diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in
index 19b1cd4..4de1c5d 100644
--- a/Source/cmConfigure.cmake.h.in
+++ b/Source/cmConfigure.cmake.h.in
@@ -26,4 +26,8 @@
 
 #define CM_FALLTHROUGH cmsys_FALLTHROUGH
 
+#if defined(_WIN32) && !defined(NOMINMAX)
+#  define NOMINMAX
+#endif
+
 #endif
diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx
index 0917d11..8767386 100644
--- a/Source/cmConfigureFileCommand.cxx
+++ b/Source/cmConfigureFileCommand.cxx
@@ -2,78 +2,74 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmConfigureFileCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmNewLineStyle.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmConfigureFileCommand
-bool cmConfigureFileCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmConfigureFileCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments, expected 2");
+    status.SetError("called with incorrect number of arguments, expected 2");
     return false;
   }
 
   std::string const& inFile = args[0];
-  this->InputFile = cmSystemTools::CollapseFullPath(
-    inFile, this->Makefile->GetCurrentSourceDirectory());
+  const std::string inputFile = cmSystemTools::CollapseFullPath(
+    inFile, status.GetMakefile().GetCurrentSourceDirectory());
 
   // If the input location is a directory, error out.
-  if (cmSystemTools::FileIsDirectory(this->InputFile)) {
-    std::ostringstream e;
-    /* clang-format off */
-    e << "input location\n"
-      << "  " << this->InputFile << "\n"
-      << "is a directory but a file was expected.";
-    /* clang-format on */
-    this->SetError(e.str());
+  if (cmSystemTools::FileIsDirectory(inputFile)) {
+    status.SetError(cmStrCat("input location\n  ", inputFile,
+                             "\n"
+                             "is a directory but a file was expected."));
     return false;
   }
 
   std::string const& outFile = args[1];
-  this->OutputFile = cmSystemTools::CollapseFullPath(
-    outFile, this->Makefile->GetCurrentBinaryDirectory());
+  std::string outputFile = cmSystemTools::CollapseFullPath(
+    outFile, status.GetMakefile().GetCurrentBinaryDirectory());
 
   // If the output location is already a directory put the file in it.
-  if (cmSystemTools::FileIsDirectory(this->OutputFile)) {
-    this->OutputFile += "/";
-    this->OutputFile += cmSystemTools::GetFilenameName(inFile);
+  if (cmSystemTools::FileIsDirectory(outputFile)) {
+    outputFile += "/";
+    outputFile += cmSystemTools::GetFilenameName(inFile);
   }
 
-  if (!this->Makefile->CanIWriteThisFile(this->OutputFile)) {
-    std::string e = "attempted to configure a file: " + this->OutputFile +
+  if (!status.GetMakefile().CanIWriteThisFile(outputFile)) {
+    std::string e = "attempted to configure a file: " + outputFile +
       " into a source directory.";
-    this->SetError(e);
+    status.SetError(e);
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
   std::string errorMessage;
-  if (!this->NewLineStyle.ReadFromArguments(args, errorMessage)) {
-    this->SetError(errorMessage);
+  cmNewLineStyle newLineStyle;
+  if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
+    status.SetError(errorMessage);
     return false;
   }
-  this->CopyOnly = false;
-  this->EscapeQuotes = false;
+  bool copyOnly = false;
+  bool escapeQuotes = false;
 
   std::string unknown_args;
-  this->AtOnly = false;
+  bool atOnly = false;
   for (unsigned int i = 2; i < args.size(); ++i) {
     if (args[i] == "COPYONLY") {
-      this->CopyOnly = true;
-      if (this->NewLineStyle.IsValid()) {
-        this->SetError("COPYONLY could not be used in combination "
-                       "with NEWLINE_STYLE");
+      copyOnly = true;
+      if (newLineStyle.IsValid()) {
+        status.SetError("COPYONLY could not be used in combination "
+                        "with NEWLINE_STYLE");
         return false;
       }
     } else if (args[i] == "ESCAPE_QUOTES") {
-      this->EscapeQuotes = true;
+      escapeQuotes = true;
     } else if (args[i] == "@ONLY") {
-      this->AtOnly = true;
+      atOnly = true;
     } else if (args[i] == "IMMEDIATE") {
       /* Ignore legacy option.  */
     } else if (args[i] == "NEWLINE_STYLE" || args[i] == "LF" ||
@@ -87,22 +83,16 @@
     }
   }
   if (!unknown_args.empty()) {
-    std::string msg = "configure_file called with unknown argument(s):\n";
-    msg += unknown_args;
-    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
+    std::string msg = cmStrCat(
+      "configure_file called with unknown argument(s):\n", unknown_args);
+    status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg);
   }
 
-  if (!this->ConfigureFile()) {
-    this->SetError("Problem configuring file");
+  if (!status.GetMakefile().ConfigureFile(
+        inputFile, outputFile, copyOnly, atOnly, escapeQuotes, newLineStyle)) {
+    status.SetError("Problem configuring file");
     return false;
   }
 
   return true;
 }
-
-int cmConfigureFileCommand::ConfigureFile()
-{
-  return this->Makefile->ConfigureFile(this->InputFile, this->OutputFile,
-                                       this->CopyOnly, this->AtOnly,
-                                       this->EscapeQuotes, this->NewLineStyle);
-}
diff --git a/Source/cmConfigureFileCommand.h b/Source/cmConfigureFileCommand.h
index 5603c50..c7f95b8 100644
--- a/Source/cmConfigureFileCommand.h
+++ b/Source/cmConfigureFileCommand.h
@@ -8,33 +8,8 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmNewLineStyle.h"
-
 class cmExecutionStatus;
 
-class cmConfigureFileCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmConfigureFileCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  int ConfigureFile();
-
-  cmNewLineStyle NewLineStyle;
-
-  std::string InputFile;
-  std::string OutputFile;
-  bool CopyOnly = false;
-  bool EscapeQuotes = false;
-  bool AtOnly = false;
-};
-
+bool cmConfigureFileCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 #endif
diff --git a/Source/cmConnection.cxx b/Source/cmConnection.cxx
index 166426b..884e314 100644
--- a/Source/cmConnection.cxx
+++ b/Source/cmConnection.cxx
@@ -2,12 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmConnection.h"
 
-#include "cmServer.h"
-#include "cm_uv.h"
-
 #include <cassert>
 #include <cstring>
 
+#include "cm_uv.h"
+
+#include "cmServer.h"
+
 struct write_req_t
 {
   uv_write_t req;
diff --git a/Source/cmConnection.h b/Source/cmConnection.h
index 092b913..7bb2494 100644
--- a/Source/cmConnection.h
+++ b/Source/cmConnection.h
@@ -5,13 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmUVHandlePtr.h"
-#include "cm_uv.h"
-
 #include <cstddef>
 #include <memory>
 #include <string>
 
+#include "cm_uv.h"
+
+#include "cmUVHandlePtr.h"
+
 class cmServerBase;
 
 /***
diff --git a/Source/cmContinueCommand.cxx b/Source/cmContinueCommand.cxx
index 48f1f41..bb63dff 100644
--- a/Source/cmContinueCommand.cxx
+++ b/Source/cmContinueCommand.cxx
@@ -8,13 +8,14 @@
 #include "cmSystemTools.h"
 
 // cmContinueCommand
-bool cmContinueCommand::InitialPass(std::vector<std::string> const& args,
-                                    cmExecutionStatus& status)
+bool cmContinueCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-  if (!this->Makefile->IsLoopBlock()) {
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                 "A CONTINUE command was found outside of a "
-                                 "proper FOREACH or WHILE loop scope.");
+  if (!status.GetMakefile().IsLoopBlock()) {
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      "A CONTINUE command was found outside of a "
+      "proper FOREACH or WHILE loop scope.");
     cmSystemTools::SetFatalErrorOccured();
     return true;
   }
@@ -22,9 +23,10 @@
   status.SetContinueInvoked();
 
   if (!args.empty()) {
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                 "The CONTINUE command does not accept any "
-                                 "arguments.");
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      "The CONTINUE command does not accept any "
+      "arguments.");
     cmSystemTools::SetFatalErrorOccured();
     return true;
   }
diff --git a/Source/cmContinueCommand.h b/Source/cmContinueCommand.h
index d383d1d..ff903aa 100644
--- a/Source/cmContinueCommand.h
+++ b/Source/cmContinueCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmContinueCommand
+/**
  * \brief Continue from an enclosing foreach or while loop
  *
  * cmContinueCommand returns from an enclosing foreach or while loop
  */
-class cmContinueCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmContinueCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmContinueCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index f12ef0b..515f446 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -2,14 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCoreTryCompile.h"
 
-#include "cmsys/Directory.hxx"
+#include <cstdio>
+#include <cstring>
 #include <set>
 #include <sstream>
-#include <stdio.h>
-#include <string.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include "cmsys/Directory.hxx"
+
+#include "cm_static_string_view.hxx"
+
 #include "cmExportTryCompileFileGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -17,10 +19,10 @@
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmVersion.h"
-#include "cm_static_string_view.hxx"
 #include "cmake.h"
 
 static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN =
@@ -234,11 +236,11 @@
     } else if (doing == DoingCMakeFlags) {
       cmakeFlags.push_back(argv[i]);
     } else if (doing == DoingCompileDefinitions) {
-      cmSystemTools::ExpandListArgument(argv[i], compileDefs);
+      cmExpandList(argv[i], compileDefs);
     } else if (doing == DoingLinkOptions) {
       linkOptions.push_back(argv[i]);
     } else if (doing == DoingLinkLibraries) {
-      libsToLink += "\"" + cmSystemTools::TrimWhitespace(argv[i]) + "\" ";
+      libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" ";
       if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) {
         switch (tgt->GetType()) {
           case cmStateEnums::SHARED_LIBRARY:
@@ -411,8 +413,7 @@
   // compute the binary dir when TRY_COMPILE is called with a src file
   // signature
   if (this->SrcFileSignature) {
-    this->BinaryDirectory += "/CMakeFiles";
-    this->BinaryDirectory += "/CMakeTmp";
+    this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
   } else {
     // only valid for srcfile signatures
     if (!compileDefs.empty()) {
@@ -514,7 +515,7 @@
     for (std::string const& li : testLangs) {
       projectLangs += " " + li;
       std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
-      std::string rulesOverrideLang = rulesOverrideBase + "_" + li;
+      std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
       if (const char* rulesOverridePath =
             this->Makefile->GetDefinition(rulesOverrideLang)) {
         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
@@ -574,7 +575,8 @@
         std::string const cfg =
           !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault;
         for (std::string const& li : testLangs) {
-          std::string const langFlagsCfg = "CMAKE_" + li + "_FLAGS_" + cfg;
+          std::string const langFlagsCfg =
+            cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
           const char* flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
           fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
                   cmOutputConverter::EscapeForCMake(flagsCfg ? flagsCfg : "")
@@ -674,8 +676,7 @@
 
       if (const char* varListStr = this->Makefile->GetDefinition(
             kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
-        std::vector<std::string> varList;
-        cmSystemTools::ExpandListArgument(varListStr, varList);
+        std::vector<std::string> varList = cmExpandedList(varListStr);
         vars.insert(varList.begin(), varList.end());
       }
 
@@ -932,7 +933,7 @@
                                      cmStateEnums::INTERNAL);
 
   if (!outputVariable.empty()) {
-    this->Makefile->AddDefinition(outputVariable, output.c_str());
+    this->Makefile->AddDefinition(outputVariable, output);
   }
 
   if (this->SrcFileSignature) {
@@ -961,8 +962,7 @@
     }
 
     if (!copyFileError.empty()) {
-      this->Makefile->AddDefinition(copyFileError,
-                                    copyFileErrorMessage.c_str());
+      this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
     }
   }
   return res;
@@ -1046,16 +1046,14 @@
     this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
   // if a config was specified try that first
   if (config && config[0]) {
-    std::string tmp = "/";
-    tmp += config;
+    std::string tmp = cmStrCat('/', config);
     searchDirs.push_back(std::move(tmp));
   }
   searchDirs.emplace_back("/Debug");
 #if defined(__APPLE__)
   std::string app = "/" + targetName + ".app";
   if (config && config[0]) {
-    std::string tmp = "/";
-    tmp += config + app;
+    std::string tmp = cmStrCat('/', config, app);
     searchDirs.push_back(std::move(tmp));
   }
   std::string tmp = "/Debug" + app;
@@ -1065,9 +1063,7 @@
   searchDirs.emplace_back("/Development");
 
   for (std::string const& sdir : searchDirs) {
-    std::string command = this->BinaryDirectory;
-    command += sdir;
-    command += tmpOutputFile;
+    std::string command = cmStrCat(this->BinaryDirectory, sdir, tmpOutputFile);
     if (cmSystemTools::FileExists(command)) {
       this->OutputFile = cmSystemTools::CollapseFullPath(command);
       return;
diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx
index b78493f..9d492ba 100644
--- a/Source/cmCreateTestSourceList.cxx
+++ b/Source/cmCreateTestSourceList.cxx
@@ -4,22 +4,21 @@
 
 #include <algorithm>
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
-// cmCreateTestSourceList
-bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmCreateTestSourceList(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with wrong number of arguments.");
+    status.SetError("called with wrong number of arguments.");
     return false;
   }
 
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
   std::string extraInclude;
   std::string function;
   std::vector<std::string> tests;
@@ -28,20 +27,17 @@
     if (*i == "EXTRA_INCLUDE") {
       ++i;
       if (i == args.end()) {
-        this->SetError("incorrect arguments to EXTRA_INCLUDE");
+        status.SetError("incorrect arguments to EXTRA_INCLUDE");
         return false;
       }
-      extraInclude = "#include \"";
-      extraInclude += *i;
-      extraInclude += "\"\n";
+      extraInclude = cmStrCat("#include \"", *i, "\"\n");
     } else if (*i == "FUNCTION") {
       ++i;
       if (i == args.end()) {
-        this->SetError("incorrect arguments to FUNCTION");
+        status.SetError("incorrect arguments to FUNCTION");
         return false;
       }
-      function = *i;
-      function += "(&ac, &av);\n";
+      function = cmStrCat(*i, "(&ac, &av);\n");
     } else {
       tests.push_back(*i);
     }
@@ -56,13 +52,12 @@
   // Name of the test driver
   // make sure they specified an extension
   if (cmSystemTools::GetFilenameExtension(*i).size() < 2) {
-    this->SetError(
+    status.SetError(
       "You must specify a file extension for the test driver file.");
     return false;
   }
-  std::string driver = this->Makefile->GetCurrentBinaryDirectory();
-  driver += "/";
-  driver += *i;
+  cmMakefile& mf = status.GetMakefile();
+  std::string driver = cmStrCat(mf.GetCurrentBinaryDirectory(), '/', *i);
   ++i;
 
   std::string configFile = cmSystemTools::GetCMakeRoot();
@@ -70,7 +65,7 @@
   configFile += "/Templates/TestDriver.cxx.in";
   // Create the test driver file
 
-  std::vector<std::string>::const_iterator testsBegin = i;
+  auto testsBegin = i;
   std::vector<std::string> tests_func_name;
 
   // The rest of the arguments consist of a list of test source files.
@@ -124,36 +119,32 @@
     numTests++;
   }
   if (!extraInclude.empty()) {
-    this->Makefile->AddDefinition("CMAKE_TESTDRIVER_EXTRA_INCLUDES",
-                                  extraInclude.c_str());
+    mf.AddDefinition("CMAKE_TESTDRIVER_EXTRA_INCLUDES", extraInclude);
   }
   if (!function.empty()) {
-    this->Makefile->AddDefinition("CMAKE_TESTDRIVER_ARGVC_FUNCTION",
-                                  function.c_str());
+    mf.AddDefinition("CMAKE_TESTDRIVER_ARGVC_FUNCTION", function);
   }
-  this->Makefile->AddDefinition("CMAKE_FORWARD_DECLARE_TESTS",
-                                forwardDeclareCode.c_str());
-  this->Makefile->AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES",
-                                functionMapCode.c_str());
+  mf.AddDefinition("CMAKE_FORWARD_DECLARE_TESTS", forwardDeclareCode);
+  mf.AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode);
   bool res = true;
-  if (!this->Makefile->ConfigureFile(configFile, driver, false, true, false)) {
+  if (!mf.ConfigureFile(configFile, driver, false, true, false)) {
     res = false;
   }
 
   // Construct the source list.
   std::string sourceListValue;
   {
-    cmSourceFile* sf = this->Makefile->GetOrCreateSource(driver);
+    cmSourceFile* sf = mf.GetOrCreateSource(driver);
     sf->SetProperty("ABSTRACT", "0");
     sourceListValue = args[1];
   }
   for (i = testsBegin; i != tests.end(); ++i) {
-    cmSourceFile* sf = this->Makefile->GetOrCreateSource(*i);
+    cmSourceFile* sf = mf.GetOrCreateSource(*i);
     sf->SetProperty("ABSTRACT", "0");
     sourceListValue += ";";
     sourceListValue += *i;
   }
 
-  this->Makefile->AddDefinition(sourceList, sourceListValue.c_str());
+  mf.AddDefinition(sourceList, sourceListValue);
   return res;
 }
diff --git a/Source/cmCreateTestSourceList.h b/Source/cmCreateTestSourceList.h
index 005b32c..19503f4 100644
--- a/Source/cmCreateTestSourceList.h
+++ b/Source/cmCreateTestSourceList.h
@@ -8,29 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmCreateTestSourceList
- * \brief Test driver generation command
- *
- */
-
-class cmCreateTestSourceList : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmCreateTestSourceList; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmCreateTestSourceList(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmCryptoHash.cxx b/Source/cmCryptoHash.cxx
index d914eb1..dc7d939 100644
--- a/Source/cmCryptoHash.cxx
+++ b/Source/cmCryptoHash.cxx
@@ -2,13 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCryptoHash.h"
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
 #include "cm_kwiml.h"
 #include "cm_rhash.h"
-#include "cmsys/FStream.hxx"
-#include <string.h>
-
-#include <memory> // IWYU pragma: keep
 
 static unsigned int const cmCryptoHashAlgoToId[] = {
   /* clang-format needs this comment to break after the opening brace */
@@ -46,36 +45,36 @@
   rhash_free(this->CTX);
 }
 
-std::unique_ptr<cmCryptoHash> cmCryptoHash::New(const char* algo)
+std::unique_ptr<cmCryptoHash> cmCryptoHash::New(cm::string_view algo)
 {
-  if (strcmp(algo, "MD5") == 0) {
+  if (algo == "MD5") {
     return cm::make_unique<cmCryptoHash>(AlgoMD5);
   }
-  if (strcmp(algo, "SHA1") == 0) {
+  if (algo == "SHA1") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA1);
   }
-  if (strcmp(algo, "SHA224") == 0) {
+  if (algo == "SHA224") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA224);
   }
-  if (strcmp(algo, "SHA256") == 0) {
+  if (algo == "SHA256") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA256);
   }
-  if (strcmp(algo, "SHA384") == 0) {
+  if (algo == "SHA384") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA384);
   }
-  if (strcmp(algo, "SHA512") == 0) {
+  if (algo == "SHA512") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA512);
   }
-  if (strcmp(algo, "SHA3_224") == 0) {
+  if (algo == "SHA3_224") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA3_224);
   }
-  if (strcmp(algo, "SHA3_256") == 0) {
+  if (algo == "SHA3_256") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA3_256);
   }
-  if (strcmp(algo, "SHA3_384") == 0) {
+  if (algo == "SHA3_384") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA3_384);
   }
-  if (strcmp(algo, "SHA3_512") == 0) {
+  if (algo == "SHA3_512") {
     return cm::make_unique<cmCryptoHash>(AlgoSHA3_512);
   }
   return std::unique_ptr<cmCryptoHash>(nullptr);
@@ -106,6 +105,7 @@
                                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
   std::string res;
+  res.reserve(hash.size() * 2);
   for (unsigned char v : hash) {
     res.push_back(hex[v >> 4]);
     res.push_back(hex[v & 0xF]);
@@ -113,12 +113,10 @@
   return res;
 }
 
-std::vector<unsigned char> cmCryptoHash::ByteHashString(
-  const std::string& input)
+std::vector<unsigned char> cmCryptoHash::ByteHashString(cm::string_view input)
 {
   this->Initialize();
-  this->Append(reinterpret_cast<unsigned char const*>(input.c_str()),
-               static_cast<int>(input.size()));
+  this->Append(input);
   return this->Finalize();
 }
 
@@ -156,7 +154,7 @@
   return std::vector<unsigned char>();
 }
 
-std::string cmCryptoHash::HashString(const std::string& input)
+std::string cmCryptoHash::HashString(cm::string_view input)
 {
   return ByteHashToString(this->ByteHashString(input));
 }
@@ -176,9 +174,9 @@
   rhash_update(this->CTX, buf, sz);
 }
 
-void cmCryptoHash::Append(std::string const& str)
+void cmCryptoHash::Append(cm::string_view input)
 {
-  this->Append(str.c_str(), str.size());
+  rhash_update(this->CTX, input.data(), input.size());
 }
 
 std::vector<unsigned char> cmCryptoHash::Finalize()
diff --git a/Source/cmCryptoHash.h b/Source/cmCryptoHash.h
index b712f09..f27bb5d 100644
--- a/Source/cmCryptoHash.h
+++ b/Source/cmCryptoHash.h
@@ -5,11 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <memory> // IWYU pragma: keep
-#include <stddef.h>
+#include <cstddef>
+#include <memory>
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 /**
  * @brief Abstract base class for cryptographic hash generators
  */
@@ -42,7 +44,7 @@
   ///      SHA3_224, SHA3_256, SHA3_384, SHA3_512
   /// @return A valid auto pointer if algo is supported or
   ///         an invalid/NULL pointer otherwise
-  static std::unique_ptr<cmCryptoHash> New(const char* algo);
+  static std::unique_ptr<cmCryptoHash> New(cm::string_view algo);
 
   /// @brief Converts a hex character to its binary value (4 bits)
   /// @arg input Hex character [0-9a-fA-F].
@@ -55,7 +57,7 @@
 
   /// @brief Calculates a binary hash from string input data
   /// @return Binary hash vector
-  std::vector<unsigned char> ByteHashString(const std::string& input);
+  std::vector<unsigned char> ByteHashString(cm::string_view input);
 
   /// @brief Calculates a binary hash from file content
   /// @see ByteHashString()
@@ -65,7 +67,7 @@
 
   /// @brief Calculates a hash string from string input data
   /// @return Sequence of hex characters pairs for each byte of the binary hash
-  std::string HashString(const std::string& input);
+  std::string HashString(cm::string_view input);
 
   /// @brief Calculates a hash string from file content
   /// @see HashString()
@@ -75,7 +77,7 @@
 
   void Initialize();
   void Append(void const*, size_t);
-  void Append(std::string const& str);
+  void Append(cm::string_view input);
   std::vector<unsigned char> Finalize();
   std::string FinalizeHex();
 
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx
index 0004f66..233790e 100644
--- a/Source/cmCurl.cxx
+++ b/Source/cmCurl.cxx
@@ -7,6 +7,7 @@
 #  define CMAKE_FIND_CAFILE
 #  include "cmSystemTools.h"
 #endif
+#include "cmStringAlgorithms.h"
 
 // curl versions before 7.21.5 did not provide this error code
 #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x071505
@@ -72,8 +73,8 @@
     } else if (netrc_level == "IGNORED") {
       curl_netrc_level = CURL_NETRC_IGNORED;
     } else {
-      e = "NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ";
-      e += netrc_level;
+      e = cmStrCat("NETRC accepts OPTIONAL, IGNORED or REQUIRED but got: ",
+                   netrc_level);
       return e;
     }
   }
diff --git a/Source/cmCurl.h b/Source/cmCurl.h
index fe7eb80..cb73ce6 100644
--- a/Source/cmCurl.h
+++ b/Source/cmCurl.h
@@ -5,9 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_curl.h"
 #include <string>
 
+#include "cm_curl.h"
+
 std::string cmCurlSetCAInfo(::CURL* curl, const char* cafile = nullptr);
 std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level,
                                  const std::string& netrc_file);
diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx
index 7402eeb..09d269b 100644
--- a/Source/cmCustomCommand.cxx
+++ b/Source/cmCustomCommand.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCustomCommand.h"
 
+#include <utility>
+
 #include "cmAlgorithms.h"
 #include "cmMakefile.h"
 
-#include <utility>
-
 cmCustomCommand::cmCustomCommand(cmMakefile const* mf,
                                  std::vector<std::string> outputs,
                                  std::vector<std::string> byproducts,
@@ -88,18 +88,17 @@
   return this->Backtrace;
 }
 
-cmCustomCommand::ImplicitDependsList const&
-cmCustomCommand::GetImplicitDepends() const
+cmImplicitDependsList const& cmCustomCommand::GetImplicitDepends() const
 {
   return this->ImplicitDepends;
 }
 
-void cmCustomCommand::SetImplicitDepends(ImplicitDependsList const& l)
+void cmCustomCommand::SetImplicitDepends(cmImplicitDependsList const& l)
 {
   this->ImplicitDepends = l;
 }
 
-void cmCustomCommand::AppendImplicitDepends(ImplicitDependsList const& l)
+void cmCustomCommand::AppendImplicitDepends(cmImplicitDependsList const& l)
 {
   cmAppend(this->ImplicitDepends, l);
 }
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index 9a6f239..4689ace 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -5,15 +5,20 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmCustomCommandLines.h"
-#include "cmListFileCache.h"
-
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmCustomCommandLines.h"
+#include "cmListFileCache.h"
+
 class cmMakefile;
 
+class cmImplicitDependsList
+  : public std::vector<std::pair<std::string, std::string>>
+{
+};
+
 /** \class cmCustomCommand
  * \brief A class to encapsulate a custom command
  *
@@ -68,13 +73,9 @@
   /** Backtrace of the command that created this custom command.  */
   cmListFileBacktrace const& GetBacktrace() const;
 
-  typedef std::pair<std::string, std::string> ImplicitDependsPair;
-  class ImplicitDependsList : public std::vector<ImplicitDependsPair>
-  {
-  };
-  void SetImplicitDepends(ImplicitDependsList const&);
-  void AppendImplicitDepends(ImplicitDependsList const&);
-  ImplicitDependsList const& GetImplicitDepends() const;
+  void SetImplicitDepends(cmImplicitDependsList const&);
+  void AppendImplicitDepends(cmImplicitDependsList const&);
+  cmImplicitDependsList const& GetImplicitDepends() const;
 
   /** Set/Get whether this custom command should be given access to the
       real console (if possible).  */
@@ -99,7 +100,7 @@
   std::vector<std::string> Depends;
   cmCustomCommandLines CommandLines;
   cmListFileBacktrace Backtrace;
-  ImplicitDependsList ImplicitDepends;
+  cmImplicitDependsList ImplicitDepends;
   std::string Comment;
   std::string WorkingDirectory;
   std::string Depfile;
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index fe228ff..c1f412d 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -2,6 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCustomCommandGenerator.h"
 
+#include <cstddef>
+#include <memory>
+#include <utility>
+
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
@@ -10,11 +14,28 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <memory> // IWYU pragma: keep
-#include <stddef.h>
-#include <utility>
+namespace {
+void AppendPaths(const std::vector<std::string>& inputs,
+                 cmGeneratorExpression const& ge, cmLocalGenerator* lg,
+                 std::string const& config, std::vector<std::string>& output)
+{
+  for (std::string const& in : inputs) {
+    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(in);
+    std::vector<std::string> result =
+      cmExpandedList(cge->Evaluate(lg, config));
+    for (std::string& it : result) {
+      cmSystemTools::ConvertToUnixSlashes(it);
+      if (cmSystemTools::FileIsFullPath(it)) {
+        it = cmSystemTools::CollapseFullPath(it);
+      }
+    }
+    cmAppend(output, result);
+  }
+}
+}
 
 cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
                                                    std::string config,
@@ -24,18 +45,18 @@
   , LG(lg)
   , OldStyle(cc.GetEscapeOldStyle())
   , MakeVars(cc.GetEscapeAllowMakeVars())
-  , GE(new cmGeneratorExpression(cc.GetBacktrace()))
   , EmulatorsWithArguments(cc.GetCommandLines().size())
 {
+  cmGeneratorExpression ge(cc.GetBacktrace());
+
   const cmCustomCommandLines& cmdlines = this->CC.GetCommandLines();
   for (cmCustomCommandLine const& cmdline : cmdlines) {
     cmCustomCommandLine argv;
     for (std::string const& clarg : cmdline) {
-      std::unique_ptr<cmCompiledGeneratorExpression> cge =
-        this->GE->Parse(clarg);
+      std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(clarg);
       std::string parsed_arg = cge->Evaluate(this->LG, this->Config);
       if (this->CC.GetCommandExpandLists()) {
-        cmAppend(argv, cmSystemTools::ExpandedListArgument(parsed_arg));
+        cmAppend(argv, cmExpandedList(parsed_arg));
       } else {
         argv.push_back(std::move(parsed_arg));
       }
@@ -45,29 +66,20 @@
     // lists on an empty command may have left this empty.
     // FIXME: Should we define behavior for removing empty commands?
     if (argv.empty()) {
-      argv.push_back(std::string());
+      argv.emplace_back();
     }
 
     this->CommandLines.push_back(std::move(argv));
   }
 
-  std::vector<std::string> depends = this->CC.GetDepends();
-  for (std::string const& d : depends) {
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = this->GE->Parse(d);
-    std::vector<std::string> result = cmSystemTools::ExpandedListArgument(
-      cge->Evaluate(this->LG, this->Config));
-    for (std::string& it : result) {
-      if (cmSystemTools::FileIsFullPath(it)) {
-        it = cmSystemTools::CollapseFullPath(it);
-      }
-    }
-    cmAppend(this->Depends, result);
-  }
+  AppendPaths(cc.GetByproducts(), ge, this->LG, this->Config,
+              this->Byproducts);
+  AppendPaths(cc.GetDepends(), ge, this->LG, this->Config, this->Depends);
 
   const std::string& workingdirectory = this->CC.GetWorkingDirectory();
   if (!workingdirectory.empty()) {
     std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      this->GE->Parse(workingdirectory);
+      ge.Parse(workingdirectory);
     this->WorkingDirectory = cge->Evaluate(this->LG, this->Config);
     // Convert working directory to a full path.
     if (!this->WorkingDirectory.empty()) {
@@ -80,11 +92,6 @@
   this->FillEmulatorsWithArguments();
 }
 
-cmCustomCommandGenerator::~cmCustomCommandGenerator()
-{
-  delete this->GE;
-}
-
 unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const
 {
   return static_cast<unsigned int>(this->CC.GetCommandLines().size());
@@ -108,8 +115,7 @@
         continue;
       }
 
-      cmSystemTools::ExpandListArgument(emulator_property,
-                                        this->EmulatorsWithArguments[c]);
+      cmExpandList(emulator_property, this->EmulatorsWithArguments[c]);
     }
   }
 }
@@ -169,9 +175,7 @@
   std::string temp = str;
   if (temp.find(" ") != std::string::npos &&
       temp.find("\"") == std::string::npos) {
-    result = "\"";
-    result += str;
-    result += "\"";
+    result = cmStrCat('"', str, '"');
     return result;
   }
   return str;
@@ -240,7 +244,7 @@
 
 std::vector<std::string> const& cmCustomCommandGenerator::GetByproducts() const
 {
-  return this->CC.GetByproducts();
+  return this->Byproducts;
 }
 
 std::vector<std::string> const& cmCustomCommandGenerator::GetDepends() const
diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h
index 766f4b8..67ee9e0 100644
--- a/Source/cmCustomCommandGenerator.h
+++ b/Source/cmCustomCommandGenerator.h
@@ -4,13 +4,13 @@
 #define cmCustomCommandGenerator_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmCustomCommandLines.h"
 
 #include <string>
 #include <vector>
 
+#include "cmCustomCommandLines.h"
+
 class cmCustomCommand;
-class cmGeneratorExpression;
 class cmLocalGenerator;
 
 class cmCustomCommandGenerator
@@ -20,9 +20,9 @@
   cmLocalGenerator* LG;
   bool OldStyle;
   bool MakeVars;
-  cmGeneratorExpression* GE;
   cmCustomCommandLines CommandLines;
   std::vector<std::vector<std::string>> EmulatorsWithArguments;
+  std::vector<std::string> Byproducts;
   std::vector<std::string> Depends;
   std::string WorkingDirectory;
 
@@ -33,7 +33,6 @@
 public:
   cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config,
                            cmLocalGenerator* lg);
-  ~cmCustomCommandGenerator();
   cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete;
   cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) =
     delete;
diff --git a/Source/cmCustomCommandLines.cxx b/Source/cmCustomCommandLines.cxx
new file mode 100644
index 0000000..37ad75b
--- /dev/null
+++ b/Source/cmCustomCommandLines.cxx
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCustomCommandLines.h"
+
+cmCustomCommandLine cmMakeCommandLine(
+  std::initializer_list<cm::string_view> ilist)
+{
+  cmCustomCommandLine commandLine;
+  commandLine.reserve(ilist.size());
+  for (cm::string_view cmd : ilist) {
+    commandLine.emplace_back(cmd);
+  }
+  return commandLine;
+}
+
+cmCustomCommandLines cmMakeSingleCommandLine(
+  std::initializer_list<cm::string_view> ilist)
+{
+  cmCustomCommandLines commandLines;
+  commandLines.push_back(cmMakeCommandLine(ilist));
+  return commandLines;
+}
diff --git a/Source/cmCustomCommandLines.h b/Source/cmCustomCommandLines.h
index 36838f2..ead5792 100644
--- a/Source/cmCustomCommandLines.h
+++ b/Source/cmCustomCommandLines.h
@@ -5,25 +5,28 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <initializer_list>
 #include <string>
 #include <vector>
 
+#include <cm/string_view> // IWYU pragma: keep
+
 /** Data structure to represent a single command line.  */
 class cmCustomCommandLine : public std::vector<std::string>
 {
-public:
-  typedef std::vector<std::string> Superclass;
-  typedef Superclass::iterator iterator;
-  typedef Superclass::const_iterator const_iterator;
 };
 
 /** Data structure to represent a list of command lines.  */
 class cmCustomCommandLines : public std::vector<cmCustomCommandLine>
 {
-public:
-  typedef std::vector<cmCustomCommandLine> Superclass;
-  typedef Superclass::iterator iterator;
-  typedef Superclass::const_iterator const_iterator;
 };
 
+/** Return a command line from a list of command line parts.  */
+cmCustomCommandLine cmMakeCommandLine(
+  std::initializer_list<cm::string_view> ilist);
+
+/** Return a command line vector with a single command line.  */
+cmCustomCommandLines cmMakeSingleCommandLine(
+  std::initializer_list<cm::string_view> ilist);
+
 #endif
diff --git a/Source/cmCustomCommandTypes.h b/Source/cmCustomCommandTypes.h
new file mode 100644
index 0000000..d4bf1f9
--- /dev/null
+++ b/Source/cmCustomCommandTypes.h
@@ -0,0 +1,39 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCustomCommandTypes_h
+#define cmCustomCommandTypes_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+/** Target custom command type */
+enum class cmCustomCommandType
+{
+  PRE_BUILD,
+  PRE_LINK,
+  POST_BUILD
+};
+
+/** Where the command originated from. */
+enum class cmCommandOrigin
+{
+  Project,
+  Generator
+};
+
+/** How to handle custom commands for object libraries */
+enum class cmObjectLibraryCommands
+{
+  Reject,
+  Accept
+};
+
+/** Utility target output source file name.  */
+struct cmUtilityOutput
+{
+  std::string Name;
+  std::string NameCMP0049;
+};
+
+#endif
diff --git a/Source/cmDefinePropertyCommand.cxx b/Source/cmDefinePropertyCommand.cxx
index 86a83da..f4e4fda 100644
--- a/Source/cmDefinePropertyCommand.cxx
+++ b/Source/cmDefinePropertyCommand.cxx
@@ -2,19 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDefinePropertyCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmProperty.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 
-class cmExecutionStatus;
-
-bool cmDefinePropertyCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmDefinePropertyCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -37,17 +35,17 @@
   } else if (scope_arg == "CACHED_VARIABLE") {
     scope = cmProperty::CACHED_VARIABLE;
   } else {
-    std::ostringstream e;
-    e << "given invalid scope " << scope_arg << ".  "
-      << "Valid scopes are "
-      << "GLOBAL, DIRECTORY, TARGET, SOURCE, "
-      << "TEST, VARIABLE, CACHED_VARIABLE.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat("given invalid scope ", scope_arg,
+                             ".  Valid scopes are GLOBAL, DIRECTORY, TARGET, "
+                             "SOURCE, TEST, VARIABLE, CACHED_VARIABLE."));
     return false;
   }
 
   // Parse remaining arguments.
   bool inherited = false;
+  std::string PropertyName;
+  std::string BriefDocs;
+  std::string FullDocs;
   enum Doing
   {
     DoingNone,
@@ -68,39 +66,36 @@
       inherited = true;
     } else if (doing == DoingProperty) {
       doing = DoingNone;
-      this->PropertyName = args[i];
+      PropertyName = args[i];
     } else if (doing == DoingBrief) {
-      this->BriefDocs += args[i];
+      BriefDocs += args[i];
     } else if (doing == DoingFull) {
-      this->FullDocs += args[i];
+      FullDocs += args[i];
     } else {
-      std::ostringstream e;
-      e << "given invalid argument \"" << args[i] << "\".";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("given invalid argument \"", args[i], "\"."));
       return false;
     }
   }
 
   // Make sure a property name was found.
-  if (this->PropertyName.empty()) {
-    this->SetError("not given a PROPERTY <name> argument.");
+  if (PropertyName.empty()) {
+    status.SetError("not given a PROPERTY <name> argument.");
     return false;
   }
 
   // Make sure documentation was given.
-  if (this->BriefDocs.empty()) {
-    this->SetError("not given a BRIEF_DOCS <brief-doc> argument.");
+  if (BriefDocs.empty()) {
+    status.SetError("not given a BRIEF_DOCS <brief-doc> argument.");
     return false;
   }
-  if (this->FullDocs.empty()) {
-    this->SetError("not given a FULL_DOCS <full-doc> argument.");
+  if (FullDocs.empty()) {
+    status.SetError("not given a FULL_DOCS <full-doc> argument.");
     return false;
   }
 
   // Actually define the property.
-  this->Makefile->GetState()->DefineProperty(
-    this->PropertyName, scope, this->BriefDocs.c_str(), this->FullDocs.c_str(),
-    inherited);
+  status.GetMakefile().GetState()->DefineProperty(
+    PropertyName, scope, BriefDocs.c_str(), FullDocs.c_str(), inherited);
 
   return true;
 }
diff --git a/Source/cmDefinePropertyCommand.h b/Source/cmDefinePropertyCommand.h
index a9c1856..60dd76a 100644
--- a/Source/cmDefinePropertyCommand.h
+++ b/Source/cmDefinePropertyCommand.h
@@ -8,26 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmDefinePropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmDefinePropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  std::string PropertyName;
-  std::string BriefDocs;
-  std::string FullDocs;
-};
+bool cmDefinePropertyCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index 5fafaf9..69a6427 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -2,10 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDefinitions.h"
 
-#include <assert.h>
-#include <set>
+#include <cassert>
+#include <functional>
+#include <unordered_set>
 #include <utility>
 
+#include <cm/string_view>
+
 cmDefinitions::Def cmDefinitions::NoDef;
 
 cmDefinitions::Def const& cmDefinitions::GetInternal(const std::string& key,
@@ -13,10 +16,12 @@
                                                      StackIter end, bool raise)
 {
   assert(begin != end);
-  MapType::iterator i = begin->Map.find(key);
-  if (i != begin->Map.end()) {
-    i->second.Used = true;
-    return i->second;
+  {
+    auto it = begin->Map.find(cm::String::borrow(key));
+    if (it != begin->Map.end()) {
+      it->second.Used = true;
+      return it->second;
+    }
   }
   StackIter it = begin;
   ++it;
@@ -27,14 +32,14 @@
   if (!raise) {
     return def;
   }
-  return begin->Map.insert(MapType::value_type(key, def)).first->second;
+  return begin->Map.emplace(key, def).first->second;
 }
 
 const std::string* cmDefinitions::Get(const std::string& key, StackIter begin,
                                       StackIter end)
 {
   Def const& def = cmDefinitions::GetInternal(key, begin, end, false);
-  return def.Exists ? &def : nullptr;
+  return def.Value ? def.Value.str_if_stable() : nullptr;
 }
 
 void cmDefinitions::Raise(const std::string& key, StackIter begin,
@@ -47,47 +52,27 @@
                            StackIter end)
 {
   for (StackIter it = begin; it != end; ++it) {
-    MapType::const_iterator i = it->Map.find(key);
-    if (i != it->Map.end()) {
+    if (it->Map.find(cm::String::borrow(key)) != it->Map.end()) {
       return true;
     }
   }
   return false;
 }
 
-void cmDefinitions::Set(const std::string& key, const char* value)
-{
-  Def def(value);
-  this->Map[key] = def;
-}
-
-std::vector<std::string> cmDefinitions::UnusedKeys() const
-{
-  std::vector<std::string> keys;
-  keys.reserve(this->Map.size());
-  // Consider local definitions.
-  for (auto const& mi : this->Map) {
-    if (!mi.second.Used) {
-      keys.push_back(mi.first);
-    }
-  }
-  return keys;
-}
-
 cmDefinitions cmDefinitions::MakeClosure(StackIter begin, StackIter end)
 {
   cmDefinitions closure;
-  std::set<std::string> undefined;
+  std::unordered_set<cm::string_view> undefined;
   for (StackIter it = begin; it != end; ++it) {
     // Consider local definitions.
     for (auto const& mi : it->Map) {
       // Use this key if it is not already set or unset.
       if (closure.Map.find(mi.first) == closure.Map.end() &&
-          undefined.find(mi.first) == undefined.end()) {
-        if (mi.second.Exists) {
+          undefined.find(mi.first.view()) == undefined.end()) {
+        if (mi.second.Value) {
           closure.Map.insert(mi);
         } else {
-          undefined.insert(mi.first);
+          undefined.emplace(mi.first.view());
         }
       }
     }
@@ -98,18 +83,41 @@
 std::vector<std::string> cmDefinitions::ClosureKeys(StackIter begin,
                                                     StackIter end)
 {
-  std::set<std::string> bound;
   std::vector<std::string> defined;
+  std::unordered_set<cm::string_view> bound;
 
   for (StackIter it = begin; it != end; ++it) {
     defined.reserve(defined.size() + it->Map.size());
     for (auto const& mi : it->Map) {
       // Use this key if it is not already set or unset.
-      if (bound.insert(mi.first).second && mi.second.Exists) {
-        defined.push_back(mi.first);
+      if (bound.emplace(mi.first.view()).second && mi.second.Value) {
+        defined.push_back(*mi.first.str_if_stable());
       }
     }
   }
 
   return defined;
 }
+
+void cmDefinitions::Set(const std::string& key, cm::string_view value)
+{
+  this->Map[key] = Def(value);
+}
+
+void cmDefinitions::Unset(const std::string& key)
+{
+  this->Map[key] = Def();
+}
+
+std::vector<std::string> cmDefinitions::UnusedKeys() const
+{
+  std::vector<std::string> keys;
+  keys.reserve(this->Map.size());
+  // Consider local definitions.
+  for (auto const& mi : this->Map) {
+    if (!mi.second.Used) {
+      keys.push_back(*mi.first.str_if_stable());
+    }
+  }
+  return keys;
+}
diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h
index 6c252be..0e38fb1 100644
--- a/Source/cmDefinitions.h
+++ b/Source/cmDefinitions.h
@@ -5,11 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <functional>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmLinkedTree.h"
+#include "cmString.hxx"
 
 /** \class cmDefinitions
  * \brief Store a scope of variable definitions for CMake language.
@@ -20,9 +24,11 @@
  */
 class cmDefinitions
 {
-  typedef cmLinkedTree<cmDefinitions>::iterator StackIter;
+  using StackIter = cmLinkedTree<cmDefinitions>::iterator;
 
 public:
+  // -- Static member functions
+
   static const std::string* Get(const std::string& key, StackIter begin,
                                 StackIter end);
 
@@ -30,41 +36,37 @@
 
   static bool HasKey(const std::string& key, StackIter begin, StackIter end);
 
-  /** Set (or unset if null) a value associated with a key.  */
-  void Set(const std::string& key, const char* value);
-
-  std::vector<std::string> UnusedKeys() const;
-
   static std::vector<std::string> ClosureKeys(StackIter begin, StackIter end);
 
   static cmDefinitions MakeClosure(StackIter begin, StackIter end);
 
-private:
-  // String with existence boolean.
-  struct Def : public std::string
-  {
-  private:
-    typedef std::string std_string;
+  // -- Member functions
 
+  /** Set a value associated with a key.  */
+  void Set(const std::string& key, cm::string_view value);
+
+  /** Unset a definition.  */
+  void Unset(const std::string& key);
+
+  /** List of unused keys.  */
+  std::vector<std::string> UnusedKeys() const;
+
+private:
+  /** String with existence boolean.  */
+  struct Def
+  {
   public:
     Def() = default;
-    Def(const char* v)
-      : std_string(v ? v : "")
-      , Exists(v ? true : false)
+    Def(cm::string_view value)
+      : Value(value)
     {
     }
-    Def(const std_string& v)
-      : std_string(v)
-      , Exists(true)
-    {
-    }
-    bool Exists = false;
+    cm::String Value;
     bool Used = false;
   };
   static Def NoDef;
 
-  typedef std::unordered_map<std::string, Def> MapType;
-  MapType Map;
+  std::unordered_map<cm::String, Def> Map;
 
   static Def const& GetInternal(const std::string& key, StackIter begin,
                                 StackIter end, bool raise);
diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx
index ed76dbf..129a5f7 100644
--- a/Source/cmDepends.cxx
+++ b/Source/cmDepends.cxx
@@ -2,17 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDepends.h"
 
+#include <sstream>
+#include <utility>
+
+#include "cmsys/FStream.hxx"
+
 #include "cmFileTime.h"
 #include "cmFileTimeCache.h"
 #include "cmGeneratedFileStream.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/FStream.hxx"
-#include <sstream>
-#include <utility>
-
 cmDepends::cmDepends(cmLocalGenerator* lg, std::string targetDir)
   : LocalGenerator(lg)
   , TargetDirectory(std::move(targetDir))
@@ -30,10 +32,9 @@
     {
       std::string const srcLang = "CMAKE_DEPENDS_CHECK_" + this->Language;
       cmMakefile* mf = this->LocalGenerator->GetMakefile();
-      cmSystemTools::ExpandListArgument(mf->GetSafeDefinition(srcLang), pairs);
+      cmExpandList(mf->GetSafeDefinition(srcLang), pairs);
     }
-    for (std::vector<std::string>::iterator si = pairs.begin();
-         si != pairs.end();) {
+    for (auto si = pairs.begin(); si != pairs.end();) {
       // Get the source and object file.
       std::string const& src = *si++;
       if (si == pairs.end()) {
@@ -235,21 +236,18 @@
 {
   // Look for the new per "TARGET_" variant first:
   const char* includePath = nullptr;
-  std::string includePathVar = "CMAKE_";
-  includePathVar += lang;
-  includePathVar += "_TARGET_INCLUDE_PATH";
+  std::string includePathVar =
+    cmStrCat("CMAKE_", lang, "_TARGET_INCLUDE_PATH");
   cmMakefile* mf = this->LocalGenerator->GetMakefile();
   includePath = mf->GetDefinition(includePathVar);
   if (includePath) {
-    cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
+    cmExpandList(includePath, this->IncludePath);
   } else {
     // Fallback to the old directory level variable if no per-target var:
-    includePathVar = "CMAKE_";
-    includePathVar += lang;
-    includePathVar += "_INCLUDE_PATH";
+    includePathVar = cmStrCat("CMAKE_", lang, "_INCLUDE_PATH");
     includePath = mf->GetDefinition(includePathVar);
     if (includePath) {
-      cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
+      cmExpandList(includePath, this->IncludePath);
     }
   }
 }
diff --git a/Source/cmDepends.h b/Source/cmDepends.h
index b7475f0..d938775 100644
--- a/Source/cmDepends.h
+++ b/Source/cmDepends.h
@@ -24,7 +24,7 @@
 class cmDepends
 {
 public:
-  typedef std::map<std::string, std::vector<std::string>> DependencyMap;
+  using DependencyMap = std::map<std::string, std::vector<std::string>>;
 
 public:
   /** Instances need to know the build directory name and the relative
diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx
index dc49c18..e30d959 100644
--- a/Source/cmDependsC.cxx
+++ b/Source/cmDependsC.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDependsC.h"
 
-#include "cmsys/FStream.hxx"
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include "cmsys/FStream.hxx"
+
 #include "cmFileTime.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 #define INCLUDE_REGEX_LINE                                                    \
@@ -35,15 +36,12 @@
   std::string scanRegex = "^.*$";
   std::string complainRegex = "^$";
   {
-    std::string scanRegexVar = "CMAKE_";
-    scanRegexVar += lang;
-    scanRegexVar += "_INCLUDE_REGEX_SCAN";
+    std::string scanRegexVar = cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_SCAN");
     if (const char* sr = mf->GetDefinition(scanRegexVar)) {
       scanRegex = sr;
     }
-    std::string complainRegexVar = "CMAKE_";
-    complainRegexVar += lang;
-    complainRegexVar += "_INCLUDE_REGEX_COMPLAIN";
+    std::string complainRegexVar =
+      cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_COMPLAIN");
     if (const char* cr = mf->GetDefinition(complainRegexVar)) {
       complainRegex = cr;
     }
@@ -53,17 +51,15 @@
   this->IncludeRegexScan.compile(scanRegex);
   this->IncludeRegexComplain.compile(complainRegex);
   this->IncludeRegexLineString = INCLUDE_REGEX_LINE_MARKER INCLUDE_REGEX_LINE;
-  this->IncludeRegexScanString = INCLUDE_REGEX_SCAN_MARKER;
-  this->IncludeRegexScanString += scanRegex;
-  this->IncludeRegexComplainString = INCLUDE_REGEX_COMPLAIN_MARKER;
-  this->IncludeRegexComplainString += complainRegex;
+  this->IncludeRegexScanString =
+    cmStrCat(INCLUDE_REGEX_SCAN_MARKER, scanRegex);
+  this->IncludeRegexComplainString =
+    cmStrCat(INCLUDE_REGEX_COMPLAIN_MARKER, complainRegex);
 
   this->SetupTransforms();
 
-  this->CacheFileName = this->TargetDirectory;
-  this->CacheFileName += "/";
-  this->CacheFileName += lang;
-  this->CacheFileName += ".includecache";
+  this->CacheFileName =
+    cmStrCat(this->TargetDirectory, '/', lang, ".includecache");
 
   this->ReadCacheFile();
 }
@@ -71,7 +67,6 @@
 cmDependsC::~cmDependsC()
 {
   this->WriteCacheFile();
-  cmDeleteAll(this->FileCache);
 }
 
 bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
@@ -176,9 +171,9 @@
         // Check whether this file is already in the cache
         auto fileIt = this->FileCache.find(fullName);
         if (fileIt != this->FileCache.end()) {
-          fileIt->second->Used = true;
+          fileIt->second.Used = true;
           dependencies.insert(fullName);
-          for (UnscannedEntry const& inc : fileIt->second->UnscannedEntries) {
+          for (UnscannedEntry const& inc : fileIt->second.UnscannedEntries) {
             if (this->Encountered.find(inc.FileName) ==
                 this->Encountered.end()) {
               this->Encountered.insert(inc.FileName);
@@ -264,8 +259,7 @@
 
       if (res && newer) // cache is newer than the parsed file
       {
-        cacheEntry = new cmIncludeLines;
-        this->FileCache[line] = cacheEntry;
+        cacheEntry = &this->FileCache[line];
       }
       // file doesn't exist, check that the regular expressions
       // haven't changed
@@ -317,10 +311,10 @@
   cacheOut << this->IncludeRegexTransformString << "\n\n";
 
   for (auto const& fileIt : this->FileCache) {
-    if (fileIt.second->Used) {
+    if (fileIt.second.Used) {
       cacheOut << fileIt.first << std::endl;
 
-      for (UnscannedEntry const& inc : fileIt.second->UnscannedEntries) {
+      for (UnscannedEntry const& inc : fileIt.second.UnscannedEntries) {
         cacheOut << inc.FileName << std::endl;
         if (inc.QuotedLocation.empty()) {
           cacheOut << "-" << std::endl;
@@ -336,9 +330,8 @@
 void cmDependsC::Scan(std::istream& is, const std::string& directory,
                       const std::string& fullName)
 {
-  cmIncludeLines* newCacheEntry = new cmIncludeLines;
-  newCacheEntry->Used = true;
-  this->FileCache[fullName] = newCacheEntry;
+  cmIncludeLines& newCacheEntry = this->FileCache[fullName];
+  newCacheEntry.Used = true;
 
   // Read one line at a time.
   std::string line;
@@ -374,7 +367,7 @@
       // This kind of problem will be fixed when a more
       // preprocessor-like implementation of this scanner is created.
       if (this->IncludeRegexScan.find(entry.FileName)) {
-        newCacheEntry->UnscannedEntries.push_back(entry);
+        newCacheEntry.UnscannedEntries.push_back(entry);
         if (this->Encountered.find(entry.FileName) ==
             this->Encountered.end()) {
           this->Encountered.insert(entry.FileName);
@@ -391,7 +384,7 @@
   std::vector<std::string> transformRules;
   cmMakefile* mf = this->LocalGenerator->GetMakefile();
   if (const char* xform = mf->GetDefinition("CMAKE_INCLUDE_TRANSFORMS")) {
-    cmSystemTools::ExpandListArgument(xform, transformRules, true);
+    cmExpandList(xform, transformRules, true);
   }
   for (std::string const& tr : transformRules) {
     this->ParseTransform(tr);
@@ -442,8 +435,7 @@
   if (!this->IncludeRegexTransform.find(line)) {
     return;
   }
-  TransformRulesType::const_iterator tri =
-    this->TransformRules.find(this->IncludeRegexTransform.match(3));
+  auto tri = this->TransformRules.find(this->IncludeRegexTransform.match(3));
   if (tri == this->TransformRules.end()) {
     return;
   }
diff --git a/Source/cmDependsC.h b/Source/cmDependsC.h
index 3fc839e..3bb6e36 100644
--- a/Source/cmDependsC.h
+++ b/Source/cmDependsC.h
@@ -5,16 +5,18 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmDepends.h"
-
-#include "cmsys/RegularExpression.hxx"
 #include <iosfwd>
 #include <map>
-#include <queue>
 #include <set>
 #include <string>
 #include <vector>
 
+#include <queue>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmDepends.h"
+
 class cmLocalGenerator;
 
 /** \class cmDependsC
@@ -59,7 +61,7 @@
   // Regex to transform #include lines.
   std::string IncludeRegexTransformString;
   cmsys::RegularExpression IncludeRegexTransform;
-  typedef std::map<std::string, std::string> TransformRulesType;
+  using TransformRulesType = std::map<std::string, std::string>;
   TransformRulesType TransformRules;
   void SetupTransforms();
   void ParseTransform(std::string const& xform);
@@ -84,7 +86,7 @@
   std::set<std::string> Encountered;
   std::queue<UnscannedEntry> Unscanned;
 
-  std::map<std::string, cmIncludeLines*> FileCache;
+  std::map<std::string, cmIncludeLines> FileCache;
   std::map<std::string, std::string> HeaderLocationCache;
 
   std::string CacheFileName;
diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx
index 178e18b..ee43587 100644
--- a/Source/cmDependsFortran.cxx
+++ b/Source/cmDependsFortran.cxx
@@ -2,14 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDependsFortran.h"
 
-#include "cmsys/FStream.hxx"
-#include <assert.h>
+#include <cassert>
+#include <cstdlib>
 #include <iostream>
 #include <map>
-#include <stdlib.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include "cmsys/FStream.hxx"
+
 #include "cmFortranParser.h" /* Interface to parser object.  */
 #include "cmGeneratedFileStream.h"
 #include "cmLocalGenerator.h"
@@ -17,6 +17,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
@@ -46,18 +47,17 @@
   std::set<std::string> TargetProvides;
 
   // Map modules required by this target to locations.
-  typedef std::map<std::string, std::string> TargetRequiresMap;
+  using TargetRequiresMap = std::map<std::string, std::string>;
   TargetRequiresMap TargetRequires;
 
   // Information about each object file.
-  typedef std::map<std::string, cmFortranSourceInfo> ObjectInfoMap;
+  using ObjectInfoMap = std::map<std::string, cmFortranSourceInfo>;
   ObjectInfoMap ObjectInfo;
 
   cmFortranSourceInfo& CreateObjectInfo(const std::string& obj,
                                         const std::string& src)
   {
-    std::map<std::string, cmFortranSourceInfo>::iterator i =
-      this->ObjectInfo.find(obj);
+    auto i = this->ObjectInfo.find(obj);
     if (i == this->ObjectInfo.end()) {
       std::map<std::string, cmFortranSourceInfo>::value_type entry(
         obj, cmFortranSourceInfo());
@@ -82,7 +82,7 @@
   cmMakefile* mf = this->LocalGenerator->GetMakefile();
   if (const char* c_defines =
         mf->GetDefinition("CMAKE_TARGET_DEFINITIONS_Fortran")) {
-    cmSystemTools::ExpandListArgument(c_defines, definitions);
+    cmExpandList(c_defines, definitions);
   }
 
   // translate i.e. FOO=BAR to FOO and add it to the list of defined
@@ -170,7 +170,7 @@
   }
 
   // Actually write dependencies to the streams.
-  typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
+  using ObjectInfoMap = cmDependsFortranInternals::ObjectInfoMap;
   ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
   for (auto const& i : objInfo) {
     if (!this->WriteDependenciesReal(i.first, i.second, mod_dir, stamp_dir,
@@ -180,8 +180,7 @@
   }
 
   // Store the list of modules provided by this target.
-  std::string fiName = this->TargetDirectory;
-  fiName += "/fortran.internal";
+  std::string fiName = cmStrCat(this->TargetDirectory, "/fortran.internal");
   cmGeneratedFileStream fiStream(fiName);
   fiStream << "# The fortran modules provided by this target.\n";
   fiStream << "provides\n";
@@ -192,23 +191,18 @@
 
   // Create a script to clean the modules.
   if (!provides.empty()) {
-    std::string fcName = this->TargetDirectory;
-    fcName += "/cmake_clean_Fortran.cmake";
+    std::string fcName =
+      cmStrCat(this->TargetDirectory, "/cmake_clean_Fortran.cmake");
     cmGeneratedFileStream fcStream(fcName);
     fcStream << "# Remove fortran modules provided by this target.\n";
     fcStream << "FILE(REMOVE";
     std::string currentBinDir =
       this->LocalGenerator->GetCurrentBinaryDirectory();
     for (std::string const& i : provides) {
-      std::string mod_upper = mod_dir;
-      mod_upper += "/";
-      std::string mod_lower = mod_dir;
-      mod_lower += "/";
+      std::string mod_upper = cmStrCat(mod_dir, '/');
+      std::string mod_lower = cmStrCat(mod_dir, '/');
       cmFortranModuleAppendUpperLower(i, mod_upper, mod_lower);
-      std::string stamp = stamp_dir;
-      stamp += "/";
-      stamp += i;
-      stamp += ".stamp";
+      std::string stamp = cmStrCat(stamp_dir, '/', i, ".stamp");
       fcStream << "\n";
       fcStream << "  \""
                << this->MaybeConvertToRelativePath(currentBinDir, mod_lower)
@@ -228,7 +222,7 @@
 void cmDependsFortran::LocateModules()
 {
   // Collect the set of modules provided and required by all sources.
-  typedef cmDependsFortranInternals::ObjectInfoMap ObjectInfoMap;
+  using ObjectInfoMap = cmDependsFortranInternals::ObjectInfoMap;
   ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
   for (auto const& infoI : objInfo) {
     cmFortranSourceInfo const& info = infoI.second;
@@ -254,7 +248,7 @@
   std::vector<std::string> infoFiles;
   if (const char* infoFilesValue =
         mf->GetDefinition("CMAKE_TARGET_LINKED_INFO_FILES")) {
-    cmSystemTools::ExpandListArgument(infoFilesValue, infoFiles);
+    cmExpandList(infoFilesValue, infoFiles);
   }
   for (std::string const& i : infoFiles) {
     std::string targetDir = cmSystemTools::GetFilenamePath(i);
@@ -309,16 +303,11 @@
                                       const std::string& stampDir)
 {
   // Locate each required module.
-  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
-  TargetRequiresMap::iterator required =
-    this->Internal->TargetRequires.find(name);
+  auto required = this->Internal->TargetRequires.find(name);
   if (required != this->Internal->TargetRequires.end() &&
       required->second.empty()) {
     // The module is provided by a CMake target.  It will have a stamp file.
-    std::string stampFile = stampDir;
-    stampFile += "/";
-    stampFile += name;
-    stampFile += ".stamp";
+    std::string stampFile = cmStrCat(stampDir, '/', name, ".stamp");
     required->second = stampFile;
   }
 }
@@ -330,8 +319,6 @@
                                              std::ostream& makeDepends,
                                              std::ostream& internalDepends)
 {
-  typedef cmDependsFortranInternals::TargetRequiresMap TargetRequiresMap;
-
   // Get the source file for this object.
   std::string const& src = info.Source;
 
@@ -359,8 +346,7 @@
 
     // The object file should depend on timestamped files for the
     // modules it uses.
-    TargetRequiresMap::const_iterator required =
-      this->Internal->TargetRequires.find(i);
+    auto required = this->Internal->TargetRequires.find(i);
     if (required == this->Internal->TargetRequires.end()) {
       abort();
     }
@@ -392,16 +378,11 @@
       // Always use lower case for the mod stamp file name.  The
       // cmake_copy_f90_mod will call back to this class, which will
       // try various cases for the real mod file name.
-      std::string modFile = mod_dir;
-      modFile += "/";
-      modFile += i;
+      std::string modFile = cmStrCat(mod_dir, '/', i);
       modFile = this->LocalGenerator->ConvertToOutputFormat(
         this->MaybeConvertToRelativePath(binDir, modFile),
         cmOutputConverter::SHELL);
-      std::string stampFile = stamp_dir;
-      stampFile += "/";
-      stampFile += i;
-      stampFile += ".stamp";
+      std::string stampFile = cmStrCat(stamp_dir, '/', i, ".stamp");
       stampFile = this->MaybeConvertToRelativePath(binDir, stampFile);
       std::string const stampFileForShell =
         this->LocalGenerator->ConvertToOutputFormat(stampFile,
@@ -435,8 +416,7 @@
 
     // Make sure the module timestamp rule is evaluated by the time
     // the target finishes building.
-    std::string driver = this->TargetDirectory;
-    driver += "/build";
+    std::string driver = cmStrCat(this->TargetDirectory, "/build");
     driver = cmSystemTools::ConvertToOutputPath(
       this->MaybeConvertToRelativePath(binDir, driver));
     makeDepends << driver << ": " << obj_m << ".provides.build\n";
@@ -456,18 +436,14 @@
   std::string fullName;
   for (std::string const& ip : this->IncludePath) {
     // Try the lower-case name.
-    fullName = ip;
-    fullName += "/";
-    fullName += mod_lower;
+    fullName = cmStrCat(ip, '/', mod_lower);
     if (cmSystemTools::FileExists(fullName, true)) {
       module = fullName;
       return true;
     }
 
     // Try the upper-case name.
-    fullName = ip;
-    fullName += "/";
-    fullName += mod_upper;
+    fullName = cmStrCat(ip, '/', mod_upper);
     if (cmSystemTools::FileExists(fullName, true)) {
       module = fullName;
       return true;
diff --git a/Source/cmDependsJava.h b/Source/cmDependsJava.h
index dd671a1..2a90251 100644
--- a/Source/cmDependsJava.h
+++ b/Source/cmDependsJava.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmDepends.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 
+#include "cmDepends.h"
+
 /** \class cmDependsJava
  * \brief Dependency scanner for Java class files.
  */
diff --git a/Source/cmDependsJavaParserHelper.cxx b/Source/cmDependsJavaParserHelper.cxx
index 12d875d..516bbbf 100644
--- a/Source/cmDependsJavaParserHelper.cxx
+++ b/Source/cmDependsJavaParserHelper.cxx
@@ -2,17 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDependsJavaParserHelper.h"
 
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <utility>
+
+#include <cm/string_view>
+
+#include "cmsys/FStream.hxx"
+
 #include "cmDependsJavaLexer.h"
 #include "cmSystemTools.h"
 
-#include "cm_string_view.hxx"
-#include "cmsys/FStream.hxx"
-#include <iostream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <utility>
-
 int cmDependsJava_yyparse(yyscan_t yyscanner);
 
 cmDependsJavaParserHelper::cmDependsJavaParserHelper()
diff --git a/Source/cmDisallowedCommand.cxx b/Source/cmDisallowedCommand.cxx
deleted file mode 100644
index 418d98c..0000000
--- a/Source/cmDisallowedCommand.cxx
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmDisallowedCommand.h"
-
-#include "cmMakefile.h"
-#include "cmMessageType.h"
-
-class cmExecutionStatus;
-
-bool cmDisallowedCommand::InitialPass(std::vector<std::string> const& args,
-                                      cmExecutionStatus& status)
-{
-  switch (this->Makefile->GetPolicyStatus(this->Policy)) {
-    case cmPolicies::WARN:
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING,
-                                   cmPolicies::GetPolicyWarning(this->Policy));
-      break;
-    case cmPolicies::OLD:
-      break;
-    case cmPolicies::REQUIRED_IF_USED:
-    case cmPolicies::REQUIRED_ALWAYS:
-    case cmPolicies::NEW:
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, this->Message);
-      return true;
-  }
-
-  this->Command->SetMakefile(this->GetMakefile());
-  bool const ret = this->Command->InitialPass(args, status);
-  this->SetError(this->Command->GetError());
-  return ret;
-}
diff --git a/Source/cmDisallowedCommand.h b/Source/cmDisallowedCommand.h
deleted file mode 100644
index d85c00f..0000000
--- a/Source/cmDisallowedCommand.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmDisallowedCommand_h
-#define cmDisallowedCommand_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <string>
-#include <vector>
-
-#include "cmCommand.h"
-#include "cmPolicies.h"
-
-class cmExecutionStatus;
-
-class cmDisallowedCommand : public cmCommand
-{
-public:
-  cmDisallowedCommand(cmCommand* command, cmPolicies::PolicyID policy,
-                      const char* message)
-    : Command(command)
-    , Policy(policy)
-    , Message(message)
-  {
-  }
-
-  ~cmDisallowedCommand() override { delete this->Command; }
-
-  cmCommand* Clone() override
-  {
-    return new cmDisallowedCommand(this->Command->Clone(), this->Policy,
-                                   this->Message);
-  }
-
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  void FinalPass() override { this->Command->FinalPass(); }
-
-  bool HasFinalPass() const override { return this->Command->HasFinalPass(); }
-
-private:
-  cmCommand* Command;
-  cmPolicies::PolicyID Policy;
-  const char* Message;
-};
-
-#endif
diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx
index d4628fa..1bc453d 100644
--- a/Source/cmDocumentation.cxx
+++ b/Source/cmDocumentation.cxx
@@ -2,19 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDocumentation.h"
 
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <utility>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+
 #include "cmDocumentationEntry.h"
 #include "cmDocumentationSection.h"
 #include "cmRST.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include <algorithm>
-#include <ctype.h>
-#include <string.h>
-#include <utility>
-
 static const char* cmDocumentationStandardOptions[][2] = {
   { "--help,-help,-usage,-h,-H,/?", "Print usage information and exit." },
   { "--version,-version,/V [<f>]", "Print version number and exit." },
diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h
index cf74b80..3768e1a 100644
--- a/Source/cmDocumentation.h
+++ b/Source/cmDocumentation.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmDocumentationFormatter.h"
-#include "cmDocumentationSection.h"
-
 #include <iosfwd>
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmDocumentationFormatter.h"
+#include "cmDocumentationSection.h"
+
 struct cmDocumentationEntry;
 
 /** Class to generate documentation.  */
diff --git a/Source/cmDocumentationFormatter.cxx b/Source/cmDocumentationFormatter.cxx
index 3e7f989..732637e 100644
--- a/Source/cmDocumentationFormatter.cxx
+++ b/Source/cmDocumentationFormatter.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDocumentationFormatter.h"
 
-#include "cmDocumentationEntry.h"
-#include "cmDocumentationSection.h"
-
+#include <cstring>
 #include <iomanip>
 #include <ostream>
-#include <string.h>
 #include <string>
 #include <vector>
 
+#include "cmDocumentationEntry.h"
+#include "cmDocumentationSection.h"
+
 cmDocumentationFormatter::cmDocumentationFormatter() = default;
 
 cmDocumentationFormatter::~cmDocumentationFormatter() = default;
diff --git a/Source/cmDocumentationSection.h b/Source/cmDocumentationSection.h
index 19c7407..15cada6 100644
--- a/Source/cmDocumentationSection.h
+++ b/Source/cmDocumentationSection.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmAlgorithms.h"
-#include "cmDocumentationEntry.h"
-
 #include <string>
 #include <vector>
 
+#include "cmAlgorithms.h"
+#include "cmDocumentationEntry.h"
+
 // Low-level interface for custom documents:
 /** Internal class representing a section of the documentation.
  * Cares e.g. for the different section titles in the different
diff --git a/Source/cmDuration.h b/Source/cmDuration.h
index 6df1455..ccd1cc1 100644
--- a/Source/cmDuration.h
+++ b/Source/cmDuration.h
@@ -5,7 +5,7 @@
 #include <chrono>
 #include <ratio>
 
-typedef std::chrono::duration<double, std::ratio<1>> cmDuration;
+using cmDuration = std::chrono::duration<double, std::ratio<1>>;
 
 /*
  * This function will return number of seconds in the requested type T.
diff --git a/Source/cmDynamicLoader.cxx b/Source/cmDynamicLoader.cxx
index 0549cf9..0b72a94 100644
--- a/Source/cmDynamicLoader.cxx
+++ b/Source/cmDynamicLoader.cxx
@@ -39,8 +39,7 @@
 bool cmDynamicLoaderCache::GetCacheFile(const char* path,
                                         cmsys::DynamicLoader::LibraryHandle& p)
 {
-  std::map<std::string, cmsys::DynamicLoader::LibraryHandle>::iterator it =
-    this->CacheMap.find(path);
+  auto it = this->CacheMap.find(path);
   if (it != this->CacheMap.end()) {
     p = it->second;
     return true;
@@ -50,8 +49,7 @@
 
 bool cmDynamicLoaderCache::FlushCache(const char* path)
 {
-  std::map<std::string, cmsys::DynamicLoader::LibraryHandle>::iterator it =
-    this->CacheMap.find(path);
+  auto it = this->CacheMap.find(path);
   bool ret = false;
   if (it != this->CacheMap.end()) {
     cmsys::DynamicLoader::CloseLibrary(it->second);
diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx
index 2226463..5976b2f 100644
--- a/Source/cmELF.cxx
+++ b/Source/cmELF.cxx
@@ -2,16 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmELF.h"
 
-#include "cmAlgorithms.h"
-#include "cm_kwiml.h"
-#include "cmsys/FStream.hxx"
+#include <cstddef>
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <sstream>
-#include <stddef.h>
 #include <utility>
 #include <vector>
 
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
+#include "cm_kwiml.h"
+
+#include "cmAlgorithms.h"
+
 // Include the ELF format information system header.
 #if defined(__OpenBSD__)
 #  include <elf_abi.h>
@@ -19,11 +24,11 @@
 #elif defined(__HAIKU__)
 #  include <elf32.h>
 #  include <elf64.h>
-typedef struct Elf32_Ehdr Elf32_Ehdr;
-typedef struct Elf32_Shdr Elf32_Shdr;
-typedef struct Elf32_Sym Elf32_Sym;
-typedef struct Elf32_Rel Elf32_Rel;
-typedef struct Elf32_Rela Elf32_Rela;
+using Elf32_Ehdr = struct Elf32_Ehdr;
+using Elf32_Shdr = struct Elf32_Shdr;
+using Elf32_Sym = struct Elf32_Sym;
+using Elf32_Rel = struct Elf32_Rel;
+using Elf32_Rela = struct Elf32_Rela;
 #  define ELFMAG0 0x7F
 #  define ELFMAG1 'E'
 #  define ELFMAG2 'L'
@@ -101,7 +106,7 @@
 class cmELFInternal
 {
 public:
-  typedef cmELF::StringEntry StringEntry;
+  using StringEntry = cmELF::StringEntry;
   enum ByteOrderType
   {
     ByteOrderMSB,
@@ -109,10 +114,10 @@
   };
 
   // Construct and take ownership of the file stream object.
-  cmELFInternal(cmELF* external, std::unique_ptr<cmsys::ifstream>& fin,
+  cmELFInternal(cmELF* external, std::unique_ptr<std::istream> fin,
                 ByteOrderType order)
     : External(external)
-    , Stream(*fin.release())
+    , Stream(std::move(fin))
     , ByteOrder(order)
     , ELFType(cmELF::FileTypeInvalid)
   {
@@ -132,7 +137,7 @@
   }
 
   // Destruct and delete the file stream object.
-  virtual ~cmELFInternal() { delete &this->Stream; }
+  virtual ~cmELFInternal() = default;
 
   // Forward to the per-class implementation.
   virtual unsigned int GetNumberOfSections() const = 0;
@@ -171,7 +176,7 @@
   cmELF* External;
 
   // The stream from which to read.
-  std::istream& Stream;
+  std::unique_ptr<std::istream> Stream;
 
   // The byte order of the ELF file.
   ByteOrderType ByteOrder;
@@ -199,11 +204,11 @@
 // Configure the implementation template for 32-bit ELF files.
 struct cmELFTypes32
 {
-  typedef Elf32_Ehdr ELF_Ehdr;
-  typedef Elf32_Shdr ELF_Shdr;
-  typedef Elf32_Dyn ELF_Dyn;
-  typedef Elf32_Half ELF_Half;
-  typedef KWIML_INT_uint32_t tagtype;
+  using ELF_Ehdr = Elf32_Ehdr;
+  using ELF_Shdr = Elf32_Shdr;
+  using ELF_Dyn = Elf32_Dyn;
+  using ELF_Half = Elf32_Half;
+  using tagtype = ::uint32_t;
   static const char* GetName() { return "32-bit"; }
 };
 
@@ -211,11 +216,11 @@
 #ifndef _SCO_DS
 struct cmELFTypes64
 {
-  typedef Elf64_Ehdr ELF_Ehdr;
-  typedef Elf64_Shdr ELF_Shdr;
-  typedef Elf64_Dyn ELF_Dyn;
-  typedef Elf64_Half ELF_Half;
-  typedef KWIML_INT_uint64_t tagtype;
+  using ELF_Ehdr = Elf64_Ehdr;
+  using ELF_Shdr = Elf64_Shdr;
+  using ELF_Dyn = Elf64_Dyn;
+  using ELF_Half = Elf64_Half;
+  using tagtype = ::uint64_t;
   static const char* GetName() { return "64-bit"; }
 };
 #endif
@@ -226,14 +231,14 @@
 {
 public:
   // Copy the ELF file format types from our configuration parameter.
-  typedef typename Types::ELF_Ehdr ELF_Ehdr;
-  typedef typename Types::ELF_Shdr ELF_Shdr;
-  typedef typename Types::ELF_Dyn ELF_Dyn;
-  typedef typename Types::ELF_Half ELF_Half;
-  typedef typename Types::tagtype tagtype;
+  using ELF_Ehdr = typename Types::ELF_Ehdr;
+  using ELF_Shdr = typename Types::ELF_Shdr;
+  using ELF_Dyn = typename Types::ELF_Dyn;
+  using ELF_Half = typename Types::ELF_Half;
+  using tagtype = typename Types::tagtype;
 
   // Construct with a stream and byte swap indicator.
-  cmELFInternalImpl(cmELF* external, std::unique_ptr<cmsys::ifstream>& fin,
+  cmELFInternalImpl(cmELF* external, std::unique_ptr<std::istream> fin,
                     ByteOrderType order);
 
   // Return the number of sections as specified by the ELF header.
@@ -288,9 +293,8 @@
   }
 
 private:
-  // ByteSwap(ELF_Dyn) assumes d_val and d_ptr are the same size
-  typedef char dyn_size_assert
-    [sizeof(ELF_Dyn().d_un.d_val) == sizeof(ELF_Dyn().d_un.d_ptr) ? 1 : -1];
+  static_assert(sizeof(ELF_Dyn().d_un.d_val) == sizeof(ELF_Dyn().d_un.d_ptr),
+                "ByteSwap(ELF_Dyn) assumes d_val and d_ptr are the same size");
 
   void ByteSwap(ELF_Ehdr& elf_header)
   {
@@ -352,7 +356,7 @@
   bool Read(ELF_Ehdr& x)
   {
     // Read the header from the file.
-    if (!this->Stream.read(reinterpret_cast<char*>(&x), sizeof(x))) {
+    if (!this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x))) {
       return false;
     }
 
@@ -382,26 +386,26 @@
   }
   bool Read(ELF_Shdr& x)
   {
-    if (this->Stream.read(reinterpret_cast<char*>(&x), sizeof(x)) &&
+    if (this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)) &&
         this->NeedSwap) {
       ByteSwap(x);
     }
-    return !this->Stream.fail();
+    return !this->Stream->fail();
   }
   bool Read(ELF_Dyn& x)
   {
-    if (this->Stream.read(reinterpret_cast<char*>(&x), sizeof(x)) &&
+    if (this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)) &&
         this->NeedSwap) {
       ByteSwap(x);
     }
-    return !this->Stream.fail();
+    return !this->Stream->fail();
   }
 
   bool LoadSectionHeader(ELF_Half i)
   {
     // Read the section header from the file.
-    this->Stream.seekg(this->ELFHeader.e_shoff +
-                       this->ELFHeader.e_shentsize * i);
+    this->Stream->seekg(this->ELFHeader.e_shoff +
+                        this->ELFHeader.e_shentsize * i);
     if (!this->Read(this->SectionHeaders[i])) {
       return false;
     }
@@ -426,9 +430,10 @@
 };
 
 template <class Types>
-cmELFInternalImpl<Types>::cmELFInternalImpl(
-  cmELF* external, std::unique_ptr<cmsys::ifstream>& fin, ByteOrderType order)
-  : cmELFInternal(external, fin, order)
+cmELFInternalImpl<Types>::cmELFInternalImpl(cmELF* external,
+                                            std::unique_ptr<std::istream> fin,
+                                            ByteOrderType order)
+  : cmELFInternal(external, std::move(fin), order)
 {
   // Read the main header.
   if (!this->Read(this->ELFHeader)) {
@@ -510,7 +515,7 @@
   // Read each entry.
   for (int j = 0; j < n; ++j) {
     // Seek to the beginning of the section entry.
-    this->Stream.seekg(sec.sh_offset + sec.sh_entsize * j);
+    this->Stream->seekg(sec.sh_offset + sec.sh_entsize * j);
     ELF_Dyn& dyn = this->DynamicSectionEntries[j];
 
     // Try reading the entry.
@@ -584,8 +589,7 @@
   unsigned int tag)
 {
   // Short-circuit if already checked.
-  std::map<unsigned int, StringEntry>::iterator dssi =
-    this->DynamicSectionStrings.find(tag);
+  auto dssi = this->DynamicSectionStrings.find(tag);
   if (dssi != this->DynamicSectionStrings.end()) {
     if (dssi->second.Position > 0) {
       return &dssi->second;
@@ -613,8 +617,7 @@
   ELF_Shdr const& strtab = this->SectionHeaders[sec.sh_link];
 
   // Look for the requested entry.
-  for (typename std::vector<ELF_Dyn>::iterator di =
-         this->DynamicSectionEntries.begin();
+  for (auto di = this->DynamicSectionEntries.begin();
        di != this->DynamicSectionEntries.end(); ++di) {
     ELF_Dyn& dyn = *di;
     if (static_cast<tagtype>(dyn.d_tag) == static_cast<tagtype>(tag)) {
@@ -630,7 +633,7 @@
       unsigned long first = static_cast<unsigned long>(dyn.d_un.d_val);
       unsigned long last = first;
       unsigned long end = static_cast<unsigned long>(strtab.sh_size);
-      this->Stream.seekg(strtab.sh_offset + first);
+      this->Stream->seekg(strtab.sh_offset + first);
 
       // Read the string.  It may be followed by more than one NULL
       // terminator.  Count the total size of the region allocated to
@@ -639,7 +642,7 @@
       // assumption.
       bool terminated = false;
       char c;
-      while (last != end && this->Stream.get(c) && !(terminated && c)) {
+      while (last != end && this->Stream->get(c) && !(terminated && c)) {
         ++last;
         if (c) {
           se.Value += c;
@@ -649,7 +652,7 @@
       }
 
       // Make sure the whole value was read.
-      if (!this->Stream) {
+      if (!(*this->Stream)) {
         this->SetErrorMessage("Dynamic section specifies unreadable RPATH.");
         se.Value = "";
         return nullptr;
@@ -679,10 +682,9 @@
 #endif
 
 cmELF::cmELF(const char* fname)
-  : Internal(nullptr)
 {
   // Try to open the file.
-  std::unique_ptr<cmsys::ifstream> fin(new cmsys::ifstream(fname));
+  auto fin = cm::make_unique<cmsys::ifstream>(fname);
 
   // Quit now if the file could not be opened.
   if (!fin || !*fin) {
@@ -725,12 +727,14 @@
   // parser implementation.
   if (ident[EI_CLASS] == ELFCLASS32) {
     // 32-bit ELF
-    this->Internal = new cmELFInternalImpl<cmELFTypes32>(this, fin, order);
+    this->Internal = cm::make_unique<cmELFInternalImpl<cmELFTypes32>>(
+      this, std::move(fin), order);
   }
 #ifndef _SCO_DS
   else if (ident[EI_CLASS] == ELFCLASS64) {
     // 64-bit ELF
-    this->Internal = new cmELFInternalImpl<cmELFTypes64>(this, fin, order);
+    this->Internal = cm::make_unique<cmELFInternalImpl<cmELFTypes64>>(
+      this, std::move(fin), order);
   }
 #endif
   else {
@@ -739,10 +743,7 @@
   }
 }
 
-cmELF::~cmELF()
-{
-  delete this->Internal;
-}
+cmELF::~cmELF() = default;
 
 bool cmELF::Valid() const
 {
diff --git a/Source/cmELF.h b/Source/cmELF.h
index 987f0c3..123bf9b 100644
--- a/Source/cmELF.h
+++ b/Source/cmELF.h
@@ -6,6 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -67,7 +68,7 @@
   };
 
   /** Represent entire dynamic section header */
-  typedef std::vector<std::pair<long, unsigned long>> DynamicEntryList;
+  using DynamicEntryList = std::vector<std::pair<long, unsigned long>>;
 
   /** Get the type of the file opened.  */
   FileType GetFileType() const;
@@ -108,7 +109,7 @@
 private:
   friend class cmELFInternal;
   bool Valid() const;
-  cmELFInternal* Internal;
+  std::unique_ptr<cmELFInternal> Internal;
   std::string ErrorMessage;
 };
 
diff --git a/Source/cmEnableLanguageCommand.cxx b/Source/cmEnableLanguageCommand.cxx
index ddd26de..59522c0 100644
--- a/Source/cmEnableLanguageCommand.cxx
+++ b/Source/cmEnableLanguageCommand.cxx
@@ -2,20 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmEnableLanguageCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// cmEnableLanguageCommand
-bool cmEnableLanguageCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmEnableLanguageCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
-  bool optional = false;
-  std::vector<std::string> languages;
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
+
+  bool optional = false;
+  std::vector<std::string> languages;
   for (std::string const& it : args) {
     if (it == "OPTIONAL") {
       optional = true;
@@ -24,6 +23,6 @@
     }
   }
 
-  this->Makefile->EnableLanguage(languages, optional);
+  status.GetMakefile().EnableLanguage(languages, optional);
   return true;
 }
diff --git a/Source/cmEnableLanguageCommand.h b/Source/cmEnableLanguageCommand.h
index 97645a9..1f8c4ce 100644
--- a/Source/cmEnableLanguageCommand.h
+++ b/Source/cmEnableLanguageCommand.h
@@ -8,32 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmEnableLanguageCommand
- * \brief Specify the name for this build project.
- *
- * cmEnableLanguageCommand is used to specify a name for this build project.
- * It is defined once per set of CMakeList.txt files (including
- * all subdirectories). Currently it just sets the name of the workspace
- * file for Microsoft Visual C++
- */
-class cmEnableLanguageCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmEnableLanguageCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmEnableLanguageCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmEnableTestingCommand.cxx b/Source/cmEnableTestingCommand.cxx
index 6a64450..89212c8 100644
--- a/Source/cmEnableTestingCommand.cxx
+++ b/Source/cmEnableTestingCommand.cxx
@@ -2,15 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmEnableTestingCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// we do this in the final pass so that we now the subdirs have all
-// been defined
-bool cmEnableTestingCommand::InitialPass(std::vector<std::string> const&,
-                                         cmExecutionStatus&)
+bool cmEnableTestingCommand(std::vector<std::string> const&,
+                            cmExecutionStatus& status)
 {
-  this->Makefile->AddDefinition("CMAKE_TESTING_ENABLED", "1");
+  status.GetMakefile().AddDefinition("CMAKE_TESTING_ENABLED", "1");
   return true;
 }
diff --git a/Source/cmEnableTestingCommand.h b/Source/cmEnableTestingCommand.h
index 88a17b9..e4593f2 100644
--- a/Source/cmEnableTestingCommand.h
+++ b/Source/cmEnableTestingCommand.h
@@ -8,11 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmEnableTestingCommand
+/**
  * \brief Enable testing for this directory and below.
  *
  * Produce the output testfile. This produces a file in the build directory
@@ -25,20 +23,7 @@
  * Note that CTest expects to find this file in the build directory root;
  * therefore, this command should be in the source directory root too.
  */
-class cmEnableTestingCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmEnableTestingCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const&,
-                   cmExecutionStatus&) override;
-};
+bool cmEnableTestingCommand(std::vector<std::string> const&,
+                            cmExecutionStatus&);
 
 #endif
diff --git a/Source/cmExecProgramCommand.cxx b/Source/cmExecProgramCommand.cxx
index 4b559e7..51fb219 100644
--- a/Source/cmExecProgramCommand.cxx
+++ b/Source/cmExecProgramCommand.cxx
@@ -2,21 +2,30 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecProgramCommand.h"
 
-#include "cmsys/Process.h"
-#include <stdio.h>
+#include <cstdio>
 
+#include "cmsys/Process.h"
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+using Encoding = cmProcessOutput::Encoding;
+
+namespace {
+bool RunCommand(std::string command, std::string& output, int& retVal,
+                const char* directory = nullptr, bool verbose = true,
+                Encoding encoding = cmProcessOutput::Auto);
+}
 
 // cmExecProgramCommand
-bool cmExecProgramCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmExecProgramCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::string arguments;
@@ -34,7 +43,7 @@
       haveoutput_variable = true;
     } else if (haveoutput_variable) {
       if (!output_variable.empty()) {
-        this->SetError("called with incorrect number of arguments");
+        status.SetError("called with incorrect number of arguments");
         return false;
       }
       output_variable = arg;
@@ -47,7 +56,7 @@
       havereturn_variable = true;
     } else if (havereturn_variable) {
       if (!return_variable.empty()) {
-        this->SetError("called with incorrect number of arguments");
+        status.SetError("called with incorrect number of arguments");
         return false;
       }
       return_variable = arg;
@@ -67,9 +76,8 @@
 
   std::string command;
   if (!arguments.empty()) {
-    command = cmSystemTools::ConvertToRunCommandPath(args[0]);
-    command += " ";
-    command += arguments;
+    command = cmStrCat(cmSystemTools::ConvertToRunCommandPath(args[0]), ' ',
+                       arguments);
   } else {
     command = args[0];
   }
@@ -82,11 +90,9 @@
   bool result = true;
   if (args.size() - count == 2) {
     cmSystemTools::MakeDirectory(args[1]);
-    result = cmExecProgramCommand::RunCommand(command, output, retVal,
-                                              args[1].c_str(), verbose);
+    result = RunCommand(command, output, retVal, args[1].c_str(), verbose);
   } else {
-    result = cmExecProgramCommand::RunCommand(command, output, retVal, nullptr,
-                                              verbose);
+    result = RunCommand(command, output, retVal, nullptr, verbose);
   }
   if (!result) {
     retVal = -1;
@@ -103,21 +109,21 @@
     }
 
     std::string coutput = std::string(output, first, last - first + 1);
-    this->Makefile->AddDefinition(output_variable, coutput.c_str());
+    status.GetMakefile().AddDefinition(output_variable, coutput);
   }
 
   if (!return_variable.empty()) {
     char buffer[100];
     sprintf(buffer, "%d", retVal);
-    this->Makefile->AddDefinition(return_variable, buffer);
+    status.GetMakefile().AddDefinition(return_variable, buffer);
   }
 
   return true;
 }
 
-bool cmExecProgramCommand::RunCommand(std::string command, std::string& output,
-                                      int& retVal, const char* dir,
-                                      bool verbose, Encoding encoding)
+namespace {
+bool RunCommand(std::string command, std::string& output, int& retVal,
+                const char* dir, bool verbose, Encoding encoding)
 {
   if (cmSystemTools::GetRunCommandOutput()) {
     verbose = false;
@@ -187,10 +193,7 @@
 #else
   std::string commandInDir;
   if (dir) {
-    commandInDir = "cd \"";
-    commandInDir += dir;
-    commandInDir += "\" && ";
-    commandInDir += command;
+    commandInDir = cmStrCat("cd \"", dir, "\" && ", command);
   } else {
     commandInDir = command;
   }
@@ -284,3 +287,4 @@
 
   return true;
 }
+}
diff --git a/Source/cmExecProgramCommand.h b/Source/cmExecProgramCommand.h
index ae0fa9b..7c751e1 100644
--- a/Source/cmExecProgramCommand.h
+++ b/Source/cmExecProgramCommand.h
@@ -8,38 +8,16 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmProcessOutput.h"
-
 class cmExecutionStatus;
 
-/** \class cmExecProgramCommand
+/**
  * \brief Command that adds a target to the build system.
  *
  * cmExecProgramCommand adds an extra target to the build system.
  * This is useful when you would like to add special
  * targets like "install,", "clean," and so on.
  */
-class cmExecProgramCommand : public cmCommand
-{
-public:
-  typedef cmProcessOutput::Encoding Encoding;
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmExecProgramCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  static bool RunCommand(std::string command, std::string& output, int& retVal,
-                         const char* directory = nullptr, bool verbose = true,
-                         Encoding encoding = cmProcessOutput::Auto);
-};
+bool cmExecProgramCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 494afbb..80e4bcd 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -2,23 +2,28 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecuteProcessCommand.h"
 
-#include "cm_static_string_view.hxx"
-#include "cmsys/Process.h"
 #include <algorithm>
-#include <ctype.h> /* isspace */
+#include <cctype> /* isspace */
+#include <cstdio>
 #include <iostream>
-#include <stdio.h>
+#include <memory>
+#include <vector>
+
+#include "cmsys/Process.h"
+
+#include "cm_static_string_view.hxx"
 
 #include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
-static bool cmExecuteProcessCommandIsWhitespace(char c)
+namespace {
+bool cmExecuteProcessCommandIsWhitespace(char c)
 {
   return (isspace(static_cast<int>(c)) || c == '\n' || c == '\r');
 }
@@ -27,13 +32,14 @@
                                     bool strip_trailing_whitespace);
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
                                    int length);
+}
 
 // cmExecuteProcessCommand
-bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmExecuteProcessCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -84,31 +90,31 @@
     parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
 
   if (!keywordsMissingValue.empty()) {
-    this->SetError(" called with no value for " +
-                   keywordsMissingValue.front() + ".");
+    status.SetError(" called with no value for " +
+                    keywordsMissingValue.front() + ".");
     return false;
   }
   if (!unparsedArguments.empty()) {
-    this->SetError(" given unknown argument \"" + unparsedArguments.front() +
-                   "\".");
+    status.SetError(" given unknown argument \"" + unparsedArguments.front() +
+                    "\".");
     return false;
   }
 
-  if (!this->Makefile->CanIWriteThisFile(arguments.OutputFile)) {
-    this->SetError("attempted to output into a file: " + arguments.OutputFile +
-                   " into a source directory.");
+  if (!status.GetMakefile().CanIWriteThisFile(arguments.OutputFile)) {
+    status.SetError("attempted to output into a file: " +
+                    arguments.OutputFile + " into a source directory.");
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
 
   // Check for commands given.
   if (arguments.Commands.empty()) {
-    this->SetError(" called with no COMMAND argument.");
+    status.SetError(" called with no COMMAND argument.");
     return false;
   }
   for (std::vector<std::string> const& cmd : arguments.Commands) {
     if (cmd.empty()) {
-      this->SetError(" given COMMAND argument with no value.");
+      status.SetError(" given COMMAND argument with no value.");
       return false;
     }
   }
@@ -117,7 +123,7 @@
   double timeout = -1;
   if (!arguments.Timeout.empty()) {
     if (sscanf(arguments.Timeout.c_str(), "%lg", &timeout) != 1) {
-      this->SetError(" called with TIMEOUT value that could not be parsed.");
+      status.SetError(" called with TIMEOUT value that could not be parsed.");
       return false;
     }
   }
@@ -177,8 +183,8 @@
   bool echo_stdout = false;
   bool echo_stderr = false;
   bool echo_output_from_variable = true;
-  std::string echo_output =
-    this->Makefile->GetSafeDefinition("CMAKE_EXECUTE_PROCESS_COMMAND_ECHO");
+  std::string echo_output = status.GetMakefile().GetSafeDefinition(
+    "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO");
   if (!arguments.CommandEcho.empty()) {
     echo_output_from_variable = false;
     echo_output = arguments.CommandEcho;
@@ -201,7 +207,7 @@
       if (!echo_output_from_variable) {
         error += " for COMMAND_ECHO.";
       }
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, error);
+      status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, error);
       return true;
     }
   }
@@ -275,11 +281,13 @@
 
   // Store the output obtained.
   if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
-    this->Makefile->AddDefinition(arguments.OutputVariable, tempOutput.data());
+    status.GetMakefile().AddDefinition(arguments.OutputVariable,
+                                       tempOutput.data());
   }
   if (!merge_output && !arguments.ErrorVariable.empty() &&
       !tempError.empty()) {
-    this->Makefile->AddDefinition(arguments.ErrorVariable, tempError.data());
+    status.GetMakefile().AddDefinition(arguments.ErrorVariable,
+                                       tempError.data());
   }
 
   // Store the result of running the process.
@@ -289,19 +297,19 @@
         int v = cmsysProcess_GetExitValue(cp);
         char buf[16];
         sprintf(buf, "%d", v);
-        this->Makefile->AddDefinition(arguments.ResultVariable, buf);
+        status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
       } break;
       case cmsysProcess_State_Exception:
-        this->Makefile->AddDefinition(arguments.ResultVariable,
-                                      cmsysProcess_GetExceptionString(cp));
+        status.GetMakefile().AddDefinition(
+          arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
         break;
       case cmsysProcess_State_Error:
-        this->Makefile->AddDefinition(arguments.ResultVariable,
-                                      cmsysProcess_GetErrorString(cp));
+        status.GetMakefile().AddDefinition(arguments.ResultVariable,
+                                           cmsysProcess_GetErrorString(cp));
         break;
       case cmsysProcess_State_Expired:
-        this->Makefile->AddDefinition(arguments.ResultVariable,
-                                      "Process terminated due to timeout");
+        status.GetMakefile().AddDefinition(
+          arguments.ResultVariable, "Process terminated due to timeout");
         break;
     }
   }
@@ -329,20 +337,20 @@
               break;
           }
         }
-        this->Makefile->AddDefinition(arguments.ResultsVariable,
-                                      cmJoin(res, ";").c_str());
+        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                           cmJoin(res, ";"));
       } break;
       case cmsysProcess_State_Exception:
-        this->Makefile->AddDefinition(arguments.ResultsVariable,
-                                      cmsysProcess_GetExceptionString(cp));
+        status.GetMakefile().AddDefinition(
+          arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
         break;
       case cmsysProcess_State_Error:
-        this->Makefile->AddDefinition(arguments.ResultsVariable,
-                                      cmsysProcess_GetErrorString(cp));
+        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                           cmsysProcess_GetErrorString(cp));
         break;
       case cmsysProcess_State_Expired:
-        this->Makefile->AddDefinition(arguments.ResultsVariable,
-                                      "Process terminated due to timeout");
+        status.GetMakefile().AddDefinition(
+          arguments.ResultsVariable, "Process terminated due to timeout");
         break;
     }
   }
@@ -350,6 +358,7 @@
   return true;
 }
 
+namespace {
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
                                     bool strip_trailing_whitespace)
 {
@@ -395,3 +404,4 @@
 #endif
   cmAppend(output, data, data + length);
 }
+}
diff --git a/Source/cmExecuteProcessCommand.h b/Source/cmExecuteProcessCommand.h
index b415deb..9c4b600 100644
--- a/Source/cmExecuteProcessCommand.h
+++ b/Source/cmExecuteProcessCommand.h
@@ -8,30 +8,15 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmExecuteProcessCommand
+/**
  * \brief Command that adds a target to the build system.
  *
  * cmExecuteProcessCommand is a CMake language interface to the KWSys
  * Process Execution implementation.
  */
-class cmExecuteProcessCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmExecuteProcessCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmExecuteProcessCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h
index 56199dd..d2cc9b8 100644
--- a/Source/cmExecutionStatus.h
+++ b/Source/cmExecutionStatus.h
@@ -3,6 +3,12 @@
 #ifndef cmExecutionStatus_h
 #define cmExecutionStatus_h
 
+#include <cmConfigure.h> // IWYU pragma: keep
+
+#include <string>
+
+class cmMakefile;
+
 /** \class cmExecutionStatus
  * \brief Superclass for all command status classes
  *
@@ -11,14 +17,17 @@
 class cmExecutionStatus
 {
 public:
-  void Clear()
+  cmExecutionStatus(cmMakefile& makefile)
+    : Makefile(makefile)
+    , Error("unknown error.")
   {
-    this->ReturnInvoked = false;
-    this->BreakInvoked = false;
-    this->ContinueInvoked = false;
-    this->NestedError = false;
   }
 
+  cmMakefile& GetMakefile() { return this->Makefile; }
+
+  void SetError(std::string const& e) { this->Error = e; }
+  std::string const& GetError() const { return this->Error; }
+
   void SetReturnInvoked() { this->ReturnInvoked = true; }
   bool GetReturnInvoked() const { return this->ReturnInvoked; }
 
@@ -32,6 +41,8 @@
   bool GetNestedError() const { return this->NestedError; }
 
 private:
+  cmMakefile& Makefile;
+  std::string Error;
   bool ReturnInvoked = false;
   bool BreakInvoked = false;
   bool ContinueInvoked = false;
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx
index ced27c9..561e830 100644
--- a/Source/cmExportBuildAndroidMKGenerator.cxx
+++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -2,8 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportBuildAndroidMKGenerator.h"
 
-#include <algorithm>
-#include <memory> // IWYU pragma: keep
 #include <sstream>
 #include <utility>
 
@@ -14,6 +12,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
@@ -42,8 +41,7 @@
   std::ostream& os, cmGeneratorTarget const* target,
   cmStateEnums::TargetType /*targetType*/)
 {
-  std::string targetName = this->Namespace;
-  targetName += target->GetExportName();
+  std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
   os << "include $(CLEAR_VARS)\n";
   os << "LOCAL_MODULE := ";
   os << targetName << "\n";
@@ -145,8 +143,7 @@
         }
       } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") {
         std::string includes = property.second;
-        std::vector<std::string> includeList;
-        cmSystemTools::ExpandListArgument(includes, includeList);
+        std::vector<std::string> includeList = cmExpandedList(includes);
         os << "LOCAL_EXPORT_C_INCLUDES := ";
         std::string end;
         for (std::string const& i : includeList) {
@@ -156,8 +153,8 @@
         os << "\n";
       } else if (property.first == "INTERFACE_LINK_OPTIONS") {
         os << "LOCAL_EXPORT_LDFLAGS := ";
-        std::vector<std::string> linkFlagsList;
-        cmSystemTools::ExpandListArgument(property.second, linkFlagsList);
+        std::vector<std::string> linkFlagsList =
+          cmExpandedList(property.second);
         os << cmJoin(linkFlagsList, " ") << "\n";
       } else {
         os << "# " << property.first << " " << (property.second) << "\n";
@@ -168,8 +165,7 @@
   // Tell the NDK build system if prebuilt static libraries use C++.
   if (target->GetType() == cmStateEnums::STATIC_LIBRARY) {
     cmLinkImplementation const* li = target->GetLinkImplementation(config);
-    if (std::find(li->Languages.begin(), li->Languages.end(), "CXX") !=
-        li->Languages.end()) {
+    if (cmContains(li->Languages, "CXX")) {
       os << "LOCAL_HAS_CPP := true\n";
     }
   }
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 012355b..7e9a987 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -2,7 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportBuildFileGenerator.h"
 
-#include "cmAlgorithms.h"
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
 #include "cmExportSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -12,17 +17,11 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <map>
-#include <set>
-#include <sstream>
-#include <utility>
-
 class cmSourceFile;
 
 cmExportBuildFileGenerator::cmExportBuildFileGenerator()
@@ -45,6 +44,7 @@
     std::string expectedTargets;
     std::string sep;
     std::vector<std::string> targets;
+    bool generatedInterfaceRequired = false;
     this->GetTargets(targets);
     for (std::string const& tei : targets) {
       cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
@@ -60,11 +60,13 @@
           this->LG->GetMakefile()->GetBacktrace());
         return false;
       }
-      if (this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY) {
-        this->GenerateRequiredCMakeVersion(os, "3.0.0");
-      }
+      generatedInterfaceRequired |=
+        this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
     }
 
+    if (generatedInterfaceRequired) {
+      this->GenerateRequiredCMakeVersion(os, "3.0.0");
+    }
     this->GenerateExpectedTargetsCode(os, expectedTargets);
   }
 
@@ -90,6 +92,9 @@
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
@@ -201,8 +206,7 @@
   cmMakefile* mf = target->Makefile;
 
   if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-    std::string prop = "IMPORTED_OBJECTS";
-    prop += suffix;
+    std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
 
     // Compute all the object files inside this target and setup
     // IMPORTED_OBJECTS as a list of object files
@@ -220,8 +224,7 @@
   } else {
     // Add the main target file.
     {
-      std::string prop = "IMPORTED_LOCATION";
-      prop += suffix;
+      std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
       std::string value;
       if (target->IsAppBundleOnApple()) {
         value =
@@ -234,14 +237,14 @@
     }
 
     // Add the import library for windows DLLs.
-    if (target->HasImportLibrary(config) &&
-        mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
-      std::string prop = "IMPORTED_IMPLIB";
-      prop += suffix;
+    if (target->HasImportLibrary(config)) {
+      std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
       std::string value =
         target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact);
-      target->GetImplibGNUtoMS(config, value, value,
-                               "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
+      if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
+        target->GetImplibGNUtoMS(config, value, value,
+                                 "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
+      }
       properties[prop] = value;
     }
   }
@@ -256,11 +259,11 @@
     const std::string name = dependee->GetName();
     cmGlobalGenerator* gg =
       dependee->GetLocalGenerator()->GetGlobalGenerator();
-    std::vector<std::string> namespaces = this->FindNamespaces(gg, name);
+    auto exportInfo = this->FindBuildExportInfo(gg, name);
+    std::vector<std::string> const& exportFiles = exportInfo.first;
 
-    int targetOccurrences = static_cast<int>(namespaces.size());
-    if (targetOccurrences == 1) {
-      std::string missingTarget = namespaces[0];
+    if (exportFiles.size() == 1) {
+      std::string missingTarget = exportInfo.second;
 
       missingTarget += dependee->GetExportName();
       link_libs += missingTarget;
@@ -269,7 +272,7 @@
     }
     // We are not appending, so all exported targets should be
     // known here.  This is probably user-error.
-    this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
+    this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
   }
   // Assume the target will be exported by another command.
   // Append it with the export namespace.
@@ -281,7 +284,8 @@
   std::vector<std::string>& targets) const
 {
   if (this->ExportSet) {
-    for (cmTargetExport* te : *this->ExportSet->GetTargetExports()) {
+    for (std::unique_ptr<cmTargetExport> const& te :
+         this->ExportSet->GetTargetExports()) {
       targets.push_back(te->TargetName);
     }
     return;
@@ -289,10 +293,12 @@
   targets = this->Targets;
 }
 
-std::vector<std::string> cmExportBuildFileGenerator::FindNamespaces(
-  cmGlobalGenerator* gg, const std::string& name)
+std::pair<std::vector<std::string>, std::string>
+cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
+                                                const std::string& name)
 {
-  std::vector<std::string> namespaces;
+  std::vector<std::string> exportFiles;
+  std::string ns;
 
   std::map<std::string, cmExportBuildFileGenerator*>& exportSets =
     gg->GetBuildExportSets();
@@ -301,32 +307,32 @@
     const cmExportBuildFileGenerator* exportSet = exp.second;
     std::vector<std::string> targets;
     exportSet->GetTargets(targets);
-    if (std::find(targets.begin(), targets.end(), name) != targets.end()) {
-      namespaces.push_back(exportSet->GetNamespace());
+    if (cmContains(targets, name)) {
+      exportFiles.push_back(exp.first);
+      ns = exportSet->GetNamespace();
     }
   }
 
-  return namespaces;
+  return { exportFiles, ns };
 }
 
 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee, int occurrences)
+  cmGeneratorTarget* depender, cmGeneratorTarget* dependee,
+  std::vector<std::string> const& exportFiles)
 {
-  if (cmSystemTools::GetErrorOccuredFlag()) {
-    return;
-  }
-
   std::ostringstream e;
   e << "export called with target \"" << depender->GetName()
     << "\" which requires target \"" << dependee->GetName() << "\" ";
-  if (occurrences == 0) {
-    e << "that is not in the export set.\n";
+  if (exportFiles.empty()) {
+    e << "that is not in any export set.";
   } else {
-    e << "that is not in this export set, but " << occurrences
-      << " times in others.\n";
+    e << "that is not in this export set, but in multiple other export sets: "
+      << cmJoin(exportFiles, ", ") << ".\n";
+    e << "An exported target cannot depend upon another target which is "
+         "exported multiple times. Consider consolidating the exports of the "
+         "\""
+      << dependee->GetName() << "\" target to a single export.";
   }
-  e << "If the required target is not easy to reference in this call, "
-    << "consider using the APPEND option with multiple separate calls.";
 
   this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
     MessageType::FATAL_ERROR, e.str(),
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h
index 0a1e755..11fbd02 100644
--- a/Source/cmExportBuildFileGenerator.h
+++ b/Source/cmExportBuildFileGenerator.h
@@ -5,14 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <iosfwd>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "cmAlgorithms.h"
 #include "cmExportFileGenerator.h"
 #include "cmStateTypes.h"
 
-#include <iosfwd>
-#include <string>
-#include <vector>
-
 class cmExportSet;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
@@ -64,7 +65,7 @@
 
   void ComplainAboutMissingTarget(cmGeneratorTarget* depender,
                                   cmGeneratorTarget* dependee,
-                                  int occurrences);
+                                  std::vector<std::string> const& namespaces);
 
   /** Fill in properties indicating built file locations.  */
   void SetImportLocationProperty(const std::string& config,
@@ -75,8 +76,8 @@
   std::string InstallNameDir(cmGeneratorTarget* target,
                              const std::string& config) override;
 
-  std::vector<std::string> FindNamespaces(cmGlobalGenerator* gg,
-                                          const std::string& name);
+  std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
+    cmGlobalGenerator* gg, const std::string& name);
 
   std::vector<std::string> Targets;
   cmExportSet* ExportSet;
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index a849aa2..2a6bf5d 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -2,18 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportCommand.h"
 
-#include "cm_static_string_view.hxx"
-#include "cmsys/RegularExpression.hxx"
-
-#include <algorithm>
 #include <map>
 #include <sstream>
 #include <utility>
 
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_static_string_view.hxx"
+
+#include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
+#include "cmExecutionStatus.h"
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportBuildFileGenerator.h"
-#include "cmExportSetMap.h"
+#include "cmExportSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -23,24 +25,31 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-class cmExportSet;
-class cmExecutionStatus;
-
 #if defined(__HAIKU__)
 #  include <FindDirectory.h>
 #  include <StorageDefs.h>
 #endif
 
-bool cmExportCommand::InitialPass(std::vector<std::string> const& args,
-                                  cmExecutionStatus&)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#  include <windows.h>
+#endif
+
+static bool HandlePackage(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
+
+static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
+                                 const char* content, const char* hash);
+
+bool cmExportCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with too few arguments");
+    status.SetError("called with too few arguments");
     return false;
   }
 
   if (args[0] == "PACKAGE") {
-    return this->HandlePackage(args);
+    return HandlePackage(args, status);
   }
 
   struct Arguments
@@ -73,7 +82,7 @@
     parser.Parse(args, &unknownArgs, &keywordsMissingValue);
 
   if (!unknownArgs.empty()) {
-    this->SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
+    status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
     return false;
   }
 
@@ -85,7 +94,7 @@
   }
   if (arguments.Filename.empty() && fname.empty()) {
     if (args[0] != "EXPORT") {
-      this->SetError("FILE <filename> option missing.");
+      status.SetError("FILE <filename> option missing.");
       return false;
     }
     fname = arguments.ExportSetName + ".cmake";
@@ -96,66 +105,66 @@
       std::ostringstream e;
       e << "FILE option given filename \"" << arguments.Filename
         << "\" which does not have an extension of \".cmake\".\n";
-      this->SetError(e.str());
+      status.SetError(e.str());
       return false;
     }
     fname = arguments.Filename;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Get the file to write.
   if (cmSystemTools::FileIsFullPath(fname)) {
-    if (!this->Makefile->CanIWriteThisFile(fname)) {
+    if (!mf.CanIWriteThisFile(fname)) {
       std::ostringstream e;
       e << "FILE option given filename \"" << fname
         << "\" which is in the source tree.\n";
-      this->SetError(e.str());
+      status.SetError(e.str());
       return false;
     }
   } else {
     // Interpret relative paths with respect to the current build dir.
-    std::string dir = this->Makefile->GetCurrentBinaryDirectory();
+    std::string const& dir = mf.GetCurrentBinaryDirectory();
     fname = dir + "/" + fname;
   }
 
   std::vector<std::string> targets;
 
-  cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
+  cmGlobalGenerator* gg = mf.GetGlobalGenerator();
 
-  cmExportSet* ExportSet = nullptr;
+  cmExportSet* exportSet = nullptr;
   if (args[0] == "EXPORT") {
     cmExportSetMap& setMap = gg->GetExportSets();
     auto const it = setMap.find(arguments.ExportSetName);
     if (it == setMap.end()) {
       std::ostringstream e;
       e << "Export set \"" << arguments.ExportSetName << "\" not found.";
-      this->SetError(e.str());
+      status.SetError(e.str());
       return false;
     }
-    ExportSet = it->second;
+    exportSet = &it->second;
   } else if (!arguments.Targets.empty() ||
-             std::find(keywordsMissingValue.begin(),
-                       keywordsMissingValue.end(),
-                       "TARGETS") != keywordsMissingValue.end()) {
+             cmContains(keywordsMissingValue, "TARGETS")) {
     for (std::string const& currentTarget : arguments.Targets) {
-      if (this->Makefile->IsAlias(currentTarget)) {
+      if (mf.IsAlias(currentTarget)) {
         std::ostringstream e;
         e << "given ALIAS target \"" << currentTarget
           << "\" which may not be exported.";
-        this->SetError(e.str());
+        status.SetError(e.str());
         return false;
       }
 
       if (cmTarget* target = gg->FindTarget(currentTarget)) {
         if (target->GetType() == cmStateEnums::UTILITY) {
-          this->SetError("given custom target \"" + currentTarget +
-                         "\" which may not be exported.");
+          status.SetError("given custom target \"" + currentTarget +
+                          "\" which may not be exported.");
           return false;
         }
       } else {
         std::ostringstream e;
         e << "given target \"" << currentTarget
           << "\" which is not built by this project.";
-        this->SetError(e.str());
+        status.SetError(e.str());
         return false;
       }
       targets.push_back(currentTarget);
@@ -168,7 +177,7 @@
       }
     }
   } else {
-    this->SetError("EXPORT or TARGETS specifier missing.");
+    status.SetError("EXPORT or TARGETS specifier missing.");
     return false;
   }
 
@@ -182,24 +191,24 @@
   ebfg->SetExportFile(fname.c_str());
   ebfg->SetNamespace(arguments.Namespace);
   ebfg->SetAppendMode(arguments.Append);
-  if (ExportSet != nullptr) {
-    ebfg->SetExportSet(ExportSet);
+  if (exportSet != nullptr) {
+    ebfg->SetExportSet(exportSet);
   } else {
     ebfg->SetTargets(targets);
   }
-  this->Makefile->AddExportBuildFileGenerator(ebfg);
+  mf.AddExportBuildFileGenerator(ebfg);
   ebfg->SetExportOld(arguments.ExportOld);
 
   // Compute the set of configurations exported.
   std::vector<std::string> configurationTypes;
-  this->Makefile->GetConfigurations(configurationTypes);
+  mf.GetConfigurations(configurationTypes);
   if (configurationTypes.empty()) {
     configurationTypes.emplace_back();
   }
   for (std::string const& ct : configurationTypes) {
     ebfg->AddConfiguration(ct);
   }
-  if (ExportSet != nullptr) {
+  if (exportSet != nullptr) {
     gg->AddBuildExportExportSet(ebfg);
   } else {
     gg->AddBuildExportSet(ebfg);
@@ -208,7 +217,8 @@
   return true;
 }
 
-bool cmExportCommand::HandlePackage(std::vector<std::string> const& args)
+static bool HandlePackage(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   // Parse PACKAGE mode arguments.
   enum Doing
@@ -225,14 +235,14 @@
     } else {
       std::ostringstream e;
       e << "PACKAGE given unknown argument: " << args[i];
-      this->SetError(e.str());
+      status.SetError(e.str());
       return false;
     }
   }
 
   // Verify the package name.
   if (package.empty()) {
-    this->SetError("PACKAGE must be given a package name.");
+    status.SetError("PACKAGE must be given a package name.");
     return false;
   }
   const char* packageExpr = "^[A-Za-z0-9_.-]+$";
@@ -241,16 +251,18 @@
     std::ostringstream e;
     e << "PACKAGE given invalid package name \"" << package << "\".  "
       << "Package names must match \"" << packageExpr << "\".";
-    this->SetError(e.str());
+    status.SetError(e.str());
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // CMP0090 decides both the default and what variable changes it.
-  switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0090)) {
+  switch (mf.GetPolicyStatus(cmPolicies::CMP0090)) {
     case cmPolicies::WARN:
     case cmPolicies::OLD:
       // Default is to export, but can be disabled.
-      if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
+      if (mf.IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
         return true;
       }
       break;
@@ -258,7 +270,7 @@
     case cmPolicies::REQUIRED_ALWAYS:
     case cmPolicies::NEW:
       // Default is to not export, but can be enabled.
-      if (!this->Makefile->IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) {
+      if (!mf.IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) {
         return true;
       }
       break;
@@ -267,22 +279,17 @@
   // We store the current build directory in the registry as a value
   // named by a hash of its own content.  This is deterministic and is
   // unique with high probability.
-  const std::string& outDir = this->Makefile->GetCurrentBinaryDirectory();
+  const std::string& outDir = mf.GetCurrentBinaryDirectory();
   std::string hash = cmSystemTools::ComputeStringMD5(outDir);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-  this->StorePackageRegistryWin(package, outDir.c_str(), hash.c_str());
-#else
-  this->StorePackageRegistryDir(package, outDir.c_str(), hash.c_str());
-#endif
+  StorePackageRegistry(mf, package, outDir.c_str(), hash.c_str());
 
   return true;
 }
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-#  include <windows.h>
 
-void cmExportCommand::ReportRegistryError(std::string const& msg,
-                                          std::string const& key, long err)
+static void ReportRegistryError(cmMakefile& mf, std::string const& msg,
+                                std::string const& key, long err)
 {
   std::ostringstream e;
   e << msg << "\n"
@@ -294,21 +301,19 @@
     e << "Windows reported:\n"
       << "  " << cmsys::Encoding::ToNarrow(winmsg);
   }
-  this->Makefile->IssueMessage(MessageType::WARNING, e.str());
+  mf.IssueMessage(MessageType::WARNING, e.str());
 }
 
-void cmExportCommand::StorePackageRegistryWin(std::string const& package,
-                                              const char* content,
-                                              const char* hash)
+static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
+                                 const char* content, const char* hash)
 {
-  std::string key = "Software\\Kitware\\CMake\\Packages\\";
-  key += package;
+  std::string key = cmStrCat("Software\\Kitware\\CMake\\Packages\\", package);
   HKEY hKey;
   LONG err =
     RegCreateKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(key).c_str(), 0,
                     0, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, 0, &hKey, 0);
   if (err != ERROR_SUCCESS) {
-    this->ReportRegistryError("Cannot create/open registry key", key, err);
+    ReportRegistryError(mf, "Cannot create/open registry key", key, err);
     return;
   }
 
@@ -321,14 +326,13 @@
   if (err != ERROR_SUCCESS) {
     std::ostringstream msg;
     msg << "Cannot set registry value \"" << hash << "\" under key";
-    this->ReportRegistryError(msg.str(), key, err);
+    ReportRegistryError(mf, msg.str(), key, err);
     return;
   }
 }
 #else
-void cmExportCommand::StorePackageRegistryDir(std::string const& package,
-                                              const char* content,
-                                              const char* hash)
+static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
+                                 const char* content, const char* hash)
 {
 #  if defined(__HAIKU__)
   char dir[B_PATH_NAME_LENGTH];
@@ -336,9 +340,7 @@
       B_OK) {
     return;
   }
-  std::string fname = dir;
-  fname += "/cmake/packages/";
-  fname += package;
+  std::string fname = cmStrCat(dir, "/cmake/packages/", package);
 #  else
   std::string fname;
   if (!cmSystemTools::GetEnv("HOME", fname)) {
@@ -362,7 +364,7 @@
         << "  " << fname << "\n"
         << cmSystemTools::GetLastSystemError() << "\n";
       /* clang-format on */
-      this->Makefile->IssueMessage(MessageType::WARNING, e.str());
+      mf.IssueMessage(MessageType::WARNING, e.str());
     }
   }
 }
diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h
index 99f9932..9655628 100644
--- a/Source/cmExportCommand.h
+++ b/Source/cmExportCommand.h
@@ -8,33 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmExportCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmExportCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool HandlePackage(std::vector<std::string> const& args);
-  void StorePackageRegistryWin(std::string const& package, const char* content,
-                               const char* hash);
-  void StorePackageRegistryDir(std::string const& package, const char* content,
-                               const char* hash);
-  void ReportRegistryError(std::string const& msg, std::string const& key,
-                           long err);
-};
+bool cmExportCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index a12e0c4..3d7eccc 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -2,7 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportFileGenerator.h"
 
-#include "cmAlgorithms.h"
+#include <cassert>
+#include <cstring>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
@@ -12,20 +20,13 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 
-#include "cmsys/FStream.hxx"
-#include <assert.h>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <string.h>
-#include <utility>
-
 static std::string cmExportFileGeneratorEscape(std::string const& str)
 {
   // Escape a property value for writing into a .cmake file.
@@ -215,6 +216,9 @@
     if (genexPos == 0) {
       continue;
     }
+    if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) {
+      continue;
+    }
     MessageType messageType = MessageType::FATAL_ERROR;
     std::ostringstream e;
     if (genexPos != std::string::npos) {
@@ -236,9 +240,6 @@
         hadFatalError = true;
       }
     }
-    if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) {
-      continue;
-    }
     if (!cmSystemTools::FileIsFullPath(li)) {
       /* clang-format off */
       e << "Target \"" << target->GetName() << "\" " << prop <<
@@ -380,7 +381,7 @@
   this->ReplaceInstallPrefix(dirs);
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs);
   std::string exportDirs =
-    cge->Evaluate(target->GetLocalGenerator(), "", false, target);
+    cge->Evaluate(target->GetLocalGenerator(), "", target);
 
   if (cge->GetHadContextSensitiveCondition()) {
     cmLocalGenerator* lg = target->GetLocalGenerator();
@@ -499,8 +500,7 @@
   if (!p) {
     return;
   }
-  std::vector<std::string> content;
-  cmSystemTools::ExpandListArgument(p, content);
+  std::vector<std::string> content = cmExpandedList(p);
   ifaceProperties.insert(content.begin(), content.end());
 }
 
@@ -584,8 +584,8 @@
   const ImportPropertyMap& properties)
 {
   if (!properties.empty()) {
-    std::string targetName = this->Namespace;
-    targetName += target->GetExportName();
+    std::string targetName =
+      cmStrCat(this->Namespace, target->GetExportName());
     os << "set_target_properties(" << targetName << " PROPERTIES\n";
     for (auto const& property : properties) {
       os << "  " << property.first << " "
@@ -841,19 +841,16 @@
                                 "IMPORTED_LINK_DEPENDENT_LIBRARIES",
                                 iface->SharedDeps, properties, dummy);
     if (iface->Multiplicity > 0) {
-      std::string prop = "IMPORTED_LINK_INTERFACE_MULTIPLICITY";
-      prop += suffix;
-      std::ostringstream m;
-      m << iface->Multiplicity;
-      properties[prop] = m.str();
+      std::string prop =
+        cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
+      properties[prop] = std::to_string(iface->Multiplicity);
     }
   }
 
   // Add information if this target is a managed target
   if (target->GetManagedType(config) !=
       cmGeneratorTarget::ManagedType::Native) {
-    std::string prop = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
-    prop += suffix;
+    std::string prop = cmStrCat("IMPORTED_COMMON_LANGUAGE_RUNTIME", suffix);
     std::string propval;
     if (auto* p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
       propval = p;
@@ -904,8 +901,7 @@
   }
 
   // Store the property.
-  std::string prop = propName;
-  prop += suffix;
+  std::string prop = cmStrCat(propName, suffix);
   properties[prop] = link_entries;
 }
 
@@ -1182,8 +1178,7 @@
   const std::set<std::string>& importedLocations)
 {
   // Construct the imported target name.
-  std::string targetName = this->Namespace;
-  targetName += target->GetExportName();
+  std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
 
   os << "list(APPEND _IMPORT_CHECK_TARGETS " << targetName
      << " )\n"
@@ -1191,7 +1186,7 @@
      << targetName << " ";
 
   for (std::string const& li : importedLocations) {
-    ImportPropertyMap::const_iterator pi = properties.find(li);
+    auto pi = properties.find(li);
     if (pi != properties.end()) {
       os << cmExportFileGeneratorEscape(pi->second) << " ";
     }
@@ -1205,15 +1200,12 @@
   std::string& errorMessage)
 {
   auto& targetProperties = gte->Target->GetProperties();
-  const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES");
-  if (exportProperties != targetProperties.end()) {
-    std::vector<std::string> propsToExport;
-    cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(),
-                                      propsToExport);
-    for (auto& prop : propsToExport) {
+  if (const char* exportProperties =
+        targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
+    for (auto& prop : cmExpandedList(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/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 747503e..b04a31e 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -5,17 +5,17 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGeneratorExpression.h"
-#include "cmStateTypes.h"
-#include "cmVersion.h"
-#include "cmVersionConfig.h"
-
 #include <iosfwd>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmGeneratorExpression.h"
+#include "cmStateTypes.h"
+#include "cmVersion.h"
+#include "cmVersionConfig.h"
+
 class cmGeneratorTarget;
 
 #define STRINGIFY_HELPER(X) #X
@@ -62,7 +62,7 @@
   bool GenerateImportFile();
 
 protected:
-  typedef std::map<std::string, std::string> ImportPropertyMap;
+  using ImportPropertyMap = std::map<std::string, std::string>;
 
   // Generate per-configuration target information to the given output
   // stream.
diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx
index 9bc8089..2d732c1 100644
--- a/Source/cmExportInstallAndroidMKGenerator.cxx
+++ b/Source/cmExportInstallAndroidMKGenerator.cxx
@@ -2,8 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportInstallAndroidMKGenerator.h"
 
+#include <cstddef>
+#include <memory>
 #include <ostream>
-#include <stddef.h>
 
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportSet.h"
@@ -11,6 +12,7 @@
 #include "cmInstallExportGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
@@ -34,7 +36,8 @@
   }
   os << "_IMPORT_PREFIX := "
      << "$(LOCAL_PATH)" << path << "\n\n";
-  for (cmTargetExport* te : *this->IEGen->GetExportSet()->GetTargetExports()) {
+  for (std::unique_ptr<cmTargetExport> const& te :
+       this->IEGen->GetExportSet()->GetTargetExports()) {
     // Collect import properties for this target.
     if (te->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       continue;
@@ -58,8 +61,7 @@
   std::ostream& os, cmGeneratorTarget const* target,
   cmStateEnums::TargetType /*targetType*/)
 {
-  std::string targetName = this->Namespace;
-  targetName += target->GetExportName();
+  std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
   os << "include $(CLEAR_VARS)\n";
   os << "LOCAL_MODULE := ";
   os << targetName << "\n";
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index f8bc0ab..6d29c99 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -2,9 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportInstallFileGenerator.h"
 
-#include "cmAlgorithms.h"
+#include <memory>
+#include <sstream>
+#include <utility>
+
 #include "cmExportSet.h"
-#include "cmExportSetMap.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -15,13 +17,11 @@
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 
-#include <sstream>
-#include <utility>
-
 cmExportInstallFileGenerator::cmExportInstallFileGenerator(
   cmInstallExportGenerator* iegen)
   : IEGen(iegen)
@@ -30,9 +30,7 @@
 
 std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
 {
-  std::string glob = this->FileBase;
-  glob += "-*";
-  glob += this->FileExt;
+  std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
   return glob;
 }
 
@@ -42,12 +40,12 @@
   {
     std::string expectedTargets;
     std::string sep;
-    for (cmTargetExport* te :
-         *this->IEGen->GetExportSet()->GetTargetExports()) {
+    for (std::unique_ptr<cmTargetExport> const& te :
+         this->IEGen->GetExportSet()->GetTargetExports()) {
       expectedTargets += sep + this->Namespace + te->Target->GetExportName();
       sep = " ";
       if (this->ExportedTargets.insert(te->Target).second) {
-        allTargets.push_back(te);
+        allTargets.push_back(te.get());
       } else {
         std::ostringstream e;
         e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
@@ -96,6 +94,9 @@
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt,
+                                    cmGeneratorExpression::InstallInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
@@ -277,10 +278,7 @@
   }
 
   // Construct the name of the file to generate.
-  std::string fileName = this->FileDir;
-  fileName += "/";
-  fileName += this->FileBase;
-  fileName += "-";
+  std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-');
   if (!config.empty()) {
     fileName += cmSystemTools::LowerCase(config);
   } else {
@@ -319,9 +317,11 @@
   std::vector<std::string>& missingTargets)
 {
   // Add each target in the set to the export.
-  for (cmTargetExport* te : *this->IEGen->GetExportSet()->GetTargetExports()) {
+  for (std::unique_ptr<cmTargetExport> const& te :
+       this->IEGen->GetExportSet()->GetTargetExports()) {
     // Collect import properties for this target.
-    if (this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY) {
+    if (this->GetExportTargetType(te.get()) ==
+        cmStateEnums::INTERFACE_LIBRARY) {
       continue;
     }
 
@@ -392,8 +392,7 @@
 
   if (itgen->IsImportLibrary()) {
     // Construct the property name.
-    std::string prop = "IMPORTED_IMPLIB";
-    prop += suffix;
+    std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
 
     // Append the installed file name.
     value += cmInstallTargetGenerator::GetInstallFilename(
@@ -404,15 +403,14 @@
     importedLocations.insert(prop);
   } else if (itgen->GetTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
     // Construct the property name.
-    std::string prop = "IMPORTED_OBJECTS";
-    prop += suffix;
+    std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
 
     // Compute all the object files inside this target and setup
     // IMPORTED_OBJECTS as a list of object files
     std::vector<std::string> objects;
     itgen->GetInstallObjectNames(config, objects);
     for (std::string& obj : objects) {
-      obj = value + obj;
+      obj = cmStrCat(value, obj);
     }
 
     // Store the property.
@@ -420,8 +418,7 @@
     importedLocations.insert(prop);
   } else {
     // Construct the property name.
-    std::string prop = "IMPORTED_LOCATION";
-    prop += suffix;
+    std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
 
     // Append the installed file name.
     if (target->IsAppBundleOnApple()) {
@@ -458,10 +455,10 @@
 {
   const std::string name = dependee->GetName();
   cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
-  std::vector<std::string> namespaces = this->FindNamespaces(gg, name);
-  int targetOccurrences = static_cast<int>(namespaces.size());
-  if (targetOccurrences == 1) {
-    std::string missingTarget = namespaces[0];
+  auto exportInfo = this->FindNamespaces(gg, name);
+  std::vector<std::string> const& exportFiles = exportInfo.first;
+  if (exportFiles.size() == 1) {
+    std::string missingTarget = exportInfo.second;
 
     missingTarget += dependee->GetExportName();
     link_libs += missingTarget;
@@ -469,23 +466,23 @@
   } else {
     // All exported targets should be known here and should be unique.
     // This is probably user-error.
-    this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences);
+    this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
   }
 }
 
-std::vector<std::string> cmExportInstallFileGenerator::FindNamespaces(
-  cmGlobalGenerator* gg, const std::string& name)
+std::pair<std::vector<std::string>, std::string>
+cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
+                                             const std::string& name)
 {
-  std::vector<std::string> namespaces;
+  std::vector<std::string> exportFiles;
+  std::string ns;
   const cmExportSetMap& exportSets = gg->GetExportSets();
 
   for (auto const& expIt : exportSets) {
-    const cmExportSet* exportSet = expIt.second;
-    std::vector<cmTargetExport*> const* targets =
-      exportSet->GetTargetExports();
+    const cmExportSet& exportSet = expIt.second;
 
     bool containsTarget = false;
-    for (cmTargetExport* target : *targets) {
+    for (auto const& target : exportSet.GetTargetExports()) {
       if (name == target->TargetName) {
         containsTarget = true;
         break;
@@ -494,29 +491,35 @@
 
     if (containsTarget) {
       std::vector<cmInstallExportGenerator const*> const* installs =
-        exportSet->GetInstallations();
+        exportSet.GetInstallations();
       for (cmInstallExportGenerator const* install : *installs) {
-        namespaces.push_back(install->GetNamespace());
+        exportFiles.push_back(install->GetDestinationFile());
+        ns = install->GetNamespace();
       }
     }
   }
 
-  return namespaces;
+  return { exportFiles, ns };
 }
 
 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee, int occurrences)
+  cmGeneratorTarget* depender, cmGeneratorTarget* dependee,
+  std::vector<std::string> const& exportFiles)
 {
   std::ostringstream e;
   e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
     << "\" ...) "
     << "includes target \"" << depender->GetName()
     << "\" which requires target \"" << dependee->GetName() << "\" ";
-  if (occurrences == 0) {
-    e << "that is not in the export set.";
+  if (exportFiles.empty()) {
+    e << "that is not in any export set.";
   } else {
-    e << "that is not in this export set, but " << occurrences
-      << " times in others.";
+    e << "that is not in this export set, but in multiple other export sets: "
+      << cmJoin(exportFiles, ", ") << ".\n";
+    e << "An exported target cannot depend upon another target which is "
+         "exported multiple times. Consider consolidating the exports of the "
+         "\""
+      << dependee->GetName() << "\" target to a single export.";
   }
   cmSystemTools::Error(e.str());
 }
diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h
index cbd6507..5fa812c 100644
--- a/Source/cmExportInstallFileGenerator.h
+++ b/Source/cmExportInstallFileGenerator.h
@@ -5,15 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExportFileGenerator.h"
-#include "cmStateTypes.h"
-
 #include <iosfwd>
 #include <map>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "cmExportFileGenerator.h"
+#include "cmStateTypes.h"
+
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmInstallExportGenerator;
@@ -70,10 +71,10 @@
 
   void ComplainAboutMissingTarget(cmGeneratorTarget* depender,
                                   cmGeneratorTarget* dependee,
-                                  int occurrences);
+                                  std::vector<std::string> const& exportFiles);
 
-  std::vector<std::string> FindNamespaces(cmGlobalGenerator* gg,
-                                          const std::string& name);
+  std::pair<std::vector<std::string>, std::string> FindNamespaces(
+    cmGlobalGenerator* gg, const std::string& name);
 
   /** Generate the relative import prefix.  */
   virtual void GenerateImportPrefix(std::ostream&);
diff --git a/Source/cmExportLibraryDependenciesCommand.cxx b/Source/cmExportLibraryDependenciesCommand.cxx
index b60a053..89093e9 100644
--- a/Source/cmExportLibraryDependenciesCommand.cxx
+++ b/Source/cmExportLibraryDependenciesCommand.cxx
@@ -2,74 +2,49 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportLibraryDependenciesCommand.h"
 
-#include "cmsys/FStream.hxx"
 #include <map>
-#include <memory> // IWYU pragma: keep
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
-bool cmExportLibraryDependenciesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
-{
-  if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
-    return false;
-  }
-
-  // store the arguments for the final pass
-  this->Filename = args[0];
-  this->Append = false;
-  if (args.size() > 1) {
-    if (args[1] == "APPEND") {
-      this->Append = true;
-    }
-  }
-  return true;
-}
-
-void cmExportLibraryDependenciesCommand::FinalPass()
-{
-  // export_library_dependencies() shouldn't modify anything
-  // ensure this by calling a const method
-  this->ConstFinalPass();
-}
-
-void cmExportLibraryDependenciesCommand::ConstFinalPass() const
+static void FinalAction(cmMakefile& makefile, std::string const& filename,
+                        bool append)
 {
   // Use copy-if-different if not appending.
   std::unique_ptr<cmsys::ofstream> foutPtr;
-  if (this->Append) {
+  if (append) {
     const auto openmodeApp = std::ios::app;
-    foutPtr =
-      cm::make_unique<cmsys::ofstream>(this->Filename.c_str(), openmodeApp);
+    foutPtr = cm::make_unique<cmsys::ofstream>(filename.c_str(), openmodeApp);
   } else {
     std::unique_ptr<cmGeneratedFileStream> ap(
-      new cmGeneratedFileStream(this->Filename, true));
+      new cmGeneratedFileStream(filename, true));
     ap->SetCopyIfDifferent(true);
     foutPtr = std::move(ap);
   }
   std::ostream& fout = *foutPtr;
 
   if (!fout) {
-    cmSystemTools::Error("Error Writing " + this->Filename);
+    cmSystemTools::Error("Error Writing " + filename);
     cmSystemTools::ReportLastSystemError("");
     return;
   }
 
   // Collect dependency information about all library targets built in
   // the project.
-  cmake* cm = this->Makefile->GetCMakeInstance();
+  cmake* cm = makefile.GetCMakeInstance();
   cmGlobalGenerator* global = cm->GetGlobalGenerator();
   const std::vector<cmMakefile*>& locals = global->GetMakefiles();
   std::map<std::string, std::string> libDepsOld;
@@ -87,8 +62,7 @@
       }
 
       // Construct the dependency variable name.
-      std::string targetEntry = target.GetName();
-      targetEntry += "_LIB_DEPENDS";
+      std::string targetEntry = cmStrCat(target.GetName(), "_LIB_DEPENDS");
 
       // Construct the dependency variable value with the direct link
       // dependencies.
@@ -97,8 +71,7 @@
       cmTarget::LinkLibraryVectorType const& libs =
         target.GetOriginalLinkLibraries();
       for (cmTarget::LibraryID const& li : libs) {
-        std::string ltVar = li.first;
-        ltVar += "_LINK_TYPE";
+        std::string ltVar = cmStrCat(li.first, "_LINK_TYPE");
         std::string ltValue;
         switch (li.second) {
           case GENERAL_LibraryType:
@@ -166,3 +139,21 @@
   }
   fout << "endif()\n";
 }
+
+bool cmExportLibraryDependenciesCommand(std::vector<std::string> const& args,
+                                        cmExecutionStatus& status)
+{
+  if (args.empty()) {
+    status.SetError("called with incorrect number of arguments");
+    return false;
+  }
+
+  std::string const& filename = args[0];
+  bool const append = args.size() > 1 && args[1] == "APPEND";
+  status.GetMakefile().AddFinalAction(
+    [filename, append](cmMakefile& makefile) {
+      FinalAction(makefile, filename, append);
+    });
+
+  return true;
+}
diff --git a/Source/cmExportLibraryDependenciesCommand.h b/Source/cmExportLibraryDependenciesCommand.h
index 8414866..230c906 100644
--- a/Source/cmExportLibraryDependenciesCommand.h
+++ b/Source/cmExportLibraryDependenciesCommand.h
@@ -8,27 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmExportLibraryDependenciesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override
-  {
-    return new cmExportLibraryDependenciesCommand;
-  }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  void FinalPass() override;
-  bool HasFinalPass() const override { return true; }
-
-private:
-  std::string Filename;
-  bool Append = false;
-  void ConstFinalPass() const;
-};
+bool cmExportLibraryDependenciesCommand(std::vector<std::string> const& args,
+                                        cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmExportSet.cxx b/Source/cmExportSet.cxx
index a6fa186..a20aa9a 100644
--- a/Source/cmExportSet.cxx
+++ b/Source/cmExportSet.cxx
@@ -2,28 +2,43 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportSet.h"
 
-#include "cmAlgorithms.h"
+#include <tuple>
+#include <utility>
+
 #include "cmLocalGenerator.h"
 #include "cmTargetExport.h"
 
-cmExportSet::~cmExportSet()
+cmExportSet::cmExportSet(std::string name)
+  : Name(std::move(name))
 {
-  cmDeleteAll(this->TargetExports);
 }
 
+cmExportSet::~cmExportSet() = default;
+
 void cmExportSet::Compute(cmLocalGenerator* lg)
 {
-  for (cmTargetExport* tgtExport : this->TargetExports) {
+  for (std::unique_ptr<cmTargetExport>& tgtExport : this->TargetExports) {
     tgtExport->Target = lg->FindGeneratorTargetToUse(tgtExport->TargetName);
   }
 }
 
-void cmExportSet::AddTargetExport(cmTargetExport* te)
+void cmExportSet::AddTargetExport(std::unique_ptr<cmTargetExport> te)
 {
-  this->TargetExports.push_back(te);
+  this->TargetExports.emplace_back(std::move(te));
 }
 
 void cmExportSet::AddInstallation(cmInstallExportGenerator const* installation)
 {
   this->Installations.push_back(installation);
 }
+
+cmExportSet& cmExportSetMap::operator[](const std::string& name)
+{
+  auto it = this->find(name);
+  if (it == this->end()) // Export set not found
+  {
+    auto tup_name = std::make_tuple(name);
+    it = this->emplace(std::piecewise_construct, tup_name, tup_name).first;
+  }
+  return it->second;
+}
diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h
index d654c12..f0d921f 100644
--- a/Source/cmExportSet.h
+++ b/Source/cmExportSet.h
@@ -5,8 +5,9 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
+#include <memory>
 #include <string>
-#include <utility>
 #include <vector>
 
 class cmInstallExportGenerator;
@@ -18,10 +19,7 @@
 {
 public:
   /// Construct an empty export set named \a name
-  cmExportSet(std::string name)
-    : Name(std::move(name))
-  {
-  }
+  cmExportSet(std::string name);
   /// Destructor
   ~cmExportSet();
 
@@ -30,15 +28,15 @@
 
   void Compute(cmLocalGenerator* lg);
 
-  void AddTargetExport(cmTargetExport* tgt);
+  void AddTargetExport(std::unique_ptr<cmTargetExport> tgt);
 
   void AddInstallation(cmInstallExportGenerator const* installation);
 
   std::string const& GetName() const { return this->Name; }
 
-  std::vector<cmTargetExport*> const* GetTargetExports() const
+  std::vector<std::unique_ptr<cmTargetExport>> const& GetTargetExports() const
   {
-    return &this->TargetExports;
+    return this->TargetExports;
   }
 
   std::vector<cmInstallExportGenerator const*> const* GetInstallations() const
@@ -47,9 +45,21 @@
   }
 
 private:
-  std::vector<cmTargetExport*> TargetExports;
+  std::vector<std::unique_ptr<cmTargetExport>> TargetExports;
   std::string Name;
   std::vector<cmInstallExportGenerator const*> Installations;
 };
 
+/// A name -> cmExportSet map with overloaded operator[].
+class cmExportSetMap : public std::map<std::string, cmExportSet>
+{
+public:
+  /** \brief Overloaded operator[].
+   *
+   * The operator is overloaded because cmExportSet has no default constructor:
+   * we do not want unnamed export sets.
+   */
+  cmExportSet& operator[](const std::string& name);
+};
+
 #endif
diff --git a/Source/cmExportSetMap.cxx b/Source/cmExportSetMap.cxx
deleted file mode 100644
index 293e80c..0000000
--- a/Source/cmExportSetMap.cxx
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmExportSetMap.h"
-
-#include "cmAlgorithms.h"
-#include "cmExportSet.h"
-
-#include <utility>
-
-cmExportSet* cmExportSetMap::operator[](const std::string& name)
-{
-  std::map<std::string, cmExportSet*>::iterator it = this->find(name);
-  if (it == this->end()) // Export set not found
-  {
-    it = this->insert(std::make_pair(name, new cmExportSet(name))).first;
-  }
-  return it->second;
-}
-
-void cmExportSetMap::clear()
-{
-  cmDeleteAll(*this);
-  this->derived::clear();
-}
-
-cmExportSetMap::cmExportSetMap() = default;
-
-cmExportSetMap::~cmExportSetMap()
-{
-  this->clear();
-}
diff --git a/Source/cmExportSetMap.h b/Source/cmExportSetMap.h
deleted file mode 100644
index 3853732..0000000
--- a/Source/cmExportSetMap.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmExportSetMap_h
-#define cmExportSetMap_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <map>
-#include <string>
-
-class cmExportSet;
-
-/// A name -> cmExportSet map with overloaded operator[].
-class cmExportSetMap : public std::map<std::string, cmExportSet*>
-{
-  typedef std::map<std::string, cmExportSet*> derived;
-
-public:
-  /** \brief Overloaded operator[].
-   *
-   * The operator is overloaded because cmExportSet has no default constructor:
-   * we do not want unnamed export sets.
-   */
-  cmExportSet* operator[](const std::string& name);
-
-  void clear();
-
-  cmExportSetMap();
-
-  /// Overloaded destructor deletes all member export sets.
-  ~cmExportSetMap();
-
-  cmExportSetMap(const cmExportSetMap&) = delete;
-  cmExportSetMap& operator=(const cmExportSetMap&) = delete;
-};
-
-#endif
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index c169032..fafa51b 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -2,6 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportTryCompileFileGenerator.h"
 
+#include <map>
+#include <memory>
+#include <utility>
+
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorTarget.h"
@@ -9,13 +13,9 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <utility>
-
 cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator(
   cmGlobalGenerator* gg, const std::vector<std::string>& targets,
   cmMakefile* mf, std::set<std::string> const& langs)
@@ -74,9 +74,8 @@
 
   cmGeneratorTarget gDummyHead(&dummyHead, tgt->GetLocalGenerator());
 
-  std::string result =
-    cge->Evaluate(tgt->GetLocalGenerator(), this->Config, false, &gDummyHead,
-                  tgt, &dagChecker, language);
+  std::string result = cge->Evaluate(tgt->GetLocalGenerator(), this->Config,
+                                     &gDummyHead, &dagChecker, tgt, language);
 
   const std::set<cmGeneratorTarget const*>& allTargets =
     cge->GetAllTargetsSeen();
@@ -103,8 +102,7 @@
       std::string evalResult =
         this->FindTargets(p, target, std::string(), emitted);
 
-      std::vector<std::string> depends;
-      cmSystemTools::ExpandListArgument(evalResult, depends);
+      std::vector<std::string> depends = cmExpandedList(evalResult);
       for (std::string const& li : depends) {
         cmGeneratorTarget* tgt =
           target->GetLocalGenerator()->FindGeneratorTargetToUse(li);
diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h
index 2a2ba7e..7573427 100644
--- a/Source/cmExportTryCompileFileGenerator.h
+++ b/Source/cmExportTryCompileFileGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExportFileGenerator.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmExportFileGenerator.h"
+
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmMakefile;
diff --git a/Source/cmExprParserHelper.cxx b/Source/cmExprParserHelper.cxx
index 80c78a3..56dfc6c 100644
--- a/Source/cmExprParserHelper.cxx
+++ b/Source/cmExprParserHelper.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExprParserHelper.h"
 
-#include "cmExprLexer.h"
-
 #include <iostream>
 #include <sstream>
 #include <stdexcept>
 #include <utility>
 
+#include "cmExprLexer.h"
+#include "cmStringAlgorithms.h"
+
 int cmExpr_yyparse(yyscan_t yyscanner);
 //
 cmExprParserHelper::cmExprParserHelper()
@@ -41,16 +42,13 @@
   try {
     int res = cmExpr_yyparse(yyscanner);
     if (res != 0) {
-      std::string e = "cannot parse the expression: \"" + InputBuffer + "\": ";
-      e += ErrorString;
-      e += ".";
+      std::string e = cmStrCat("cannot parse the expression: \"", InputBuffer,
+                               "\": ", ErrorString, '.');
       this->SetError(std::move(e));
     }
   } catch (std::runtime_error const& fail) {
-    std::string e =
-      "cannot evaluate the expression: \"" + InputBuffer + "\": ";
-    e += fail.what();
-    e += ".";
+    std::string e = cmStrCat("cannot evaluate the expression: \"", InputBuffer,
+                             "\": ", fail.what(), '.');
     this->SetError(std::move(e));
   } catch (std::out_of_range const&) {
     std::string e = "cannot evaluate the expression: \"" + InputBuffer +
diff --git a/Source/cmExprParserHelper.h b/Source/cmExprParserHelper.h
index 42c460a..eaf5dc7 100644
--- a/Source/cmExprParserHelper.h
+++ b/Source/cmExprParserHelper.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_kwiml.h"
-
 #include <string>
 #include <vector>
 
+#include "cm_kwiml.h"
+
 class cmExprParserHelper
 {
 public:
diff --git a/Source/cmExternalMakefileProjectGenerator.cxx b/Source/cmExternalMakefileProjectGenerator.cxx
index ac54811..5895d66 100644
--- a/Source/cmExternalMakefileProjectGenerator.cxx
+++ b/Source/cmExternalMakefileProjectGenerator.cxx
@@ -4,6 +4,8 @@
 
 #include <utility>
 
+#include "cmStringAlgorithms.h"
+
 class cmMakefile;
 
 void cmExternalMakefileProjectGenerator::EnableLanguage(
@@ -18,8 +20,7 @@
   std::string fullName;
   if (!globalGenerator.empty()) {
     if (!extraGenerator.empty()) {
-      fullName = extraGenerator;
-      fullName += " - ";
+      fullName = cmStrCat(extraGenerator, " - ");
     }
     fullName += globalGenerator;
   }
diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx
index f47744b..5a5d959 100644
--- a/Source/cmExtraCodeBlocksGenerator.cxx
+++ b/Source/cmExtraCodeBlocksGenerator.cxx
@@ -16,6 +16,7 @@
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
@@ -73,10 +74,9 @@
   std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
   std::string projectName = lgs[0]->GetProjectName();
 
-  std::string filename = outputDir + "/";
-  filename += projectName + ".cbp";
-  std::string sessionFilename = outputDir + "/";
-  sessionFilename += projectName + ".layout";
+  std::string filename = cmStrCat(outputDir, '/', projectName, ".cbp");
+  std::string sessionFilename =
+    cmStrCat(outputDir, '/', projectName, ".layout");
 
   this->CreateNewProjectFile(lgs, filename);
 }
@@ -178,18 +178,18 @@
 {
   for (std::string const& f : files) {
     xml.StartElement("Unit");
-    xml.Attribute("filename", fsPath + path + "/" + f);
+    xml.Attribute("filename", cmStrCat(fsPath, path, "/", f));
 
     xml.StartElement("Option");
     xml.Attribute("virtualFolder",
-                  "CMake Files\\" + virtualFolderPath + path + "\\");
+                  cmStrCat("CMake Files\\", virtualFolderPath, path, "\\"));
     xml.EndElement();
 
     xml.EndElement();
   }
   for (Tree const& folder : folders) {
-    folder.BuildUnitImpl(xml, virtualFolderPath + path + "\\",
-                         fsPath + path + "/");
+    folder.BuildUnitImpl(xml, cmStrCat(virtualFolderPath, path, "\\"),
+                         cmStrCat(fsPath, path, "/"));
   }
 }
 
@@ -234,7 +234,7 @@
       // Also we can disable external (outside the project) files by setting ON
       // CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable.
       const bool excludeExternal =
-        cmSystemTools::IsOn(it.second[0]->GetMakefile()->GetSafeDefinition(
+        cmIsOn(it.second[0]->GetMakefile()->GetSafeDefinition(
           "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"));
       if (!splitted.empty() &&
           (!excludeExternal || (relative.find("..") == std::string::npos)) &&
@@ -318,8 +318,7 @@
           cmGeneratorTarget* gt = target;
           this->AppendTarget(xml, targetName, gt, make, lg, compiler,
                              makeArgs);
-          std::string fastTarget = targetName;
-          fastTarget += "/fast";
+          std::string fastTarget = cmStrCat(targetName, "/fast");
           this->AppendTarget(xml, fastTarget, gt, make, lg, compiler,
                              makeArgs);
         } break;
@@ -334,7 +333,7 @@
   // Collect all used source files in the project.
   // Keep a list of C/C++ source files which might have an accompanying header
   // that should be looked for.
-  typedef std::map<std::string, CbpUnit> all_files_map_t;
+  using all_files_map_t = std::map<std::string, CbpUnit>;
   all_files_map_t allFiles;
   std::vector<std::string> cFiles;
 
@@ -366,13 +365,13 @@
 
             // check whether it is a C/C++/CUDA implementation file
             bool isCFile = false;
-            std::string lang = s->GetLanguage();
+            std::string lang = s->GetOrDetermineLanguage();
             if (lang == "C" || lang == "CXX" || lang == "CUDA") {
               std::string const& srcext = s->GetExtension();
               isCFile = cm->IsSourceExtension(srcext);
             }
 
-            std::string const& fullPath = s->GetFullPath();
+            std::string const& fullPath = s->ResolveFullPath();
 
             // Check file position relative to project root dir.
             const std::string relative =
@@ -380,7 +379,7 @@
             // Do not add this file if it has ".." in relative path and
             // if CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable is on.
             const bool excludeExternal =
-              cmSystemTools::IsOn(lg->GetMakefile()->GetSafeDefinition(
+              cmIsOn(lg->GetMakefile()->GetSafeDefinition(
                 "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"));
             if (excludeExternal &&
                 (relative.find("..") != std::string::npos)) {
@@ -412,15 +411,13 @@
   // A very similar version of that code exists also in the CodeLite
   // project generator.
   for (std::string const& fileName : cFiles) {
-    std::string headerBasename = cmSystemTools::GetFilenamePath(fileName);
-    headerBasename += "/";
-    headerBasename += cmSystemTools::GetFilenameWithoutExtension(fileName);
+    std::string headerBasename =
+      cmStrCat(cmSystemTools::GetFilenamePath(fileName), '/',
+               cmSystemTools::GetFilenameWithoutExtension(fileName));
 
     // check if there's a matching header around
     for (std::string const& ext : headerExts) {
-      std::string hname = headerBasename;
-      hname += ".";
-      hname += ext;
+      std::string hname = cmStrCat(headerBasename, '.', ext);
       // if it's already in the set, don't check if it exists on disk
       if (allFiles.find(hname) != allFiles.end()) {
         break;
@@ -465,12 +462,9 @@
   // this file doesn't seem to be used by C::B in custom makefile mode,
   // but we generate a unique file for each OBJECT library so in case
   // C::B uses it in some way, the targets don't interfere with each other.
-  std::string filename = lg->GetCurrentBinaryDirectory();
-  filename += "/";
-  filename += lg->GetTargetDirectory(target);
-  filename += "/";
-  filename += target->GetName();
-  filename += ".objlib";
+  std::string filename = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
+                                  lg->GetTargetDirectory(target), '/',
+                                  target->GetName(), ".objlib");
   cmGeneratedFileStream fout(filename);
   if (fout) {
     /* clang-format off */
@@ -490,8 +484,8 @@
   const std::string& compiler, const std::string& makeFlags)
 {
   cmMakefile const* makefile = lg->GetMakefile();
-  std::string makefileName = lg->GetCurrentBinaryDirectory();
-  makefileName += "/Makefile";
+  std::string makefileName =
+    cmStrCat(lg->GetCurrentBinaryDirectory(), "/Makefile");
 
   xml.StartElement("Target");
   xml.Attribute("title", targetName);
@@ -570,19 +564,16 @@
     std::string systemIncludeDirs = makefile->GetSafeDefinition(
       "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
     if (!systemIncludeDirs.empty()) {
-      cmAppend(allIncludeDirs,
-               cmSystemTools::ExpandedListArgument(systemIncludeDirs));
+      cmAppend(allIncludeDirs, cmExpandedList(systemIncludeDirs));
     }
 
     systemIncludeDirs = makefile->GetSafeDefinition(
       "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
     if (!systemIncludeDirs.empty()) {
-      cmAppend(allIncludeDirs,
-               cmSystemTools::ExpandedListArgument(systemIncludeDirs));
+      cmAppend(allIncludeDirs, cmExpandedList(systemIncludeDirs));
     }
 
-    std::vector<std::string>::const_iterator end =
-      cmRemoveDuplicates(allIncludeDirs);
+    auto end = cmRemoveDuplicates(allIncludeDirs);
 
     for (std::string const& str : cmMakeRange(allIncludeDirs.cbegin(), end)) {
       xml.StartElement("Add");
diff --git a/Source/cmExtraCodeBlocksGenerator.h b/Source/cmExtraCodeBlocksGenerator.h
index 173e284..d9f92bd 100644
--- a/Source/cmExtraCodeBlocksGenerator.h
+++ b/Source/cmExtraCodeBlocksGenerator.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExternalMakefileProjectGenerator.h"
-
 #include <string>
 #include <vector>
 
+#include "cmExternalMakefileProjectGenerator.h"
+
 class cmGeneratorTarget;
 class cmLocalGenerator;
 class cmMakefile;
diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx
index 6fe8c14..c7b7457 100644
--- a/Source/cmExtraCodeLiteGenerator.cxx
+++ b/Source/cmExtraCodeLiteGenerator.cxx
@@ -2,6 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExtraCodeLiteGenerator.h"
 
+#include <cstring>
+#include <map>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include "cmsys/SystemInformation.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -9,17 +17,11 @@
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
 
-#include "cmsys/SystemInformation.hxx"
-#include <map>
-#include <set>
-#include <sstream>
-#include <string.h>
-#include <utility>
-
 cmExtraCodeLiteGenerator::cmExtraCodeLiteGenerator()
   : ConfigName("NoConfig")
 {
@@ -67,8 +69,8 @@
       workspaceOutputDir = lg->GetCurrentBinaryDirectory();
       workspaceProjectName = lg->GetProjectName();
       workspaceSourcePath = lg->GetSourceDirectory();
-      workspaceFileName = workspaceOutputDir + "/";
-      workspaceFileName += workspaceProjectName + ".workspace";
+      workspaceFileName =
+        cmStrCat(workspaceOutputDir, '/', workspaceProjectName, ".workspace");
       this->WorkspacePath = lg->GetCurrentBinaryDirectory();
       break;
     }
@@ -121,7 +123,7 @@
       cmStateEnums::TargetType type = lt->GetType();
       std::string const& outputDir = lg->GetCurrentBinaryDirectory();
       std::string targetName = lt->GetName();
-      std::string filename = outputDir + "/" + targetName + ".project";
+      std::string filename = cmStrCat(outputDir, "/", targetName, ".project");
       retval.push_back(targetName);
       // Make the project file relative to the workspace
       std::string relafilename =
@@ -131,7 +133,7 @@
         case cmStateEnums::SHARED_LIBRARY:
         case cmStateEnums::STATIC_LIBRARY:
         case cmStateEnums::MODULE_LIBRARY:
-          visualname = "lib" + visualname;
+          visualname = cmStrCat("lib", visualname);
           CM_FALLTHROUGH;
         case cmStateEnums::EXECUTABLE:
           xml->StartElement("Project");
@@ -161,7 +163,7 @@
     std::string const& outputDir = it.second[0]->GetCurrentBinaryDirectory();
     std::string projectName = it.second[0]->GetProjectName();
     retval.push_back(projectName);
-    std::string filename = outputDir + "/" + projectName + ".project";
+    std::string filename = cmStrCat(outputDir, "/", projectName, ".project");
 
     // Make the project file relative to the workspace
     filename = cmSystemTools::RelativePath(this->WorkspacePath, filename);
@@ -217,22 +219,21 @@
     case cmStateEnums::STATIC_LIBRARY:
     case cmStateEnums::SHARED_LIBRARY:
     case cmStateEnums::MODULE_LIBRARY: {
+      cmake const* cm = makefile->GetCMakeInstance();
       std::vector<cmSourceFile*> sources;
       gt->GetSourceFiles(sources,
                          makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
       for (cmSourceFile* s : sources) {
+        std::string const& fullPath = s->ResolveFullPath();
+        std::string const& extLower =
+          cmSystemTools::LowerCase(s->GetExtension());
         // check whether it is a source or a include file
         // then put it accordingly into one of the two containers
-        switch (cmSystemTools::GetFileFormat(s->GetExtension())) {
-          case cmSystemTools::C_FILE_FORMAT:
-          case cmSystemTools::CXX_FILE_FORMAT:
-          case cmSystemTools::CUDA_FILE_FORMAT:
-          case cmSystemTools::FORTRAN_FILE_FORMAT: {
-            cFiles[s->GetFullPath()] = s;
-          } break;
-          default: {
-            otherFiles.insert(s->GetFullPath());
-          }
+        if (cm->IsSourceExtension(extLower) || cm->IsCudaExtension(extLower) ||
+            cm->IsFortranExtension(extLower)) {
+          cFiles[fullPath] = s;
+        } else {
+          otherFiles.insert(fullPath);
         }
       }
     }
@@ -299,17 +300,15 @@
   // A very similar version of that code exists also in the CodeBlocks
   // project generator.
   for (auto const& sit : cFiles) {
-    std::string headerBasename = cmSystemTools::GetFilenamePath(sit.first);
-    headerBasename += "/";
-    headerBasename += cmSystemTools::GetFilenameWithoutExtension(sit.first);
+    std::string headerBasename =
+      cmStrCat(cmSystemTools::GetFilenamePath(sit.first), '/',
+               cmSystemTools::GetFilenameWithoutExtension(sit.first));
 
     // check if there's a matching header around
     for (std::string const& ext : headerExts) {
-      std::string hname = headerBasename;
-      hname += ".";
-      hname += ext;
+      std::string hname = cmStrCat(headerBasename, '.', ext);
       // if it's already in the set, don't check if it exists on disk
-      std::set<std::string>::const_iterator headerIt = otherFiles.find(hname);
+      auto headerIt = otherFiles.find(hname);
       if (headerIt != otherFiles.end()) {
         break;
       }
diff --git a/Source/cmExtraCodeLiteGenerator.h b/Source/cmExtraCodeLiteGenerator.h
index dea7ebc..0ce90b0 100644
--- a/Source/cmExtraCodeLiteGenerator.h
+++ b/Source/cmExtraCodeLiteGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExternalMakefileProjectGenerator.h"
-
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmExternalMakefileProjectGenerator.h"
+
 class cmLocalGenerator;
 class cmMakefile;
 class cmGeneratorTarget;
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx
index aece3bc..b286acf 100644
--- a/Source/cmExtraEclipseCDT4Generator.cxx
+++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -2,14 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExtraEclipseCDT4Generator.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
 #include <map>
 #include <sstream>
-#include <stdio.h>
 #include <utility>
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -21,6 +22,7 @@
 #include "cmSourceGroup.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
@@ -44,6 +46,8 @@
 
 cmExtraEclipseCDT4Generator::cmExtraEclipseCDT4Generator()
 {
+  this->IsOutOfSourceBuild = false;
+  this->GenerateSourceProject = false;
   this->SupportsVirtualFolders = true;
   this->GenerateLinkedResources = true;
   this->SupportsGmakeErrorParser = true;
@@ -164,6 +168,29 @@
 
   // create a .cproject file
   this->CreateCProjectFile();
+
+  // create resource settings
+  this->CreateSettingsResourcePrefsFile();
+}
+
+void cmExtraEclipseCDT4Generator::CreateSettingsResourcePrefsFile()
+{
+  cmLocalGenerator* lg = this->GlobalGenerator->GetLocalGenerators()[0];
+  cmMakefile* mf = lg->GetMakefile();
+
+  const std::string filename =
+    this->HomeOutputDirectory + "/.settings/org.eclipse.core.resources.prefs";
+
+  cmGeneratedFileStream fout(filename);
+  if (!fout) {
+    return;
+  }
+
+  fout << "eclipse.preferences.version=1" << std::endl;
+  const char* encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING");
+  if (encoding) {
+    fout << "encoding/<project>=" << encoding << std::endl;
+  }
 }
 
 void cmExtraEclipseCDT4Generator::CreateSourceProjectFile()
@@ -214,8 +241,7 @@
   std::string envVarValue;
   const bool envVarSet = cmSystemTools::GetEnv(envVar, envVarValue);
 
-  std::string cacheEntryName = "CMAKE_ECLIPSE_ENVVAR_";
-  cacheEntryName += envVar;
+  std::string cacheEntryName = cmStrCat("CMAKE_ECLIPSE_ENVVAR_", envVar);
   const std::string* cacheValue =
     lg->GetState()->GetInitializedCacheValue(cacheEntryName);
 
@@ -390,8 +416,7 @@
 
   if (const char* extraNaturesProp =
         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
-    std::vector<std::string> extraNatures;
-    cmSystemTools::ExpandListArgument(extraNaturesProp, extraNatures);
+    std::vector<std::string> extraNatures = cmExpandedList(extraNaturesProp);
     for (std::string const& n : extraNatures) {
       xml.Element("nature", n);
     }
@@ -438,9 +463,7 @@
   cmXMLWriter& xml)
 {
   for (cmSourceGroup const& sg : sourceGroups) {
-    std::string linkName3 = linkName;
-    linkName3 += "/";
-    linkName3 += sg.GetFullName();
+    std::string linkName3 = cmStrCat(linkName, '/', sg.GetFullName());
 
     std::replace(linkName3.begin(), linkName3.end(), '\\', '/');
 
@@ -455,9 +478,8 @@
       std::string const& fullPath = file->GetFullPath();
 
       if (!cmSystemTools::FileIsDirectory(fullPath)) {
-        std::string linkName4 = linkName3;
-        linkName4 += "/";
-        linkName4 += cmSystemTools::GetFilenameName(fullPath);
+        std::string linkName4 =
+          cmStrCat(linkName3, '/', cmSystemTools::GetFilenameName(fullPath));
         cmExtraEclipseCDT4Generator::AppendLinkedResource(
           xml, linkName4,
           cmExtraEclipseCDT4Generator::GetEclipsePath(fullPath), LinkToFile);
@@ -477,8 +499,7 @@
     const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets();
 
     for (cmGeneratorTarget* target : targets) {
-      std::string linkName2 = linkName;
-      linkName2 += "/";
+      std::string linkName2 = cmStrCat(linkName, '/');
       switch (target->GetType()) {
         case cmStateEnums::EXECUTABLE:
         case cmStateEnums::STATIC_LIBRARY:
@@ -504,7 +525,7 @@
                              makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
           for (cmSourceFile* sf : files) {
             // Add the file to the list of sources.
-            std::string const& source = sf->GetFullPath();
+            std::string const& source = sf->ResolveFullPath();
             cmSourceGroup* sourceGroup =
               makefile->FindSourceGroup(source, sourceGroups);
             sourceGroup->AssignSource(sf);
@@ -540,8 +561,7 @@
     // .project itself
     if ((baseDir != linkSourceDirectory) &&
         !cmSystemTools::IsSubDirectory(baseDir, linkSourceDirectory)) {
-      std::string linkName = "[Subprojects]/";
-      linkName += it.first;
+      std::string linkName = cmStrCat("[Subprojects]/", it.first);
       cmExtraEclipseCDT4Generator::AppendLinkedResource(
         xml, linkName,
         cmExtraEclipseCDT4Generator::GetEclipsePath(linkSourceDirectory),
@@ -777,12 +797,11 @@
     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS");
   if (this->CEnabled && cDefs) {
     // Expand the list.
-    std::vector<std::string> defs;
-    cmSystemTools::ExpandListArgument(cDefs, defs, true);
+    std::vector<std::string> defs = cmExpandedList(cDefs, true);
 
     // the list must contain only definition-value pairs:
     if ((defs.size() % 2) == 0) {
-      std::vector<std::string>::const_iterator di = defs.begin();
+      auto di = defs.begin();
       while (di != defs.end()) {
         std::string def = *di;
         ++di;
@@ -810,12 +829,11 @@
     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS");
   if (this->CXXEnabled && cxxDefs) {
     // Expand the list.
-    std::vector<std::string> defs;
-    cmSystemTools::ExpandListArgument(cxxDefs, defs, true);
+    std::vector<std::string> defs = cmExpandedList(cxxDefs, true);
 
     // the list must contain only definition-value pairs:
     if ((defs.size() % 2) == 0) {
-      std::vector<std::string>::const_iterator di = defs.begin();
+      auto di = defs.begin();
       while (di != defs.end()) {
         std::string def = *di;
         ++di;
@@ -861,16 +879,14 @@
   if (this->CEnabled && !compiler.empty()) {
     std::string systemIncludeDirs =
       mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
-    std::vector<std::string> dirs;
-    cmSystemTools::ExpandListArgument(systemIncludeDirs, dirs);
+    std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
     this->AppendIncludeDirectories(xml, dirs, emmited);
   }
   compiler = mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
   if (this->CXXEnabled && !compiler.empty()) {
     std::string systemIncludeDirs =
       mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
-    std::vector<std::string> dirs;
-    cmSystemTools::ExpandListArgument(systemIncludeDirs, dirs);
+    std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
     this->AppendIncludeDirectories(xml, dirs, emmited);
   }
 
@@ -944,28 +960,21 @@
                                                            : "[lib] ");
           cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
                                                     makeArgs, subdir, prefix);
-          std::string fastTarget = targetName;
-          fastTarget += "/fast";
+          std::string fastTarget = cmStrCat(targetName, "/fast");
           cmExtraEclipseCDT4Generator::AppendTarget(xml, fastTarget, make,
                                                     makeArgs, subdir, prefix);
 
           // Add Build and Clean targets in the virtual folder of targets:
           if (this->SupportsVirtualFolders) {
-            std::string virtDir = "[Targets]/";
-            virtDir += prefix;
-            virtDir += targetName;
-            std::string buildArgs = "-C \"";
-            buildArgs += lgen->GetBinaryDirectory();
-            buildArgs += "\" ";
-            buildArgs += makeArgs;
+            std::string virtDir = cmStrCat("[Targets]/", prefix, targetName);
+            std::string buildArgs =
+              cmStrCat("-C \"", lgen->GetBinaryDirectory(), "\" ", makeArgs);
             cmExtraEclipseCDT4Generator::AppendTarget(
               xml, "Build", make, buildArgs, virtDir, "", targetName.c_str());
 
-            std::string cleanArgs = "-E chdir \"";
-            cleanArgs += lgen->GetCurrentBinaryDirectory();
-            cleanArgs += "\" \"";
-            cleanArgs += cmSystemTools::GetCMakeCommand();
-            cleanArgs += "\" -P \"";
+            std::string cleanArgs =
+              cmStrCat("-E chdir \"", lgen->GetCurrentBinaryDirectory(),
+                       "\" \"", cmSystemTools::GetCMakeCommand(), "\" -P \"");
             cmGeneratorTarget* gt = target;
             cleanArgs += lgen->GetTargetDirectory(gt);
             cleanArgs += "/cmake_clean.cmake\"";
diff --git a/Source/cmExtraEclipseCDT4Generator.h b/Source/cmExtraEclipseCDT4Generator.h
index 5136660..ff4c59e 100644
--- a/Source/cmExtraEclipseCDT4Generator.h
+++ b/Source/cmExtraEclipseCDT4Generator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExternalMakefileProjectGenerator.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmExternalMakefileProjectGenerator.h"
+
 class cmLocalGenerator;
 class cmMakefile;
 class cmSourceGroup;
@@ -43,6 +43,9 @@
   // create .project file in the source tree
   void CreateSourceProjectFile();
 
+  // create .settings/org.eclipse.core.resources.prefs
+  void CreateSettingsResourcePrefsFile();
+
   // create .project file
   void CreateProjectFile();
 
diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx
index 877f109..e8c9dd0 100644
--- a/Source/cmExtraKateGenerator.cxx
+++ b/Source/cmExtraKateGenerator.cxx
@@ -2,6 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExtraKateGenerator.h"
 
+#include <cstring>
+#include <ostream>
+#include <set>
+#include <vector>
+
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -9,13 +14,9 @@
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <ostream>
-#include <set>
-#include <string.h>
-#include <vector>
-
 cmExtraKateGenerator::cmExtraKateGenerator() = default;
 
 cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory()
@@ -53,8 +54,7 @@
 void cmExtraKateGenerator::CreateKateProjectFile(
   const cmLocalGenerator* lg) const
 {
-  std::string filename = lg->GetBinaryDirectory();
-  filename += "/.kateproject";
+  std::string filename = cmStrCat(lg->GetBinaryDirectory(), "/.kateproject");
   cmGeneratedFileStream fout(filename);
   if (!fout) {
     return;
@@ -164,8 +164,7 @@
         case cmStateEnums::OBJECT_LIBRARY: {
           this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
                              homeOutputDir);
-          std::string fastTarget = targetName;
-          fastTarget += "/fast";
+          std::string fastTarget = cmStrCat(targetName, "/fast");
           this->AppendTarget(fout, fastTarget, make, makeArgs, currentDir,
                              homeOutputDir);
 
@@ -208,10 +207,8 @@
 void cmExtraKateGenerator::CreateDummyKateProjectFile(
   const cmLocalGenerator* lg) const
 {
-  std::string filename = lg->GetBinaryDirectory();
-  filename += "/";
-  filename += this->ProjectName;
-  filename += ".kateproject";
+  std::string filename =
+    cmStrCat(lg->GetBinaryDirectory(), '/', this->ProjectName, ".kateproject");
   cmGeneratedFileStream fout(filename);
   if (!fout) {
     return;
@@ -224,20 +221,17 @@
 std::string cmExtraKateGenerator::GenerateFilesString(
   const cmLocalGenerator* lg) const
 {
-  std::string s = lg->GetSourceDirectory();
-  s += "/.git";
+  std::string s = cmStrCat(lg->GetSourceDirectory(), "/.git");
   if (cmSystemTools::FileExists(s)) {
     return "\"git\": 1 ";
   }
 
-  s = lg->GetSourceDirectory();
-  s += "/.svn";
+  s = cmStrCat(lg->GetSourceDirectory(), "/.svn");
   if (cmSystemTools::FileExists(s)) {
     return "\"svn\": 1 ";
   }
 
-  s = lg->GetSourceDirectory();
-  s += "/";
+  s = cmStrCat(lg->GetSourceDirectory(), '/');
 
   std::set<std::string> files;
   std::string tmp;
@@ -260,7 +254,7 @@
         continue;
       }
 
-      tmp = sf->GetFullPath();
+      tmp = sf->ResolveFullPath();
       files.insert(tmp);
     }
   }
diff --git a/Source/cmExtraKateGenerator.h b/Source/cmExtraKateGenerator.h
index a4355f0..be1376a 100644
--- a/Source/cmExtraKateGenerator.h
+++ b/Source/cmExtraKateGenerator.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExternalMakefileProjectGenerator.h"
-
 #include <string>
 
+#include "cmExternalMakefileProjectGenerator.h"
+
 class cmGeneratedFileStream;
 class cmLocalGenerator;
 
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
index 71c8fcd..495324c 100644
--- a/Source/cmExtraSublimeTextGenerator.cxx
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -2,12 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExtraSublimeTextGenerator.h"
 
-#include "cmsys/RegularExpression.hxx"
+#include <cstring>
 #include <set>
 #include <sstream>
-#include <string.h>
 #include <utility>
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -17,6 +18,7 @@
 #include "cmMessageType.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -133,8 +135,7 @@
   // End of build_systems
   fout << "\n\t]";
   std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
-  std::vector<std::string> tokens;
-  cmSystemTools::ExpandListArgument(this->EnvSettings, tokens);
+  std::vector<std::string> tokens = cmExpandedList(this->EnvSettings);
 
   if (!this->EnvSettings.empty()) {
     fout << ",";
@@ -218,8 +219,7 @@
           this->AppendTarget(fout, targetName, lg, target, make.c_str(),
                              makefile, compiler.c_str(), sourceFileFlags,
                              false);
-          std::string fastTarget = targetName;
-          fastTarget += "/fast";
+          std::string fastTarget = cmStrCat(targetName, "/fast");
           this->AppendTarget(fout, fastTarget, lg, target, make.c_str(),
                              makefile, compiler.c_str(), sourceFileFlags,
                              false);
@@ -243,13 +243,13 @@
     target->GetSourceFiles(sourceFiles,
                            makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
     for (cmSourceFile* sourceFile : sourceFiles) {
-      MapSourceFileFlags::iterator sourceFileFlagsIter =
-        sourceFileFlags.find(sourceFile->GetFullPath());
+      auto sourceFileFlagsIter =
+        sourceFileFlags.find(sourceFile->ResolveFullPath());
       if (sourceFileFlagsIter == sourceFileFlags.end()) {
         sourceFileFlagsIter =
           sourceFileFlags
-            .insert(MapSourceFileFlags::value_type(sourceFile->GetFullPath(),
-                                                   std::vector<std::string>()))
+            .insert(MapSourceFileFlags::value_type(
+              sourceFile->ResolveFullPath(), std::vector<std::string>()))
             .first;
       }
       std::vector<std::string>& flags = sourceFileFlagsIter->second;
@@ -266,7 +266,7 @@
         R"((^|[ ])-[DIOUWfgs][^= ]+(=\"[^"]+\"|=[^"][^ ]+)?)";
       flagRegex.compile(regexString);
       std::string workString =
-        flagsString + " " + definesString + " " + includesString;
+        cmStrCat(flagsString, " ", definesString, " ", includesString);
       while (flagRegex.find(workString)) {
         std::string::size_type start = flagRegex.start();
         if (workString[start] == ' ') {
@@ -310,8 +310,7 @@
 std::string cmExtraSublimeTextGenerator::BuildMakeCommand(
   const std::string& make, const char* makefile, const std::string& target)
 {
-  std::string command = "\"";
-  command += make + "\"";
+  std::string command = cmStrCat('"', make, '"');
   std::string generator = this->GlobalGenerator->GetName();
   if (generator == "NMake Makefiles") {
     std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
@@ -344,7 +343,7 @@
   cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* gtgt)
 {
   std::string flags;
-  std::string language = source->GetLanguage();
+  std::string language = source->GetOrDetermineLanguage();
   if (language.empty()) {
     language = "C";
   }
@@ -379,7 +378,7 @@
 {
   std::set<std::string> defines;
   cmMakefile* makefile = lg->GetMakefile();
-  const std::string& language = source->GetLanguage();
+  const std::string& language = source->GetOrDetermineLanguage();
   const std::string& config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target,
                                                     language);
@@ -392,8 +391,8 @@
       defines, genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS));
   }
 
-  std::string defPropName = "COMPILE_DEFINITIONS_";
-  defPropName += cmSystemTools::UpperCase(config);
+  std::string defPropName =
+    cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
   if (const char* config_compile_defs = source->GetProperty(defPropName)) {
     lg->AppendDefines(
       defines,
@@ -412,7 +411,7 @@
 {
   std::vector<std::string> includes;
   cmMakefile* makefile = lg->GetMakefile();
-  const std::string& language = source->GetLanguage();
+  const std::string& language = source->GetOrDetermineLanguage();
   const std::string& config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target,
                                                     language);
@@ -444,7 +443,7 @@
   if (!sublExecutable) {
     return false;
   }
-  if (cmSystemTools::IsNOTFOUND(sublExecutable)) {
+  if (cmIsNOTFOUND(sublExecutable)) {
     return false;
   }
 
diff --git a/Source/cmExtraSublimeTextGenerator.h b/Source/cmExtraSublimeTextGenerator.h
index bc158f6..7e8f2d4 100644
--- a/Source/cmExtraSublimeTextGenerator.h
+++ b/Source/cmExtraSublimeTextGenerator.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmExternalMakefileProjectGenerator.h"
-
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmExternalMakefileProjectGenerator.h"
+
 class cmGeneratedFileStream;
 class cmGeneratorTarget;
 class cmLocalGenerator;
@@ -24,7 +24,7 @@
 {
 public:
   static cmExternalMakefileProjectGeneratorFactory* GetFactory();
-  typedef std::map<std::string, std::vector<std::string>> MapSourceFileFlags;
+  using MapSourceFileFlags = std::map<std::string, std::vector<std::string>>;
   cmExtraSublimeTextGenerator();
 
   void Generate() override;
diff --git a/Source/cmFLTKWrapUICommand.cxx b/Source/cmFLTKWrapUICommand.cxx
index 89629c7..11844e4 100644
--- a/Source/cmFLTKWrapUICommand.cxx
+++ b/Source/cmFLTKWrapUICommand.cxx
@@ -2,122 +2,121 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFLTKWrapUICommand.h"
 
-#include <stddef.h>
+#include <cstddef>
 
 #include "cmCustomCommandLines.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
 class cmTarget;
 
-// cmFLTKWrapUICommand
-bool cmFLTKWrapUICommand::InitialPass(std::vector<std::string> const& args,
-                                      cmExecutionStatus&)
+static void FinalAction(cmMakefile& makefile, std::string const& name)
+{
+  // people should add the srcs to the target themselves, but the old command
+  // didn't support that, so check and see if they added the files in and if
+  // they didn;t then print a warning and add then anyhow
+  cmTarget* target = makefile.FindLocalNonAliasTarget(name);
+  if (!target) {
+    std::string msg = cmStrCat(
+      "FLTK_WRAP_UI was called with a target that was never created: ", name,
+      ".  The problem was found while processing the source directory: ",
+      makefile.GetCurrentSourceDirectory(),
+      ".  This FLTK_WRAP_UI call will be ignored.");
+    cmSystemTools::Message(msg, "Warning");
+  }
+}
+
+bool cmFLTKWrapUICommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  // what is the current source dir
-  std::string cdir = this->Makefile->GetCurrentSourceDirectory();
-  std::string const& fluid_exe =
-    this->Makefile->GetRequiredDefinition("FLTK_FLUID_EXECUTABLE");
+  cmMakefile& mf = status.GetMakefile();
 
-  // get parameter for the command
-  this->Target = args[0]; // Target that will use the generated files
+  // what is the current source dir
+  std::string cdir = mf.GetCurrentSourceDirectory();
+  std::string const& fluid_exe =
+    mf.GetRequiredDefinition("FLTK_FLUID_EXECUTABLE");
+
+  // Target that will use the generated files
+  std::string const& target = args[0];
 
   // get the list of GUI files from which .cxx and .h will be generated
-  std::string outputDirectory = this->Makefile->GetCurrentBinaryDirectory();
+  std::string outputDirectory = mf.GetCurrentBinaryDirectory();
 
   {
     // Some of the generated files are *.h so the directory "GUI"
     // where they are created have to be added to the include path
     std::vector<std::string> outputDirectories;
     outputDirectories.push_back(outputDirectory);
-    this->Makefile->AddIncludeDirectories(outputDirectories);
+    mf.AddIncludeDirectories(outputDirectories);
   }
 
+  // List of produced files.
+  std::vector<cmSourceFile*> generatedSourcesClasses;
+
   for (std::string const& arg : cmMakeRange(args).advance(1)) {
-    cmSourceFile* curr = this->Makefile->GetSource(arg);
+    cmSourceFile* curr = mf.GetSource(arg);
     // if we should use the source GUI
     // to generate .cxx and .h files
     if (!curr || !curr->GetPropertyAsBool("WRAP_EXCLUDE")) {
-      std::string outName = outputDirectory;
-      outName += "/";
-      outName += cmSystemTools::GetFilenameWithoutExtension(arg);
-      std::string hname = outName;
-      hname += ".h";
-      std::string origname = cdir + "/" + arg;
+      std::string outName = cmStrCat(
+        outputDirectory, "/", cmSystemTools::GetFilenameWithoutExtension(arg));
+      std::string hname = cmStrCat(outName, ".h");
+      std::string origname = cmStrCat(cdir, "/", arg);
       // add starting depends
       std::vector<std::string> depends;
       depends.push_back(origname);
       depends.push_back(fluid_exe);
-      std::string cxxres = outName;
-      cxxres += ".cxx";
+      std::string cxxres = cmStrCat(outName, ".cxx");
 
-      cmCustomCommandLine commandLine;
-      commandLine.push_back(fluid_exe);
-      commandLine.push_back("-c"); // instructs Fluid to run in command line
-      commandLine.push_back("-h"); // optionally rename .h files
-      commandLine.push_back(hname);
-      commandLine.push_back("-o"); // optionally rename .cxx files
-      commandLine.push_back(cxxres);
-      commandLine.push_back(origname); // name of the GUI fluid file
-      cmCustomCommandLines commandLines;
-      commandLines.push_back(commandLine);
+      cmCustomCommandLines commandLines = cmMakeSingleCommandLine({
+        fluid_exe,
+        "-c", // instructs Fluid to run in command line
+        "-h", // optionally rename .h files
+        hname,
+        "-o", // optionally rename .cxx files
+        cxxres,
+        origname // name of the GUI fluid file
+      });
 
       // Add command for generating the .h and .cxx files
       std::string no_main_dependency;
       const char* no_comment = nullptr;
       const char* no_working_dir = nullptr;
-      this->Makefile->AddCustomCommandToOutput(
-        cxxres, depends, no_main_dependency, commandLines, no_comment,
-        no_working_dir);
-      this->Makefile->AddCustomCommandToOutput(
-        hname, depends, no_main_dependency, commandLines, no_comment,
-        no_working_dir);
+      mf.AddCustomCommandToOutput(cxxres, depends, no_main_dependency,
+                                  commandLines, no_comment, no_working_dir);
+      mf.AddCustomCommandToOutput(hname, depends, no_main_dependency,
+                                  commandLines, no_comment, no_working_dir);
 
-      cmSourceFile* sf = this->Makefile->GetSource(cxxres);
+      cmSourceFile* sf = mf.GetSource(cxxres);
       sf->AddDepend(hname);
       sf->AddDepend(origname);
-      this->GeneratedSourcesClasses.push_back(sf);
+      generatedSourcesClasses.push_back(sf);
     }
   }
 
   // create the variable with the list of sources in it
-  size_t lastHeadersClass = this->GeneratedSourcesClasses.size();
+  size_t lastHeadersClass = generatedSourcesClasses.size();
   std::string sourceListValue;
   for (size_t classNum = 0; classNum < lastHeadersClass; classNum++) {
     if (classNum) {
       sourceListValue += ";";
     }
-    sourceListValue += this->GeneratedSourcesClasses[classNum]->GetFullPath();
+    sourceListValue += generatedSourcesClasses[classNum]->ResolveFullPath();
   }
-  std::string varName = this->Target;
-  varName += "_FLTK_UI_SRCS";
-  this->Makefile->AddDefinition(varName, sourceListValue.c_str());
 
+  std::string const varName = target + "_FLTK_UI_SRCS";
+  mf.AddDefinition(varName, sourceListValue);
+
+  mf.AddFinalAction(
+    [target](cmMakefile& makefile) { FinalAction(makefile, target); });
   return true;
 }
-
-void cmFLTKWrapUICommand::FinalPass()
-{
-  // people should add the srcs to the target themselves, but the old command
-  // didn't support that, so check and see if they added the files in and if
-  // they didn;t then print a warning and add then anyhow
-  cmTarget* target = this->Makefile->FindLocalNonAliasTarget(this->Target);
-  if (!target) {
-    std::string msg =
-      "FLTK_WRAP_UI was called with a target that was never created: ";
-    msg += this->Target;
-    msg += ".  The problem was found while processing the source directory: ";
-    msg += this->Makefile->GetCurrentSourceDirectory();
-    msg += ".  This FLTK_WRAP_UI call will be ignored.";
-    cmSystemTools::Message(msg, "Warning");
-    return;
-  }
-}
diff --git a/Source/cmFLTKWrapUICommand.h b/Source/cmFLTKWrapUICommand.h
index 044755e..bb56dbd 100644
--- a/Source/cmFLTKWrapUICommand.h
+++ b/Source/cmFLTKWrapUICommand.h
@@ -8,52 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmSourceFile;
 
-/** \class cmFLTKWrapUICommand
- * \brief Create .h and .cxx files rules for FLTK user interfaces files
- *
- * cmFLTKWrapUICommand is used to create wrappers for FLTK classes into
- * normal C++
- */
-class cmFLTKWrapUICommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFLTKWrapUICommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   * This is called at the end after all the information
-   * specified by the command is accumulated. Most commands do
-   * not implement this method.  At this point, reading and
-   * writing to the cache can be done.
-   */
-  void FinalPass() override;
-  bool HasFinalPass() const override { return true; }
-
-private:
-  /**
-   * List of produced files.
-   */
-  std::vector<cmSourceFile*> GeneratedSourcesClasses;
-
-  /**
-   * List of Fluid files that provide the source
-   * generating .cxx and .h files
-   */
-  std::string Target;
-};
+bool cmFLTKWrapUICommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmFSPermissions.h b/Source/cmFSPermissions.h
index 7a6e708..fef72e6 100644
--- a/Source/cmFSPermissions.h
+++ b/Source/cmFSPermissions.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_sys_stat.h"
-
 #include <string>
 
+#include "cm_sys_stat.h"
+
 namespace cmFSPermissions {
 
 // Table of permissions flags.
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index ba42669..a56ad22 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -2,25 +2,27 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPI.h"
 
-#include "cmAlgorithms.h"
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <utility>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
+
 #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"
-#include "cmsys/Directory.hxx"
-#include "cmsys/FStream.hxx"
-
-#include <algorithm>
-#include <cassert>
-#include <chrono>
-#include <ctime>
-#include <iomanip>
-#include <sstream>
-#include <utility>
 
 cmFileAPI::cmFileAPI(cmake* cm)
   : CMakeInstance(cm)
@@ -94,7 +96,7 @@
   std::vector<std::string> files = this->LoadDir(reply_dir);
   for (std::string const& f : files) {
     if (this->ReplyFiles.find(f) == this->ReplyFiles.end()) {
-      std::string file = reply_dir + "/" + f;
+      std::string file = cmStrCat(reply_dir, "/", f);
       cmSystemTools::RemoveFile(file);
     }
   }
@@ -407,9 +409,7 @@
 
 std::string cmFileAPI::ObjectName(Object const& o)
 {
-  std::string name = ObjectKindName(o.Kind);
-  name += "-v";
-  name += std::to_string(o.Version);
+  std::string name = cmStrCat(ObjectKindName(o.Kind), "-v", o.Version);
   return name;
 }
 
@@ -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/cmFileAPI.h b/Source/cmFileAPI.h
index 602efa8..e183e0d 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -5,16 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_jsoncpp_reader.h"
-#include "cm_jsoncpp_value.h"
-#include "cm_jsoncpp_writer.h"
-
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <unordered_set>
 #include <vector>
 
+#include "cm_jsoncpp_reader.h"
+#include "cm_jsoncpp_value.h"
+#include "cm_jsoncpp_writer.h"
+
 class cmake;
 
 class cmFileAPI
diff --git a/Source/cmFileAPICMakeFiles.cxx b/Source/cmFileAPICMakeFiles.cxx
index 5590bc2..f419997 100644
--- a/Source/cmFileAPICMakeFiles.cxx
+++ b/Source/cmFileAPICMakeFiles.cxx
@@ -2,6 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPICMakeFiles.h"
 
+#include <string>
+#include <vector>
+
+#include "cm_jsoncpp_value.h"
+
 #include "cmFileAPI.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
@@ -9,11 +14,6 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include "cm_jsoncpp_value.h"
-
-#include <string>
-#include <vector>
-
 namespace {
 
 class CMakeFiles
diff --git a/Source/cmFileAPICache.cxx b/Source/cmFileAPICache.cxx
index f96bc90..ef77795 100644
--- a/Source/cmFileAPICache.cxx
+++ b/Source/cmFileAPICache.cxx
@@ -2,17 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPICache.h"
 
-#include "cmFileAPI.h"
-#include "cmState.h"
-#include "cmake.h"
-
-#include "cm_jsoncpp_value.h"
-
 #include <algorithm>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cm_jsoncpp_value.h"
+
+#include "cmFileAPI.h"
+#include "cmState.h"
+#include "cmake.h"
+
 namespace {
 
 class Cache
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index fecbf63..48561de 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -2,6 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileAPICodemodel.h"
 
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "cm_jsoncpp_value.h"
+
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmFileAPI.h"
@@ -21,22 +36,12 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
 #include "cmake.h"
 
-#include "cm_jsoncpp_value.h"
-
-#include <algorithm>
-#include <cassert>
-#include <map>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
 namespace {
 
 class Codemodel
@@ -134,6 +139,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;
@@ -168,7 +207,7 @@
 
 public:
   BacktraceData(std::string topSource);
-  bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index);
+  JBTIndex Add(cmListFileBacktrace const& bt);
   Json::Value Dump();
 };
 
@@ -177,16 +216,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);
@@ -196,13 +236,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()
@@ -221,32 +260,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;
@@ -271,24 +343,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);
@@ -305,8 +385,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);
@@ -343,20 +423,17 @@
 
 Json::Value Codemodel::DumpConfigurations()
 {
-  std::vector<std::string> configs;
+  Json::Value configurations = Json::arrayValue;
   cmGlobalGenerator* gg =
     this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
   auto makefiles = gg->GetMakefiles();
   if (!makefiles.empty()) {
-    makefiles[0]->GetConfigurations(configs);
-    if (configs.empty()) {
-      configs.emplace_back();
+    std::vector<std::string> const& configs =
+      makefiles[0]->GetGeneratorConfigs();
+    for (std::string const& config : configs) {
+      configurations.append(this->DumpConfiguration(config));
     }
   }
-  Json::Value configurations = Json::arrayValue;
-  for (std::string const& config : configs) {
-    configurations.append(this->DumpConfiguration(config));
-  }
   return configurations;
 }
 
@@ -725,25 +802,32 @@
   {
     // FIXME: Add flags from end section of ExpandRuleVariable,
     // 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());
+    std::vector<BT<std::string>> flags =
+      lg->GetTargetCompileFlags(this->GT, this->Config, lang);
+
+    cd.Flags.reserve(flags.size());
+    for (const BT<std::string>& f : flags) {
+      cd.Flags.emplace_back(this->ToJBT(f));
+    }
   }
   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));
   }
 }
 
 Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si)
 {
-  std::unordered_map<cmSourceGroup const*, Json::ArrayIndex>::iterator i =
-    this->SourceGroupsMap.find(sg);
+  auto i = this->SourceGroupsMap.find(sg);
   if (i == this->SourceGroupsMap.end()) {
     auto sgIndex = static_cast<Json::ArrayIndex>(this->SourceGroups.size());
     i = this->SourceGroupsMap.emplace(sg, sgIndex).first;
@@ -759,87 +843,164 @@
 {
   CompileData fd;
 
-  fd.Language = sf->GetLanguage();
+  fd.Language = sf->GetOrDetermineLanguage();
   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());
+  for (BT<std::string> tmpOpt : sf->GetCompileOptions()) {
+    tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS);
+    // After generator evaluation we need to use the AppendCompileOptions
+    // method so we handle situations where backtrace entries have lists
+    // and properly escape flags.
+    std::string tmp;
+    lg->AppendCompileOptions(tmp, tmpOpt.Value);
+    BT<std::string> opt(tmp, tmpOpt.Backtrace);
+    fd.Flags.emplace_back(this->ToJBT(opt));
+  }
+
+  // Add precompile headers compile options.
+  const std::string pchSource =
+    this->GT->GetPchSource(this->Config, fd.Language);
+
+  if (!pchSource.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (sf->GetFullPath() == pchSource) {
+      pchOptions =
+        this->GT->GetPchCreateCompileOptions(this->Config, fd.Language);
+    } else {
+      pchOptions =
+        this->GT->GetPchUseCompileOptions(this->Config, fd.Language);
+    }
+
+    BT<std::string> tmpOpt(pchOptions);
+    tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS);
+
+    // After generator evaluation we need to use the AppendCompileOptions
+    // method so we handle situations where backtrace entries have lists
+    // and properly escape flags.
+    std::string tmp;
+    lg->AppendCompileOptions(tmp, tmpOpt.Value);
+    BT<std::string> opt(tmp, tmpOpt.Backtrace);
+    fd.Flags.emplace_back(this->ToJBT(opt));
   }
 
   // Add include directories from source file properties.
   {
-    std::vector<std::string> includes;
     const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-    if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
-      const std::string& evaluatedIncludes =
-        genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES);
-      lg->AppendIncludeDirectories(includes, evaluatedIncludes, *sf);
+    for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) {
+      tmpInclude.Value =
+        genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES);
 
-      for (std::string const& include : includes) {
-        bool const isSystemInclude = this->GT->IsSystemIncludeDirectory(
-          include, this->Config, fd.Language);
-        fd.Includes.emplace_back(include, isSystemInclude);
+      // After generator evaluation we need to use the AppendIncludeDirectories
+      // method so we handle situations where backtrace entries have lists.
+      std::vector<std::string> tmp;
+      lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf);
+      for (std::string& i : tmp) {
+        bool const isSystemInclude =
+          this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language);
+        BT<std::string> include(i, tmpInclude.Backtrace);
+        fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude);
       }
     }
   }
-  fd.Includes.insert(fd.Includes.end(), cd.Includes.begin(),
-                     cd.Includes.end());
 
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-  std::set<std::string> fileDefines;
-  if (const char* defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
-    lg->AppendDefines(fileDefines,
-                      genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS));
+  std::set<BT<std::string>> fileDefines;
+  for (BT<std::string> tmpDef : sf->GetCompileDefinitions()) {
+    tmpDef.Value =
+      genexInterpreter.Evaluate(tmpDef.Value, COMPILE_DEFINITIONS);
+
+    // After generator evaluation we need to use the AppendDefines method
+    // so we handle situations where backtrace entries have lists.
+    std::set<std::string> tmp;
+    lg->AppendDefines(tmp, tmpDef.Value);
+    for (const std::string& i : tmp) {
+      BT<std::string> def(i, tmpDef.Backtrace);
+      fileDefines.insert(def);
+    }
   }
 
+  std::set<std::string> configFileDefines;
   const std::string defPropName =
     "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config);
   if (const char* config_defs = sf->GetProperty(defPropName)) {
     lg->AppendDefines(
-      fileDefines,
+      configFileDefines,
       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.Defines.reserve(fileDefines.size() + configFileDefines.size());
 
-  fd.SetDefines(defines);
+  for (BT<std::string> const& def : fileDefines) {
+    fd.Defines.emplace_back(this->ToJBT(def));
+  }
+
+  for (std::string const& d : configFileDefines) {
+    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));
@@ -850,9 +1011,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;
   }
 }
 
@@ -886,7 +1053,7 @@
 {
   Json::Value source = Json::objectValue;
 
-  std::string const path = sk.Source.Value->GetFullPath();
+  std::string const path = sk.Source.Value->ResolveFullPath();
   source["path"] = RelativeIfUnder(this->TopSource, path);
   if (sk.Source.Value->GetIsGenerated()) {
     source["isGenerated"] = true;
@@ -920,7 +1087,7 @@
   return source;
 }
 
-Json::Value Target::DumpCompileData(CompileData cd)
+Json::Value Target::DumpCompileData(CompileData const& cd)
 {
   Json::Value result = Json::objectValue;
 
@@ -942,7 +1109,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);
@@ -962,7 +1129,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;
@@ -998,7 +1165,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;
 }
@@ -1078,17 +1246,16 @@
   }
 
   // Add Windows-specific artifacts produced by the linker.
+  if (this->GT->HasImportLibrary(this->Config)) {
+    Json::Value artifact = Json::objectValue;
+    artifact["path"] =
+      RelativeIfUnder(this->TopBuild,
+                      this->GT->GetFullPath(
+                        this->Config, cmStateEnums::ImportLibraryArtifact));
+    artifacts.append(std::move(artifact)); // NOLINT(*)
+  }
   if (this->GT->IsDLLPlatform() &&
       this->GT->GetType() != cmStateEnums::STATIC_LIBRARY) {
-    if (this->GT->GetType() == cmStateEnums::SHARED_LIBRARY ||
-        this->GT->IsExecutableWithExports()) {
-      Json::Value artifact = Json::objectValue;
-      artifact["path"] =
-        RelativeIfUnder(this->TopBuild,
-                        this->GT->GetFullPath(
-                          this->Config, cmStateEnums::ImportLibraryArtifact));
-      artifacts.append(std::move(artifact)); // NOLINT(*)
-    }
     cmGeneratorTarget::OutputInfo const* output =
       this->GT->GetOutputInfo(this->Config);
     if (output && !output->PdbDir.empty()) {
@@ -1148,21 +1315,18 @@
   Json::Value linkFragments = Json::arrayValue;
 
   std::string linkLanguageFlags;
-  std::string linkFlags;
+  std::vector<BT<std::string>> linkFlags;
   std::string frameworkPath;
-  std::string linkPath;
-  std::string linkLibs;
+  std::vector<BT<std::string>> linkPath;
+  std::vector<BT<std::string>> linkLibs;
   cmLocalGenerator* lg = this->GT->GetLocalGenerator();
   cmLinkLineComputer linkLineComputer(lg,
                                       lg->GetStateSnapshot().GetDirectory());
   lg->GetTargetFlags(&linkLineComputer, this->Config, linkLibs,
                      linkLanguageFlags, linkFlags, frameworkPath, linkPath,
                      this->GT);
-  linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags);
-  linkFlags = cmSystemTools::TrimWhitespace(linkFlags);
-  frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath);
-  linkPath = cmSystemTools::TrimWhitespace(linkPath);
-  linkLibs = cmSystemTools::TrimWhitespace(linkLibs);
+  linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags);
+  frameworkPath = cmTrimWhitespace(frameworkPath);
 
   if (!linkLanguageFlags.empty()) {
     linkFragments.append(
@@ -1170,8 +1334,11 @@
   }
 
   if (!linkFlags.empty()) {
-    linkFragments.append(
-      this->DumpCommandFragment(std::move(linkFlags), "flags"));
+    for (BT<std::string> frag : linkFlags) {
+      frag.Value = cmTrimWhitespace(frag.Value);
+      linkFragments.append(
+        this->DumpCommandFragment(this->ToJBT(frag), "flags"));
+    }
   }
 
   if (!frameworkPath.empty()) {
@@ -1180,29 +1347,35 @@
   }
 
   if (!linkPath.empty()) {
-    linkFragments.append(
-      this->DumpCommandFragment(std::move(linkPath), "libraryPath"));
+    for (BT<std::string> frag : linkPath) {
+      frag.Value = cmTrimWhitespace(frag.Value);
+      linkFragments.append(
+        this->DumpCommandFragment(this->ToJBT(frag), "libraryPath"));
+    }
   }
 
   if (!linkLibs.empty()) {
-    linkFragments.append(
-      this->DumpCommandFragment(std::move(linkLibs), "libraries"));
+    for (BT<std::string> frag : linkLibs) {
+      frag.Value = cmTrimWhitespace(frag.Value);
+      linkFragments.append(
+        this->DumpCommandFragment(this->ToJBT(frag), "libraries"));
+    }
   }
 
   return linkFragments;
 }
 
 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 7a3954e..d55b959 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -2,26 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileCommand.h"
 
-#include "cm_kwiml.h"
-#include "cm_static_string_view.hxx"
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <set>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 
-#include <algorithm>
-#include <assert.h>
-#include <cmath>
-#include <ctype.h>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <utility>
-#include <vector>
+#include "cm_kwiml.h"
+#include "cm_static_string_view.hxx"
+#include "cm_sys_stat.h"
 
 #include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
 #include "cmCryptoHash.h"
+#include "cmExecutionStatus.h"
 #include "cmFileCopier.h"
 #include "cmFileInstaller.h"
 #include "cmFileLockPool.h"
@@ -34,15 +40,19 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmState.h"
+#include "cmStringAlgorithms.h"
+#include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
-#include "cm_sys_stat.h"
 #include "cmake.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
+#  include "cm_curl.h"
+
 #  include "cmCurl.h"
 #  include "cmFileLockResult.h"
-#  include "cm_curl.h"
 #endif
 
 #if defined(CMAKE_USE_ELF_PARSER)
@@ -53,10 +63,12 @@
 #  include <windows.h>
 #endif
 
+namespace {
+
 #if defined(_WIN32)
 // libcurl doesn't support file:// urls for unicode filenames on Windows.
 // Convert string from UTF-8 to ACP if this is a file:// URL.
-static std::string fix_file_url_windows(const std::string& url)
+std::string fix_file_url_windows(const std::string& url)
 {
   std::string ret = url;
   if (strncmp(url.c_str(), "file://", 7) == 0) {
@@ -78,137 +90,25 @@
 }
 #endif
 
-// cmLibraryCommand
-bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
-                                cmExecutionStatus&)
+bool HandleWriteImpl(std::vector<std::string> const& args, bool append,
+                     cmExecutionStatus& status)
 {
-  if (args.size() < 2) {
-    this->SetError("must be called with at least two arguments.");
-    return false;
-  }
-  std::string const& subCommand = args[0];
-  if (subCommand == "WRITE") {
-    return this->HandleWriteCommand(args, false);
-  }
-  if (subCommand == "APPEND") {
-    return this->HandleWriteCommand(args, true);
-  }
-  if (subCommand == "DOWNLOAD") {
-    return this->HandleDownloadCommand(args);
-  }
-  if (subCommand == "UPLOAD") {
-    return this->HandleUploadCommand(args);
-  }
-  if (subCommand == "READ") {
-    return this->HandleReadCommand(args);
-  }
-  if (subCommand == "MD5" || subCommand == "SHA1" || subCommand == "SHA224" ||
-      subCommand == "SHA256" || subCommand == "SHA384" ||
-      subCommand == "SHA512" || subCommand == "SHA3_224" ||
-      subCommand == "SHA3_256" || subCommand == "SHA3_384" ||
-      subCommand == "SHA3_512") {
-    return this->HandleHashCommand(args);
-  }
-  if (subCommand == "STRINGS") {
-    return this->HandleStringsCommand(args);
-  }
-  if (subCommand == "GLOB") {
-    return this->HandleGlobCommand(args, false);
-  }
-  if (subCommand == "GLOB_RECURSE") {
-    return this->HandleGlobCommand(args, true);
-  }
-  if (subCommand == "MAKE_DIRECTORY") {
-    return this->HandleMakeDirectoryCommand(args);
-  }
-  if (subCommand == "RENAME") {
-    return this->HandleRename(args);
-  }
-  if (subCommand == "REMOVE") {
-    return this->HandleRemove(args, false);
-  }
-  if (subCommand == "REMOVE_RECURSE") {
-    return this->HandleRemove(args, true);
-  }
-  if (subCommand == "COPY") {
-    return this->HandleCopyCommand(args);
-  }
-  if (subCommand == "INSTALL") {
-    return this->HandleInstallCommand(args);
-  }
-  if (subCommand == "DIFFERENT") {
-    return this->HandleDifferentCommand(args);
-  }
-  if (subCommand == "RPATH_CHANGE" || subCommand == "CHRPATH") {
-    return this->HandleRPathChangeCommand(args);
-  }
-  if (subCommand == "RPATH_CHECK") {
-    return this->HandleRPathCheckCommand(args);
-  }
-  if (subCommand == "RPATH_REMOVE") {
-    return this->HandleRPathRemoveCommand(args);
-  }
-  if (subCommand == "READ_ELF") {
-    return this->HandleReadElfCommand(args);
-  }
-  if (subCommand == "RELATIVE_PATH") {
-    return this->HandleRelativePathCommand(args);
-  }
-  if (subCommand == "TO_CMAKE_PATH") {
-    return this->HandleCMakePathCommand(args, false);
-  }
-  if (subCommand == "TO_NATIVE_PATH") {
-    return this->HandleCMakePathCommand(args, true);
-  }
-  if (subCommand == "TOUCH") {
-    return this->HandleTouchCommand(args, true);
-  }
-  if (subCommand == "TOUCH_NOCREATE") {
-    return this->HandleTouchCommand(args, false);
-  }
-  if (subCommand == "TIMESTAMP") {
-    return this->HandleTimestampCommand(args);
-  }
-  if (subCommand == "GENERATE") {
-    return this->HandleGenerateCommand(args);
-  }
-  if (subCommand == "LOCK") {
-    return this->HandleLockCommand(args);
-  }
-  if (subCommand == "SIZE") {
-    return this->HandleSizeCommand(args);
-  }
-  if (subCommand == "READ_SYMLINK") {
-    return this->HandleReadSymlinkCommand(args);
-  }
-  if (subCommand == "CREATE_LINK") {
-    return this->HandleCreateLinkCommand(args);
-  }
-
-  std::string e = "does not recognize sub-command " + subCommand;
-  this->SetError(e);
-  return false;
-}
-
-bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args,
-                                       bool append)
-{
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
 
   i++; // Get rid of subcommand
 
   std::string fileName = *i;
   if (!cmsys::SystemTools::FileIsFullPath(*i)) {
-    fileName = this->Makefile->GetCurrentSourceDirectory();
-    fileName += "/" + *i;
+    fileName =
+      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', *i);
   }
 
   i++;
 
-  if (!this->Makefile->CanIWriteThisFile(fileName)) {
+  if (!status.GetMakefile().CanIWriteThisFile(fileName)) {
     std::string e =
       "attempted to write a file: " + fileName + " into a source directory.";
-    this->SetError(e);
+    status.SetError(e);
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
@@ -236,21 +136,19 @@
   cmsys::ofstream file(fileName.c_str(),
                        append ? std::ios::app : std::ios::out);
   if (!file) {
-    std::string error = "failed to open for writing (";
-    error += cmSystemTools::GetLastSystemError();
-    error += "):\n  ";
-    error += fileName;
-    this->SetError(error);
+    std::string error =
+      cmStrCat("failed to open for writing (",
+               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
+    status.SetError(error);
     return false;
   }
   std::string message = cmJoin(cmMakeRange(i, args.end()), std::string());
   file << message;
   if (!file) {
-    std::string error = "write failed (";
-    error += cmSystemTools::GetLastSystemError();
-    error += "):\n  ";
-    error += fileName;
-    this->SetError(error);
+    std::string error =
+      cmStrCat("write failed (", cmSystemTools::GetLastSystemError(), "):\n  ",
+               fileName);
+    status.SetError(error);
     return false;
   }
   file.close();
@@ -260,11 +158,24 @@
   return true;
 }
 
-bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
+bool HandleWriteCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
+{
+  return HandleWriteImpl(args, false, status);
+}
+
+bool HandleAppendCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
+{
+  return HandleWriteImpl(args, true, status);
+}
+
+bool HandleReadCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("READ must be called with at least two additional "
-                   "arguments");
+    status.SetError("READ must be called with at least two additional "
+                    "arguments");
     return false;
   }
 
@@ -287,8 +198,8 @@
 
   std::string fileName = fileNameArg;
   if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
-    fileName = this->Makefile->GetCurrentSourceDirectory();
-    fileName += "/" + fileNameArg;
+    fileName = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
+                        fileNameArg);
   }
 
 // Open the specified file.
@@ -301,11 +212,10 @@
 #endif
 
   if (!file) {
-    std::string error = "failed to open for reading (";
-    error += cmSystemTools::GetLastSystemError();
-    error += "):\n  ";
-    error += fileName;
-    this->SetError(error);
+    std::string error =
+      cmStrCat("failed to open for reading (",
+               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
+    status.SetError(error);
     return false;
   }
 
@@ -357,53 +267,50 @@
       }
     }
   }
-  this->Makefile->AddDefinition(variable, output.c_str());
+  status.GetMakefile().AddDefinition(variable, output);
   return true;
 }
 
-bool cmFileCommand::HandleHashCommand(std::vector<std::string> const& args)
+bool HandleHashCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   if (args.size() != 3) {
-    std::ostringstream e;
-    e << args[0] << " requires a file name and output variable";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " requires a file name and output variable"));
     return false;
   }
 
-  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0].c_str()));
+  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
   if (hash) {
     std::string out = hash->HashFile(args[1]);
     if (!out.empty()) {
-      this->Makefile->AddDefinition(args[2], out.c_str());
+      status.GetMakefile().AddDefinition(args[2], out);
       return true;
     }
-    std::ostringstream e;
-    e << args[0] << " failed to read file \"" << args[1]
-      << "\": " << cmSystemTools::GetLastSystemError();
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " failed to read file \"", args[1],
+                             "\": ", cmSystemTools::GetLastSystemError()));
   }
   return false;
 #else
-  std::ostringstream e;
-  e << args[0] << " not available during bootstrap";
-  this->SetError(e.str());
+  status.SetError(cmStrCat(args[0], " not available during bootstrap"));
   return false;
 #endif
 }
 
-bool cmFileCommand::HandleStringsCommand(std::vector<std::string> const& args)
+bool HandleStringsCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("STRINGS requires a file name and output variable");
+    status.SetError("STRINGS requires a file name and output variable");
     return false;
   }
 
   // Get the file to read.
   std::string fileName = args[1];
   if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
-    fileName = this->Makefile->GetCurrentSourceDirectory();
-    fileName += "/" + args[1];
+    fileName =
+      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
   }
 
   // Get the variable in which to store the results.
@@ -466,30 +373,24 @@
     } else if (arg_mode == arg_limit_input) {
       if (sscanf(args[i].c_str(), "%d", &limit_input) != 1 ||
           limit_input < 0) {
-        std::ostringstream e;
-        e << "STRINGS option LIMIT_INPUT value \"" << args[i]
-          << "\" is not an unsigned integer.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option LIMIT_INPUT value \"",
+                                 args[i], "\" is not an unsigned integer."));
         return false;
       }
       arg_mode = arg_none;
     } else if (arg_mode == arg_limit_output) {
       if (sscanf(args[i].c_str(), "%d", &limit_output) != 1 ||
           limit_output < 0) {
-        std::ostringstream e;
-        e << "STRINGS option LIMIT_OUTPUT value \"" << args[i]
-          << "\" is not an unsigned integer.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option LIMIT_OUTPUT value \"",
+                                 args[i], "\" is not an unsigned integer."));
         return false;
       }
       arg_mode = arg_none;
     } else if (arg_mode == arg_limit_count) {
       int count;
       if (sscanf(args[i].c_str(), "%d", &count) != 1 || count < 0) {
-        std::ostringstream e;
-        e << "STRINGS option LIMIT_COUNT value \"" << args[i]
-          << "\" is not an unsigned integer.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option LIMIT_COUNT value \"",
+                                 args[i], "\" is not an unsigned integer."));
         return false;
       }
       limit_count = count;
@@ -497,10 +398,8 @@
     } else if (arg_mode == arg_length_minimum) {
       int len;
       if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
-        std::ostringstream e;
-        e << "STRINGS option LENGTH_MINIMUM value \"" << args[i]
-          << "\" is not an unsigned integer.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option LENGTH_MINIMUM value \"",
+                                 args[i], "\" is not an unsigned integer."));
         return false;
       }
       minlen = len;
@@ -508,20 +407,16 @@
     } else if (arg_mode == arg_length_maximum) {
       int len;
       if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
-        std::ostringstream e;
-        e << "STRINGS option LENGTH_MAXIMUM value \"" << args[i]
-          << "\" is not an unsigned integer.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option LENGTH_MAXIMUM value \"",
+                                 args[i], "\" is not an unsigned integer."));
         return false;
       }
       maxlen = len;
       arg_mode = arg_none;
     } else if (arg_mode == arg_regex) {
       if (!regex.compile(args[i])) {
-        std::ostringstream e;
-        e << "STRINGS option REGEX value \"" << args[i]
-          << "\" could not be compiled.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option REGEX value \"", args[i],
+                                 "\" could not be compiled."));
         return false;
       }
       have_regex = true;
@@ -538,25 +433,23 @@
       } else if (args[i] == "UTF-32BE") {
         encoding = encoding_utf32be;
       } else {
-        std::ostringstream e;
-        e << "STRINGS option ENCODING \"" << args[i] << "\" not recognized.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat("STRINGS option ENCODING \"", args[i],
+                                 "\" not recognized."));
         return false;
       }
       arg_mode = arg_none;
     } else {
-      std::ostringstream e;
-      e << "STRINGS given unknown argument \"" << args[i] << "\"";
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("STRINGS given unknown argument \"", args[i], "\""));
       return false;
     }
   }
 
   if (hex_conversion_enabled) {
     // TODO: should work without temp file, but just on a memory buffer
-    std::string binaryFileName = this->Makefile->GetCurrentBinaryDirectory();
-    binaryFileName += "/CMakeFiles";
-    binaryFileName += "/FileCommandStringsBinaryFile";
+    std::string binaryFileName =
+      cmStrCat(status.GetMakefile().GetCurrentBinaryDirectory(),
+               "/CMakeFiles/FileCommandStringsBinaryFile");
     if (cmHexFileConverter::TryConvert(fileName, binaryFileName)) {
       fileName = binaryFileName;
     }
@@ -569,9 +462,8 @@
   cmsys::ifstream fin(fileName.c_str());
 #endif
   if (!fin) {
-    std::ostringstream e;
-    e << "STRINGS file \"" << fileName << "\" cannot be read.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("STRINGS file \"", fileName, "\" cannot be read."));
     return false;
   }
 
@@ -743,17 +635,17 @@
   }
 
   // Save the output in a makefile variable.
-  this->Makefile->AddDefinition(outVar, output.c_str());
+  status.GetMakefile().AddDefinition(outVar, output);
   return true;
 }
 
-bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args,
-                                      bool recurse)
+bool HandleGlobImpl(std::vector<std::string> const& args, bool recurse,
+                    cmExecutionStatus& status)
 {
   // File commands has at least one argument
   assert(args.size() > 1);
 
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
 
   i++; // Get rid of subcommand
 
@@ -763,10 +655,10 @@
   g.SetRecurse(recurse);
 
   bool explicitFollowSymlinks = false;
-  cmPolicies::PolicyStatus status =
-    this->Makefile->GetPolicyStatus(cmPolicies::CMP0009);
+  cmPolicies::PolicyStatus policyStatus =
+    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0009);
   if (recurse) {
-    switch (status) {
+    switch (policyStatus) {
       case cmPolicies::REQUIRED_IF_USED:
       case cmPolicies::REQUIRED_ALWAYS:
       case cmPolicies::NEW:
@@ -784,24 +676,24 @@
   bool warnConfigureLate = false;
   bool warnFollowedSymlinks = false;
   const cmake::WorkingMode workingMode =
-    this->Makefile->GetCMakeInstance()->GetWorkingMode();
+    status.GetMakefile().GetCMakeInstance()->GetWorkingMode();
   while (i != args.end()) {
     if (*i == "LIST_DIRECTORIES") {
       ++i; // skip LIST_DIRECTORIES
       if (i != args.end()) {
-        if (cmSystemTools::IsOn(*i)) {
+        if (cmIsOn(*i)) {
           g.SetListDirs(true);
           g.SetRecurseListDirs(true);
-        } else if (cmSystemTools::IsOff(*i)) {
+        } else if (cmIsOff(*i)) {
           g.SetListDirs(false);
           g.SetRecurseListDirs(false);
         } else {
-          this->SetError("LIST_DIRECTORIES missing bool value.");
+          status.SetError("LIST_DIRECTORIES missing bool value.");
           return false;
         }
         ++i;
       } else {
-        this->SetError("LIST_DIRECTORIES missing bool value.");
+        status.SetError("LIST_DIRECTORIES missing bool value.");
         return false;
       }
     } else if (*i == "FOLLOW_SYMLINKS") {
@@ -810,7 +702,7 @@
         explicitFollowSymlinks = true;
         g.RecurseThroughSymlinksOn();
         if (i == args.end()) {
-          this->SetError(
+          status.SetError(
             "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS.");
           return false;
         }
@@ -818,25 +710,26 @@
     } else if (*i == "RELATIVE") {
       ++i; // skip RELATIVE
       if (i == args.end()) {
-        this->SetError("GLOB requires a directory after the RELATIVE tag.");
+        status.SetError("GLOB requires a directory after the RELATIVE tag.");
         return false;
       }
       g.SetRelative(i->c_str());
       ++i;
       if (i == args.end()) {
-        this->SetError("GLOB requires a glob expression after the directory.");
+        status.SetError(
+          "GLOB requires a glob expression after the directory.");
         return false;
       }
     } else if (*i == "CONFIGURE_DEPENDS") {
       // Generated build system depends on glob results
       if (!configureDepends && warnConfigureLate) {
-        this->Makefile->IssueMessage(
+        status.GetMakefile().IssueMessage(
           MessageType::AUTHOR_WARNING,
           "CONFIGURE_DEPENDS flag was given after a glob expression was "
           "already evaluated.");
       }
       if (workingMode != cmake::NORMAL_MODE) {
-        this->Makefile->IssueMessage(
+        status.GetMakefile().IssueMessage(
           MessageType::FATAL_ERROR,
           "CONFIGURE_DEPENDS is invalid for script and find package modes.");
         return false;
@@ -844,14 +737,14 @@
       configureDepends = true;
       ++i;
       if (i == args.end()) {
-        this->SetError(
+        status.SetError(
           "GLOB requires a glob expression after CONFIGURE_DEPENDS.");
         return false;
       }
     } else {
       std::string expr = *i;
       if (!cmsys::SystemTools::FileIsFullPath(*i)) {
-        expr = this->Makefile->GetCurrentSourceDirectory();
+        expr = status.GetMakefile().GetCurrentSourceDirectory();
         // Handle script mode
         if (!expr.empty()) {
           expr += "/" + *i;
@@ -867,12 +760,12 @@
         bool shouldExit = false;
         for (cmsys::Glob::Message const& globMessage : globMessages) {
           if (globMessage.type == cmsys::Glob::cyclicRecursion) {
-            this->Makefile->IssueMessage(
+            status.GetMakefile().IssueMessage(
               MessageType::AUTHOR_WARNING,
               "Cyclic recursion detected while globbing for '" + *i + "':\n" +
                 globMessage.content);
           } else {
-            this->Makefile->IssueMessage(
+            status.GetMakefile().IssueMessage(
               MessageType::FATAL_ERROR,
               "Error has occurred while globbing for '" + *i + "' - " +
                 globMessage.content);
@@ -896,11 +789,11 @@
         std::sort(foundFiles.begin(), foundFiles.end());
         foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()),
                          foundFiles.end());
-        this->Makefile->GetCMakeInstance()->AddGlobCacheEntry(
+        status.GetMakefile().GetCMakeInstance()->AddGlobCacheEntry(
           recurse, (recurse ? g.GetRecurseListDirs() : g.GetListDirs()),
           (recurse ? g.GetRecurseThroughSymlinks() : false),
           (g.GetRelative() ? g.GetRelative() : ""), expr, foundFiles, variable,
-          this->Makefile->GetBacktrace());
+          status.GetMakefile().GetBacktrace());
       } else {
         warnConfigureLate = true;
       }
@@ -908,7 +801,7 @@
     }
   }
 
-  switch (status) {
+  switch (policyStatus) {
     case cmPolicies::REQUIRED_IF_USED:
     case cmPolicies::REQUIRED_ALWAYS:
     case cmPolicies::NEW:
@@ -921,7 +814,7 @@
       // Possibly unexpected old behavior *and* we actually traversed
       // symlinks without being explicitly asked to: warn the author.
       if (warnFollowedSymlinks) {
-        this->Makefile->IssueMessage(
+        status.GetMakefile().IssueMessage(
           MessageType::AUTHOR_WARNING,
           cmPolicies::GetPolicyWarning(cmPolicies::CMP0009));
       }
@@ -930,12 +823,24 @@
 
   std::sort(files.begin(), files.end());
   files.erase(std::unique(files.begin(), files.end()), files.end());
-  this->Makefile->AddDefinition(variable, cmJoin(files, ";").c_str());
+  status.GetMakefile().AddDefinition(variable, cmJoin(files, ";"));
   return true;
 }
 
-bool cmFileCommand::HandleMakeDirectoryCommand(
-  std::vector<std::string> const& args)
+bool HandleGlobCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
+{
+  return HandleGlobImpl(args, false, status);
+}
+
+bool HandleGlobRecurseCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
+{
+  return HandleGlobImpl(args, true, status);
+}
+
+bool HandleMakeDirectoryCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
   // File command has at least one argument
   assert(args.size() > 1);
@@ -946,28 +851,28 @@
   {
     const std::string* cdir = &arg;
     if (!cmsys::SystemTools::FileIsFullPath(arg)) {
-      expr = this->Makefile->GetCurrentSourceDirectory();
-      expr += "/" + arg;
+      expr =
+        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
       cdir = &expr;
     }
-    if (!this->Makefile->CanIWriteThisFile(*cdir)) {
+    if (!status.GetMakefile().CanIWriteThisFile(*cdir)) {
       std::string e = "attempted to create a directory: " + *cdir +
         " into a source directory.";
-      this->SetError(e);
+      status.SetError(e);
       cmSystemTools::SetFatalErrorOccured();
       return false;
     }
     if (!cmSystemTools::MakeDirectory(*cdir)) {
       std::string error = "problem creating directory: " + *cdir;
-      this->SetError(error);
+      status.SetError(error);
       return false;
     }
   }
   return true;
 }
 
-bool cmFileCommand::HandleTouchCommand(std::vector<std::string> const& args,
-                                       bool create)
+bool HandleTouchImpl(std::vector<std::string> const& args, bool create,
+                     cmExecutionStatus& status)
 {
   // File command has at least one argument
   assert(args.size() > 1);
@@ -977,27 +882,39 @@
   {
     std::string tfile = arg;
     if (!cmsys::SystemTools::FileIsFullPath(tfile)) {
-      tfile = this->Makefile->GetCurrentSourceDirectory();
-      tfile += "/" + arg;
+      tfile =
+        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
     }
-    if (!this->Makefile->CanIWriteThisFile(tfile)) {
+    if (!status.GetMakefile().CanIWriteThisFile(tfile)) {
       std::string e =
         "attempted to touch a file: " + tfile + " in a source directory.";
-      this->SetError(e);
+      status.SetError(e);
       cmSystemTools::SetFatalErrorOccured();
       return false;
     }
     if (!cmSystemTools::Touch(tfile, create)) {
       std::string error = "problem touching file: " + tfile;
-      this->SetError(error);
+      status.SetError(error);
       return false;
     }
   }
   return true;
 }
 
-bool cmFileCommand::HandleDifferentCommand(
-  std::vector<std::string> const& args)
+bool HandleTouchCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
+{
+  return HandleTouchImpl(args, true, status);
+}
+
+bool HandleTouchNocreateCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
+{
+  return HandleTouchImpl(args, false, status);
+}
+
+bool HandleDifferentCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   /*
     FILE(DIFFERENT <variable> FILES <lhs> <rhs>)
@@ -1028,41 +945,41 @@
       file_rhs = args[i].c_str();
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << "DIFFERENT given unknown argument " << args[i];
-      this->SetError(e.str());
+      status.SetError(cmStrCat("DIFFERENT given unknown argument ", args[i]));
       return false;
     }
   }
   if (!var) {
-    this->SetError("DIFFERENT not given result variable name.");
+    status.SetError("DIFFERENT not given result variable name.");
     return false;
   }
   if (!file_lhs || !file_rhs) {
-    this->SetError("DIFFERENT not given FILES option with two file names.");
+    status.SetError("DIFFERENT not given FILES option with two file names.");
     return false;
   }
 
   // Compare the files.
   const char* result =
     cmSystemTools::FilesDiffer(file_lhs, file_rhs) ? "1" : "0";
-  this->Makefile->AddDefinition(var, result);
+  status.GetMakefile().AddDefinition(var, result);
   return true;
 }
 
-bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args)
+bool HandleCopyCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-  cmFileCopier copier(this);
+  cmFileCopier copier(status);
   return copier.Run(args);
 }
 
-bool cmFileCommand::HandleRPathChangeCommand(
-  std::vector<std::string> const& args)
+bool HandleRPathChangeCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   // Evaluate arguments.
   std::string file;
   const char* oldRPath = nullptr;
   const char* newRPath = nullptr;
+  bool removeEnvironmentRPath = false;
   enum Doing
   {
     DoingNone,
@@ -1078,6 +995,8 @@
       doing = DoingNew;
     } else if (args[i] == "FILE") {
       doing = DoingFile;
+    } else if (args[i] == "INSTALL_REMOVE_ENVIRONMENT_RPATH") {
+      removeEnvironmentRPath = true;
     } else if (doing == DoingFile) {
       file = args[i];
       doing = DoingNone;
@@ -1088,62 +1007,53 @@
       newRPath = args[i].c_str();
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << "RPATH_CHANGE given unknown argument " << args[i];
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("RPATH_CHANGE given unknown argument ", args[i]));
       return false;
     }
   }
   if (file.empty()) {
-    this->SetError("RPATH_CHANGE not given FILE option.");
+    status.SetError("RPATH_CHANGE not given FILE option.");
     return false;
   }
   if (!oldRPath) {
-    this->SetError("RPATH_CHANGE not given OLD_RPATH option.");
+    status.SetError("RPATH_CHANGE not given OLD_RPATH option.");
     return false;
   }
   if (!newRPath) {
-    this->SetError("RPATH_CHANGE not given NEW_RPATH option.");
+    status.SetError("RPATH_CHANGE not given NEW_RPATH option.");
     return false;
   }
   if (!cmSystemTools::FileExists(file, true)) {
-    std::ostringstream e;
-    e << "RPATH_CHANGE given FILE \"" << file << "\" that does not exist.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("RPATH_CHANGE given FILE \"", file, "\" that does not exist."));
     return false;
   }
   bool success = true;
   cmFileTimes const ft(file);
   std::string emsg;
   bool changed;
-  if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, &emsg, &changed)) {
-    std::ostringstream e;
-    /* clang-format off */
-    e << "RPATH_CHANGE could not write new RPATH:\n"
-      << "  " << newRPath << "\n"
-      << "to the file:\n"
-      << "  " << file << "\n"
-      << emsg;
-    /* clang-format on */
-    this->SetError(e.str());
+
+  if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath,
+                                  removeEnvironmentRPath, &emsg, &changed)) {
+    status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n  ",
+                             newRPath, "\nto the file:\n  ", file, "\n",
+                             emsg));
     success = false;
   }
   if (success) {
     if (changed) {
-      std::string message = "Set runtime path of \"";
-      message += file;
-      message += "\" to \"";
-      message += newRPath;
-      message += "\"";
-      this->Makefile->DisplayStatus(message, -1);
+      std::string message =
+        cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"');
+      status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
   }
   return success;
 }
 
-bool cmFileCommand::HandleRPathRemoveCommand(
-  std::vector<std::string> const& args)
+bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   // Evaluate arguments.
   std::string file;
@@ -1160,20 +1070,18 @@
       file = args[i];
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << "RPATH_REMOVE given unknown argument " << args[i];
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("RPATH_REMOVE given unknown argument ", args[i]));
       return false;
     }
   }
   if (file.empty()) {
-    this->SetError("RPATH_REMOVE not given FILE option.");
+    status.SetError("RPATH_REMOVE not given FILE option.");
     return false;
   }
   if (!cmSystemTools::FileExists(file, true)) {
-    std::ostringstream e;
-    e << "RPATH_REMOVE given FILE \"" << file << "\" that does not exist.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("RPATH_REMOVE given FILE \"", file, "\" that does not exist."));
     return false;
   }
   bool success = true;
@@ -1181,29 +1089,24 @@
   std::string emsg;
   bool removed;
   if (!cmSystemTools::RemoveRPath(file, &emsg, &removed)) {
-    std::ostringstream e;
-    /* clang-format off */
-    e << "RPATH_REMOVE could not remove RPATH from file:\n"
-      << "  " << file << "\n"
-      << emsg;
-    /* clang-format on */
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("RPATH_REMOVE could not remove RPATH from file: \n  ", file,
+               "\n", emsg));
     success = false;
   }
   if (success) {
     if (removed) {
-      std::string message = "Removed runtime path from \"";
-      message += file;
-      message += "\"";
-      this->Makefile->DisplayStatus(message, -1);
+      std::string message =
+        cmStrCat("Removed runtime path from \"", file, '"');
+      status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
   }
   return success;
 }
 
-bool cmFileCommand::HandleRPathCheckCommand(
-  std::vector<std::string> const& args)
+bool HandleRPathCheckCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   // Evaluate arguments.
   std::string file;
@@ -1227,18 +1130,17 @@
       rpath = args[i].c_str();
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << "RPATH_CHECK given unknown argument " << args[i];
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("RPATH_CHECK given unknown argument ", args[i]));
       return false;
     }
   }
   if (file.empty()) {
-    this->SetError("RPATH_CHECK not given FILE option.");
+    status.SetError("RPATH_CHECK not given FILE option.");
     return false;
   }
   if (!rpath) {
-    this->SetError("RPATH_CHECK not given RPATH option.");
+    status.SetError("RPATH_CHECK not given RPATH option.");
     return false;
   }
 
@@ -1253,11 +1155,12 @@
   return true;
 }
 
-bool cmFileCommand::HandleReadElfCommand(std::vector<std::string> const& args)
+bool HandleReadElfCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 4) {
-    this->SetError("READ_ELF must be called with at least three additional "
-                   "arguments.");
+    status.SetError("READ_ELF must be called with at least three additional "
+                    "arguments.");
     return false;
   }
 
@@ -1277,9 +1180,8 @@
   Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2));
 
   if (!cmSystemTools::FileExists(fileNameArg, true)) {
-    std::ostringstream e;
-    e << "READ_ELF given FILE \"" << fileNameArg << "\" that does not exist.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg,
+                             "\" that does not exist."));
     return false;
   }
 
@@ -1290,14 +1192,14 @@
     if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
       std::string rpath(se_rpath->Value);
       std::replace(rpath.begin(), rpath.end(), ':', ';');
-      this->Makefile->AddDefinition(arguments.RPath, rpath.c_str());
+      status.GetMakefile().AddDefinition(arguments.RPath, rpath);
     }
   }
   if (!arguments.RunPath.empty()) {
     if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
       std::string runpath(se_runpath->Value);
       std::replace(runpath.begin(), runpath.end(), ':', ';');
-      this->Makefile->AddDefinition(arguments.RunPath, runpath.c_str());
+      status.GetMakefile().AddDefinition(arguments.RunPath, runpath);
     }
   }
 
@@ -1305,25 +1207,26 @@
 #else
   std::string error = "ELF parser not available on this platform.";
   if (arguments.Error.empty()) {
-    this->SetError(error);
+    status.SetError(error);
     return false;
   }
-  this->Makefile->AddDefinition(arguments.Error, error.c_str());
+  status.GetMakefile().AddDefinition(arguments.Error, error);
   return true;
 #endif
 }
 
-bool cmFileCommand::HandleInstallCommand(std::vector<std::string> const& args)
+bool HandleInstallCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
-  cmFileInstaller installer(this);
+  cmFileInstaller installer(status);
   return installer.Run(args);
 }
 
-bool cmFileCommand::HandleRelativePathCommand(
-  std::vector<std::string> const& args)
+bool HandleRelativePathCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
   if (args.size() != 4) {
-    this->SetError("RELATIVE_PATH called with incorrect number of arguments");
+    status.SetError("RELATIVE_PATH called with incorrect number of arguments");
     return false;
   }
 
@@ -1335,58 +1238,52 @@
     std::string errstring =
       "RELATIVE_PATH must be passed a full path to the directory: " +
       directoryName;
-    this->SetError(errstring);
+    status.SetError(errstring);
     return false;
   }
   if (!cmSystemTools::FileIsFullPath(fileName)) {
     std::string errstring =
       "RELATIVE_PATH must be passed a full path to the file: " + fileName;
-    this->SetError(errstring);
+    status.SetError(errstring);
     return false;
   }
 
   std::string res = cmSystemTools::RelativePath(directoryName, fileName);
-  this->Makefile->AddDefinition(outVar, res.c_str());
+  status.GetMakefile().AddDefinition(outVar, res);
   return true;
 }
 
-bool cmFileCommand::HandleRename(std::vector<std::string> const& args)
+bool HandleRename(std::vector<std::string> const& args,
+                  cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("RENAME given incorrect number of arguments.");
+    status.SetError("RENAME given incorrect number of arguments.");
     return false;
   }
 
   // Compute full path for old and new names.
   std::string oldname = args[1];
   if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
-    oldname = this->Makefile->GetCurrentSourceDirectory();
-    oldname += "/" + args[1];
+    oldname =
+      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
   }
   std::string newname = args[2];
   if (!cmsys::SystemTools::FileIsFullPath(newname)) {
-    newname = this->Makefile->GetCurrentSourceDirectory();
-    newname += "/" + args[2];
+    newname =
+      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
   }
 
   if (!cmSystemTools::RenameFile(oldname, newname)) {
     std::string err = cmSystemTools::GetLastSystemError();
-    std::ostringstream e;
-    /* clang-format off */
-    e << "RENAME failed to rename\n"
-      << "  " << oldname << "\n"
-      << "to\n"
-      << "  " << newname << "\n"
-      << "because: " << err << "\n";
-    /* clang-format on */
-    this->SetError(e.str());
+    status.SetError(cmStrCat("RENAME failed to rename\n  ", oldname,
+                             "\nto\n  ", newname, "\nbecause: ", err, "\n"));
     return false;
   }
   return true;
 }
 
-bool cmFileCommand::HandleRemove(std::vector<std::string> const& args,
-                                 bool recurse)
+bool HandleRemoveImpl(std::vector<std::string> const& args, bool recurse,
+                      cmExecutionStatus& status)
 {
 
   std::string message;
@@ -1397,18 +1294,18 @@
     std::string fileName = arg;
     if (fileName.empty()) {
       std::string const r = recurse ? "REMOVE_RECURSE" : "REMOVE";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING,
-                                   "Ignoring empty file name in " + r + ".");
+      status.GetMakefile().IssueMessage(
+        MessageType::AUTHOR_WARNING, "Ignoring empty file name in " + r + ".");
       continue;
     }
     if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
-      fileName = this->Makefile->GetCurrentSourceDirectory();
-      fileName += "/" + arg;
+      fileName =
+        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
     }
 
     if (cmSystemTools::FileIsDirectory(fileName) &&
         !cmSystemTools::FileIsSymlink(fileName) && recurse) {
-      cmSystemTools::RemoveADirectory(fileName);
+      cmSystemTools::RepeatedRemoveDirectory(fileName);
     } else {
       cmSystemTools::RemoveFile(fileName);
     }
@@ -1416,7 +1313,18 @@
   return true;
 }
 
-namespace {
+bool HandleRemove(std::vector<std::string> const& args,
+                  cmExecutionStatus& status)
+{
+  return HandleRemoveImpl(args, false, status);
+}
+
+bool HandleRemoveRecurse(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
+{
+  return HandleRemoveImpl(args, true, status);
+}
+
 std::string ToNativePath(const std::string& path)
 {
   const auto& outPath = cmSystemTools::ConvertToOutputPath(path);
@@ -1433,14 +1341,14 @@
   cmSystemTools::ConvertToUnixSlashes(temp);
   return temp;
 }
-}
 
-bool cmFileCommand::HandleCMakePathCommand(
-  std::vector<std::string> const& args, bool nativePath)
+bool HandlePathCommand(std::vector<std::string> const& args,
+                       std::string (*convert)(std::string const&),
+                       cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
-                   "called with exactly three arguments.");
+    status.SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
+                    "called with exactly three arguments.");
     return false;
   }
 #if defined(_WIN32) && !defined(__CYGWIN__)
@@ -1450,18 +1358,27 @@
 #endif
   std::vector<std::string> path = cmSystemTools::SplitString(args[1], pathSep);
 
-  std::string value = cmJoin(
-    cmMakeRange(path).transform(nativePath ? ToNativePath : ToCMakePath), ";");
-  this->Makefile->AddDefinition(args[2], value.c_str());
+  std::string value = cmJoin(cmMakeRange(path).transform(convert), ";");
+  status.GetMakefile().AddDefinition(args[2], value);
   return true;
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+bool HandleCMakePathCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
+{
+  return HandlePathCommand(args, ToCMakePath, status);
+}
+
+bool HandleNativePathCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
+{
+  return HandlePathCommand(args, ToNativePath, status);
+}
+
+#if !defined(CMAKE_BOOTSTRAP)
 
 // Stuff for curl download/upload
-typedef std::vector<char> cmFileCommandVectorOfChar;
-
-namespace {
+using cmFileCommandVectorOfChar = std::vector<char>;
 
 size_t cmWriteToFileCallback(void* ptr, size_t size, size_t nmemb, void* data)
 {
@@ -1513,11 +1430,10 @@
 class cURLProgressHelper
 {
 public:
-  cURLProgressHelper(cmFileCommand* fc, const char* text)
+  cURLProgressHelper(cmMakefile* mf, const char* text)
+    : Makefile(mf)
+    , Text(text)
   {
-    this->CurrentPercentage = -1;
-    this->FileCommand = fc;
-    this->Text = text;
   }
 
   bool UpdatePercentage(double value, double total, std::string& status)
@@ -1535,20 +1451,18 @@
     bool updated = (OldPercentage != this->CurrentPercentage);
 
     if (updated) {
-      std::ostringstream oss;
-      oss << "[" << this->Text << " " << this->CurrentPercentage
-          << "% complete]";
-      status = oss.str();
+      status =
+        cmStrCat("[", this->Text, " ", this->CurrentPercentage, "% complete]");
     }
 
     return updated;
   }
 
-  cmFileCommand* GetFileCommand() { return this->FileCommand; }
+  cmMakefile* GetMakefile() { return this->Makefile; }
 
 private:
-  long CurrentPercentage;
-  cmFileCommand* FileCommand;
+  long CurrentPercentage = -1;
+  cmMakefile* Makefile;
   std::string Text;
 };
 
@@ -1562,8 +1476,7 @@
 
   std::string status;
   if (helper->UpdatePercentage(dlnow, dltotal, status)) {
-    cmFileCommand* fc = helper->GetFileCommand();
-    cmMakefile* mf = fc->GetMakefile();
+    cmMakefile* mf = helper->GetMakefile();
     mf->DisplayStatus(status, -1);
   }
 
@@ -1580,16 +1493,12 @@
 
   std::string status;
   if (helper->UpdatePercentage(ulnow, ultotal, status)) {
-    cmFileCommand* fc = helper->GetFileCommand();
-    cmMakefile* mf = fc->GetMakefile();
+    cmMakefile* mf = helper->GetMakefile();
     mf->DisplayStatus(status, -1);
   }
 
   return 0;
 }
-}
-
-namespace {
 
 class cURLEasyGuard
 {
@@ -1614,7 +1523,7 @@
 private:
   ::CURL* Easy;
 };
-}
+
 #endif
 
 #define check_curl_result(result, errstr)                                     \
@@ -1622,17 +1531,18 @@
     if (result != CURLE_OK) {                                                 \
       std::string e(errstr);                                                  \
       e += ::curl_easy_strerror(result);                                      \
-      this->SetError(e);                                                      \
+      status.SetError(e);                                                     \
       return false;                                                           \
     }                                                                         \
   } while (false)
 
-bool cmFileCommand::HandleDownloadCommand(std::vector<std::string> const& args)
+bool HandleDownloadCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-  std::vector<std::string>::const_iterator i = args.begin();
+#if !defined(CMAKE_BOOTSTRAP)
+  auto i = args.begin();
   if (args.size() < 3) {
-    this->SetError("DOWNLOAD must be called with at least three arguments.");
+    status.SetError("DOWNLOAD must be called with at least three arguments.");
     return false;
   }
   ++i; // Get rid of subcommand
@@ -1645,11 +1555,12 @@
   long inactivity_timeout = 0;
   std::string logVar;
   std::string statusVar;
-  bool tls_verify = this->Makefile->IsOn("CMAKE_TLS_VERIFY");
-  const char* cainfo = this->Makefile->GetDefinition("CMAKE_TLS_CAINFO");
-  std::string netrc_level = this->Makefile->GetSafeDefinition("CMAKE_NETRC");
+  bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY");
+  const char* cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
+  std::string netrc_level =
+    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
   std::string netrc_file =
-    this->Makefile->GetSafeDefinition("CMAKE_NETRC_FILE");
+    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
   std::string expectedHash;
   std::string hashMatchMSG;
   std::unique_ptr<cmCryptoHash> hash;
@@ -1664,7 +1575,7 @@
       if (i != args.end()) {
         timeout = atol(i->c_str());
       } else {
-        this->SetError("DOWNLOAD missing time for TIMEOUT.");
+        status.SetError("DOWNLOAD missing time for TIMEOUT.");
         return false;
       }
     } else if (*i == "INACTIVITY_TIMEOUT") {
@@ -1672,29 +1583,29 @@
       if (i != args.end()) {
         inactivity_timeout = atol(i->c_str());
       } else {
-        this->SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
+        status.SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
         return false;
       }
     } else if (*i == "LOG") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing VAR for LOG.");
+        status.SetError("DOWNLOAD missing VAR for LOG.");
         return false;
       }
       logVar = *i;
     } else if (*i == "STATUS") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing VAR for STATUS.");
+        status.SetError("DOWNLOAD missing VAR for STATUS.");
         return false;
       }
       statusVar = *i;
     } else if (*i == "TLS_VERIFY") {
       ++i;
       if (i != args.end()) {
-        tls_verify = cmSystemTools::IsOn(*i);
+        tls_verify = cmIsOn(*i);
       } else {
-        this->SetError("TLS_VERIFY missing bool value.");
+        status.SetError("TLS_VERIFY missing bool value.");
         return false;
       }
     } else if (*i == "TLS_CAINFO") {
@@ -1702,7 +1613,7 @@
       if (i != args.end()) {
         cainfo = i->c_str();
       } else {
-        this->SetError("TLS_CAFILE missing file value.");
+        status.SetError("TLS_CAFILE missing file value.");
         return false;
       }
     } else if (*i == "NETRC_FILE") {
@@ -1710,7 +1621,7 @@
       if (i != args.end()) {
         netrc_file = *i;
       } else {
-        this->SetError("DOWNLOAD missing file value for NETRC_FILE.");
+        status.SetError("DOWNLOAD missing file value for NETRC_FILE.");
         return false;
       }
     } else if (*i == "NETRC") {
@@ -1718,13 +1629,13 @@
       if (i != args.end()) {
         netrc_level = *i;
       } else {
-        this->SetError("DOWNLOAD missing level value for NETRC.");
+        status.SetError("DOWNLOAD missing level value for NETRC.");
         return false;
       }
     } else if (*i == "EXPECTED_MD5") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
+        status.SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
         return false;
       }
       hash = cm::make_unique<cmCryptoHash>(cmCryptoHash::AlgoMD5);
@@ -1735,46 +1646,44 @@
     } else if (*i == "EXPECTED_HASH") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing ALGO=value for EXPECTED_HASH.");
+        status.SetError("DOWNLOAD missing ALGO=value for EXPECTED_HASH.");
         return false;
       }
       std::string::size_type pos = i->find("=");
       if (pos == std::string::npos) {
         std::string err =
-          "DOWNLOAD EXPECTED_HASH expects ALGO=value but got: ";
-        err += *i;
-        this->SetError(err);
+          cmStrCat("DOWNLOAD EXPECTED_HASH expects ALGO=value but got: ", *i);
+        status.SetError(err);
         return false;
       }
       std::string algo = i->substr(0, pos);
       expectedHash = cmSystemTools::LowerCase(i->substr(pos + 1));
-      hash = std::unique_ptr<cmCryptoHash>(cmCryptoHash::New(algo.c_str()));
+      hash = std::unique_ptr<cmCryptoHash>(cmCryptoHash::New(algo));
       if (!hash) {
-        std::string err = "DOWNLOAD EXPECTED_HASH given unknown ALGO: ";
-        err += algo;
-        this->SetError(err);
+        std::string err =
+          cmStrCat("DOWNLOAD EXPECTED_HASH given unknown ALGO: ", algo);
+        status.SetError(err);
         return false;
       }
       hashMatchMSG = algo + " hash";
     } else if (*i == "USERPWD") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing string for USERPWD.");
+        status.SetError("DOWNLOAD missing string for USERPWD.");
         return false;
       }
       userpwd = *i;
     } else if (*i == "HTTPHEADER") {
       ++i;
       if (i == args.end()) {
-        this->SetError("DOWNLOAD missing string for HTTPHEADER.");
+        status.SetError("DOWNLOAD missing string for HTTPHEADER.");
         return false;
       }
       curl_headers.push_back(*i);
     } else {
       // Do not return error for compatibility reason.
-      std::string err = "Unexpected argument: ";
-      err += *i;
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, err);
+      std::string err = cmStrCat("Unexpected argument: ", *i);
+      status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err);
     }
     ++i;
   }
@@ -1786,13 +1695,10 @@
     std::string msg;
     std::string actualHash = hash->HashFile(file);
     if (actualHash == expectedHash) {
-      msg = "returning early; file already exists with expected ";
-      msg += hashMatchMSG;
-      msg += "\"";
+      msg = cmStrCat("returning early; file already exists with expected ",
+                     hashMatchMSG, '"');
       if (!statusVar.empty()) {
-        std::ostringstream result;
-        result << 0 << ";\"" << msg;
-        this->Makefile->AddDefinition(statusVar, result.str().c_str());
+        status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg));
       }
       return true;
     }
@@ -1805,13 +1711,13 @@
     std::string errstring = "DOWNLOAD error: cannot create directory '" + dir +
       "' - Specify file by full path name and verify that you "
       "have directory creation and file write privileges.";
-    this->SetError(errstring);
+    status.SetError(errstring);
     return false;
   }
 
   cmsys::ofstream fout(file.c_str(), std::ios::binary);
   if (!fout) {
-    this->SetError("DOWNLOAD cannot open file for write.");
+    status.SetError("DOWNLOAD cannot open file for write.");
     return false;
   }
 
@@ -1823,7 +1729,7 @@
   ::curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = ::curl_easy_init();
   if (!curl) {
-    this->SetError("DOWNLOAD error initializing curl.");
+    status.SetError("DOWNLOAD error initializing curl.");
     return false;
   }
 
@@ -1857,7 +1763,7 @@
   // command arg comes first
   std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
   if (!cainfo_err.empty()) {
-    this->SetError(cainfo_err);
+    status.SetError(cainfo_err);
     return false;
   }
 
@@ -1867,7 +1773,7 @@
   std::string const& netrc_option_err =
     cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
   if (!netrc_option_err.empty()) {
-    this->SetError(netrc_option_err);
+    status.SetError(netrc_option_err);
     return false;
   }
 
@@ -1903,7 +1809,7 @@
   // scope intentionally, rather than inside the "if(showProgress)"
   // block...
   //
-  cURLProgressHelper helper(this, "download");
+  cURLProgressHelper helper(&status.GetMakefile(), "download");
 
   if (showProgress) {
     res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
@@ -1938,10 +1844,9 @@
   ::curl_easy_cleanup(curl);
 
   if (!statusVar.empty()) {
-    std::ostringstream result;
-    result << static_cast<int>(res) << ";\"" << ::curl_easy_strerror(res)
-           << "\"";
-    this->Makefile->AddDefinition(statusVar, result.str().c_str());
+    status.GetMakefile().AddDefinition(
+      statusVar,
+      cmStrCat(static_cast<int>(res), ";\"", ::curl_easy_strerror(res), "\""));
   }
 
   ::curl_global_cleanup();
@@ -1956,51 +1861,57 @@
   if (hash) {
     std::string actualHash = hash->HashFile(file);
     if (actualHash.empty()) {
-      this->SetError("DOWNLOAD cannot compute hash on downloaded file");
+      status.SetError("DOWNLOAD cannot compute hash on downloaded file");
       return false;
     }
 
     if (expectedHash != actualHash) {
-      std::ostringstream oss;
-      oss << "DOWNLOAD HASH mismatch" << std::endl
-          << "  for file: [" << file << "]" << std::endl
-          << "    expected hash: [" << expectedHash << "]" << std::endl
-          << "      actual hash: [" << actualHash << "]" << std::endl
-          << "           status: [" << static_cast<int>(res) << ";\""
-          << ::curl_easy_strerror(res) << "\"]" << std::endl;
-
       if (!statusVar.empty() && res == 0) {
-        std::string status = "1;HASH mismatch: "
-                             "expected: " +
-          expectedHash + " actual: " + actualHash;
-        this->Makefile->AddDefinition(statusVar, status.c_str());
+        status.GetMakefile().AddDefinition(statusVar,
+                                           "1;HASH mismatch: "
+                                           "expected: " +
+                                             expectedHash +
+                                             " actual: " + actualHash);
       }
 
-      this->SetError(oss.str());
+      status.SetError(cmStrCat("DOWNLOAD HASH mismatch\n"
+                               "  for file: [",
+                               file,
+                               "]\n"
+                               "    expected hash: [",
+                               expectedHash,
+                               "]\n"
+                               "      actual hash: [",
+                               actualHash,
+                               "]\n"
+                               "           status: [",
+                               static_cast<int>(res), ";\"",
+                               ::curl_easy_strerror(res), "\"]\n"));
       return false;
     }
   }
 
   if (!logVar.empty()) {
     chunkDebug.push_back(0);
-    this->Makefile->AddDefinition(logVar, chunkDebug.data());
+    status.GetMakefile().AddDefinition(logVar, chunkDebug.data());
   }
 
   return true;
 #else
-  this->SetError("DOWNLOAD not supported by bootstrap cmake.");
+  status.SetError("DOWNLOAD not supported by bootstrap cmake.");
   return false;
 #endif
 }
 
-bool cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
+bool HandleUploadCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   if (args.size() < 3) {
-    this->SetError("UPLOAD must be called with at least three arguments.");
+    status.SetError("UPLOAD must be called with at least three arguments.");
     return false;
   }
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
   ++i;
   std::string filename = *i;
   ++i;
@@ -2013,9 +1924,10 @@
   std::string statusVar;
   bool showProgress = false;
   std::string userpwd;
-  std::string netrc_level = this->Makefile->GetSafeDefinition("CMAKE_NETRC");
+  std::string netrc_level =
+    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
   std::string netrc_file =
-    this->Makefile->GetSafeDefinition("CMAKE_NETRC_FILE");
+    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
 
   std::vector<std::string> curl_headers;
 
@@ -2025,7 +1937,7 @@
       if (i != args.end()) {
         timeout = atol(i->c_str());
       } else {
-        this->SetError("UPLOAD missing time for TIMEOUT.");
+        status.SetError("UPLOAD missing time for TIMEOUT.");
         return false;
       }
     } else if (*i == "INACTIVITY_TIMEOUT") {
@@ -2033,20 +1945,20 @@
       if (i != args.end()) {
         inactivity_timeout = atol(i->c_str());
       } else {
-        this->SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
+        status.SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
         return false;
       }
     } else if (*i == "LOG") {
       ++i;
       if (i == args.end()) {
-        this->SetError("UPLOAD missing VAR for LOG.");
+        status.SetError("UPLOAD missing VAR for LOG.");
         return false;
       }
       logVar = *i;
     } else if (*i == "STATUS") {
       ++i;
       if (i == args.end()) {
-        this->SetError("UPLOAD missing VAR for STATUS.");
+        status.SetError("UPLOAD missing VAR for STATUS.");
         return false;
       }
       statusVar = *i;
@@ -2057,7 +1969,7 @@
       if (i != args.end()) {
         netrc_file = *i;
       } else {
-        this->SetError("UPLOAD missing file value for NETRC_FILE.");
+        status.SetError("UPLOAD missing file value for NETRC_FILE.");
         return false;
       }
     } else if (*i == "NETRC") {
@@ -2065,28 +1977,27 @@
       if (i != args.end()) {
         netrc_level = *i;
       } else {
-        this->SetError("UPLOAD missing level value for NETRC.");
+        status.SetError("UPLOAD missing level value for NETRC.");
         return false;
       }
     } else if (*i == "USERPWD") {
       ++i;
       if (i == args.end()) {
-        this->SetError("UPLOAD missing string for USERPWD.");
+        status.SetError("UPLOAD missing string for USERPWD.");
         return false;
       }
       userpwd = *i;
     } else if (*i == "HTTPHEADER") {
       ++i;
       if (i == args.end()) {
-        this->SetError("UPLOAD missing string for HTTPHEADER.");
+        status.SetError("UPLOAD missing string for HTTPHEADER.");
         return false;
       }
       curl_headers.push_back(*i);
     } else {
       // Do not return error for compatibility reason.
-      std::string err = "Unexpected argument: ";
-      err += *i;
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, err);
+      std::string err = cmStrCat("Unexpected argument: ", *i);
+      status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err);
     }
 
     ++i;
@@ -2096,9 +2007,9 @@
   //
   FILE* fin = cmsys::SystemTools::Fopen(filename, "rb");
   if (!fin) {
-    std::string errStr = "UPLOAD cannot open file '";
-    errStr += filename + "' for reading.";
-    this->SetError(errStr);
+    std::string errStr =
+      cmStrCat("UPLOAD cannot open file '", filename, "' for reading.");
+    status.SetError(errStr);
     return false;
   }
 
@@ -2112,7 +2023,7 @@
   ::curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = ::curl_easy_init();
   if (!curl) {
-    this->SetError("UPLOAD error initializing curl.");
+    status.SetError("UPLOAD error initializing curl.");
     fclose(fin);
     return false;
   }
@@ -2171,7 +2082,7 @@
   // scope intentionally, rather than inside the "if(showProgress)"
   // block...
   //
-  cURLProgressHelper helper(this, "upload");
+  cURLProgressHelper helper(&status.GetMakefile(), "upload");
 
   if (showProgress) {
     res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
@@ -2206,7 +2117,7 @@
   std::string const& netrc_option_err =
     cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
   if (!netrc_option_err.empty()) {
-    this->SetError(netrc_option_err);
+    status.SetError(netrc_option_err);
     return false;
   }
 
@@ -2225,10 +2136,9 @@
   ::curl_easy_cleanup(curl);
 
   if (!statusVar.empty()) {
-    std::ostringstream result;
-    result << static_cast<int>(res) << ";\"" << ::curl_easy_strerror(res)
-           << "\"";
-    this->Makefile->AddDefinition(statusVar, result.str().c_str());
+    status.GetMakefile().AddDefinition(
+      statusVar,
+      cmStrCat(static_cast<int>(res), ";\"", ::curl_easy_strerror(res), "\""));
   }
 
   ::curl_global_cleanup();
@@ -2253,22 +2163,22 @@
       log += "\n";
     }
 
-    this->Makefile->AddDefinition(logVar, log.c_str());
+    status.GetMakefile().AddDefinition(logVar, log);
   }
 
   return true;
 #else
-  this->SetError("UPLOAD not supported by bootstrap cmake.");
+  status.SetError("UPLOAD not supported by bootstrap cmake.");
   return false;
 #endif
 }
 
-void cmFileCommand::AddEvaluationFile(const std::string& inputName,
-                                      const std::string& outputExpr,
-                                      const std::string& condition,
-                                      bool inputIsContent)
+void AddEvaluationFile(const std::string& inputName,
+                       const std::string& outputExpr,
+                       const std::string& condition, bool inputIsContent,
+                       cmExecutionStatus& status)
 {
-  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+  cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
 
   cmGeneratorExpression outputGe(lfbt);
   std::unique_ptr<cmCompiledGeneratorExpression> outputCge =
@@ -2278,52 +2188,54 @@
   std::unique_ptr<cmCompiledGeneratorExpression> conditionCge =
     conditionGe.Parse(condition);
 
-  this->Makefile->AddEvaluationFile(inputName, std::move(outputCge),
-                                    std::move(conditionCge), inputIsContent);
+  status.GetMakefile().AddEvaluationFile(
+    inputName, std::move(outputCge), std::move(conditionCge), inputIsContent);
 }
 
-bool cmFileCommand::HandleGenerateCommand(std::vector<std::string> const& args)
+bool HandleGenerateCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   if (args.size() < 5) {
-    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    status.SetError("Incorrect arguments to GENERATE subcommand.");
     return false;
   }
   if (args[1] != "OUTPUT") {
-    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    status.SetError("Incorrect arguments to GENERATE subcommand.");
     return false;
   }
   std::string condition;
   if (args.size() > 5) {
     if (args[5] != "CONDITION") {
-      this->SetError("Incorrect arguments to GENERATE subcommand.");
+      status.SetError("Incorrect arguments to GENERATE subcommand.");
       return false;
     }
     if (args.size() != 7) {
-      this->SetError("Incorrect arguments to GENERATE subcommand.");
+      status.SetError("Incorrect arguments to GENERATE subcommand.");
       return false;
     }
     condition = args[6];
     if (condition.empty()) {
-      this->SetError("CONDITION of sub-command GENERATE must not be empty if "
-                     "specified.");
+      status.SetError("CONDITION of sub-command GENERATE must not be empty if "
+                      "specified.");
       return false;
     }
   }
   std::string output = args[2];
   const bool inputIsContent = args[3] != "INPUT";
   if (inputIsContent && args[3] != "CONTENT") {
-    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    status.SetError("Incorrect arguments to GENERATE subcommand.");
     return false;
   }
   std::string input = args[4];
 
-  this->AddEvaluationFile(input, output, condition, inputIsContent);
+  AddEvaluationFile(input, output, condition, inputIsContent, status);
   return true;
 }
 
-bool cmFileCommand::HandleLockCommand(std::vector<std::string> const& args)
+bool HandleLockCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   // Default values
   bool directory = false;
   bool release = false;
@@ -2339,7 +2251,7 @@
 
   // Parse arguments
   if (args.size() < 2) {
-    this->Makefile->IssueMessage(
+    status.GetMakefile().IssueMessage(
       MessageType::FATAL_ERROR,
       "sub-command LOCK requires at least two arguments.");
     return false;
@@ -2355,7 +2267,7 @@
       ++i;
       const char* merr = "expected FUNCTION, FILE or PROCESS after GUARD";
       if (i >= args.size()) {
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, merr);
+        status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, merr);
         return false;
       }
       if (args[i] == "FUNCTION") {
@@ -2365,16 +2277,16 @@
       } else if (args[i] == "PROCESS") {
         guard = GUARD_PROCESS;
       } else {
-        std::ostringstream e;
-        e << merr << ", but got:\n  \"" << args[i] << "\".";
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        status.GetMakefile().IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(merr, ", but got:\n  \"", args[i], "\"."));
         return false;
       }
 
     } else if (args[i] == "RESULT_VARIABLE") {
       ++i;
       if (i >= args.size()) {
-        this->Makefile->IssueMessage(
+        status.GetMakefile().IssueMessage(
           MessageType::FATAL_ERROR,
           "expected variable name after RESULT_VARIABLE");
         return false;
@@ -2383,24 +2295,24 @@
     } else if (args[i] == "TIMEOUT") {
       ++i;
       if (i >= args.size()) {
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                     "expected timeout value after TIMEOUT");
+        status.GetMakefile().IssueMessage(
+          MessageType::FATAL_ERROR, "expected timeout value after TIMEOUT");
         return false;
       }
       long scanned;
-      if (!cmSystemTools::StringToLong(args[i].c_str(), &scanned) ||
-          scanned < 0) {
-        std::ostringstream e;
-        e << "TIMEOUT value \"" << args[i] << "\" is not an unsigned integer.";
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      if (!cmStrToLong(args[i], &scanned) || scanned < 0) {
+        status.GetMakefile().IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("TIMEOUT value \"", args[i],
+                   "\" is not an unsigned integer."));
         return false;
       }
       timeout = static_cast<unsigned long>(scanned);
     } else {
-      std::ostringstream e;
-      e << "expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT\n";
-      e << "but got: \"" << args[i] << "\".";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      status.GetMakefile().IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or ",
+                 "TIMEOUT\nbut got: \"", args[i], "\"."));
       return false;
     }
   }
@@ -2410,7 +2322,7 @@
   }
 
   if (!cmsys::SystemTools::FileIsFullPath(path)) {
-    path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
+    path = status.GetMakefile().GetCurrentSourceDirectory() + "/" + path;
   }
 
   // Unify path (remove '//', '/../', ...)
@@ -2419,18 +2331,19 @@
   // Create file and directories if needed
   std::string parentDir = cmSystemTools::GetParentDirectory(path);
   if (!cmSystemTools::MakeDirectory(parentDir)) {
-    std::ostringstream e;
-    e << "directory\n  \"" << parentDir << "\"\ncreation failed ";
-    e << "(check permissions).";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("directory\n  \"", parentDir,
+               "\"\ncreation failed (check permissions)."));
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
   FILE* file = cmsys::SystemTools::Fopen(path, "w");
   if (!file) {
-    std::ostringstream e;
-    e << "file\n  \"" << path << "\"\ncreation failed (check permissions).";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("file\n  \"", path,
+               "\"\ncreation failed (check permissions)."));
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
@@ -2438,7 +2351,7 @@
 
   // Actual lock/unlock
   cmFileLockPool& lockPool =
-    this->Makefile->GetGlobalGenerator()->GetFileLockPool();
+    status.GetMakefile().GetGlobalGenerator()->GetFileLockPool();
 
   cmFileLockResult fileLockResult(cmFileLockResult::MakeOk());
   if (release) {
@@ -2463,34 +2376,34 @@
   const std::string result = fileLockResult.GetOutputMessage();
 
   if (resultVariable.empty() && !fileLockResult.IsOk()) {
-    std::ostringstream e;
-    e << "error locking file\n  \"" << path << "\"\n" << result << ".";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("error locking file\n  \"", path, "\"\n", result, "."));
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
 
   if (!resultVariable.empty()) {
-    this->Makefile->AddDefinition(resultVariable, result.c_str());
+    status.GetMakefile().AddDefinition(resultVariable, result);
   }
 
   return true;
 #else
   static_cast<void>(args);
-  this->SetError("sub-command LOCK not implemented in bootstrap cmake");
+  status.SetError("sub-command LOCK not implemented in bootstrap cmake");
   return false;
 #endif
 }
 
-bool cmFileCommand::HandleTimestampCommand(
-  std::vector<std::string> const& args)
+bool HandleTimestampCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("sub-command TIMESTAMP requires at least two arguments.");
+    status.SetError("sub-command TIMESTAMP requires at least two arguments.");
     return false;
   }
   if (args.size() > 5) {
-    this->SetError("sub-command TIMESTAMP takes at most four arguments.");
+    status.SetError("sub-command TIMESTAMP takes at most four arguments.");
     return false;
   }
 
@@ -2512,7 +2425,7 @@
     } else {
       std::string e = " TIMESTAMP sub-command does not recognize option " +
         args[argsIndex] + ".";
-      this->SetError(e);
+      status.SetError(e);
       return false;
     }
   }
@@ -2520,17 +2433,17 @@
   cmTimestamp timestamp;
   std::string result =
     timestamp.FileModificationTime(filename.c_str(), formatString, utcFlag);
-  this->Makefile->AddDefinition(outputVariable, result.c_str());
+  status.GetMakefile().AddDefinition(outputVariable, result);
 
   return true;
 }
 
-bool cmFileCommand::HandleSizeCommand(std::vector<std::string> const& args)
+bool HandleSizeCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    std::ostringstream e;
-    e << args[0] << " requires a file name and output variable";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " requires a file name and output variable"));
     return false;
   }
 
@@ -2541,26 +2454,23 @@
   const std::string& outputVariable = args[argsIndex++];
 
   if (!cmSystemTools::FileExists(filename, true)) {
-    std::ostringstream e;
-    e << "SIZE requested of path that is not readable:\n  " << filename;
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("SIZE requested of path that is not readable:\n  ", filename));
     return false;
   }
 
-  this->Makefile->AddDefinition(
-    outputVariable,
-    std::to_string(cmSystemTools::FileLength(filename)).c_str());
+  status.GetMakefile().AddDefinition(
+    outputVariable, std::to_string(cmSystemTools::FileLength(filename)));
 
   return true;
 }
 
-bool cmFileCommand::HandleReadSymlinkCommand(
-  std::vector<std::string> const& args)
+bool HandleReadSymlinkCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    std::ostringstream e;
-    e << args[0] << " requires a file name and output variable";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " requires a file name and output variable"));
     return false;
   }
 
@@ -2569,24 +2479,22 @@
 
   std::string result;
   if (!cmSystemTools::ReadSymlink(filename, result)) {
-    std::ostringstream e;
-    e << "READ_SYMLINK requested of path that is not a symlink:\n  "
-      << filename;
-    this->SetError(e.str());
+    status.SetError(cmStrCat(
+      "READ_SYMLINK requested of path that is not a symlink:\n  ", filename));
     return false;
   }
 
-  this->Makefile->AddDefinition(outputVariable, result.c_str());
+  status.GetMakefile().AddDefinition(outputVariable, result);
 
   return true;
 }
 
-bool cmFileCommand::HandleCreateLinkCommand(
-  std::vector<std::string> const& args)
+bool HandleCreateLinkCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("CREATE_LINK must be called with at least two additional "
-                   "arguments");
+    status.SetError("CREATE_LINK must be called with at least two additional "
+                    "arguments");
     return false;
   }
 
@@ -2611,7 +2519,7 @@
     parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
 
   if (!unconsumedArgs.empty()) {
-    this->SetError("unknown argument: \"" + unconsumedArgs.front() + '\"');
+    status.SetError("unknown argument: \"" + unconsumedArgs.front() + '\"');
     return false;
   }
 
@@ -2622,10 +2530,10 @@
   if (fileName == newFileName) {
     result = "CREATE_LINK cannot use same file and newfile";
     if (!arguments.Result.empty()) {
-      this->Makefile->AddDefinition(arguments.Result, result.c_str());
+      status.GetMakefile().AddDefinition(arguments.Result, result);
       return true;
     }
-    this->SetError(result);
+    status.SetError(result);
     return false;
   }
 
@@ -2633,10 +2541,10 @@
   if (!arguments.Symbolic && !cmSystemTools::FileExists(fileName)) {
     result = "Cannot hard link \'" + fileName + "\' as it does not exist.";
     if (!arguments.Result.empty()) {
-      this->Makefile->AddDefinition(arguments.Result, result.c_str());
+      status.GetMakefile().AddDefinition(arguments.Result, result);
       return true;
     }
-    this->SetError(result);
+    status.SetError(result);
     return false;
   }
 
@@ -2650,10 +2558,10 @@
       << cmSystemTools::GetLastSystemError() << "\n";
 
     if (!arguments.Result.empty()) {
-      this->Makefile->AddDefinition(arguments.Result, e.str().c_str());
+      status.GetMakefile().AddDefinition(arguments.Result, e.str());
       return true;
     }
-    this->SetError(e.str());
+    status.SetError(e.str());
     return false;
   }
 
@@ -2680,13 +2588,235 @@
     result = "0";
   } else if (arguments.Result.empty()) {
     // The operation failed and the result is not reported in a variable.
-    this->SetError(result);
+    status.SetError(result);
     return false;
   }
 
   if (!arguments.Result.empty()) {
-    this->Makefile->AddDefinition(arguments.Result, result.c_str());
+    status.GetMakefile().AddDefinition(arguments.Result, result);
   }
 
   return true;
 }
+
+bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
+                                         cmExecutionStatus& status)
+{
+  static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
+                                                            "Darwin" };
+  std::string platform =
+    status.GetMakefile().GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+  if (!supportedPlatforms.count(platform)) {
+    status.SetError(
+      cmStrCat("GET_RUNTIME_DEPENDENCIES is not supported on system \"",
+               platform, "\""));
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  if (status.GetMakefile().GetState()->GetMode() == cmState::Project) {
+    status.GetMakefile().IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      "You have used file(GET_RUNTIME_DEPENDENCIES)"
+      " in project mode. This is probably not what "
+      "you intended to do. Instead, please consider"
+      " using it in an install(CODE) or "
+      "install(SCRIPT) command. For example:"
+      "\n  install(CODE [["
+      "\n    file(GET_RUNTIME_DEPENDENCIES"
+      "\n      # ..."
+      "\n      )"
+      "\n    ]])");
+  }
+
+  struct Arguments
+  {
+    std::string ResolvedDependenciesVar;
+    std::string UnresolvedDependenciesVar;
+    std::string ConflictingDependenciesPrefix;
+    std::string BundleExecutable;
+    std::vector<std::string> Executables;
+    std::vector<std::string> Libraries;
+    std::vector<std::string> Directories;
+    std::vector<std::string> Modules;
+    std::vector<std::string> PreIncludeRegexes;
+    std::vector<std::string> PreExcludeRegexes;
+    std::vector<std::string> PostIncludeRegexes;
+    std::vector<std::string> PostExcludeRegexes;
+  };
+
+  static auto const parser =
+    cmArgumentParser<Arguments>{}
+      .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar)
+      .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s,
+            &Arguments::UnresolvedDependenciesVar)
+      .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s,
+            &Arguments::ConflictingDependenciesPrefix)
+      .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable)
+      .Bind("EXECUTABLES"_s, &Arguments::Executables)
+      .Bind("LIBRARIES"_s, &Arguments::Libraries)
+      .Bind("MODULES"_s, &Arguments::Modules)
+      .Bind("DIRECTORIES"_s, &Arguments::Directories)
+      .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes)
+      .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes)
+      .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes)
+      .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes);
+
+  std::vector<std::string> unrecognizedArguments;
+  std::vector<std::string> keywordsMissingValues;
+  auto parsedArgs =
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
+                 &keywordsMissingValues);
+  auto argIt = unrecognizedArguments.begin();
+  if (argIt != unrecognizedArguments.end()) {
+    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+  argIt = keywordsMissingValues.begin();
+  if (argIt != keywordsMissingValues.end()) {
+    status.SetError(cmStrCat("Keyword missing value: ", *argIt));
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  cmRuntimeDependencyArchive archive(
+    status, parsedArgs.Directories, parsedArgs.BundleExecutable,
+    parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes,
+    parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes);
+  if (!archive.Prepare()) {
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  if (!archive.GetRuntimeDependencies(
+        parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) {
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
+  std::vector<std::string> deps;
+  std::vector<std::string> unresolvedDeps;
+  std::vector<std::string> conflictingDeps;
+  for (auto const& val : archive.GetResolvedPaths()) {
+    bool unique = true;
+    auto it = val.second.begin();
+    assert(it != val.second.end());
+    auto const& firstPath = *it;
+    while (++it != val.second.end()) {
+      if (!cmSystemTools::SameFile(firstPath, *it)) {
+        unique = false;
+        break;
+      }
+    }
+
+    if (unique) {
+      deps.push_back(firstPath);
+    } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+      conflictingDeps.push_back(val.first);
+      std::vector<std::string> paths;
+      paths.insert(paths.begin(), val.second.begin(), val.second.end());
+      std::string varName =
+        parsedArgs.ConflictingDependenciesPrefix + "_" + val.first;
+      std::string pathsStr = cmJoin(paths, ";");
+      status.GetMakefile().AddDefinition(varName, pathsStr);
+    } else {
+      std::ostringstream e;
+      e << "Multiple conflicting paths found for " << val.first << ":";
+      for (auto const& path : val.second) {
+        e << "\n  " << path;
+      }
+      status.SetError(e.str());
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
+  }
+  if (!archive.GetUnresolvedPaths().empty()) {
+    if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+      unresolvedDeps.insert(unresolvedDeps.begin(),
+                            archive.GetUnresolvedPaths().begin(),
+                            archive.GetUnresolvedPaths().end());
+    } else {
+      auto it = archive.GetUnresolvedPaths().begin();
+      assert(it != archive.GetUnresolvedPaths().end());
+      status.SetError(cmStrCat("Could not resolve file ", *it));
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
+  }
+
+  if (!parsedArgs.ResolvedDependenciesVar.empty()) {
+    std::string val = cmJoin(deps, ";");
+    status.GetMakefile().AddDefinition(parsedArgs.ResolvedDependenciesVar,
+                                       val);
+  }
+  if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+    std::string val = cmJoin(unresolvedDeps, ";");
+    status.GetMakefile().AddDefinition(parsedArgs.UnresolvedDependenciesVar,
+                                       val);
+  }
+  if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+    std::string val = cmJoin(conflictingDeps, ";");
+    status.GetMakefile().AddDefinition(
+      parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val);
+  }
+  return true;
+}
+
+} // namespace
+
+bool cmFileCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
+{
+  if (args.size() < 2) {
+    status.SetError("must be called with at least two arguments.");
+    return false;
+  }
+
+  static cmSubcommandTable const subcommand{
+    { "WRITE"_s, HandleWriteCommand },
+    { "APPEND"_s, HandleAppendCommand },
+    { "DOWNLOAD"_s, HandleDownloadCommand },
+    { "UPLOAD"_s, HandleUploadCommand },
+    { "READ"_s, HandleReadCommand },
+    { "MD5"_s, HandleHashCommand },
+    { "SHA1"_s, HandleHashCommand },
+    { "SHA224"_s, HandleHashCommand },
+    { "SHA256"_s, HandleHashCommand },
+    { "SHA384"_s, HandleHashCommand },
+    { "SHA512"_s, HandleHashCommand },
+    { "SHA3_224"_s, HandleHashCommand },
+    { "SHA3_256"_s, HandleHashCommand },
+    { "SHA3_384"_s, HandleHashCommand },
+    { "SHA3_512"_s, HandleHashCommand },
+    { "STRINGS"_s, HandleStringsCommand },
+    { "GLOB"_s, HandleGlobCommand },
+    { "GLOB_RECURSE"_s, HandleGlobRecurseCommand },
+    { "MAKE_DIRECTORY"_s, HandleMakeDirectoryCommand },
+    { "RENAME"_s, HandleRename },
+    { "REMOVE"_s, HandleRemove },
+    { "REMOVE_RECURSE"_s, HandleRemoveRecurse },
+    { "COPY"_s, HandleCopyCommand },
+    { "INSTALL"_s, HandleInstallCommand },
+    { "DIFFERENT"_s, HandleDifferentCommand },
+    { "RPATH_CHANGE"_s, HandleRPathChangeCommand },
+    { "CHRPATH"_s, HandleRPathChangeCommand },
+    { "RPATH_CHECK"_s, HandleRPathCheckCommand },
+    { "RPATH_REMOVE"_s, HandleRPathRemoveCommand },
+    { "READ_ELF"_s, HandleReadElfCommand },
+    { "RELATIVE_PATH"_s, HandleRelativePathCommand },
+    { "TO_CMAKE_PATH"_s, HandleCMakePathCommand },
+    { "TO_NATIVE_PATH"_s, HandleNativePathCommand },
+    { "TOUCH"_s, HandleTouchCommand },
+    { "TOUCH_NOCREATE"_s, HandleTouchNocreateCommand },
+    { "TIMESTAMP"_s, HandleTimestampCommand },
+    { "GENERATE"_s, HandleGenerateCommand },
+    { "LOCK"_s, HandleLockCommand },
+    { "SIZE"_s, HandleSizeCommand },
+    { "READ_SYMLINK"_s, HandleReadSymlinkCommand },
+    { "CREATE_LINK"_s, HandleCreateLinkCommand },
+    { "GET_RUNTIME_DEPENDENCIES"_s, HandleGetRuntimeDependenciesCommand },
+  };
+
+  return subcommand(args[0], args, status);
+}
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index 12c5115..8c9b219 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -8,65 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmFileCommand
- * \brief Command for manipulation of files
- *
- */
-class cmFileCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFileCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  bool HandleRename(std::vector<std::string> const& args);
-  bool HandleRemove(std::vector<std::string> const& args, bool recurse);
-  bool HandleWriteCommand(std::vector<std::string> const& args, bool append);
-  bool HandleReadCommand(std::vector<std::string> const& args);
-  bool HandleHashCommand(std::vector<std::string> const& args);
-  bool HandleStringsCommand(std::vector<std::string> const& args);
-  bool HandleGlobCommand(std::vector<std::string> const& args, bool recurse);
-  bool HandleTouchCommand(std::vector<std::string> const& args, bool create);
-  bool HandleMakeDirectoryCommand(std::vector<std::string> const& args);
-
-  bool HandleRelativePathCommand(std::vector<std::string> const& args);
-  bool HandleCMakePathCommand(std::vector<std::string> const& args,
-                              bool nativePath);
-  bool HandleReadElfCommand(std::vector<std::string> const& args);
-  bool HandleRPathChangeCommand(std::vector<std::string> const& args);
-  bool HandleRPathCheckCommand(std::vector<std::string> const& args);
-  bool HandleRPathRemoveCommand(std::vector<std::string> const& args);
-  bool HandleDifferentCommand(std::vector<std::string> const& args);
-
-  bool HandleCopyCommand(std::vector<std::string> const& args);
-  bool HandleInstallCommand(std::vector<std::string> const& args);
-  bool HandleDownloadCommand(std::vector<std::string> const& args);
-  bool HandleUploadCommand(std::vector<std::string> const& args);
-
-  bool HandleTimestampCommand(std::vector<std::string> const& args);
-  bool HandleGenerateCommand(std::vector<std::string> const& args);
-  bool HandleLockCommand(std::vector<std::string> const& args);
-  bool HandleSizeCommand(std::vector<std::string> const& args);
-  bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
-  bool HandleCreateLinkCommand(std::vector<std::string> const& args);
-
-private:
-  void AddEvaluationFile(const std::string& inputName,
-                         const std::string& outputExpr,
-                         const std::string& condition, bool inputIsContent);
-};
+bool cmFileCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx
index 49e8cd5..627e05b 100644
--- a/Source/cmFileCopier.cxx
+++ b/Source/cmFileCopier.cxx
@@ -3,26 +3,28 @@
 
 #include "cmFileCopier.h"
 
-#include "cmFSPermissions.h"
-#include "cmFileCommand.h"
-#include "cmFileTimes.h"
-#include "cmMakefile.h"
-#include "cmSystemTools.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/Glob.hxx"
 
+#include "cmExecutionStatus.h"
+#include "cmFSPermissions.h"
+#include "cmFileTimes.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 #ifdef _WIN32
 #  include "cmsys/FStream.hxx"
 #endif
 
+#include <cstring>
 #include <sstream>
-#include <string.h>
 
 using namespace cmFSPermissions;
 
-cmFileCopier::cmFileCopier(cmFileCommand* command, const char* name)
-  : FileCommand(command)
-  , Makefile(command->GetMakefile())
+cmFileCopier::cmFileCopier(cmExecutionStatus& status, const char* name)
+  : Status(status)
+  , Makefile(&status.GetMakefile())
   , Name(name)
   , Always(false)
   , MatchlessFiles(true)
@@ -90,8 +92,9 @@
 
     if (!cmSystemTools::SetPermissions(toFile, permissions)) {
       std::ostringstream e;
-      e << this->Name << " cannot set permissions on \"" << toFile << "\"";
-      this->FileCommand->SetError(e.str());
+      e << this->Name << " cannot set permissions on \"" << toFile
+        << "\": " << cmSystemTools::GetLastSystemError() << ".";
+      this->Status.SetError(e.str());
       return false;
     }
   }
@@ -105,7 +108,7 @@
   if (!cmFSPermissions::stringToModeT(arg, permissions)) {
     std::ostringstream e;
     e << this->Name << " given invalid permission \"" << arg << "\".";
-    this->FileCommand->SetError(e.str());
+    this->Status.SetError(e.str());
     return false;
   }
   return true;
@@ -120,8 +123,9 @@
 {
   // The input file does not exist and installation is not optional.
   std::ostringstream e;
-  e << this->Name << " cannot find \"" << fromFile << "\".";
-  this->FileCommand->SetError(e.str());
+  e << this->Name << " cannot find \"" << fromFile
+    << "\": " << cmSystemTools::GetLastSystemError() << ".";
+  this->Status.SetError(e.str());
   return false;
 }
 
@@ -129,7 +133,7 @@
 {
   std::ostringstream e;
   e << "option " << arg << " may not appear before PATTERN or REGEX.";
-  this->FileCommand->SetError(e.str());
+  this->Status.SetError(e.str());
   this->Doing = DoingError;
 }
 
@@ -137,7 +141,7 @@
 {
   std::ostringstream e;
   e << "option " << arg << " may not appear after PATTERN or REGEX.";
-  this->FileCommand->SetError(e.str());
+  this->Status.SetError(e.str());
   this->Doing = DoingError;
 }
 
@@ -170,15 +174,12 @@
   const char* default_dir_install_permissions = this->Makefile->GetDefinition(
     "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (default_dir_install_permissions && *default_dir_install_permissions) {
-    std::vector<std::string> items;
-    cmSystemTools::ExpandListArgument(default_dir_install_permissions, items);
+    std::vector<std::string> items =
+      cmExpandedList(default_dir_install_permissions);
     for (const auto& arg : items) {
       if (!this->CheckPermissions(arg, **mode)) {
-        std::ostringstream e;
-        e << this->FileCommand->GetError()
-          << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
-             "variable.";
-        this->FileCommand->SetError(e.str());
+        this->Status.SetError(
+          " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS variable.");
         return false;
       }
     }
@@ -197,7 +198,7 @@
     if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) {
       std::ostringstream e;
       e << "called with unknown argument \"" << args[i] << "\".";
-      this->FileCommand->SetError(e.str());
+      this->Status.SetError(e.str());
       return false;
     }
 
@@ -211,7 +212,7 @@
   if (this->Destination.empty()) {
     std::ostringstream e;
     e << this->Name << " given no DESTINATION";
-    this->FileCommand->SetError(e.str());
+    this->Status.SetError(e.str());
     return false;
   }
 
@@ -314,8 +315,8 @@
       if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) {
         this->Destination = arg;
       } else {
-        this->Destination = this->Makefile->GetCurrentBinaryDirectory();
-        this->Destination += "/" + arg;
+        this->Destination =
+          cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', arg);
       }
       this->Doing = DoingNone;
       break;
@@ -323,8 +324,8 @@
       if (cmSystemTools::FileIsFullPath(arg)) {
         this->FilesFromDir = arg;
       } else {
-        this->FilesFromDir = this->Makefile->GetCurrentSourceDirectory();
-        this->FilesFromDir += "/" + arg;
+        this->FilesFromDir =
+          cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', arg);
       }
       cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir);
       this->Doing = DoingNone;
@@ -334,9 +335,8 @@
       // leading slash and trailing end-of-string in the matched
       // string to make sure the pattern matches only whole file
       // names.
-      std::string regex = "/";
-      regex += cmsys::Glob::PatternToRegex(arg, false);
-      regex += "$";
+      std::string regex =
+        cmStrCat('/', cmsys::Glob::PatternToRegex(arg, false), '$');
       this->MatchRules.emplace_back(regex);
       this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
       if (this->CurrentMatchRule->Regex.is_valid()) {
@@ -344,7 +344,7 @@
       } else {
         std::ostringstream e;
         e << "could not compile PATTERN \"" << arg << "\".";
-        this->FileCommand->SetError(e.str());
+        this->Status.SetError(e.str());
         this->Doing = DoingError;
       }
     } break;
@@ -356,7 +356,7 @@
       } else {
         std::ostringstream e;
         e << "could not compile REGEX \"" << arg << "\".";
-        this->FileCommand->SetError(e.str());
+        this->Status.SetError(e.str());
         this->Doing = DoingError;
       }
       break;
@@ -399,8 +399,8 @@
       file += "/";
       file += f;
     } else if (!this->FilesFromDir.empty()) {
-      this->FileCommand->SetError("option FILES_FROM_DIR requires all files "
-                                  "to be specified as relative paths.");
+      this->Status.SetError("option FILES_FROM_DIR requires all files "
+                            "to be specified as relative paths.");
       return false;
     } else {
       file = f;
@@ -447,9 +447,8 @@
                            const std::string& toFile)
 {
   if (fromFile.empty()) {
-    std::ostringstream e;
-    e << "INSTALL encountered an empty string input file name.";
-    this->FileCommand->SetError(e.str());
+    this->Status.SetError(
+      "INSTALL encountered an empty string input file name.");
     return false;
   }
 
@@ -493,7 +492,7 @@
   while (cmSystemTools::ReadSymlink(fromFile, newFromFile)) {
     if (!cmSystemTools::FileIsFullPath(newFromFile)) {
       std::string fromFilePath = cmSystemTools::GetFilenamePath(fromFile);
-      newFromFile = fromFilePath + "/" + newFromFile;
+      newFromFile = cmStrCat(fromFilePath, "/", newFromFile);
     }
 
     std::string symlinkTarget = cmSystemTools::GetFilenameName(newFromFile);
@@ -516,14 +515,15 @@
 
       if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
         std::ostringstream e;
-        e << this->Name << " cannot create symlink \"" << toFile << "\".";
-        this->FileCommand->SetError(e.str());
+        e << this->Name << " cannot create symlink \"" << toFile
+          << "\": " << cmSystemTools::GetLastSystemError() << ".";
+        this->Status.SetError(e.str());
         return false;
       }
     }
 
     fromFile = newFromFile;
-    toFile = toFilePath + "/" + symlinkTarget;
+    toFile = cmStrCat(toFilePath, "/", symlinkTarget);
   }
 
   return true;
@@ -537,8 +537,9 @@
   if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) {
     std::ostringstream e;
     e << this->Name << " cannot read symlink \"" << fromFile
-      << "\" to duplicate at \"" << toFile << "\".";
-    this->FileCommand->SetError(e.str());
+      << "\" to duplicate at \"" << toFile
+      << "\": " << cmSystemTools::GetLastSystemError() << ".";
+    this->Status.SetError(e.str());
     return false;
   }
 
@@ -568,8 +569,9 @@
     if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
       std::ostringstream e;
       e << this->Name << " cannot duplicate symlink \"" << fromFile
-        << "\" at \"" << toFile << "\".";
-      this->FileCommand->SetError(e.str());
+        << "\" at \"" << toFile
+        << "\": " << cmSystemTools::GetLastSystemError() << ".";
+      this->Status.SetError(e.str());
       return false;
     }
   }
@@ -597,8 +599,8 @@
   if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) {
     std::ostringstream e;
     e << this->Name << " cannot copy file \"" << fromFile << "\" to \""
-      << toFile << "\".";
-    this->FileCommand->SetError(e.str());
+      << toFile << "\": " << cmSystemTools::GetLastSystemError() << ".";
+    this->Status.SetError(e.str());
     return false;
   }
 
@@ -613,8 +615,8 @@
     if (!cmFileTimes::Copy(fromFile, toFile)) {
       std::ostringstream e;
       e << this->Name << " cannot set modification time on \"" << toFile
-        << "\"";
-      this->FileCommand->SetError(e.str());
+        << "\": " << cmSystemTools::GetLastSystemError() << ".";
+      this->Status.SetError(e.str());
       return false;
     }
   }
@@ -650,8 +652,8 @@
   if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
     std::ostringstream e;
     e << this->Name << " cannot make directory \"" << destination
-      << "\": " << cmSystemTools::GetLastSystemError();
-    this->FileCommand->SetError(e.str());
+      << "\": " << cmSystemTools::GetLastSystemError() << ".";
+    this->Status.SetError(e.str());
     return false;
   }
 
@@ -696,12 +698,8 @@
   for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) {
     if (!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
           strcmp(dir.GetFile(fileNum), "..") == 0)) {
-      std::string fromPath = source;
-      fromPath += "/";
-      fromPath += dir.GetFile(fileNum);
-      std::string toPath = destination;
-      toPath += "/";
-      toPath += dir.GetFile(fileNum);
+      std::string fromPath = cmStrCat(source, '/', dir.GetFile(fileNum));
+      std::string toPath = cmStrCat(destination, '/', dir.GetFile(fileNum));
       if (!this->Install(fromPath, toPath)) {
         return false;
       }
diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h
index a79a60b..612a57f 100644
--- a/Source/cmFileCopier.h
+++ b/Source/cmFileCopier.h
@@ -5,26 +5,28 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileTimeCache.h"
-#include "cm_sys_stat.h"
-#include "cmsys/RegularExpression.hxx"
-
 #include <string>
 #include <vector>
 
-class cmFileCommand;
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_sys_stat.h"
+
+#include "cmFileTimeCache.h"
+
+class cmExecutionStatus;
 class cmMakefile;
 
 // File installation helper class.
 struct cmFileCopier
 {
-  cmFileCopier(cmFileCommand* command, const char* name = "COPY");
+  cmFileCopier(cmExecutionStatus& status, const char* name = "COPY");
   virtual ~cmFileCopier();
 
   bool Run(std::vector<std::string> const& args);
 
 protected:
-  cmFileCommand* FileCommand;
+  cmExecutionStatus& Status;
   cmMakefile* Makefile;
   const char* Name;
   bool Always;
diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx
index d4f76fd..c89be96 100644
--- a/Source/cmFileInstaller.cxx
+++ b/Source/cmFileInstaller.cxx
@@ -3,19 +3,20 @@
 
 #include "cmFileInstaller.h"
 
-#include "cmFSPermissions.h"
-#include "cmFileCommand.h"
-#include "cmMakefile.h"
-#include "cmSystemTools.h"
+#include <sstream>
 
 #include "cm_sys_stat.h"
 
-#include <sstream>
+#include "cmExecutionStatus.h"
+#include "cmFSPermissions.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 using namespace cmFSPermissions;
 
-cmFileInstaller::cmFileInstaller(cmFileCommand* command)
-  : cmFileCopier(command, "INSTALL")
+cmFileInstaller::cmFileInstaller(cmExecutionStatus& status)
+  : cmFileCopier(status, "INSTALL")
   , InstallType(cmInstallType_FILES)
   , Optional(false)
   , MessageAlways(false)
@@ -28,7 +29,7 @@
   // Check whether to copy files always or only if they have changed.
   std::string install_always;
   if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) {
-    this->Always = cmSystemTools::IsOn(install_always);
+    this->Always = cmIsOn(install_always);
   }
   // Get the current manifest.
   this->Manifest =
@@ -38,7 +39,7 @@
 {
   // Save the updated install manifest.
   this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
-                                this->Manifest.c_str());
+                                this->Manifest);
 }
 
 void cmFileInstaller::ManifestAppend(std::string const& file)
@@ -58,8 +59,8 @@
                                  bool copy)
 {
   if (!this->MessageNever && (copy || !this->MessageLazy)) {
-    std::string message = (copy ? "Installing: " : "Up-to-date: ");
-    message += toFile;
+    std::string message =
+      cmStrCat((copy ? "Installing: " : "Up-to-date: "), toFile);
     this->Makefile->DisplayStatus(message, -1);
   }
   if (type != TypeDir) {
@@ -111,19 +112,19 @@
 
   if (!this->Rename.empty()) {
     if (!this->FilesFromDir.empty()) {
-      this->FileCommand->SetError("INSTALL option RENAME may not be "
-                                  "combined with FILES_FROM_DIR.");
+      this->Status.SetError("INSTALL option RENAME may not be "
+                            "combined with FILES_FROM_DIR.");
       return false;
     }
     if (this->InstallType != cmInstallType_FILES &&
         this->InstallType != cmInstallType_PROGRAMS) {
-      this->FileCommand->SetError("INSTALL option RENAME may be used "
-                                  "only with FILES or PROGRAMS.");
+      this->Status.SetError("INSTALL option RENAME may be used "
+                            "only with FILES or PROGRAMS.");
       return false;
     }
     if (this->Files.size() > 1) {
-      this->FileCommand->SetError("INSTALL option RENAME may be used "
-                                  "only with one file.");
+      this->Status.SetError("INSTALL option RENAME may be used "
+                            "only with one file.");
       return false;
     }
   }
@@ -134,9 +135,9 @@
 
   if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) +
        (this->MessageNever ? 1 : 0)) > 1) {
-    this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, "
-                                "MESSAGE_LAZY, and MESSAGE_NEVER "
-                                "are mutually exclusive.");
+    this->Status.SetError("INSTALL options MESSAGE_ALWAYS, "
+                          "MESSAGE_LAZY, and MESSAGE_NEVER "
+                          "are mutually exclusive.");
     return false;
   }
 
@@ -213,7 +214,7 @@
     e << "INSTALL called with old-style " << arg << " argument.  "
       << "This script was generated with an older version of CMake.  "
       << "Re-run this cmake version on your build tree.";
-    this->FileCommand->SetError(e.str());
+    this->Status.SetError(e.str());
     this->Doing = DoingError;
   } else {
     return this->cmFileCopier::CheckKeyword(arg);
@@ -257,7 +258,7 @@
   } else {
     std::ostringstream e;
     e << "Option TYPE given unknown value \"" << stype << "\".";
-    this->FileCommand->SetError(e.str());
+    this->Status.SetError(e.str());
     return false;
   }
   return true;
@@ -269,8 +270,8 @@
 
   // allow for / to be a valid destination
   if (destination.size() < 2 && destination != "/") {
-    this->FileCommand->SetError("called with inappropriate arguments. "
-                                "No DESTINATION provided or .");
+    this->Status.SetError("called with inappropriate arguments. "
+                          "No DESTINATION provided or .");
     return false;
   }
 
@@ -300,7 +301,7 @@
       if (relative) {
         // This is relative path on unix or windows. Since we are doing
         // destdir, this case does not make sense.
-        this->FileCommand->SetError(
+        this->Status.SetError(
           "called with relative DESTINATION. This "
           "does not make sense when using DESTDIR. Specify "
           "absolute path or remove DESTDIR environment variable.");
@@ -310,12 +311,12 @@
       if (ch2 == '/') {
         // looks like a network path.
         std::string message =
-          "called with network path DESTINATION. This "
-          "does not make sense when using DESTDIR. Specify local "
-          "absolute path or remove DESTDIR environment variable."
-          "\nDESTINATION=\n";
-        message += destination;
-        this->FileCommand->SetError(message);
+          cmStrCat("called with network path DESTINATION. This "
+                   "does not make sense when using DESTDIR. Specify local "
+                   "absolute path or remove DESTDIR environment variable."
+                   "\nDESTINATION=\n",
+                   destination);
+        this->Status.SetError(message);
         return false;
       }
     }
@@ -335,14 +336,14 @@
       if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
         std::string errstring = "cannot create directory: " + destination +
           ". Maybe need administrative privileges.";
-        this->FileCommand->SetError(errstring);
+        this->Status.SetError(errstring);
         return false;
       }
     }
     if (!cmSystemTools::FileIsDirectory(destination)) {
       std::string errstring =
         "INSTALL destination: " + destination + " is not a directory.";
-      this->FileCommand->SetError(errstring);
+      this->Status.SetError(errstring);
       return false;
     }
   }
diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h
index 312529a..537cd53 100644
--- a/Source/cmFileInstaller.h
+++ b/Source/cmFileInstaller.h
@@ -5,18 +5,17 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileCopier.h"
-
-#include "cmInstallType.h"
-
 #include <string>
 #include <vector>
 
-class cmFileCommand;
+#include "cmFileCopier.h"
+#include "cmInstallType.h"
+
+class cmExecutionStatus;
 
 struct cmFileInstaller : public cmFileCopier
 {
-  cmFileInstaller(cmFileCommand* command);
+  cmFileInstaller(cmExecutionStatus& status);
   ~cmFileInstaller() override;
 
 protected:
diff --git a/Source/cmFileLock.cxx b/Source/cmFileLock.cxx
index 1e472da..e90f571 100644
--- a/Source/cmFileLock.cxx
+++ b/Source/cmFileLock.cxx
@@ -2,8 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileLock.h"
 
+#include <cassert>
+
 #include "cmFileLockResult.h"
-#include <assert.h>
 
 // Common implementation
 
diff --git a/Source/cmFileLockPool.cxx b/Source/cmFileLockPool.cxx
index d700a79..8db2db2 100644
--- a/Source/cmFileLockPool.cxx
+++ b/Source/cmFileLockPool.cxx
@@ -2,7 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileLockPool.h"
 
-#include <assert.h>
+#include <cassert>
 
 #include "cmAlgorithms.h"
 #include "cmFileLock.h"
diff --git a/Source/cmFileLockPool.h b/Source/cmFileLockPool.h
index 41203ba..dae68dd 100644
--- a/Source/cmFileLockPool.h
+++ b/Source/cmFileLockPool.h
@@ -72,17 +72,17 @@
     bool IsAlreadyLocked(const std::string& filename) const;
 
   private:
-    typedef std::vector<cmFileLock*> List;
-    typedef List::iterator It;
-    typedef List::const_iterator CIt;
+    using List = std::vector<cmFileLock*>;
+    using It = List::iterator;
+    using CIt = List::const_iterator;
 
     List Locks;
   };
 
-  typedef std::vector<ScopePool*> List;
+  using List = std::vector<ScopePool*>;
 
-  typedef List::iterator It;
-  typedef List::const_iterator CIt;
+  using It = List::iterator;
+  using CIt = List::const_iterator;
 
   List FunctionScopes;
   List FileScopes;
diff --git a/Source/cmFileLockResult.cxx b/Source/cmFileLockResult.cxx
index 9ca5d8a..9d5a6c6 100644
--- a/Source/cmFileLockResult.cxx
+++ b/Source/cmFileLockResult.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileLockResult.h"
 
-#include <errno.h>
-#include <string.h>
+#include <cerrno>
+#include <cstring>
 
 #define WINMSG_BUF_LEN (1024)
 cmFileLockResult cmFileLockResult::MakeOk()
 {
-  return cmFileLockResult(OK, 0);
+  return { OK, 0 };
 }
 
 cmFileLockResult cmFileLockResult::MakeSystem()
@@ -18,27 +18,27 @@
 #else
   const Error lastError = errno;
 #endif
-  return cmFileLockResult(SYSTEM, lastError);
+  return { SYSTEM, lastError };
 }
 
 cmFileLockResult cmFileLockResult::MakeTimeout()
 {
-  return cmFileLockResult(TIMEOUT, 0);
+  return { TIMEOUT, 0 };
 }
 
 cmFileLockResult cmFileLockResult::MakeAlreadyLocked()
 {
-  return cmFileLockResult(ALREADY_LOCKED, 0);
+  return { ALREADY_LOCKED, 0 };
 }
 
 cmFileLockResult cmFileLockResult::MakeInternal()
 {
-  return cmFileLockResult(INTERNAL, 0);
+  return { INTERNAL, 0 };
 }
 
 cmFileLockResult cmFileLockResult::MakeNoFunction()
 {
-  return cmFileLockResult(NO_FUNCTION, 0);
+  return { NO_FUNCTION, 0 };
 }
 
 bool cmFileLockResult::IsOk() const
diff --git a/Source/cmFileLockResult.h b/Source/cmFileLockResult.h
index 8b4929a..81c1906 100644
--- a/Source/cmFileLockResult.h
+++ b/Source/cmFileLockResult.h
@@ -19,9 +19,9 @@
 {
 public:
 #if defined(_WIN32)
-  typedef DWORD Error;
+  using Error = DWORD;
 #else
-  typedef int Error;
+  using Error = int;
 #endif
 
   /**
diff --git a/Source/cmFileLockUnix.cxx b/Source/cmFileLockUnix.cxx
index 1bf5013..1456ea8 100644
--- a/Source/cmFileLockUnix.cxx
+++ b/Source/cmFileLockUnix.cxx
@@ -1,13 +1,14 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmFileLock.h"
+#include <cerrno> // errno
+#include <cstdio> // SEEK_SET
 
-#include "cmSystemTools.h"
-#include <errno.h> // errno
 #include <fcntl.h>
-#include <stdio.h> // SEEK_SET
 #include <unistd.h>
 
+#include "cmFileLock.h"
+#include "cmSystemTools.h"
+
 cmFileLock::cmFileLock() = default;
 
 cmFileLockResult cmFileLock::Release()
diff --git a/Source/cmFileLockWin32.cxx b/Source/cmFileLockWin32.cxx
index a61d360..b8e435a 100644
--- a/Source/cmFileLockWin32.cxx
+++ b/Source/cmFileLockWin32.cxx
@@ -1,10 +1,10 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmFileLock.h"
-
-#include "cmSystemTools.h"
 #include <windows.h> // CreateFileW
 
+#include "cmFileLock.h"
+#include "cmSystemTools.h"
+
 cmFileLock::cmFileLock()
 {
 }
diff --git a/Source/cmFileMonitor.cxx b/Source/cmFileMonitor.cxx
index 56ee739..ac8a37e 100644
--- a/Source/cmFileMonitor.cxx
+++ b/Source/cmFileMonitor.cxx
@@ -2,14 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileMonitor.h"
 
-#include "cmAlgorithms.h"
-#include "cmsys/SystemTools.hxx"
-
 #include <cassert>
-#include <stddef.h>
+#include <cstddef>
 #include <unordered_map>
 #include <utility>
 
+#include "cmsys/SystemTools.hxx"
+
+#include "cmAlgorithms.h"
+
 namespace {
 void on_directory_change(uv_fs_event_t* handle, const char* filename,
                          int events, int status);
diff --git a/Source/cmFilePathChecksum.cxx b/Source/cmFilePathChecksum.cxx
index 47a223a..bb3f610 100644
--- a/Source/cmFilePathChecksum.cxx
+++ b/Source/cmFilePathChecksum.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFilePathChecksum.h"
 
+#include <vector>
+
 #include "cmBase32.h"
 #include "cmCryptoHash.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
-#include <vector>
-
 cmFilePathChecksum::cmFilePathChecksum() = default;
 
 cmFilePathChecksum::cmFilePathChecksum(std::string const& currentSrcDir,
diff --git a/Source/cmFilePathChecksum.h b/Source/cmFilePathChecksum.h
index 30881ce..b7d5cd2 100644
--- a/Source/cmFilePathChecksum.h
+++ b/Source/cmFilePathChecksum.h
@@ -6,7 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <array>
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 #include <utility>
 
diff --git a/Source/cmFileTime.cxx b/Source/cmFileTime.cxx
index 253457f..96c70fe 100644
--- a/Source/cmFileTime.cxx
+++ b/Source/cmFileTime.cxx
@@ -2,15 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileTime.h"
 
+#include <ctime>
 #include <string>
-#include <time.h>
 
 // Use a platform-specific API to get file times efficiently.
 #if !defined(_WIN32) || defined(__CYGWIN__)
 #  include "cm_sys_stat.h"
 #else
-#  include "cmsys/Encoding.hxx"
 #  include <windows.h>
+
+#  include "cmsys/Encoding.hxx"
 #endif
 
 bool cmFileTime::Load(std::string const& fileName)
diff --git a/Source/cmFileTime.h b/Source/cmFileTime.h
index d4de4e0..e9a8559 100644
--- a/Source/cmFileTime.h
+++ b/Source/cmFileTime.h
@@ -14,7 +14,7 @@
 class cmFileTime
 {
 public:
-  typedef long long NSC;
+  using NSC = long long;
   static constexpr NSC NsPerS = 1000000000;
 
   cmFileTime() = default;
diff --git a/Source/cmFileTimeCache.cxx b/Source/cmFileTimeCache.cxx
index 24d6bf6..0d1dae5 100644
--- a/Source/cmFileTimeCache.cxx
+++ b/Source/cmFileTimeCache.cxx
@@ -38,7 +38,8 @@
                               int* result)
 {
   // Get the modification time for each file.
-  cmFileTime ft1, ft2;
+  cmFileTime ft1;
+  cmFileTime ft2;
   if (this->Load(f1, ft1) && this->Load(f2, ft2)) {
     // Compare the two modification times.
     *result = ft1.Compare(ft2);
@@ -52,7 +53,8 @@
 bool cmFileTimeCache::DifferS(std::string const& f1, std::string const& f2)
 {
   // Get the modification time for each file.
-  cmFileTime ft1, ft2;
+  cmFileTime ft1;
+  cmFileTime ft2;
   if (this->Load(f1, ft1) && this->Load(f2, ft2)) {
     // Compare the two modification times.
     return ft1.DifferS(ft2);
diff --git a/Source/cmFileTimeCache.h b/Source/cmFileTimeCache.h
index 4f1a3a2..83b77b6 100644
--- a/Source/cmFileTimeCache.h
+++ b/Source/cmFileTimeCache.h
@@ -5,10 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileTime.h" // IWYU pragma: keep
 #include <string>
 #include <unordered_map>
 
+#include "cmFileTime.h" // IWYU pragma: keep
+
 /** \class cmFileTimeCache
  * \brief Caches file modification times in an internal map for fast lookups.
  */
diff --git a/Source/cmFileTimes.cxx b/Source/cmFileTimes.cxx
index fd4f679..d8fe24c 100644
--- a/Source/cmFileTimes.cxx
+++ b/Source/cmFileTimes.cxx
@@ -2,14 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileTimes.h"
 
-#include "cmAlgorithms.h"
-#include "cm_sys_stat.h"
-
 #include <utility>
 
+#include <cm/memory>
+
+#include "cm_sys_stat.h"
+
 #if defined(_WIN32)
-#  include "cmSystemTools.h"
 #  include <windows.h>
+
+#  include "cmSystemTools.h"
 #else
 #  include <utime.h>
 #endif
diff --git a/Source/cmFileTimes.h b/Source/cmFileTimes.h
index cbf0fe2..191d89e 100644
--- a/Source/cmFileTimes.h
+++ b/Source/cmFileTimes.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 
 /** \class cmFileTimes
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 42aff04..7d74118 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFindBase.h"
 
+#include <cstddef>
 #include <deque>
 #include <iostream>
 #include <map>
-#include <stddef.h>
 
 #include "cmAlgorithms.h"
 #include "cmMakefile.h"
@@ -13,9 +13,13 @@
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-cmFindBase::cmFindBase()
+class cmExecutionStatus;
+
+cmFindBase::cmFindBase(cmExecutionStatus& status)
+  : cmFindCommon(status)
 {
   this->AlreadyInCache = false;
   this->AlreadyInCacheWithoutMetaInfo = false;
@@ -67,6 +71,9 @@
   }
   this->AlreadyInCache = false;
 
+  // Find what search path locations have been enabled/disable
+  this->SelectDefaultSearchModes();
+
   // Find the current root path mode.
   this->SelectDefaultRootPathMode();
 
@@ -185,9 +192,7 @@
   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeEnvironment];
 
   // Add CMAKE_*_PATH environment variables
-  std::string var = "CMAKE_";
-  var += this->CMakePathName;
-  var += "_PATH";
+  std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
   paths.AddEnvPrefixPath("CMAKE_PREFIX_PATH");
   paths.AddEnvPath(var);
 
@@ -219,9 +224,7 @@
   // Add CMake variables of the same name as the previous environment
   // variables CMAKE_*_PATH to be used most of the time with -D
   // command line options
-  std::string var = "CMAKE_";
-  var += this->CMakePathName;
-  var += "_PATH";
+  std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
   paths.AddCMakePrefixPath("CMAKE_PREFIX_PATH");
   paths.AddCMakePath(var);
 
@@ -253,9 +256,7 @@
 {
   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem];
 
-  std::string var = "CMAKE_SYSTEM_";
-  var += this->CMakePathName;
-  var += "_PATH";
+  std::string var = cmStrCat("CMAKE_SYSTEM_", this->CMakePathName, "_PATH");
   paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
   paths.AddCMakePath(var);
 
@@ -320,7 +321,7 @@
         this->Makefile->GetDefinition(this->VariableName)) {
     cmState* state = this->Makefile->GetState();
     const char* cacheEntry = state->GetCacheEntryValue(this->VariableName);
-    bool found = !cmSystemTools::IsNOTFOUND(cacheValue);
+    bool found = !cmIsNOTFOUND(cacheValue);
     bool cached = cacheEntry != nullptr;
     if (found) {
       // If the user specifies the entry on the command line without a
diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h
index 88b5b6c..f75db5f 100644
--- a/Source/cmFindBase.h
+++ b/Source/cmFindBase.h
@@ -10,6 +10,8 @@
 
 #include "cmFindCommon.h"
 
+class cmExecutionStatus;
+
 /** \class cmFindBase
  * \brief Base class for most FIND_XXX commands.
  *
@@ -19,7 +21,9 @@
 class cmFindBase : public cmFindCommon
 {
 public:
-  cmFindBase();
+  cmFindBase(cmExecutionStatus& status);
+  virtual ~cmFindBase() = default;
+
   /**
    * This is called when the command is first encountered in
    * the CMakeLists.txt file.
diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx
index 954558f..badec55 100644
--- a/Source/cmFindCommon.cxx
+++ b/Source/cmFindCommon.cxx
@@ -3,11 +3,14 @@
 #include "cmFindCommon.h"
 
 #include <algorithm>
-#include <string.h>
+#include <array>
+#include <cstring>
 #include <utility>
 
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmFindCommon::PathGroup cmFindCommon::PathGroup::All("ALL");
@@ -22,7 +25,9 @@
 cmFindCommon::PathLabel cmFindCommon::PathLabel::CMakeSystem("CMAKE_SYSTEM");
 cmFindCommon::PathLabel cmFindCommon::PathLabel::Guess("GUESS");
 
-cmFindCommon::cmFindCommon()
+cmFindCommon::cmFindCommon(cmExecutionStatus& status)
+  : Makefile(&status.GetMakefile())
+  , Status(status)
 {
   this->FindRootPathMode = RootPathModeBoth;
   this->NoDefaultPath = false;
@@ -49,7 +54,10 @@
   this->InitializeSearchPathGroups();
 }
 
-cmFindCommon::~cmFindCommon() = default;
+void cmFindCommon::SetError(std::string const& e)
+{
+  this->Status.SetError(e);
+}
 
 void cmFindCommon::InitializeSearchPathGroups()
 {
@@ -90,8 +98,8 @@
 void cmFindCommon::SelectDefaultRootPathMode()
 {
   // Check the policy variable for this find command type.
-  std::string findRootPathVar = "CMAKE_FIND_ROOT_PATH_MODE_";
-  findRootPathVar += this->CMakePathName;
+  std::string findRootPathVar =
+    cmStrCat("CMAKE_FIND_ROOT_PATH_MODE_", this->CMakePathName);
   std::string rootPathMode =
     this->Makefile->GetSafeDefinition(findRootPathVar);
   if (rootPathMode == "NEVER") {
@@ -144,6 +152,26 @@
   }
 }
 
+void cmFindCommon::SelectDefaultSearchModes()
+{
+  const std::array<std::pair<bool&, std::string>, 5> search_paths = {
+    { { this->NoPackageRootPath, "CMAKE_FIND_USE_PACKAGE_ROOT_PATH" },
+      { this->NoCMakePath, "CMAKE_FIND_USE_CMAKE_PATH" },
+      { this->NoCMakeEnvironmentPath,
+        "CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH" },
+      { this->NoSystemEnvironmentPath,
+        "CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH" },
+      { this->NoCMakeSystemPath, "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH" } }
+  };
+
+  for (auto& path : search_paths) {
+    const char* def = this->Makefile->GetDefinition(path.second);
+    if (def) {
+      path.first = !cmIsOn(def);
+    }
+  }
+}
+
 void cmFindCommon::RerootPaths(std::vector<std::string>& paths)
 {
 #if 0
@@ -174,7 +202,7 @@
   // Construct the list of path roots with no trailing slashes.
   std::vector<std::string> roots;
   if (rootPath) {
-    cmSystemTools::ExpandListArgument(rootPath, roots);
+    cmExpandList(rootPath, roots);
   }
   if (sysrootCompile) {
     roots.emplace_back(sysrootCompile);
@@ -207,8 +235,7 @@
         rootedDir = up;
       } else if (!up.empty() && up[0] != '~') {
         // Start with the new root.
-        rootedDir = r;
-        rootedDir += "/";
+        rootedDir = cmStrCat(r, '/');
 
         // Append the original path with its old root removed.
         rootedDir += cmSystemTools::SplitPathRootComponent(up);
@@ -240,7 +267,7 @@
       continue;
     }
 
-    cmSystemTools::ExpandListArgument(ignorePath, ignore);
+    cmExpandList(ignorePath, ignore);
   }
 
   for (std::string& i : ignore) {
diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h
index 89ff174..8177eac 100644
--- a/Source/cmFindCommon.h
+++ b/Source/cmFindCommon.h
@@ -10,10 +10,12 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
 #include "cmPathLabel.h"
 #include "cmSearchPath.h"
 
+class cmExecutionStatus;
+class cmMakefile;
+
 /** \class cmFindCommon
  * \brief Base class for FIND_XXX implementations.
  *
@@ -21,11 +23,12 @@
  * cmFindProgramCommand, cmFindPathCommand, cmFindLibraryCommand,
  * cmFindFileCommand, and cmFindPackageCommand.
  */
-class cmFindCommon : public cmCommand
+class cmFindCommon
 {
 public:
-  cmFindCommon();
-  ~cmFindCommon() override;
+  cmFindCommon(cmExecutionStatus& status);
+
+  void SetError(std::string const& e);
 
 protected:
   friend class cmSearchPath;
@@ -90,6 +93,9 @@
   /** Compute the current default bundle/framework search policy.  */
   void SelectDefaultMacMode();
 
+  /** Compute the current default search modes based on global variables.  */
+  void SelectDefaultSearchModes();
+
   // Path arguments prior to path manipulation routines
   std::vector<std::string> UserHintsArgs;
   std::vector<std::string> UserGuessArgs;
@@ -124,6 +130,9 @@
   bool SearchAppBundleFirst;
   bool SearchAppBundleOnly;
   bool SearchAppBundleLast;
+
+  cmMakefile* Makefile;
+  cmExecutionStatus& Status;
 };
 
 #endif
diff --git a/Source/cmFindFileCommand.cxx b/Source/cmFindFileCommand.cxx
index 9840c4f..29a2bc4 100644
--- a/Source/cmFindFileCommand.cxx
+++ b/Source/cmFindFileCommand.cxx
@@ -2,7 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFindFileCommand.h"
 
-cmFindFileCommand::cmFindFileCommand()
+class cmExecutionStatus;
+
+cmFindFileCommand::cmFindFileCommand(cmExecutionStatus& status)
+  : cmFindPathCommand(status)
 {
   this->IncludeFileInPath = true;
 }
+
+bool cmFindFile(std::vector<std::string> const& args,
+                cmExecutionStatus& status)
+{
+  return cmFindFileCommand(status).InitialPass(args);
+}
diff --git a/Source/cmFindFileCommand.h b/Source/cmFindFileCommand.h
index 4309449..7dc6e55 100644
--- a/Source/cmFindFileCommand.h
+++ b/Source/cmFindFileCommand.h
@@ -5,9 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+#include <vector>
+
 #include "cmFindPathCommand.h"
 
-class cmCommand;
+class cmExecutionStatus;
 
 /** \class cmFindFileCommand
  * \brief Define a command to search for an executable program.
@@ -20,11 +23,10 @@
 class cmFindFileCommand : public cmFindPathCommand
 {
 public:
-  cmFindFileCommand();
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFindFileCommand; }
+  cmFindFileCommand(cmExecutionStatus& status);
 };
 
+bool cmFindFile(std::vector<std::string> const& args,
+                cmExecutionStatus& status);
+
 #endif
diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx
index 73d602d..20221b1 100644
--- a/Source/cmFindLibraryCommand.cxx
+++ b/Source/cmFindLibraryCommand.cxx
@@ -2,30 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFindLibraryCommand.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
+#include <cstdio>
+#include <cstring>
 #include <set>
-#include <stdio.h>
-#include <string.h>
 #include <utility>
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
 
-cmFindLibraryCommand::cmFindLibraryCommand()
+cmFindLibraryCommand::cmFindLibraryCommand(cmExecutionStatus& status)
+  : cmFindBase(status)
 {
   this->EnvironmentPath = "LIB";
   this->NamesPerDirAllowed = true;
 }
 
 // cmFindLibraryCommand
-bool cmFindLibraryCommand::InitialPass(std::vector<std::string> const& argsIn,
-                                       cmExecutionStatus&)
+bool cmFindLibraryCommand::InitialPass(std::vector<std::string> const& argsIn)
 {
   this->VariableDocumentation = "Path to a library.";
   this->CMakePathName = "LIBRARY";
@@ -190,7 +192,7 @@
   std::string SuffixRegexStr;
 
   // Keep track of the best library file found so far.
-  typedef std::vector<std::string>::size_type size_type;
+  using size_type = std::vector<std::string>::size_type;
   std::string BestPath;
 
   // Support for OpenBSD shared library naming: lib<name>.so.<major>.<minor>
@@ -237,8 +239,8 @@
     this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_PREFIXES");
   std::string const& suffixes_list =
     this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_SUFFIXES");
-  cmSystemTools::ExpandListArgument(prefixes_list, this->Prefixes, true);
-  cmSystemTools::ExpandListArgument(suffixes_list, this->Suffixes, true);
+  cmExpandList(prefixes_list, this->Prefixes, true);
+  cmExpandList(suffixes_list, this->Suffixes, true);
   this->RegexFromList(this->PrefixRegexStr, this->Prefixes);
   this->RegexFromList(this->SuffixRegexStr, this->Suffixes);
 
@@ -311,8 +313,7 @@
   entry.Raw = name;
 
   // Build a regular expression to match library names.
-  std::string regex = "^";
-  regex += this->PrefixRegexStr;
+  std::string regex = cmStrCat('^', this->PrefixRegexStr);
   this->RegexFromLiteral(regex, name);
   regex += this->SuffixRegexStr;
   if (this->OpenBSD) {
@@ -348,8 +349,7 @@
   // one cannot tell just from the library name whether it is a static
   // library or an import library).
   if (name.TryRaw) {
-    this->TestPath = path;
-    this->TestPath += name.Raw;
+    this->TestPath = cmStrCat(path, name.Raw);
     if (cmSystemTools::FileExists(this->TestPath, true)) {
       this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
       cmSystemTools::ConvertToUnixSlashes(this->BestPath);
@@ -374,8 +374,7 @@
     std::string const& testName = origName;
 #endif
     if (name.Regex.find(testName)) {
-      this->TestPath = path;
-      this->TestPath += origName;
+      this->TestPath = cmStrCat(path, origName);
       if (!cmSystemTools::FileIsDirectory(this->TestPath)) {
         // This is a matching file.  Check if it is better than the
         // best name found so far.  Earlier prefixes are preferred,
@@ -465,9 +464,7 @@
   // Search for all names in each search path.
   for (std::string const& d : this->SearchPaths) {
     for (std::string const& n : this->Names) {
-      fwPath = d;
-      fwPath += n;
-      fwPath += ".framework";
+      fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
         return cmSystemTools::CollapseFullPath(fwPath);
       }
@@ -484,9 +481,7 @@
   // Search for each name in all search paths.
   for (std::string const& n : this->Names) {
     for (std::string const& d : this->SearchPaths) {
-      fwPath = d;
-      fwPath += n;
-      fwPath += ".framework";
+      fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
         return cmSystemTools::CollapseFullPath(fwPath);
       }
@@ -496,3 +491,9 @@
   // No framework found.
   return "";
 }
+
+bool cmFindLibrary(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
+{
+  return cmFindLibraryCommand(status).InitialPass(args);
+}
diff --git a/Source/cmFindLibraryCommand.h b/Source/cmFindLibraryCommand.h
index fb8a700..b2f71b3 100644
--- a/Source/cmFindLibraryCommand.h
+++ b/Source/cmFindLibraryCommand.h
@@ -10,7 +10,6 @@
 
 #include "cmFindBase.h"
 
-class cmCommand;
 class cmExecutionStatus;
 
 /** \class cmFindLibraryCommand
@@ -23,18 +22,9 @@
 class cmFindLibraryCommand : public cmFindBase
 {
 public:
-  cmFindLibraryCommand();
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFindLibraryCommand; }
+  cmFindLibraryCommand(cmExecutionStatus& status);
 
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
+  bool InitialPass(std::vector<std::string> const& args);
 
 protected:
   void AddArchitecturePaths(const char* suffix);
@@ -52,4 +42,7 @@
   std::string FindFrameworkLibraryDirsPerName();
 };
 
+bool cmFindLibrary(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+
 #endif
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 171fa77..ea936cf 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -2,22 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFindPackageCommand.h"
 
-#include "cmSystemTools.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <deque>
+#include <functional>
+#include <iterator>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/String.h"
-#include <algorithm>
-#include <assert.h>
-#include <deque>
-#include <functional>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <string.h>
-#include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmMakefile.h"
@@ -27,6 +28,8 @@
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmVersion.h"
 
 #if defined(__HAIKU__)
@@ -83,7 +86,8 @@
   // else do not sort
 }
 
-cmFindPackageCommand::cmFindPackageCommand()
+cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status)
+  : cmFindCommon(status)
 {
   this->CMakePathName = "PACKAGE";
   this->Quiet = false;
@@ -141,8 +145,7 @@
     std::make_pair(PathLabel::SystemRegistry, cmSearchPath(this)));
 }
 
-bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
 {
   if (args.empty()) {
     this->SetError("called with incorrect number of arguments");
@@ -188,7 +191,12 @@
   }
 
   // Check if User Package Registry should be disabled
-  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
+  // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has
+  // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
+  if (const char* def =
+        this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) {
+    this->NoUserRegistry = !cmIsOn(def);
+  } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
     this->NoUserRegistry = true;
   }
 
@@ -219,6 +227,9 @@
     this->SortDirection = strcmp(sd, "ASC") == 0 ? Asc : Dec;
   }
 
+  // Find what search path locations have been enabled/disable
+  this->SelectDefaultSearchModes();
+
   // Find the current root path mode.
   this->SelectDefaultRootPathMode();
 
@@ -338,11 +349,10 @@
     } else if (doing == DoingConfigs) {
       if (args[i].find_first_of(":/\\") != std::string::npos ||
           cmSystemTools::GetFilenameLastExtension(args[i]) != ".cmake") {
-        std::ostringstream e;
-        e << "given CONFIGS option followed by invalid file name \"" << args[i]
-          << "\".  The names given must be file names without "
-          << "a path and with a \".cmake\" extension.";
-        this->SetError(e.str());
+        this->SetError(cmStrCat(
+          "given CONFIGS option followed by invalid file name \"", args[i],
+          "\".  The names given must be file names without "
+          "a path and with a \".cmake\" extension."));
         return false;
       }
       this->Configs.push_back(args[i]);
@@ -350,9 +360,8 @@
       haveVersion = true;
       this->Version = args[i];
     } else {
-      std::ostringstream e;
-      e << "called with invalid argument \"" << args[i] << "\"";
-      this->SetError(e.str());
+      this->SetError(
+        cmStrCat("called with invalid argument \"", args[i], "\""));
       return false;
     }
   }
@@ -362,10 +371,10 @@
                         optionalComponents.begin(), optionalComponents.end(),
                         std::back_inserter(doubledComponents));
   if (!doubledComponents.empty()) {
-    std::ostringstream e;
-    e << "called with components that are both required and optional:\n";
-    e << cmWrap("  ", doubledComponents, "", "\n") << "\n";
-    this->SetError(e.str());
+    this->SetError(
+      cmStrCat("called with components that are both required and "
+               "optional:\n",
+               cmWrap("  ", doubledComponents, "", "\n"), "\n"));
     return false;
   }
 
@@ -398,19 +407,16 @@
   if (this->Version.empty() || components.empty()) {
     // Check whether we are recursing inside "Find<name>.cmake" within
     // another find_package(<name>) call.
-    std::string mod = this->Name;
-    mod += "_FIND_MODULE";
+    std::string mod = cmStrCat(this->Name, "_FIND_MODULE");
     if (this->Makefile->IsOn(mod)) {
       if (this->Version.empty()) {
         // Get version information from the outer call if necessary.
         // Requested version string.
-        std::string ver = this->Name;
-        ver += "_FIND_VERSION";
+        std::string ver = cmStrCat(this->Name, "_FIND_VERSION");
         this->Version = this->Makefile->GetSafeDefinition(ver);
 
         // Whether an exact version is required.
-        std::string exact = this->Name;
-        exact += "_FIND_VERSION_EXACT";
+        std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
         this->VersionExact = this->Makefile->IsOn(exact);
       }
       if (components.empty()) {
@@ -448,15 +454,14 @@
     }
   }
 
-  std::string disableFindPackageVar = "CMAKE_DISABLE_FIND_PACKAGE_";
-  disableFindPackageVar += this->Name;
+  std::string disableFindPackageVar =
+    cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name);
   if (this->Makefile->IsOn(disableFindPackageVar)) {
     if (this->Required) {
-      std::ostringstream e;
-      e << "for module " << this->Name << " called with REQUIRED, but "
-        << disableFindPackageVar
-        << " is enabled. A REQUIRED package cannot be disabled.";
-      this->SetError(e.str());
+      this->SetError(
+        cmStrCat("for module ", this->Name, " called with REQUIRED, but ",
+                 disableFindPackageVar,
+                 " is enabled. A REQUIRED package cannot be disabled."));
       return false;
     }
 
@@ -488,7 +493,7 @@
         // NEW behavior is to honor the <pkg>_ROOT variables.
         std::string const rootVar = this->Name + "_ROOT";
         if (const char* pkgRoot = this->Makefile->GetDefinition(rootVar)) {
-          cmSystemTools::ExpandListArgument(pkgRoot, rootPaths, false);
+          cmExpandList(pkgRoot, rootPaths, false);
         }
         cmSystemTools::GetPath(rootPaths, rootVar.c_str());
       } break;
@@ -579,8 +584,7 @@
 
 bool cmFindPackageCommand::FindPackageUsingConfigMode()
 {
-  this->Variable = this->Name;
-  this->Variable += "_DIR";
+  this->Variable = cmStrCat(this->Name, "_DIR");
 
   // Add the default name.
   if (this->Names.empty()) {
@@ -590,12 +594,10 @@
   // Add the default configs.
   if (this->Configs.empty()) {
     for (std::string const& n : this->Names) {
-      std::string config = n;
-      config += "Config.cmake";
+      std::string config = cmStrCat(n, "Config.cmake");
       this->Configs.push_back(config);
 
-      config = cmSystemTools::LowerCase(n);
-      config += "-config.cmake";
+      config = cmStrCat(cmSystemTools::LowerCase(n), "-config.cmake");
       this->Configs.push_back(std::move(config));
     }
   }
@@ -624,24 +626,21 @@
   if (this->Quiet) {
     // Tell the module that is about to be read that it should find
     // quietly.
-    std::string quietly = this->Name;
-    quietly += "_FIND_QUIETLY";
+    std::string quietly = cmStrCat(this->Name, "_FIND_QUIETLY");
     this->AddFindDefinition(quietly, "1");
   }
 
   if (this->Required) {
     // Tell the module that is about to be read that it should report
     // a fatal error if the package is not found.
-    std::string req = this->Name;
-    req += "_FIND_REQUIRED";
+    std::string req = cmStrCat(this->Name, "_FIND_REQUIRED");
     this->AddFindDefinition(req, "1");
   }
 
   if (!this->Version.empty()) {
     // Tell the module that is about to be read what version of the
     // package has been requested.
-    std::string ver = this->Name;
-    ver += "_FIND_VERSION";
+    std::string ver = cmStrCat(this->Name, "_FIND_VERSION");
     this->AddFindDefinition(ver, this->Version.c_str());
     char buf[64];
     sprintf(buf, "%u", this->VersionMajor);
@@ -656,8 +655,7 @@
     this->AddFindDefinition(ver + "_COUNT", buf);
 
     // Tell the module whether an exact version has been requested.
-    std::string exact = this->Name;
-    exact += "_FIND_VERSION_EXACT";
+    std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
     this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
   }
 }
@@ -671,7 +669,9 @@
   } else {
     this->OriginalDefs[var].exists = false;
   }
-  this->Makefile->AddDefinition(var, val);
+  if (val) {
+    this->Makefile->AddDefinition(var, val);
+  }
 }
 
 void cmFindPackageCommand::RestoreFindDefinitions()
@@ -679,7 +679,7 @@
   for (auto const& i : this->OriginalDefs) {
     OriginalDef const& od = i.second;
     if (od.exists) {
-      this->Makefile->AddDefinition(i.first, od.value.c_str());
+      this->Makefile->AddDefinition(i.first, od.value);
     } else {
       this->Makefile->RemoveDefinition(i.first);
     }
@@ -688,9 +688,7 @@
 
 bool cmFindPackageCommand::FindModule(bool& found)
 {
-  std::string module = "Find";
-  module += this->Name;
-  module += ".cmake";
+  std::string module = cmStrCat("Find", this->Name, ".cmake");
   bool system = false;
   std::string mfile = this->Makefile->GetModulesFile(module, system);
   if (!mfile.empty()) {
@@ -701,9 +699,9 @@
           this->Makefile->GetPolicyStatus(it->second);
         switch (status) {
           case cmPolicies::WARN: {
-            std::ostringstream e;
-            e << cmPolicies::GetPolicyWarning(it->second) << "\n";
-            this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+            this->Makefile->IssueMessage(
+              MessageType::AUTHOR_WARNING,
+              cmStrCat(cmPolicies::GetPolicyWarning(it->second), "\n"));
             CM_FALLTHROUGH;
           }
           case cmPolicies::OLD:
@@ -719,8 +717,7 @@
     // Load the module we found, and set "<name>_FIND_MODULE" to true
     // while inside it.
     found = true;
-    std::string var = this->Name;
-    var += "_FIND_MODULE";
+    std::string var = cmStrCat(this->Name, "_FIND_MODULE");
     this->Makefile->AddDefinition(var, "1");
     bool result = this->ReadListFile(mfile, DoPolicyScope);
     this->Makefile->RemoveDefinition(var);
@@ -734,19 +731,13 @@
 {
   this->ConsideredConfigs.clear();
 
-  // Support old capitalization behavior.
-  std::string upperDir = cmSystemTools::UpperCase(this->Name);
-  std::string upperFound = cmSystemTools::UpperCase(this->Name);
-  upperDir += "_DIR";
-  upperFound += "_FOUND";
-
   // Try to find the config file.
   const char* def = this->Makefile->GetDefinition(this->Variable);
 
   // Try to load the config file if the directory is known
   bool fileFound = false;
   if (this->UseConfigFiles) {
-    if (!cmSystemTools::IsOff(def)) {
+    if (!cmIsOff(def)) {
       // Get the directory from the variable value.
       std::string dir = def;
       cmSystemTools::ConvertToUnixSlashes(dir);
@@ -766,7 +757,7 @@
     }
 
     // Search for the config file if it is not already found.
-    if (cmSystemTools::IsOff(def) || !fileFound) {
+    if (cmIsOff(def) || !fileFound) {
       fileFound = this->FindConfig();
     }
 
@@ -779,10 +770,8 @@
     }
   }
 
-  std::string foundVar = this->Name;
-  foundVar += "_FOUND";
-  std::string notFoundMessageVar = this->Name;
-  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
+  std::string foundVar = cmStrCat(this->Name, "_FOUND");
+  std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
   std::string notFoundMessage;
 
   // If the directory for the config file was found, try to read the file.
@@ -856,8 +845,7 @@
       // If there are files in ConsideredConfigs, it means that FooConfig.cmake
       // have been found, but they didn't have appropriate versions.
       else if (!this->ConsideredConfigs.empty()) {
-        std::vector<ConfigFileInfo>::const_iterator duplicate_end =
-          cmRemoveDuplicates(this->ConsideredConfigs);
+        auto duplicate_end = cmRemoveDuplicates(this->ConsideredConfigs);
         e << "Could not find a configuration file for package \"" << this->Name
           << "\" that "
           << (this->VersionExact ? "exactly matches" : "is compatible with")
@@ -872,9 +860,8 @@
       } else {
         std::string requestedVersionString;
         if (!this->Version.empty()) {
-          requestedVersionString = " (requested version ";
-          requestedVersionString += this->Version;
-          requestedVersionString += ")";
+          requestedVersionString =
+            cmStrCat(" (requested version ", this->Version, ')');
         }
 
         if (this->UseConfigFiles) {
@@ -945,10 +932,10 @@
     }
     // output result if in config mode but not in quiet mode
     else if (!this->Quiet) {
-      std::ostringstream aw;
-      aw << "Could NOT find " << this->Name << " (missing: " << this->Name
-         << "_DIR)";
-      this->Makefile->DisplayStatus(aw.str(), -1);
+      this->Makefile->DisplayStatus(cmStrCat("Could NOT find ", this->Name,
+                                             " (missing: ", this->Name,
+                                             "_DIR)"),
+                                    -1);
     }
   }
 
@@ -956,18 +943,17 @@
   this->Makefile->AddDefinition(foundVar, found ? "1" : "0");
 
   // Set a variable naming the configuration file that was found.
-  std::string fileVar = this->Name;
-  fileVar += "_CONFIG";
+  std::string fileVar = cmStrCat(this->Name, "_CONFIG");
   if (found) {
-    this->Makefile->AddDefinition(fileVar, this->FileFound.c_str());
+    this->Makefile->AddDefinition(fileVar, this->FileFound);
   } else {
     this->Makefile->RemoveDefinition(fileVar);
   }
 
-  std::string consideredConfigsVar = this->Name;
-  consideredConfigsVar += "_CONSIDERED_CONFIGS";
-  std::string consideredVersionsVar = this->Name;
-  consideredVersionsVar += "_CONSIDERED_VERSIONS";
+  std::string consideredConfigsVar =
+    cmStrCat(this->Name, "_CONSIDERED_CONFIGS");
+  std::string consideredVersionsVar =
+    cmStrCat(this->Name, "_CONSIDERED_VERSIONS");
 
   std::string consideredConfigFiles;
   std::string consideredVersions;
@@ -981,11 +967,9 @@
     sep = ";";
   }
 
-  this->Makefile->AddDefinition(consideredConfigsVar,
-                                consideredConfigFiles.c_str());
+  this->Makefile->AddDefinition(consideredConfigsVar, consideredConfigFiles);
 
-  this->Makefile->AddDefinition(consideredVersionsVar,
-                                consideredVersions.c_str());
+  this->Makefile->AddDefinition(consideredVersionsVar, consideredVersions);
 
   return result;
 }
@@ -1031,9 +1015,8 @@
     init = this->Variable + "-NOTFOUND";
   }
   std::string help =
-    "The directory containing a CMake configuration file for ";
-  help += this->Name;
-  help += ".";
+    cmStrCat("The directory containing a CMake configuration file for ",
+             this->Name, '.');
   // We force the value since we do not get here if it was already set.
   this->Makefile->AddCacheDefinition(this->Variable, init.c_str(),
                                      help.c_str(), cmStateEnums::PATH, true);
@@ -1080,9 +1063,7 @@
   if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
     return true;
   }
-  std::string e = "Error reading CMake code from \"";
-  e += f;
-  e += "\".";
+  std::string e = cmStrCat("Error reading CMake code from \"", f, "\".");
   this->SetError(e);
   return false;
 }
@@ -1095,8 +1076,8 @@
   if (foundProp && *foundProp) {
     std::string tmp = foundProp;
 
-    cmSystemTools::ExpandListArgument(tmp, foundContents, false);
-    std::vector<std::string>::iterator nameIt =
+    cmExpandList(tmp, foundContents, false);
+    auto nameIt =
       std::find(foundContents.begin(), foundContents.end(), this->Name);
     if (nameIt != foundContents.end()) {
       foundContents.erase(nameIt);
@@ -1109,8 +1090,8 @@
   if (notFoundProp && *notFoundProp) {
     std::string tmp = notFoundProp;
 
-    cmSystemTools::ExpandListArgument(tmp, notFoundContents, false);
-    std::vector<std::string>::iterator nameIt =
+    cmExpandList(tmp, notFoundContents, false);
+    auto nameIt =
       std::find(notFoundContents.begin(), notFoundContents.end(), this->Name);
     if (nameIt != notFoundContents.end()) {
       notFoundContents.erase(nameIt);
@@ -1134,45 +1115,38 @@
 void cmFindPackageCommand::AppendSuccessInformation()
 {
   {
-    std::string transitivePropName = "_CMAKE_";
-    transitivePropName += this->Name + "_TRANSITIVE_DEPENDENCY";
+    std::string transitivePropName =
+      cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY");
     this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False");
   }
-  std::string found = this->Name;
-  found += "_FOUND";
+  std::string found = cmStrCat(this->Name, "_FOUND");
   std::string upperFound = cmSystemTools::UpperCase(found);
 
   const char* upperResult = this->Makefile->GetDefinition(upperFound);
   const char* result = this->Makefile->GetDefinition(found);
-  bool packageFound =
-    ((cmSystemTools::IsOn(result)) || (cmSystemTools::IsOn(upperResult)));
+  bool packageFound = ((cmIsOn(result)) || (cmIsOn(upperResult)));
 
   this->AppendToFoundProperty(packageFound);
 
   // Record whether the find was quiet or not, so this can be used
   // e.g. in FeatureSummary.cmake
-  std::string quietInfoPropName = "_CMAKE_";
-  quietInfoPropName += this->Name;
-  quietInfoPropName += "_QUIET";
+  std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET");
   this->Makefile->GetState()->SetGlobalProperty(
     quietInfoPropName, this->Quiet ? "TRUE" : "FALSE");
 
   // set a global property to record the required version of this package
-  std::string versionInfoPropName = "_CMAKE_";
-  versionInfoPropName += this->Name;
-  versionInfoPropName += "_REQUIRED_VERSION";
+  std::string versionInfoPropName =
+    cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION");
   std::string versionInfo;
   if (!this->Version.empty()) {
-    versionInfo = this->VersionExact ? "==" : ">=";
-    versionInfo += " ";
-    versionInfo += this->Version;
+    versionInfo =
+      cmStrCat(this->VersionExact ? "==" : ">=", ' ', this->Version);
   }
   this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName,
                                                 versionInfo.c_str());
   if (this->Required) {
-    std::string requiredInfoPropName = "_CMAKE_";
-    requiredInfoPropName += this->Name;
-    requiredInfoPropName += "_TYPE";
+    std::string requiredInfoPropName =
+      cmStrCat("_CMAKE_", this->Name, "_TYPE");
     this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName,
                                                   "REQUIRED");
   }
@@ -1222,8 +1196,7 @@
   cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot];
 
   // Add the PACKAGE_ROOT_PATH from each enclosing find_package call.
-  for (std::deque<std::vector<std::string>>::const_reverse_iterator pkgPaths =
-         this->Makefile->FindPackageRootPathStack.rbegin();
+  for (auto pkgPaths = this->Makefile->FindPackageRootPathStack.rbegin();
        pkgPaths != this->Makefile->FindPackageRootPathStack.rend();
        ++pkgPaths) {
     for (std::string const& path : *pkgPaths) {
@@ -1282,9 +1255,7 @@
   char dir[B_PATH_NAME_LENGTH];
   if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, dir, sizeof(dir)) ==
       B_OK) {
-    std::string fname = dir;
-    fname += "/cmake/packages/";
-    fname += Name;
+    std::string fname = cmStrCat(dir, "/cmake/packages/", Name);
     this->LoadPackageRegistryDir(fname,
                                  this->LabeledPaths[PathLabel::UserRegistry]);
   }
@@ -1425,9 +1396,7 @@
 
   std::string fname;
   for (unsigned long i = 0; i < files.GetNumberOfFiles(); ++i) {
-    fname = dir;
-    fname += "/";
-    fname += files.GetFile(i);
+    fname = cmStrCat(dir, '/', files.GetFile(i));
 
     if (!cmSystemTools::FileIsDirectory(fname)) {
       // Hold this file hostage until it behaves.
@@ -1542,9 +1511,7 @@
   }
 
   for (std::string const& c : this->Configs) {
-    file = dir;
-    file += "/";
-    file += c;
+    file = cmStrCat(dir, '/', c);
     if (this->DebugMode) {
       fprintf(stderr, "Checking file [%s]\n", file.c_str());
     }
@@ -1570,16 +1537,14 @@
   std::string version_file_base = config_file.substr(0, pos);
 
   // Look for foo-config-version.cmake
-  std::string version_file = version_file_base;
-  version_file += "-version.cmake";
+  std::string version_file = cmStrCat(version_file_base, "-version.cmake");
   if (!haveResult && cmSystemTools::FileExists(version_file, true)) {
     result = this->CheckVersionFile(version_file, version);
     haveResult = true;
   }
 
   // Look for fooConfigVersion.cmake
-  version_file = version_file_base;
-  version_file += "Version.cmake";
+  version_file = cmStrCat(version_file_base, "Version.cmake");
   if (!haveResult && cmSystemTools::FileExists(version_file, true)) {
     result = this->CheckVersionFile(version_file, version);
     haveResult = true;
@@ -1614,8 +1579,8 @@
   this->Makefile->RemoveDefinition("PACKAGE_VERSION_EXACT");
 
   // Set the input variables.
-  this->Makefile->AddDefinition("PACKAGE_FIND_NAME", this->Name.c_str());
-  this->Makefile->AddDefinition("PACKAGE_FIND_VERSION", this->Version.c_str());
+  this->Makefile->AddDefinition("PACKAGE_FIND_NAME", this->Name);
+  this->Makefile->AddDefinition("PACKAGE_FIND_VERSION", this->Version);
   char buf[64];
   sprintf(buf, "%u", this->VersionMajor);
   this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MAJOR", buf);
@@ -1687,12 +1652,11 @@
 void cmFindPackageCommand::StoreVersionFound()
 {
   // Store the whole version string.
-  std::string ver = this->Name;
-  ver += "_VERSION";
+  std::string ver = cmStrCat(this->Name, "_VERSION");
   if (this->VersionFound.empty()) {
     this->Makefile->RemoveDefinition(ver);
   } else {
-    this->Makefile->AddDefinition(ver, this->VersionFound.c_str());
+    this->Makefile->AddDefinition(ver, this->VersionFound);
   }
 
   // Store the version components.
@@ -2036,8 +2000,7 @@
   bool Search(std::string const& parent, cmFileList& lister) override
   {
     // Glob the set of matching files.
-    std::string expr = parent;
-    expr += this->Pattern;
+    std::string expr = cmStrCat(parent, this->Pattern);
     cmsys::Glob g;
     if (!g.FindFiles(expr)) {
       return false;
@@ -2315,3 +2278,9 @@
 }
 
 // TODO: Debug cmsys::Glob double slash problem.
+
+bool cmFindPackage(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
+{
+  return cmFindPackageCommand(status).InitialPass(args);
+}
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 316ca0f..85fe7b6 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -4,9 +4,7 @@
 #define cmFindPackageCommand_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmPolicies.h"
 
-#include "cm_kwiml.h"
 #include <cstddef>
 #include <functional>
 #include <map>
@@ -14,6 +12,11 @@
 #include <string>
 #include <vector>
 
+#include "cm_kwiml.h"
+
+#include "cmFindCommon.h"
+#include "cmPolicies.h"
+
 // IWYU insists we should forward-declare instead of including <functional>,
 // but we cannot forward-declare reliably because some C++ standard libraries
 // put the template in an inline namespace.
@@ -25,9 +28,6 @@
 /* clang-format on */
 #endif
 
-#include "cmFindCommon.h"
-
-class cmCommand;
 class cmExecutionStatus;
 class cmSearchPath;
 
@@ -60,19 +60,9 @@
                    std::vector<std::string>::iterator end, SortOrderType order,
                    SortDirectionType dir);
 
-  cmFindPackageCommand();
+  cmFindPackageCommand(cmExecutionStatus& status);
 
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFindPackageCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
+  bool InitialPass(std::vector<std::string> const& args);
 
 private:
   class PathLabel : public cmFindCommon::PathLabel
@@ -230,8 +220,8 @@
 template <>
 struct hash<cmFindPackageCommand::ConfigFileInfo>
 {
-  typedef cmFindPackageCommand::ConfigFileInfo argument_type;
-  typedef size_t result_type;
+  using argument_type = cmFindPackageCommand::ConfigFileInfo;
+  using result_type = size_t;
 
   result_type operator()(argument_type const& s) const noexcept
   {
@@ -241,4 +231,7 @@
 };
 }
 
+bool cmFindPackage(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+
 #endif
diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx
index 38ff2ed..f5e2631 100644
--- a/Source/cmFindPathCommand.cxx
+++ b/Source/cmFindPathCommand.cxx
@@ -6,19 +6,20 @@
 
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 class cmExecutionStatus;
 
-cmFindPathCommand::cmFindPathCommand()
+cmFindPathCommand::cmFindPathCommand(cmExecutionStatus& status)
+  : cmFindBase(status)
 {
   this->EnvironmentPath = "INCLUDE";
   this->IncludeFileInPath = false;
 }
 
 // cmFindPathCommand
-bool cmFindPathCommand::InitialPass(std::vector<std::string> const& argsIn,
-                                    cmExecutionStatus&)
+bool cmFindPathCommand::InitialPass(std::vector<std::string> const& argsIn)
 {
   this->VariableDocumentation = "Path to a file.";
   this->CMakePathName = "INCLUDE";
@@ -88,12 +89,8 @@
       frameWorkName.clear();
     }
     if (!frameWorkName.empty()) {
-      std::string fpath = dir;
-      fpath += frameWorkName;
-      fpath += ".framework";
-      std::string intPath = fpath;
-      intPath += "/Headers/";
-      intPath += fileName;
+      std::string fpath = cmStrCat(dir, frameWorkName, ".framework");
+      std::string intPath = cmStrCat(fpath, "/Headers/", fileName);
       if (cmSystemTools::FileExists(intPath)) {
         if (this->IncludeFileInPath) {
           return intPath;
@@ -104,9 +101,7 @@
   }
   // if it is not found yet or not a framework header, then do a glob search
   // for all frameworks in the directory: dir/*.framework/Headers/<file>
-  std::string glob = dir;
-  glob += "*.framework/Headers/";
-  glob += file;
+  std::string glob = cmStrCat(dir, "*.framework/Headers/", file);
   cmsys::Glob globIt;
   globIt.FindFiles(glob);
   std::vector<std::string> files = globIt.GetFiles();
@@ -126,8 +121,7 @@
   std::string tryPath;
   for (std::string const& n : this->Names) {
     for (std::string const& sp : this->SearchPaths) {
-      tryPath = sp;
-      tryPath += n;
+      tryPath = cmStrCat(sp, n);
       if (cmSystemTools::FileExists(tryPath)) {
         if (this->IncludeFileInPath) {
           return tryPath;
@@ -151,3 +145,9 @@
   }
   return "";
 }
+
+bool cmFindPath(std::vector<std::string> const& args,
+                cmExecutionStatus& status)
+{
+  return cmFindPathCommand(status).InitialPass(args);
+}
diff --git a/Source/cmFindPathCommand.h b/Source/cmFindPathCommand.h
index cb0db4c..8d1ea8b 100644
--- a/Source/cmFindPathCommand.h
+++ b/Source/cmFindPathCommand.h
@@ -10,7 +10,6 @@
 
 #include "cmFindBase.h"
 
-class cmCommand;
 class cmExecutionStatus;
 
 /** \class cmFindPathCommand
@@ -23,18 +22,9 @@
 class cmFindPathCommand : public cmFindBase
 {
 public:
-  cmFindPathCommand();
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFindPathCommand; }
+  cmFindPathCommand(cmExecutionStatus& status);
 
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
+  bool InitialPass(std::vector<std::string> const& args);
 
   bool IncludeFileInPath;
 
@@ -46,4 +36,7 @@
   std::string FindFrameworkHeader();
 };
 
+bool cmFindPath(std::vector<std::string> const& args,
+                cmExecutionStatus& status);
+
 #endif
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx
index 782f746..e0a3fbf 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,11 +72,10 @@
   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;
-      this->TestNameExt += ext;
+      this->TestNameExt = cmStrCat(name, ext);
       this->TestPath =
         cmSystemTools::CollapseFullPath(this->TestNameExt, path);
 
@@ -88,14 +88,14 @@
   }
 };
 
-cmFindProgramCommand::cmFindProgramCommand()
+cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status)
+  : cmFindBase(status)
 {
   this->NamesPerDirAllowed = true;
 }
 
 // cmFindProgramCommand
-bool cmFindProgramCommand::InitialPass(std::vector<std::string> const& argsIn,
-                                       cmExecutionStatus&)
+bool cmFindProgramCommand::InitialPass(std::vector<std::string> const& argsIn)
 {
   this->VariableDocumentation = "Path to a program.";
   this->CMakePathName = "PROGRAM";
@@ -270,3 +270,9 @@
 
   return executable;
 }
+
+bool cmFindProgram(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
+{
+  return cmFindProgramCommand(status).InitialPass(args);
+}
diff --git a/Source/cmFindProgramCommand.h b/Source/cmFindProgramCommand.h
index 147936c..043b43c 100644
--- a/Source/cmFindProgramCommand.h
+++ b/Source/cmFindProgramCommand.h
@@ -10,7 +10,6 @@
 
 #include "cmFindBase.h"
 
-class cmCommand;
 class cmExecutionStatus;
 
 /** \class cmFindProgramCommand
@@ -24,18 +23,9 @@
 class cmFindProgramCommand : public cmFindBase
 {
 public:
-  cmFindProgramCommand();
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFindProgramCommand; }
+  cmFindProgramCommand(cmExecutionStatus& status);
 
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
+  bool InitialPass(std::vector<std::string> const& args);
 
 private:
   std::string FindProgram();
@@ -46,4 +36,7 @@
   std::string GetBundleExecutable(std::string const& bundlePath);
 };
 
+bool cmFindProgram(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+
 #endif
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index 08003eb..44392ba 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -2,21 +2,50 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmForEachCommand.h"
 
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cstdio>
+#include <cstdlib>
+#include <utility>
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmExecutionStatus.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
+namespace {
+bool HandleInMode(std::vector<std::string> const& args, cmMakefile& makefile);
+
+class cmForEachFunctionBlocker : public cmFunctionBlocker
+{
+public:
+  cmForEachFunctionBlocker(cmMakefile* mf);
+  ~cmForEachFunctionBlocker() override;
+
+  cm::string_view StartCommandName() const override { return "foreach"_s; }
+  cm::string_view EndCommandName() const override { return "endforeach"_s; }
+
+  bool ArgumentsMatch(cmListFileFunction const& lff,
+                      cmMakefile& mf) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& inStatus) override;
+
+  std::vector<std::string> Args;
+
+private:
+  cmMakefile* Makefile;
+};
+
 cmForEachFunctionBlocker::cmForEachFunctionBlocker(cmMakefile* mf)
   : Makefile(mf)
-  , Depth(0)
 {
   this->Makefile->PushLoopBlock();
 }
@@ -26,102 +55,71 @@
   this->Makefile->PopLoopBlock();
 }
 
-bool cmForEachFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
-                                                 cmMakefile& mf,
-                                                 cmExecutionStatus& inStatus)
+bool cmForEachFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                              cmMakefile& mf) const
 {
-  if (lff.Name.Lower == "foreach") {
-    // record the number of nested foreach commands
-    this->Depth++;
-  } else if (lff.Name.Lower == "endforeach") {
-    // if this is the endofreach for this statement
-    if (!this->Depth) {
-      // Remove the function blocker for this scope or bail.
-      std::unique_ptr<cmFunctionBlocker> fb(
-        mf.RemoveFunctionBlocker(this, lff));
-      if (!fb) {
-        return false;
-      }
+  std::vector<std::string> expandedArguments;
+  mf.ExpandArguments(lff.Arguments, expandedArguments);
+  return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
+}
 
-      // at end of for each execute recorded commands
-      // store the old value
-      std::string oldDef;
-      if (mf.GetDefinition(this->Args[0])) {
-        oldDef = mf.GetDefinition(this->Args[0]);
-      }
-
-      for (std::string const& arg : cmMakeRange(this->Args).advance(1)) {
-        // set the variable to the loop value
-        mf.AddDefinition(this->Args[0], arg.c_str());
-        // Invoke all the functions that were collected in the block.
-        cmExecutionStatus status;
-        for (cmListFileFunction const& func : this->Functions) {
-          status.Clear();
-          mf.ExecuteCommand(func, status);
-          if (status.GetReturnInvoked()) {
-            inStatus.SetReturnInvoked();
-            // restore the variable to its prior value
-            mf.AddDefinition(this->Args[0], oldDef.c_str());
-            return true;
-          }
-          if (status.GetBreakInvoked()) {
-            // restore the variable to its prior value
-            mf.AddDefinition(this->Args[0], oldDef.c_str());
-            return true;
-          }
-          if (status.GetContinueInvoked()) {
-            break;
-          }
-          if (cmSystemTools::GetFatalErrorOccured()) {
-            return true;
-          }
-        }
-      }
-
-      // restore the variable to its prior value
-      mf.AddDefinition(this->Args[0], oldDef.c_str());
-      return true;
-    }
-    // close out a nested foreach
-    this->Depth--;
+bool cmForEachFunctionBlocker::Replay(
+  std::vector<cmListFileFunction> functions, cmExecutionStatus& inStatus)
+{
+  cmMakefile& mf = inStatus.GetMakefile();
+  // at end of for each execute recorded commands
+  // store the old value
+  std::string oldDef;
+  if (mf.GetDefinition(this->Args[0])) {
+    oldDef = mf.GetDefinition(this->Args[0]);
   }
 
-  // record the command
-  this->Functions.push_back(lff);
+  for (std::string const& arg : cmMakeRange(this->Args).advance(1)) {
+    // set the variable to the loop value
+    mf.AddDefinition(this->Args[0], arg);
+    // Invoke all the functions that were collected in the block.
+    for (cmListFileFunction const& func : functions) {
+      cmExecutionStatus status(mf);
+      mf.ExecuteCommand(func, status);
+      if (status.GetReturnInvoked()) {
+        inStatus.SetReturnInvoked();
+        // restore the variable to its prior value
+        mf.AddDefinition(this->Args[0], oldDef);
+        return true;
+      }
+      if (status.GetBreakInvoked()) {
+        // restore the variable to its prior value
+        mf.AddDefinition(this->Args[0], oldDef);
+        return true;
+      }
+      if (status.GetContinueInvoked()) {
+        break;
+      }
+      if (cmSystemTools::GetFatalErrorOccured()) {
+        return true;
+      }
+    }
+  }
 
-  // always return true
+  // restore the variable to its prior value
+  mf.AddDefinition(this->Args[0], oldDef);
   return true;
 }
-
-bool cmForEachFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
-                                            cmMakefile& mf)
-{
-  if (lff.Name.Lower == "endforeach") {
-    std::vector<std::string> expandedArguments;
-    mf.ExpandArguments(lff.Arguments, expandedArguments);
-    // if the endforeach has arguments then make sure
-    // they match the begin foreach arguments
-    if ((expandedArguments.empty() ||
-         (expandedArguments[0] == this->Args[0]))) {
-      return true;
-    }
-  }
-  return false;
 }
 
-bool cmForEachCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool cmForEachCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   if (args.size() > 1 && args[1] == "IN") {
-    return this->HandleInMode(args);
+    return HandleInMode(args, status.GetMakefile());
   }
 
   // create a function blocker
-  auto f = cm::make_unique<cmForEachFunctionBlocker>(this->Makefile);
+  auto fb = cm::make_unique<cmForEachFunctionBlocker>(&status.GetMakefile());
   if (args.size() > 1) {
     if (args[1] == "RANGE") {
       int start = 0;
@@ -148,10 +146,9 @@
       }
       if ((start > stop && step > 0) || (start < stop && step < 0) ||
           step == 0) {
-        std::ostringstream str;
-        str << "called with incorrect range specification: start ";
-        str << start << ", stop " << stop << ", step " << step;
-        this->SetError(str.str());
+        status.SetError(
+          cmStrCat("called with incorrect range specification: start ", start,
+                   ", stop ", stop, ", step ", step));
         return false;
       }
       std::vector<std::string> range;
@@ -168,23 +165,23 @@
           break;
         }
       }
-      f->Args = range;
+      fb->Args = range;
     } else {
-      f->Args = args;
+      fb->Args = args;
     }
   } else {
-    f->Args = args;
+    fb->Args = args;
   }
-  this->Makefile->AddFunctionBlocker(f.release());
+  status.GetMakefile().AddFunctionBlocker(std::move(fb));
 
   return true;
 }
 
-bool cmForEachCommand::HandleInMode(std::vector<std::string> const& args)
+namespace {
+bool HandleInMode(std::vector<std::string> const& args, cmMakefile& makefile)
 {
-  std::unique_ptr<cmForEachFunctionBlocker> f(
-    new cmForEachFunctionBlocker(this->Makefile));
-  f->Args.push_back(args[0]);
+  auto fb = cm::make_unique<cmForEachFunctionBlocker>(&makefile);
+  fb->Args.push_back(args[0]);
 
   enum Doing
   {
@@ -195,26 +192,26 @@
   Doing doing = DoingNone;
   for (unsigned int i = 2; i < args.size(); ++i) {
     if (doing == DoingItems) {
-      f->Args.push_back(args[i]);
+      fb->Args.push_back(args[i]);
     } else if (args[i] == "LISTS") {
       doing = DoingLists;
     } else if (args[i] == "ITEMS") {
       doing = DoingItems;
     } else if (doing == DoingLists) {
-      const char* value = this->Makefile->GetDefinition(args[i]);
+      const char* value = makefile.GetDefinition(args[i]);
       if (value && *value) {
-        cmSystemTools::ExpandListArgument(value, f->Args, true);
+        cmExpandList(value, fb->Args, true);
       }
     } else {
-      std::ostringstream e;
-      e << "Unknown argument:\n"
-        << "  " << args[i] << "\n";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      makefile.IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Unknown argument:\n", "  ", args[i], "\n"));
       return true;
     }
   }
 
-  this->Makefile->AddFunctionBlocker(f.release()); // TODO: pass unique_ptr
+  makefile.AddFunctionBlocker(std::move(fb));
 
   return true;
 }
+}
diff --git a/Source/cmForEachCommand.h b/Source/cmForEachCommand.h
index 5131a4f..1feb965 100644
--- a/Source/cmForEachCommand.h
+++ b/Source/cmForEachCommand.h
@@ -8,48 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmFunctionBlocker.h"
-#include "cmListFileCache.h"
-
 class cmExecutionStatus;
-class cmMakefile;
-
-class cmForEachFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  cmForEachFunctionBlocker(cmMakefile* mf);
-  ~cmForEachFunctionBlocker() override;
-  bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
-                         cmExecutionStatus&) override;
-  bool ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) override;
-
-  std::vector<std::string> Args;
-  std::vector<cmListFileFunction> Functions;
-
-private:
-  cmMakefile* Makefile;
-  int Depth;
-};
 
 /// Starts foreach() ... endforeach() block
-class cmForEachCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmForEachCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool HandleInMode(std::vector<std::string> const& args);
-};
-
+bool cmForEachCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 #endif
diff --git a/Source/cmFortranParser.h b/Source/cmFortranParser.h
index 6a33be5..6f97b42 100644
--- a/Source/cmFortranParser.h
+++ b/Source/cmFortranParser.h
@@ -12,10 +12,10 @@
 #  include <vector>
 #endif
 
-#include <stddef.h> /* size_t */
+#include <cstddef> /* size_t */
 
 /* Forward declare parser object type.  */
-typedef struct cmFortranParser_s cmFortranParser;
+using cmFortranParser = struct cmFortranParser_s;
 
 /* Functions to enter/exit #include'd files in order.  */
 bool cmFortranParser_FilePush(cmFortranParser* parser, const char* fname);
diff --git a/Source/cmFortranParserImpl.cxx b/Source/cmFortranParserImpl.cxx
index e8b1da8..054a2a9 100644
--- a/Source/cmFortranParserImpl.cxx
+++ b/Source/cmFortranParserImpl.cxx
@@ -1,16 +1,17 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmFortranParser.h"
-#include "cmSystemTools.h"
-
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
 #include <set>
 #include <stack>
-#include <stdio.h>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmFortranParser.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 bool cmFortranParser_s::FindIncludeFile(const char* dir,
                                         const char* includeName,
                                         std::string& fileName)
@@ -22,9 +23,7 @@
   }
   // Check for the file in the directory containing the including
   // file.
-  std::string fullName = dir;
-  fullName += "/";
-  fullName += includeName;
+  std::string fullName = cmStrCat(dir, '/', includeName);
   if (cmSystemTools::FileExists(fullName, true)) {
     fileName = fullName;
     return true;
@@ -32,9 +31,7 @@
 
   // Search the include path for the file.
   for (std::string const& i : this->IncludePath) {
-    fullName = i;
-    fullName += "/";
-    fullName += includeName;
+    fullName = cmStrCat(i, '/', includeName);
     if (cmSystemTools::FileExists(fullName, true)) {
       fileName = fullName;
       return true;
diff --git a/Source/cmFunctionBlocker.cxx b/Source/cmFunctionBlocker.cxx
new file mode 100644
index 0000000..5778a71
--- /dev/null
+++ b/Source/cmFunctionBlocker.cxx
@@ -0,0 +1,46 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFunctionBlocker.h"
+
+#include <cassert>
+#include <sstream>
+#include <utility>
+
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+
+bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
+                                          cmExecutionStatus& status)
+{
+  if (lff.Name.Lower == this->StartCommandName()) {
+    this->ScopeDepth++;
+  } else if (lff.Name.Lower == this->EndCommandName()) {
+    this->ScopeDepth--;
+    if (this->ScopeDepth == 0U) {
+      cmMakefile& mf = status.GetMakefile();
+      auto self = mf.RemoveFunctionBlocker();
+      assert(self.get() == this);
+
+      if (!this->ArgumentsMatch(lff, mf)) {
+        cmListFileContext const& lfc = this->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 */
+        mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+      }
+
+      return this->Replay(std::move(this->Functions), status);
+    }
+  }
+
+  this->Functions.push_back(lff);
+  return true;
+}
diff --git a/Source/cmFunctionBlocker.h b/Source/cmFunctionBlocker.h
index cd6b05d..59bb892 100644
--- a/Source/cmFunctionBlocker.h
+++ b/Source/cmFunctionBlocker.h
@@ -3,6 +3,12 @@
 #ifndef cmFunctionBlocker_h
 #define cmFunctionBlocker_h
 
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <vector>
+
+#include <cm/string_view>
+
 #include "cmListFileCache.h"
 
 class cmExecutionStatus;
@@ -14,17 +20,8 @@
   /**
    * should a function be blocked
    */
-  virtual bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
-                                 cmExecutionStatus& status) = 0;
-
-  /**
-   * should this function blocker be removed, useful when one function adds a
-   * blocker and another must remove it
-   */
-  virtual bool ShouldRemove(const cmListFileFunction&, cmMakefile&)
-  {
-    return false;
-  }
+  bool IsFunctionBlocked(cmListFileFunction const& lff,
+                         cmExecutionStatus& status);
 
   virtual ~cmFunctionBlocker() = default;
 
@@ -39,7 +36,19 @@
   }
 
 private:
+  virtual cm::string_view StartCommandName() const = 0;
+  virtual cm::string_view EndCommandName() const = 0;
+
+  virtual bool ArgumentsMatch(cmListFileFunction const& lff,
+                              cmMakefile& mf) const = 0;
+
+  virtual bool Replay(std::vector<cmListFileFunction> functions,
+                      cmExecutionStatus& status) = 0;
+
+private:
   cmListFileContext StartingContext;
+  std::vector<cmListFileFunction> Functions;
+  unsigned int ScopeDepth = 1;
 };
 
 #endif
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 9067a5f..b3ddfe0 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -3,106 +3,96 @@
 #include "cmFunctionCommand.h"
 
 #include <sstream>
+#include <utility>
+
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
 
 #include "cmAlgorithms.h"
 #include "cmExecutionStatus.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 
+namespace {
 // define the class for function commands
-class cmFunctionHelperCommand : public cmCommand
+class cmFunctionHelperCommand
 {
 public:
   /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmFunctionHelperCommand* newC = new cmFunctionHelperCommand;
-    // we must copy when we clone
-    newC->Args = this->Args;
-    newC->Functions = this->Functions;
-    newC->Policies = this->Policies;
-    newC->FilePath = this->FilePath;
-    return newC;
-  }
-
-  /**
    * This is called when the command is first encountered in
    * the CMakeLists.txt file.
    */
-  bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
-                         cmExecutionStatus&) override;
-
-  bool InitialPass(std::vector<std::string> const&,
-                   cmExecutionStatus&) override
-  {
-    return false;
-  }
+  bool operator()(std::vector<cmListFileArgument> const& args,
+                  cmExecutionStatus& inStatus) const;
 
   std::vector<std::string> Args;
   std::vector<cmListFileFunction> Functions;
   cmPolicies::PolicyMap Policies;
   std::string FilePath;
 };
+}
 
-bool cmFunctionHelperCommand::InvokeInitialPass(
-  const std::vector<cmListFileArgument>& args, cmExecutionStatus& inStatus)
+bool cmFunctionHelperCommand::operator()(
+  std::vector<cmListFileArgument> const& args,
+  cmExecutionStatus& inStatus) const
 {
+  cmMakefile& makefile = inStatus.GetMakefile();
+
   // Expand the argument list to the function.
   std::vector<std::string> expandedArgs;
-  this->Makefile->ExpandArguments(args, expandedArgs);
+  makefile.ExpandArguments(args, expandedArgs);
 
   // make sure the number of arguments passed is at least the number
   // required by the signature
   if (expandedArgs.size() < this->Args.size() - 1) {
-    std::string errorMsg =
-      "Function invoked with incorrect arguments for function named: ";
-    errorMsg += this->Args[0];
-    this->SetError(errorMsg);
+    std::string errorMsg = cmStrCat(
+      "Function invoked with incorrect arguments for function named: ",
+      this->Args[0]);
+    inStatus.SetError(errorMsg);
     return false;
   }
 
-  cmMakefile::FunctionPushPop functionScope(this->Makefile, this->FilePath,
+  cmMakefile::FunctionPushPop functionScope(&makefile, this->FilePath,
                                             this->Policies);
 
   // set the value of argc
-  std::ostringstream strStream;
-  strStream << expandedArgs.size();
-  this->Makefile->AddDefinition("ARGC", strStream.str().c_str());
-  this->Makefile->MarkVariableAsUsed("ARGC");
+  makefile.AddDefinition("ARGC", std::to_string(expandedArgs.size()));
+  makefile.MarkVariableAsUsed("ARGC");
 
   // set the values for ARGV0 ARGV1 ...
   for (unsigned int t = 0; t < expandedArgs.size(); ++t) {
     std::ostringstream tmpStream;
     tmpStream << "ARGV" << t;
-    this->Makefile->AddDefinition(tmpStream.str(), expandedArgs[t].c_str());
-    this->Makefile->MarkVariableAsUsed(tmpStream.str());
+    makefile.AddDefinition(tmpStream.str(), expandedArgs[t]);
+    makefile.MarkVariableAsUsed(tmpStream.str());
   }
 
   // define the formal arguments
   for (unsigned int j = 1; j < this->Args.size(); ++j) {
-    this->Makefile->AddDefinition(this->Args[j], expandedArgs[j - 1].c_str());
+    makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
   }
 
   // define ARGV and ARGN
   std::string argvDef = cmJoin(expandedArgs, ";");
-  std::vector<std::string>::const_iterator eit =
-    expandedArgs.begin() + (this->Args.size() - 1);
+  auto eit = expandedArgs.begin() + (this->Args.size() - 1);
   std::string argnDef = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
-  this->Makefile->AddDefinition("ARGV", argvDef.c_str());
-  this->Makefile->MarkVariableAsUsed("ARGV");
-  this->Makefile->AddDefinition("ARGN", argnDef.c_str());
-  this->Makefile->MarkVariableAsUsed("ARGN");
+  makefile.AddDefinition("ARGV", argvDef);
+  makefile.MarkVariableAsUsed("ARGV");
+  makefile.AddDefinition("ARGN", argnDef);
+  makefile.MarkVariableAsUsed("ARGN");
 
   // Invoke all the functions that were collected in the block.
   // for each function
   for (cmListFileFunction const& func : this->Functions) {
-    cmExecutionStatus status;
-    if (!this->Makefile->ExecuteCommand(func, status) ||
-        status.GetNestedError()) {
+    cmExecutionStatus status(makefile);
+    if (!makefile.ExecuteCommand(func, status) || status.GetNestedError()) {
       // The error message should have already included the call stack
       // so we do not need to report an error here.
       functionScope.Quiet();
@@ -118,66 +108,57 @@
   return true;
 }
 
-bool cmFunctionFunctionBlocker::IsFunctionBlocked(
-  const cmListFileFunction& lff, cmMakefile& mf, cmExecutionStatus&)
+class cmFunctionFunctionBlocker : public cmFunctionBlocker
 {
-  // record commands until we hit the ENDFUNCTION
-  // at the ENDFUNCTION call we shift gears and start looking for invocations
-  if (lff.Name.Lower == "function") {
-    this->Depth++;
-  } else if (lff.Name.Lower == "endfunction") {
-    // if this is the endfunction for this function then execute
-    if (!this->Depth) {
-      // create a new command and add it to cmake
-      cmFunctionHelperCommand* f = new cmFunctionHelperCommand();
-      f->Args = this->Args;
-      f->Functions = this->Functions;
-      f->FilePath = this->GetStartingContext().FilePath;
-      mf.RecordPolicies(f->Policies);
-      mf.GetState()->AddScriptedCommand(this->Args[0], f);
-      // remove the function blocker now that the function is defined
-      mf.RemoveFunctionBlocker(this, lff);
-      return true;
-    }
-    // decrement for each nested function that ends
-    this->Depth--;
-  }
+public:
+  cm::string_view StartCommandName() const override { return "function"_s; }
+  cm::string_view EndCommandName() const override { return "endfunction"_s; }
 
-  // if it wasn't an endfunction and we are not executing then we must be
-  // recording
-  this->Functions.push_back(lff);
+  bool ArgumentsMatch(cmListFileFunction const&,
+                      cmMakefile& mf) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& status) override;
+
+  std::vector<std::string> Args;
+};
+
+bool cmFunctionFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                               cmMakefile& mf) const
+{
+  std::vector<std::string> expandedArguments;
+  mf.ExpandArguments(lff.Arguments, expandedArguments,
+                     this->GetStartingContext().FilePath.c_str());
+  return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
+}
+
+bool cmFunctionFunctionBlocker::Replay(
+  std::vector<cmListFileFunction> functions, cmExecutionStatus& status)
+{
+  cmMakefile& mf = status.GetMakefile();
+  // create a new command and add it to cmake
+  cmFunctionHelperCommand f;
+  f.Args = this->Args;
+  f.Functions = std::move(functions);
+  f.FilePath = this->GetStartingContext().FilePath;
+  mf.RecordPolicies(f.Policies);
+  mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f));
   return true;
 }
 
-bool cmFunctionFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
-                                             cmMakefile& mf)
-{
-  if (lff.Name.Lower == "endfunction") {
-    std::vector<std::string> expandedArguments;
-    mf.ExpandArguments(lff.Arguments, expandedArguments,
-                       this->GetStartingContext().FilePath.c_str());
-    // if the endfunction has arguments then make sure
-    // they match the ones in the opening function command
-    if ((expandedArguments.empty() ||
-         (expandedArguments[0] == this->Args[0]))) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool cmFunctionCommand::InitialPass(std::vector<std::string> const& args,
-                                    cmExecutionStatus&)
+bool cmFunctionCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // create a function blocker
-  cmFunctionFunctionBlocker* f = new cmFunctionFunctionBlocker();
-  cmAppend(f->Args, args);
-  this->Makefile->AddFunctionBlocker(f);
+  {
+    auto fb = cm::make_unique<cmFunctionFunctionBlocker>();
+    cmAppend(fb->Args, args);
+    status.GetMakefile().AddFunctionBlocker(std::move(fb));
+  }
   return true;
 }
diff --git a/Source/cmFunctionCommand.h b/Source/cmFunctionCommand.h
index 8b37df0..d6b549c 100644
--- a/Source/cmFunctionCommand.h
+++ b/Source/cmFunctionCommand.h
@@ -8,40 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmFunctionBlocker.h"
-#include "cmListFileCache.h"
-
 class cmExecutionStatus;
-class cmMakefile;
-
-class cmFunctionFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  bool IsFunctionBlocked(const cmListFileFunction&, cmMakefile& mf,
-                         cmExecutionStatus&) override;
-  bool ShouldRemove(const cmListFileFunction&, cmMakefile& mf) override;
-
-  std::vector<std::string> Args;
-  std::vector<cmListFileFunction> Functions;
-  int Depth = 0;
-};
 
 /// Starts function() ... endfunction() block
-class cmFunctionCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmFunctionCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmFunctionCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx
index 2f47788..2af04b6 100644
--- a/Source/cmGeneratedFileStream.cxx
+++ b/Source/cmGeneratedFileStream.cxx
@@ -2,18 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratedFileStream.h"
 
-#include <stdio.h>
+#include <cstdio>
 
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cm_codecvt.hxx"
 #  include "cm_zlib.h"
 #endif
 
 cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   if (encoding != codecvt::None) {
     imbue(std::locale(getloc(), new codecvt(encoding)));
   }
@@ -32,7 +33,7 @@
     cmSystemTools::Error("Cannot open file for write: " + this->TempName);
     cmSystemTools::ReportLastSystemError("");
   }
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   if (encoding != codecvt::None) {
     imbue(std::locale(getloc(), new codecvt(encoding)));
   }
@@ -149,7 +150,7 @@
     // The destination is to be replaced.  Rename the temporary to the
     // destination atomically.
     if (this->Compress) {
-      std::string gzname = this->TempName + ".temp.gz";
+      std::string gzname = cmStrCat(this->TempName, ".temp.gz");
       if (this->CompressFile(this->TempName, gzname)) {
         this->RenameFile(gzname, resname);
       }
@@ -169,7 +170,7 @@
   return replaced;
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 int cmGeneratedFileStreamBase::CompressFile(std::string const& oldname,
                                             std::string const& newname)
 {
diff --git a/Source/cmGeneratedFileStream.h b/Source/cmGeneratedFileStream.h
index 5b1eb51..a9088ac 100644
--- a/Source/cmGeneratedFileStream.h
+++ b/Source/cmGeneratedFileStream.h
@@ -5,10 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_codecvt.hxx"
-#include "cmsys/FStream.hxx"
 #include <string>
 
+#include "cmsys/FStream.hxx"
+
+#include "cm_codecvt.hxx"
+
 // This is the first base class of cmGeneratedFileStream.  It will be
 // created before and destroyed after the ofstream portion and can
 // therefore be used to manage the temporary file.
@@ -72,8 +74,8 @@
   , public cmsys::ofstream
 {
 public:
-  typedef cmsys::ofstream Stream;
-  typedef codecvt::Encoding Encoding;
+  using Stream = cmsys::ofstream;
+  using Encoding = codecvt::Encoding;
 
   /**
    * This constructor prepares a default stream.  The open method must
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 175a26d..b7f7d1d 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -2,17 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpression.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <memory> // IWYU pragma: keep
+#include <cassert>
+#include <memory>
 #include <utility>
 
-#include "assert.h"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorExpressionEvaluator.h"
 #include "cmGeneratorExpressionLexer.h"
 #include "cmGeneratorExpressionParser.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmGeneratorExpression::cmGeneratorExpression(cmListFileBacktrace backtrace)
@@ -20,40 +22,56 @@
 {
 }
 
+cmGeneratorExpression::~cmGeneratorExpression() = default;
+
 std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
-  std::string const& input)
+  std::string input) const
 {
   return std::unique_ptr<cmCompiledGeneratorExpression>(
-    new cmCompiledGeneratorExpression(this->Backtrace, input));
+    new cmCompiledGeneratorExpression(this->Backtrace, std::move(input)));
 }
 
 std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
-  const char* input)
+  const char* input) const
 {
   return this->Parse(std::string(input ? input : ""));
 }
 
-cmGeneratorExpression::~cmGeneratorExpression() = default;
-
-const std::string& cmCompiledGeneratorExpression::Evaluate(
-  cmLocalGenerator* lg, const std::string& config, bool quiet,
-  const cmGeneratorTarget* headTarget,
+std::string cmGeneratorExpression::Evaluate(
+  std::string input, cmLocalGenerator* lg, const std::string& config,
+  cmGeneratorTarget const* headTarget,
   cmGeneratorExpressionDAGChecker* dagChecker,
-  std::string const& language) const
+  cmGeneratorTarget const* currentTarget, std::string const& language)
 {
-  return this->Evaluate(lg, config, quiet, headTarget, headTarget, dagChecker,
+  if (Find(input) != std::string::npos) {
+    cmCompiledGeneratorExpression cge(cmListFileBacktrace(), std::move(input));
+    return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
                         language);
+  }
+  return input;
+}
+
+std::string cmGeneratorExpression::Evaluate(
+  const char* input, cmLocalGenerator* lg, const std::string& config,
+  cmGeneratorTarget const* headTarget,
+  cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget const* currentTarget, std::string const& language)
+{
+  return input ? Evaluate(std::string(input), lg, config, headTarget,
+                          dagChecker, currentTarget, language)
+               : "";
 }
 
 const std::string& cmCompiledGeneratorExpression::Evaluate(
-  cmLocalGenerator* lg, const std::string& config, bool quiet,
-  const cmGeneratorTarget* headTarget, const cmGeneratorTarget* currentTarget,
+  cmLocalGenerator* lg, const std::string& config,
+  const cmGeneratorTarget* headTarget,
   cmGeneratorExpressionDAGChecker* dagChecker,
-  std::string const& language) const
+  const cmGeneratorTarget* currentTarget, std::string const& language) const
 {
   cmGeneratorExpressionContext context(
-    lg, config, quiet, headTarget, currentTarget ? currentTarget : headTarget,
-    this->EvaluateForBuildsystem, this->Backtrace, language);
+    lg, config, this->Quiet, headTarget,
+    currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
+    this->Backtrace, language);
 
   return this->EvaluateWithContext(context, dagChecker);
 }
@@ -96,9 +114,10 @@
   cmListFileBacktrace backtrace, std::string input)
   : Backtrace(std::move(backtrace))
   , Input(std::move(input))
+  , EvaluateForBuildsystem(false)
+  , Quiet(false)
   , HadContextSensitiveCondition(false)
   , HadHeadSensitiveCondition(false)
-  , EvaluateForBuildsystem(false)
 {
   cmGeneratorExpressionLexer l;
   std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
@@ -294,7 +313,7 @@
         preGenex = input.substr(startPos + 1, pos - startPos - 1);
       }
       if (!part.empty()) {
-        cmSystemTools::ExpandListArgument(part, output);
+        cmExpandList(part, output);
       }
     }
     pos += 2;
@@ -327,7 +346,7 @@
     lastPos = pos;
   }
   if (lastPos < input.size()) {
-    cmSystemTools::ExpandListArgument(input.substr(lastPos), output);
+    cmExpandList(input.substr(lastPos), output);
   }
 }
 
@@ -369,20 +388,17 @@
 void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
   const cmGeneratorTarget* tgt, std::map<std::string, std::string>& mapping)
 {
-  typedef std::map<cmGeneratorTarget const*,
-                   std::map<std::string, std::string>>
-    MapType;
-  MapType::const_iterator it = this->MaxLanguageStandard.find(tgt);
+  auto it = this->MaxLanguageStandard.find(tgt);
   if (it != this->MaxLanguageStandard.end()) {
     mapping = it->second;
   }
 }
 
 const std::string& cmGeneratorExpressionInterpreter::Evaluate(
-  const char* expression, const std::string& property)
+  std::string expression, const std::string& property)
 {
   this->CompiledGeneratorExpression =
-    this->GeneratorExpression.Parse(expression);
+    this->GeneratorExpression.Parse(std::move(expression));
 
   // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
   cmGeneratorExpressionDAGChecker dagChecker(
@@ -391,6 +407,12 @@
     nullptr);
 
   return this->CompiledGeneratorExpression->Evaluate(
-    this->LocalGenerator, this->Config, false, this->HeadTarget, &dagChecker,
+    this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
     this->Language);
 }
+
+const std::string& cmGeneratorExpressionInterpreter::Evaluate(
+  const char* expression, const std::string& property)
+{
+  return this->Evaluate(std::string(expression ? expression : ""), property);
+}
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index fd36c4b..4bd1c9f 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -5,15 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmListFileCache.h"
-
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmListFileCache.h"
+
 class cmCompiledGeneratorExpression;
 class cmGeneratorTarget;
 class cmLocalGenerator;
@@ -41,8 +41,22 @@
   cmGeneratorExpression& operator=(cmGeneratorExpression const&) = delete;
 
   std::unique_ptr<cmCompiledGeneratorExpression> Parse(
-    std::string const& input);
-  std::unique_ptr<cmCompiledGeneratorExpression> Parse(const char* input);
+    std::string input) const;
+  std::unique_ptr<cmCompiledGeneratorExpression> Parse(
+    const char* input) const;
+
+  static std::string Evaluate(
+    std::string input, cmLocalGenerator* lg, const std::string& config,
+    cmGeneratorTarget const* headTarget = nullptr,
+    cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
+    cmGeneratorTarget const* currentTarget = nullptr,
+    std::string const& language = std::string());
+  static std::string Evaluate(
+    const char* input, cmLocalGenerator* lg, const std::string& config,
+    cmGeneratorTarget const* headTarget = nullptr,
+    cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
+    cmGeneratorTarget const* currentTarget = nullptr,
+    std::string const& language = std::string());
 
   enum PreprocessContext
   {
@@ -87,15 +101,10 @@
     cmCompiledGeneratorExpression const&) = delete;
 
   const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet = false,
+    cmLocalGenerator* lg, const std::string& config,
     cmGeneratorTarget const* headTarget = nullptr,
-    cmGeneratorTarget const* currentTarget = nullptr,
     cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
-    std::string const& language = std::string()) const;
-  const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet,
-    cmGeneratorTarget const* headTarget,
-    cmGeneratorExpressionDAGChecker* dagChecker,
+    cmGeneratorTarget const* currentTarget = nullptr,
     std::string const& language = std::string()) const;
 
   /** Get set of targets found during evaluations.  */
@@ -135,6 +144,8 @@
     this->EvaluateForBuildsystem = eval;
   }
 
+  void SetQuiet(bool quiet) { this->Quiet = quiet; }
+
   void GetMaxLanguageStandard(cmGeneratorTarget const* tgt,
                               std::map<std::string, std::string>& mapping);
 
@@ -152,6 +163,8 @@
   std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
   const std::string Input;
   bool NeedsEvaluation;
+  bool EvaluateForBuildsystem;
+  bool Quiet;
 
   mutable std::set<cmGeneratorTarget*> DependTargets;
   mutable std::set<cmGeneratorTarget const*> AllTargetsSeen;
@@ -163,7 +176,6 @@
   mutable bool HadContextSensitiveCondition;
   mutable bool HadHeadSensitiveCondition;
   mutable std::set<cmGeneratorTarget const*> SourceSensitiveTargets;
-  bool EvaluateForBuildsystem;
 };
 
 class cmGeneratorExpressionInterpreter
@@ -172,11 +184,11 @@
   cmGeneratorExpressionInterpreter(cmLocalGenerator* localGenerator,
                                    std::string config,
                                    cmGeneratorTarget const* headTarget,
-                                   std::string lang = std::string())
+                                   std::string language = std::string())
     : LocalGenerator(localGenerator)
     , Config(std::move(config))
     , HeadTarget(headTarget)
-    , Language(std::move(lang))
+    , Language(std::move(language))
   {
   }
 
@@ -185,13 +197,10 @@
   cmGeneratorExpressionInterpreter& operator=(
     cmGeneratorExpressionInterpreter const&) = delete;
 
+  const std::string& Evaluate(std::string expression,
+                              const std::string& property);
   const std::string& Evaluate(const char* expression,
                               const std::string& property);
-  const std::string& Evaluate(const std::string& expression,
-                              const std::string& property)
-  {
-    return this->Evaluate(expression.c_str(), property);
-  }
 
 protected:
   cmGeneratorExpression GeneratorExpression;
diff --git a/Source/cmGeneratorExpressionContext.h b/Source/cmGeneratorExpressionContext.h
index 6e076bf..4709fa0 100644
--- a/Source/cmGeneratorExpressionContext.h
+++ b/Source/cmGeneratorExpressionContext.h
@@ -3,12 +3,12 @@
 #ifndef cmGeneratorExpressionContext_h
 #define cmGeneratorExpressionContext_h
 
-#include "cmListFileCache.h"
-
 #include <map>
 #include <set>
 #include <string>
 
+#include "cmListFileCache.h"
+
 class cmGeneratorTarget;
 class cmLocalGenerator;
 
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 728f2a4..643ba34 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -2,18 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionDAGChecker.h"
 
-#include "cmAlgorithms.h"
+#include <cstring>
+#include <sstream>
+#include <utility>
+
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionEvaluator.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmake.h"
 
-#include <sstream>
-#include <string.h>
-#include <utility>
-
 cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
   cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
   std::string property, const GeneratorExpressionContent* content,
@@ -59,8 +59,7 @@
         TEST_TRANSITIVE_PROPERTY_METHOD) false)) // NOLINT(*)
 #undef TEST_TRANSITIVE_PROPERTY_METHOD
   {
-    std::map<cmGeneratorTarget const*, std::set<std::string>>::const_iterator
-      it = top->Seen.find(this->Target);
+    auto it = top->Seen.find(this->Target);
     if (it != top->Seen.end()) {
       const std::set<std::string>& propSet = it->second;
       if (propSet.find(this->Property) != propSet.end()) {
@@ -68,9 +67,7 @@
         return;
       }
     }
-    const_cast<cmGeneratorExpressionDAGChecker*>(top)
-      ->Seen[this->Target]
-      .insert(this->Property);
+    top->Seen[this->Target].insert(this->Property);
   }
 }
 
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index e1fba5e..f2c49bb 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmListFileCache.h"
-
 #include <map>
 #include <set>
 #include <string>
 
+#include "cmListFileCache.h"
+
 struct GeneratorExpressionContent;
 struct cmGeneratorExpressionContext;
 class cmGeneratorTarget;
@@ -29,7 +29,8 @@
   SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
   SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
   SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES)                      \
-  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
+  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)                              \
+  SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
@@ -88,7 +89,7 @@
   const cmGeneratorExpressionDAGChecker* const Parent;
   cmGeneratorTarget const* Target;
   const std::string Property;
-  std::map<cmGeneratorTarget const*, std::set<std::string>> Seen;
+  mutable std::map<cmGeneratorTarget const*, std::set<std::string>> Seen;
   const GeneratorExpressionContent* const Content;
   const cmListFileBacktrace Backtrace;
   Result CheckResult;
diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx
index 326cb0e..9e8707d 100644
--- a/Source/cmGeneratorExpressionEvaluationFile.cxx
+++ b/Source/cmGeneratorExpressionEvaluationFile.cxx
@@ -2,11 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionEvaluationFile.h"
 
-#include "cmsys/FStream.hxx"
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <sstream>
 #include <utility>
 
+#include "cmsys/FStream.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
@@ -37,8 +38,8 @@
 {
   std::string rawCondition = this->Condition->GetInput();
   if (!rawCondition.empty()) {
-    std::string condResult = this->Condition->Evaluate(
-      lg, config, false, nullptr, nullptr, nullptr, lang);
+    std::string condResult =
+      this->Condition->Evaluate(lg, config, nullptr, nullptr, nullptr, lang);
     if (condResult == "0") {
       return;
     }
@@ -54,9 +55,9 @@
   }
 
   std::string outputFileName = this->OutputFileExpr->Evaluate(
-    lg, config, false, nullptr, nullptr, nullptr, lang);
-  const std::string& outputContent = inputExpression->Evaluate(
-    lg, config, false, nullptr, nullptr, nullptr, lang);
+    lg, config, nullptr, nullptr, nullptr, lang);
+  const std::string& outputContent =
+    inputExpression->Evaluate(lg, config, nullptr, nullptr, nullptr, lang);
 
   if (cmSystemTools::FileIsFullPath(outputFileName)) {
     outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
@@ -64,8 +65,7 @@
     outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg);
   }
 
-  std::map<std::string, std::string>::iterator it =
-    outputFiles.find(outputFileName);
+  auto it = outputFiles.find(outputFileName);
 
   if (it != outputFiles.end()) {
     if (it->second == outputContent) {
@@ -101,8 +101,8 @@
   gg->GetEnabledLanguages(enabledLanguages);
 
   for (std::string const& le : enabledLanguages) {
-    std::string name = this->OutputFileExpr->Evaluate(
-      lg, config, false, nullptr, nullptr, nullptr, le);
+    std::string name = this->OutputFileExpr->Evaluate(lg, config, nullptr,
+                                                      nullptr, nullptr, le);
     cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource(
       name, false, cmSourceFileLocationKind::Known);
     // Tell TraceDependencies that the file is not expected to exist
diff --git a/Source/cmGeneratorExpressionEvaluationFile.h b/Source/cmGeneratorExpressionEvaluationFile.h
index 89a2390..c3bc4c8 100644
--- a/Source/cmGeneratorExpressionEvaluationFile.h
+++ b/Source/cmGeneratorExpressionEvaluationFile.h
@@ -6,13 +6,14 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <vector>
 
+#include "cm_sys_stat.h"
+
 #include "cmGeneratorExpression.h"
 #include "cmPolicies.h"
-#include "cm_sys_stat.h"
 
 class cmLocalGenerator;
 
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index 7442018..e0ae170 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionEvaluator.h"
 
+#include <algorithm>
+#include <sstream>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionNode.h"
 
-#include <algorithm>
-#include <sstream>
-
 GeneratorExpressionContent::GeneratorExpressionContent(
   const char* startContent, size_t length)
   : StartContent(startContent)
@@ -30,9 +30,7 @@
 {
   std::string result;
 
-  const std::vector<
-    std::vector<cmGeneratorExpressionEvaluator*>>::const_iterator pend =
-    this->ParamChildren.end();
+  const auto pend = this->ParamChildren.end();
   for (; pit != pend; ++pit) {
     for (cmGeneratorExpressionEvaluator* pExprEval : *pit) {
       if (node->RequiresLiteralInput()) {
@@ -116,11 +114,8 @@
 {
   const int numExpected = node->NumExpectedParameters();
   {
-    std::vector<std::vector<cmGeneratorExpressionEvaluator*>>::const_iterator
-      pit = this->ParamChildren.begin();
-    const std::vector<
-      std::vector<cmGeneratorExpressionEvaluator*>>::const_iterator pend =
-      this->ParamChildren.end();
+    auto pit = this->ParamChildren.begin();
+    const auto pend = this->ParamChildren.end();
     const bool acceptsArbitraryContent =
       node->AcceptsArbitraryContentParameter();
     int counter = 1;
diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h
index 4530152..b10bb5b 100644
--- a/Source/cmGeneratorExpressionEvaluator.h
+++ b/Source/cmGeneratorExpressionEvaluator.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/Source/cmGeneratorExpressionLexer.h b/Source/cmGeneratorExpressionLexer.h
index bf24308..9e01948 100644
--- a/Source/cmGeneratorExpressionLexer.h
+++ b/Source/cmGeneratorExpressionLexer.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 #include <vector>
 
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index a60c75c..66f1c71 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -2,6 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionNode.h"
 
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include <cm/iterator>
+
+#include "cmsys/RegularExpression.hxx"
+#include "cmsys/String.h"
+
+#include "cm_static_string_view.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionContext.h"
@@ -19,39 +37,25 @@
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmString.hxx"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
-#include "cm_static_string_view.hxx"
-#include "cm_string_view.hxx"
 #include "cmake.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include "cmsys/String.h"
-
-#include <algorithm>
-#include <assert.h>
-#include <errno.h>
-#include <iterator>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <set>
-#include <sstream>
-#include <stdlib.h>
-#include <string.h>
-#include <utility>
-
 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
   std::string const& prop, cmLocalGenerator* lg,
   cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
-  cmGeneratorTarget const* currentTarget,
-  cmGeneratorExpressionDAGChecker* dagChecker)
+  cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget const* currentTarget)
 {
   cmGeneratorExpression ge(context->Backtrace);
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
   cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
+  cge->SetQuiet(context->Quiet);
   std::string result =
-    cge->Evaluate(lg, context->Config, context->Quiet, headTarget,
-                  currentTarget, dagChecker, context->Language);
+    cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget,
+                  context->Language);
   if (cge->GetHadContextSensitiveCondition()) {
     context->HadContextSensitiveCondition = true;
   }
@@ -168,7 +172,7 @@
     const GeneratorExpressionContent* /*content*/,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    return !cmSystemTools::IsOff(parameters.front()) ? "1" : "0";
+    return !cmIsOff(parameters.front()) ? "1" : "0";
   }
 } boolNode;
 
@@ -274,17 +278,18 @@
     const GeneratorExpressionContent* /*content*/,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    std::vector<std::string> values, checkValues;
+    std::vector<std::string> values;
+    std::vector<std::string> checkValues;
     bool check = false;
     switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
       case cmPolicies::WARN:
         if (parameters.front().empty()) {
           check = true;
-          cmSystemTools::ExpandListArgument(parameters[1], checkValues, true);
+          cmExpandList(parameters[1], checkValues, true);
         }
         CM_FALLTHROUGH;
       case cmPolicies::OLD:
-        cmSystemTools::ExpandListArgument(parameters[1], values);
+        cmExpandList(parameters[1], values);
         if (check && values != checkValues) {
           std::ostringstream e;
           e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
@@ -301,14 +306,11 @@
       case cmPolicies::REQUIRED_IF_USED:
       case cmPolicies::REQUIRED_ALWAYS:
       case cmPolicies::NEW:
-        cmSystemTools::ExpandListArgument(parameters[1], values, true);
+        cmExpandList(parameters[1], values, true);
         break;
     }
 
-    return std::find(values.cbegin(), values.cend(), parameters.front()) ==
-        values.cend()
-      ? "0"
-      : "1";
+    return cmContains(values, parameters.front()) ? "1" : "0";
   }
 } inListNode;
 
@@ -346,8 +348,9 @@
       return {};
     }
 
-    std::vector<std::string> values, result;
-    cmSystemTools::ExpandListArgument(parameters.front(), values, true);
+    std::vector<std::string> values;
+    std::vector<std::string> result;
+    cmExpandList(parameters.front(), values, true);
 
     std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
                  [&re, exclude](std::string const& input) {
@@ -375,8 +378,7 @@
         "$<REMOVE_DUPLICATES:...> expression requires one parameter");
     }
 
-    std::vector<std::string> values;
-    cmSystemTools::ExpandListArgument(parameters.front(), values, true);
+    std::vector<std::string> values = cmExpandedList(parameters.front(), true);
 
     auto valuesEnd = cmRemoveDuplicates(values);
     auto valuesBegin = values.cbegin();
@@ -477,13 +479,13 @@
       }
 
       return this->EvaluateDependentExpression(
-        expression, context->LG, context, context->HeadTarget,
-        context->CurrentTarget, &dagChecker);
+        expression, context->LG, context, context->HeadTarget, &dagChecker,
+        context->CurrentTarget);
     }
 
     return this->EvaluateDependentExpression(
-      expression, context->LG, context, context->HeadTarget,
-      context->CurrentTarget, dagCheckerParent);
+      expression, context->LG, context, context->HeadTarget, dagCheckerParent,
+      context->CurrentTarget);
   }
 };
 
@@ -684,10 +686,10 @@
       if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) {
         switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
           case cmPolicies::WARN: {
-            std::ostringstream e;
-            e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0044);
             context->LG->GetCMakeInstance()->IssueMessage(
-              MessageType::AUTHOR_WARNING, e.str(), context->Backtrace);
+              MessageType::AUTHOR_WARNING,
+              cmPolicies::GetPolicyWarning(cmPolicies::CMP0044),
+              context->Backtrace);
             CM_FALLTHROUGH;
           }
           case cmPolicies::OLD:
@@ -706,7 +708,8 @@
 };
 
 static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"),
-  cudaCompilerIdNode("CUDA"), fortranCompilerIdNode("Fortran");
+  cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"),
+  objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran");
 
 struct CompilerVersionNode : public cmGeneratorExpressionNode
 {
@@ -770,6 +773,7 @@
 
 static const CompilerVersionNode cCompilerVersionNode("C"),
   cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"),
+  objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"),
   fortranCompilerVersionNode("Fortran");
 
 struct PlatformIdNode : public cmGeneratorExpressionNode
@@ -909,15 +913,13 @@
         // for this (possibly mapped) config.
         // Check if there is a proper config mapping for the tested config.
         std::vector<std::string> mappedConfigs;
-        std::string mapProp = "MAP_IMPORTED_CONFIG_";
-        mapProp += cmSystemTools::UpperCase(context->Config);
+        std::string mapProp = cmStrCat(
+          "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
         if (const char* mapValue =
               context->CurrentTarget->GetProperty(mapProp)) {
-          cmSystemTools::ExpandListArgument(cmSystemTools::UpperCase(mapValue),
-                                            mappedConfigs);
-          return std::find(mappedConfigs.begin(), mappedConfigs.end(),
-                           cmSystemTools::UpperCase(parameters.front())) !=
-              mappedConfigs.end()
+          cmExpandList(cmSystemTools::UpperCase(mapValue), mappedConfigs);
+          return cmContains(mappedConfigs,
+                            cmSystemTools::UpperCase(parameters.front()))
             ? "1"
             : "0";
         }
@@ -941,8 +943,7 @@
     const GeneratorExpressionContent* /*content*/,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(parameters.front(), list);
+    std::vector<std::string> list = cmExpandedList(parameters.front());
     return cmJoin(list, parameters[1]);
   }
 } joinNode;
@@ -1038,45 +1039,38 @@
   }
 } languageAndIdNode;
 
-#define TRANSITIVE_PROPERTY_NAME(PROPERTY) , "INTERFACE_" #PROPERTY
-
-static const char* targetPropertyTransitiveWhitelist[] = {
-  nullptr CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_NAME)
-};
-
-#undef TRANSITIVE_PROPERTY_NAME
-
-template <typename T>
 std::string getLinkedTargetsContent(
-  std::vector<T> const& libraries, cmGeneratorTarget const* target,
-  cmGeneratorTarget const* headTarget, cmGeneratorExpressionContext* context,
-  cmGeneratorExpressionDAGChecker* dagChecker,
-  const std::string& interfacePropertyName)
+  cmGeneratorTarget const* target, std::string const& prop,
+  cmGeneratorExpressionContext* context,
+  cmGeneratorExpressionDAGChecker* dagChecker)
 {
-  std::string linkedTargetsContent;
-  std::string sep;
-  std::string depString;
-  for (T const& l : libraries) {
-    // Broken code can have a target in its own link interface.
-    // Don't follow such link interface entries so as not to create a
-    // self-referencing loop.
-    if (l.Target && l.Target != target) {
-      std::string uniqueName =
-        target->GetGlobalGenerator()->IndexGeneratorTargetUniquely(l.Target);
-      depString += sep + "$<TARGET_PROPERTY:" + std::move(uniqueName) + "," +
-        interfacePropertyName + ">";
-      sep = ";";
+  std::string result;
+  if (cmLinkImplementationLibraries const* impl =
+        target->GetLinkImplementationLibraries(context->Config)) {
+    for (cmLinkImplItem const& lib : impl->Libraries) {
+      if (lib.Target) {
+        // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
+        // caller's property and hand-evaluate it as if it were compiled.
+        // Create a context as cmCompiledGeneratorExpression::Evaluate does.
+        cmGeneratorExpressionContext libContext(
+          target->GetLocalGenerator(), context->Config, context->Quiet, target,
+          target, context->EvaluateForBuildsystem, lib.Backtrace,
+          context->Language);
+        std::string libResult =
+          lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker);
+        if (!libResult.empty()) {
+          if (result.empty()) {
+            result = std::move(libResult);
+          } else {
+            result.reserve(result.size() + 1 + libResult.size());
+            result += ";";
+            result += libResult;
+          }
+        }
+      }
     }
   }
-  if (!depString.empty()) {
-    linkedTargetsContent =
-      cmGeneratorExpressionNode::EvaluateDependentExpression(
-        depString, target->GetLocalGenerator(), context, headTarget, target,
-        dagChecker);
-  }
-  linkedTargetsContent =
-    cmGeneratorExpression::StripEmptyListElements(linkedTargetsContent);
-  return linkedTargetsContent;
+  return result;
 }
 
 static const struct TargetPropertyNode : public cmGeneratorExpressionNode
@@ -1116,7 +1110,8 @@
     static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
 
     cmGeneratorTarget const* target = nullptr;
-    std::string targetName, propertyName;
+    std::string targetName;
+    std::string propertyName;
 
     if (parameters.size() == 2) {
       targetName = parameters[0];
@@ -1205,67 +1200,6 @@
       return target->GetLinkerLanguage(context->Config);
     }
 
-    cmGeneratorExpressionDAGChecker dagChecker(
-      context->Backtrace, target, propertyName, content, dagCheckerParent);
-
-    switch (dagChecker.Check()) {
-      case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
-        dagChecker.ReportError(context, content->GetOriginalExpression());
-        return std::string();
-      case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
-        // No error. We just skip cyclic references.
-        return std::string();
-      case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
-        for (size_t i = 1; i < cm::size(targetPropertyTransitiveWhitelist);
-             ++i) {
-          if (targetPropertyTransitiveWhitelist[i] == propertyName) {
-            // No error. We're not going to find anything new here.
-            return std::string();
-          }
-        }
-      case cmGeneratorExpressionDAGChecker::DAG:
-        break;
-    }
-
-    std::string prop;
-    bool haveProp = false;
-    if (const char* p = target->GetProperty(propertyName)) {
-      prop = p;
-      haveProp = true;
-    }
-
-    if (dagCheckerParent) {
-      if (dagCheckerParent->EvaluatingGenexExpression() ||
-          dagCheckerParent->EvaluatingPICExpression()) {
-        // No check required.
-      } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
-#define TRANSITIVE_PROPERTY_COMPARE(PROPERTY)                                 \
-  (#PROPERTY == propertyName || "INTERFACE_" #PROPERTY == propertyName) ||
-        if (CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(
-              TRANSITIVE_PROPERTY_COMPARE) false) { // NOLINT(*)
-          reportError(
-            context, content->GetOriginalExpression(),
-            "$<TARGET_PROPERTY:...> expression in link libraries "
-            "evaluation depends on target property which is transitive "
-            "over the link libraries, creating a recursion.");
-          return std::string();
-        }
-#undef TRANSITIVE_PROPERTY_COMPARE
-
-        if (!haveProp) {
-          return std::string();
-        }
-      } else {
-#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
-
-        assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
-          ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
-#undef ASSERT_TRANSITIVE_PROPERTY_METHOD
-      }
-    }
-
-    std::string linkedTargetsContent;
-
     std::string interfacePropertyName;
     bool isInterfaceProperty = false;
 
@@ -1287,32 +1221,64 @@
       }
     }
 #undef POPULATE_INTERFACE_PROPERTY_NAME
-    cmGeneratorTarget const* headTarget =
-      context->HeadTarget && isInterfaceProperty ? context->HeadTarget
-                                                 : target;
 
-    if (isInterfaceProperty) {
-      if (cmLinkInterfaceLibraries const* iface =
-            target->GetLinkInterfaceLibraries(context->Config, headTarget,
-                                              true)) {
-        linkedTargetsContent =
-          getLinkedTargetsContent(iface->Libraries, target, headTarget,
-                                  context, &dagChecker, interfacePropertyName);
-      }
-    } else if (!interfacePropertyName.empty()) {
-      if (cmLinkImplementationLibraries const* impl =
-            target->GetLinkImplementationLibraries(context->Config)) {
-        linkedTargetsContent =
-          getLinkedTargetsContent(impl->Libraries, target, target, context,
-                                  &dagChecker, interfacePropertyName);
+    bool evaluatingLinkLibraries = false;
+
+    if (dagCheckerParent) {
+      if (dagCheckerParent->EvaluatingGenexExpression() ||
+          dagCheckerParent->EvaluatingPICExpression()) {
+        // No check required.
+      } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
+        evaluatingLinkLibraries = true;
+        if (!interfacePropertyName.empty()) {
+          reportError(
+            context, content->GetOriginalExpression(),
+            "$<TARGET_PROPERTY:...> expression in link libraries "
+            "evaluation depends on target property which is transitive "
+            "over the link libraries, creating a recursion.");
+          return std::string();
+        }
+      } else {
+#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
+        assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
+          ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
+#undef ASSERT_TRANSITIVE_PROPERTY_METHOD
       }
     }
 
-    if (!haveProp) {
-      if (target->IsImported() ||
-          target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
-        return linkedTargetsContent;
-      }
+    if (isInterfaceProperty) {
+      return target->EvaluateInterfaceProperty(propertyName, context,
+                                               dagCheckerParent);
+    }
+
+    cmGeneratorExpressionDAGChecker dagChecker(
+      context->Backtrace, target, propertyName, content, dagCheckerParent);
+
+    switch (dagChecker.Check()) {
+      case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
+        dagChecker.ReportError(context, content->GetOriginalExpression());
+        return std::string();
+      case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
+        // No error. We just skip cyclic references.
+        return std::string();
+      case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
+        // We handle transitive properties above.  For non-transitive
+        // properties we accept repeats anyway.
+      case cmGeneratorExpressionDAGChecker::DAG:
+        break;
+    }
+
+    std::string result;
+    bool haveProp = false;
+    if (const char* p = target->GetProperty(propertyName)) {
+      result = p;
+      haveProp = true;
+    } else if (evaluatingLinkLibraries) {
+      return std::string();
+    }
+
+    if (!haveProp && !target->IsImported() &&
+        target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
       if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
                                                        context->Config)) {
         context->HadContextSensitiveCondition = true;
@@ -1345,8 +1311,6 @@
                                                              context->Config);
         return propContent ? propContent : "";
       }
-
-      return linkedTargetsContent;
     }
 
     if (!target->IsImported() && dagCheckerParent &&
@@ -1368,15 +1332,17 @@
         return propContent ? propContent : "";
       }
     }
+
     if (!interfacePropertyName.empty()) {
-      std::string result = this->EvaluateDependentExpression(
-        prop, context->LG, context, headTarget, target, &dagChecker);
+      result = this->EvaluateDependentExpression(result, context->LG, context,
+                                                 target, &dagChecker, target);
+      std::string linkedTargetsContent = getLinkedTargetsContent(
+        target, interfacePropertyName, context, &dagChecker);
       if (!linkedTargetsContent.empty()) {
         result += (result.empty() ? "" : ";") + linkedTargetsContent;
       }
-      return result;
     }
-    return prop;
+    return result;
   }
 } targetPropertyNode;
 
@@ -1456,7 +1422,7 @@
       const char* imp = nullptr;
       std::string suffix;
       if (gt->Target->GetMappedConfig(context->Config, &loc, &imp, suffix)) {
-        cmSystemTools::ExpandListArgument(loc, objects);
+        cmExpandList(loc, objects);
       }
       context->HadContextSensitiveCondition = true;
     } else {
@@ -1476,7 +1442,7 @@
       }
 
       for (std::string& o : objects) {
-        o = obj_dir + o;
+        o = cmStrCat(obj_dir, o);
       }
     }
 
@@ -1512,7 +1478,7 @@
     }
     context->HadHeadSensitiveCondition = true;
 
-    typedef std::map<std::string, std::vector<std::string>> LangMap;
+    using LangMap = std::map<std::string, std::vector<std::string>>;
     static LangMap availableFeatures;
 
     LangMap testedFeatures;
@@ -1534,8 +1500,7 @@
           reportError(context, content->GetOriginalExpression(), error);
           return std::string();
         }
-        cmSystemTools::ExpandListArgument(featuresKnown,
-                                          availableFeatures[lang]);
+        cmExpandList(featuresKnown, availableFeatures[lang]);
       }
     }
 
@@ -1547,8 +1512,7 @@
       const char* standardDefault = context->LG->GetMakefile()->GetDefinition(
         "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
       for (std::string const& it : lit.second) {
-        if (std::find(langAvailable.begin(), langAvailable.end(), it) ==
-            langAvailable.end()) {
+        if (!cmContains(langAvailable, it)) {
           return "0";
         }
         if (standardDefault && !*standardDefault) {
@@ -1734,9 +1698,8 @@
                     "SHARED libraries.");
       return std::string();
     }
-    std::string result = target->GetDirectory(context->Config);
-    result += "/";
-    result += target->GetSOName(context->Config);
+    std::string result = cmStrCat(target->GetDirectory(context->Config), '/',
+                                  target->GetSOName(context->Config));
     return result;
   }
 };
@@ -1775,9 +1738,8 @@
       return std::string();
     }
 
-    std::string result = target->GetPDBDirectory(context->Config);
-    result += "/";
-    result += target->GetPDBName(context->Config);
+    std::string result = cmStrCat(target->GetPDBDirectory(context->Config),
+                                  '/', target->GetPDBName(context->Config));
     return result;
   }
 };
@@ -2250,8 +2212,7 @@
     const GeneratorExpressionContent* content,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    std::vector<std::string> listIn;
-    cmSystemTools::ExpandListArgument(parameters.front(), listIn);
+    std::vector<std::string> listIn = cmExpandedList(parameters.front());
     if (listIn.empty()) {
       reportError(context, content->GetOriginalExpression(),
                   "\"\" is not an absolute path.");
@@ -2285,6 +2246,8 @@
     { "NOT", &notNode },
     { "C_COMPILER_ID", &cCompilerIdNode },
     { "CXX_COMPILER_ID", &cxxCompilerIdNode },
+    { "OBJC_COMPILER_ID", &objcCompilerIdNode },
+    { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
     { "CUDA_COMPILER_ID", &cudaCompilerIdNode },
     { "Fortran_COMPILER_ID", &fortranCompilerIdNode },
     { "VERSION_GREATER", &versionGreaterNode },
@@ -2295,6 +2258,8 @@
     { "C_COMPILER_VERSION", &cCompilerVersionNode },
     { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
     { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
+    { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
+    { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
     { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
     { "PLATFORM_ID", &platformIdNode },
     { "COMPILE_FEATURES", &compileFeaturesNode },
diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h
index 7a36924..13e8484 100644
--- a/Source/cmGeneratorExpressionNode.h
+++ b/Source/cmGeneratorExpressionNode.h
@@ -43,8 +43,8 @@
   static std::string EvaluateDependentExpression(
     std::string const& prop, cmLocalGenerator* lg,
     cmGeneratorExpressionContext* context, const cmGeneratorTarget* headTarget,
-    const cmGeneratorTarget* currentTarget,
-    cmGeneratorExpressionDAGChecker* dagChecker);
+    cmGeneratorExpressionDAGChecker* dagChecker,
+    const cmGeneratorTarget* currentTarget);
 
   static const cmGeneratorExpressionNode* GetNode(
     const std::string& identifier);
diff --git a/Source/cmGeneratorExpressionParser.cxx b/Source/cmGeneratorExpressionParser.cxx
index 304378d..d6cc6ab 100644
--- a/Source/cmGeneratorExpressionParser.cxx
+++ b/Source/cmGeneratorExpressionParser.cxx
@@ -2,13 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpressionParser.h"
 
+#include <cassert>
+#include <cstddef>
+#include <utility>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpressionEvaluator.h"
 
-#include <assert.h>
-#include <stddef.h>
-#include <utility>
-
 cmGeneratorExpressionParser::cmGeneratorExpressionParser(
   std::vector<cmGeneratorExpressionToken> tokens)
   : Tokens(std::move(tokens))
@@ -66,8 +66,7 @@
   unsigned int nestedLevel = this->NestingLevel;
   ++this->NestingLevel;
 
-  std::vector<cmGeneratorExpressionToken>::const_iterator startToken =
-    this->it - 1;
+  auto startToken = this->it - 1;
 
   std::vector<cmGeneratorExpressionEvaluator*> identifier;
   while (this->it->TokenType != cmGeneratorExpressionToken::EndExpression &&
@@ -174,13 +173,9 @@
     if (!parameters.empty()) {
       extendText(result, colonToken);
 
-      typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
-      typedef std::vector<cmGeneratorExpressionToken> TokenVector;
-      std::vector<EvaluatorVector>::const_iterator pit = parameters.begin();
-      const std::vector<EvaluatorVector>::const_iterator pend =
-        parameters.end();
-      std::vector<TokenVector::const_iterator>::const_iterator commaIt =
-        commaTokens.begin();
+      auto pit = parameters.begin();
+      const auto pend = parameters.end();
+      auto commaIt = commaTokens.begin();
       assert(parameters.size() > commaTokens.size());
       for (; pit != pend; ++pit, ++commaIt) {
         if (!pit->empty() && !emptyParamTermination) {
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 036a07d..949d9d9 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -2,27 +2,35 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorTarget.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
-#include <errno.h>
+#include <cassert>
+#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <queue>
+#include <memory>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <unordered_set>
 #include <utility>
 
+#include <cm/string_view>
+
+#include <queue>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionDAGChecker.h"
+#include "cmGeneratorExpressionNode.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
@@ -31,7 +39,9 @@
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
+#include "cmSourceFileLocationKind.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
@@ -76,25 +86,16 @@
   virtual ~TargetPropertyEntry() = default;
 
   virtual const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet = false,
-    cmGeneratorTarget const* headTarget = nullptr,
-    cmGeneratorTarget const* currentTarget = nullptr,
-    cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
-    std::string const& language = std::string()) const = 0;
-  virtual const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet,
+    cmLocalGenerator* lg, const std::string& config,
     cmGeneratorTarget const* headTarget,
     cmGeneratorExpressionDAGChecker* dagChecker,
-    std::string const& language = std::string()) const = 0;
+    std::string const& language) const = 0;
 
   virtual cmListFileBacktrace GetBacktrace() const = 0;
   virtual std::string const& GetInput() const = 0;
   virtual bool GetHadContextSensitiveCondition() const { return false; }
 
   cmLinkImplItem const& LinkImplItem;
-
-private:
-  cmListFileBacktrace Backtrace;
 };
 cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
 
@@ -108,23 +109,12 @@
   {
   }
 
-  const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet = false,
-    cmGeneratorTarget const* headTarget = nullptr,
-    cmGeneratorTarget const* currentTarget = nullptr,
-    cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
-    std::string const& language = std::string()) const override
+  const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config,
+                              cmGeneratorTarget const* headTarget,
+                              cmGeneratorExpressionDAGChecker* dagChecker,
+                              std::string const& language) const override
   {
-    return this->ge->Evaluate(lg, config, quiet, headTarget, currentTarget,
-                              dagChecker, language);
-  }
-  const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config, bool quiet,
-    cmGeneratorTarget const* headTarget,
-    cmGeneratorExpressionDAGChecker* dagChecker,
-    std::string const& language = std::string()) const override
-  {
-    return this->ge->Evaluate(lg, config, quiet, headTarget, dagChecker,
+    return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr,
                               language);
   }
 
@@ -156,15 +146,7 @@
   {
   }
 
-  const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool,
-                              cmGeneratorTarget const*,
-                              cmGeneratorTarget const*,
-                              cmGeneratorExpressionDAGChecker*,
-                              std::string const&) const override
-  {
-    return this->PropertyValue;
-  }
-  const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool,
+  const std::string& Evaluate(cmLocalGenerator*, const std::string&,
                               cmGeneratorTarget const*,
                               cmGeneratorExpressionDAGChecker*,
                               std::string const&) const override
@@ -193,7 +175,7 @@
     return new TargetPropertyEntryGenex(std::move(cge));
   }
 
-  return new TargetPropertyEntryString(propertyValue, backtrace);
+  return new TargetPropertyEntryString(propertyValue, std::move(backtrace));
 }
 
 void CreatePropertyGeneratorExpressions(
@@ -201,14 +183,69 @@
   std::vector<cmGeneratorTarget::TargetPropertyEntry*>& items,
   bool evaluateForBuildsystem = false)
 {
-  std::vector<cmListFileBacktrace>::const_iterator btIt = backtraces.begin();
-  for (std::vector<std::string>::const_iterator it = entries.begin();
-       it != entries.end(); ++it, ++btIt) {
+  auto btIt = backtraces.begin();
+  for (auto it = entries.begin(); it != entries.end(); ++it, ++btIt) {
     items.push_back(
       CreateTargetPropertyEntry(*it, *btIt, evaluateForBuildsystem));
   }
 }
 
+namespace {
+// Represent a target property entry after evaluating generator expressions
+// and splitting up lists.
+struct EvaluatedTargetPropertyEntry
+{
+  EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
+                               cmListFileBacktrace bt)
+    : LinkImplItem(item)
+    , Backtrace(std::move(bt))
+  {
+  }
+
+  // Move-only.
+  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
+  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
+  EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
+    delete;
+  EvaluatedTargetPropertyEntry& operator=(
+    EvaluatedTargetPropertyEntry const&) = delete;
+
+  cmLinkImplItem const& LinkImplItem;
+  cmListFileBacktrace Backtrace;
+  std::vector<std::string> Values;
+  bool ContextDependent = false;
+};
+
+EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget::TargetPropertyEntry* entry)
+{
+  EvaluatedTargetPropertyEntry ee(entry->LinkImplItem, entry->GetBacktrace());
+  cmExpandList(entry->Evaluate(thisTarget->GetLocalGenerator(), config,
+                               thisTarget, dagChecker, lang),
+               ee.Values);
+  if (entry->GetHadContextSensitiveCondition()) {
+    ee.ContextDependent = true;
+  }
+  return ee;
+}
+
+std::vector<EvaluatedTargetPropertyEntry> EvaluateTargetPropertyEntries(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  std::vector<cmGeneratorTarget::TargetPropertyEntry*> const& in)
+{
+  std::vector<EvaluatedTargetPropertyEntry> out;
+  out.reserve(in.size());
+  for (cmGeneratorTarget::TargetPropertyEntry* entry : in) {
+    out.emplace_back(EvaluateTargetPropertyEntry(thisTarget, config, lang,
+                                                 dagChecker, entry));
+  }
+  return out;
+}
+}
+
 cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   : Target(t)
   , FortranModuleDirectoryCreated(false)
@@ -221,6 +258,7 @@
   , DebugCompileDefinitionsDone(false)
   , DebugLinkOptionsDone(false)
   , DebugLinkDirectoriesDone(false)
+  , DebugPrecompileHeadersDone(false)
   , DebugSourcesDone(false)
   , LinkImplementationLanguageIsContextDependent(true)
   , UtilityItemsDone(false)
@@ -255,13 +293,14 @@
                                      t->GetLinkDirectoriesBacktraces(),
                                      this->LinkDirectoriesEntries);
 
+  CreatePropertyGeneratorExpressions(t->GetPrecompileHeadersEntries(),
+                                     t->GetPrecompileHeadersBacktraces(),
+                                     this->PrecompileHeadersEntries);
+
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
                                      t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
 
-  this->DLLPlatform =
-    !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty();
-
   this->PolicyMap = t->GetPolicyMap();
 }
 
@@ -273,6 +312,7 @@
   cmDeleteAll(this->CompileDefinitionsEntries);
   cmDeleteAll(this->LinkOptionsEntries);
   cmDeleteAll(this->LinkDirectoriesEntries);
+  cmDeleteAll(this->PrecompileHeadersEntries);
   cmDeleteAll(this->SourceEntries);
   cmDeleteAll(this->LinkInformation);
 }
@@ -409,8 +449,7 @@
 {
   // Lookup/compute/cache the output name for this configuration.
   OutputNameKey key(config, artifact);
-  cmGeneratorTarget::OutputNameMapType::iterator i =
-    this->OutputNameMap.find(key);
+  auto i = this->OutputNameMap.find(key);
   if (i == this->OutputNameMap.end()) {
     // Add empty name in map to detect potential recursion.
     OutputNameMapType::value_type entry(key, "");
@@ -450,9 +489,8 @@
     }
 
     // Now evaluate genex and update the previously-prepared map entry.
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outName);
-    i->second = cge->Evaluate(this->LocalGenerator, config);
+    i->second =
+      cmGeneratorExpression::Evaluate(outName, this->LocalGenerator, config);
   } else if (i->second.empty()) {
     // An empty map entry indicates we have been called recursively
     // from the above block.
@@ -468,12 +506,14 @@
   const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   if (this->IsImported()) {
-    const char* prefix = this->GetFilePrefixInternal(artifact);
+    const char* prefix = this->GetFilePrefixInternal(config, artifact);
 
     return prefix ? prefix : std::string();
   }
 
-  std::string prefix, suffix, base;
+  std::string prefix;
+  std::string suffix;
+  std::string base;
   this->GetFullNameInternal(config, artifact, prefix, base, suffix);
   return prefix;
 }
@@ -481,12 +521,14 @@
   const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   if (this->IsImported()) {
-    const char* suffix = this->GetFileSuffixInternal(artifact);
+    const char* suffix = this->GetFileSuffixInternal(config, artifact);
 
     return suffix ? suffix : std::string();
   }
 
-  std::string prefix, suffix, base;
+  std::string prefix;
+  std::string suffix;
+  std::string base;
   this->GetFullNameInternal(config, artifact, prefix, base, suffix);
   return suffix;
 }
@@ -495,8 +537,8 @@
 {
   const char* postfix = nullptr;
   if (!config.empty()) {
-    std::string configProp = cmSystemTools::UpperCase(config);
-    configProp += "_POSTFIX";
+    std::string configProp =
+      cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
     postfix = this->GetProperty(configProp);
     // Mac application bundles and frameworks have no postfix.
     if (!this->IsImported() && postfix &&
@@ -508,7 +550,8 @@
 }
 
 const char* cmGeneratorTarget::GetFilePrefixInternal(
-  cmStateEnums::ArtifactType artifact, const std::string& language) const
+  std::string const& config, cmStateEnums::ArtifactType artifact,
+  const std::string& language) const
 {
   // no prefix for non-main target types.
   if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
@@ -523,8 +566,7 @@
 
   // Return an empty prefix for the import library if this platform
   // does not support import libraries.
-  if (isImportedLibraryArtifact &&
-      !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
+  if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
     return nullptr;
   }
 
@@ -558,7 +600,8 @@
   return targetPrefix;
 }
 const char* cmGeneratorTarget::GetFileSuffixInternal(
-  cmStateEnums::ArtifactType artifact, const std::string& language) const
+  std::string const& config, cmStateEnums::ArtifactType artifact,
+  const std::string& language) const
 {
   // no suffix for non-main target types.
   if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
@@ -573,8 +616,7 @@
 
   // Return an empty suffix for the import library if this platform
   // does not support import libraries.
-  if (isImportedLibraryArtifact &&
-      !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
+  if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
     return nullptr;
   }
 
@@ -650,27 +692,27 @@
 std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends(
   cmSourceFile const* sf) const
 {
-  SourceEntriesType::const_iterator i = this->SourceDepends.find(sf);
+  auto i = this->SourceDepends.find(sf);
   if (i != this->SourceDepends.end()) {
     return &i->second.Depends;
   }
   return nullptr;
 }
 
-static void handleSystemIncludesDep(
-  cmLocalGenerator* lg, cmGeneratorTarget const* depTgt,
-  const std::string& config, cmGeneratorTarget const* headTarget,
-  cmGeneratorExpressionDAGChecker* dagChecker,
-  std::vector<std::string>& result, bool excludeImported,
-  std::string const& language)
+namespace {
+void handleSystemIncludesDep(cmLocalGenerator* lg,
+                             cmGeneratorTarget const* depTgt,
+                             const std::string& config,
+                             cmGeneratorTarget const* headTarget,
+                             cmGeneratorExpressionDAGChecker* dagChecker,
+                             std::vector<std::string>& result,
+                             bool excludeImported, std::string const& language)
 {
   if (const char* dirs =
         depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) {
-    cmGeneratorExpression ge;
-    cmSystemTools::ExpandListArgument(
-      ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, depTgt,
-                               dagChecker, language),
-      result);
+    cmExpandList(cmGeneratorExpression::Evaluate(dirs, lg, config, headTarget,
+                                                 dagChecker, depTgt, language),
+                 result);
   }
   if (!depTgt->IsImported() || excludeImported) {
     return;
@@ -678,13 +720,12 @@
 
   if (const char* dirs =
         depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
-    cmGeneratorExpression ge;
-    cmSystemTools::ExpandListArgument(
-      ge.Parse(dirs)->Evaluate(lg, config, false, headTarget, depTgt,
-                               dagChecker, language),
-      result);
+    cmExpandList(cmGeneratorExpression::Evaluate(dirs, lg, config, headTarget,
+                                                 dagChecker, depTgt, language),
+                 result);
   }
 }
+}
 
 /* clang-format off */
 #define IMPLEMENT_VISIT(KIND)                                                 \
@@ -720,11 +761,8 @@
     return;
   }
 
-  std::vector<std::string> configs;
-  this->Makefile->GetConfigurations(configs);
-  if (configs.empty()) {
-    configs.emplace_back();
-  }
+  std::vector<std::string> const& configs =
+    this->Makefile->GetGeneratorConfigs();
   for (std::string const& c : configs) {
     std::vector<cmSourceFile const*> sourceFiles;
     this->GetObjectSources(sourceFiles, c);
@@ -735,9 +773,8 @@
                                           const std::string& config) const
 {
   if (!config.empty()) {
-    std::string featureConfig = feature;
-    featureConfig += "_";
-    featureConfig += cmSystemTools::UpperCase(config);
+    std::string featureConfig =
+      cmStrCat(feature, '_', cmSystemTools::UpperCase(config));
     if (const char* value = this->GetProperty(featureConfig)) {
       return value;
     }
@@ -771,7 +808,7 @@
                                      std::string const& config) const
 {
   const char* feature = "INTERPROCEDURAL_OPTIMIZATION";
-  const bool result = cmSystemTools::IsOn(this->GetFeature(feature, config));
+  const bool result = cmIsOn(this->GetFeature(feature, config));
 
   if (!result) {
     // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies
@@ -862,8 +899,7 @@
 bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const
 {
   const_cast<cmGeneratorTarget*>(this)->ComputeObjectMapping();
-  std::set<cmSourceFile const*>::const_iterator it =
-    this->ExplicitObjectName.find(file);
+  auto it = this->ExplicitObjectName.find(file);
   return it != this->ExplicitObjectName.end();
 }
 
@@ -1044,9 +1080,8 @@
     config_upper = cmSystemTools::UpperCase(config);
   }
 
-  typedef std::map<std::string, std::vector<std::string>> IncludeCacheType;
-  IncludeCacheType::const_iterator iter =
-    this->SystemIncludesCache.find(config_upper);
+  using IncludeCacheType = std::map<std::string, std::vector<std::string>>;
+  auto iter = this->SystemIncludesCache.find(config_upper);
 
   if (iter == this->SystemIncludesCache.end()) {
     cmGeneratorExpressionDAGChecker dagChecker(
@@ -1056,11 +1091,10 @@
 
     std::vector<std::string> result;
     for (std::string const& it : this->Target->GetSystemIncludeDirectories()) {
-      cmGeneratorExpression ge;
-      cmSystemTools::ExpandListArgument(
-        ge.Parse(it)->Evaluate(this->LocalGenerator, config, false, this,
-                               &dagChecker, language),
-        result);
+      cmExpandList(cmGeneratorExpression::Evaluate(it, this->LocalGenerator,
+                                                   config, this, &dagChecker,
+                                                   nullptr, language),
+                   result);
     }
 
     std::vector<cmGeneratorTarget const*> const& deps =
@@ -1087,80 +1121,207 @@
   return this->Target->GetPropertyAsBool(prop);
 }
 
-static void AddInterfaceEntries(
-  cmGeneratorTarget const* thisTarget, std::string const& config,
-  std::string const& prop,
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries)
+bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
+  std::string const& prop, cmGeneratorExpressionContext* context) const
+{
+  std::string const key = prop + '@' + context->Config;
+  auto i = this->MaybeInterfacePropertyExists.find(key);
+  if (i == this->MaybeInterfacePropertyExists.end()) {
+    // Insert an entry now in case there is a cycle.
+    i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
+    bool& maybeInterfaceProp = i->second;
+
+    // If this target itself has a non-empty property value, we are done.
+    const char* p = this->GetProperty(prop);
+    maybeInterfaceProp = p && *p;
+
+    // Otherwise, recurse to interface dependencies.
+    if (!maybeInterfaceProp) {
+      cmGeneratorTarget const* headTarget =
+        context->HeadTarget ? context->HeadTarget : this;
+      if (cmLinkInterfaceLibraries const* iface =
+            this->GetLinkInterfaceLibraries(context->Config, headTarget,
+                                            true)) {
+        if (iface->HadHeadSensitiveCondition) {
+          // With a different head target we may get to a library with
+          // this interface property.
+          maybeInterfaceProp = true;
+        } else {
+          // The transitive interface libraries do not depend on the
+          // head target, so we can follow them.
+          for (cmLinkItem const& lib : iface->Libraries) {
+            if (lib.Target &&
+                lib.Target->MaybeHaveInterfaceProperty(prop, context)) {
+              maybeInterfaceProp = true;
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+  return i->second;
+}
+
+std::string cmGeneratorTarget::EvaluateInterfaceProperty(
+  std::string const& prop, cmGeneratorExpressionContext* context,
+  cmGeneratorExpressionDAGChecker* dagCheckerParent) const
+{
+  std::string result;
+
+  // If the property does not appear transitively at all, we are done.
+  if (!this->MaybeHaveInterfaceProperty(prop, context)) {
+    return result;
+  }
+
+  // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled.  This is
+  // a subset of TargetPropertyNode::Evaluate without stringify/parse steps
+  // but sufficient for transitive interface properties.
+  cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop,
+                                             nullptr, dagCheckerParent);
+  switch (dagChecker.Check()) {
+    case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
+      dagChecker.ReportError(
+        context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">");
+      return result;
+    case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
+      // No error. We just skip cyclic references.
+      return result;
+    case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
+      // No error. We have already seen this transitive property.
+      return result;
+    case cmGeneratorExpressionDAGChecker::DAG:
+      break;
+  }
+
+  cmGeneratorTarget const* headTarget =
+    context->HeadTarget ? context->HeadTarget : this;
+
+  if (const char* p = this->GetProperty(prop)) {
+    result = cmGeneratorExpressionNode::EvaluateDependentExpression(
+      p, context->LG, context, headTarget, &dagChecker, this);
+  }
+
+  if (cmLinkInterfaceLibraries const* iface =
+        this->GetLinkInterfaceLibraries(context->Config, headTarget, true)) {
+    for (cmLinkItem const& lib : iface->Libraries) {
+      // Broken code can have a target in its own link interface.
+      // Don't follow such link interface entries so as not to create a
+      // self-referencing loop.
+      if (lib.Target && lib.Target != this) {
+        // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the
+        // above property and hand-evaluate it as if it were compiled.
+        // Create a context as cmCompiledGeneratorExpression::Evaluate does.
+        cmGeneratorExpressionContext libContext(
+          context->LG, context->Config, context->Quiet, headTarget, this,
+          context->EvaluateForBuildsystem, context->Backtrace,
+          context->Language);
+        std::string libResult = cmGeneratorExpression::StripEmptyListElements(
+          lib.Target->EvaluateInterfaceProperty(prop, &libContext,
+                                                &dagChecker));
+        if (!libResult.empty()) {
+          if (result.empty()) {
+            result = std::move(libResult);
+          } else {
+            result.reserve(result.size() + 1 + libResult.size());
+            result += ";";
+            result += libResult;
+          }
+        }
+        context->HadContextSensitiveCondition =
+          context->HadContextSensitiveCondition ||
+          libContext.HadContextSensitiveCondition;
+        context->HadHeadSensitiveCondition =
+          context->HadHeadSensitiveCondition ||
+          libContext.HadHeadSensitiveCondition;
+      }
+    }
+  }
+
+  return result;
+}
+
+namespace {
+void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
+                         std::string const& config, std::string const& prop,
+                         std::string const& lang,
+                         cmGeneratorExpressionDAGChecker* dagChecker,
+                         std::vector<EvaluatedTargetPropertyEntry>& entries)
 {
   if (cmLinkImplementationLibraries const* impl =
-        thisTarget->GetLinkImplementationLibraries(config)) {
+        headTarget->GetLinkImplementationLibraries(config)) {
     for (cmLinkImplItem const& lib : impl->Libraries) {
       if (lib.Target) {
-        std::string uniqueName =
-          thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
-            lib.Target);
-        std::string genex =
-          "$<TARGET_PROPERTY:" + std::move(uniqueName) + "," + prop + ">";
-        cmGeneratorExpression ge(lib.Backtrace);
-        std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
-        cge->SetEvaluateForBuildsystem(true);
-        entries.push_back(new TargetPropertyEntryGenex(std::move(cge), lib));
+        EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
+        // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
+        // caller's property and hand-evaluate it as if it were compiled.
+        // Create a context as cmCompiledGeneratorExpression::Evaluate does.
+        cmGeneratorExpressionContext context(
+          headTarget->GetLocalGenerator(), config, false, headTarget,
+          headTarget, true, lib.Backtrace, lang);
+        cmExpandList(
+          lib.Target->EvaluateInterfaceProperty(prop, &context, dagChecker),
+          ee.Values);
+        ee.ContextDependent = context.HadContextSensitiveCondition;
+        entries.emplace_back(std::move(ee));
       }
     }
   }
 }
 
-static void AddObjectEntries(
-  cmGeneratorTarget const* thisTarget, std::string const& config,
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries)
+void AddObjectEntries(cmGeneratorTarget const* headTarget,
+                      std::string const& config,
+                      cmGeneratorExpressionDAGChecker* dagChecker,
+                      std::vector<EvaluatedTargetPropertyEntry>& entries)
 {
   if (cmLinkImplementationLibraries const* impl =
-        thisTarget->GetLinkImplementationLibraries(config)) {
+        headTarget->GetLinkImplementationLibraries(config)) {
     for (cmLinkImplItem const& lib : impl->Libraries) {
       if (lib.Target &&
           lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
         std::string uniqueName =
-          thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
+          headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
             lib.Target);
         std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
         cmGeneratorExpression ge(lib.Backtrace);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
         cge->SetEvaluateForBuildsystem(true);
-        entries.push_back(new TargetPropertyEntryGenex(std::move(cge), lib));
+
+        EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
+        cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config,
+                                   headTarget, dagChecker),
+                     ee.Values);
+        if (cge->GetHadContextSensitiveCondition()) {
+          ee.ContextDependent = true;
+        }
+        entries.emplace_back(std::move(ee));
       }
     }
   }
 }
 
-static bool processSources(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& srcs,
-  std::unordered_set<std::string>& uniqueSrcs,
-  cmGeneratorExpressionDAGChecker* dagChecker, std::string const& config,
-  bool debugSources)
+bool processSources(cmGeneratorTarget const* tgt,
+                    std::vector<EvaluatedTargetPropertyEntry>& entries,
+                    std::vector<BT<std::string>>& srcs,
+                    std::unordered_set<std::string>& uniqueSrcs,
+                    bool debugSources)
 {
   cmMakefile* mf = tgt->Target->GetMakefile();
 
   bool contextDependent = false;
 
-  for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
-    cmLinkImplItem const& item = entry->LinkImplItem;
-    std::string const& targetName = item.AsStr();
-    std::vector<std::string> entrySources;
-    cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(),
-                                                      config, false, tgt, tgt,
-                                                      dagChecker),
-                                      entrySources);
-
-    if (entry->GetHadContextSensitiveCondition()) {
+  for (EvaluatedTargetPropertyEntry& entry : entries) {
+    if (entry.ContextDependent) {
       contextDependent = true;
     }
 
-    for (std::string& src : entrySources) {
+    cmLinkImplItem const& item = entry.LinkImplItem;
+    std::string const& targetName = item.AsStr();
+
+    for (std::string& src : entry.Values) {
       cmSourceFile* sf = mf->GetOrCreateSource(src);
       std::string e;
-      std::string fullPath = sf->GetFullPath(&e);
+      std::string fullPath = sf->ResolveFullPath(&e);
       if (fullPath.empty()) {
         if (!e.empty()) {
           cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
@@ -1186,9 +1347,9 @@
       src = fullPath;
     }
     std::string usedSources;
-    for (std::string const& src : entrySources) {
+    for (std::string const& src : entry.Values) {
       if (uniqueSrcs.insert(src).second) {
-        srcs.emplace_back(src, entry->GetBacktrace());
+        srcs.emplace_back(src, entry.Backtrace);
         if (debugSources) {
           usedSources += " * " + src + "\n";
         }
@@ -1199,11 +1360,12 @@
         MessageType::LOG,
         std::string("Used sources for target ") + tgt->GetName() + ":\n" +
           usedSources,
-        entry->GetBacktrace());
+        entry.Backtrace);
     }
   }
   return contextDependent;
 }
+}
 
 std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
   std::string const& config) const
@@ -1220,8 +1382,7 @@
 
     cmStringRange sourceEntries = this->Target->GetSourceEntries();
     for (std::string const& entry : sourceEntries) {
-      std::vector<std::string> items;
-      cmSystemTools::ExpandListArgument(entry, items);
+      std::vector<std::string> items = cmExpandedList(entry);
       for (std::string const& item : items) {
         if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") &&
             item.back() == '>') {
@@ -1237,12 +1398,11 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
-  bool debugSources = !this->DebugSourcesDone &&
-    std::find(debugProperties.begin(), debugProperties.end(), "SOURCES") !=
-      debugProperties.end();
+  bool debugSources =
+    !this->DebugSourcesDone && cmContains(debugProperties, "SOURCES");
 
   if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
     this->DebugSourcesDone = true;
@@ -1251,28 +1411,31 @@
   cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr,
                                              nullptr);
 
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, std::string(), &dagChecker,
+                                  this->SourceEntries);
+
   std::unordered_set<std::string> uniqueSrcs;
   bool contextDependentDirectSources =
-    processSources(this, this->SourceEntries, files, uniqueSrcs, &dagChecker,
-                   config, debugSources);
+    processSources(this, entries, files, uniqueSrcs, debugSources);
 
   // Collect INTERFACE_SOURCES of all direct link-dependencies.
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceSourcesEntries;
-  AddInterfaceEntries(this, config, "INTERFACE_SOURCES",
-                      linkInterfaceSourcesEntries);
+  std::vector<EvaluatedTargetPropertyEntry> linkInterfaceSourcesEntries;
+  AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
+                      &dagChecker, linkInterfaceSourcesEntries);
   std::vector<std::string>::size_type numFilesBefore = files.size();
-  bool contextDependentInterfaceSources =
-    processSources(this, linkInterfaceSourcesEntries, files, uniqueSrcs,
-                   &dagChecker, config, debugSources);
+  bool contextDependentInterfaceSources = processSources(
+    this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
 
   // Collect TARGET_OBJECTS of direct object link-dependencies.
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkObjectsEntries;
-  AddObjectEntries(this, config, linkObjectsEntries);
+  bool contextDependentObjects = false;
   std::vector<std::string>::size_type numFilesBefore2 = files.size();
-  bool contextDependentObjects =
-    processSources(this, linkObjectsEntries, files, uniqueSrcs, &dagChecker,
-                   config, debugSources);
+  if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
+    std::vector<EvaluatedTargetPropertyEntry> linkObjectsEntries;
+    AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
+    contextDependentObjects = processSources(this, linkObjectsEntries, files,
+                                             uniqueSrcs, debugSources);
+  }
 
   if (!contextDependentDirectSources &&
       !(contextDependentInterfaceSources && numFilesBefore < files.size()) &&
@@ -1280,8 +1443,6 @@
     this->LinkImplementationLanguageIsContextDependent = false;
   }
 
-  cmDeleteAll(linkInterfaceSourcesEntries);
-  cmDeleteAll(linkObjectsEntries);
   return files;
 }
 
@@ -1362,7 +1523,7 @@
 
   // Lookup any existing link implementation for this configuration.
   std::string const key = cmSystemTools::UpperCase(config);
-  KindedSourcesMapType::iterator it = this->KindedSourcesMap.find(key);
+  auto it = this->KindedSourcesMap.find(key);
   if (it != this->KindedSourcesMap.end()) {
     if (!it->second.Initialized) {
       std::ostringstream e;
@@ -1412,7 +1573,7 @@
       kind = SourceKindHeader;
     } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
       kind = SourceKindExternalObject;
-    } else if (!sf->GetLanguage().empty()) {
+    } else if (!sf->GetOrDetermineLanguage().empty()) {
       kind = SourceKindObjectSource;
     } else if (ext == "def") {
       kind = SourceKindModuleDefinition;
@@ -1431,7 +1592,7 @@
       // Both names would have been auto generated from Visual Studio
       // where the user supplied the file name and Visual Studio
       // appended the suffix.
-      std::string resx = sf->GetFullPath();
+      std::string resx = sf->ResolveFullPath();
       std::string hFileName = resx.substr(0, resx.find_last_of('.')) + ".h";
       files.ExpectedResxHeaders.insert(hFileName);
     } else if (ext == "appxmanifest") {
@@ -1447,12 +1608,12 @@
       // Both names would have been auto generated from Visual Studio
       // where the user supplied the file name and Visual Studio
       // appended the suffix.
-      std::string xaml = sf->GetFullPath();
+      std::string xaml = sf->ResolveFullPath();
       std::string hFileName = xaml + ".h";
       std::string cppFileName = xaml + ".cpp";
       files.ExpectedXamlHeaders.insert(hFileName);
       files.ExpectedXamlSources.insert(cppFileName);
-    } else if (header_regex.find(sf->GetFullPath())) {
+    } else if (header_regex.find(sf->ResolveFullPath())) {
       kind = SourceKindHeader;
     } else {
       kind = SourceKindExtra;
@@ -1494,8 +1655,7 @@
   for (size_t ci = 0; ci < configs.size(); ++ci) {
     KindedSources const& sources = this->GetKindedSources(configs[ci]);
     for (SourceAndKind const& src : sources.Sources) {
-      std::map<cmSourceFile const*, size_t>::iterator mi =
-        index.find(src.Source.Value);
+      auto mi = index.find(src.Source.Value);
       if (mi == index.end()) {
         AllConfigSource acs;
         acs.Source = src.Source.Value;
@@ -1521,8 +1681,7 @@
 
   // Check for a per-configuration output directory target property.
   std::string configUpper = cmSystemTools::UpperCase(config);
-  std::string configProp = "COMPILE_PDB_NAME_";
-  configProp += configUpper;
+  std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper);
   const char* config_name = this->GetProperty(configProp);
   if (config_name && *config_name) {
     return prefix + config_name + ".pdb";
@@ -1594,9 +1753,8 @@
   // Check for rpath support on this platform.
   std::string ll = this->GetLinkerLanguage(config);
   if (!ll.empty()) {
-    std::string flagVar = "CMAKE_SHARED_LIBRARY_RUNTIME_";
-    flagVar += ll;
-    flagVar += "_FLAG";
+    std::string flagVar =
+      cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG");
     if (!this->Makefile->IsSet(flagVar)) {
       // There is no rpath support on this platform so nothing needs
       // relinking.
@@ -1612,7 +1770,7 @@
   // will likely change between the build tree and install tree and
   // this target must be relinked.
   bool have_rpath =
-    this->HaveBuildTreeRPATH(config) || this->HaveInstallTreeRPATH();
+    this->HaveBuildTreeRPATH(config) || this->HaveInstallTreeRPATH(config);
   bool is_ninja =
     this->LocalGenerator->GetGlobalGenerator()->GetName() == "Ninja";
 
@@ -1674,9 +1832,8 @@
   // binaries.
   std::string ll = this->GetLinkerLanguage(config);
   if (!ll.empty()) {
-    std::string sepVar = "CMAKE_SHARED_LIBRARY_RUNTIME_";
-    sepVar += ll;
-    sepVar += "_FLAG_SEP";
+    std::string sepVar =
+      cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP");
     const char* sep = this->Makefile->GetDefinition(sepVar);
     if (sep && *sep) {
       // TODO: Add ELF check to ABI detection and get rid of
@@ -1793,7 +1950,7 @@
   const char* build_with_install_name =
     this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR");
   if (build_with_install_name) {
-    return cmSystemTools::IsOn(build_with_install_name);
+    return cmIsOn(build_with_install_name);
   }
 
   cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068();
@@ -1858,23 +2015,23 @@
   return this->GetLibraryNames(config).SharedObject;
 }
 
-static bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level)
+namespace {
+bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level)
 {
   return level == cmGeneratorTarget::FullLevel;
 }
 
-static bool shouldAddContentLevel(
-  cmGeneratorTarget::BundleDirectoryLevel level)
+bool shouldAddContentLevel(cmGeneratorTarget::BundleDirectoryLevel level)
 {
   return level == cmGeneratorTarget::ContentLevel || shouldAddFullLevel(level);
 }
+}
 
 std::string cmGeneratorTarget::GetAppBundleDirectory(
   const std::string& config, BundleDirectoryLevel level) const
 {
-  std::string fpath =
-    this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact);
-  fpath += ".";
+  std::string fpath = cmStrCat(
+    this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
   const char* ext = this->GetProperty("BUNDLE_EXTENSION");
   if (!ext) {
     ext = "app";
@@ -1899,9 +2056,8 @@
 std::string cmGeneratorTarget::GetCFBundleDirectory(
   const std::string& config, BundleDirectoryLevel level) const
 {
-  std::string fpath;
-  fpath += this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact);
-  fpath += ".";
+  std::string fpath = cmStrCat(
+    this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
   const char* ext = this->GetProperty("BUNDLE_EXTENSION");
   if (!ext) {
     if (this->IsXCTestOnApple()) {
@@ -1924,9 +2080,8 @@
 std::string cmGeneratorTarget::GetFrameworkDirectory(
   const std::string& config, BundleDirectoryLevel level) const
 {
-  std::string fpath;
-  fpath += this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact);
-  fpath += ".";
+  std::string fpath = cmStrCat(
+    this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
   const char* ext = this->GetProperty("BUNDLE_EXTENSION");
   if (!ext) {
     ext = "framework";
@@ -1983,8 +2138,7 @@
 
     if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) {
       if (install_name_dir && *install_name_dir) {
-        dir = install_name_dir;
-        dir += "/";
+        dir = cmStrCat(install_name_dir, '/');
       }
     }
     if (!install_name_dir) {
@@ -2025,8 +2179,7 @@
     if (const char* custom_export_name = this->GetProperty("DEFINE_SYMBOL")) {
       this->ExportMacro = custom_export_name;
     } else {
-      std::string in = this->GetName();
-      in += "_EXPORTS";
+      std::string in = cmStrCat(this->GetName(), "_EXPORTS");
       this->ExportMacro = cmSystemTools::MakeCidentifier(in);
     }
     return &this->ExportMacro;
@@ -2113,7 +2266,7 @@
   const std::string& config) const
 {
   std::string key(cmSystemTools::UpperCase(config));
-  LinkClosureMapType::iterator i = this->LinkClosureMap.find(key);
+  auto i = this->LinkClosureMap.find(key);
   if (i == this->LinkClosureMap.end()) {
     LinkClosure lc;
     this->ComputeLinkClosure(config, lc);
@@ -2247,8 +2400,7 @@
   const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   // Start with the output directory for the target.
-  std::string fpath = this->GetDirectory(config, artifact);
-  fpath += "/";
+  std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
   BundleDirectoryLevel level = ContentLevel;
   if (this->IsFrameworkOnApple()) {
     // additional files with a framework go into the version specific
@@ -2284,10 +2436,9 @@
   }
 
   if (this->GetType() > cmStateEnums::OBJECT_LIBRARY) {
-    std::string msg = "cmTarget::GetCompileInfo called for ";
-    msg += this->GetName();
-    msg += " which has type ";
-    msg += cmState::GetTargetTypeName(this->GetType());
+    std::string msg = cmStrCat("cmTarget::GetCompileInfo called for ",
+                               this->GetName(), " which has type ",
+                               cmState::GetTargetTypeName(this->GetType()));
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
     return nullptr;
   }
@@ -2297,8 +2448,7 @@
   if (!config.empty()) {
     config_upper = cmSystemTools::UpperCase(config);
   }
-  CompileInfoMapType::const_iterator i =
-    this->CompileInfoMap.find(config_upper);
+  auto i = this->CompileInfoMap.find(config_upper);
   if (i == this->CompileInfoMap.end()) {
     CompileInfo info;
     this->ComputePDBOutputDir("COMPILE_PDB", config, info.CompilePdbDir);
@@ -2323,8 +2473,7 @@
   if (!config.empty()) {
     config_upper = cmSystemTools::UpperCase(config);
   }
-  ModuleDefinitionInfoMapType::const_iterator i =
-    this->ModuleDefinitionInfoMap.find(config_upper);
+  auto i = this->ModuleDefinitionInfoMap.find(config_upper);
   if (i == this->ModuleDefinitionInfoMap.end()) {
     ModuleDefinitionInfo info;
     this->ComputeModuleDefinitionInfo(config, info);
@@ -2341,7 +2490,7 @@
   info.WindowsExportAllSymbols =
     this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") &&
     this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS");
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
   info.DefFileGenerated =
     info.WindowsExportAllSymbols || info.Sources.size() > 1;
 #else
@@ -2357,7 +2506,7 @@
 
 bool cmGeneratorTarget::IsDLLPlatform() const
 {
-  return this->DLLPlatform;
+  return this->Target->IsDLLPlatform();
 }
 
 void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result,
@@ -2368,14 +2517,12 @@
   if (!prop) {
     return;
   }
-  cmGeneratorExpression ge;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr,
                                              nullptr);
-  cmSystemTools::ExpandListArgument(
-    ge.Parse(prop)->Evaluate(this->LocalGenerator, config, false, this,
-                             &dagChecker),
-    result);
+  cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator,
+                                               config, this, &dagChecker),
+               result);
 }
 
 void processILibs(const std::string& config,
@@ -2426,11 +2573,11 @@
   cmMakefile* Makefile;
   cmLocalGenerator* LocalGenerator;
   cmGlobalGenerator const* GlobalGenerator;
-  typedef cmGeneratorTarget::SourceEntry SourceEntry;
+  using SourceEntry = cmGeneratorTarget::SourceEntry;
   SourceEntry* CurrentEntry;
   std::queue<cmSourceFile*> SourceQueue;
   std::set<cmSourceFile*> SourcesQueued;
-  typedef std::map<std::string, cmSourceFile*> NameMapType;
+  using NameMapType = std::map<std::string, cmSourcesWithOutput>;
   NameMapType NameMap;
   std::vector<std::string> NewSources;
 
@@ -2456,21 +2603,18 @@
 
   // Queue all the source files already specified for the target.
   if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
-    std::vector<std::string> configs;
-    this->Makefile->GetConfigurations(configs);
-    if (configs.empty()) {
-      configs.emplace_back();
-    }
     std::set<cmSourceFile*> emitted;
+    std::vector<std::string> const& configs =
+      this->Makefile->GetGeneratorConfigs();
     for (std::string const& c : configs) {
       std::vector<cmSourceFile*> sources;
       this->GeneratorTarget->GetSourceFiles(sources, c);
       for (cmSourceFile* sf : sources) {
         const std::set<cmGeneratorTarget const*> tgts =
           this->GlobalGenerator->GetFilenameTargetDepends(sf);
-        if (tgts.find(this->GeneratorTarget) != tgts.end()) {
+        if (cmContains(tgts, this->GeneratorTarget)) {
           std::ostringstream e;
-          e << "Evaluation output file\n  \"" << sf->GetFullPath()
+          e << "Evaluation output file\n  \"" << sf->ResolveFullPath()
             << "\"\ndepends on the sources of a target it is used in.  This "
                "is a dependency loop and is not allowed.";
           this->GeneratorTarget->LocalGenerator->IssueMessage(
@@ -2502,8 +2646,7 @@
 
     // Queue dependencies added explicitly by the user.
     if (const char* additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) {
-      std::vector<std::string> objDeps;
-      cmSystemTools::ExpandListArgument(additionalDeps, objDeps);
+      std::vector<std::string> objDeps = cmExpandedList(additionalDeps);
       for (std::string& objDep : objDeps) {
         if (cmSystemTools::FileIsFullPath(objDep)) {
           objDep = cmSystemTools::CollapseFullPath(objDep);
@@ -2513,7 +2656,7 @@
     }
 
     // Queue the source needed to generate this file, if any.
-    this->FollowName(sf->GetFullPath());
+    this->FollowName(sf->ResolveFullPath());
 
     // Queue dependencies added programmatically by commands.
     this->FollowNames(sf->GetDepends());
@@ -2534,25 +2677,36 @@
     this->SourceQueue.push(sf);
 
     // Make sure this file is in the target at the end.
-    this->NewSources.push_back(sf->GetFullPath());
+    this->NewSources.push_back(sf->ResolveFullPath());
   }
 }
 
 void cmTargetTraceDependencies::FollowName(std::string const& name)
 {
-  NameMapType::iterator i = this->NameMap.find(name);
-  if (i == this->NameMap.end()) {
+  // Use lower bound with key comparison to not repeat the search for the
+  // insert position if the name could not be found (which is the common case).
+  auto i = this->NameMap.lower_bound(name);
+  if (i == this->NameMap.end() || i->first != name) {
     // Check if we know how to generate this file.
-    cmSourceFile* sf = this->Makefile->GetSourceFileWithOutput(name);
-    NameMapType::value_type entry(name, sf);
-    i = this->NameMap.insert(entry).first;
+    cmSourcesWithOutput sources = this->Makefile->GetSourcesWithOutput(name);
+    i = this->NameMap.emplace_hint(i, name, sources);
   }
-  if (cmSourceFile* sf = i->second) {
-    // Record the dependency we just followed.
-    if (this->CurrentEntry) {
-      this->CurrentEntry->Depends.push_back(sf);
+  if (cmTarget* t = i->second.Target) {
+    // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or
+    // POST_BUILD command.
+    this->GeneratorTarget->Target->AddUtility(t->GetName());
+  }
+  if (cmSourceFile* sf = i->second.Source) {
+    // For now only follow the dependency if the source file is not a
+    // byproduct.  Semantics of byproducts in a non-Ninja context will have to
+    // be defined first.
+    if (!i->second.SourceIsByproduct) {
+      // Record the dependency we just followed.
+      if (this->CurrentEntry) {
+        this->CurrentEntry->Depends.push_back(sf);
+      }
+      this->QueueSource(sf);
     }
-    this->QueueSource(sf);
   }
 }
 
@@ -2637,7 +2791,8 @@
     // Check for target references in generator expressions.
     for (std::string const& cl : cCmdLine) {
       const std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(cl);
-      cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), "", true);
+      cge->SetQuiet(true);
+      cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), "");
       std::set<cmGeneratorTarget*> geTargets = cge->GetTargets();
       targets.insert(geTargets.begin(), geTargets.end());
     }
@@ -2648,12 +2803,9 @@
   }
 
   // Queue the custom command dependencies.
-  std::vector<std::string> configs;
   std::set<std::string> emitted;
-  this->Makefile->GetConfigurations(configs);
-  if (configs.empty()) {
-    configs.emplace_back();
-  }
+  std::vector<std::string> const& configs =
+    this->Makefile->GetGeneratorConfigs();
   for (std::string const& conf : configs) {
     this->FollowCommandDepends(cc, conf, emitted);
   }
@@ -2716,15 +2868,15 @@
 {
   const char* archs = nullptr;
   if (!config.empty()) {
-    std::string defVarName = "OSX_ARCHITECTURES_";
-    defVarName += cmSystemTools::UpperCase(config);
+    std::string defVarName =
+      cmStrCat("OSX_ARCHITECTURES_", cmSystemTools::UpperCase(config));
     archs = this->GetProperty(defVarName);
   }
   if (!archs) {
     archs = this->GetProperty("OSX_ARCHITECTURES");
   }
   if (archs) {
-    cmSystemTools::ExpandListArgument(std::string(archs), archVec);
+    cmExpandList(std::string(archs), archVec);
   }
 }
 
@@ -2757,33 +2909,35 @@
     case cmStateEnums::MODULE_LIBRARY:
       return "CMAKE_" + lang + "_CREATE_SHARED_MODULE";
     case cmStateEnums::EXECUTABLE:
+      if (this->IsExecutableWithExports()) {
+        std::string linkExeWithExports =
+          "CMAKE_" + lang + "_LINK_EXECUTABLE_WITH_EXPORTS";
+        if (this->Makefile->IsDefinitionSet(linkExeWithExports)) {
+          return linkExeWithExports;
+        }
+      }
       return "CMAKE_" + lang + "_LINK_EXECUTABLE";
     default:
       break;
   }
   return "";
 }
-static void processIncludeDirectories(
+
+namespace {
+void processIncludeDirectories(
   cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
+  std::vector<EvaluatedTargetPropertyEntry>& entries,
   std::vector<BT<std::string>>& includes,
-  std::unordered_set<std::string>& uniqueIncludes,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugIncludes, const std::string& language)
+  std::unordered_set<std::string>& uniqueIncludes, bool debugIncludes)
 {
-  for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
-    cmLinkImplItem const& item = entry->LinkImplItem;
+  for (EvaluatedTargetPropertyEntry& entry : entries) {
+    cmLinkImplItem const& item = entry.LinkImplItem;
     std::string const& targetName = item.AsStr();
     bool const fromImported = item.Target && item.Target->IsImported();
     bool const checkCMP0027 = item.FromGenex;
-    std::vector<std::string> entryIncludes;
-    cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(),
-                                                      config, false, tgt,
-                                                      dagChecker, language),
-                                      entryIncludes);
 
     std::string usedIncludes;
-    for (std::string& entryInclude : entryIncludes) {
+    for (std::string& entryInclude : entry.Values) {
       if (fromImported && !cmSystemTools::FileExists(entryInclude)) {
         std::ostringstream e;
         MessageType messageType = MessageType::FATAL_ERROR;
@@ -2852,15 +3006,14 @@
         }
       }
 
-      if (!cmSystemTools::IsOff(entryInclude)) {
+      if (!cmIsOff(entryInclude)) {
         cmSystemTools::ConvertToUnixSlashes(entryInclude);
       }
-      std::string inc = entryInclude;
 
-      if (uniqueIncludes.insert(inc).second) {
-        includes.emplace_back(inc, entry->GetBacktrace());
+      if (uniqueIncludes.insert(entryInclude).second) {
+        includes.emplace_back(entryInclude, entry.Backtrace);
         if (debugIncludes) {
-          usedIncludes += " * " + inc + "\n";
+          usedIncludes += " * " + entryInclude + "\n";
         }
       }
     }
@@ -2869,10 +3022,11 @@
         MessageType::LOG,
         std::string("Used includes for target ") + tgt->GetName() + ":\n" +
           usedIncludes,
-        entry->GetBacktrace());
+        entry.Backtrace);
     }
   }
 }
+}
 
 std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
   const std::string& config, const std::string& lang) const
@@ -2887,25 +3041,22 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
   bool debugIncludes = !this->DebugIncludesDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "INCLUDE_DIRECTORIES") != debugProperties.end();
+    cmContains(debugProperties, "INCLUDE_DIRECTORIES");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugIncludesDone = true;
   }
 
-  processIncludeDirectories(this, this->IncludeDirectoriesEntries, includes,
-                            uniqueIncludes, &dagChecker, config, debugIncludes,
-                            lang);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, lang, &dagChecker,
+                                  this->IncludeDirectoriesEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceIncludeDirectoriesEntries;
-  AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES",
-                      linkInterfaceIncludeDirectoriesEntries);
+  AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang,
+                      &dagChecker, entries);
 
   if (this->Makefile->IsOn("APPLE")) {
     cmLinkImplementationLibraries const* impl =
@@ -2921,16 +3072,14 @@
 
       libDir = frameworkCheck.match(1);
 
-      linkInterfaceIncludeDirectoriesEntries.push_back(
-        CreateTargetPropertyEntry(libDir));
+      EvaluatedTargetPropertyEntry ee(lib, cmListFileBacktrace());
+      ee.Values.emplace_back(std::move(libDir));
+      entries.emplace_back(std::move(ee));
     }
   }
 
-  processIncludeDirectories(this, linkInterfaceIncludeDirectoriesEntries,
-                            includes, uniqueIncludes, &dagChecker, config,
-                            debugIncludes, lang);
-
-  cmDeleteAll(linkInterfaceIncludeDirectoriesEntries);
+  processIncludeDirectories(this, entries, includes, uniqueIncludes,
+                            debugIncludes);
 
   return includes;
 }
@@ -2941,33 +3090,26 @@
   Shell
 };
 
-static void processOptionsInternal(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions, const char* logName, std::string const& language,
-  OptionsParse parse)
+namespace {
+void processOptions(cmGeneratorTarget const* tgt,
+                    std::vector<EvaluatedTargetPropertyEntry> const& entries,
+                    std::vector<BT<std::string>>& options,
+                    std::unordered_set<std::string>& uniqueOptions,
+                    bool debugOptions, const char* logName, OptionsParse parse)
 {
-  for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
-    std::vector<std::string> entryOptions;
-    cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(),
-                                                      config, false, tgt,
-                                                      dagChecker, language),
-                                      entryOptions);
+  for (EvaluatedTargetPropertyEntry const& entry : entries) {
     std::string usedOptions;
-    for (std::string const& opt : entryOptions) {
+    for (std::string const& opt : entry.Values) {
       if (uniqueOptions.insert(opt).second) {
         if (parse == OptionsParse::Shell &&
             cmHasLiteralPrefix(opt, "SHELL:")) {
           std::vector<std::string> tmp;
           cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
           for (std::string& o : tmp) {
-            options.emplace_back(std::move(o), entry->GetBacktrace());
+            options.emplace_back(std::move(o), entry.Backtrace);
           }
         } else {
-          options.emplace_back(opt, entry->GetBacktrace());
+          options.emplace_back(opt, entry.Backtrace);
         }
         if (debugOptions) {
           usedOptions += " * " + opt + "\n";
@@ -2979,22 +3121,10 @@
         MessageType::LOG,
         std::string("Used ") + logName + std::string(" for target ") +
           tgt->GetName() + ":\n" + usedOptions,
-        entry->GetBacktrace());
+        entry.Backtrace);
     }
   }
 }
-
-static void processCompileOptions(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions, std::string const& language)
-{
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, debugOptions, "compile options", language,
-                         OptionsParse::Shell);
 }
 
 void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
@@ -3021,48 +3151,29 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
   bool debugOptions = !this->DebugCompileOptionsDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "COMPILE_OPTIONS") != debugProperties.end();
+    cmContains(debugProperties, "COMPILE_OPTIONS");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugCompileOptionsDone = true;
   }
 
-  processCompileOptions(this, this->CompileOptionsEntries, result,
-                        uniqueOptions, &dagChecker, config, debugOptions,
-                        language);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->CompileOptionsEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceCompileOptionsEntries;
+  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language,
+                      &dagChecker, entries);
 
-  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS",
-                      linkInterfaceCompileOptionsEntries);
+  processOptions(this, entries, result, uniqueOptions, debugOptions,
+                 "compile options", OptionsParse::Shell);
 
-  processCompileOptions(this, linkInterfaceCompileOptionsEntries, result,
-                        uniqueOptions, &dagChecker, config, debugOptions,
-                        language);
-
-  cmDeleteAll(linkInterfaceCompileOptionsEntries);
   return result;
 }
 
-static void processCompileFeatures(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions)
-{
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, debugOptions, "compile features",
-                         std::string(), OptionsParse::None);
-}
-
 void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
                                            const std::string& config) const
 {
@@ -3086,45 +3197,29 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
   bool debugFeatures = !this->DebugCompileFeaturesDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "COMPILE_FEATURES") != debugProperties.end();
+    cmContains(debugProperties, "COMPILE_FEATURES");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugCompileFeaturesDone = true;
   }
 
-  processCompileFeatures(this, this->CompileFeaturesEntries, result,
-                         uniqueFeatures, &dagChecker, config, debugFeatures);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, std::string(), &dagChecker,
+                                  this->CompileFeaturesEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceCompileFeaturesEntries;
   AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES",
-                      linkInterfaceCompileFeaturesEntries);
+                      std::string(), &dagChecker, entries);
 
-  processCompileFeatures(this, linkInterfaceCompileFeaturesEntries, result,
-                         uniqueFeatures, &dagChecker, config, debugFeatures);
+  processOptions(this, entries, result, uniqueFeatures, debugFeatures,
+                 "compile features", OptionsParse::None);
 
-  cmDeleteAll(linkInterfaceCompileFeaturesEntries);
   return result;
 }
 
-static void processCompileDefinitions(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions, std::string const& language)
-{
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, debugOptions, "compile definitions", language,
-                         OptionsParse::None);
-}
-
 void cmGeneratorTarget::GetCompileDefinitions(
   std::vector<std::string>& result, const std::string& config,
   const std::string& language) const
@@ -3150,25 +3245,23 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
   bool debugDefines = !this->DebugCompileDefinitionsDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "COMPILE_DEFINITIONS") != debugProperties.end();
+    cmContains(debugProperties, "COMPILE_DEFINITIONS");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugCompileDefinitionsDone = true;
   }
 
-  processCompileDefinitions(this, this->CompileDefinitionsEntries, list,
-                            uniqueOptions, &dagChecker, config, debugDefines,
-                            language);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->CompileDefinitionsEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceCompileDefinitionsEntries;
-  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS",
-                      linkInterfaceCompileDefinitionsEntries);
+  AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language,
+                      &dagChecker, entries);
+
   if (!config.empty()) {
     std::string configPropName =
       "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
@@ -3176,15 +3269,16 @@
     if (configProp) {
       switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
         case cmPolicies::WARN: {
-          std::ostringstream e;
-          e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0043);
-          this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
-                                             e.str());
+          this->LocalGenerator->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmPolicies::GetPolicyWarning(cmPolicies::CMP0043));
           CM_FALLTHROUGH;
         }
         case cmPolicies::OLD: {
-          linkInterfaceCompileDefinitionsEntries.push_back(
+          std::unique_ptr<TargetPropertyEntry> entry(
             CreateTargetPropertyEntry(configProp));
+          entries.emplace_back(EvaluateTargetPropertyEntry(
+            this, config, language, &dagChecker, entry.get()));
         } break;
         case cmPolicies::NEW:
         case cmPolicies::REQUIRED_ALWAYS:
@@ -3194,27 +3288,288 @@
     }
   }
 
-  processCompileDefinitions(this, linkInterfaceCompileDefinitionsEntries, list,
-                            uniqueOptions, &dagChecker, config, debugDefines,
-                            language);
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "compile definitions", OptionsParse::None);
 
-  cmDeleteAll(linkInterfaceCompileDefinitionsEntries);
   return list;
 }
 
-namespace {
-void processLinkOptions(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugOptions, std::string const& language)
+std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
+  const std::string& config, const std::string& language) const
 {
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, debugOptions, "link options", language,
-                         OptionsParse::Shell);
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
+                                             nullptr, nullptr);
+
+  std::vector<std::string> debugProperties;
+  const char* debugProp =
+    this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
+  if (debugProp) {
+    cmExpandList(debugProp, debugProperties);
+  }
+
+  bool debugDefines = !this->DebugPrecompileHeadersDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "PRECOMPILE_HEADERS") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugPrecompileHeadersDone = true;
+  }
+
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->PrecompileHeadersEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
+                      &dagChecker, entries);
+
+  std::vector<BT<std::string>> list;
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "precompile headers", OptionsParse::None);
+
+  return list;
 }
+
+std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchHeaders.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::vector<BT<std::string>> headers =
+      this->GetPrecompileHeaders(config, language);
+    if (headers.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+
+    const cmGeneratorTarget* generatorTarget = this;
+    const char* pchReuseFrom =
+      generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+    if (pchReuseFrom) {
+      generatorTarget =
+        this->GetGlobalGenerator()->FindGeneratorTarget(pchReuseFrom);
+    }
+
+    if (this->GetGlobalGenerator()->IsMultiConfig()) {
+      filename = cmStrCat(
+        generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), "/");
+    } else {
+      // For GCC we need to have the header file .h[xx]
+      // next to the .h[xx].gch file
+      filename = generatorTarget->ObjectDirectory;
+    }
+
+    filename = cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(),
+                        ".dir/cmake_pch", ((language == "C") ? ".h" : ".hxx"));
+
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    if (!pchReuseFrom) {
+      auto pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
+      auto pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
+
+      {
+        cmGeneratedFileStream file(
+          filename_tmp, false,
+          this->GetGlobalGenerator()->GetMakefileEncoding());
+        file << "/* generated by CMake */\n\n";
+        if (pchPrologue) {
+          file << pchPrologue << "\n";
+        }
+        if (this->GetGlobalGenerator()->IsXcode()) {
+          file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+        }
+        if (language == "CXX") {
+          file << "#ifdef __cplusplus\n";
+        }
+        for (auto const& header_bt : headers) {
+          if (header_bt.Value.empty()) {
+            continue;
+          }
+          if (header_bt.Value[0] == '<' || header_bt.Value[0] == '\"') {
+            file << "#include " << header_bt.Value << "\n";
+          } else {
+            file << "#include \"" << header_bt.Value << "\"\n";
+          }
+        }
+        if (language == "CXX") {
+          file << "#endif // __cplusplus\n";
+        }
+        if (this->GetGlobalGenerator()->IsXcode()) {
+          file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+        }
+        if (pchEpilogue) {
+          file << pchEpilogue << "\n";
+        }
+      }
+      cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
+    }
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchSource(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchSources.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    if (pchHeader.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+
+    const cmGeneratorTarget* generatorTarget = this;
+    const char* pchReuseFrom =
+      generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+    if (pchReuseFrom) {
+      generatorTarget =
+        this->GetGlobalGenerator()->FindGeneratorTarget(pchReuseFrom);
+    }
+
+    filename =
+      cmStrCat(generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(),
+               "/CMakeFiles/", generatorTarget->GetName(), ".dir/cmake_pch");
+
+    // For GCC the source extension will be tranformed into .h[xx].gch
+    if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      filename += ((language == "C") ? ".h.c" : ".hxx.cxx");
+    } else {
+      filename += ((language == "C") ? ".c" : ".cxx");
+    }
+
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    if (!pchReuseFrom) {
+      {
+        cmGeneratedFileStream file(filename_tmp);
+        file << "/* generated by CMake */\n";
+      }
+      cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
+    }
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchFileObject(const std::string& config,
+                                                const std::string& language)
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchObjectFiles.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::string pchSource = this->GetPchSource(config, language);
+    if (pchSource.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+
+    this->AddSource(pchSource, true);
+
+    auto pchSf = this->Makefile->GetOrCreateSource(
+      pchSource, false, cmSourceFileLocationKind::Known);
+
+    filename = cmStrCat(this->ObjectDirectory, this->GetObjectName(pchSf));
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchFile(const std::string& config,
+                                          const std::string& language)
+{
+  const auto inserted =
+    this->PchFiles.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& pchFile = inserted.first->second;
+
+    const std::string pchExtension =
+      this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+    if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      auto replaceExtension = [](const std::string& str,
+                                 const std::string& ext) -> std::string {
+        auto dot_pos = str.rfind('.');
+        std::string result;
+        if (dot_pos != std::string::npos) {
+          result = str.substr(0, dot_pos);
+        }
+        result += ext;
+        return result;
+      };
+
+      cmGeneratorTarget* generatorTarget = this;
+      const char* pchReuseFrom =
+        generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+      if (pchReuseFrom) {
+        generatorTarget =
+          this->GetGlobalGenerator()->FindGeneratorTarget(pchReuseFrom);
+      }
+
+      const std::string pchFileObject =
+        generatorTarget->GetPchFileObject(config, language);
+      if (!pchExtension.empty()) {
+        pchFile = replaceExtension(pchFileObject, pchExtension);
+      }
+    } else {
+      pchFile = this->GetPchHeader(config, language);
+      pchFile += pchExtension;
+    }
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchCreateCompileOptions(
+  const std::string& config, const std::string& language)
+{
+  const auto inserted = this->PchCreateCompileOptions.insert(
+    std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& createOptionList = inserted.first->second;
+
+    const std::string createOptVar =
+      cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH");
+    createOptionList = this->Makefile->GetSafeDefinition(createOptVar);
+
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    const std::string pchFile = this->GetPchFile(config, language);
+
+    cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", pchHeader);
+    cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile);
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchUseCompileOptions(
+  const std::string& config, const std::string& language)
+{
+  const auto inserted =
+    this->PchUseCompileOptions.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& useOptionList = inserted.first->second;
+
+    const std::string useOptVar =
+      cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_USE_PCH");
+    useOptionList = this->Makefile->GetSafeDefinition(useOptVar);
+
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    const std::string pchFile = this->GetPchFile(config, language);
+
+    cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader);
+    cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile);
+  }
+  return inserted.first->second;
 }
 
 void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
@@ -3241,38 +3596,31 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
-  bool debugOptions = !this->DebugLinkOptionsDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "LINK_OPTIONS") != debugProperties.end();
+  bool debugOptions =
+    !this->DebugLinkOptionsDone && cmContains(debugProperties, "LINK_OPTIONS");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugLinkOptionsDone = true;
   }
 
-  processLinkOptions(this, this->LinkOptionsEntries, result, uniqueOptions,
-                     &dagChecker, config, debugOptions, language);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->LinkOptionsEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceLinkOptionsEntries;
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
+                      &dagChecker, entries);
 
-  AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS",
-                      linkInterfaceLinkOptionsEntries);
-
-  processLinkOptions(this, linkInterfaceLinkOptionsEntries, result,
-                     uniqueOptions, &dagChecker, config, debugOptions,
-                     language);
-
-  cmDeleteAll(linkInterfaceLinkOptionsEntries);
+  processOptions(this, entries, result, uniqueOptions, debugOptions,
+                 "link options", OptionsParse::Shell);
 
   // Last step: replace "LINKER:" prefixed elements by
   // actual linker wrapper
   const std::string wrapper(this->Makefile->GetSafeDefinition(
     "CMAKE_" + language + "_LINKER_WRAPPER_FLAG"));
-  std::vector<std::string> wrapperFlag;
-  cmSystemTools::ExpandListArgument(wrapper, wrapperFlag);
+  std::vector<std::string> wrapperFlag = cmExpandedList(wrapper);
   const std::string wrapperSep(this->Makefile->GetSafeDefinition(
     "CMAKE_" + language + "_LINKER_WRAPPER_FLAG_SEP"));
   bool concatFlagAndArgs = true;
@@ -3300,8 +3648,7 @@
       cmSystemTools::ParseUnixCommandLine(
         value.c_str() + LINKER_SHELL.length(), linkerOptions);
     } else {
-      linkerOptions =
-        cmSystemTools::tokenize(value.substr(LINKER.length()), ",");
+      linkerOptions = cmTokenize(value.substr(LINKER.length()), ",");
     }
 
     if (linkerOptions.empty() ||
@@ -3371,21 +3718,6 @@
   return result;
 }
 
-namespace {
-void processStaticLibraryLinkOptions(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  std::string const& language)
-{
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, false, "static library link options",
-                         language, OptionsParse::Shell);
-}
-}
-
 void cmGeneratorTarget::GetStaticLibraryLinkOptions(
   std::vector<std::string>& result, const std::string& config,
   const std::string& language) const
@@ -3402,47 +3734,40 @@
   std::string const& config, std::string const& language) const
 {
   std::vector<BT<std::string>> result;
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*> entries;
   std::unordered_set<std::string> uniqueOptions;
 
   cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS",
                                              nullptr, nullptr);
 
+  std::vector<EvaluatedTargetPropertyEntry> entries;
   if (const char* linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
-    std::vector<std::string> options;
-    cmSystemTools::ExpandListArgument(linkOptions, options);
+    std::vector<std::string> options = cmExpandedList(linkOptions);
     for (const auto& option : options) {
-      entries.push_back(CreateTargetPropertyEntry(option));
+      std::unique_ptr<TargetPropertyEntry> entry(
+        CreateTargetPropertyEntry(option));
+      entries.emplace_back(EvaluateTargetPropertyEntry(
+        this, config, language, &dagChecker, entry.get()));
     }
   }
-  processStaticLibraryLinkOptions(this, entries, result, uniqueOptions,
-                                  &dagChecker, config, language);
+  processOptions(this, entries, result, uniqueOptions, false,
+                 "static library link options", OptionsParse::Shell);
 
-  cmDeleteAll(entries);
   return result;
 }
 
 namespace {
-void processLinkDirectories(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& directories,
-  std::unordered_set<std::string>& uniqueDirectories,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  bool debugDirectories, std::string const& language)
+void processLinkDirectories(cmGeneratorTarget const* tgt,
+                            std::vector<EvaluatedTargetPropertyEntry>& entries,
+                            std::vector<BT<std::string>>& directories,
+                            std::unordered_set<std::string>& uniqueDirectories,
+                            bool debugDirectories)
 {
-  for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
-    cmLinkImplItem const& item = entry->LinkImplItem;
+  for (EvaluatedTargetPropertyEntry& entry : entries) {
+    cmLinkImplItem const& item = entry.LinkImplItem;
     std::string const& targetName = item.AsStr();
 
-    std::vector<std::string> entryDirectories;
-    cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(),
-                                                      config, false, tgt,
-                                                      dagChecker, language),
-                                      entryDirectories);
-
     std::string usedDirectories;
-    for (std::string& entryDirectory : entryDirectories) {
+    for (std::string& entryDirectory : entry.Values) {
       if (!cmSystemTools::FileIsFullPath(entryDirectory)) {
         std::ostringstream e;
         bool noMessage = false;
@@ -3484,7 +3809,7 @@
       // in case projects set the LINK_DIRECTORIES property directly.
       cmSystemTools::ConvertToUnixSlashes(entryDirectory);
       if (uniqueDirectories.insert(entryDirectory).second) {
-        directories.emplace_back(entryDirectory);
+        directories.emplace_back(entryDirectory, entry.Backtrace);
         if (debugDirectories) {
           usedDirectories += " * " + entryDirectory + "\n";
         }
@@ -3495,7 +3820,7 @@
         MessageType::LOG,
         std::string("Used link directories for target ") + tgt->GetName() +
           ":\n" + usedDirectories,
-        entry->GetBacktrace());
+        entry.Backtrace);
     }
   }
 }
@@ -3526,50 +3851,29 @@
   const char* debugProp =
     this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
   bool debugDirectories = !this->DebugLinkDirectoriesDone &&
-    std::find(debugProperties.begin(), debugProperties.end(),
-              "LINK_DIRECTORIES") != debugProperties.end();
+    cmContains(debugProperties, "LINK_DIRECTORIES");
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugLinkDirectoriesDone = true;
   }
 
-  processLinkDirectories(this, this->LinkDirectoriesEntries, result,
-                         uniqueDirectories, &dagChecker, config,
-                         debugDirectories, language);
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->LinkDirectoriesEntries);
 
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
-    linkInterfaceLinkDirectoriesEntries;
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language,
+                      &dagChecker, entries);
 
-  AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES",
-                      linkInterfaceLinkDirectoriesEntries);
+  processLinkDirectories(this, entries, result, uniqueDirectories,
+                         debugDirectories);
 
-  processLinkDirectories(this, linkInterfaceLinkDirectoriesEntries, result,
-                         uniqueDirectories, &dagChecker, config,
-                         debugDirectories, language);
-
-  cmDeleteAll(linkInterfaceLinkDirectoriesEntries);
   return result;
 }
 
-namespace {
-void processLinkDepends(
-  cmGeneratorTarget const* tgt,
-  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
-  std::vector<BT<std::string>>& options,
-  std::unordered_set<std::string>& uniqueOptions,
-  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
-  std::string const& language)
-{
-  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
-                         config, false, "link depends", language,
-                         OptionsParse::None);
-}
-}
-
 void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
                                        const std::string& config,
                                        const std::string& language) const
@@ -3585,24 +3889,26 @@
   std::string const& config, std::string const& language) const
 {
   std::vector<BT<std::string>> result;
-  std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkDependsEntries;
   std::unordered_set<std::string> uniqueOptions;
   cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
                                              nullptr);
 
+  std::vector<EvaluatedTargetPropertyEntry> entries;
   if (const char* linkDepends = this->GetProperty("LINK_DEPENDS")) {
-    std::vector<std::string> depends;
-    cmSystemTools::ExpandListArgument(linkDepends, depends);
+    std::vector<std::string> depends = cmExpandedList(linkDepends);
     for (const auto& depend : depends) {
-      linkDependsEntries.push_back(CreateTargetPropertyEntry(depend));
+      std::unique_ptr<TargetPropertyEntry> entry(
+        CreateTargetPropertyEntry(depend));
+      entries.emplace_back(EvaluateTargetPropertyEntry(
+        this, config, language, &dagChecker, entry.get()));
     }
   }
-  AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS",
-                      linkDependsEntries);
-  processLinkDepends(this, linkDependsEntries, result, uniqueOptions,
-                     &dagChecker, config, language);
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
+                      &dagChecker, entries);
 
-  cmDeleteAll(linkDependsEntries);
+  processOptions(this, entries, result, uniqueOptions, false, "link depends",
+                 OptionsParse::None);
+
   return result;
 }
 
@@ -3632,33 +3938,25 @@
   // Add each name.
   std::string f;
   if (!targetNames.Output.empty()) {
-    f = dir;
-    f += "/";
-    f += targetNames.Output;
+    f = cmStrCat(dir, '/', targetNames.Output);
     gg->AddToManifest(f);
   }
   if (!targetNames.SharedObject.empty()) {
-    f = dir;
-    f += "/";
-    f += targetNames.SharedObject;
+    f = cmStrCat(dir, '/', targetNames.SharedObject);
     gg->AddToManifest(f);
   }
   if (!targetNames.Real.empty()) {
-    f = dir;
-    f += "/";
-    f += targetNames.Real;
+    f = cmStrCat(dir, '/', targetNames.Real);
     gg->AddToManifest(f);
   }
   if (!targetNames.PDB.empty()) {
-    f = dir;
-    f += "/";
-    f += targetNames.PDB;
+    f = cmStrCat(dir, '/', targetNames.PDB);
     gg->AddToManifest(f);
   }
   if (!targetNames.ImportLibrary.empty()) {
-    f = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
-    f += "/";
-    f += targetNames.ImportLibrary;
+    f =
+      cmStrCat(this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact),
+               '/', targetNames.ImportLibrary);
     gg->AddToManifest(f);
   }
 }
@@ -3698,11 +3996,10 @@
   const std::string& config, cmStateEnums::ArtifactType artifact,
   bool realname) const
 {
-  std::string fpath = this->GetDirectory(config, artifact);
-  fpath += "/";
+  std::string fpath = cmStrCat(this->GetDirectory(config, artifact), '/');
   if (this->IsAppBundleOnApple()) {
-    fpath = this->BuildBundleDirectory(fpath, config, FullLevel);
-    fpath += "/";
+    fpath =
+      cmStrCat(this->BuildBundleDirectory(fpath, config, FullLevel), '/');
   }
 
   // Add the full name of the target.
@@ -3729,8 +4026,8 @@
   // TODO: Split cmTarget into a class hierarchy to get compile-time
   // enforcement of the limited imported target API.
   if (this->IsImported()) {
-    std::string msg = "NormalGetRealName called on imported target: ";
-    msg += this->GetName();
+    std::string msg = cmStrCat("NormalGetRealName called on imported target: ",
+                               this->GetName());
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
   }
 
@@ -3751,8 +4048,8 @@
   // TODO: Split cmTarget into a class hierarchy to get compile-time
   // enforcement of the limited imported target API.
   if (this->IsImported()) {
-    std::string msg = "GetLibraryNames called on imported target: ";
-    msg += this->GetName();
+    std::string msg =
+      cmStrCat("GetLibraryNames called on imported target: ", this->GetName());
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
   }
 
@@ -3828,8 +4125,8 @@
   // TODO: Split cmTarget into a class hierarchy to get compile-time
   // enforcement of the limited imported target API.
   if (this->IsImported()) {
-    std::string msg = "GetExecutableNames called on imported target: ";
-    msg += this->GetName();
+    std::string msg = cmStrCat(
+      "GetExecutableNames called on imported target: ", this->GetName());
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
   }
 
@@ -3924,8 +4221,7 @@
 
   // Return an empty name for the import library if this platform
   // does not support import libraries.
-  if (isImportedLibraryArtifact &&
-      !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
+  if (isImportedLibraryArtifact && !this->NeedImportLibraryName(config)) {
     outPrefix.clear();
     outBase.clear();
     outSuffix.clear();
@@ -3934,8 +4230,8 @@
 
   // retrieve prefix and suffix
   std::string ll = this->GetLinkerLanguage(config);
-  const char* targetPrefix = this->GetFilePrefixInternal(artifact, ll);
-  const char* targetSuffix = this->GetFileSuffixInternal(artifact, ll);
+  const char* targetPrefix = this->GetFilePrefixInternal(config, artifact, ll);
+  const char* targetSuffix = this->GetFileSuffixInternal(config, artifact, ll);
 
   // The implib option is only allowed for shared libraries, module
   // libraries, and executables.
@@ -3951,15 +4247,14 @@
   // frameworks have directory prefix but no suffix
   std::string fw_prefix;
   if (this->IsFrameworkOnApple()) {
-    fw_prefix = this->GetFrameworkDirectory(config, ContentLevel);
-    fw_prefix += "/";
+    fw_prefix =
+      cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
     targetPrefix = fw_prefix.c_str();
     targetSuffix = nullptr;
   }
 
   if (this->IsCFBundleOnApple()) {
-    fw_prefix = this->GetCFBundleDirectory(config, FullLevel);
-    fw_prefix += "/";
+    fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/');
     targetPrefix = fw_prefix.c_str();
     targetSuffix = nullptr;
   }
@@ -4080,8 +4375,7 @@
 
   for (cmSourceFile const* src : objectSources) {
     // Find the object file name corresponding to this source file.
-    std::map<cmSourceFile const*, std::string>::const_iterator map_it =
-      mapping.find(src);
+    auto map_it = mapping.find(src);
     // It must exist because we populated the mapping just above.
     assert(!map_it->second.empty());
     objects.push_back(map_it->second);
@@ -4105,8 +4399,7 @@
 {
   struct SourceFileFlags flags;
   this->ConstructSourceFileFlags();
-  std::map<cmSourceFile const*, SourceFileFlags>::iterator si =
-    this->SourceFlagsMap.find(sf);
+  auto si = this->SourceFlagsMap.find(sf);
   if (si != this->SourceFlagsMap.end()) {
     flags = si->second;
   } else {
@@ -4121,7 +4414,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/");
@@ -4143,8 +4436,7 @@
 
   // Process public headers to mark the source files.
   if (const char* files = this->GetProperty("PUBLIC_HEADER")) {
-    std::vector<std::string> relFiles;
-    cmSystemTools::ExpandListArgument(files, relFiles);
+    std::vector<std::string> relFiles = cmExpandedList(files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
         SourceFileFlags& flags = this->SourceFlagsMap[sf];
@@ -4157,8 +4449,7 @@
   // Process private headers after public headers so that they take
   // precedence if a file is listed in both.
   if (const char* files = this->GetProperty("PRIVATE_HEADER")) {
-    std::vector<std::string> relFiles;
-    cmSystemTools::ExpandListArgument(files, relFiles);
+    std::vector<std::string> relFiles = cmExpandedList(files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
         SourceFileFlags& flags = this->SourceFlagsMap[sf];
@@ -4170,8 +4461,7 @@
 
   // Mark sources listed as resources.
   if (const char* files = this->GetProperty("RESOURCE")) {
-    std::vector<std::string> relFiles;
-    cmSystemTools::ExpandListArgument(files, relFiles);
+    std::vector<std::string> relFiles = cmExpandedList(files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
         SourceFileFlags& flags = this->SourceFlagsMap[sf];
@@ -4200,7 +4490,7 @@
 #define CM_READ_COMPATIBLE_INTERFACE(X, x)                                    \
   if (const char* prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) {       \
     std::vector<std::string> props;                                           \
-    cmSystemTools::ExpandListArgument(prop, props);                           \
+    cmExpandList(prop, props);                                                \
     compat.Props##x.insert(props.begin(), props.end());                       \
   }
       CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool)
@@ -4313,10 +4603,9 @@
     return;
   }
 
-  std::vector<std::string> props;
-  cmSystemTools::ExpandListArgument(prop, props);
-  std::string pdir = cmSystemTools::GetCMakeRoot();
-  pdir += "/Help/prop_tgt/";
+  std::vector<std::string> props = cmExpandedList(prop);
+  std::string pdir =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/");
 
   for (std::string const& p : props) {
     std::string pname = cmSystemTools::HelpFileName(p);
@@ -4343,8 +4632,9 @@
   }
 }
 
-static std::string intersect(const std::set<std::string>& s1,
-                             const std::set<std::string>& s2)
+namespace {
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2)
 {
   std::set<std::string> intersect;
   std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
@@ -4355,9 +4645,9 @@
   return "";
 }
 
-static std::string intersect(const std::set<std::string>& s1,
-                             const std::set<std::string>& s2,
-                             const std::set<std::string>& s3)
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2,
+                      const std::set<std::string>& s3)
 {
   std::string result;
   result = intersect(s1, s2);
@@ -4371,10 +4661,10 @@
   return intersect(s2, s3);
 }
 
-static std::string intersect(const std::set<std::string>& s1,
-                             const std::set<std::string>& s2,
-                             const std::set<std::string>& s3,
-                             const std::set<std::string>& s4)
+std::string intersect(const std::set<std::string>& s1,
+                      const std::set<std::string>& s2,
+                      const std::set<std::string>& s3,
+                      const std::set<std::string>& s4)
 {
   std::string result;
   result = intersect(s1, s2);
@@ -4391,6 +4681,7 @@
   }
   return intersect(s2, s3, s4);
 }
+}
 
 void cmGeneratorTarget::CheckPropertyCompatibility(
   cmComputeLinkInformation* info, const std::string& config) const
@@ -4442,7 +4733,7 @@
   if (!prop.empty()) {
     // Use a sorted std::vector to keep the error message sorted.
     std::vector<std::string> props;
-    std::set<std::string>::const_iterator i = emittedBools.find(prop);
+    auto i = emittedBools.find(prop);
     if (i != emittedBools.end()) {
       props.push_back(strBool);
     }
@@ -4460,8 +4751,8 @@
     }
     std::sort(props.begin(), props.end());
 
-    std::string propsString = cmJoin(cmMakeRange(props).retreat(1), ", ");
-    propsString += " and the " + props.back();
+    std::string propsString = cmStrCat(
+      cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back());
 
     std::ostringstream e;
     e << "Property \"" << prop << "\" appears in both the " << propsString
@@ -4542,7 +4833,7 @@
   }
 
   const char* value = tgt->GetProperty(prop);
-  return cmSystemTools::IsOn(genexInterpreter->Evaluate(value, prop));
+  return cmIsOn(genexInterpreter->Evaluate(value, prop));
 }
 
 template <>
@@ -4600,21 +4891,21 @@
 std::pair<bool, bool> consistentProperty(bool lhs, bool rhs,
                                          CompatibleType /*unused*/)
 {
-  return std::make_pair(lhs == rhs, lhs);
+  return { lhs == rhs, lhs };
 }
 
 std::pair<bool, const char*> consistentStringProperty(const char* lhs,
                                                       const char* rhs)
 {
   const bool b = strcmp(lhs, rhs) == 0;
-  return std::make_pair(b, b ? lhs : nullptr);
+  return { b, b ? lhs : nullptr };
 }
 
 std::pair<bool, std::string> consistentStringProperty(const std::string& lhs,
                                                       const std::string& rhs)
 {
   const bool b = lhs == rhs;
-  return std::make_pair(b, b ? lhs : valueAsString(nullptr));
+  return { b, b ? lhs : valueAsString(nullptr) };
 }
 
 std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
@@ -4623,22 +4914,21 @@
 {
   char* pEnd;
 
-  const char* const null_ptr = nullptr;
-
   long lnum = strtol(lhs, &pEnd, 0);
   if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
-    return std::pair<bool, const char*>(false, null_ptr);
+    return { false, nullptr };
   }
 
   long rnum = strtol(rhs, &pEnd, 0);
   if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
-    return std::pair<bool, const char*>(false, null_ptr);
+    return { false, nullptr };
   }
 
   if (t == NumberMaxType) {
-    return std::make_pair(true, std::max(lnum, rnum) == lnum ? lhs : rhs);
+    return { true, std::max(lnum, rnum) == lnum ? lhs : rhs };
   }
-  return std::make_pair(true, std::min(lnum, rnum) == lnum ? lhs : rhs);
+
+  return { true, std::min(lnum, rnum) == lnum ? lhs : rhs };
 }
 
 template <>
@@ -4647,21 +4937,19 @@
                                                 CompatibleType t)
 {
   if (!lhs && !rhs) {
-    return std::make_pair(true, lhs);
+    return { true, lhs };
   }
   if (!lhs) {
-    return std::make_pair(true, rhs);
+    return { true, rhs };
   }
   if (!rhs) {
-    return std::make_pair(true, lhs);
+    return { true, lhs };
   }
 
-  const char* const null_ptr = nullptr;
-
   switch (t) {
     case BoolType: {
-      bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs);
-      return std::make_pair(same, same ? lhs : nullptr);
+      bool same = cmIsOn(lhs) == cmIsOn(rhs);
+      return { same, same ? lhs : nullptr };
     }
     case StringType:
       return consistentStringProperty(lhs, rhs);
@@ -4670,7 +4958,7 @@
       return consistentNumberProperty(lhs, rhs, t);
   }
   assert(false && "Unreachable!");
-  return std::pair<bool, const char*>(false, null_ptr);
+  return { false, nullptr };
 }
 
 std::pair<bool, std::string> consistentProperty(const std::string& lhs,
@@ -4680,31 +4968,31 @@
   const std::string null_ptr = valueAsString(nullptr);
 
   if (lhs == null_ptr && rhs == null_ptr) {
-    return std::make_pair(true, lhs);
+    return { true, lhs };
   }
   if (lhs == null_ptr) {
-    return std::make_pair(true, rhs);
+    return { true, rhs };
   }
   if (rhs == null_ptr) {
-    return std::make_pair(true, lhs);
+    return { true, lhs };
   }
 
   switch (t) {
     case BoolType: {
-      bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs);
-      return std::make_pair(same, same ? lhs : null_ptr);
+      bool same = cmIsOn(lhs) == cmIsOn(rhs);
+      return { same, same ? lhs : null_ptr };
     }
     case StringType:
       return consistentStringProperty(lhs, rhs);
     case NumberMinType:
     case NumberMaxType: {
       auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
-      return std::make_pair(
-        value.first, value.first ? std::string(value.second) : null_ptr);
+      return { value.first,
+               value.first ? std::string(value.second) : null_ptr };
     }
   }
   assert(false && "Unreachable!");
-  return std::pair<bool, std::string>(false, null_ptr);
+  return { false, null_ptr };
 }
 
 template <typename PropertyType>
@@ -4718,9 +5006,7 @@
   PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
 
   std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
-  const bool explicitlySet =
-    std::find(headPropKeys.begin(), headPropKeys.end(), p) !=
-    headPropKeys.end();
+  const bool explicitlySet = cmContains(headPropKeys, p);
 
   const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p);
   assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet));
@@ -4733,8 +5019,7 @@
   }
   bool propInitialized = explicitlySet;
 
-  std::string report = " * Target \"";
-  report += tgt->GetName();
+  std::string report = cmStrCat(" * Target \"", tgt->GetName());
   if (explicitlySet) {
     report += "\" has property content \"";
     report += valueAsString<PropertyType>(propContent);
@@ -4760,8 +5045,7 @@
 
     std::vector<std::string> propKeys = theTarget->GetPropertyKeys();
 
-    const bool ifaceIsSet = std::find(propKeys.begin(), propKeys.end(),
-                                      interfaceProperty) != propKeys.end();
+    const bool ifaceIsSet = cmContains(propKeys, interfaceProperty);
     PropertyType ifacePropContent = getTypedProperty<PropertyType>(
       theTarget, interfaceProperty, genexInterpreter.get());
 
@@ -4894,7 +5178,7 @@
 {
   // Lookup any existing information for this configuration.
   std::string key(cmSystemTools::UpperCase(config));
-  cmTargetLinkInformationMap::iterator i = this->LinkInformation.find(key);
+  auto i = this->LinkInformation.find(key);
   if (i == this->LinkInformation.end()) {
     // Compute information for this configuration.
     cmComputeLinkInformation* info =
@@ -4991,9 +5275,8 @@
       mod_dir = target_mod_dir;
     } else {
       // Interpret relative to the current output directory.
-      mod_dir = this->LocalGenerator->GetCurrentBinaryDirectory();
-      mod_dir += "/";
-      mod_dir += target_mod_dir;
+      mod_dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
+                         '/', target_mod_dir);
     }
 
     // Make sure the module output directory exists.
@@ -5032,13 +5315,7 @@
 
 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
 {
-  cmPropertyMap const& propsObject = this->Target->GetProperties();
-  std::vector<std::string> props;
-  props.reserve(propsObject.size());
-  for (auto const& it : propsObject) {
-    props.push_back(it.first);
-  }
-  return props;
+  return this->Target->GetProperties().GetKeys();
 }
 
 void cmGeneratorTarget::ReportPropertyOrigin(
@@ -5049,12 +5326,11 @@
   const char* debugProp = this->Target->GetMakefile()->GetDefinition(
     "CMAKE_DEBUG_TARGET_PROPERTIES");
   if (debugProp) {
-    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+    cmExpandList(debugProp, debugProperties);
   }
 
-  bool debugOrigin = !this->DebugCompatiblePropertiesDone[p] &&
-    std::find(debugProperties.begin(), debugProperties.end(), p) !=
-      debugProperties.end();
+  bool debugOrigin =
+    !this->DebugCompatiblePropertiesDone[p] && cmContains(debugProperties, p);
 
   if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
     this->DebugCompatiblePropertiesDone[p] = true;
@@ -5063,12 +5339,9 @@
     return;
   }
 
-  std::string areport = compatibilityType;
-  areport += std::string(" of property \"") + p + "\" for target \"";
-  areport += std::string(this->GetName());
-  areport += "\" (result: \"";
-  areport += result;
-  areport += "\"):\n" + report;
+  std::string areport =
+    cmStrCat(compatibilityType, " of property \"", p, "\" for target \"",
+             this->GetName(), "\" (result: \"", result, "\"):\n", report);
 
   this->LocalGenerator->GetCMakeInstance()->IssueMessage(MessageType::LOG,
                                                          areport);
@@ -5101,10 +5374,9 @@
   }
   std::vector<std::string> libs;
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
-  cmSystemTools::ExpandListArgument(cge->Evaluate(this->LocalGenerator, config,
-                                                  false, headTarget, this,
-                                                  &dagChecker),
-                                    libs);
+  cmExpandList(
+    cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this),
+    libs);
   this->LookupLinkItems(libs, cge->GetBacktrace(), items);
   hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition();
 }
@@ -5152,7 +5424,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) {
@@ -5209,8 +5481,7 @@
 
     // How many repetitions are needed if this library has cyclic
     // dependencies?
-    std::string propName = "LINK_INTERFACE_MULTIPLICITY";
-    propName += suffix;
+    std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix);
     if (const char* config_reps = this->GetProperty(propName)) {
       sscanf(config_reps, "%u", &iface.Multiplicity);
     } else if (const char* reps =
@@ -5296,10 +5567,9 @@
 
   // Only libraries and executables have well-defined output files.
   if (!this->HaveWellDefinedOutputFiles()) {
-    std::string msg = "cmGeneratorTarget::GetOutputInfo called for ";
-    msg += this->GetName();
-    msg += " which has type ";
-    msg += cmState::GetTargetTypeName(this->GetType());
+    std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ",
+                               this->GetName(), " which has type ",
+                               cmState::GetTargetTypeName(this->GetType()));
     this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
     return nullptr;
   }
@@ -5309,7 +5579,7 @@
   if (!config.empty()) {
     config_upper = cmSystemTools::UpperCase(config);
   }
-  OutputInfoMapType::iterator i = this->OutputInfoMap.find(config_upper);
+  auto i = this->OutputInfoMap.find(config_upper);
   if (i == this->OutputInfoMap.end()) {
     // Add empty info in map to detect potential recursion.
     OutputInfo info;
@@ -5369,18 +5639,15 @@
   // Select an output directory.
   if (const char* config_outdir = this->GetProperty(configProp)) {
     // Use the user-specified per-configuration output directory.
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(config_outdir);
-    out = cge->Evaluate(this->LocalGenerator, config);
+    out = cmGeneratorExpression::Evaluate(config_outdir, this->LocalGenerator,
+                                          config);
 
     // Skip per-configuration subdirectory.
     conf.clear();
   } else if (const char* outdir = this->GetProperty(propertyName)) {
     // Use the user-specified output directory.
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir);
-    out = cge->Evaluate(this->LocalGenerator, config);
+    out =
+      cmGeneratorExpression::Evaluate(outdir, this->LocalGenerator, config);
 
     // Skip per-configuration subdirectory if the value contained a
     // generator expression.
@@ -5448,18 +5715,15 @@
   // Select an output directory.
   if (const char* config_outdir = this->GetProperty(configProp)) {
     // Use the user-specified per-configuration output directory.
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(config_outdir);
-    out = cge->Evaluate(this->LocalGenerator, config);
+    out = cmGeneratorExpression::Evaluate(config_outdir, this->LocalGenerator,
+                                          config);
 
     // Skip per-configuration subdirectory.
     conf.clear();
   } else if (const char* outdir = this->GetProperty(propertyName)) {
     // Use the user-specified output directory.
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(outdir);
-    out = cge->Evaluate(this->LocalGenerator, config);
+    out =
+      cmGeneratorExpression::Evaluate(outdir, this->LocalGenerator, config);
 
     // Skip per-configuration subdirectory if the value contained a
     // generator expression.
@@ -5485,13 +5749,40 @@
   return true;
 }
 
-bool cmGeneratorTarget::HaveInstallTreeRPATH() const
+bool cmGeneratorTarget::HaveInstallTreeRPATH(const std::string& config) const
 {
-  const char* install_rpath = this->GetProperty("INSTALL_RPATH");
-  return (install_rpath && *install_rpath) &&
+  std::string install_rpath;
+  this->GetInstallRPATH(config, install_rpath);
+  return !install_rpath.empty() &&
     !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH");
 }
 
+bool cmGeneratorTarget::GetBuildRPATH(const std::string& config,
+                                      std::string& rpath) const
+{
+  return this->GetRPATH(config, "BUILD_RPATH", rpath);
+}
+
+bool cmGeneratorTarget::GetInstallRPATH(const std::string& config,
+                                        std::string& rpath) const
+{
+  return this->GetRPATH(config, "INSTALL_RPATH", rpath);
+}
+
+bool cmGeneratorTarget::GetRPATH(const std::string& config,
+                                 const std::string& prop,
+                                 std::string& rpath) const
+{
+  const char* value = this->GetProperty(prop);
+  if (!value) {
+    return false;
+  }
+
+  rpath = cmGeneratorExpression::Evaluate(value, this->LocalGenerator, config);
+
+  return true;
+}
+
 void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
   const std::string& config, cmOptionalLinkInterface& iface,
   cmGeneratorTarget const* headTarget, bool usage_requirements_only) const
@@ -5508,8 +5799,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);
@@ -5519,8 +5811,7 @@
     // shared lib or executable.
 
     // Lookup the per-configuration property.
-    linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
-    linkIfaceProp += suffix;
+    linkIfaceProp = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix);
     explicitLibraries = this->GetProperty(linkIfaceProp);
 
     // If not set, try the generic property.
@@ -5564,15 +5855,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
@@ -5653,12 +5943,11 @@
   if (!iface.AllDone) {
     iface.AllDone = true;
     iface.Multiplicity = info->Multiplicity;
-    cmSystemTools::ExpandListArgument(info->Languages, iface.Languages);
+    cmExpandList(info->Languages, iface.Languages);
     this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
                           headTarget, usage_requirements_only, iface.Libraries,
                           iface.HadHeadSensitiveCondition);
-    std::vector<std::string> deps;
-    cmSystemTools::ExpandListArgument(info->SharedDeps, deps);
+    std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
     this->LookupLinkItems(deps, cmListFileBacktrace(), iface.SharedDeps);
   }
 
@@ -5682,7 +5971,7 @@
     config_upper = "NOCONFIG";
   }
 
-  ImportInfoMapType::const_iterator i = this->ImportInfoMap.find(config_upper);
+  auto i = this->ImportInfoMap.find(config_upper);
   if (i == this->ImportInfoMap.end()) {
     ImportInfo info;
     this->ComputeImportInfo(config_upper, info);
@@ -5727,8 +6016,7 @@
 
     if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
       if (!propertyLibs) {
-        linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
-        linkProp += suffix;
+        linkProp = cmStrCat("IMPORTED_LINK_INTERFACE_LIBRARIES", suffix);
         propertyLibs = this->GetProperty(linkProp);
       }
 
@@ -5756,8 +6044,7 @@
   if (loc) {
     info.Location = loc;
   } else {
-    std::string impProp = "IMPORTED_LOCATION";
-    impProp += suffix;
+    std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
     if (const char* config_location = this->GetProperty(impProp)) {
       info.Location = config_location;
     } else if (const char* location = this->GetProperty("IMPORTED_LOCATION")) {
@@ -5767,8 +6054,7 @@
 
   // Get the soname.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
-    std::string soProp = "IMPORTED_SONAME";
-    soProp += suffix;
+    std::string soProp = cmStrCat("IMPORTED_SONAME", suffix);
     if (const char* config_soname = this->GetProperty(soProp)) {
       info.SOName = config_soname;
     } else if (const char* soname = this->GetProperty("IMPORTED_SONAME")) {
@@ -5778,13 +6064,12 @@
 
   // Get the "no-soname" mark.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
-    std::string soProp = "IMPORTED_NO_SONAME";
-    soProp += suffix;
+    std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix);
     if (const char* config_no_soname = this->GetProperty(soProp)) {
-      info.NoSOName = cmSystemTools::IsOn(config_no_soname);
+      info.NoSOName = cmIsOn(config_no_soname);
     } else if (const char* no_soname =
                  this->GetProperty("IMPORTED_NO_SONAME")) {
-      info.NoSOName = cmSystemTools::IsOn(no_soname);
+      info.NoSOName = cmIsOn(no_soname);
     }
   }
 
@@ -5793,8 +6078,7 @@
     info.ImportLibrary = imp;
   } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
              this->IsExecutableWithExports()) {
-    std::string impProp = "IMPORTED_IMPLIB";
-    impProp += suffix;
+    std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
     if (const char* config_implib = this->GetProperty(impProp)) {
       info.ImportLibrary = config_implib;
     } else if (const char* implib = this->GetProperty("IMPORTED_IMPLIB")) {
@@ -5804,8 +6088,8 @@
 
   // Get the link dependencies.
   {
-    std::string linkProp = "IMPORTED_LINK_DEPENDENT_LIBRARIES";
-    linkProp += suffix;
+    std::string linkProp =
+      cmStrCat("IMPORTED_LINK_DEPENDENT_LIBRARIES", suffix);
     if (const char* config_libs = this->GetProperty(linkProp)) {
       info.SharedDeps = config_libs;
     } else if (const char* libs =
@@ -5816,8 +6100,8 @@
 
   // Get the link languages.
   if (this->LinkLanguagePropagatesToDependents()) {
-    std::string linkProp = "IMPORTED_LINK_INTERFACE_LANGUAGES";
-    linkProp += suffix;
+    std::string linkProp =
+      cmStrCat("IMPORTED_LINK_INTERFACE_LANGUAGES", suffix);
     if (const char* config_libs = this->GetProperty(linkProp)) {
       info.Languages = config_libs;
     } else if (const char* libs =
@@ -5838,8 +6122,8 @@
 
   // Get the cyclic repetition count.
   if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
-    std::string linkProp = "IMPORTED_LINK_INTERFACE_MULTIPLICITY";
-    linkProp += suffix;
+    std::string linkProp =
+      cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
     if (const char* config_reps = this->GetProperty(linkProp)) {
       sscanf(config_reps, "%u", &info.Multiplicity);
     } else if (const char* reps =
@@ -5888,13 +6172,10 @@
 bool cmGeneratorTarget::GetConfigCommonSourceFiles(
   std::vector<cmSourceFile*>& files) const
 {
-  std::vector<std::string> configs;
-  this->Makefile->GetConfigurations(configs);
-  if (configs.empty()) {
-    configs.emplace_back();
-  }
+  std::vector<std::string> const& configs =
+    this->Makefile->GetGeneratorConfigs();
 
-  std::vector<std::string>::const_iterator it = configs.begin();
+  auto it = configs.begin();
   const std::string& firstConfig = *it;
   this->GetSourceFilesWithoutObjectLibraries(files, firstConfig);
 
@@ -5906,7 +6187,7 @@
       const char* sep = "";
       for (cmSourceFile* f : files) {
         firstConfigFiles += sep;
-        firstConfigFiles += f->GetFullPath();
+        firstConfigFiles += f->ResolveFullPath();
         sep = "\n  ";
       }
 
@@ -5914,7 +6195,7 @@
       sep = "";
       for (cmSourceFile* f : configFiles) {
         thisConfigFiles += sep;
-        thisConfigFiles += f->GetFullPath();
+        thisConfigFiles += f->ResolveFullPath();
         sep = "\n  ";
       }
       std::ostringstream e;
@@ -5946,8 +6227,7 @@
   // behavior of CMP0024 and CMP0026 only.
   cmStringRange rng = this->Target->GetSourceEntries();
   for (std::string const& entry : rng) {
-    std::vector<std::string> files;
-    cmSystemTools::ExpandListArgument(entry, files);
+    std::vector<std::string> files = cmExpandedList(entry);
     for (std::string const& li : files) {
       if (cmHasLiteralPrefix(li, "$<TARGET_OBJECTS:") && li.back() == '>') {
         std::string objLibName = li.substr(17, li.size() - 18);
@@ -6020,7 +6300,7 @@
   std::vector<cmSourceFile*> sourceFiles;
   this->GetSourceFiles(sourceFiles, config);
   for (cmSourceFile* src : sourceFiles) {
-    const std::string& lang = src->GetLanguage();
+    const std::string& lang = src->GetOrDetermineLanguage();
     if (!lang.empty()) {
       languages.insert(lang);
     }
@@ -6091,7 +6371,8 @@
   if (this->GetPropertyAsBool("SKIP_BUILD_RPATH")) {
     return false;
   }
-  if (this->GetProperty("BUILD_RPATH")) {
+  std::string build_rpath;
+  if (this->GetBuildRPATH(config, build_rpath)) {
     return true;
   }
   if (cmLinkImplementationLibraries const* impl =
@@ -6138,8 +6419,7 @@
 bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
   const std::string& p) const
 {
-  return this->LinkImplicitNullProperties.find(p) !=
-    this->LinkImplicitNullProperties.end();
+  return cmContains(this->LinkImplicitNullProperties, p);
 }
 
 void cmGeneratorTarget::ComputeLinkImplementationLibraries(
@@ -6159,8 +6439,8 @@
     cmGeneratorExpression ge(*btIt);
     std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le);
     std::string const& evaluated =
-      cge->Evaluate(this->LocalGenerator, config, false, head, &dagChecker);
-    cmSystemTools::ExpandListArgument(evaluated, llibs);
+      cge->Evaluate(this->LocalGenerator, config, head, &dagChecker);
+    cmExpandList(evaluated, llibs);
     if (cge->GetHadHeadSensitiveCondition()) {
       impl.HadHeadSensitiveCondition = true;
     }
@@ -6317,8 +6597,8 @@
 {
   if (this->HasImplibGNUtoMS(config) && gnuName.size() > 6 &&
       gnuName.substr(gnuName.size() - 6) == ".dll.a") {
-    out = gnuName.substr(0, gnuName.size() - 6);
-    out += newExt ? newExt : ".lib";
+    out = cmStrCat(cm::string_view(gnuName).substr(0, gnuName.size() - 6),
+                   newExt ? newExt : ".lib");
     return true;
   }
   return false;
@@ -6337,15 +6617,24 @@
            this->IsExecutableWithExports()) &&
           // Assemblies which have only managed code do not have
           // import libraries.
-          this->GetManagedType(config) != ManagedType::Managed);
+          this->GetManagedType(config) != ManagedType::Managed) ||
+    (this->Target->IsAIX() && this->IsExecutableWithExports());
+}
+
+bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const
+{
+  return this->HasImportLibrary(config) ||
+    // On DLL platforms we always generate the import library name
+    // just in case the sources have export markup.
+    (this->IsDLLPlatform() &&
+     (this->GetType() == cmStateEnums::EXECUTABLE ||
+      this->GetType() == cmStateEnums::MODULE_LIBRARY));
 }
 
 std::string cmGeneratorTarget::GetSupportDirectory() const
 {
-  std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/CMakeFiles";
-  dir += "/";
-  dir += this->GetName();
+  std::string dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
+                             "/CMakeFiles/", this->GetName());
 #if defined(__VMS)
   dir += "_dir";
 #else
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 0e0ee6a..1f824b1 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -5,18 +5,19 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
+#include <map>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
 #include "cmLinkItem.h"
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 
-#include <map>
-#include <set>
-#include <stddef.h>
-#include <string>
-#include <utility>
-#include <vector>
-
 class cmComputeLinkInformation;
 class cmCustomCommand;
 class cmGlobalGenerator;
@@ -25,6 +26,9 @@
 class cmSourceFile;
 class cmTarget;
 
+struct cmGeneratorExpressionContext;
+struct cmGeneratorExpressionDAGChecker;
+
 class cmGeneratorTarget
 {
 public:
@@ -451,6 +455,22 @@
   std::vector<BT<std::string>> GetLinkDepends(
     std::string const& config, std::string const& language) const;
 
+  std::vector<BT<std::string>> GetPrecompileHeaders(
+    const std::string& config, const std::string& language) const;
+
+  std::string GetPchHeader(const std::string& config,
+                           const std::string& language) const;
+  std::string GetPchSource(const std::string& config,
+                           const std::string& language) const;
+  std::string GetPchFileObject(const std::string& config,
+                               const std::string& language);
+  std::string GetPchFile(const std::string& config,
+                         const std::string& language);
+  std::string GetPchCreateCompileOptions(const std::string& config,
+                                         const std::string& language);
+  std::string GetPchUseCompileOptions(const std::string& config,
+                                      const std::string& language);
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;
@@ -519,7 +539,7 @@
 
   CompileInfo const* GetCompileInfo(const std::string& config) const;
 
-  typedef std::map<std::string, CompileInfo> CompileInfoMapType;
+  using CompileInfoMapType = std::map<std::string, CompileInfo>;
   mutable CompileInfoMapType CompileInfoMap;
 
   bool IsNullImpliedByLinkLibraries(const std::string& p) const;
@@ -674,7 +694,14 @@
 
   class TargetPropertyEntry;
 
-  bool HaveInstallTreeRPATH() const;
+  std::string EvaluateInterfaceProperty(
+    std::string const& prop, cmGeneratorExpressionContext* context,
+    cmGeneratorExpressionDAGChecker* dagCheckerParent) const;
+
+  bool HaveInstallTreeRPATH(const std::string& config) const;
+
+  bool GetBuildRPATH(const std::string& config, std::string& rpath) const;
+  bool GetInstallRPATH(const std::string& config, std::string& rpath) const;
 
   /** Whether this library has \@rpath and platform supports it.  */
   bool HasMacOSXRpathInstallNameDir(const std::string& config) const;
@@ -726,7 +753,7 @@
   {
     std::vector<cmSourceFile*> Depends;
   };
-  typedef std::map<cmSourceFile const*, SourceEntry> SourceEntriesType;
+  using SourceEntriesType = std::map<cmSourceFile const*, SourceEntry>;
   SourceEntriesType SourceDepends;
   mutable std::map<cmSourceFile const*, std::string> Objects;
   std::set<cmSourceFile const*> ExplicitObjectName;
@@ -740,9 +767,13 @@
 
   mutable std::map<std::string, bool> DebugCompatiblePropertiesDone;
 
-  const char* GetFilePrefixInternal(cmStateEnums::ArtifactType artifact,
+  bool NeedImportLibraryName(std::string const& config) const;
+
+  const char* GetFilePrefixInternal(std::string const& config,
+                                    cmStateEnums::ArtifactType artifact,
                                     const std::string& language = "") const;
-  const char* GetFileSuffixInternal(cmStateEnums::ArtifactType artifact,
+  const char* GetFileSuffixInternal(std::string const& config,
+                                    cmStateEnums::ArtifactType artifact,
                                     const std::string& language = "") const;
 
   std::string GetFullNameInternal(const std::string& config,
@@ -752,7 +783,7 @@
                            std::string& outPrefix, std::string& outBase,
                            std::string& outSuffix) const;
 
-  typedef std::map<std::string, LinkClosure> LinkClosureMapType;
+  using LinkClosureMapType = std::map<std::string, LinkClosure>;
   mutable LinkClosureMapType LinkClosureMap;
 
   // Returns ARCHIVE, LIBRARY, or RUNTIME based on platform and type.
@@ -779,8 +810,8 @@
   };
   mutable std::map<std::string, CompatibleInterfaces> CompatibleInterfacesMap;
 
-  typedef std::map<std::string, cmComputeLinkInformation*>
-    cmTargetLinkInformationMap;
+  using cmTargetLinkInformationMap =
+    std::map<std::string, cmComputeLinkInformation*>;
   mutable cmTargetLinkInformationMap LinkInformation;
 
   void CheckPropertyCompatibility(cmComputeLinkInformation* info,
@@ -792,7 +823,7 @@
   };
   mutable std::map<std::string, LinkImplClosure> LinkImplClosureMap;
 
-  typedef std::map<std::string, cmHeadToLinkInterfaceMap> LinkInterfaceMapType;
+  using LinkInterfaceMapType = std::map<std::string, cmHeadToLinkInterfaceMap>;
   mutable LinkInterfaceMapType LinkInterfaceMap;
   mutable LinkInterfaceMapType LinkInterfaceUsageRequirementsOnlyMap;
 
@@ -820,7 +851,7 @@
     std::string SharedDeps;
   };
 
-  typedef std::map<std::string, ImportInfo> ImportInfoMapType;
+  using ImportInfoMapType = std::map<std::string, ImportInfo>;
   mutable ImportInfoMapType ImportInfoMap;
   void ComputeImportInfo(std::string const& desired_config,
                          ImportInfo& info) const;
@@ -834,7 +865,7 @@
     const std::string& config, const cmGeneratorTarget* head,
     bool usage_requirements_only) const;
 
-  typedef std::map<std::string, KindedSources> KindedSourcesMapType;
+  using KindedSourcesMapType = std::map<std::string, KindedSources>;
   mutable KindedSourcesMapType KindedSourcesMap;
   void ComputeKindedSources(KindedSources& files,
                             std::string const& config) const;
@@ -842,14 +873,25 @@
   mutable std::vector<AllConfigSource> AllConfigSources;
   void ComputeAllConfigSources() const;
 
+  mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists;
+  bool MaybeHaveInterfaceProperty(std::string const& prop,
+                                  cmGeneratorExpressionContext* context) const;
+
   std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
   std::vector<TargetPropertyEntry*> CompileOptionsEntries;
   std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
   std::vector<TargetPropertyEntry*> LinkOptionsEntries;
   std::vector<TargetPropertyEntry*> LinkDirectoriesEntries;
+  std::vector<TargetPropertyEntry*> PrecompileHeadersEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   mutable std::set<std::string> LinkImplicitNullProperties;
+  mutable std::map<std::string, std::string> PchHeaders;
+  mutable std::map<std::string, std::string> PchSources;
+  mutable std::map<std::string, std::string> PchObjectFiles;
+  mutable std::map<std::string, std::string> PchFiles;
+  mutable std::map<std::string, std::string> PchCreateCompileOptions;
+  mutable std::map<std::string, std::string> PchUseCompileOptions;
 
   void ExpandLinkItems(std::string const& prop, std::string const& value,
                        std::string const& config,
@@ -872,7 +914,7 @@
     : public std::map<cmGeneratorTarget const*, cmOptionalLinkImplementation>
   {
   };
-  typedef std::map<std::string, HeadToLinkImplementationMap> LinkImplMapType;
+  using LinkImplMapType = std::map<std::string, HeadToLinkImplementationMap>;
   mutable LinkImplMapType LinkImplMap;
 
   cmLinkImplementationLibraries const* GetLinkImplementationLibrariesInternal(
@@ -881,17 +923,17 @@
                         cmStateEnums::ArtifactType artifact,
                         std::string& out) const;
 
-  typedef std::map<std::string, OutputInfo> OutputInfoMapType;
+  using OutputInfoMapType = std::map<std::string, OutputInfo>;
   mutable OutputInfoMapType OutputInfoMap;
 
-  typedef std::map<std::string, ModuleDefinitionInfo>
-    ModuleDefinitionInfoMapType;
+  using ModuleDefinitionInfoMapType =
+    std::map<std::string, ModuleDefinitionInfo>;
   mutable ModuleDefinitionInfoMapType ModuleDefinitionInfoMap;
   void ComputeModuleDefinitionInfo(std::string const& config,
                                    ModuleDefinitionInfo& info) const;
 
-  typedef std::pair<std::string, cmStateEnums::ArtifactType> OutputNameKey;
-  typedef std::map<OutputNameKey, std::string> OutputNameMapType;
+  using OutputNameKey = std::pair<std::string, cmStateEnums::ArtifactType>;
+  using OutputNameMapType = std::map<OutputNameKey, std::string>;
   mutable OutputNameMapType OutputNameMap;
   mutable std::set<cmLinkItem> UtilityItems;
   cmPolicies::PolicyMap PolicyMap;
@@ -903,16 +945,19 @@
   mutable bool DebugCompileDefinitionsDone;
   mutable bool DebugLinkOptionsDone;
   mutable bool DebugLinkDirectoriesDone;
+  mutable bool DebugPrecompileHeadersDone;
   mutable bool DebugSourcesDone;
   mutable bool LinkImplementationLanguageIsContextDependent;
   mutable bool UtilityItemsDone;
-  bool DLLPlatform;
 
   bool ComputePDBOutputDir(const std::string& kind, const std::string& config,
                            std::string& out) const;
 
   ManagedType CheckManagedType(std::string const& propval) const;
 
+  bool GetRPATH(const std::string& config, const std::string& prop,
+                std::string& rpath) const;
+
 public:
   const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure(
     const std::string& config) const;
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index fc82fee..ff4e312 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -4,19 +4,18 @@
 
 #include <set>
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
-
-class cmExecutionStatus;
+#include "cmStringAlgorithms.h"
 
 // cmGetCMakePropertyCommand
-bool cmGetCMakePropertyCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetCMakePropertyCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -24,29 +23,29 @@
   std::string output = "NOTFOUND";
 
   if (args[1] == "VARIABLES") {
-    if (const char* varsProp = this->Makefile->GetProperty("VARIABLES")) {
+    if (const char* varsProp = status.GetMakefile().GetProperty("VARIABLES")) {
       output = varsProp;
     }
   } else if (args[1] == "MACROS") {
     output.clear();
-    if (const char* macrosProp = this->Makefile->GetProperty("MACROS")) {
+    if (const char* macrosProp = status.GetMakefile().GetProperty("MACROS")) {
       output = macrosProp;
     }
   } else if (args[1] == "COMPONENTS") {
     const std::set<std::string>* components =
-      this->Makefile->GetGlobalGenerator()->GetInstallComponents();
+      status.GetMakefile().GetGlobalGenerator()->GetInstallComponents();
     output = cmJoin(*components, ";");
   } else {
     const char* prop = nullptr;
     if (!args[1].empty()) {
-      prop = this->Makefile->GetState()->GetGlobalProperty(args[1]);
+      prop = status.GetMakefile().GetState()->GetGlobalProperty(args[1]);
     }
     if (prop) {
       output = prop;
     }
   }
 
-  this->Makefile->AddDefinition(variable, output.c_str());
+  status.GetMakefile().AddDefinition(variable, output);
 
   return true;
 }
diff --git a/Source/cmGetCMakePropertyCommand.h b/Source/cmGetCMakePropertyCommand.h
index 1f29c78..7a6728c 100644
--- a/Source/cmGetCMakePropertyCommand.h
+++ b/Source/cmGetCMakePropertyCommand.h
@@ -8,21 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetCMakePropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmGetCMakePropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmGetCMakePropertyCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetDirectoryPropertyCommand.cxx b/Source/cmGetDirectoryPropertyCommand.cxx
index a92eb71..64438d5 100644
--- a/Source/cmGetDirectoryPropertyCommand.cxx
+++ b/Source/cmGetDirectoryPropertyCommand.cxx
@@ -2,51 +2,54 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetDirectoryPropertyCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+namespace {
+void StoreResult(cmMakefile& makefile, std::string const& variable,
+                 const char* prop);
+}
 
 // cmGetDirectoryPropertyCommand
-bool cmGetDirectoryPropertyCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetDirectoryPropertyCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
   std::string const& variable = *i;
   ++i;
 
   // get the directory argument if there is one
-  cmMakefile* dir = this->Makefile;
+  cmMakefile* dir = &status.GetMakefile();
   if (*i == "DIRECTORY") {
     ++i;
     if (i == args.end()) {
-      this->SetError(
+      status.SetError(
         "DIRECTORY argument provided without subsequent arguments");
       return false;
     }
     std::string sd = *i;
     // make sure the start dir is a full path
     if (!cmSystemTools::FileIsFullPath(sd)) {
-      sd = this->Makefile->GetCurrentSourceDirectory();
-      sd += "/";
-      sd += *i;
+      sd = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', *i);
     }
 
     // The local generators are associated with collapsed paths.
     sd = cmSystemTools::CollapseFullPath(sd);
 
     // lookup the makefile from the directory name
-    dir = this->Makefile->GetGlobalGenerator()->FindMakefile(sd);
+    dir = status.GetMakefile().GetGlobalGenerator()->FindMakefile(sd);
     if (!dir) {
-      this->SetError(
+      status.SetError(
         "DIRECTORY argument provided but requested directory not found. "
         "This could be because the directory argument was invalid or, "
         "it is valid but has not been processed yet.");
@@ -61,26 +64,27 @@
   if (*i == "DEFINITION") {
     ++i;
     if (i == args.end()) {
-      this->SetError("A request for a variable definition was made without "
-                     "providing the name of the variable to get.");
+      status.SetError("A request for a variable definition was made without "
+                      "providing the name of the variable to get.");
       return false;
     }
     std::string const& output = dir->GetSafeDefinition(*i);
-    this->Makefile->AddDefinition(variable, output.c_str());
+    status.GetMakefile().AddDefinition(variable, output);
     return true;
   }
 
   const char* prop = nullptr;
   if (!i->empty()) {
     if (*i == "DEFINITIONS") {
-      switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0059)) {
+      switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0059)) {
         case cmPolicies::WARN:
-          this->Makefile->IssueMessage(
+          status.GetMakefile().IssueMessage(
             MessageType::AUTHOR_WARNING,
             cmPolicies::GetPolicyWarning(cmPolicies::CMP0059));
           CM_FALLTHROUGH;
         case cmPolicies::OLD:
-          this->StoreResult(variable, this->Makefile->GetDefineFlagsCMP0059());
+          StoreResult(status.GetMakefile(), variable,
+                      status.GetMakefile().GetDefineFlagsCMP0059());
           return true;
         case cmPolicies::NEW:
         case cmPolicies::REQUIRED_ALWAYS:
@@ -90,16 +94,14 @@
     }
     prop = dir->GetProperty(*i);
   }
-  this->StoreResult(variable, prop);
+  StoreResult(status.GetMakefile(), variable, prop);
   return true;
 }
 
-void cmGetDirectoryPropertyCommand::StoreResult(std::string const& variable,
-                                                const char* prop)
+namespace {
+void StoreResult(cmMakefile& makefile, std::string const& variable,
+                 const char* prop)
 {
-  if (prop) {
-    this->Makefile->AddDefinition(variable, prop);
-    return;
-  }
-  this->Makefile->AddDefinition(variable, "");
+  makefile.AddDefinition(variable, prop ? prop : "");
+}
 }
diff --git a/Source/cmGetDirectoryPropertyCommand.h b/Source/cmGetDirectoryPropertyCommand.h
index 02ea056..f356ea5 100644
--- a/Source/cmGetDirectoryPropertyCommand.h
+++ b/Source/cmGetDirectoryPropertyCommand.h
@@ -8,24 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetDirectoryPropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmGetDirectoryPropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void StoreResult(const std::string& variable, const char* prop);
-};
+bool cmGetDirectoryPropertyCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetFilenameComponentCommand.cxx b/Source/cmGetFilenameComponentCommand.cxx
index 163b4c8..7d91a75 100644
--- a/Source/cmGetFilenameComponentCommand.cxx
+++ b/Source/cmGetFilenameComponentCommand.cxx
@@ -2,26 +2,26 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetFilenameComponentCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmGetFilenameComponentCommand
-bool cmGetFilenameComponentCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetFilenameComponentCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // Check and see if the value has been stored in the cache
   // already, if so use that value
   if (args.size() >= 4 && args.back() == "CACHE") {
-    const char* cacheValue = this->Makefile->GetDefinition(args.front());
-    if (cacheValue && !cmSystemTools::IsNOTFOUND(cacheValue)) {
+    const char* cacheValue = status.GetMakefile().GetDefinition(args.front());
+    if (cacheValue && !cmIsNOTFOUND(cacheValue)) {
       return true;
     }
   }
@@ -32,7 +32,7 @@
     // Check the registry as the target application would view it.
     cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
     cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
-    if (this->Makefile->PlatformIs64Bit()) {
+    if (status.GetMakefile().PlatformIs64Bit()) {
       view = cmSystemTools::KeyWOW64_64;
       other_view = cmSystemTools::KeyWOW64_32;
     }
@@ -64,7 +64,7 @@
     // First assume the path to the program was specified with no
     // arguments and with no quoting or escaping for spaces.
     // Only bother doing this if there is non-whitespace.
-    if (!cmSystemTools::TrimWhitespace(filename).empty()) {
+    if (!cmTrimWhitespace(filename).empty()) {
       result = cmSystemTools::FindProgram(filename);
     }
 
@@ -96,7 +96,7 @@
     // If the path given is relative, evaluate it relative to the
     // current source directory unless the user passes a different
     // base directory.
-    std::string baseDir = this->Makefile->GetCurrentSourceDirectory();
+    std::string baseDir = status.GetMakefile().GetCurrentSourceDirectory();
     for (unsigned int i = 3; i < args.size(); ++i) {
       if (args[i] == "BASE_DIR") {
         ++i;
@@ -113,24 +113,24 @@
     }
   } else {
     std::string err = "unknown component " + args[2];
-    this->SetError(err);
+    status.SetError(err);
     return false;
   }
 
   if (args.size() >= 4 && args.back() == "CACHE") {
     if (!programArgs.empty() && !storeArgs.empty()) {
-      this->Makefile->AddCacheDefinition(
+      status.GetMakefile().AddCacheDefinition(
         storeArgs, programArgs.c_str(), "",
         args[2] == "PATH" ? cmStateEnums::FILEPATH : cmStateEnums::STRING);
     }
-    this->Makefile->AddCacheDefinition(
+    status.GetMakefile().AddCacheDefinition(
       args.front(), result.c_str(), "",
       args[2] == "PATH" ? cmStateEnums::FILEPATH : cmStateEnums::STRING);
   } else {
     if (!programArgs.empty() && !storeArgs.empty()) {
-      this->Makefile->AddDefinition(storeArgs, programArgs.c_str());
+      status.GetMakefile().AddDefinition(storeArgs, programArgs);
     }
-    this->Makefile->AddDefinition(args.front(), result.c_str());
+    status.GetMakefile().AddDefinition(args.front(), result);
   }
 
   return true;
diff --git a/Source/cmGetFilenameComponentCommand.h b/Source/cmGetFilenameComponentCommand.h
index 8c26655..db5293b 100644
--- a/Source/cmGetFilenameComponentCommand.h
+++ b/Source/cmGetFilenameComponentCommand.h
@@ -8,30 +8,15 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmGetFilenameComponentCommand
+/**
  * \brief Get a specific component of a filename.
  *
  * cmGetFilenameComponentCommand is a utility command used to get the path,
  * name, extension or name without extension of a full filename.
  */
-class cmGetFilenameComponentCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmGetFilenameComponentCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmGetFilenameComponentCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetPipes.cxx b/Source/cmGetPipes.cxx
index ad323f7..4eda1c5 100644
--- a/Source/cmGetPipes.cxx
+++ b/Source/cmGetPipes.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetPipes.h"
 
-#include "cm_uv.h"
-
 #include <fcntl.h>
 
+#include "cm_uv.h"
+
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #  include <io.h>
 
@@ -28,7 +28,8 @@
   return 0;
 }
 #else
-#  include <errno.h>
+#  include <cerrno>
+
 #  include <unistd.h>
 
 int cmGetPipes(int* fds)
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx
index 039f439..947d893 100644
--- a/Source/cmGetPropertyCommand.cxx
+++ b/Source/cmGetPropertyCommand.cxx
@@ -2,8 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetPropertyCommand.h"
 
-#include <sstream>
-
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
@@ -14,30 +13,70 @@
 #include "cmPropertyDefinition.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetPropertyComputer.h"
 #include "cmTest.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
 class cmMessenger;
 
-cmGetPropertyCommand::cmGetPropertyCommand()
+namespace {
+enum OutType
 {
-  this->InfoType = OutValue;
+  OutValue,
+  OutDefined,
+  OutBriefDoc,
+  OutFullDoc,
+  OutSet
+};
+
+// Implementation of result storage.
+bool StoreResult(OutType infoType, cmMakefile& makefile,
+                 const std::string& variable, const char* value);
+
+// Implementation of each property type.
+bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName);
+bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
+                         OutType infoType, const std::string& variable,
+                         const std::string& propertyName);
+bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName);
+bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName);
+bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
+                    OutType infoType, const std::string& variable,
+                    const std::string& propertyName);
+bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
+                        OutType infoType, const std::string& variable,
+                        const std::string& propertyName);
+bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
+                     OutType infoType, const std::string& variable,
+                     const std::string& propertyName);
+bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
+                       OutType infoType, const std::string& variable,
+                       const std::string& propertyName);
 }
 
-bool cmGetPropertyCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmGetPropertyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
+  OutType infoType = OutValue;
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // The cmake variable in which to store the result.
-  this->Variable = args[0];
+  const std::string variable = args[0];
+
+  std::string name;
+  std::string propertyName;
 
   // Get the scope from which to get the property.
   cmProperty::ScopeType scope;
@@ -58,11 +97,11 @@
   } else if (args[1] == "INSTALL") {
     scope = cmProperty::INSTALL;
   } else {
-    std::ostringstream e;
-    e << "given invalid scope " << args[1] << ".  "
-      << "Valid scopes are "
-      << "GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(
+      "given invalid scope ", args[1],
+      ".  "
+      "Valid scopes are "
+      "GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL."));
     return false;
   }
 
@@ -80,86 +119,90 @@
       doing = DoingProperty;
     } else if (args[i] == "BRIEF_DOCS") {
       doing = DoingNone;
-      this->InfoType = OutBriefDoc;
+      infoType = OutBriefDoc;
     } else if (args[i] == "FULL_DOCS") {
       doing = DoingNone;
-      this->InfoType = OutFullDoc;
+      infoType = OutFullDoc;
     } else if (args[i] == "SET") {
       doing = DoingNone;
-      this->InfoType = OutSet;
+      infoType = OutSet;
     } else if (args[i] == "DEFINED") {
       doing = DoingNone;
-      this->InfoType = OutDefined;
+      infoType = OutDefined;
     } else if (doing == DoingName) {
       doing = DoingNone;
-      this->Name = args[i];
+      name = args[i];
     } else if (doing == DoingProperty) {
       doing = DoingNone;
-      this->PropertyName = args[i];
+      propertyName = args[i];
     } else {
-      std::ostringstream e;
-      e << "given invalid argument \"" << args[i] << "\".";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("given invalid argument \"", args[i], "\"."));
       return false;
     }
   }
 
   // Make sure a property name was found.
-  if (this->PropertyName.empty()) {
-    this->SetError("not given a PROPERTY <name> argument.");
+  if (propertyName.empty()) {
+    status.SetError("not given a PROPERTY <name> argument.");
     return false;
   }
 
   // Compute requested output.
-  if (this->InfoType == OutBriefDoc) {
+  if (infoType == OutBriefDoc) {
     // Lookup brief documentation.
     std::string output;
     if (cmPropertyDefinition const* def =
-          this->Makefile->GetState()->GetPropertyDefinition(this->PropertyName,
-                                                            scope)) {
+          status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
+                                                                 scope)) {
       output = def->GetShortDescription();
     } else {
       output = "NOTFOUND";
     }
-    this->Makefile->AddDefinition(this->Variable, output.c_str());
-  } else if (this->InfoType == OutFullDoc) {
+    status.GetMakefile().AddDefinition(variable, output);
+  } else if (infoType == OutFullDoc) {
     // Lookup full documentation.
     std::string output;
     if (cmPropertyDefinition const* def =
-          this->Makefile->GetState()->GetPropertyDefinition(this->PropertyName,
-                                                            scope)) {
+          status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
+                                                                 scope)) {
       output = def->GetFullDescription();
     } else {
       output = "NOTFOUND";
     }
-    this->Makefile->AddDefinition(this->Variable, output.c_str());
-  } else if (this->InfoType == OutDefined) {
+    status.GetMakefile().AddDefinition(variable, output);
+  } else if (infoType == OutDefined) {
     // Lookup if the property is defined
-    if (this->Makefile->GetState()->GetPropertyDefinition(this->PropertyName,
-                                                          scope)) {
-      this->Makefile->AddDefinition(this->Variable, "1");
+    if (status.GetMakefile().GetState()->GetPropertyDefinition(propertyName,
+                                                               scope)) {
+      status.GetMakefile().AddDefinition(variable, "1");
     } else {
-      this->Makefile->AddDefinition(this->Variable, "0");
+      status.GetMakefile().AddDefinition(variable, "0");
     }
   } else {
     // Dispatch property getting.
     switch (scope) {
       case cmProperty::GLOBAL:
-        return this->HandleGlobalMode();
+        return HandleGlobalMode(status, name, infoType, variable,
+                                propertyName);
       case cmProperty::DIRECTORY:
-        return this->HandleDirectoryMode();
+        return HandleDirectoryMode(status, name, infoType, variable,
+                                   propertyName);
       case cmProperty::TARGET:
-        return this->HandleTargetMode();
+        return HandleTargetMode(status, name, infoType, variable,
+                                propertyName);
       case cmProperty::SOURCE_FILE:
-        return this->HandleSourceMode();
+        return HandleSourceMode(status, name, infoType, variable,
+                                propertyName);
       case cmProperty::TEST:
-        return this->HandleTestMode();
+        return HandleTestMode(status, name, infoType, variable, propertyName);
       case cmProperty::VARIABLE:
-        return this->HandleVariableMode();
+        return HandleVariableMode(status, name, infoType, variable,
+                                  propertyName);
       case cmProperty::CACHE:
-        return this->HandleCacheMode();
+        return HandleCacheMode(status, name, infoType, variable, propertyName);
       case cmProperty::INSTALL:
-        return this->HandleInstallMode();
+        return HandleInstallMode(status, name, infoType, variable,
+                                 propertyName);
 
       case cmProperty::CACHED_VARIABLE:
         break; // should never happen
@@ -169,58 +212,64 @@
   return true;
 }
 
-bool cmGetPropertyCommand::StoreResult(const char* value)
+namespace {
+
+bool StoreResult(OutType infoType, cmMakefile& makefile,
+                 const std::string& variable, const char* value)
 {
-  if (this->InfoType == OutSet) {
-    this->Makefile->AddDefinition(this->Variable, value ? "1" : "0");
-  } else // if(this->InfoType == OutValue)
+  if (infoType == OutSet) {
+    makefile.AddDefinition(variable, value ? "1" : "0");
+  } else // if(infoType == OutValue)
   {
     if (value) {
-      this->Makefile->AddDefinition(this->Variable, value);
+      makefile.AddDefinition(variable, value);
     } else {
-      this->Makefile->RemoveDefinition(this->Variable);
+      makefile.RemoveDefinition(variable);
     }
   }
   return true;
 }
 
-bool cmGetPropertyCommand::HandleGlobalMode()
+bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName)
 {
-  if (!this->Name.empty()) {
-    this->SetError("given name for GLOBAL scope.");
+  if (!name.empty()) {
+    status.SetError("given name for GLOBAL scope.");
     return false;
   }
 
   // Get the property.
-  cmake* cm = this->Makefile->GetCMakeInstance();
-  return this->StoreResult(
-    cm->GetState()->GetGlobalProperty(this->PropertyName));
+  cmake* cm = status.GetMakefile().GetCMakeInstance();
+  return StoreResult(infoType, status.GetMakefile(), variable,
+                     cm->GetState()->GetGlobalProperty(propertyName));
 }
 
-bool cmGetPropertyCommand::HandleDirectoryMode()
+bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
+                         OutType infoType, const std::string& variable,
+                         const std::string& propertyName)
 {
   // Default to the current directory.
-  cmMakefile* mf = this->Makefile;
+  cmMakefile* mf = &status.GetMakefile();
 
   // Lookup the directory if given.
-  if (!this->Name.empty()) {
+  if (!name.empty()) {
     // Construct the directory name.  Interpret relative paths with
     // respect to the current directory.
-    std::string dir = this->Name;
+    std::string dir = name;
     if (!cmSystemTools::FileIsFullPath(dir)) {
-      dir = this->Makefile->GetCurrentSourceDirectory();
-      dir += "/";
-      dir += this->Name;
+      dir =
+        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', name);
     }
 
     // The local generators are associated with collapsed paths.
     dir = cmSystemTools::CollapseFullPath(dir);
 
     // Lookup the generator.
-    mf = this->Makefile->GetGlobalGenerator()->FindMakefile(dir);
+    mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
     if (!mf) {
       // Could not find the directory.
-      this->SetError(
+      status.SetError(
         "DIRECTORY scope provided but requested directory was not found. "
         "This could be because the directory argument was invalid or, "
         "it is valid but has not been processed yet.");
@@ -228,14 +277,15 @@
     }
   }
 
-  if (this->PropertyName == "DEFINITIONS") {
+  if (propertyName == "DEFINITIONS") {
     switch (mf->GetPolicyStatus(cmPolicies::CMP0059)) {
       case cmPolicies::WARN:
         mf->IssueMessage(MessageType::AUTHOR_WARNING,
                          cmPolicies::GetPolicyWarning(cmPolicies::CMP0059));
         CM_FALLTHROUGH;
       case cmPolicies::OLD:
-        return this->StoreResult(mf->GetDefineFlagsCMP0059());
+        return StoreResult(infoType, status.GetMakefile(), variable,
+                           mf->GetDefineFlagsCMP0059());
       case cmPolicies::NEW:
       case cmPolicies::REQUIRED_ALWAYS:
       case cmPolicies::REQUIRED_IF_USED:
@@ -244,124 +294,136 @@
   }
 
   // Get the property.
-  return this->StoreResult(mf->GetProperty(this->PropertyName));
+  return StoreResult(infoType, status.GetMakefile(), variable,
+                     mf->GetProperty(propertyName));
 }
 
-bool cmGetPropertyCommand::HandleTargetMode()
+bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName)
 {
-  if (this->Name.empty()) {
-    this->SetError("not given name for TARGET scope.");
+  if (name.empty()) {
+    status.SetError("not given name for TARGET scope.");
     return false;
   }
 
-  if (cmTarget* target = this->Makefile->FindTargetToUse(this->Name)) {
-    if (this->PropertyName == "ALIASED_TARGET") {
-      if (this->Makefile->IsAlias(this->Name)) {
-        return this->StoreResult(target->GetName().c_str());
+  if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
+    if (propertyName == "ALIASED_TARGET") {
+      if (status.GetMakefile().IsAlias(name)) {
+        return StoreResult(infoType, status.GetMakefile(), variable,
+                           target->GetName().c_str());
       }
-      return this->StoreResult(nullptr);
+      return StoreResult(infoType, status.GetMakefile(), variable, nullptr);
     }
     const char* prop_cstr = nullptr;
-    cmListFileBacktrace bt = this->Makefile->GetBacktrace();
-    cmMessenger* messenger = this->Makefile->GetMessenger();
+    cmListFileBacktrace bt = status.GetMakefile().GetBacktrace();
+    cmMessenger* messenger = status.GetMakefile().GetMessenger();
     if (cmTargetPropertyComputer::PassesWhitelist(
-          target->GetType(), this->PropertyName, messenger, bt)) {
-      prop_cstr =
-        target->GetComputedProperty(this->PropertyName, messenger, bt);
+          target->GetType(), propertyName, messenger, bt)) {
+      prop_cstr = target->GetComputedProperty(propertyName, messenger, bt);
       if (!prop_cstr) {
-        prop_cstr = target->GetProperty(this->PropertyName);
+        prop_cstr = target->GetProperty(propertyName);
       }
     }
-    return this->StoreResult(prop_cstr);
+    return StoreResult(infoType, status.GetMakefile(), variable, prop_cstr);
   }
-  std::ostringstream e;
-  e << "could not find TARGET " << this->Name
-    << ".  Perhaps it has not yet been created.";
-  this->SetError(e.str());
+  status.SetError(cmStrCat("could not find TARGET ", name,
+                           ".  Perhaps it has not yet been created."));
   return false;
 }
 
-bool cmGetPropertyCommand::HandleSourceMode()
+bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
+                      OutType infoType, const std::string& variable,
+                      const std::string& propertyName)
 {
-  if (this->Name.empty()) {
-    this->SetError("not given name for SOURCE scope.");
+  if (name.empty()) {
+    status.SetError("not given name for SOURCE scope.");
     return false;
   }
 
   // Get the source file.
-  if (cmSourceFile* sf = this->Makefile->GetOrCreateSource(this->Name)) {
-    return this->StoreResult(sf->GetPropertyForUser(this->PropertyName));
+  if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(name)) {
+    return StoreResult(infoType, status.GetMakefile(), variable,
+                       sf->GetPropertyForUser(propertyName));
   }
-  std::ostringstream e;
-  e << "given SOURCE name that could not be found or created: " << this->Name;
-  this->SetError(e.str());
+  status.SetError(
+    cmStrCat("given SOURCE name that could not be found or created: ", name));
   return false;
 }
 
-bool cmGetPropertyCommand::HandleTestMode()
+bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
+                    OutType infoType, const std::string& variable,
+                    const std::string& propertyName)
 {
-  if (this->Name.empty()) {
-    this->SetError("not given name for TEST scope.");
+  if (name.empty()) {
+    status.SetError("not given name for TEST scope.");
     return false;
   }
 
   // Loop over all tests looking for matching names.
-  if (cmTest* test = this->Makefile->GetTest(this->Name)) {
-    return this->StoreResult(test->GetProperty(this->PropertyName));
+  if (cmTest* test = status.GetMakefile().GetTest(name)) {
+    return StoreResult(infoType, status.GetMakefile(), variable,
+                       test->GetProperty(propertyName));
   }
 
   // If not found it is an error.
-  std::ostringstream e;
-  e << "given TEST name that does not exist: " << this->Name;
-  this->SetError(e.str());
+  status.SetError(cmStrCat("given TEST name that does not exist: ", name));
   return false;
 }
 
-bool cmGetPropertyCommand::HandleVariableMode()
+bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
+                        OutType infoType, const std::string& variable,
+                        const std::string& propertyName)
 {
-  if (!this->Name.empty()) {
-    this->SetError("given name for VARIABLE scope.");
+  if (!name.empty()) {
+    status.SetError("given name for VARIABLE scope.");
     return false;
   }
 
-  return this->StoreResult(this->Makefile->GetDefinition(this->PropertyName));
+  return StoreResult(infoType, status.GetMakefile(), variable,
+                     status.GetMakefile().GetDefinition(propertyName));
 }
 
-bool cmGetPropertyCommand::HandleCacheMode()
+bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
+                     OutType infoType, const std::string& variable,
+                     const std::string& propertyName)
 {
-  if (this->Name.empty()) {
-    this->SetError("not given name for CACHE scope.");
+  if (name.empty()) {
+    status.SetError("not given name for CACHE scope.");
     return false;
   }
 
   const char* value = nullptr;
-  if (this->Makefile->GetState()->GetCacheEntryValue(this->Name)) {
-    value = this->Makefile->GetState()->GetCacheEntryProperty(
-      this->Name, this->PropertyName);
+  if (status.GetMakefile().GetState()->GetCacheEntryValue(name)) {
+    value = status.GetMakefile().GetState()->GetCacheEntryProperty(
+      name, propertyName);
   }
-  this->StoreResult(value);
+  StoreResult(infoType, status.GetMakefile(), variable, value);
   return true;
 }
 
-bool cmGetPropertyCommand::HandleInstallMode()
+bool HandleInstallMode(cmExecutionStatus& status, const std::string& name,
+                       OutType infoType, const std::string& variable,
+                       const std::string& propertyName)
 {
-  if (this->Name.empty()) {
-    this->SetError("not given name for INSTALL scope.");
+  if (name.empty()) {
+    status.SetError("not given name for INSTALL scope.");
     return false;
   }
 
   // Get the installed file.
-  cmake* cm = this->Makefile->GetCMakeInstance();
+  cmake* cm = status.GetMakefile().GetCMakeInstance();
 
   if (cmInstalledFile* file =
-        cm->GetOrCreateInstalledFile(this->Makefile, this->Name)) {
+        cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
     std::string value;
-    bool isSet = file->GetProperty(this->PropertyName, value);
+    bool isSet = file->GetProperty(propertyName, value);
 
-    return this->StoreResult(isSet ? value.c_str() : nullptr);
+    return StoreResult(infoType, status.GetMakefile(), variable,
+                       isSet ? value.c_str() : nullptr);
   }
-  std::ostringstream e;
-  e << "given INSTALL name that could not be found or created: " << this->Name;
-  this->SetError(e.str());
+  status.SetError(
+    cmStrCat("given INSTALL name that could not be found or created: ", name));
   return false;
 }
+}
diff --git a/Source/cmGetPropertyCommand.h b/Source/cmGetPropertyCommand.h
index c3f653e..cc600f4 100644
--- a/Source/cmGetPropertyCommand.h
+++ b/Source/cmGetPropertyCommand.h
@@ -8,50 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetPropertyCommand : public cmCommand
-{
-public:
-  cmGetPropertyCommand();
-
-  cmCommand* Clone() override { return new cmGetPropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  enum OutType
-  {
-    OutValue,
-    OutDefined,
-    OutBriefDoc,
-    OutFullDoc,
-    OutSet
-  };
-  std::string Variable;
-  std::string Name;
-  std::string PropertyName;
-  OutType InfoType;
-
-  // Implementation of result storage.
-  bool StoreResult(const char* value);
-
-  // Implementation of each property type.
-  bool HandleGlobalMode();
-  bool HandleDirectoryMode();
-  bool HandleTargetMode();
-  bool HandleSourceMode();
-  bool HandleTestMode();
-  bool HandleVariableMode();
-  bool HandleCacheMode();
-  bool HandleInstallMode();
-};
+bool cmGetPropertyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx
index 75879a5..eefdc6c 100644
--- a/Source/cmGetSourceFilePropertyCommand.cxx
+++ b/Source/cmGetSourceFilePropertyCommand.cxx
@@ -2,42 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetSourceFilePropertyCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 
-class cmExecutionStatus;
-
-// cmSetSourceFilePropertyCommand
-bool cmGetSourceFilePropertyCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::string const& var = args[0];
   std::string const& file = args[1];
-  cmSourceFile* sf = this->Makefile->GetSource(file);
+  cmMakefile& mf = status.GetMakefile();
+  cmSourceFile* sf = mf.GetSource(file);
 
   // for the location we must create a source file first
   if (!sf && args[2] == "LOCATION") {
-    sf = this->Makefile->CreateSource(file);
+    sf = mf.CreateSource(file);
   }
   if (sf) {
-    if (args[2] == "LANGUAGE") {
-      this->Makefile->AddDefinition(var, sf->GetLanguage().c_str());
-      return true;
-    }
     const char* prop = nullptr;
     if (!args[2].empty()) {
       prop = sf->GetPropertyForUser(args[2]);
     }
     if (prop) {
-      this->Makefile->AddDefinition(var, prop);
+      mf.AddDefinition(var, prop);
       return true;
     }
   }
 
-  this->Makefile->AddDefinition(var, "NOTFOUND");
+  mf.AddDefinition(var, "NOTFOUND");
   return true;
 }
diff --git a/Source/cmGetSourceFilePropertyCommand.h b/Source/cmGetSourceFilePropertyCommand.h
index 43bc330..f0c319b 100644
--- a/Source/cmGetSourceFilePropertyCommand.h
+++ b/Source/cmGetSourceFilePropertyCommand.h
@@ -8,21 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetSourceFilePropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmGetSourceFilePropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetTargetPropertyCommand.cxx b/Source/cmGetTargetPropertyCommand.cxx
index fc0e9c6..7f5df9c 100644
--- a/Source/cmGetTargetPropertyCommand.cxx
+++ b/Source/cmGetTargetPropertyCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <sstream>
 
+#include "cmExecutionStatus.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -11,32 +12,31 @@
 #include "cmTarget.h"
 #include "cmTargetPropertyComputer.h"
 
-class cmExecutionStatus;
 class cmMessenger;
 
-// cmSetTargetPropertyCommand
-bool cmGetTargetPropertyCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetTargetPropertyCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::string const& var = args[0];
   std::string const& targetName = args[1];
   std::string prop;
   bool prop_exists = false;
+  cmMakefile& mf = status.GetMakefile();
 
-  if (cmTarget* tgt = this->Makefile->FindTargetToUse(targetName)) {
+  if (cmTarget* tgt = mf.FindTargetToUse(targetName)) {
     if (args[2] == "ALIASED_TARGET") {
-      if (this->Makefile->IsAlias(targetName)) {
+      if (mf.IsAlias(targetName)) {
         prop = tgt->GetName();
         prop_exists = true;
       }
     } else if (!args[2].empty()) {
       const char* prop_cstr = nullptr;
-      cmListFileBacktrace bt = this->Makefile->GetBacktrace();
-      cmMessenger* messenger = this->Makefile->GetMessenger();
+      cmListFileBacktrace bt = mf.GetBacktrace();
+      cmMessenger* messenger = mf.GetMessenger();
       if (cmTargetPropertyComputer::PassesWhitelist(tgt->GetType(), args[2],
                                                     messenger, bt)) {
         prop_cstr = tgt->GetComputedProperty(args[2], messenger, bt);
@@ -53,7 +53,7 @@
     bool issueMessage = false;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0045)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0045)) {
       case cmPolicies::WARN:
         issueMessage = true;
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0045) << "\n";
@@ -68,16 +68,16 @@
     if (issueMessage) {
       e << "get_target_property() called with non-existent target \""
         << targetName << "\".";
-      this->Makefile->IssueMessage(messageType, e.str());
+      mf.IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
     }
   }
   if (prop_exists) {
-    this->Makefile->AddDefinition(var, prop.c_str());
+    mf.AddDefinition(var, prop);
     return true;
   }
-  this->Makefile->AddDefinition(var, (var + "-NOTFOUND").c_str());
+  mf.AddDefinition(var, var + "-NOTFOUND");
   return true;
 }
diff --git a/Source/cmGetTargetPropertyCommand.h b/Source/cmGetTargetPropertyCommand.h
index 63ee5fd..c13078f 100644
--- a/Source/cmGetTargetPropertyCommand.h
+++ b/Source/cmGetTargetPropertyCommand.h
@@ -8,21 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetTargetPropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmGetTargetPropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmGetTargetPropertyCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGetTestPropertyCommand.cxx b/Source/cmGetTestPropertyCommand.cxx
index 0b0d6eb..cf8c1d5 100644
--- a/Source/cmGetTestPropertyCommand.cxx
+++ b/Source/cmGetTestPropertyCommand.cxx
@@ -2,33 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetTestPropertyCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmTest.h"
 
-class cmExecutionStatus;
-
-// cmGetTestPropertyCommand
-bool cmGetTestPropertyCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   std::string const& testName = args[0];
   std::string const& var = args[2];
-  cmTest* test = this->Makefile->GetTest(testName);
+  cmMakefile& mf = status.GetMakefile();
+  cmTest* test = mf.GetTest(testName);
   if (test) {
     const char* prop = nullptr;
     if (!args[1].empty()) {
       prop = test->GetProperty(args[1]);
     }
     if (prop) {
-      this->Makefile->AddDefinition(var, prop);
+      mf.AddDefinition(var, prop);
       return true;
     }
   }
-  this->Makefile->AddDefinition(var, "NOTFOUND");
+  mf.AddDefinition(var, "NOTFOUND");
   return true;
 }
diff --git a/Source/cmGetTestPropertyCommand.h b/Source/cmGetTestPropertyCommand.h
index 4a74f59..30beb8f 100644
--- a/Source/cmGetTestPropertyCommand.h
+++ b/Source/cmGetTestPropertyCommand.h
@@ -8,21 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmGetTestPropertyCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmGetTestPropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmGhsMultiGpj.h b/Source/cmGhsMultiGpj.h
index e588150..fbbef5d 100644
--- a/Source/cmGhsMultiGpj.h
+++ b/Source/cmGhsMultiGpj.h
@@ -4,6 +4,7 @@
 #define cmGhsMultiGpj_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
+
 #include <iosfwd>
 
 class GhsMultiGpj
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index b80da72..8e4352e 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -2,6 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGhsMultiTargetGenerator.h"
 
+#include <algorithm>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <utility>
+#include <vector>
+
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmGeneratedFileStream.h"
@@ -18,14 +25,10 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-#include <algorithm>
-#include <ostream>
-#include <set>
-#include <utility>
-
 cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target)
   : GeneratorTarget(target)
   , LocalGenerator(
@@ -72,8 +75,8 @@
       break;
     }
     case cmStateEnums::SHARED_LIBRARY: {
-      std::string msg = "add_library(<name> SHARED ...) not supported: ";
-      msg += this->Name;
+      std::string msg =
+        cmStrCat("add_library(<name> SHARED ...) not supported: ", this->Name);
       cmSystemTools::Message(msg);
       return;
     }
@@ -84,8 +87,8 @@
       break;
     }
     case cmStateEnums::MODULE_LIBRARY: {
-      std::string msg = "add_library(<name> MODULE ...) not supported: ";
-      msg += this->Name;
+      std::string msg =
+        cmStrCat("add_library(<name> MODULE ...) not supported: ", this->Name);
       cmSystemTools::Message(msg);
       return;
     }
@@ -120,10 +123,9 @@
 void cmGhsMultiTargetGenerator::GenerateTarget()
 {
   // Open the target file in copy-if-different mode.
-  std::string fproj = this->LocalGenerator->GetCurrentBinaryDirectory();
-  fproj += "/";
-  fproj += this->Name;
-  fproj += cmGlobalGhsMultiGenerator::FILE_EXTENSION;
+  std::string fproj =
+    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             this->Name, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
   cmGeneratedFileStream fout(fproj);
   fout.SetCopyIfDifferent(true);
 
@@ -175,8 +177,7 @@
 void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
                                                  const std::string& language)
 {
-  std::map<std::string, std::string>::iterator i =
-    this->FlagsByLanguage.find(language);
+  auto i = this->FlagsByLanguage.find(language);
   if (i == this->FlagsByLanguage.end()) {
     std::string flags;
     const char* lang = language.c_str();
@@ -207,8 +208,7 @@
 std::string cmGhsMultiTargetGenerator::GetDefines(const std::string& language,
                                                   std::string const& config)
 {
-  std::map<std::string, std::string>::iterator i =
-    this->DefinesByLanguage.find(language);
+  auto i = this->DefinesByLanguage.find(language);
   if (i == this->DefinesByLanguage.end()) {
     std::set<std::string> defines;
     const char* lang = language.c_str();
@@ -230,8 +230,7 @@
                                                    std::string const&,
                                                    const std::string& language)
 {
-  std::map<std::string, std::string>::iterator flagsByLangI =
-    this->FlagsByLanguage.find(language);
+  auto flagsByLangI = this->FlagsByLanguage.find(language);
   if (flagsByLangI != this->FlagsByLanguage.end()) {
     if (!flagsByLangI->second.empty()) {
       std::vector<std::string> ghsCompFlags =
@@ -344,12 +343,11 @@
   for (cmCustomCommand const& cc : ccv) {
     cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator);
     // Open the filestream for this custom command
-    std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory();
-    fname +=
-      "/" + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-    fname += "/" + this->Name + "_" + name;
-    fname += std::to_string(cmdcount++);
-    fname += this->CmdWindowsShell ? ".bat" : ".sh";
+    std::string fname =
+      cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+               this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+               '/', this->Name, '_', name, cmdcount++,
+               this->CmdWindowsShell ? ".bat" : ".sh");
     cmGeneratedFileStream f(fname);
     f.SetCopyIfDifferent(true);
     this->WriteCustomCommandsHelper(f, ccg);
@@ -392,8 +390,7 @@
   // Echo the custom command's comment text.
   const char* comment = ccg.GetComment();
   if (comment && *comment) {
-    std::string echocmd = "echo ";
-    echocmd += comment;
+    std::string echocmd = cmStrCat("echo ", comment);
     cmdLines.push_back(std::move(echocmd));
   }
 
@@ -440,12 +437,12 @@
         // This command was specified as a path to a file in the
         // current directory.  Add a leading "./" so it can run
         // without the current directory being in the search path.
-        cmd = "./" + cmd;
+        cmd = cmStrCat("./", cmd);
       }
       cmd = this->LocalGenerator->ConvertToOutputFormat(
         cmd, cmOutputConverter::SHELL);
       if (useCall) {
-        cmd = "call " + cmd;
+        cmd = cmStrCat("call ", cmd);
       }
       ccg.AppendArguments(c, cmd);
       cmdLines.push_back(std::move(cmd));
@@ -465,8 +462,7 @@
 {
   const char* prop = sf->GetProperty(propName);
   if (prop) {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(prop, list);
+    std::vector<std::string> list = cmExpandedList(prop);
     for (auto& p : list) {
       fout << "    " << propFlag << p << std::endl;
     }
@@ -489,7 +485,7 @@
   std::set<std::string> groupNames;
   for (auto& sf : sources) {
     cmSourceGroup* sourceGroup =
-      this->Makefile->FindSourceGroup(sf->GetFullPath(), sourceGroups);
+      this->Makefile->FindSourceGroup(sf->ResolveFullPath(), sourceGroups);
     std::string gn = sourceGroup->GetFullName();
     groupFiles[gn].push_back(sf);
     groupNames.insert(std::move(gn));
@@ -544,7 +540,7 @@
   for (auto& n : groupFilesList) {
     std::sort(groupFiles[n].begin(), groupFiles[n].end(),
               [](cmSourceFile* l, cmSourceFile* r) {
-                return l->GetFullPath() < r->GetFullPath();
+                return l->ResolveFullPath() < r->ResolveFullPath();
               });
   }
 
@@ -558,24 +554,19 @@
   for (auto& sg : groupFilesList) {
     std::ostream* fout;
     bool useProjectFile =
-      cmSystemTools::IsOn(
-        this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) ||
-      cmSystemTools::IsOn(
-        this->Makefile->GetDefinition("CMAKE_GHS_NO_SOURCE_GROUP_FILE"));
+      cmIsOn(this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) ||
+      cmIsOn(this->Makefile->GetDefinition("CMAKE_GHS_NO_SOURCE_GROUP_FILE"));
     if (useProjectFile || sg.empty()) {
       fout = &fout_proj;
     } else {
       // Open the filestream in copy-if-different mode.
       std::string gname = sg;
       cmsys::SystemTools::ReplaceString(gname, "\\", "_");
-      std::string lpath =
-        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-      lpath += "/";
-      lpath += gname;
-      lpath += cmGlobalGhsMultiGenerator::FILE_EXTENSION;
-      std::string fpath = this->LocalGenerator->GetCurrentBinaryDirectory();
-      fpath += "/";
-      fpath += lpath;
+      std::string lpath = cmStrCat(
+        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
+        gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
+      std::string fpath = cmStrCat(
+        this->LocalGenerator->GetCurrentBinaryDirectory(), '/', lpath);
       cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath);
       f->SetCopyIfDifferent(true);
       gfiles.push_back(f);
@@ -667,14 +658,12 @@
                                        this->LocalGenerator);
 
           // Open the filestream for this custom command
-          std::string fname =
-            this->LocalGenerator->GetCurrentBinaryDirectory();
-          fname += "/" +
-            this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-          fname += "/" + this->Name + "_cc";
-          fname += std::to_string(cmdcount++) + "_";
-          fname += (sf->GetLocation()).GetName();
-          fname += this->CmdWindowsShell ? ".bat" : ".sh";
+          std::string fname = cmStrCat(
+            this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+            this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+            '/', this->Name, "_cc", cmdcount++, '_',
+            (sf->GetLocation()).GetName(),
+            this->CmdWindowsShell ? ".bat" : ".sh");
           cmGeneratedFileStream f(fname);
           f.SetCopyIfDifferent(true);
           this->WriteCustomCommandsHelper(f, ccg);
@@ -737,8 +726,7 @@
 {
   const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app");
   if (p) {
-    return cmSystemTools::IsOn(
-      this->GeneratorTarget->GetProperty("ghs_integrity_app"));
+    return cmIsOn(this->GeneratorTarget->GetProperty("ghs_integrity_app"));
   }
   std::vector<cmSourceFile*> sources;
   this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h
index a131567..f03ca44 100644
--- a/Source/cmGhsMultiTargetGenerator.h
+++ b/Source/cmGhsMultiTargetGenerator.h
@@ -3,14 +3,14 @@
 #ifndef cmGhsMultiTargetGenerator_h
 #define cmGhsMultiTargetGenerator_h
 
-#include "cmGhsMultiGpj.h"
-
 #include <iosfwd>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmGhsMultiGpj.h"
+
 class cmCustomCommand;
 class cmCustomCommandGenerator;
 class cmGeneratorTarget;
diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx
index 9fb4170..4817773 100644
--- a/Source/cmGlobVerificationManager.cxx
+++ b/Source/cmGlobVerificationManager.cxx
@@ -2,11 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobVerificationManager.h"
 
-#include "cmsys/FStream.hxx"
 #include <sstream>
 
+#include "cmsys/FStream.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmListFileCache.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
@@ -16,8 +18,7 @@
     return true;
   }
 
-  std::string scriptFile = path;
-  scriptFile += "/CMakeFiles";
+  std::string scriptFile = cmStrCat(path, "/CMakeFiles");
   std::string stampFile = scriptFile;
   cmSystemTools::MakeDirectory(scriptFile);
   scriptFile += "/VerifyGlobs.cmake";
diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h
index 48606d3..2e7e1ca 100644
--- a/Source/cmGlobVerificationManager.h
+++ b/Source/cmGlobVerificationManager.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmListFileCache.h"
-
 #include <iosfwd>
 #include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmListFileCache.h"
+
 /** \class cmGlobVerificationManager
  * \brief Class for expressing build-time dependencies on glob expressions.
  *
@@ -72,7 +72,7 @@
     std::vector<std::pair<std::string, cmListFileBacktrace>> Backtraces;
   };
 
-  typedef std::map<CacheEntryKey, CacheEntryValue> CacheEntryMap;
+  using CacheEntryMap = std::map<CacheEntryKey, CacheEntryValue>;
   CacheEntryMap Cache;
   std::string VerifyScript;
   std::string VerifyStamp;
diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h
index ee7de70..da04743 100644
--- a/Source/cmGlobalBorlandMakefileGenerator.h
+++ b/Source/cmGlobalBorlandMakefileGenerator.h
@@ -3,10 +3,10 @@
 #ifndef cmGlobalBorlandMakefileGenerator_h
 #define cmGlobalBorlandMakefileGenerator_h
 
-#include "cmGlobalNMakeMakefileGenerator.h"
-
 #include <iosfwd>
 
+#include "cmGlobalNMakeMakefileGenerator.h"
+
 /** \class cmGlobalBorlandMakefileGenerator
  * \brief Write a Borland makefiles.
  *
diff --git a/Source/cmGlobalCommonGenerator.cxx b/Source/cmGlobalCommonGenerator.cxx
index bf992b4..9fa4467 100644
--- a/Source/cmGlobalCommonGenerator.cxx
+++ b/Source/cmGlobalCommonGenerator.cxx
@@ -2,6 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalCommonGenerator.h"
 
+#include <utility>
+
+#include "cmGeneratorTarget.h"
+#include "cmLocalGenerator.h"
+#include "cmStateDirectory.h"
+#include "cmStateSnapshot.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+
 class cmake;
 
 cmGlobalCommonGenerator::cmGlobalCommonGenerator(cmake* cm)
@@ -10,3 +19,60 @@
 }
 
 cmGlobalCommonGenerator::~cmGlobalCommonGenerator() = default;
+
+std::map<std::string, cmGlobalCommonGenerator::DirectoryTarget>
+cmGlobalCommonGenerator::ComputeDirectoryTargets() const
+{
+  std::map<std::string, DirectoryTarget> dirTargets;
+  for (cmLocalGenerator* lg : this->LocalGenerators) {
+    std::string const& currentBinaryDir(
+      lg->GetStateSnapshot().GetDirectory().GetCurrentBinary());
+    DirectoryTarget& dirTarget = dirTargets[currentBinaryDir];
+    dirTarget.LG = lg;
+
+    // The directory-level rule should depend on the target-level rules
+    // for all targets in the directory.
+    for (auto gt : lg->GetGeneratorTargets()) {
+      cmStateEnums::TargetType const type = gt->GetType();
+      if (type != cmStateEnums::EXECUTABLE &&
+          type != cmStateEnums::STATIC_LIBRARY &&
+          type != cmStateEnums::SHARED_LIBRARY &&
+          type != cmStateEnums::MODULE_LIBRARY &&
+          type != cmStateEnums::OBJECT_LIBRARY &&
+          type != cmStateEnums::UTILITY) {
+        continue;
+      }
+      DirectoryTarget::Target t;
+      t.GT = gt;
+      if (const char* exclude = gt->GetProperty("EXCLUDE_FROM_ALL")) {
+        if (cmIsOn(exclude)) {
+          // This target has been explicitly excluded.
+          t.ExcludeFromAll = true;
+        } else {
+          // This target has been explicitly un-excluded.  The directory-level
+          // rule for every directory between this and the root should depend
+          // on the target-level rule for this target.
+          for (cmStateSnapshot dir =
+                 lg->GetStateSnapshot().GetBuildsystemDirectoryParent();
+               dir.IsValid(); dir = dir.GetBuildsystemDirectoryParent()) {
+            std::string const& d = dir.GetDirectory().GetCurrentBinary();
+            dirTargets[d].Targets.emplace_back(t);
+          }
+        }
+      }
+      dirTarget.Targets.emplace_back(t);
+    }
+
+    // The directory-level rule should depend on the directory-level
+    // rules of the subdirectories.
+    for (cmStateSnapshot const& state : lg->GetStateSnapshot().GetChildren()) {
+      DirectoryTarget::Dir d;
+      d.Path = state.GetDirectory().GetCurrentBinary();
+      d.ExcludeFromAll =
+        state.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL");
+      dirTarget.Children.emplace_back(std::move(d));
+    }
+  }
+
+  return dirTargets;
+}
diff --git a/Source/cmGlobalCommonGenerator.h b/Source/cmGlobalCommonGenerator.h
index e19118b..7d16dac 100644
--- a/Source/cmGlobalCommonGenerator.h
+++ b/Source/cmGlobalCommonGenerator.h
@@ -5,9 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include "cmGlobalGenerator.h"
 
 class cmake;
+class cmGeneratorTarget;
+class cmLocalGenerator;
 
 /** \class cmGlobalCommonGenerator
  * \brief Common infrastructure for Makefile and Ninja global generators.
@@ -17,6 +23,24 @@
 public:
   cmGlobalCommonGenerator(cmake* cm);
   ~cmGlobalCommonGenerator() override;
+
+  struct DirectoryTarget
+  {
+    cmLocalGenerator* LG = nullptr;
+    struct Target
+    {
+      cmGeneratorTarget const* GT = nullptr;
+      bool ExcludeFromAll = false;
+    };
+    std::vector<Target> Targets;
+    struct Dir
+    {
+      std::string Path;
+      bool ExcludeFromAll = false;
+    };
+    std::vector<Dir> Children;
+  };
+  std::map<std::string, DirectoryTarget> ComputeDirectoryTargets() const;
 };
 
 #endif
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index ea898e1..96656a5 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -2,16 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalGenerator.h"
 
-#include "cmsys/Directory.hxx"
-#include "cmsys/FStream.hxx"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
 #include <cstring>
 #include <initializer_list>
 #include <iterator>
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #  include <windows.h>
@@ -44,11 +45,12 @@
 #include "cmWorkingDirectory.h"
 #include "cmake.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-#  include "cmCryptoHash.h"
-#  include "cmQtAutoGenGlobalInitializer.h"
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cm_jsoncpp_value.h"
 #  include "cm_jsoncpp_writer.h"
+
+#  include "cmCryptoHash.h"
+#  include "cmQtAutoGenGlobalInitializer.h"
 #endif
 
 #if defined(_MSC_VER) && _MSC_VER >= 1800
@@ -92,7 +94,6 @@
   // how long to let try compiles run
   this->TryCompileTimeout = cmDuration::zero();
 
-  this->ExtraGenerator = nullptr;
   this->CurrentConfigureMakefile = nullptr;
   this->TryCompileOuterMakefile = nullptr;
 
@@ -113,10 +114,9 @@
 cmGlobalGenerator::~cmGlobalGenerator()
 {
   this->ClearGeneratorMembers();
-  delete this->ExtraGenerator;
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 Json::Value cmGlobalGenerator::GetJson() const
 {
   Json::Value generator = Json::objectValue;
@@ -188,15 +188,15 @@
   const std::string& inMakeProgram, const std::string& makeDefault) const
 {
   std::string makeProgram = inMakeProgram;
-  if (cmSystemTools::IsOff(makeProgram)) {
+  if (cmIsOff(makeProgram)) {
     const char* makeProgramCSTR =
       this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
-    if (cmSystemTools::IsOff(makeProgramCSTR)) {
+    if (cmIsOff(makeProgramCSTR)) {
       makeProgram = makeDefault;
     } else {
       makeProgram = makeProgramCSTR;
     }
-    if (cmSystemTools::IsOff(makeProgram) && !makeProgram.empty()) {
+    if (cmIsOff(makeProgram) && !makeProgram.empty()) {
       makeProgram = "CMAKE_MAKE_PROGRAM-NOTFOUND";
     }
   }
@@ -207,9 +207,7 @@
                                                 cmMakefile* mf,
                                                 bool optional) const
 {
-  std::string langComp = "CMAKE_";
-  langComp += lang;
-  langComp += "_COMPILER";
+  std::string langComp = cmStrCat("CMAKE_", lang, "_COMPILER");
 
   if (!mf->GetDefinition(langComp)) {
     if (!optional) {
@@ -302,7 +300,7 @@
       if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET ||
           target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY ||
           target->GetType() == cmStateEnums::TargetType::UTILITY ||
-          cmSystemTools::IsOn(target->GetProperty("ghs_integrity_app"))) {
+          cmIsOn(target->GetProperty("ghs_integrity_app"))) {
         continue;
       }
 
@@ -341,12 +339,8 @@
     for (cmGeneratorTarget* target : generator->GetGeneratorTargets()) {
       if (target->GetType() == cmStateEnums::EXECUTABLE &&
           target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
-        std::vector<std::string> configs;
-        target->Makefile->GetConfigurations(configs);
-        if (configs.empty()) {
-          configs.emplace_back();
-        }
-
+        std::vector<std::string> const& configs =
+          target->Makefile->GetGeneratorConfigs();
         for (std::string const& config : configs) {
           if (target->GetLinkerLanguage(config) == "Swift") {
             this->GetCMakeInstance()->IssueMessage(
@@ -363,6 +357,42 @@
   return failed;
 }
 
+bool cmGlobalGenerator::CheckTargetsForPchCompilePdb() const
+{
+  if (!this->GetLanguageEnabled("C") && !this->GetLanguageEnabled("CXX")) {
+    return false;
+  }
+  bool failed = false;
+  for (cmLocalGenerator* generator : this->LocalGenerators) {
+    for (cmGeneratorTarget* target : generator->GetGeneratorTargets()) {
+      if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET ||
+          target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY ||
+          target->GetType() == cmStateEnums::TargetType::UTILITY ||
+          cmIsOn(target->GetProperty("ghs_integrity_app"))) {
+        continue;
+      }
+
+      const std::string reuseFrom =
+        target->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+      const std::string compilePdb =
+        target->GetSafeProperty("COMPILE_PDB_NAME");
+
+      if (!reuseFrom.empty() && reuseFrom != compilePdb) {
+        const std::string e = cmStrCat(
+          "PRECOMPILE_HEADERS_REUSE_FROM property is set on target (\"",
+          target->GetName(),
+          "\"). Reusable precompile headers requires the COMPILE_PDB_NAME"
+          " property to have the value \"",
+          reuseFrom, "\"\n");
+        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
+                                               target->GetBacktrace());
+        failed = true;
+      }
+    }
+  }
+  return failed;
+}
+
 bool cmGlobalGenerator::IsExportedTargetsFile(
   const std::string& filename) const
 {
@@ -370,8 +400,7 @@
   if (it == this->BuildExportSets.end()) {
     return false;
   }
-  return this->BuildExportExportSets.find(filename) ==
-    this->BuildExportExportSets.end();
+  return !cmContains(this->BuildExportExportSets, filename);
 }
 
 // Find the make program for the generator, required for try compiles
@@ -384,14 +413,14 @@
     return false;
   }
   if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") ||
-      cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+      cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
     std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile);
     if (!setMakeProgram.empty()) {
       mf->ReadListFile(setMakeProgram);
     }
   }
   if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") ||
-      cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+      cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
     std::ostringstream err;
     err << "CMake was unable to find a build program corresponding to \""
         << this->GetName() << "\".  CMAKE_MAKE_PROGRAM is not set.  You "
@@ -411,9 +440,7 @@
     std::string saveFile = file;
     cmSystemTools::GetShortPath(makeProgram, makeProgram);
     cmSystemTools::SplitProgramPath(makeProgram, dir, file);
-    makeProgram = dir;
-    makeProgram += "/";
-    makeProgram += saveFile;
+    makeProgram = cmStrCat(dir, '/', saveFile);
     mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", makeProgram.c_str(),
                            "make program", cmStateEnums::FILEPATH);
   }
@@ -499,7 +526,7 @@
       if (lang == "NONE") {
         this->SetLanguageEnabled("NONE", mf);
       } else {
-        if (this->LanguagesReady.find(lang) == this->LanguagesReady.end()) {
+        if (!cmContains(this->LanguagesReady, lang)) {
           std::ostringstream e;
           e << "The test project needs language " << lang
             << " which is not enabled.";
@@ -514,9 +541,9 @@
 
   bool fatalError = false;
 
-  mf->AddDefinition("RUN_CONFIGURE", true);
-  std::string rootBin = this->CMakeInstance->GetHomeOutputDirectory();
-  rootBin += "/CMakeFiles";
+  mf->AddDefinitionBool("RUN_CONFIGURE", true);
+  std::string rootBin =
+    cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
 
   // If the configuration files path has been set,
   // then we are in a try compile and need to copy the enable language
@@ -524,11 +551,11 @@
   if (!this->ConfiguredFilesPath.empty()) {
     rootBin = this->ConfiguredFilesPath;
   }
-  rootBin += "/";
+  rootBin += '/';
   rootBin += cmVersion::GetCMakeVersion();
 
   // set the dir for parent files so they can be used by modules
-  mf->AddDefinition("CMAKE_PLATFORM_INFO_DIR", rootBin.c_str());
+  mf->AddDefinition("CMAKE_PLATFORM_INFO_DIR", rootBin);
 
   if (!this->CMakeInstance->GetIsInTryCompile()) {
     // Keep a mark in the cache to indicate that we've initialized the
@@ -588,16 +615,14 @@
     windowsVersionString << osviex.dwMajorVersion << "."
                          << osviex.dwMinorVersion << "."
                          << osviex.dwBuildNumber;
-    mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION",
-                      windowsVersionString.str().c_str());
+    mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str());
 #endif
     // Read the DetermineSystem file
     std::string systemFile = mf->GetModulesFile("CMakeDetermineSystem.cmake");
     mf->ReadListFile(systemFile);
     // load the CMakeSystem.cmake from the binary directory
     // this file is configured by the CMakeDetermineSystem.cmake file
-    fpath = rootBin;
-    fpath += "/CMakeSystem.cmake";
+    fpath = cmStrCat(rootBin, "/CMakeSystem.cmake");
     mf->ReadListFile(fpath);
   }
 
@@ -663,14 +688,9 @@
       this->SetLanguageEnabled("NONE", mf);
       continue;
     }
-    std::string loadedLang = "CMAKE_";
-    loadedLang += lang;
-    loadedLang += "_COMPILER_LOADED";
+    std::string loadedLang = cmStrCat("CMAKE_", lang, "_COMPILER_LOADED");
     if (!mf->GetDefinition(loadedLang)) {
-      fpath = rootBin;
-      fpath += "/CMake";
-      fpath += lang;
-      fpath += "Compiler.cmake";
+      fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
 
       // If the existing build tree was already configured with this
       // version of CMake then try to load the configured file first
@@ -697,9 +717,8 @@
       }
       // if the CMake(LANG)Compiler.cmake file was not found then
       // load CMakeDetermine(LANG)Compiler.cmake
-      std::string determineCompiler = "CMakeDetermine";
-      determineCompiler += lang;
-      determineCompiler += "Compiler.cmake";
+      std::string determineCompiler =
+        cmStrCat("CMakeDetermine", lang, "Compiler.cmake");
       std::string determineFile = mf->GetModulesFile(determineCompiler);
       if (!mf->ReadListFile(determineFile)) {
         cmSystemTools::Error("Could not find cmake module file: " +
@@ -715,27 +734,19 @@
         // put ${CMake_(LANG)_COMPILER_ENV_VAR}=${CMAKE_(LANG)_COMPILER
         // into the environment, in case user scripts want to run
         // configure, or sub cmakes
-        std::string compilerName = "CMAKE_";
-        compilerName += lang;
-        compilerName += "_COMPILER";
-        std::string compilerEnv = "CMAKE_";
-        compilerEnv += lang;
-        compilerEnv += "_COMPILER_ENV_VAR";
+        std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
+        std::string compilerEnv =
+          cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
         const std::string& envVar = mf->GetRequiredDefinition(compilerEnv);
         const std::string& envVarValue =
           mf->GetRequiredDefinition(compilerName);
-        std::string env = envVar;
-        env += "=";
-        env += envVarValue;
+        std::string env = cmStrCat(envVar, '=', envVarValue);
         cmSystemTools::PutEnv(env);
       }
 
       // if determineLanguage was called then load the file it
       // configures CMake(LANG)Compiler.cmake
-      fpath = rootBin;
-      fpath += "/CMake";
-      fpath += lang;
-      fpath += "Compiler.cmake";
+      fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
       if (!mf->ReadListFile(fpath)) {
         cmSystemTools::Error("Could not find cmake module file: " + fpath);
       }
@@ -766,16 +777,11 @@
     }
 
     // Check that the compiler was found.
-    std::string compilerName = "CMAKE_";
-    compilerName += lang;
-    compilerName += "_COMPILER";
-    std::string compilerEnv = "CMAKE_";
-    compilerEnv += lang;
-    compilerEnv += "_COMPILER_ENV_VAR";
+    std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
+    std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
     std::ostringstream noCompiler;
     const char* compilerFile = mf->GetDefinition(compilerName);
-    if (!compilerFile || !*compilerFile ||
-        cmSystemTools::IsNOTFOUND(compilerFile)) {
+    if (!compilerFile || !*compilerFile || cmIsNOTFOUND(compilerFile)) {
       /* clang-format off */
       noCompiler <<
         "No " << compilerName << " could be found.\n"
@@ -806,10 +812,8 @@
       if (!optional) {
         // The compiler was not found and it is not optional.  Remove
         // CMake(LANG)Compiler.cmake so we try again next time CMake runs.
-        std::string compilerLangFile = rootBin;
-        compilerLangFile += "/CMake";
-        compilerLangFile += lang;
-        compilerLangFile += "Compiler.cmake";
+        std::string compilerLangFile =
+          cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
         cmSystemTools::RemoveFile(compilerLangFile);
         if (!this->CMakeInstance->GetIsInTryCompile()) {
           this->PrintCompilerAdvice(noCompiler, lang,
@@ -820,13 +824,10 @@
       }
     }
 
-    std::string langLoadedVar = "CMAKE_";
-    langLoadedVar += lang;
-    langLoadedVar += "_INFORMATION_LOADED";
+    std::string langLoadedVar =
+      cmStrCat("CMAKE_", lang, "_INFORMATION_LOADED");
     if (!mf->GetDefinition(langLoadedVar)) {
-      fpath = "CMake";
-      fpath += lang;
-      fpath += "Information.cmake";
+      fpath = cmStrCat("CMake", lang, "Information.cmake");
       std::string informationFile = mf->GetModulesFile(fpath);
       if (informationFile.empty()) {
         cmSystemTools::Error("Could not find cmake module file: " + fpath);
@@ -847,33 +848,27 @@
     // If the language is untested then test it now with a try compile.
     if (needTestLanguage[lang]) {
       if (!this->CMakeInstance->GetIsInTryCompile()) {
-        std::string testLang = "CMakeTest";
-        testLang += lang;
-        testLang += "Compiler.cmake";
+        std::string testLang = cmStrCat("CMakeTest", lang, "Compiler.cmake");
         std::string ifpath = mf->GetModulesFile(testLang);
         if (!mf->ReadListFile(ifpath)) {
           cmSystemTools::Error("Could not find cmake module file: " +
                                testLang);
         }
-        std::string compilerWorks = "CMAKE_";
-        compilerWorks += lang;
-        compilerWorks += "_COMPILER_WORKS";
+        std::string compilerWorks =
+          cmStrCat("CMAKE_", lang, "_COMPILER_WORKS");
         // if the compiler did not work, then remove the
         // CMake(LANG)Compiler.cmake file so that it will get tested the
         // next time cmake is run
         if (!mf->IsOn(compilerWorks)) {
-          std::string compilerLangFile = rootBin;
-          compilerLangFile += "/CMake";
-          compilerLangFile += lang;
-          compilerLangFile += "Compiler.cmake";
+          std::string compilerLangFile =
+            cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
           cmSystemTools::RemoveFile(compilerLangFile);
         }
       } // end if in try compile
     }   // end need test language
     // Store the shared library flags so that we can satisfy CMP0018
-    std::string sharedLibFlagsVar = "CMAKE_SHARED_LIBRARY_";
-    sharedLibFlagsVar += lang;
-    sharedLibFlagsVar += "_FLAGS";
+    std::string sharedLibFlagsVar =
+      cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
     this->LanguageToOriginalSharedLibFlags[lang] =
       mf->GetSafeDefinition(sharedLibFlagsVar);
 
@@ -884,10 +879,9 @@
   // Now load files that can override any settings on the platform or for
   // the project First load the project compatibility file if it is in
   // cmake
-  std::string projectCompatibility = cmSystemTools::GetCMakeRoot();
-  projectCompatibility += "/Modules/";
-  projectCompatibility += mf->GetSafeDefinition("PROJECT_NAME");
-  projectCompatibility += "Compatibility.cmake";
+  std::string projectCompatibility =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/",
+             mf->GetSafeDefinition("PROJECT_NAME"), "Compatibility.cmake");
   if (cmSystemTools::FileExists(projectCompatibility)) {
     mf->ReadListFile(projectCompatibility);
   }
@@ -1097,8 +1091,7 @@
 {
   // use LanguageToLinkerPreference to detect whether this functions has
   // run before
-  if (this->LanguageToLinkerPreference.find(l) !=
-      this->LanguageToLinkerPreference.end()) {
+  if (cmContains(this->LanguageToLinkerPreference, l)) {
     return;
   }
 
@@ -1121,8 +1114,8 @@
   }
 
   if (preference < 0) {
-    std::string msg = linkerPrefVar;
-    msg += " is negative, adjusting it to 0";
+    std::string msg =
+      cmStrCat(linkerPrefVar, " is negative, adjusting it to 0");
     cmSystemTools::Message(msg, "Warning");
     preference = 0;
   }
@@ -1148,8 +1141,7 @@
   std::string ignoreExtensionsVar =
     std::string("CMAKE_") + std::string(l) + std::string("_IGNORE_EXTENSIONS");
   std::string ignoreExts = mf->GetSafeDefinition(ignoreExtensionsVar);
-  std::vector<std::string> extensionList;
-  cmSystemTools::ExpandListArgument(ignoreExts, extensionList);
+  std::vector<std::string> extensionList = cmExpandedList(ignoreExts);
   for (std::string const& i : extensionList) {
     this->IgnoreExtensions[i] = true;
   }
@@ -1161,8 +1153,7 @@
   std::string extensionsVar = std::string("CMAKE_") + std::string(l) +
     std::string("_SOURCE_FILE_EXTENSIONS");
   const std::string& exts = mf->GetSafeDefinition(extensionsVar);
-  std::vector<std::string> extensionList;
-  cmSystemTools::ExpandListArgument(exts, extensionList);
+  std::vector<std::string> extensionList = cmExpandedList(exts);
   for (std::string const& i : extensionList) {
     this->ExtensionToLanguage[i] = l;
   }
@@ -1277,10 +1268,8 @@
       msg << "Configuring incomplete, errors occurred!";
       const char* logs[] = { "CMakeOutput.log", "CMakeError.log", nullptr };
       for (const char** log = logs; *log; ++log) {
-        std::string f = this->CMakeInstance->GetHomeOutputDirectory();
-        f += "/CMakeFiles";
-        f += "/";
-        f += *log;
+        std::string f = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+                                 "/CMakeFiles/", *log);
         if (cmSystemTools::FileExists(f)) {
           msg << "\nSee also \"" << f << "\".";
         }
@@ -1392,6 +1381,11 @@
     return false;
   }
 
+  // Add automatically generated sources (e.g. unity build).
+  if (!this->AddAutomaticSources()) {
+    return false;
+  }
+
   // Add generator specific helper commands
   for (cmLocalGenerator* localGen : this->LocalGenerators) {
     localGen->AddHelperCommands();
@@ -1447,6 +1441,10 @@
     return false;
   }
 
+  if (this->CheckTargetsForPchCompilePdb()) {
+    return false;
+  }
+
   for (cmLocalGenerator* localGen : this->LocalGenerators) {
     localGen->ComputeHomeRelativeOutputPath();
   }
@@ -1497,7 +1495,7 @@
 
   this->WriteSummary();
 
-  if (this->ExtraGenerator != nullptr) {
+  if (this->ExtraGenerator) {
     this->ExtraGenerator->Generate();
   }
 
@@ -1547,7 +1545,7 @@
 
 bool cmGlobalGenerator::QtAutoGen()
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmQtAutoGenGlobalInitializer initializer(this->LocalGenerators);
   return initializer.generate();
 #else
@@ -1555,6 +1553,21 @@
 #endif
 }
 
+bool cmGlobalGenerator::AddAutomaticSources()
+{
+  for (cmLocalGenerator* lg : this->LocalGenerators) {
+    lg->CreateEvaluationFileOutputs();
+    for (cmGeneratorTarget* gt : lg->GetGeneratorTargets()) {
+      if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+        continue;
+      }
+      lg->AddUnityBuild(gt);
+      lg->AddPchDependencies(gt);
+    }
+  }
+  return true;
+}
+
 cmLinkLineComputer* cmGlobalGenerator::CreateLinkLineComputer(
   cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
 {
@@ -1606,8 +1619,8 @@
         mf->GetConfigurations(configs);
 
         for (std::string const& c : configs) {
-          std::string defPropName = "COMPILE_DEFINITIONS_";
-          defPropName += cmSystemTools::UpperCase(c);
+          std::string defPropName =
+            cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
           t->AppendProperty(defPropName, mf->GetProperty(defPropName));
         }
       }
@@ -1621,9 +1634,8 @@
         "CMAKE_" + li + "_STANDARD_INCLUDE_DIRECTORIES";
       std::string const& standardIncludesStr =
         mf->GetSafeDefinition(standardIncludesVar);
-      std::vector<std::string> standardIncludesVec;
-      cmSystemTools::ExpandListArgument(standardIncludesStr,
-                                        standardIncludesVec);
+      std::vector<std::string> standardIncludesVec =
+        cmExpandedList(standardIncludesStr);
       standardIncludesSet.insert(standardIncludesVec.begin(),
                                  standardIncludesVec.end());
     }
@@ -1710,17 +1722,15 @@
         continue;
       }
       for (auto const& lib : target.second.GetOriginalLinkLibraries()) {
-        if (lib.first.size() > 9 &&
-            cmSystemTools::IsNOTFOUND(lib.first.c_str())) {
+        if (lib.first.size() > 9 && cmIsNOTFOUND(lib.first)) {
           std::string varName = lib.first.substr(0, lib.first.size() - 9);
           if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
             varName += " (ADVANCED)";
           }
-          std::string text = notFoundMap[varName];
-          text += "\n    linked by target \"";
-          text += target.second.GetName();
-          text += "\" in directory ";
-          text += this->Makefiles[i]->GetCurrentSourceDirectory();
+          std::string text =
+            cmStrCat(notFoundMap[varName], "\n    linked by target \"",
+                     target.second.GetName(), "\" in directory ",
+                     this->Makefiles[i]->GetCurrentSourceDirectory());
           notFoundMap[varName] = text;
         }
       }
@@ -1734,17 +1744,18 @@
       std::string incDirs = cmGeneratorExpression::Preprocess(
         incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
 
-      cmSystemTools::ExpandListArgument(incDirs, incs);
+      cmExpandList(incDirs, incs);
 
       for (std::string const& incDir : incs) {
-        if (incDir.size() > 9 && cmSystemTools::IsNOTFOUND(incDir.c_str())) {
+        if (incDir.size() > 9 && cmIsNOTFOUND(incDir)) {
           std::string varName = incDir.substr(0, incDir.size() - 9);
           if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
             varName += " (ADVANCED)";
           }
-          std::string text = notFoundMap[varName];
-          text += "\n   used as include directory in directory ";
-          text += this->Makefiles[i]->GetCurrentSourceDirectory();
+          std::string text =
+            cmStrCat(notFoundMap[varName],
+                     "\n   used as include directory in directory ",
+                     this->Makefiles[i]->GetCurrentSourceDirectory());
           notFoundMap[varName] = text;
         }
       }
@@ -1843,8 +1854,8 @@
   output += "\n";
   if (workdir.Failed()) {
     cmSystemTools::SetRunCommandHideConsole(hideconsole);
-    std::string err = "Failed to change directory: ";
-    err += std::strerror(workdir.GetLastResult());
+    std::string err = cmStrCat("Failed to change directory: ",
+                               std::strerror(workdir.GetLastResult()));
     cmSystemTools::Error(err);
     output += err;
     output += "\n";
@@ -1949,8 +1960,8 @@
   const std::string& native, bool ignoreErrors)
 {
   std::string makeCommand = cmSystemTools::GetCMakeCommand();
-  makeCommand = cmSystemTools::ConvertToOutputPath(makeCommand);
-  makeCommand += " --build .";
+  makeCommand =
+    cmStrCat(cmSystemTools::ConvertToOutputPath(makeCommand), " --build .");
   if (!config.empty()) {
     makeCommand += " --config \"";
     makeCommand += config;
@@ -2053,8 +2064,8 @@
   if (!gen->ConfiguredFilesPath.empty()) {
     this->ConfiguredFilesPath = gen->ConfiguredFilesPath;
   } else {
-    this->ConfiguredFilesPath = gen->CMakeInstance->GetHomeOutputDirectory();
-    this->ConfiguredFilesPath += "/CMakeFiles";
+    this->ConfiguredFilesPath =
+      cmStrCat(gen->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
   }
 }
 
@@ -2095,7 +2106,7 @@
     return true;
   }
   if (const char* exclude = target->GetProperty("EXCLUDE_FROM_ALL")) {
-    return cmSystemTools::IsOn(exclude);
+    return cmIsOn(exclude);
   }
   // This target is included in its directory.  Check whether the
   // directory is excluded.
@@ -2162,7 +2173,7 @@
 
 bool cmGlobalGenerator::IsAlias(const std::string& name) const
 {
-  return this->AliasTargets.find(name) != this->AliasTargets.end();
+  return cmContains(this->AliasTargets, name);
 }
 
 void cmGlobalGenerator::IndexTarget(cmTarget* t)
@@ -2278,14 +2289,6 @@
   return false;
 }
 
-inline std::string removeQuotes(const std::string& s)
-{
-  if (s.front() == '\"' && s.back() == '\"') {
-    return s.substr(1, s.size() - 2);
-  }
-  return s;
-}
-
 bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
                                      std::string const& reason) const
 {
@@ -2340,8 +2343,8 @@
   std::vector<GlobalTargetInfo>& targets)
 {
   cmMakefile* mf = this->Makefiles[0];
-  std::string configFile = mf->GetCurrentBinaryDirectory();
-  configFile += "/CPackConfig.cmake";
+  std::string configFile =
+    cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackConfig.cmake");
   if (!cmSystemTools::FileExists(configFile)) {
     return;
   }
@@ -2373,7 +2376,7 @@
   } else {
     const char* noPackageAll =
       mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY");
-    if (!noPackageAll || cmSystemTools::IsOff(noPackageAll)) {
+    if (!noPackageAll || cmIsOff(noPackageAll)) {
       gti.Depends.emplace_back(this->GetAllTargetName());
     }
   }
@@ -2389,8 +2392,8 @@
   }
 
   cmMakefile* mf = this->Makefiles[0];
-  std::string configFile = mf->GetCurrentBinaryDirectory();
-  configFile += "/CPackSourceConfig.cmake";
+  std::string configFile =
+    cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackSourceConfig.cmake");
   if (!cmSystemTools::FileExists(configFile)) {
     return;
   }
@@ -2542,7 +2545,7 @@
     } else {
       const char* noall =
         mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
-      if (!noall || cmSystemTools::IsOff(noall)) {
+      if (!noall || cmIsOff(noall)) {
         gti.Depends.emplace_back(this->GetAllTargetName());
       }
     }
@@ -2625,7 +2628,7 @@
   // If this property is defined, let the setter turn this on or off...
   //
   if (prop) {
-    return cmSystemTools::IsOn(prop);
+    return cmIsOn(prop);
   }
 
   // By default, this feature is OFF, since it is not supported in the
@@ -2649,7 +2652,7 @@
   cmCustomCommand cc(nullptr, no_outputs, no_byproducts, no_depends,
                      gti.CommandLines, nullptr, gti.WorkingDir.c_str());
   cc.SetUsesTerminal(gti.UsesTerminal);
-  target.AddPostBuildCommand(cc);
+  target.AddPostBuildCommand(std::move(cc));
   if (!gti.Message.empty()) {
     target.SetProperty("EchoString", gti.Message.c_str());
   }
@@ -2669,8 +2672,7 @@
 std::string cmGlobalGenerator::GenerateRuleFile(
   std::string const& output) const
 {
-  std::string ruleFile = output;
-  ruleFile += ".rule";
+  std::string ruleFile = cmStrCat(output, ".rule");
   const char* dir = this->GetCMakeCFGIntDir();
   if (dir && dir[0] == '$') {
     cmSystemTools::ReplaceString(ruleFile, dir, "/CMakeFiles");
@@ -2720,15 +2722,14 @@
                                     "clean",     "edit_cache", "rebuild_cache",
                                     "ZERO_CHECK" };
 
-  return std::find(cm::cbegin(reservedTargets), cm::cend(reservedTargets),
-                   name) != cm::cend(reservedTargets);
+  return cmContains(reservedTargets, name);
 }
 
 void cmGlobalGenerator::SetExternalMakefileProjectGenerator(
   cmExternalMakefileProjectGenerator* extraGenerator)
 {
-  this->ExtraGenerator = extraGenerator;
-  if (this->ExtraGenerator != nullptr) {
+  this->ExtraGenerator.reset(extraGenerator);
+  if (this->ExtraGenerator) {
     this->ExtraGenerator->SetGlobalGenerator(this);
   }
 }
@@ -2837,7 +2838,7 @@
 void cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs,
                                     std::string const& content)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   // Ignore if there are no outputs.
   if (outputs.empty()) {
     return;
@@ -2867,11 +2868,9 @@
 
 void cmGlobalGenerator::CheckRuleHashes()
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory();
-  std::string pfile = home;
-  pfile += "/CMakeFiles";
-  pfile += "/CMakeRuleHashes.txt";
+  std::string pfile = cmStrCat(home, "/CMakeFiles/CMakeRuleHashes.txt");
   this->CheckRuleHashes(pfile, home);
   this->WriteRuleHashes(pfile);
 #endif
@@ -2947,9 +2946,8 @@
 void cmGlobalGenerator::WriteSummary()
 {
   // Record all target directories in a central location.
-  std::string fname = this->CMakeInstance->GetHomeOutputDirectory();
-  fname += "/CMakeFiles";
-  fname += "/TargetDirectories.txt";
+  std::string fname = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+                               "/CMakeFiles/TargetDirectories.txt");
   cmGeneratedFileStream fout(fname);
 
   for (cmLocalGenerator* lg : this->LocalGenerators) {
@@ -2967,11 +2965,10 @@
 {
   // Place the labels file in a per-target support directory.
   std::string dir = target->GetSupportDirectory();
-  std::string file = dir;
-  file += "/Labels.txt";
+  std::string file = cmStrCat(dir, "/Labels.txt");
   std::string json_file = dir + "/Labels.json";
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   // Check whether labels are enabled for this target.
   const char* targetLabels = target->GetProperty("LABELS");
   const char* directoryLabels =
@@ -2993,7 +2990,7 @@
     // List the target-wide labels.  All sources in the target get
     // these labels.
     if (targetLabels) {
-      cmSystemTools::ExpandListArgument(targetLabels, labels);
+      cmExpandList(targetLabels, labels);
       if (!labels.empty()) {
         fout << "# Target labels\n";
         for (std::string const& l : labels) {
@@ -3008,12 +3005,11 @@
     std::vector<std::string> cmakeDirectoryLabelsList;
 
     if (directoryLabels) {
-      cmSystemTools::ExpandListArgument(directoryLabels, directoryLabelsList);
+      cmExpandList(directoryLabels, directoryLabelsList);
     }
 
     if (cmakeDirectoryLabels) {
-      cmSystemTools::ExpandListArgument(cmakeDirectoryLabels,
-                                        cmakeDirectoryLabelsList);
+      cmExpandList(cmakeDirectoryLabels, cmakeDirectoryLabelsList);
     }
 
     if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) {
@@ -3033,24 +3029,21 @@
     // List the source files with any per-source labels.
     fout << "# Source files and their labels\n";
     std::vector<cmSourceFile*> sources;
-    std::vector<std::string> configs;
-    target->Target->GetMakefile()->GetConfigurations(configs);
-    if (configs.empty()) {
-      configs.emplace_back();
-    }
+    std::vector<std::string> const& configs =
+      target->Target->GetMakefile()->GetGeneratorConfigs();
     for (std::string const& c : configs) {
       target->GetSourceFiles(sources, c);
     }
     auto const sourcesEnd = cmRemoveDuplicates(sources);
     for (cmSourceFile* sf : cmMakeRange(sources.cbegin(), sourcesEnd)) {
       Json::Value& lj_source = lj_sources.append(Json::objectValue);
-      std::string const& sfp = sf->GetFullPath();
+      std::string const& sfp = sf->ResolveFullPath();
       fout << sfp << "\n";
       lj_source["file"] = sfp;
       if (const char* svalue = sf->GetProperty("LABELS")) {
         labels.clear();
         Json::Value& lj_source_labels = lj_source["labels"] = Json::arrayValue;
-        cmSystemTools::ExpandListArgument(svalue, labels);
+        cmExpandList(svalue, labels);
         for (std::string const& label : labels) {
           fout << " " << label << "\n";
           lj_source_labels.append(label);
@@ -3104,14 +3097,6 @@
   return this->FilenameTargetDepends[sf];
 }
 
-void cmGlobalGenerator::CreateEvaluationSourceFiles(
-  std::string const& config) const
-{
-  for (cmLocalGenerator* localGen : this->LocalGenerators) {
-    localGen->CreateEvaluationFileOutputs(config);
-  }
-}
-
 void cmGlobalGenerator::ProcessEvaluationFiles()
 {
   std::vector<std::string> generatedFiles;
@@ -3137,8 +3122,8 @@
   std::vector<std::string> configs;
   std::string config = mf->GetConfigurations(configs, false);
 
-  std::string path = this->CMakeInstance->GetHomeOutputDirectory();
-  path += "/CPackProperties.cmake";
+  std::string path = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+                              "/CPackProperties.cmake");
 
   if (!cmSystemTools::FileExists(path) && installedFiles.empty()) {
     return true;
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 5008534..f25ff7b 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -7,25 +7,29 @@
 
 #include <iosfwd>
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
 #include <unordered_map>
 #include <utility>
 #include <vector>
 
+#include "cm_codecvt.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCustomCommandLines.h"
 #include "cmDuration.h"
-#include "cmExportSetMap.h"
+#include "cmExportSet.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
-#include "cm_codecvt.hxx"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-#  include "cmFileLockPool.h"
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cm_jsoncpp_value.h"
+
+#  include "cmFileLockPool.h"
 #endif
 
 #define CMAKE_DIRECTORY_ID_SEP "::@"
@@ -107,7 +111,7 @@
     return codecvt::None;
   }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   /** Get a JSON object describing the generator.  */
   virtual Json::Value GetJson() const;
 #endif
@@ -374,7 +378,7 @@
   virtual std::string GetEditCacheCommand() const { return ""; }
 
   // Class to track a set of dependencies.
-  typedef cmTargetDependSet TargetDependSet;
+  using TargetDependSet = cmTargetDependSet;
 
   // what targets does the specified target depend on directly
   // via a target_link_libraries or add_dependencies
@@ -453,14 +457,12 @@
 
   bool GenerateCPackPropertiesFile();
 
-  void CreateEvaluationSourceFiles(std::string const& config) const;
-
   void SetFilenameTargetDepends(
     cmSourceFile* sf, std::set<cmGeneratorTarget const*> const& tgts);
   const std::set<const cmGeneratorTarget*>& GetFilenameTargetDepends(
     cmSourceFile* sf) const;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   cmFileLockPool& GetFileLockPool() { return FileLockPool; }
 #endif
 
@@ -474,7 +476,7 @@
   int RecursionDepth;
 
 protected:
-  typedef std::vector<cmLocalGenerator*> GeneratorVector;
+  using GeneratorVector = std::vector<cmLocalGenerator*>;
   // for a project collect all its targets by following depend
   // information, and also collect all the targets
   void GetTargetSets(TargetDependSet& projectTargets,
@@ -499,6 +501,8 @@
   /// @return true on success
   bool QtAutoGen();
 
+  bool AddAutomaticSources();
+
   std::string SelectMakeProgram(const std::string& makeProgram,
                                 const std::string& makeDefault = "") const;
 
@@ -553,17 +557,15 @@
   cmTarget* FindTargetImpl(std::string const& name) const;
 
   cmGeneratorTarget* FindGeneratorTargetImpl(std::string const& name) const;
-  cmGeneratorTarget* FindImportedGeneratorTargetImpl(
-    std::string const& name) const;
 
   const char* GetPredefinedTargetsFolder();
 
 private:
-  typedef std::unordered_map<std::string, cmTarget*> TargetMap;
-  typedef std::unordered_map<std::string, cmGeneratorTarget*>
-    GeneratorTargetMap;
-  typedef std::unordered_map<std::string, cmMakefile*> MakefileMap;
-  typedef std::unordered_map<std::string, cmLocalGenerator*> LocalGeneratorMap;
+  using TargetMap = std::unordered_map<std::string, cmTarget*>;
+  using GeneratorTargetMap =
+    std::unordered_map<std::string, cmGeneratorTarget*>;
+  using MakefileMap = std::unordered_map<std::string, cmMakefile*>;
+  using LocalGeneratorMap = std::unordered_map<std::string, cmLocalGenerator*>;
   // Map efficiently from target name to cmTarget instance.
   // Do not use this structure for looping over all targets.
   // It contains both normal and globally visible imported targets.
@@ -610,6 +612,7 @@
 
   bool CheckTargetsForMissingSources() const;
   bool CheckTargetsForType() const;
+  bool CheckTargetsForPchCompilePdb() const;
 
   void CreateLocalGenerators();
 
@@ -618,13 +621,13 @@
 
   void ComputeBuildFileGenerators();
 
-  cmExternalMakefileProjectGenerator* ExtraGenerator;
+  std::unique_ptr<cmExternalMakefileProjectGenerator> ExtraGenerator;
 
   // track files replaced during a Generate
   std::vector<std::string> FilesReplacedDuringGenerate;
 
   // Store computed inter-target dependencies.
-  typedef std::map<cmGeneratorTarget const*, TargetDependSet> TargetDependMap;
+  using TargetDependMap = std::map<cmGeneratorTarget const*, TargetDependSet>;
   TargetDependMap TargetDependencies;
 
   friend class cmake;
@@ -663,7 +666,7 @@
   mutable std::map<cmSourceFile*, std::set<cmGeneratorTarget const*>>
     FilenameTargetDepends;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   // Pool of file locks
   cmFileLockPool FileLockPool;
 #endif
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index b69dea0..5a708ab 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -2,6 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalGhsMultiGenerator.h"
 
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <ostream>
+#include <utility>
+
+#include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
@@ -11,16 +18,11 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <map>
-#include <ostream>
-#include <string.h>
-#include <utility>
-
 const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
 #ifdef __linux__
 const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
@@ -55,11 +57,9 @@
   cmGeneratorTarget* gt) const
 {
   // Compute full path to object file directory for this target.
-  std::string dir;
-  dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/";
-  dir += gt->LocalGenerator->GetTargetDirectory(gt);
-  dir += "/";
+  std::string dir =
+    cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             gt->LocalGenerator->GetTargetDirectory(gt), '/');
   gt->ObjectDirectory = dir;
 }
 
@@ -76,10 +76,9 @@
   }
   if (ts.empty()) {
     std::string message;
-    message =
-      "Green Hills MULTI: -T <toolset> not specified; defaulting to \"";
-    message += tsp;
-    message += "\"";
+    message = cmStrCat(
+      "Green Hills MULTI: -T <toolset> not specified; defaulting to \"", tsp,
+      '"');
     cmSystemTools::Message(message);
 
     /* store the full toolset for later use
@@ -97,12 +96,11 @@
 
   /* check if the toolset changed from last generate */
   if (prevTool != nullptr && (gbuild != prevTool)) {
-    std::string message = "toolset build tool: ";
-    message += gbuild;
-    message += "\nDoes not match the previously used build tool: ";
-    message += prevTool;
-    message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
-               "directory or choose a different binary directory.";
+    std::string message =
+      cmStrCat("toolset build tool: ", gbuild,
+               "\nDoes not match the previously used build tool: ", prevTool,
+               "\nEither remove the CMakeCache.txt file and CMakeFiles "
+               "directory or choose a different binary directory.");
     cmSystemTools::Error(message);
     return false;
   }
@@ -111,7 +109,7 @@
   mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(),
                          "build program to use", cmStateEnums::INTERNAL, true);
 
-  mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp.c_str());
+  mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp);
 
   return true;
 }
@@ -138,24 +136,20 @@
   /* check if OS location has been updated by platform scripts */
   std::string platform = mf->GetSafeDefinition("GHS_TARGET_PLATFORM");
   std::string osdir = mf->GetSafeDefinition("GHS_OS_DIR");
-  if (cmSystemTools::IsOff(osdir.c_str()) &&
-      platform.find("integrity") != std::string::npos) {
+  if (cmIsOff(osdir) && platform.find("integrity") != std::string::npos) {
     if (!this->CMakeInstance->GetIsInTryCompile()) {
       /* required OS location is not found */
-      std::string m =
-        "Green Hills MULTI: GHS_OS_DIR not specified; No OS found in \"";
-      m += mf->GetSafeDefinition("GHS_OS_ROOT");
-      m += "\"";
+      std::string m = cmStrCat(
+        "Green Hills MULTI: GHS_OS_DIR not specified; No OS found in \"",
+        mf->GetSafeDefinition("GHS_OS_ROOT"), '"');
       cmSystemTools::Message(m);
     }
     osdir = "GHS_OS_DIR-NOT-SPECIFIED";
   } else if (!this->CMakeInstance->GetIsInTryCompile() &&
-             cmSystemTools::IsOff(this->OsDir) &&
-             !cmSystemTools::IsOff(osdir)) {
+             cmIsOff(this->OsDir) && !cmIsOff(osdir)) {
     /* OS location was updated by auto-selection */
-    std::string m = "Green Hills MULTI: GHS_OS_DIR not specified; found \"";
-    m += osdir;
-    m += "\"";
+    std::string m = cmStrCat(
+      "Green Hills MULTI: GHS_OS_DIR not specified; found \"", osdir, '"');
     cmSystemTools::Message(m);
   }
   this->OsDir = osdir;
@@ -163,17 +157,15 @@
   // Determine GHS_BSP_NAME
   std::string bspName = mf->GetSafeDefinition("GHS_BSP_NAME");
 
-  if (cmSystemTools::IsOff(bspName.c_str()) &&
-      platform.find("integrity") != std::string::npos) {
+  if (cmIsOff(bspName) && platform.find("integrity") != std::string::npos) {
     bspName = "sim" + arch;
     /* write back the calculate name for next time */
     mf->AddCacheDefinition("GHS_BSP_NAME", bspName.c_str(),
                            "Name of GHS target platform.",
                            cmStateEnums::STRING, true);
-    std::string m =
-      "Green Hills MULTI: GHS_BSP_NAME not specified; defaulting to \"";
-    m += bspName;
-    m += "\"";
+    std::string m = cmStrCat(
+      "Green Hills MULTI: GHS_BSP_NAME not specified; defaulting to \"",
+      bspName, '"');
     cmSystemTools::Message(m);
   }
 
@@ -334,18 +326,18 @@
   // Specify BSP option if supplied by user
   const char* bspName =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME");
-  if (!cmSystemTools::IsOff(bspName)) {
+  if (!cmIsOff(bspName)) {
     fout << "    -bsp " << bspName << std::endl;
   }
 
   // Specify OS DIR if supplied by user
   // -- not all platforms require this entry in the project file
-  if (!cmSystemTools::IsOff(this->OsDir.c_str())) {
+  if (!cmIsOff(this->OsDir)) {
     const char* osDirOption =
       this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION");
     std::replace(this->OsDir.begin(), this->OsDir.end(), '\\', '/');
     fout << "    ";
-    if (cmSystemTools::IsOff(osDirOption)) {
+    if (cmIsOff(osDirOption)) {
       fout << "";
     } else {
       fout << osDirOption;
@@ -404,8 +396,8 @@
 
 void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
 {
-  std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
-  rootBinaryDir += "/CMakeFiles";
+  std::string rootBinaryDir =
+    cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeFiles");
 
   // All known targets
   for (cmGeneratorTarget const* target : this->ProjectTargets) {
@@ -418,17 +410,17 @@
     }
 
     // create target build file
-    std::string name = target->GetName() + ".tgt" + FILE_EXTENSION;
-    std::string fname = rootBinaryDir + "/" + name;
+    std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION);
+    std::string fname = cmStrCat(rootBinaryDir, "/", name);
     cmGeneratedFileStream fbld(fname);
     fbld.SetCopyIfDifferent(true);
     this->WriteFileHeader(fbld);
     GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
     std::vector<cmGeneratorTarget const*> build;
     if (ComputeTargetBuildOrder(target, build)) {
-      std::string message = "The inter-target dependency graph for target [" +
-        target->GetName() + "] had a cycle.\n";
-      cmSystemTools::Error(message);
+      cmSystemTools::Error(
+        cmStrCat("The inter-target dependency graph for target [",
+                 target->GetName(), "] had a cycle.\n"));
     } else {
       for (auto& tgt : build) {
         WriteProjectLine(fbld, tgt, root, rootBinaryDir);
@@ -469,7 +461,7 @@
     if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       continue;
     }
-    if (!cmSystemTools::IsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) {
+    if (!cmIsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) {
       defaultTargets.push_back(t);
     }
   }
@@ -480,8 +472,8 @@
     cmSystemTools::Error(message);
   } else {
     // determine the targets for ALL target
-    std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
-    rootBinaryDir += "/CMakeFiles";
+    std::string rootBinaryDir =
+      cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeFiles");
     for (cmGeneratorTarget const* target : build) {
       if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
           target->GetType() == cmStateEnums::MODULE_LIBRARY ||
@@ -539,11 +531,8 @@
    * with target projects.  This avoid the issue where the project has
    * the same name as the executable target.
    */
-  fname = root->GetCurrentBinaryDirectory();
-  fname += "/";
-  fname += root->GetProjectName();
-  fname += ".top";
-  fname += FILE_EXTENSION;
+  fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
+                   root->GetProjectName(), ".top", FILE_EXTENSION);
 
   cmGeneratedFileStream top(fname);
   top.SetCopyIfDifferent(true);
@@ -588,16 +577,14 @@
     /* if multiple top-projects are found in build directory
      * then prefer projectName top-project.
      */
-    auto p = std::find(files.begin(), files.end(), proj);
-    if (p == files.end()) {
+    if (!cmContains(files, proj)) {
       proj = files.at(0);
     }
   }
 
   makeCommand.Add("-top", proj);
   if (!targetNames.empty()) {
-    if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
-        targetNames.end()) {
+    if (cmContains(targetNames, "clean")) {
       makeCommand.Add("-clean");
     } else {
       for (const auto& tname : targetNames) {
@@ -623,8 +610,8 @@
   char const* ghsGpjMacros =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
   if (nullptr != ghsGpjMacros) {
-    std::vector<std::string> expandedList;
-    cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList);
+    std::vector<std::string> expandedList =
+      cmExpandedList(std::string(ghsGpjMacros));
     for (std::string const& arg : expandedList) {
       fout << "macro " << arg << std::endl;
     }
@@ -646,10 +633,7 @@
       this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
     const char* p =
       this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM");
-    tgt = (a ? a : "");
-    tgt += "_";
-    tgt += (p ? p : "");
-    tgt += ".tgt";
+    tgt = cmStrCat((a ? a : ""), '_', (p ? p : ""), ".tgt");
   }
 
   fout << "primaryTarget=" << tgt << std::endl;
diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h
index 98358c7..ccfe073 100644
--- a/Source/cmGlobalGhsMultiGenerator.h
+++ b/Source/cmGlobalGhsMultiGenerator.h
@@ -3,17 +3,16 @@
 #ifndef cmGhsMultiGenerator_h
 #define cmGhsMultiGenerator_h
 
-#include "cmGlobalGenerator.h"
-
-#include "cmGlobalGeneratorFactory.h"
-#include "cmTargetDepend.h"
-
 #include <iosfwd>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmGlobalGenerator.h"
+#include "cmGlobalGeneratorFactory.h"
+#include "cmTargetDepend.h"
+
 class cmGeneratorTarget;
 class cmLocalGenerator;
 class cmMakefile;
@@ -148,12 +147,11 @@
   : public std::multiset<cmTargetDepend,
                          cmGlobalGhsMultiGenerator::TargetCompare>
 {
-  typedef std::multiset<cmTargetDepend,
-                        cmGlobalGhsMultiGenerator::TargetCompare>
-    derived;
+  using derived =
+    std::multiset<cmTargetDepend, cmGlobalGhsMultiGenerator::TargetCompare>;
 
 public:
-  typedef cmGlobalGenerator::TargetDependSet TargetDependSet;
+  using TargetDependSet = cmGlobalGenerator::TargetDependSet;
   OrderedTargetDependSet(TargetDependSet const&, std::string const& first);
 };
 
diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h
index df3aec9..fc39ddf 100644
--- a/Source/cmGlobalJOMMakefileGenerator.h
+++ b/Source/cmGlobalJOMMakefileGenerator.h
@@ -3,10 +3,10 @@
 #ifndef cmGlobalJOMMakefileGenerator_h
 #define cmGlobalJOMMakefileGenerator_h
 
-#include "cmGlobalUnixMakefileGenerator3.h"
-
 #include <iosfwd>
 
+#include "cmGlobalUnixMakefileGenerator3.h"
+
 /** \class cmGlobalJOMMakefileGenerator
  * \brief Write a JOM makefiles.
  *
diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx
index 7b58389..ae9d5a7 100644
--- a/Source/cmGlobalMSYSMakefileGenerator.cxx
+++ b/Source/cmGlobalMSYSMakefileGenerator.cxx
@@ -24,8 +24,7 @@
 std::string cmGlobalMSYSMakefileGenerator::FindMinGW(
   std::string const& makeloc)
 {
-  std::string fstab = makeloc;
-  fstab += "/../etc/fstab";
+  std::string fstab = cmStrCat(makeloc, "/../etc/fstab");
   cmsys::ifstream fin(fstab.c_str());
   std::string path;
   std::string mount;
@@ -34,8 +33,7 @@
     fin >> path;
     fin >> mount;
     if (mount == "/mingw") {
-      mingwBin = path;
-      mingwBin += "/bin";
+      mingwBin = cmStrCat(path, "/bin");
     }
   }
   return mingwBin;
@@ -69,9 +67,9 @@
     rc = trc;
   }
   mf->AddDefinition("MSYS", "1");
-  mf->AddDefinition("CMAKE_GENERATOR_CC", gcc.c_str());
-  mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx.c_str());
-  mf->AddDefinition("CMAKE_GENERATOR_RC", rc.c_str());
+  mf->AddDefinition("CMAKE_GENERATOR_CC", gcc);
+  mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx);
+  mf->AddDefinition("CMAKE_GENERATOR_RC", rc);
   this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional);
 
   if (!mf->IsSet("CMAKE_AR") && !this->CMakeInstance->GetIsInTryCompile() &&
diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx
index e218b4b..d9fc505 100644
--- a/Source/cmGlobalMinGWMakefileGenerator.cxx
+++ b/Source/cmGlobalMinGWMakefileGenerator.cxx
@@ -44,9 +44,9 @@
   if (!trc.empty()) {
     rc = trc;
   }
-  mf->AddDefinition("CMAKE_GENERATOR_CC", gcc.c_str());
-  mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx.c_str());
-  mf->AddDefinition("CMAKE_GENERATOR_RC", rc.c_str());
+  mf->AddDefinition("CMAKE_GENERATOR_CC", gcc);
+  mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx);
+  mf->AddDefinition("CMAKE_GENERATOR_RC", rc);
   this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional);
 }
 
diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h
index 2fdf1ce..4586b77 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.h
+++ b/Source/cmGlobalNMakeMakefileGenerator.h
@@ -3,10 +3,10 @@
 #ifndef cmGlobalNMakeMakefileGenerator_h
 #define cmGlobalNMakeMakefileGenerator_h
 
-#include "cmGlobalUnixMakefileGenerator3.h"
-
 #include <iosfwd>
 
+#include "cmGlobalUnixMakefileGenerator3.h"
+
 /** \class cmGlobalNMakeMakefileGenerator
  * \brief Write a NMake makefiles.
  *
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 2d52356..da21d6c 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -2,16 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalNinjaGenerator.h"
 
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <iterator>
+#include <sstream>
+
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
 #include "cm_jsoncpp_reader.h"
 #include "cm_jsoncpp_value.h"
 #include "cm_jsoncpp_writer.h"
-#include "cmsys/FStream.hxx"
-#include <algorithm>
-#include <ctype.h>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
 
 #include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
@@ -19,6 +22,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
 #include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalNinjaGenerator.h"
@@ -31,6 +35,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
@@ -381,7 +386,7 @@
   }
 
   // Do not add a variable if the value is empty.
-  std::string val = cmSystemTools::TrimWhitespace(value);
+  std::string val = cmTrimWhitespace(value);
   if (val.empty()) {
     return;
   }
@@ -413,14 +418,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);
@@ -534,7 +531,7 @@
       cmSystemTools::SetFatalErrorOccured();
       return false;
     }
-    this->NinjaVersion = cmSystemTools::TrimWhitespace(version);
+    this->NinjaVersion = cmTrimWhitespace(version);
     this->CheckNinjaFeatures();
   }
   return true;
@@ -554,14 +551,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;
+      cmStrToULong(fv, &dyndep);
+      if (dyndep == 1) {
+        this->NinjaSupportsDyndeps = true;
+      }
     }
   }
 }
@@ -569,8 +574,7 @@
 bool cmGlobalNinjaGenerator::CheckLanguages(
   std::vector<std::string> const& languages, cmMakefile* mf) const
 {
-  if (std::find(languages.begin(), languages.end(), "Fortran") !=
-      languages.end()) {
+  if (cmContains(languages, "Fortran")) {
     return this->CheckFortran(mf);
   }
   return true;
@@ -578,37 +582,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;
@@ -638,7 +630,9 @@
         (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "GNU") ||
         (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "GNU") ||
         (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "Clang") ||
-        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang")))) {
+        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang") ||
+        (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "QCC") ||
+        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "QCC")))) {
     this->UsingGCCOnWindows = true;
   }
 #endif
@@ -713,11 +707,9 @@
   cmGeneratorTarget* gt) const
 {
   // Compute full path to object file directory for this target.
-  std::string dir;
-  dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/";
-  dir += gt->LocalGenerator->GetTargetDirectory(gt);
-  dir += "/";
+  std::string dir =
+    cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             gt->LocalGenerator->GetTargetDirectory(gt), '/');
   gt->ObjectDirectory = dir;
 }
 
@@ -727,9 +719,8 @@
 {
   // Compute Ninja's build file path.
   std::string buildFilePath =
-    this->GetCMakeInstance()->GetHomeOutputDirectory();
-  buildFilePath += "/";
-  buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/',
+             cmGlobalNinjaGenerator::NINJA_BUILD_FILE);
 
   // Get a stream where to generate things.
   if (!this->BuildFileStream) {
@@ -766,9 +757,8 @@
 {
   // Compute Ninja's build file path.
   std::string rulesFilePath =
-    this->GetCMakeInstance()->GetHomeOutputDirectory();
-  rulesFilePath += "/";
-  rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/',
+             cmGlobalNinjaGenerator::NINJA_RULES_FILE);
 
   // Get a stream where to generate things.
   if (!this->RulesFileStream) {
@@ -901,16 +891,6 @@
      << cmVersion::GetMinorVersion() << "\n\n";
 }
 
-void cmGlobalNinjaGenerator::AddDependencyToAll(cmGeneratorTarget* target)
-{
-  this->AppendTargetOutputs(target, this->AllDependencies);
-}
-
-void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input)
-{
-  this->AllDependencies.push_back(input);
-}
-
 void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
 {
   for (auto const& asd : this->AssumedSourceDependencies) {
@@ -1109,59 +1089,32 @@
   cmGlobalNinjaGenerator::WriteDivider(os);
   os << "# Folder targets.\n\n";
 
-  std::string const& rootBinaryDir =
-    this->LocalGenerators[0]->GetBinaryDirectory();
+  std::map<std::string, DirectoryTarget> dirTargets =
+    this->ComputeDirectoryTargets();
 
-  std::map<std::string, cmNinjaDeps> targetsPerFolder;
-  for (cmLocalGenerator const* lg : this->LocalGenerators) {
-    std::string const& currentBinaryFolder(
-      lg->GetStateSnapshot().GetDirectory().GetCurrentBinary());
+  for (auto const& it : dirTargets) {
+    cmNinjaBuild build("phony");
+    cmGlobalNinjaGenerator::WriteDivider(os);
+    std::string const& currentBinaryDir = it.first;
+    DirectoryTarget const& dt = it.second;
 
-    // Do not generate a rule for the root binary dir.
-    if (currentBinaryFolder == rootBinaryDir) {
-      continue;
-    }
-
-    // The directory-level rule should depend on the target-level rules
-    // for all targets in the directory.
-    cmNinjaDeps& folderTargets = targetsPerFolder[currentBinaryFolder];
-    for (auto gt : lg->GetGeneratorTargets()) {
-      cmStateEnums::TargetType const type = gt->GetType();
-      if ((type == cmStateEnums::EXECUTABLE ||
-           type == cmStateEnums::STATIC_LIBRARY ||
-           type == cmStateEnums::SHARED_LIBRARY ||
-           type == cmStateEnums::MODULE_LIBRARY ||
-           type == cmStateEnums::OBJECT_LIBRARY ||
-           type == cmStateEnums::UTILITY) &&
-          !gt->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
-        folderTargets.push_back(gt->GetName());
+    // Setup target
+    build.Comment = "Folder: " + currentBinaryDir;
+    build.Outputs.emplace_back(
+      this->ConvertToNinjaPath(currentBinaryDir + "/all"));
+    for (DirectoryTarget::Target const& t : dt.Targets) {
+      if (!t.ExcludeFromAll) {
+        this->AppendTargetOutputs(t.GT, build.ExplicitDeps);
       }
     }
-
-    // The directory-level rule should depend on the directory-level
-    // rules of the subdirectories.
-    for (cmStateSnapshot const& state : lg->GetStateSnapshot().GetChildren()) {
-      std::string const& currentBinaryDir =
-        state.GetDirectory().GetCurrentBinary();
-      folderTargets.push_back(
-        this->ConvertToNinjaPath(currentBinaryDir + "/all"));
+    for (DirectoryTarget::Dir const& d : dt.Children) {
+      if (!d.ExcludeFromAll) {
+        build.ExplicitDeps.emplace_back(
+          this->ConvertToNinjaPath(d.Path + "/all"));
+      }
     }
-  }
-
-  if (!targetsPerFolder.empty()) {
-    cmNinjaBuild build("phony");
-    build.Outputs.emplace_back("");
-    for (auto& it : targetsPerFolder) {
-      cmGlobalNinjaGenerator::WriteDivider(os);
-      std::string const& currentBinaryDir = it.first;
-
-      // Setup target
-      build.Comment = "Folder: " + currentBinaryDir;
-      build.Outputs[0] = this->ConvertToNinjaPath(currentBinaryDir + "/all");
-      build.ExplicitDeps = std::move(it.second);
-      // Write target
-      this->WriteBuild(os, build);
-    }
+    // Write target
+    this->WriteBuild(os, build);
   }
 }
 
@@ -1294,22 +1247,18 @@
   cmGlobalNinjaGenerator::WriteDivider(os);
   os << "# Built-in targets\n\n";
 
-  this->WriteTargetAll(os);
+  this->WriteTargetDefault(os);
   this->WriteTargetRebuildManifest(os);
   this->WriteTargetClean(os);
   this->WriteTargetHelp(os);
 }
 
-void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
+void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os)
 {
-  cmNinjaBuild build("phony");
-  build.Comment = "The main all target.";
-  build.Outputs.push_back(this->TargetAll);
-  build.ExplicitDeps = this->AllDependencies;
-  this->WriteBuild(os, build);
-
   if (!this->HasOutputPathPrefix()) {
-    cmGlobalNinjaGenerator::WriteDefault(os, build.Outputs,
+    cmNinjaDeps all;
+    all.push_back(this->TargetAll);
+    cmGlobalNinjaGenerator::WriteDefault(os, all,
                                          "Make the all target the default.");
   }
 }
@@ -1323,13 +1272,13 @@
 
   {
     cmNinjaRule rule("RERUN_CMAKE");
-    rule.Command = CMakeCmd();
-    rule.Command += " -S";
-    rule.Command += lg->ConvertToOutputFormat(lg->GetSourceDirectory(),
-                                              cmOutputConverter::SHELL);
-    rule.Command += " -B";
-    rule.Command += lg->ConvertToOutputFormat(lg->GetBinaryDirectory(),
-                                              cmOutputConverter::SHELL);
+    rule.Command =
+      cmStrCat(CMakeCmd(), " -S",
+               lg->ConvertToOutputFormat(lg->GetSourceDirectory(),
+                                         cmOutputConverter::SHELL),
+               " -B",
+               lg->ConvertToOutputFormat(lg->GetBinaryDirectory(),
+                                         cmOutputConverter::SHELL));
     rule.Description = "Re-running CMake...";
     rule.Comment = "Rule for re-running cmake.";
     rule.Generator = true;
@@ -1357,10 +1306,10 @@
   if (this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) {
     {
       cmNinjaRule rule("VERIFY_GLOBS");
-      rule.Command = CMakeCmd();
-      rule.Command += " -P ";
-      rule.Command += lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
-                                                cmOutputConverter::SHELL);
+      rule.Command =
+        cmStrCat(CMakeCmd(), " -P ",
+                 lg->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
+                                           cmOutputConverter::SHELL));
       rule.Description = "Re-checking globbed directories...";
       rule.Comment = "Rule for re-checking globbed directories.";
       rule.Generator = true;
@@ -1466,9 +1415,8 @@
 {
   cmLocalGenerator* lgr = this->LocalGenerators.at(0);
   std::string cleanScriptRel = "CMakeFiles/clean_additional.cmake";
-  std::string cleanScriptAbs = lgr->GetBinaryDirectory();
-  cleanScriptAbs += '/';
-  cleanScriptAbs += cleanScriptRel;
+  std::string cleanScriptAbs =
+    cmStrCat(lgr->GetBinaryDirectory(), '/', cleanScriptRel);
 
   // Check if there are additional files to clean
   if (this->AdditionalCleanFiles.empty()) {
@@ -1498,10 +1446,10 @@
   // Write rule
   {
     cmNinjaRule rule("CLEAN_ADDITIONAL");
-    rule.Command = CMakeCmd();
-    rule.Command += " -P ";
-    rule.Command += lgr->ConvertToOutputFormat(
-      this->NinjaOutputPath(cleanScriptRel), cmOutputConverter::SHELL);
+    rule.Command = cmStrCat(
+      CMakeCmd(), " -P ",
+      lgr->ConvertToOutputFormat(this->NinjaOutputPath(cleanScriptRel),
+                                 cmOutputConverter::SHELL));
     rule.Description = "Cleaning additional files...";
     rule.Comment = "Rule for cleaning additional files.";
     WriteRule(*this->RulesFileStream, rule);
@@ -1739,8 +1687,9 @@
   if (arg_lang == "Fortran") {
     info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_pp);
   } else {
-    cmSystemTools::Error("-E cmake_ninja_depends does not understand the " +
-                         arg_lang + " language");
+    cmSystemTools::Error(
+      cmStrCat("-E cmake_ninja_depends does not understand the ", arg_lang,
+               " language"));
     return 1;
   }
 
@@ -1794,8 +1743,9 @@
       cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary);
       Json::Reader reader;
       if (!reader.parse(tdif, tdio, false)) {
-        cmSystemTools::Error("-E cmake_ninja_depends failed to parse " +
-                             arg_tdi + reader.getFormattedErrorMessages());
+        cmSystemTools::Error(
+          cmStrCat("-E cmake_ninja_depends failed to parse ", arg_tdi,
+                   reader.getFormattedErrorMessages()));
         return nullptr;
       }
     }
@@ -1874,8 +1824,9 @@
     cmsys::ifstream ddif(arg_ddi.c_str(), std::ios::in | std::ios::binary);
     Json::Reader reader;
     if (!reader.parse(ddif, ddio, false)) {
-      cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " + arg_ddi +
-                           reader.getFormattedErrorMessages());
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
+                                    arg_ddi,
+                                    reader.getFormattedErrorMessages()));
       return false;
     }
 
@@ -1902,14 +1853,14 @@
   // Populate the module map with those provided by linked targets first.
   for (std::string const& linked_target_dir : linked_target_dirs) {
     std::string const ltmn =
-      linked_target_dir + "/" + arg_lang + "Modules.json";
+      cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json");
     Json::Value ltm;
     cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary);
     Json::Reader reader;
     if (ltmf && !reader.parse(ltmf, ltm, false)) {
-      cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " +
-                           linked_target_dir +
-                           reader.getFormattedErrorMessages());
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
+                                    linked_target_dir,
+                                    reader.getFormattedErrorMessages()));
       return false;
     }
     if (ltm.isObject()) {
@@ -2013,8 +1964,9 @@
     cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary);
     Json::Reader reader;
     if (!reader.parse(tdif, tdio, false)) {
-      cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " + arg_tdi +
-                           reader.getFormattedErrorMessages());
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
+                                    arg_tdi,
+                                    reader.getFormattedErrorMessages()));
       return 1;
     }
   }
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 04d8e37..244e9fd 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -7,7 +7,7 @@
 
 #include <iosfwd>
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -15,13 +15,13 @@
 #include <utility>
 #include <vector>
 
+#include "cm_codecvt.hxx"
+
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalCommonGenerator.h"
-#include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmNinjaTypes.h"
 #include "cmPolicies.h"
-#include "cm_codecvt.hxx"
 
 class cmCustomCommand;
 class cmGeneratorTarget;
@@ -228,7 +228,7 @@
       return this->GG->ConvertToNinjaPath(path);
     }
   };
-  MapToNinjaPathImpl MapToNinjaPath() { return MapToNinjaPathImpl(this); }
+  MapToNinjaPathImpl MapToNinjaPath() { return { this }; }
 
   // -- Additional clean files
   void AddAdditionalCleanFile(std::string fileName);
@@ -294,19 +294,12 @@
                                   cmNinjaDeps& outputs);
   void AppendTargetDependsClosure(cmGeneratorTarget const* target,
                                   cmNinjaOuts& outputs, bool omit_self);
-  void AddDependencyToAll(cmGeneratorTarget* target);
-  void AddDependencyToAll(const std::string& input);
 
   const std::vector<cmLocalGenerator*>& GetLocalGenerators() const
   {
     return LocalGenerators;
   }
 
-  bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target)
-  {
-    return cmGlobalGenerator::IsExcluded(root, target);
-  }
-
   int GetRuleCmdLength(const std::string& name) { return RuleCmdLength[name]; }
 
   void AddTargetAlias(const std::string& alias, cmGeneratorTarget* target);
@@ -322,6 +315,7 @@
   {
     return "1.9";
   }
+  static std::string RequiredNinjaVersionForDyndeps() { return "1.10"; }
   bool SupportsConsolePool() const;
   bool SupportsImplicitOuts() const;
   bool SupportsManifestRestat() const;
@@ -372,7 +366,7 @@
   void WriteUnknownExplicitDependencies(std::ostream& os);
 
   void WriteBuiltinTargets(std::ostream& os);
-  void WriteTargetAll(std::ostream& os);
+  void WriteTargetDefault(std::ostream& os);
   void WriteTargetRebuildManifest(std::ostream& os);
   bool WriteTargetCleanAdditional(std::ostream& os);
   void WriteTargetClean(std::ostream& os);
@@ -399,10 +393,7 @@
   /// Length of rule command, used by rsp file evaluation
   std::unordered_map<std::string, int> RuleCmdLength;
 
-  /// 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 +403,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;
@@ -425,7 +416,7 @@
   /// The mapping from source file to assumed dependencies.
   std::map<std::string, std::set<std::string>> AssumedSourceDependencies;
 
-  typedef std::map<std::string, cmGeneratorTarget*> TargetAliasMap;
+  using TargetAliasMap = std::map<std::string, cmGeneratorTarget*>;
   TargetAliasMap TargetAliases;
 
   std::map<cmGeneratorTarget const*, cmNinjaOuts> TargetDependsClosures;
@@ -435,11 +426,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/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index 4eb2252..4c2d69f 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -7,6 +7,8 @@
 #include <sstream>
 #include <utility>
 
+#include <cm/memory>
+
 #include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
@@ -20,6 +22,7 @@
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTargetDepend.h"
 #include "cmake.h"
@@ -105,11 +108,9 @@
   cmGeneratorTarget* gt) const
 {
   // Compute full path to object file directory for this target.
-  std::string dir;
-  dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/";
-  dir += gt->LocalGenerator->GetTargetDirectory(gt);
-  dir += "/";
+  std::string dir =
+    cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             gt->LocalGenerator->GetTargetDirectory(gt), '/');
   gt->ObjectDirectory = dir;
 }
 
@@ -144,10 +145,8 @@
     pmi.second.WriteProgressVariables(total, current);
   }
   for (cmLocalGenerator* lg : this->LocalGenerators) {
-    std::string markFileName = lg->GetCurrentBinaryDirectory();
-    markFileName += "/";
-    markFileName += "/CMakeFiles";
-    markFileName += "/progress.marks";
+    std::string markFileName =
+      cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles/progress.marks");
     cmGeneratedFileStream markFile(markFileName);
     markFile << this->CountProgressMarksInAll(lg) << "\n";
   }
@@ -195,9 +194,8 @@
   // because the check-build-system step compares the makefile time to
   // see if the build system must be regenerated.
   std::string makefileName =
-    this->GetCMakeInstance()->GetHomeOutputDirectory();
-  makefileName += "/CMakeFiles";
-  makefileName += "/Makefile2";
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
+             "/CMakeFiles/Makefile2");
   cmGeneratedFileStream makefileStream(makefileName, false,
                                        this->GetMakefileEncoding());
   if (!makefileStream) {
@@ -232,17 +230,14 @@
     depends.push_back(this->EmptyRuleHackDepends);
   }
 
-  // Write and empty all:
-  lg->WriteMakeRule(makefileStream, "The main recursive all target", "all",
-                    depends, no_commands, true);
-
-  // Write an empty preinstall:
-  lg->WriteMakeRule(makefileStream, "The main recursive preinstall target",
-                    "preinstall", depends, no_commands, true);
-
   // Write out the "special" stuff
   lg->WriteSpecialTargetsTop(makefileStream);
 
+  // Write the directory level rules.
+  for (auto const& it : this->ComputeDirectoryTargets()) {
+    this->WriteDirectoryRules2(makefileStream, it.second);
+  }
+
   // Write the target convenience rules
   for (cmLocalGenerator* localGen : this->LocalGenerators) {
     this->WriteConvenienceRules2(
@@ -263,17 +258,15 @@
   // because the check-build-system step compares the makefile time to
   // see if the build system must be regenerated.
   std::string cmakefileName =
-    this->GetCMakeInstance()->GetHomeOutputDirectory();
-  cmakefileName += "/CMakeFiles";
-  cmakefileName += "/Makefile.cmake";
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
+             "/CMakeFiles/Makefile.cmake");
   cmGeneratedFileStream cmakefileStream(cmakefileName);
   if (!cmakefileStream) {
     return;
   }
 
   std::string makefileName =
-    this->GetCMakeInstance()->GetHomeOutputDirectory();
-  makefileName += "/Makefile";
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), "/Makefile");
 
   // get a local generator for some useful methods
   cmLocalUnixMakefileGenerator3* lg =
@@ -303,8 +296,7 @@
   // Sort the list and remove duplicates.
   std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
 #if !defined(__VMS) // The Compaq STL on VMS crashes, so accept duplicates.
-  std::vector<std::string>::iterator new_end =
-    std::unique(lfiles.begin(), lfiles.end());
+  auto new_end = std::unique(lfiles.begin(), lfiles.end());
   lfiles.erase(new_end, lfiles.end());
 #endif
 
@@ -325,9 +317,9 @@
   cmakefileStream << "  )\n\n";
 
   // Build the path to the cache check file.
-  std::string check = this->GetCMakeInstance()->GetHomeOutputDirectory();
-  check += "/CMakeFiles";
-  check += "/cmake.check_cache";
+  std::string check =
+    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
+             "/CMakeFiles/cmake.check_cache");
 
   // Set the corresponding makefile in the cmake file.
   cmakefileStream << "# The corresponding makefile is:\n"
@@ -357,9 +349,8 @@
     std::string tmpStr;
     for (cmLocalGenerator* localGen : this->LocalGenerators) {
       lg = static_cast<cmLocalUnixMakefileGenerator3*>(localGen);
-      tmpStr = lg->GetCurrentBinaryDirectory();
-      tmpStr += "/CMakeFiles";
-      tmpStr += "/CMakeDirectoryInformation.cmake";
+      tmpStr = cmStrCat(lg->GetCurrentBinaryDirectory(),
+                        "/CMakeFiles/CMakeDirectoryInformation.cmake");
       cmakefileStream << "  \""
                       << lg->MaybeConvertToRelativePath(binDir, tmpStr)
                       << "\"\n";
@@ -391,8 +382,8 @@
           (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) ||
           (tgt->GetType() == cmStateEnums::UTILITY)) {
         cmGeneratorTarget* gt = tgt;
-        std::string tname = lg->GetRelativeTargetDirectory(gt);
-        tname += "/DependInfo.cmake";
+        std::string tname =
+          cmStrCat(lg->GetRelativeTargetDirectory(gt), "/DependInfo.cmake");
         cmSystemTools::ConvertToUnixSlashes(tname);
         cmakefileStream << "  \"" << tname << "\"\n";
       }
@@ -402,44 +393,37 @@
 }
 
 void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2(
-  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg,
-  const char* pass, bool check_all, bool check_relink,
-  std::vector<std::string> const& commands)
+  std::ostream& ruleFileStream, DirectoryTarget const& dt, const char* pass,
+  bool check_all, bool check_relink, std::vector<std::string> const& commands)
 {
-  // Get the relative path to the subdirectory from the top.
-  std::string makeTarget = lg->GetCurrentBinaryDirectory();
-  makeTarget += '/';
-  makeTarget += pass;
+  auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
+  std::string makeTarget =
+    cmStrCat(lg->GetCurrentBinaryDirectory(), '/', pass);
 
   // The directory-level rule should depend on the target-level rules
   // for all targets in the directory.
   std::vector<std::string> depends;
-  for (cmGeneratorTarget* gtarget : lg->GetGeneratorTargets()) {
-    int type = gtarget->GetType();
-    if ((type == cmStateEnums::EXECUTABLE) ||
-        (type == cmStateEnums::STATIC_LIBRARY) ||
-        (type == cmStateEnums::SHARED_LIBRARY) ||
-        (type == cmStateEnums::MODULE_LIBRARY) ||
-        (type == cmStateEnums::OBJECT_LIBRARY) ||
-        (type == cmStateEnums::UTILITY)) {
-      // Add this to the list of depends rules in this directory.
-      if ((!check_all || !gtarget->GetPropertyAsBool("EXCLUDE_FROM_ALL")) &&
-          (!check_relink ||
-           gtarget->NeedRelinkBeforeInstall(lg->GetConfigName()))) {
-        std::string tname = lg->GetRelativeTargetDirectory(gtarget);
-        tname += "/";
-        tname += pass;
-        depends.push_back(std::move(tname));
-      }
+  for (DirectoryTarget::Target const& t : dt.Targets) {
+    // Add this to the list of depends rules in this directory.
+    if ((!check_all || !t.ExcludeFromAll) &&
+        (!check_relink ||
+         t.GT->NeedRelinkBeforeInstall(lg->GetConfigName()))) {
+      // The target may be from a different directory; use its local gen.
+      auto const* tlg = static_cast<cmLocalUnixMakefileGenerator3 const*>(
+        t.GT->GetLocalGenerator());
+      std::string tname =
+        cmStrCat(tlg->GetRelativeTargetDirectory(t.GT), '/', pass);
+      depends.push_back(std::move(tname));
     }
   }
 
   // The directory-level rule should depend on the directory-level
   // rules of the subdirectories.
-  for (cmStateSnapshot const& c : lg->GetStateSnapshot().GetChildren()) {
-    std::string subdir = c.GetDirectory().GetCurrentBinary();
-    subdir += '/';
-    subdir += pass;
+  for (DirectoryTarget::Dir const& d : dt.Children) {
+    if (check_all && d.ExcludeFromAll) {
+      continue;
+    }
+    std::string subdir = cmStrCat(d.Path, '/', pass);
     depends.push_back(std::move(subdir));
   }
 
@@ -452,21 +436,18 @@
   // Write the rule.
   std::string doc;
   if (lg->IsRootMakefile()) {
-    doc = "The main recursive \"";
-    doc += pass;
-    doc += "\" target.";
+    doc = cmStrCat("The main recursive \"", pass, "\" target.");
   } else {
-    doc = "Recursive \"";
-    doc += pass;
-    doc += "\" directory target.";
+    doc = cmStrCat("Recursive \"", pass, "\" directory target.");
   }
   lg->WriteMakeRule(ruleFileStream, doc.c_str(), makeTarget, depends, commands,
                     true);
 }
 
 void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2(
-  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg)
+  std::ostream& ruleFileStream, DirectoryTarget const& dt)
 {
+  auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
   // Begin the directory-level rules section.
   {
     std::string dir =
@@ -481,19 +462,17 @@
     ruleFileStream << "\n\n";
   }
 
-  if (!lg->IsRootMakefile()) {
-    // Write directory-level rules for "all".
-    this->WriteDirectoryRule2(ruleFileStream, lg, "all", true, false);
+  // Write directory-level rules for "all".
+  this->WriteDirectoryRule2(ruleFileStream, dt, "all", true, false);
 
-    // Write directory-level rules for "preinstall".
-    this->WriteDirectoryRule2(ruleFileStream, lg, "preinstall", true, true);
-  }
+  // Write directory-level rules for "preinstall".
+  this->WriteDirectoryRule2(ruleFileStream, dt, "preinstall", true, true);
 
   // Write directory-level rules for "clean".
   {
     std::vector<std::string> cmds;
     lg->AppendDirectoryCleanCommand(cmds);
-    this->WriteDirectoryRule2(ruleFileStream, lg, "clean", false, false, cmds);
+    this->WriteDirectoryRule2(ruleFileStream, dt, "clean", false, false, cmds);
   }
 }
 
@@ -591,8 +570,7 @@
 
         // Write the rule.
         commands.clear();
-        std::string tmp = "CMakeFiles/";
-        tmp += "Makefile2";
+        std::string tmp = "CMakeFiles/Makefile2";
         commands.push_back(lg->GetRecursiveMakeCall(tmp, name));
         depends.clear();
         if (regenerate) {
@@ -604,14 +582,11 @@
         // Add a fast rule to build the target
         std::string localName = lg->GetRelativeTargetDirectory(gtarget);
         std::string makefileName;
-        makefileName = localName;
-        makefileName += "/build.make";
+        makefileName = cmStrCat(localName, "/build.make");
         depends.clear();
         commands.clear();
-        std::string makeTargetName = localName;
-        makeTargetName += "/build";
-        localName = name;
-        localName += "/fast";
+        std::string makeTargetName = cmStrCat(localName, "/build");
+        localName = cmStrCat(name, "/fast");
         commands.push_back(
           lg->GetRecursiveMakeCall(makefileName, makeTargetName));
         lg->WriteMakeRule(ruleFileStream, "fast build rule for target.",
@@ -620,10 +595,9 @@
         // Add a local name for the rule to relink the target before
         // installation.
         if (gtarget->NeedRelinkBeforeInstall(lg->GetConfigName())) {
-          makeTargetName = lg->GetRelativeTargetDirectory(gtarget);
-          makeTargetName += "/preinstall";
-          localName = name;
-          localName += "/preinstall";
+          makeTargetName =
+            cmStrCat(lg->GetRelativeTargetDirectory(gtarget), "/preinstall");
+          localName = cmStrCat(name, "/preinstall");
           depends.clear();
           commands.clear();
           commands.push_back(
@@ -645,9 +619,6 @@
   std::string localName;
   std::string makeTargetName;
 
-  // write the directory level rules for this local gen
-  this->WriteDirectoryRules2(ruleFileStream, lg);
-
   bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
   if (regenerate) {
     depends.emplace_back("cmake_check_build_system");
@@ -667,20 +638,17 @@
       std::string makefileName;
       // Add a rule to build the target by name.
       localName = lg->GetRelativeTargetDirectory(gtarget);
-      makefileName = localName;
-      makefileName += "/build.make";
+      makefileName = cmStrCat(localName, "/build.make");
 
       lg->WriteDivider(ruleFileStream);
       ruleFileStream << "# Target rules for target " << localName << "\n\n";
 
       commands.clear();
-      makeTargetName = localName;
-      makeTargetName += "/depend";
+      makeTargetName = cmStrCat(localName, "/depend");
       commands.push_back(
         lg->GetRecursiveMakeCall(makefileName, makeTargetName));
 
-      makeTargetName = localName;
-      makeTargetName += "/build";
+      makeTargetName = cmStrCat(localName, "/build");
       commands.push_back(
         lg->GetRecursiveMakeCall(makefileName, makeTargetName));
 
@@ -689,8 +657,7 @@
       depends.clear();
 
       cmLocalUnixMakefileGenerator3::EchoProgress progress;
-      progress.Dir = lg->GetBinaryDirectory();
-      progress.Dir += "/CMakeFiles";
+      progress.Dir = cmStrCat(lg->GetBinaryDirectory(), "/CMakeFiles");
       {
         std::ostringstream progressArg;
         const char* sep = "";
@@ -705,7 +672,7 @@
       if (const char* tgtMsg =
             this->GetCMakeInstance()->GetState()->GetGlobalProperty(
               "TARGET_MESSAGES")) {
-        targetMessages = cmSystemTools::IsOn(tgtMsg);
+        targetMessages = cmIsOn(tgtMsg);
       }
 
       if (targetMessages) {
@@ -717,15 +684,6 @@
       lg->WriteMakeRule(ruleFileStream, "All Build rule for target.",
                         localName, depends, commands, true);
 
-      // add the all/all dependency
-      if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) {
-        depends.clear();
-        depends.push_back(localName);
-        commands.clear();
-        lg->WriteMakeRule(ruleFileStream, "Include target in all.", "all",
-                          depends, commands, true);
-      }
-
       // Write the rule.
       commands.clear();
 
@@ -742,8 +700,7 @@
         progCmd << " " << this->CountProgressMarksInTarget(gtarget, emitted);
         commands.push_back(progCmd.str());
       }
-      std::string tmp = "CMakeFiles/";
-      tmp += "Makefile2";
+      std::string tmp = "CMakeFiles/Makefile2";
       commands.push_back(lg->GetRecursiveMakeCall(tmp, localName));
       {
         std::ostringstream progCmd;
@@ -758,8 +715,7 @@
       if (regenerate) {
         depends.emplace_back("cmake_check_build_system");
       }
-      localName = lg->GetRelativeTargetDirectory(gtarget);
-      localName += "/rule";
+      localName = cmStrCat(lg->GetRelativeTargetDirectory(gtarget), "/rule");
       lg->WriteMakeRule(ruleFileStream,
                         "Build rule for subdir invocation for target.",
                         localName, depends, commands, true);
@@ -773,28 +729,19 @@
 
       // Add rules to prepare the target for installation.
       if (gtarget->NeedRelinkBeforeInstall(lg->GetConfigName())) {
-        localName = lg->GetRelativeTargetDirectory(gtarget);
-        localName += "/preinstall";
+        localName =
+          cmStrCat(lg->GetRelativeTargetDirectory(gtarget), "/preinstall");
         depends.clear();
         commands.clear();
         commands.push_back(lg->GetRecursiveMakeCall(makefileName, localName));
         lg->WriteMakeRule(ruleFileStream,
                           "Pre-install relink rule for target.", localName,
                           depends, commands, true);
-
-        if (!this->IsExcluded(this->LocalGenerators[0], gtarget)) {
-          depends.clear();
-          depends.push_back(localName);
-          commands.clear();
-          lg->WriteMakeRule(ruleFileStream, "Prepare target for install.",
-                            "preinstall", depends, commands, true);
-        }
       }
 
       // add the clean rule
       localName = lg->GetRelativeTargetDirectory(gtarget);
-      makeTargetName = localName;
-      makeTargetName += "/clean";
+      makeTargetName = cmStrCat(localName, "/clean");
       depends.clear();
       commands.clear();
       commands.push_back(
@@ -913,9 +860,9 @@
     }
     cmLocalUnixMakefileGenerator3* lg3 =
       static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
-    std::string tgtName =
-      lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep));
-    tgtName += "/all";
+    std::string tgtName = cmStrCat(
+      lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
+      "/all");
     depends.push_back(tgtName);
   }
 }
@@ -958,8 +905,7 @@
             (type == cmStateEnums::UTILITY)) {
           std::string const& name = target->GetName();
           if (emittedTargets.insert(name).second) {
-            path = "... ";
-            path += name;
+            path = cmStrCat("... ", name);
             lg->AppendEcho(commands, path);
           }
         }
@@ -967,8 +913,7 @@
     }
   }
   for (std::string const& o : lg->GetLocalHelp()) {
-    path = "... ";
-    path += o;
+    path = cmStrCat("... ", o);
     lg->AppendEcho(commands, path);
   }
   lg->WriteMakeRule(ruleFileStream, "Help Target", "help", no_depends,
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 287472c..79db30e 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <iosfwd>
 #include <map>
 #include <set>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
@@ -164,11 +164,11 @@
                               cmLocalUnixMakefileGenerator3*);
 
   void WriteDirectoryRule2(std::ostream& ruleFileStream,
-                           cmLocalUnixMakefileGenerator3* lg, const char* pass,
+                           DirectoryTarget const& dt, const char* pass,
                            bool check_all, bool check_relink,
                            std::vector<std::string> const& commands = {});
   void WriteDirectoryRules2(std::ostream& ruleFileStream,
-                            cmLocalUnixMakefileGenerator3* lg);
+                            DirectoryTarget const& dt);
 
   void AppendGlobalTargetDepends(std::vector<std::string>& depends,
                                  cmGeneratorTarget* target);
@@ -220,9 +220,8 @@
     std::vector<unsigned long> Marks;
     void WriteProgressVariables(unsigned long total, unsigned long& current);
   };
-  typedef std::map<cmGeneratorTarget const*, TargetProgress,
-                   cmGeneratorTarget::StrictTargetComparison>
-    ProgressMapType;
+  using ProgressMapType = std::map<cmGeneratorTarget const*, TargetProgress,
+                                   cmGeneratorTarget::StrictTargetComparison>;
   ProgressMapType ProgressMap;
 
   size_t CountProgressMarksInTarget(
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 55374a4..09a49e1 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -2,6 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudio10Generator.h"
 
+#include <algorithm>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_jsoncpp_reader.h"
+
 #include "cmAlgorithms.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratorTarget.h"
@@ -13,15 +21,8 @@
 #include "cmVisualStudioSlnData.h"
 #include "cmVisualStudioSlnParser.h"
 #include "cmXMLWriter.h"
-#include "cm_jsoncpp_reader.h"
 #include "cmake.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include "cmsys/RegularExpression.hxx"
-
-#include <algorithm>
-
 static const char vs10generatorName[] = "Visual Studio 10 2010";
 static std::map<std::string, std::vector<cmIDEFlagTable>> loadedFlagJsonFiles;
 
@@ -232,7 +233,15 @@
   if (this->GeneratorToolsetCuda.empty()) {
     // Find the highest available version of the CUDA tools.
     std::vector<std::string> cudaTools;
-    std::string const bcDir = this->VCTargetsPath + "/BuildCustomizations";
+    std::string bcDir;
+    if (this->GeneratorToolsetCudaCustomDir.empty()) {
+      bcDir = this->VCTargetsPath + "/BuildCustomizations";
+    } else {
+      bcDir = this->GetPlatformToolsetCudaCustomDirString() +
+        "CUDAVisualStudioIntegration\\extras\\"
+        "visual_studio_integration\\MSBuildExtensions";
+      cmSystemTools::ConvertToUnixSlashes(bcDir);
+    }
     cmsys::Glob gl;
     gl.SetRelative(bcDir.c_str());
     if (gl.FindFiles(bcDir + "/CUDA *.props")) {
@@ -243,6 +252,24 @@
       std::sort(cudaTools.begin(), cudaTools.end(),
                 cmSystemTools::VersionCompareGreater);
       this->GeneratorToolsetCuda = cudaTools.at(0);
+    } else if (!this->GeneratorToolsetCudaCustomDir.empty()) {
+      // Generate an error if Visual Studio integration files are not found
+      // inside of custom cuda toolset.
+      std::ostringstream e;
+      /* clang-format off */
+      e <<
+        "Generator\n"
+        "  " << this->GetName() << "\n"
+        "given toolset\n"
+        "  cuda=" << this->GeneratorToolsetCudaCustomDir << "\n"
+        "cannot detect Visual Studio integration files in path\n"
+        "  " << bcDir;
+
+      /* clang-format on */
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+
+      // Clear the configured tool-set
+      this->GeneratorToolsetCuda.clear();
     }
   }
 
@@ -319,13 +346,16 @@
   if (const char* cuda = this->GetPlatformToolsetCuda()) {
     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA", cuda);
   }
+  if (const char* cudaDir = this->GetPlatformToolsetCudaCustomDir()) {
+    mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR", cudaDir);
+  }
   return true;
 }
 
 bool cmGlobalVisualStudio10Generator::ParseGeneratorToolset(
   std::string const& ts, cmMakefile* mf)
 {
-  std::vector<std::string> const fields = cmSystemTools::tokenize(ts, ",");
+  std::vector<std::string> const fields = cmTokenize(ts, ",");
   std::vector<std::string>::const_iterator fi = fields.begin();
   if (fi == fields.end()) {
     return true;
@@ -395,7 +425,17 @@
   std::string const& key, std::string const& value)
 {
   if (key == "cuda") {
-    this->GeneratorToolsetCuda = value;
+    /* test if cuda toolset is path to custom dir or cuda version */
+    auto pos = value.find_first_not_of("0123456789.");
+    if (pos != std::string::npos) {
+      this->GeneratorToolsetCudaCustomDir = value;
+      /* ensure trailing backslash for easy path joining */
+      if (this->GeneratorToolsetCudaCustomDir.back() != '\\') {
+        this->GeneratorToolsetCudaCustomDir.push_back('\\');
+      }
+    } else {
+      this->GeneratorToolsetCuda = value;
+    }
     return true;
   }
   if (key == "version") {
@@ -445,7 +485,7 @@
     this->DefaultPlatformName = "Tegra-Android";
     this->DefaultPlatformToolset = "Default";
     this->NsightTegraVersion = v;
-    mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v.c_str());
+    mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v);
   }
 
   return true;
@@ -643,6 +683,21 @@
   return this->GeneratorToolsetCuda;
 }
 
+const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaCustomDir()
+  const
+{
+  if (!this->GeneratorToolsetCudaCustomDir.empty()) {
+    return this->GeneratorToolsetCudaCustomDir.c_str();
+  }
+  return nullptr;
+}
+
+std::string const&
+cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaCustomDirString() const
+{
+  return this->GeneratorToolsetCudaCustomDir;
+}
+
 bool cmGlobalVisualStudio10Generator::IsDefaultToolset(
   const std::string&) const
 {
@@ -659,8 +714,7 @@
   if (!this->cmGlobalVisualStudio8Generator::FindMakeProgram(mf)) {
     return false;
   }
-  mf->AddDefinition("CMAKE_VS_MSBUILD_COMMAND",
-                    this->GetMSBuildCommand().c_str());
+  mf->AddDefinition("CMAKE_VS_MSBUILD_COMMAND", this->GetMSBuildCommand());
   return true;
 }
 
@@ -679,9 +733,9 @@
   std::string mskey;
 
   // Search in standard location.
-  mskey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\";
-  mskey += this->GetToolsVersion();
-  mskey += ";MSBuildToolsPath";
+  mskey = cmStrCat(
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\",
+    this->GetToolsVersion(), ";MSBuildToolsPath");
   if (cmSystemTools::ReadRegistryValue(mskey.c_str(), msbuild,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(msbuild);
@@ -721,8 +775,8 @@
     // In a try-compile we are given the outer CMakeFiles directory.
     wd = this->ConfiguredFilesPath;
   } else {
-    wd = this->GetCMakeInstance()->GetHomeOutputDirectory();
-    wd += "/CMakeFiles";
+    wd = cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
+                  "/CMakeFiles");
   }
   wd += "/";
   wd += cmVersion::GetCMakeVersion();
@@ -906,8 +960,7 @@
   {
     std::string slnFile;
     if (!projectDir.empty()) {
-      slnFile = projectDir;
-      slnFile += "/";
+      slnFile = cmStrCat(projectDir, '/');
     }
     slnFile += projectName;
     slnFile += ".sln";
@@ -954,8 +1007,7 @@
       makeCommand.Add(std::string(projectName) + ".sln");
       makeCommand.Add("/t:Clean");
     } else {
-      std::string targetProject(tname);
-      targetProject += ".vcxproj";
+      std::string targetProject = cmStrCat(tname, ".vcxproj");
       if (targetProject.find('/') == std::string::npos) {
         // it might be in a subdir
         if (cmSlnProjectEntry const* proj = slnData.GetProjectByName(tname)) {
@@ -1038,14 +1090,12 @@
 {
   // The VS 10 generator needs to create the .rule files on disk.
   // Hide them away under the CMakeFiles directory.
-  std::string ruleDir = this->GetCMakeInstance()->GetHomeOutputDirectory();
-  ruleDir += "/CMakeFiles";
-  ruleDir += "/";
-  ruleDir += cmSystemTools::ComputeStringMD5(
-    cmSystemTools::GetFilenamePath(output).c_str());
-  std::string ruleFile = ruleDir + "/";
-  ruleFile += cmSystemTools::GetFilenameName(output);
-  ruleFile += ".rule";
+  std::string ruleDir = cmStrCat(
+    this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeFiles/",
+    cmSystemTools::ComputeStringMD5(
+      cmSystemTools::GetFilenamePath(output).c_str()));
+  std::string ruleFile =
+    cmStrCat(ruleDir, '/', cmSystemTools::GetFilenameName(output), ".rule");
   return ruleFile;
 }
 
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index 1d30cd6..9adcf08 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -61,6 +61,10 @@
   const char* GetPlatformToolsetCuda() const;
   std::string const& GetPlatformToolsetCudaString() const;
 
+  /** The custom cuda install directory */
+  const char* GetPlatformToolsetCudaCustomDir() const;
+  std::string const& GetPlatformToolsetCudaCustomDirString() const;
+
   /** Return whether we need to use No/Debug instead of false/true
       for GenerateDebugInformation.  */
   bool GetPlatformToolsetNeedsDebugEnum() const
@@ -152,6 +156,7 @@
   std::string GeneratorToolsetVersion;
   std::string GeneratorToolsetHostArchitecture;
   std::string GeneratorToolsetCuda;
+  std::string GeneratorToolsetCudaCustomDir;
   std::string DefaultPlatformToolset;
   std::string DefaultPlatformToolsetHostArchitecture;
   std::string WindowsTargetPlatformVersion;
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index 6509b56..cd48474 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -182,7 +182,7 @@
     mf->DisplayStatus(e.str(), -1);
   }
   mf->AddDefinition("CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION",
-                    this->WindowsTargetPlatformVersion.c_str());
+                    this->WindowsTargetPlatformVersion);
 }
 
 bool cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx
index 8e67fad..e8e9ece 100644
--- a/Source/cmGlobalVisualStudio71Generator.cxx
+++ b/Source/cmGlobalVisualStudio71Generator.cxx
@@ -145,10 +145,8 @@
   for (std::string const& name : depends) {
     std::string guid = this->GetGUID(name);
     if (guid.empty()) {
-      std::string m = "Target: ";
-      m += target->GetName();
-      m += " depends on unknown target: ";
-      m += name;
+      std::string m = cmStrCat("Target: ", target->GetName(),
+                               " depends on unknown target: ", name);
       cmSystemTools::Error(m);
     }
     fout << "\t\t{" << guid << "} = {" << guid << "}\n";
@@ -201,7 +199,7 @@
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
       if (const char* m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
                                              cmSystemTools::UpperCase(i))) {
-        cmSystemTools::ExpandListArgument(m, mapConfig);
+        cmExpandList(m, mapConfig);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
         }
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 8764ee4..40b214c 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -2,19 +2,26 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudio7Generator.h"
 
+#include <vector>
+
+#include <cm/string_view>
+
+#include <windows.h>
+
+#include <assert.h>
+
+#include "cmsys/Encoding.hxx"
+
 #include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalVisualStudio7Generator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmUuid.h"
 #include "cmake.h"
-#include "cmsys/Encoding.hxx"
-
-#include <assert.h>
-#include <vector>
-#include <windows.h>
 
 static cmVS7FlagTable cmVS7ExtraFlagTable[] = {
   // Precompiled header and related options.  Note that the
@@ -41,6 +48,14 @@
   { "", "", "", "", 0 }
 };
 
+namespace {
+std::string GetSLNFile(cmLocalGenerator* root)
+{
+  return cmStrCat(root->GetCurrentBinaryDirectory(), '/',
+                  root->GetProjectName(), ".sln");
+}
+}
+
 cmGlobalVisualStudio7Generator::cmGlobalVisualStudio7Generator(
   cmake* cm, std::string const& platformInGeneratorName)
   : cmGlobalVisualStudioGenerator(cm, platformInGeneratorName)
@@ -64,8 +79,9 @@
     // Compute the version of the Intel plugin to the VS IDE.
     // If the key does not exist then use a default guess.
     std::string intelVersion;
-    std::string vskey = this->GetRegistryBase();
-    vskey += "\\Packages\\" CM_INTEL_PLUGIN_GUID ";ProductVersion";
+    std::string vskey =
+      cmStrCat(this->GetRegistryBase(),
+               "\\Packages\\" CM_INTEL_PLUGIN_GUID ";ProductVersion");
     cmSystemTools::ReadRegistryValue(vskey, intelVersion,
                                      cmSystemTools::KeyWOW64_32);
     unsigned int intelVersionNumber = ~0u;
@@ -121,8 +137,7 @@
   if (!this->cmGlobalVisualStudioGenerator::FindMakeProgram(mf)) {
     return false;
   }
-  mf->AddDefinition("CMAKE_VS_DEVENV_COMMAND",
-                    this->GetDevEnvCommand().c_str());
+  mf->AddDefinition("CMAKE_VS_DEVENV_COMMAND", this->GetDevEnvCommand());
   return true;
 }
 
@@ -152,8 +167,9 @@
   }
 
   // Search where VS15Preview places it.
-  vskey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7;";
-  vskey += this->GetIDEVersion();
+  vskey = cmStrCat(
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7;",
+    this->GetIDEVersion());
   if (cmSystemTools::ReadRegistryValue(vskey.c_str(), vscmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vscmd);
@@ -255,7 +271,7 @@
   return lg;
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 Json::Value cmGlobalVisualStudio7Generator::GetJson() const
 {
   Json::Value generator = this->cmGlobalVisualStudioGenerator::GetJson();
@@ -268,7 +284,7 @@
                                                    cmMakefile* mf)
 {
   mf->AddDefinition("CMAKE_VS_INTEL_Fortran_PROJECT_VERSION",
-                    this->GetIntelProjectVersion().c_str());
+                    this->GetIntelProjectVersion());
   return this->cmGlobalVisualStudioGenerator::SetSystemName(s, mf);
 }
 
@@ -281,8 +297,10 @@
   this->OutputSLNFile();
   // If any solution or project files changed during the generation,
   // tell Visual Studio to reload them...
-  if (!cmSystemTools::GetErrorOccuredFlag()) {
-    this->CallVisualStudioMacro(MacroReload);
+  if (!cmSystemTools::GetErrorOccuredFlag() &&
+      !this->LocalGenerators.empty()) {
+    this->CallVisualStudioMacro(MacroReload,
+                                GetSLNFile(this->LocalGenerators[0]));
   }
 }
 
@@ -293,10 +311,7 @@
     return;
   }
   this->CurrentProject = root->GetProjectName();
-  std::string fname = root->GetCurrentBinaryDirectory();
-  fname += "/";
-  fname += root->GetProjectName();
-  fname += ".sln";
+  std::string fname = GetSLNFile(root);
   cmGeneratedFileStream fout(fname.c_str());
   fout.SetCopyIfDifferent(true);
   if (!fout) {
@@ -433,16 +448,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);
@@ -511,16 +525,15 @@
           extensibilityAddInsOverridden = true;
         }
         fout << "\tGlobalSection(" << name << ") = " << sectionType << "\n";
-        std::vector<std::string> keyValuePairs;
-        cmSystemTools::ExpandListArgument(root->GetMakefile()->GetProperty(it),
-                                          keyValuePairs);
+        std::vector<std::string> keyValuePairs =
+          cmExpandedList(root->GetMakefile()->GetProperty(it));
         for (std::string const& itPair : keyValuePairs) {
           const std::string::size_type posEqual = itPair.find('=');
           if (posEqual != std::string::npos) {
             const std::string key =
-              cmSystemTools::TrimWhitespace(itPair.substr(0, posEqual));
+              cmTrimWhitespace(itPair.substr(0, posEqual));
             const std::string value =
-              cmSystemTools::TrimWhitespace(itPair.substr(posEqual + 1));
+              cmTrimWhitespace(itPair.substr(posEqual + 1));
             fout << "\t\t" << key << " = " << value << "\n";
             if (key == "SolutionGuid") {
               addGuid = false;
@@ -555,12 +568,10 @@
 {
   std::vector<std::string> configs;
   target->Target->GetMakefile()->GetConfigurations(configs);
-  std::string pname = target->GetName();
-  pname += "_UTILITY";
-  std::string fname = target->GetLocalGenerator()->GetCurrentBinaryDirectory();
-  fname += "/";
-  fname += pname;
-  fname += ".vcproj";
+  std::string pname = cmStrCat(target->GetName(), "_UTILITY");
+  std::string fname =
+    cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
+             pname, ".vcproj");
   cmGeneratedFileStream fout(fname.c_str());
   fout.SetCopyIfDifferent(true);
   std::string guid = this->GetGUID(pname.c_str());
@@ -617,9 +628,8 @@
     return std::string(storedGUID);
   }
   // Compute a GUID that is deterministic but unique to the build tree.
-  std::string input = this->CMakeInstance->GetState()->GetBinaryDirectory();
-  input += "|";
-  input += name;
+  std::string input =
+    cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name);
 
   cmUuid uuidGenerator;
 
@@ -665,11 +675,8 @@
         for (std::string const& i : configs) {
           const char* propertyValue =
             target->Target->GetMakefile()->GetDefinition(propertyName);
-          cmGeneratorExpression ge;
-          std::unique_ptr<cmCompiledGeneratorExpression> cge =
-            ge.Parse(propertyValue);
-          if (cmSystemTools::IsOn(
-                cge->Evaluate(target->GetLocalGenerator(), i))) {
+          if (cmIsOn(cmGeneratorExpression::Evaluate(
+                propertyValue, target->GetLocalGenerator(), i))) {
             activeConfigs.insert(i);
           }
         }
@@ -685,7 +692,7 @@
   for (std::string const& i : configs) {
     const char* propertyValue =
       target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i);
-    if (cmSystemTools::IsOff(propertyValue)) {
+    if (cmIsOff(propertyValue)) {
       activeConfigs.insert(i);
     }
   }
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index f004afb..7a9fcef 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -3,9 +3,8 @@
 #ifndef cmGlobalVisualStudio7Generator_h
 #define cmGlobalVisualStudio7Generator_h
 
-#include "cmGlobalVisualStudioGenerator.h"
-
 #include "cmGlobalGeneratorFactory.h"
+#include "cmGlobalVisualStudioGenerator.h"
 
 class cmTarget;
 struct cmIDEFlagTable;
@@ -23,7 +22,7 @@
   //! Create a local generator appropriate to this Global Generator
   cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   Json::Value GetJson() const override;
 #endif
 
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 85ddc85..8e6125b 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -2,8 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudio8Generator.h"
 
+#include "cmCustomCommand.h"
+#include "cmCustomCommandLines.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalVisualStudio7Generator.h"
 #include "cmMakefile.h"
@@ -26,9 +29,9 @@
 {
   // First look for VCExpress.
   std::string vsxcmd;
-  std::string vsxkey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\";
-  vsxkey += this->GetIDEVersion();
-  vsxkey += ";InstallDir";
+  std::string vsxkey =
+    cmStrCat("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\",
+             this->GetIDEVersion(), ";InstallDir");
   if (cmSystemTools::ReadRegistryValue(vsxkey.c_str(), vsxcmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vsxcmd);
@@ -54,8 +57,7 @@
 void cmGlobalVisualStudio8Generator::AddPlatformDefinitions(cmMakefile* mf)
 {
   if (this->TargetsWindowsCE()) {
-    mf->AddDefinition("CMAKE_VS_WINCE_VERSION",
-                      this->WindowsCEVersion.c_str());
+    mf->AddDefinition("CMAKE_VS_WINCE_VERSION", this->WindowsCEVersion);
   }
 }
 
@@ -94,17 +96,18 @@
     return false;
   }
 
-  const char* no_working_directory = nullptr;
-  std::vector<std::string> no_depends;
   std::vector<cmLocalGenerator*> const& generators = this->LocalGenerators;
   cmLocalVisualStudio7Generator* lg =
     static_cast<cmLocalVisualStudio7Generator*>(generators[0]);
   cmMakefile* mf = lg->GetMakefile();
 
-  cmCustomCommandLines noCommandLines;
+  const char* no_working_directory = nullptr;
+  std::vector<std::string> no_byproducts;
+  std::vector<std::string> no_depends;
+  cmCustomCommandLines no_commands;
   cmTarget* tgt = mf->AddUtilityCommand(
-    CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator,
-    false, no_working_directory, no_depends, noCommandLines);
+    CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, false,
+    no_working_directory, no_byproducts, no_depends, no_commands);
 
   cmGeneratorTarget* gt = new cmGeneratorTarget(tgt, lg);
   lg->AddGeneratorTarget(gt);
@@ -117,20 +120,17 @@
 
   // Create a list of all stamp files for this project.
   std::vector<std::string> stamps;
-  std::string stampList = "CMakeFiles/";
-  stampList += cmGlobalVisualStudio8Generator::GetGenerateStampList();
+  std::string stampList = cmStrCat(
+    "CMakeFiles/", cmGlobalVisualStudio8Generator::GetGenerateStampList());
   {
     std::string stampListFile =
-      generators[0]->GetMakefile()->GetCurrentBinaryDirectory();
-    stampListFile += "/";
-    stampListFile += stampList;
+      cmStrCat(generators[0]->GetMakefile()->GetCurrentBinaryDirectory(), '/',
+               stampList);
     std::string stampFile;
     cmGeneratedFileStream fout(stampListFile.c_str());
     for (cmLocalGenerator const* gi : generators) {
-      stampFile = gi->GetMakefile()->GetCurrentBinaryDirectory();
-      stampFile += "/";
-      stampFile += "CMakeFiles/";
-      stampFile += "generate.stamp";
+      stampFile = cmStrCat(gi->GetMakefile()->GetCurrentBinaryDirectory(),
+                           "/CMakeFiles/generate.stamp");
       fout << stampFile << "\n";
       stamps.push_back(stampFile);
     }
@@ -148,19 +148,15 @@
     // Add a custom prebuild target to run the VerifyGlobs script.
     cmake* cm = this->GetCMakeInstance();
     if (cm->DoWriteGlobVerifyTarget()) {
-      cmCustomCommandLine verifyCommandLine;
-      verifyCommandLine.push_back(cmSystemTools::GetCMakeCommand());
-      verifyCommandLine.push_back("-P");
-      verifyCommandLine.push_back(cm->GetGlobVerifyScript());
-      cmCustomCommandLines verifyCommandLines;
-      verifyCommandLines.push_back(verifyCommandLine);
+      cmCustomCommandLines verifyCommandLines = cmMakeSingleCommandLine(
+        { cmSystemTools::GetCMakeCommand(), "-P", cm->GetGlobVerifyScript() });
       std::vector<std::string> byproducts;
       byproducts.push_back(cm->GetGlobVerifyStamp());
 
-      mf->AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts,
-                                   no_depends, verifyCommandLines,
-                                   cmTarget::PRE_BUILD, "Checking File Globs",
-                                   no_working_directory, false);
+      mf->AddCustomCommandToTarget(
+        CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, no_depends,
+        verifyCommandLines, cmCustomCommandType::PRE_BUILD,
+        "Checking File Globs", no_working_directory, false);
 
       // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild,
       // otherwise the prebuild command will not be run.
@@ -175,33 +171,25 @@
     listFiles.erase(new_end, listFiles.end());
 
     // Create a rule to re-run CMake.
-    cmCustomCommandLine commandLine;
-    commandLine.push_back(cmSystemTools::GetCMakeCommand());
-    std::string argS = "-S";
-    argS += lg->GetSourceDirectory();
-    commandLine.push_back(argS);
-    std::string argB = "-B";
-    argB += lg->GetBinaryDirectory();
-    commandLine.push_back(argB);
-    commandLine.push_back("--check-stamp-list");
-    commandLine.push_back(stampList.c_str());
-    commandLine.push_back("--vs-solution-file");
+    std::string argS = cmStrCat("-S", lg->GetSourceDirectory());
+    std::string argB = cmStrCat("-B", lg->GetBinaryDirectory());
     std::string const sln =
       lg->GetBinaryDirectory() + "/" + lg->GetProjectName() + ".sln";
-    commandLine.push_back(sln);
-    cmCustomCommandLines commandLines;
-    commandLines.push_back(commandLine);
+    cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+      { cmSystemTools::GetCMakeCommand(), argS, argB, "--check-stamp-list",
+        stampList, "--vs-solution-file", sln });
 
     // Add the rule.  Note that we cannot use the CMakeLists.txt
     // file as the main dependency because it would get
     // overwritten by the CreateVCProjBuildRule.
     // (this could be avoided with per-target source files)
     std::string no_main_dependency;
-    std::vector<std::string> no_byproducts;
+    cmImplicitDependsList no_implicit_depends;
     if (cmSourceFile* file = mf->AddCustomCommandToOutput(
-          stamps, no_byproducts, listFiles, no_main_dependency, commandLines,
-          "Checking Build System", no_working_directory, true, false)) {
-      gt->AddSource(file->GetFullPath());
+          stamps, no_byproducts, listFiles, no_main_dependency,
+          no_implicit_depends, commandLines, "Checking Build System",
+          no_working_directory, true, false)) {
+      gt->AddSource(file->ResolveFullPath());
     } else {
       cmSystemTools::Error("Error adding rule for " + stamps[0]);
     }
@@ -251,7 +239,7 @@
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
       if (const char* m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
                                              cmSystemTools::UpperCase(i))) {
-        cmSystemTools::ExpandListArgument(m, mapConfig);
+        cmExpandList(m, mapConfig);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
         }
@@ -296,11 +284,9 @@
   cmGeneratorTarget const& target, const char* config) const
 {
   bool rVal = false;
-  if (const char* propStr = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(propStr);
-    std::string prop = cge->Evaluate(target.LocalGenerator, config);
-    rVal = cmSystemTools::IsOn(prop);
+  if (const char* prop = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) {
+    rVal = cmIsOn(
+      cmGeneratorExpression::Evaluate(prop, target.LocalGenerator, config));
   }
   return rVal;
 }
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index cc97cef..ed0cba7 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -3,16 +3,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalVisualStudioGenerator.h"
 
-#include "cmsys/Encoding.hxx"
 #include <future>
 #include <iostream>
-#include <objbase.h>
-#include <shellapi.h>
+
+#include <cm/iterator>
+
 #include <windows.h>
 
-#include "cmAlgorithms.h"
+#include <objbase.h>
+#include <shellapi.h>
+
+#include "cmsys/Encoding.hxx"
+
 #include "cmCallVisualStudioMacro.h"
 #include "cmCustomCommand.h"
+#include "cmCustomCommandLines.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalVisualStudioGenerator.h"
@@ -57,7 +62,7 @@
   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
 {
   mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
-                    this->DefaultPlatformName.c_str());
+                    this->DefaultPlatformName);
   this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
 }
 
@@ -69,7 +74,7 @@
   } else if (this->GetPlatformName() == "Itanium") {
     mf->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
   }
-  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName().c_str());
+  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
   return this->cmGlobalGenerator::SetGeneratorPlatform(p, mf);
 }
 
@@ -104,6 +109,9 @@
 
 void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
 {
+  char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
+  fout.write(utf8bom, 3);
+
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VS9:
       fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
@@ -178,7 +186,8 @@
 {
   // Add a special target that depends on ALL projects for easy build
   // of one configuration only.
-  const char* no_working_dir = 0;
+  const char* no_working_dir = nullptr;
+  std::vector<std::string> no_byproducts;
   std::vector<std::string> no_depends;
   cmCustomCommandLines no_commands;
   for (auto const& it : this->ProjectMap) {
@@ -188,8 +197,8 @@
       // Use no actual command lines so that the target itself is not
       // considered always out of date.
       cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand(
-        "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_working_dir,
-        no_depends, no_commands, false, "Build all projects");
+        "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_dir,
+        no_byproducts, no_depends, no_commands, false, "Build all projects");
 
       cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]);
       gen[0]->AddGeneratorTarget(gt);
@@ -224,8 +233,8 @@
 void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
   cmGeneratorTarget* gt) const
 {
-  std::string dir = gt->LocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/";
+  std::string dir =
+    cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/');
   std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
   if (!tgtDir.empty()) {
     dir += tgtDir;
@@ -258,8 +267,8 @@
   std::string dir = this->GetUserMacrosDirectory();
 
   if (!dir.empty()) {
-    std::string src = cmSystemTools::GetCMakeRoot();
-    src += "/Templates/" CMAKE_VSMACROS_FILENAME;
+    std::string src = cmStrCat(cmSystemTools::GetCMakeRoot(),
+                               "/Templates/" CMAKE_VSMACROS_FILENAME);
 
     std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
 
@@ -283,11 +292,10 @@
 }
 
 void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
-  MacroName m, const char* vsSolutionFile)
+  MacroName m, const std::string& vsSolutionFile)
 {
   // If any solution or project files changed during the generation,
   // tell Visual Studio to reload them...
-  cmMakefile* mf = this->LocalGenerators[0]->GetMakefile();
   std::string dir = this->GetUserMacrosDirectory();
 
   // Only really try to call the macro if:
@@ -302,28 +310,18 @@
     if (cmSystemTools::FileExists(macrosFile.c_str()) &&
         IsVisualStudioMacrosFileRegistered(
           macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
-      std::string topLevelSlnName;
-      if (vsSolutionFile) {
-        topLevelSlnName = vsSolutionFile;
-      } else {
-        topLevelSlnName = mf->GetCurrentBinaryDirectory();
-        topLevelSlnName += "/";
-        topLevelSlnName += this->LocalGenerators[0]->GetProjectName();
-        topLevelSlnName += ".sln";
-      }
-
       if (m == MacroReload) {
         std::vector<std::string> filenames;
         this->GetFilesReplacedDuringGenerate(filenames);
         if (!filenames.empty()) {
           std::string projects = cmJoin(filenames, ";");
           cmCallVisualStudioMacro::CallMacro(
-            topLevelSlnName, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
+            vsSolutionFile, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
             this->GetCMakeInstance()->GetDebugOutput());
         }
       } else if (m == MacroStop) {
         cmCallVisualStudioMacro::CallMacro(
-          topLevelSlnName, CMAKE_VSMACROS_STOP_MACRONAME, "",
+          vsSolutionFile, CMAKE_VSMACROS_STOP_MACRONAME, "",
           this->GetCMakeInstance()->GetDebugOutput());
       }
     }
@@ -487,8 +485,8 @@
   // Visual Studio generators know how to lookup their build tool
   // directly instead of needing a helper module to do it, so we
   // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
-  if (cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
-    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram().c_str());
+  if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
   }
   return true;
 }
@@ -553,9 +551,9 @@
   if (ERROR_SUCCESS == result) {
     // Iterate the subkeys and look for the values of interest in each subkey:
     wchar_t subkeyname[256];
-    DWORD cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
+    DWORD cch_subkeyname = cm::size(subkeyname);
     wchar_t keyclass[256];
-    DWORD cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
+    DWORD cch_keyclass = cm::size(keyclass);
     FILETIME lastWriteTime;
     lastWriteTime.dwHighDateTime = 0;
     lastWriteTime.dwLowDateTime = 0;
@@ -569,8 +567,8 @@
       if (ERROR_SUCCESS == result) {
         DWORD valueType = REG_SZ;
         wchar_t data1[256];
-        DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
-        RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
+        DWORD cch_data1 = sizeof(data1);
+        RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)data1,
                          &cch_data1);
 
         DWORD data2 = 0;
@@ -618,8 +616,8 @@
       }
 
       ++index;
-      cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
-      cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
+      cch_subkeyname = cm::size(subkeyname);
+      cch_keyclass = cm::size(keyclass);
       lastWriteTime.dwHighDateTime = 0;
       lastWriteTime.dwLowDateTime = 0;
     }
@@ -634,9 +632,7 @@
   // follow the expected naming scheme. Expected naming scheme is that
   // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
   // as the name of the next subkey.
-  std::ostringstream ossNext;
-  ossNext << index;
-  nextAvailableSubKeyName = ossNext.str();
+  nextAvailableSubKeyName = std::to_string(index);
 
   keyname = regKeyBase + "\\RecordingProject7";
   hkey = NULL;
@@ -646,9 +642,8 @@
   if (ERROR_SUCCESS == result) {
     DWORD valueType = REG_SZ;
     wchar_t data1[256];
-    DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
-    RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
-                     &cch_data1);
+    DWORD cch_data1 = sizeof(data1);
+    RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)data1, &cch_data1);
 
     DWORD data2 = 0;
     DWORD cch_data2 = sizeof(data2);
@@ -900,18 +895,11 @@
   gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
   std::string obj_dir = gt->ObjectDirectory;
   std::string cmakeCommand = cmSystemTools::GetCMakeCommand();
-  cmSystemTools::ConvertToWindowsExtendedPath(cmakeCommand);
-  cmCustomCommandLine cmdl;
-  cmdl.push_back(cmakeCommand);
-  cmdl.push_back("-E");
-  cmdl.push_back("__create_def");
-  cmdl.push_back(mdi->DefFile);
   std::string obj_dir_expanded = obj_dir;
   cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
                                configName.c_str());
   cmSystemTools::MakeDirectory(obj_dir_expanded);
   std::string const objs_file = obj_dir_expanded + "/objects.txt";
-  cmdl.push_back(objs_file);
   cmGeneratedFileStream fout(objs_file.c_str());
   if (!fout) {
     cmSystemTools::Error("could not open " + objs_file);
@@ -949,8 +937,8 @@
     fout << i->GetFullPath() << "\n";
   }
 
-  cmCustomCommandLines commandLines;
-  commandLines.push_back(cmdl);
+  cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+    { cmakeCommand, "-E", "__create_def", mdi->DefFile, objs_file });
   cmCustomCommand command(gt->Target->GetMakefile(), outputs, empty, empty,
                           commandLines, "Auto build dll exports", ".");
   commands.push_back(command);
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index cbab329..4f2007f 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -90,7 +90,7 @@
    * Call the ReloadProjects macro if necessary based on
    * GetFilesReplacedDuringGenerate results.
    */
-  void CallVisualStudioMacro(MacroName m, const char* vsSolutionFile = 0);
+  void CallVisualStudioMacro(MacroName m, const std::string& vsSolutionFile);
 
   // return true if target is fortran only
   bool TargetIsFortranOnly(const cmGeneratorTarget* gt);
@@ -110,6 +110,13 @@
 
   bool IsIncludeExternalMSProjectSupported() const override { return true; }
 
+  /** Get encoding used by generator for generated source files
+   */
+  codecvt::Encoding GetMakefileEncoding() const override
+  {
+    return codecvt::ANSI;
+  }
+
   class TargetSet : public std::set<cmGeneratorTarget const*>
   {
   };
@@ -173,7 +180,7 @@
                                   const std::string&);
   virtual std::string WriteUtilityDepend(cmGeneratorTarget const*) = 0;
   std::string GetUtilityDepend(const cmGeneratorTarget* target);
-  typedef std::map<cmGeneratorTarget const*, std::string> UtilityDependsMap;
+  using UtilityDependsMap = std::map<cmGeneratorTarget const*, std::string>;
   UtilityDependsMap UtilityDepends;
 
 protected:
@@ -206,13 +213,12 @@
   : public std::multiset<cmTargetDepend,
                          cmGlobalVisualStudioGenerator::TargetCompare>
 {
-  typedef std::multiset<cmTargetDepend,
-                        cmGlobalVisualStudioGenerator::TargetCompare>
-    derived;
+  using derived = std::multiset<cmTargetDepend,
+                                cmGlobalVisualStudioGenerator::TargetCompare>;
 
 public:
-  typedef cmGlobalGenerator::TargetDependSet TargetDependSet;
-  typedef cmGlobalVisualStudioGenerator::TargetSet TargetSet;
+  using TargetDependSet = cmGlobalGenerator::TargetDependSet;
+  using TargetSet = cmGlobalVisualStudioGenerator::TargetSet;
   OrderedTargetDependSet(TargetDependSet const&, std::string const& first);
   OrderedTargetDependSet(TargetSet const&, std::string const& first);
 };
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 2ba1aff..1eca1f6 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -390,7 +390,9 @@
     GetVSInstance(instancePath);
     std::stringstream path;
     path << instancePath;
-    path << "/VC/Auxiliary/Build/";
+    path << "/VC/Auxiliary/Build";
+    path << (cmSystemTools::VersionCompareGreaterEq(version, "14.20") ? '.'
+                                                                      : '/');
     path << version;
     path << "/Microsoft.VCToolsVersion." << version << ".props";
 
diff --git a/Source/cmGlobalWatcomWMakeGenerator.cxx b/Source/cmGlobalWatcomWMakeGenerator.cxx
index 8a27384..308ddda 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.cxx
+++ b/Source/cmGlobalWatcomWMakeGenerator.cxx
@@ -2,14 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalWatcomWMakeGenerator.h"
 
+#include <ostream>
+
 #include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmake.h"
 
-#include <ostream>
-
 cmGlobalWatcomWMakeGenerator::cmGlobalWatcomWMakeGenerator(cmake* cm)
   : cmGlobalUnixMakefileGenerator3(cm)
 {
diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h
index 3ca5e7d..64ace13 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.h
+++ b/Source/cmGlobalWatcomWMakeGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGlobalGeneratorFactory.h"
-#include "cmGlobalUnixMakefileGenerator3.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmGlobalGeneratorFactory.h"
+#include "cmGlobalUnixMakefileGenerator3.h"
+
 class cmMakefile;
 class cmake;
 struct cmDocumentationEntry;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 9d72528..67f1a46 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -2,18 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalXCodeGenerator.h"
 
-#include "cmsys/RegularExpression.hxx"
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
 #include <iomanip>
-#include <memory> // IWYU pragma: keep
 #include <sstream>
-#include <stdio.h>
-#include <string.h>
+
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmCustomCommandLines.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
@@ -37,12 +40,12 @@
 
 struct cmLinkImplementation;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(__APPLE__)
+#if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
 #  define HAVE_APPLICATION_SERVICES
 #  include <ApplicationServices/ApplicationServices.h>
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmXMLParser.h"
 
 // parse the xml file storing the installed version of Xcode on
@@ -187,7 +190,7 @@
   if (name != GetActualName()) {
     return nullptr;
   }
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   cmXcodeVersionParser parser;
   std::string versionFile;
   {
@@ -239,9 +242,8 @@
   // The Xcode generator knows how to lookup its build tool
   // directly instead of needing a helper module to do it, so we
   // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
-  if (cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
-    mf->AddDefinition("CMAKE_MAKE_PROGRAM",
-                      this->GetXcodeBuildCommand().c_str());
+  if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetXcodeBuildCommand());
   }
   return true;
 }
@@ -282,8 +284,7 @@
   }
   this->GeneratorToolset = ts;
   if (!this->GeneratorToolset.empty()) {
-    mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET",
-                      this->GeneratorToolset.c_str());
+    mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset);
   }
   return true;
 }
@@ -292,7 +293,7 @@
   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
 {
   mf->AddDefinition("XCODE", "1");
-  mf->AddDefinition("XCODE_VERSION", this->VersionString.c_str());
+  mf->AddDefinition("XCODE_VERSION", this->VersionString);
   if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
     mf->AddCacheDefinition(
       "CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
@@ -350,13 +351,10 @@
 
   if (!projectName.empty()) {
     makeCommand.Add("-project");
-    std::string projectArg = projectName;
-    projectArg += ".xcode";
-    projectArg += "proj";
+    std::string projectArg = cmStrCat(projectName, ".xcodeproj");
     makeCommand.Add(projectArg);
   }
-  if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
-      targetNames.end()) {
+  if (cmContains(targetNames, "clean")) {
     makeCommand.Add("clean");
     makeCommand.Add("-target", "ALL_BUILD");
   } else {
@@ -477,8 +475,8 @@
     this->CurrentLocalGenerator->GetCurrentBinaryDirectory(),
     this->ProjectOutputDirectoryComponents);
 
-  this->CurrentXCodeHackMakefile = root->GetCurrentBinaryDirectory();
-  this->CurrentXCodeHackMakefile += "/CMakeScripts";
+  this->CurrentXCodeHackMakefile =
+    cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
   cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile);
   this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
 }
@@ -488,8 +486,7 @@
 {
   std::string target = tName;
   std::replace(target.begin(), target.end(), ' ', '_');
-  std::string out = "PostBuild." + target;
-  out += "." + configName;
+  std::string out = cmStrCat("PostBuild.", target, '.', configName);
   return out;
 }
 
@@ -501,26 +498,24 @@
 {
   cmMakefile* mf = root->GetMakefile();
 
-  // Add ALL_BUILD
   const char* no_working_directory = nullptr;
+  std::vector<std::string> no_byproducts;
   std::vector<std::string> no_depends;
+
+  // Add ALL_BUILD
   cmTarget* allbuild = mf->AddUtilityCommand(
-    "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_depends,
-    no_working_directory, "echo", "Build all projects");
+    "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_directory,
+    no_byproducts, no_depends,
+    cmMakeSingleCommandLine({ "echo", "Build all projects" }));
 
   cmGeneratorTarget* allBuildGt = new cmGeneratorTarget(allbuild, root);
   root->AddGeneratorTarget(allBuildGt);
 
   // Add XCODE depend helper
   std::string dir = root->GetCurrentBinaryDirectory();
-  cmCustomCommandLine makeHelper;
-  makeHelper.push_back("make");
-  makeHelper.push_back("-C");
-  makeHelper.push_back(dir);
-  makeHelper.push_back("-f");
-  makeHelper.push_back(this->CurrentXCodeHackMakefile);
-  makeHelper.push_back("OBJDIR=$(OBJDIR)");
-  makeHelper.push_back(""); // placeholder, see below
+  cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+    { "make", "-C", dir, "-f", this->CurrentXCodeHackMakefile,
+      "OBJDIR=$(OBJDIR)", /* placeholder, see below */ "" });
 
   // Add ZERO_CHECK
   bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
@@ -534,8 +529,9 @@
       this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
     cmSystemTools::ReplaceString(file, "\\ ", " ");
     cmTarget* check = mf->AddUtilityCommand(
-      CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator,
-      true, no_depends, no_working_directory, "make", "-f", file.c_str());
+      CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, true,
+      no_working_directory, no_byproducts, no_depends,
+      cmMakeSingleCommandLine({ "make", "-f", file }));
 
     cmGeneratorTarget* checkGt = new cmGeneratorTarget(check, root);
     root->AddGeneratorTarget(checkGt);
@@ -549,9 +545,8 @@
         continue;
       }
 
-      std::string targetName = target->GetName();
-
-      if (regenerate && (targetName != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
+      if (regenerate &&
+          (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
         target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
       }
 
@@ -560,15 +555,13 @@
       // this will make sure that when the next target is built
       // things are up-to-date
       if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-        makeHelper.back() = // fill placeholder
+        commandLines.front().back() = // fill placeholder
           this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
-        cmCustomCommandLines commandLines;
-        commandLines.push_back(makeHelper);
-        std::vector<std::string> no_byproducts;
         gen->GetMakefile()->AddCustomCommandToTarget(
           target->GetName(), no_byproducts, no_depends, commandLines,
-          cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str(), true,
-          false, "", "", false, cmMakefile::AcceptObjectLibraryCommands);
+          cmCustomCommandType::POST_BUILD, "Depend check for xcode",
+          dir.c_str(), true, false, "", "", false,
+          cmObjectLibraryCommands::Accept);
       }
 
       if (!this->IsExcluded(gens[0], target)) {
@@ -587,18 +580,16 @@
   }
 
   // sort the array
-  std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
-  std::vector<std::string>::iterator new_end =
-    std::unique(lfiles.begin(), lfiles.end());
-  lfiles.erase(new_end, lfiles.end());
+  std::sort(lfiles.begin(), lfiles.end());
+  lfiles.erase(std::unique(lfiles.begin(), lfiles.end()), lfiles.end());
 
   cmake* cm = this->GetCMakeInstance();
   if (cm->DoWriteGlobVerifyTarget()) {
     lfiles.emplace_back(cm->GetGlobVerifyStamp());
   }
 
-  this->CurrentReRunCMakeMakefile = root->GetCurrentBinaryDirectory();
-  this->CurrentReRunCMakeMakefile += "/CMakeScripts";
+  this->CurrentReRunCMakeMakefile =
+    cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
   cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile);
   this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
   cmGeneratedFileStream makefileStream(this->CurrentReRunCMakeMakefile);
@@ -616,10 +607,8 @@
   }
   makefileStream << "\n";
 
-  std::string checkCache = root->GetBinaryDirectory();
-  checkCache += "/";
-  checkCache += "CMakeFiles/";
-  checkCache += "cmake.check_cache";
+  std::string checkCache =
+    cmStrCat(root->GetBinaryDirectory(), "/CMakeFiles/cmake.check_cache");
 
   if (cm->DoWriteGlobVerifyTarget()) {
     makefileStream << ".NOTPARALLEL:\n\n";
@@ -838,6 +827,11 @@
       genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS).c_str(),
       true);
   }
+
+  if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+    this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
+  }
+
   if (!flagsBuild.IsEmpty()) {
     if (!flags.empty()) {
       flags += ' ';
@@ -882,8 +876,7 @@
   const char* extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
   if (extraFileAttributes) {
     // Expand the list of attributes.
-    std::vector<std::string> attributes;
-    cmSystemTools::ExpandListArgument(extraFileAttributes, attributes);
+    std::vector<std::string> attributes = cmExpandedList(extraFileAttributes);
 
     // Store the attributes.
     for (const auto& attribute : attributes) {
@@ -901,11 +894,10 @@
   cmGeneratorTarget* target, std::vector<cmSourceFile*>& sources) const
 {
   std::string listfile =
-    target->GetLocalGenerator()->GetCurrentSourceDirectory();
-  listfile += "/CMakeLists.txt";
+    cmStrCat(target->GetLocalGenerator()->GetCurrentSourceDirectory(),
+             "/CMakeLists.txt");
   cmSourceFile* srcCMakeLists = target->Makefile->GetOrCreateSource(listfile);
-  if (std::find(sources.begin(), sources.end(), srcCMakeLists) ==
-      sources.end()) {
+  if (!cmContains(sources, srcCMakeLists)) {
     sources.push_back(srcCMakeLists);
   }
 }
@@ -1253,8 +1245,8 @@
   // framework or bundle targets
   std::vector<cmXCodeObject*> contentBuildPhases;
   if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
-    typedef std::map<std::string, std::vector<cmSourceFile*>>
-      mapOfVectorOfSourceFiles;
+    using mapOfVectorOfSourceFiles =
+      std::map<std::string, std::vector<cmSourceFile*>>;
     mapOfVectorOfSourceFiles bundleFiles;
     for (auto sourceFile : classes) {
       cmGeneratorTarget::SourceFileFlags tsFlags =
@@ -1263,7 +1255,7 @@
         bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
       }
     }
-    for (auto keySources : bundleFiles) {
+    for (auto const& keySources : bundleFiles) {
       cmXCodeObject* copyFilesBuildPhase =
         this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
       copyFilesBuildPhase->SetComment("Copy files");
@@ -1301,8 +1293,8 @@
   // create vector of "resource content file" build phases - only for
   // framework or bundle targets
   if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
-    typedef std::map<std::string, std::vector<cmSourceFile*>>
-      mapOfVectorOfSourceFiles;
+    using mapOfVectorOfSourceFiles =
+      std::map<std::string, std::vector<cmSourceFile*>>;
     mapOfVectorOfSourceFiles bundleFiles;
     for (auto sourceFile : classes) {
       cmGeneratorTarget::SourceFileFlags tsFlags =
@@ -1311,7 +1303,7 @@
         bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
       }
     }
-    for (auto keySources : bundleFiles) {
+    for (auto const& keySources : bundleFiles) {
       cmXCodeObject* copyFilesBuildPhase =
         this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
       copyFilesBuildPhase->SetComment("Copy files");
@@ -1399,13 +1391,9 @@
   // linker language.  This should convince Xcode to choose the proper
   // language.
   cmMakefile* mf = gtgt->Target->GetMakefile();
-  std::string fname = gtgt->GetLocalGenerator()->GetCurrentBinaryDirectory();
-  fname += "/CMakeFiles";
-  fname += "/";
-  fname += gtgt->GetName();
-  fname += "-CMakeForceLinker";
-  fname += ".";
-  fname += cmSystemTools::LowerCase(llang);
+  std::string fname = cmStrCat(
+    gtgt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/",
+    gtgt->GetName(), "-CMakeForceLinker.", cmSystemTools::LowerCase(llang));
   {
     cmGeneratedFileStream fout(fname);
     fout << "\n";
@@ -1418,10 +1406,8 @@
 
 bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf)
 {
-  const std::vector<std::string>& hdrExts =
-    this->CMakeInstance->GetHeaderExtensions();
-  return (std::find(hdrExts.begin(), hdrExts.end(), sf->GetExtension()) !=
-          hdrExts.end());
+  return cmContains(this->CMakeInstance->GetHeaderExtensions(),
+                    sf->GetExtension());
 }
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateBuildPhase(
@@ -1448,7 +1434,7 @@
 void cmGlobalXCodeGenerator::CreateCustomCommands(
   cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
   cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
-  std::vector<cmXCodeObject*> contentBuildPhases,
+  std::vector<cmXCodeObject*> const& contentBuildPhases,
   cmXCodeObject* frameworkBuildPhase, cmGeneratorTarget* gtgt)
 {
   std::vector<cmCustomCommand> const& prebuild = gtgt->GetPreBuildCommands();
@@ -1457,23 +1443,14 @@
 
   if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
       !gtgt->IsFrameworkOnApple()) {
-    cmCustomCommandLines cmd;
-    cmd.resize(1);
-    cmd[0].push_back(cmSystemTools::GetCMakeCommand());
-    cmd[0].push_back("-E");
-    cmd[0].push_back("cmake_symlink_library");
-    std::string str_file = "$<TARGET_FILE:";
-    str_file += gtgt->GetName();
-    str_file += ">";
-    std::string str_so_file = "$<TARGET_SONAME_FILE:";
-    str_so_file += gtgt->GetName();
-    str_so_file += ">";
-    std::string str_link_file = "$<TARGET_LINKER_FILE:";
-    str_link_file += gtgt->GetName();
-    str_link_file += ">";
-    cmd[0].push_back(str_file);
-    cmd[0].push_back(str_so_file);
-    cmd[0].push_back(str_link_file);
+    std::string str_file = cmStrCat("$<TARGET_FILE:", gtgt->GetName(), '>');
+    std::string str_so_file =
+      cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
+    std::string str_link_file =
+      cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
+    cmCustomCommandLines cmd = cmMakeSingleCommandLine(
+      { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
+        str_file, str_so_file, str_link_file });
 
     cmCustomCommand command(this->CurrentMakefile, std::vector<std::string>(),
                             std::vector<std::string>(),
@@ -1637,15 +1614,11 @@
   cmXCodeObject* buildphase, cmGeneratorTarget* target,
   std::vector<cmCustomCommand> const& commands, const char* name)
 {
-  std::string dir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
-  dir += "/CMakeScripts";
+  std::string dir = cmStrCat(
+    this->CurrentLocalGenerator->GetCurrentBinaryDirectory(), "/CMakeScripts");
   cmSystemTools::MakeDirectory(dir);
-  std::string makefile = dir;
-  makefile += "/";
-  makefile += target->GetName();
-  makefile += "_";
-  makefile += name;
-  makefile += ".make";
+  std::string makefile =
+    cmStrCat(dir, '/', target->GetName(), '_', name, ".make");
 
   for (const auto& currentConfig : this->CurrentConfigurationTypes) {
     this->CreateCustomRulesMakefile(makefile.c_str(), target, commands,
@@ -1654,12 +1627,10 @@
 
   std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
   cdir = this->ConvertToRelativeForMake(cdir);
-  std::string makecmd = "make -C ";
-  makecmd += cdir;
-  makecmd += " -f ";
-  makecmd += this->ConvertToRelativeForMake((makefile + "$CONFIGURATION"));
-  makecmd += " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\")";
-  makecmd += " all";
+  std::string makecmd =
+    cmStrCat("make -C ", cdir, " -f ",
+             this->ConvertToRelativeForMake((makefile + "$CONFIGURATION")),
+             " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
   buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
   buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
 }
@@ -1668,8 +1639,7 @@
   const char* makefileBasename, cmGeneratorTarget* target,
   std::vector<cmCustomCommand> const& commands, const std::string& configName)
 {
-  std::string makefileName = makefileBasename;
-  makefileName += configName;
+  std::string makefileName = cmStrCat(makefileBasename, configName);
   cmGeneratedFileStream makefileStream(makefileName);
   if (!makefileStream) {
     return;
@@ -1732,9 +1702,10 @@
       makefileStream << "\n";
 
       if (const char* comment = ccg.GetComment()) {
-        std::string echo_cmd = "echo ";
-        echo_cmd += (this->CurrentLocalGenerator->EscapeForShell(
-          comment, ccg.GetCC().GetEscapeAllowMakeVars()));
+        std::string echo_cmd =
+          cmStrCat("echo ",
+                   (this->CurrentLocalGenerator->EscapeForShell(
+                     comment, ccg.GetCC().GetEscapeAllowMakeVars())));
         makefileStream << "\t" << echo_cmd << "\n";
       }
 
@@ -1775,8 +1746,7 @@
   }
 
   buildSettings->AddAttribute(
-    "LD_NO_PIE",
-    this->CreateString(cmSystemTools::IsOn(PICValue) ? "NO" : "YES"));
+    "LD_NO_PIE", this->CreateString(cmIsOn(PICValue) ? "NO" : "YES"));
 }
 
 void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
@@ -1883,8 +1853,8 @@
                                                targetLinkFlags);
     }
     if (!configName.empty()) {
-      std::string linkFlagsVar = "LINK_FLAGS_";
-      linkFlagsVar += cmSystemTools::UpperCase(configName);
+      std::string linkFlagsVar =
+        cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(configName));
       if (const char* linkFlags = gtgt->GetProperty(linkFlagsVar)) {
         this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, linkFlags);
       }
@@ -2136,8 +2106,7 @@
 
   for (auto& include : includes) {
     if (this->NameResolvesToFramework(include)) {
-      std::string frameworkDir = include;
-      frameworkDir += "/../";
+      std::string frameworkDir = cmStrCat(include, "/../");
       frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
       if (emitted.insert(frameworkDir).second) {
         std::string incpath = this->XCodeEscapePath(frameworkDir);
@@ -2401,6 +2370,16 @@
     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
                                 this->CreateString(vso.str()));
   }
+
+  // Precompile Headers
+  std::string pchHeader = gtgt->GetPchHeader(configName, llang);
+  if (!pchHeader.empty()) {
+    buildSettings->AddAttribute("GCC_PREFIX_HEADER",
+                                this->CreateString(pchHeader));
+    buildSettings->AddAttribute("GCC_PRECOMPILE_PREFIX_HEADER",
+                                this->CreateString("YES"));
+  }
+
   // put this last so it can override existing settings
   // Convert "XCODE_ATTRIBUTE_*" properties directly.
   {
@@ -2409,10 +2388,8 @@
         std::string attribute = prop.substr(16);
         this->FilterConfigurationAttribute(configName, attribute);
         if (!attribute.empty()) {
-          cmGeneratorExpression ge;
-          std::string processed =
-            ge.Parse(gtgt->GetProperty(prop))
-              ->Evaluate(this->CurrentLocalGenerator, configName);
+          std::string processed = cmGeneratorExpression::Evaluate(
+            gtgt->GetProperty(prop), this->CurrentLocalGenerator, configName);
           buildSettings->AddAttribute(attribute,
                                       this->CreateString(processed));
         }
@@ -2483,20 +2460,16 @@
 std::string cmGlobalXCodeGenerator::AddConfigurations(cmXCodeObject* target,
                                                       cmGeneratorTarget* gtgt)
 {
-  std::vector<std::string> const configVector =
-    cmSystemTools::ExpandedListArgument(
-      this->CurrentMakefile->GetRequiredDefinition(
-        "CMAKE_CONFIGURATION_TYPES"));
+  std::vector<std::string> const configVector = cmExpandedList(
+    this->CurrentMakefile->GetRequiredDefinition("CMAKE_CONFIGURATION_TYPES"));
   cmXCodeObject* configlist =
     this->CreateObject(cmXCodeObject::XCConfigurationList);
   cmXCodeObject* buildConfigurations =
     this->CreateObject(cmXCodeObject::OBJECT_LIST);
   configlist->AddAttribute("buildConfigurations", buildConfigurations);
-  std::string comment = "Build configuration list for ";
-  comment += cmXCodeObject::PBXTypeNames[target->GetIsA()];
-  comment += " \"";
-  comment += gtgt->GetName();
-  comment += "\"";
+  std::string comment = cmStrCat("Build configuration list for ",
+                                 cmXCodeObject::PBXTypeNames[target->GetIsA()],
+                                 " \"", gtgt->GetName(), '"');
   configlist->SetComment(comment);
   target->AddAttribute("buildConfigurationList",
                        this->CreateObjectReference(configlist));
@@ -2624,9 +2597,7 @@
   }
   std::string fullName;
   if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-    fullName = "lib";
-    fullName += gtgt->GetName();
-    fullName += ".a";
+    fullName = cmStrCat("lib", gtgt->GetName(), ".a");
   } else {
     fullName = gtgt->GetFullName(defConfig);
   }
@@ -2652,8 +2623,7 @@
     return nullptr;
   }
 
-  std::map<cmGeneratorTarget const*, cmXCodeObject*>::const_iterator const i =
-    this->XCodeObjectMap.find(t);
+  auto const i = this->XCodeObjectMap.find(t);
   if (i == this->XCodeObjectMap.end()) {
     return nullptr;
   }
@@ -2663,8 +2633,7 @@
 std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name,
                                                   const std::string& id)
 {
-  std::string guidStoreName = name;
-  guidStoreName += "_GUID_CMAKE";
+  std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   const char* storedGUID =
     this->CMakeInstance->GetCacheDefinition(guidStoreName);
 
@@ -2719,9 +2688,7 @@
     if (!attr) {
       settings->AddAttribute(attribute, this->CreateString(value));
     } else {
-      std::string oldValue = attr->GetString();
-      oldValue += " ";
-      oldValue += value;
+      std::string oldValue = cmStrCat(attr->GetString(), ' ', value);
       attr->SetString(oldValue);
     }
   }
@@ -2886,8 +2853,8 @@
       // Add CMakeLists.txt file for user convenience.
       {
         std::string listfile =
-          gtgt->GetLocalGenerator()->GetCurrentSourceDirectory();
-        listfile += "/CMakeLists.txt";
+          cmStrCat(gtgt->GetLocalGenerator()->GetCurrentSourceDirectory(),
+                   "/CMakeLists.txt");
         cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(listfile);
         addSourceToGroup(sf->GetFullPath());
       }
@@ -2929,14 +2896,11 @@
   std::string target;
   const std::string targetFolder = gtgt->GetEffectiveFolderName();
   if (!targetFolder.empty()) {
-    target = targetFolder;
-    target += "/";
+    target = cmStrCat(targetFolder, '/');
   }
   target += gtgt->GetName();
-  s = target + "/";
-  s += sg->GetFullName();
-  std::map<std::string, cmXCodeObject*>::iterator it =
-    this->GroupNameMap.find(s);
+  s = cmStrCat(target, '/', sg->GetFullName());
+  auto it = this->GroupNameMap.find(s);
   if (it != this->GroupNameMap.end()) {
     return it->second;
   }
@@ -2946,8 +2910,7 @@
   if (it != this->TargetGroup.end()) {
     tgroup = it->second;
   } else {
-    std::vector<std::string> tgt_folders =
-      cmSystemTools::tokenize(target, "/");
+    std::vector<std::string> tgt_folders = cmTokenize(target, "/");
     std::string curr_tgt_folder;
     for (std::vector<std::string>::size_type i = 0; i < tgt_folders.size();
          i++) {
@@ -2979,13 +2942,10 @@
 
   // It's a recursive folder structure, let's find the real parent group
   if (sg->GetFullName() != sg->GetName()) {
-    std::string curr_folder = target;
-    curr_folder += "/";
-    for (auto const& folder :
-         cmSystemTools::tokenize(sg->GetFullName(), "\\")) {
+    std::string curr_folder = cmStrCat(target, '/');
+    for (auto const& folder : cmTokenize(sg->GetFullName(), "\\")) {
       curr_folder += folder;
-      std::map<std::string, cmXCodeObject*>::iterator i_folder =
-        this->GroupNameMap.find(curr_folder);
+      auto const i_folder = this->GroupNameMap.find(curr_folder);
       // Create new folder
       if (i_folder == this->GroupNameMap.end()) {
         cmXCodeObject* group = this->CreatePBXGroup(tgroup, folder);
@@ -2994,7 +2954,7 @@
       } else {
         tgroup = i_folder->second;
       }
-      curr_folder = curr_folder + "\\";
+      curr_folder += "\\";
     }
     return tgroup;
   }
@@ -3046,8 +3006,7 @@
   this->RootObject = this->CreateObject(cmXCodeObject::PBXProject);
   this->RootObject->SetComment("Project object");
 
-  std::string project_id = "PROJECT_";
-  project_id += root->GetProjectName();
+  std::string project_id = cmStrCat("PROJECT_", root->GetProjectName());
   this->RootObject->SetId(
     this->GetOrCreateId(project_id, this->RootObject->GetId()));
 
@@ -3078,7 +3037,7 @@
     this->CreateObject(cmXCodeObject::XCConfigurationList);
   cmXCodeObject* buildConfigurations =
     this->CreateObject(cmXCodeObject::OBJECT_LIST);
-  typedef std::vector<std::pair<std::string, cmXCodeObject*>> Configs;
+  using Configs = std::vector<std::pair<std::string, cmXCodeObject*>>;
   Configs configs;
   std::string defaultConfigName;
   for (const auto& name : this->CurrentConfigurationTypes) {
@@ -3098,10 +3057,8 @@
   }
   configlist->AddAttribute("buildConfigurations", buildConfigurations);
 
-  std::string comment = "Build configuration list for PBXProject";
-  comment += " \"";
-  comment += this->CurrentProject;
-  comment += "\"";
+  std::string comment = cmStrCat("Build configuration list for PBXProject \"",
+                                 this->CurrentProject, '"');
   configlist->SetComment(comment);
   configlist->AddAttribute("defaultConfigurationIsVisible",
                            this->CreateString("0"));
@@ -3150,8 +3107,7 @@
                                 this->CreateString(swiftVersion));
   }
 
-  std::string symroot = root->GetCurrentBinaryDirectory();
-  symroot += "/build";
+  std::string symroot = cmStrCat(root->GetCurrentBinaryDirectory(), "/build");
   buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot));
 
   for (auto& config : configs) {
@@ -3164,10 +3120,9 @@
         std::string attribute = var.substr(22);
         this->FilterConfigurationAttribute(config.first, attribute);
         if (!attribute.empty()) {
-          cmGeneratorExpression ge;
-          std::string processed =
-            ge.Parse(this->CurrentMakefile->GetDefinition(var))
-              ->Evaluate(this->CurrentLocalGenerator, config.first);
+          std::string processed = cmGeneratorExpression::Evaluate(
+            this->CurrentMakefile->GetDefinition(var),
+            this->CurrentLocalGenerator, config.first);
           buildSettingsForCfg->AddAttribute(attribute,
                                             this->CreateString(processed));
         }
@@ -3208,15 +3163,9 @@
   const std::string& projName, const std::string& configName,
   const cmGeneratorTarget* t, const std::string& variant) const
 {
-  std::string dir = t->GetLocalGenerator()->GetCurrentBinaryDirectory();
-  dir += "/";
-  dir += projName;
-  dir += ".build/";
-  dir += configName;
-  dir += "/";
-  dir += t->GetName();
-  dir += ".build/";
-  dir += variant;
+  std::string dir = cmStrCat(
+    t->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', projName,
+    ".build/", configName, '/', t->GetName(), ".build/", variant);
   return dir;
 }
 
@@ -3226,8 +3175,7 @@
   const char* osxArch = mf->GetDefinition("CMAKE_OSX_ARCHITECTURES");
   const char* sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
   if (osxArch && sysroot) {
-    cmSystemTools::ExpandListArgument(std::string(osxArch),
-                                      this->Architectures);
+    cmExpandList(std::string(osxArch), this->Architectures);
   }
 
   if (this->Architectures.empty()) {
@@ -3308,8 +3256,7 @@
         std::string trel = this->ConvertToRelativeForMake(tfull);
 
         // Add this target to the post-build phases of its dependencies.
-        std::map<std::string, cmXCodeObject::StringVec>::const_iterator y =
-          target->GetDependTargets().find(configName);
+        auto const y = target->GetDependTargets().find(configName);
         if (y != target->GetDependTargets().end()) {
           for (auto const& deptgt : y->second) {
             makefileStream << this->PostBuildMakeTarget(deptgt, configName)
@@ -3329,8 +3276,7 @@
         makefileStream << trel << ":";
 
         // List dependencies if any exist.
-        std::map<std::string, cmXCodeObject::StringVec>::const_iterator x =
-          target->GetDependLibraries().find(configName);
+        auto const x = target->GetDependLibraries().find(configName);
         if (x != target->GetDependLibraries().end()) {
           for (auto const& deplib : x->second) {
             std::string file = this->ConvertToRelativeForMake(deplib);
@@ -3342,12 +3288,10 @@
         for (auto objLib : objlibs) {
 
           const std::string objLibName = objLib->GetName();
-          std::string d =
+          std::string d = cmStrCat(
             this->GetObjectsDirectory(this->CurrentProject, configName, objLib,
-                                      OBJECT_LIBRARY_ARTIFACT_DIR);
-          d += "lib";
-          d += objLibName;
-          d += ".a";
+                                      OBJECT_LIBRARY_ARTIFACT_DIR),
+            "lib", objLibName, ".a");
 
           std::string dependency = this->ConvertToRelativeForMake(d);
           makefileStream << "\\\n\t" << dependency;
@@ -3364,10 +3308,8 @@
           std::string universal = this->GetObjectsDirectory(
             this->CurrentProject, configName, gt, "$(OBJDIR)/");
           for (const auto& architecture : this->Architectures) {
-            std::string universalFile = universal;
-            universalFile += architecture;
-            universalFile += "/";
-            universalFile += gt->GetFullName(configName);
+            std::string universalFile = cmStrCat(universal, architecture, '/',
+                                                 gt->GetFullName(configName));
             makefileStream << "\t/bin/rm -f "
                            << this->ConvertToRelativeForMake(universalFile)
                            << "\n";
@@ -3395,10 +3337,8 @@
   if (!this->CreateXCodeObjects(root, generators)) {
     return;
   }
-  std::string xcodeDir = root->GetCurrentBinaryDirectory();
-  xcodeDir += "/";
-  xcodeDir += root->GetProjectName();
-  xcodeDir += ".xcodeproj";
+  std::string xcodeDir = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
+                                  root->GetProjectName(), ".xcodeproj");
   cmSystemTools::MakeDirectory(xcodeDir);
   std::string xcodeProjFile = xcodeDir + "/project.pbxproj";
   cmGeneratedFileStream fout(xcodeProjFile);
@@ -3472,12 +3412,12 @@
 void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings(
   const std::string& xcProjDir, bool hasGeneratedSchemes)
 {
-  std::string xcodeSharedDataDir = xcProjDir;
-  xcodeSharedDataDir += "/project.xcworkspace/xcshareddata";
+  std::string xcodeSharedDataDir =
+    cmStrCat(xcProjDir, "/project.xcworkspace/xcshareddata");
   cmSystemTools::MakeDirectory(xcodeSharedDataDir);
 
-  std::string workspaceSettingsFile = xcodeSharedDataDir;
-  workspaceSettingsFile += "/WorkspaceSettings.xcsettings";
+  std::string workspaceSettingsFile =
+    cmStrCat(xcodeSharedDataDir, "/WorkspaceSettings.xcsettings");
 
   cmGeneratedFileStream fout(workspaceSettingsFile);
   fout.SetCopyIfDifferent(true);
@@ -3583,9 +3523,7 @@
 std::string cmGlobalXCodeGenerator::XCodeEscapePath(const std::string& p)
 {
   if (p.find(' ') != std::string::npos) {
-    std::string t = "\"";
-    t += p;
-    t += "\"";
+    std::string t = cmStrCat('"', p, '"');
     return t;
   }
   return p;
@@ -3607,9 +3545,7 @@
   const std::string& varNameSuffix, const std::string& default_flags)
 {
   if (!varNameLang.empty()) {
-    std::string varName = varNamePrefix;
-    varName += varNameLang;
-    varName += varNameSuffix;
+    std::string varName = cmStrCat(varNamePrefix, varNameLang, varNameSuffix);
     if (const char* varValue = this->CurrentMakefile->GetDefinition(varName)) {
       if (*varValue) {
         return varValue;
@@ -3629,8 +3565,7 @@
   }
 
   // Expand the list of definitions.
-  std::vector<std::string> defines;
-  cmSystemTools::ExpandListArgument(defines_list, defines);
+  std::vector<std::string> defines = cmExpandedList(defines_list);
 
   // Store the definitions in the string.
   this->AppendDefines(defs, defines, dflag);
@@ -3644,8 +3579,7 @@
   std::string def;
   for (auto const& define : defines) {
     // Start with -D if requested.
-    def = dflag ? "-D" : "";
-    def += define;
+    def = cmStrCat(dflag ? "-D" : "", define);
 
     // Append the flag with needed escapes.
     std::string tmp;
@@ -3706,11 +3640,9 @@
 std::string cmGlobalXCodeGenerator::ComputeInfoPListLocation(
   cmGeneratorTarget* target)
 {
-  std::string plist = target->GetLocalGenerator()->GetCurrentBinaryDirectory();
-  plist += "/CMakeFiles";
-  plist += "/";
-  plist += target->GetName();
-  plist += ".dir/Info.plist";
+  std::string plist =
+    cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
+             "/CMakeFiles/", target->GetName(), ".dir/Info.plist");
   return plist;
 }
 
@@ -3744,7 +3676,7 @@
     return mf->PlatformIsAppleEmbedded();
   }
 
-  return cmSystemTools::IsOn(epnValue);
+  return cmIsOn(epnValue);
 }
 
 bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
@@ -3757,10 +3689,10 @@
   cmGeneratorTarget* gt) const
 {
   std::string configName = this->GetCMakeCFGIntDir();
-  std::string dir = this->GetObjectsDirectory(
-    "$(PROJECT_NAME)", configName, gt, "$(OBJECT_FILE_DIR_normal:base)/");
-  dir += this->ObjectDirArch;
-  dir += "/";
+  std::string dir =
+    cmStrCat(this->GetObjectsDirectory("$(PROJECT_NAME)", configName, gt,
+                                       "$(OBJECT_FILE_DIR_normal:base)/"),
+             this->ObjectDirArch, '/');
   gt->ObjectDirectory = dir;
 }
 
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 71446f9..af905d0 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -122,13 +122,11 @@
   std::string RelativeToSource(const std::string& p);
   std::string RelativeToBinary(const std::string& p);
   std::string ConvertToRelativeForMake(std::string const& p);
-  void CreateCustomCommands(cmXCodeObject* buildPhases,
-                            cmXCodeObject* sourceBuildPhase,
-                            cmXCodeObject* headerBuildPhase,
-                            cmXCodeObject* resourceBuildPhase,
-                            std::vector<cmXCodeObject*> contentBuildPhases,
-                            cmXCodeObject* frameworkBuildPhase,
-                            cmGeneratorTarget* gtgt);
+  void CreateCustomCommands(
+    cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
+    cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
+    std::vector<cmXCodeObject*> const& contentBuildPhases,
+    cmXCodeObject* frameworkBuildPhase, cmGeneratorTarget* gtgt);
 
   std::string ComputeInfoPListLocation(cmGeneratorTarget* target);
 
diff --git a/Source/cmGraphAdjacencyList.h b/Source/cmGraphAdjacencyList.h
index 5ca9269..5ed6af4 100644
--- a/Source/cmGraphAdjacencyList.h
+++ b/Source/cmGraphAdjacencyList.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmListFileCache.h"
-
 #include <utility>
 #include <vector>
 
+#include "cmListFileCache.h"
+
 /**
  * Graph edge representation.  Most use cases just need the
  * destination vertex, so we support conversion to/from an int.  We
diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx
index a75d8a9..e0d545d 100644
--- a/Source/cmGraphVizWriter.cxx
+++ b/Source/cmGraphVizWriter.cxx
@@ -4,7 +4,7 @@
 
 #include <cstddef>
 #include <iostream>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <sstream>
 #include <utility>
 
@@ -15,6 +15,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
@@ -73,7 +74,8 @@
 {
   char sep = ';';
   std::map<std::string, LinkLibraryScopeType> tokens;
-  size_t start = 0, end = 0;
+  size_t start = 0;
+  size_t end = 0;
 
   const char* pInterfaceLinkLibraries =
     Target->GetProperty("INTERFACE_LINK_LIBRARIES");
@@ -233,9 +235,8 @@
 
   this->TargetsToIgnoreRegex.clear();
   if (!ignoreTargetsRegexes.empty()) {
-    std::vector<std::string> ignoreTargetsRegExVector;
-    cmSystemTools::ExpandListArgument(ignoreTargetsRegexes,
-                                      ignoreTargetsRegExVector);
+    std::vector<std::string> ignoreTargetsRegExVector =
+      cmExpandedList(ignoreTargetsRegexes);
     for (std::string const& currentRegexString : ignoreTargetsRegExVector) {
       cmsys::RegularExpression currentRegex;
       if (!currentRegex.compile(currentRegexString)) {
@@ -266,10 +267,8 @@
       continue;
     }
 
-    std::string currentFilename = fileName;
-    currentFilename += ".";
-    currentFilename += ptr.first;
-    currentFilename += ".dependers";
+    std::string currentFilename =
+      cmStrCat(fileName, '.', ptr.first, ".dependers");
 
     cmGeneratedFileStream str(currentFilename);
     if (!str) {
@@ -311,9 +310,7 @@
     std::set<std::string> insertedConnections;
     std::set<std::string> insertedNodes;
 
-    std::string currentFilename = fileName;
-    currentFilename += ".";
-    currentFilename += ptr.first;
+    std::string currentFilename = cmStrCat(fileName, '.', ptr.first);
     cmGeneratedFileStream str(currentFilename);
     if (!str) {
       return;
@@ -371,8 +368,7 @@
   const std::string& targetName, std::set<std::string>& insertedNodes,
   std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
 {
-  std::map<std::string, const cmGeneratorTarget*>::const_iterator targetPtrIt =
-    this->TargetPtrs.find(targetName);
+  auto targetPtrIt = this->TargetPtrs.find(targetName);
 
   if (targetPtrIt == this->TargetPtrs.end()) // not found at all
   {
@@ -393,17 +389,14 @@
 
   for (auto const& llit : ll) {
     const std::string& libName = llit.first;
-    std::map<std::string, std::string>::const_iterator libNameIt =
-      this->TargetNamesNodes.find(libName);
+    auto libNameIt = this->TargetNamesNodes.find(libName);
 
     // can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
     if (libNameIt == this->TargetNamesNodes.end()) {
       continue;
     }
 
-    std::string connectionName = myNodeName;
-    connectionName += "-";
-    connectionName += libNameIt->second;
+    std::string connectionName = cmStrCat(myNodeName, '-', libNameIt->second);
     if (insertedConnections.find(connectionName) ==
         insertedConnections.end()) {
       insertedConnections.insert(connectionName);
@@ -424,8 +417,7 @@
   const std::string& targetName, std::set<std::string>& insertedNodes,
   std::set<std::string>& insertedConnections, cmGeneratedFileStream& str) const
 {
-  std::map<std::string, const cmGeneratorTarget*>::const_iterator targetPtrIt =
-    this->TargetPtrs.find(targetName);
+  auto targetPtrIt = this->TargetPtrs.find(targetName);
 
   if (targetPtrIt == this->TargetPtrs.end()) // not found at all
   {
@@ -459,13 +451,11 @@
     for (auto const& llit : ll) {
       if (llit.first == targetName) {
         // So this target links against targetName.
-        std::map<std::string, std::string>::const_iterator dependerNodeNameIt =
-          this->TargetNamesNodes.find(tptr.first);
+        auto dependerNodeNameIt = this->TargetNamesNodes.find(tptr.first);
 
         if (dependerNodeNameIt != this->TargetNamesNodes.end()) {
-          std::string connectionName = dependerNodeNameIt->second;
-          connectionName += "-";
-          connectionName += myNodeName;
+          std::string connectionName =
+            cmStrCat(dependerNodeNameIt->second, '-', myNodeName);
 
           if (insertedConnections.find(connectionName) ==
               insertedConnections.end()) {
@@ -493,8 +483,7 @@
 {
   if (insertedNodes.find(targetName) == insertedNodes.end()) {
     insertedNodes.insert(targetName);
-    std::map<std::string, std::string>::const_iterator nameIt =
-      this->TargetNamesNodes.find(targetName);
+    auto nameIt = this->TargetNamesNodes.find(targetName);
 
     str << "    \"" << nameIt->second << "\" [ label=\"" << targetName
         << "\" shape=\"" << getShapeForTarget(target) << "\"];" << std::endl;
@@ -562,8 +551,7 @@
           }
         }
 
-        std::map<std::string, const cmGeneratorTarget*>::const_iterator tarIt =
-          this->TargetPtrs.find(libName);
+        auto tarIt = this->TargetPtrs.find(libName);
         if (tarIt == this->TargetPtrs.end()) {
           std::ostringstream ostr;
           ostr << this->GraphNodePrefix << cnt++;
diff --git a/Source/cmGraphVizWriter.h b/Source/cmGraphVizWriter.h
index 768683a..9c3051f 100644
--- a/Source/cmGraphVizWriter.h
+++ b/Source/cmGraphVizWriter.h
@@ -5,14 +5,15 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmStateTypes.h"
-
-#include "cmsys/RegularExpression.hxx"
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmStateTypes.h"
+
 class cmGeneratedFileStream;
 class cmGeneratorTarget;
 class cmLocalGenerator;
diff --git a/Source/cmHexFileConverter.cxx b/Source/cmHexFileConverter.cxx
index 190f2e3..2fa4b55 100644
--- a/Source/cmHexFileConverter.cxx
+++ b/Source/cmHexFileConverter.cxx
@@ -2,9 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmHexFileConverter.h"
 
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
 
 #include "cmSystemTools.h"
 
diff --git a/Source/cmHexFileConverter.h b/Source/cmHexFileConverter.h
index cb5de8f..26f9ea8 100644
--- a/Source/cmHexFileConverter.h
+++ b/Source/cmHexFileConverter.h
@@ -4,6 +4,7 @@
 #define cmHexFileConverter_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
+
 #include <string>
 
 /** \class cmHexFileConverter
diff --git a/Source/cmIDEOptions.cxx b/Source/cmIDEOptions.cxx
index 7b992d7..71326d2 100644
--- a/Source/cmIDEOptions.cxx
+++ b/Source/cmIDEOptions.cxx
@@ -2,13 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmIDEOptions.h"
 
-#include "cmsys/String.h"
 #include <iterator>
+
 #include <string.h>
 
+#include "cmsys/String.h"
+
 #include "cmAlgorithms.h"
 #include "cmIDEFlagTable.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 
 cmIDEOptions::cmIDEOptions()
 {
@@ -166,7 +168,7 @@
 {
   if (!defines.empty()) {
     // Expand the list of definitions.
-    cmSystemTools::ExpandListArgument(defines, this->Defines);
+    cmExpandList(defines, this->Defines);
   }
 }
 void cmIDEOptions::AddDefines(const std::vector<std::string>& defines)
@@ -188,7 +190,7 @@
 {
   if (!includes.empty()) {
     // Expand the list of includes.
-    cmSystemTools::ExpandListArgument(includes, this->Includes);
+    cmExpandList(includes, this->Includes);
   }
 }
 void cmIDEOptions::AddIncludes(const std::vector<std::string>& includes)
diff --git a/Source/cmIDEOptions.h b/Source/cmIDEOptions.h
index 4a43073..f949ae3 100644
--- a/Source/cmIDEOptions.h
+++ b/Source/cmIDEOptions.h
@@ -51,7 +51,7 @@
   // and overwrite or add new values to this map
   class FlagValue : public std::vector<std::string>
   {
-    typedef std::vector<std::string> derived;
+    using derived = std::vector<std::string>;
 
   public:
     FlagValue& operator=(std::string const& r)
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx
index d1f8f58..290e064 100644
--- a/Source/cmIfCommand.cxx
+++ b/Source/cmIfCommand.cxx
@@ -2,17 +2,26 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmIfCommand.h"
 
+#include <string>
+#include <utility>
+
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmConditionEvaluator.h"
 #include "cmExecutionStatus.h"
 #include "cmExpandedCommandArgument.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <memory> // IWYU pragma: keep
-
 static std::string cmIfCommandError(
   std::vector<cmExpandedCommandArgument> const& args)
 {
@@ -25,192 +34,178 @@
   return err;
 }
 
-//=========================================================================
-bool cmIfFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
-                                            cmMakefile& mf,
-                                            cmExecutionStatus& inStatus)
+class cmIfFunctionBlocker : public cmFunctionBlocker
 {
-  // we start by recording all the functions
-  if (lff.Name.Lower == "if") {
-    this->ScopeDepth++;
-  } else if (lff.Name.Lower == "endif") {
-    this->ScopeDepth--;
-    // if this is the endif for this if statement, then start executing
-    if (!this->ScopeDepth) {
-      // Remove the function blocker for this scope or bail.
-      std::unique_ptr<cmFunctionBlocker> fb(
-        mf.RemoveFunctionBlocker(this, lff));
-      if (!fb) {
-        return false;
+public:
+  cm::string_view StartCommandName() const override { return "if"_s; }
+  cm::string_view EndCommandName() const override { return "endif"_s; }
+
+  bool ArgumentsMatch(cmListFileFunction const& lff,
+                      cmMakefile&) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& inStatus) override;
+
+  std::vector<cmListFileArgument> Args;
+  bool IsBlocking;
+  bool HasRun = false;
+  bool ElseSeen = false;
+};
+
+bool cmIfFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                         cmMakefile&) const
+{
+  return lff.Arguments.empty() || lff.Arguments == this->Args;
+}
+
+bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
+                                 cmExecutionStatus& inStatus)
+{
+  cmMakefile& mf = inStatus.GetMakefile();
+  // execute the functions for the true parts of the if statement
+  int scopeDepth = 0;
+  for (cmListFileFunction const& func : functions) {
+    // keep track of scope depth
+    if (func.Name.Lower == "if") {
+      scopeDepth++;
+    }
+    if (func.Name.Lower == "endif") {
+      scopeDepth--;
+    }
+    // watch for our state change
+    if (scopeDepth == 0 && func.Name.Lower == "else") {
+
+      if (this->ElseSeen) {
+        cmListFileBacktrace bt = mf.GetBacktrace(func);
+        mf.GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          "A duplicate ELSE command was found inside an IF block.", bt);
+        cmSystemTools::SetFatalErrorOccured();
+        return true;
       }
 
-      // execute the functions for the true parts of the if statement
-      cmExecutionStatus status;
-      int scopeDepth = 0;
-      for (cmListFileFunction const& func : this->Functions) {
-        // keep track of scope depth
-        if (func.Name.Lower == "if") {
-          scopeDepth++;
-        }
-        if (func.Name.Lower == "endif") {
-          scopeDepth--;
-        }
-        // watch for our state change
-        if (scopeDepth == 0 && func.Name.Lower == "else") {
+      this->IsBlocking = this->HasRun;
+      this->HasRun = true;
+      this->ElseSeen = true;
 
-          if (this->ElseSeen) {
-            cmListFileBacktrace bt = mf.GetBacktrace(func);
-            mf.GetCMakeInstance()->IssueMessage(
-              MessageType::FATAL_ERROR,
-              "A duplicate ELSE command was found inside an IF block.", bt);
+      // if trace is enabled, print a (trivially) evaluated "else"
+      // statement
+      if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) {
+        mf.PrintCommandTrace(func);
+      }
+    } else if (scopeDepth == 0 && func.Name.Lower == "elseif") {
+      if (this->ElseSeen) {
+        cmListFileBacktrace bt = mf.GetBacktrace(func);
+        mf.GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          "An ELSEIF command was found after an ELSE command.", bt);
+        cmSystemTools::SetFatalErrorOccured();
+        return true;
+      }
+
+      if (this->HasRun) {
+        this->IsBlocking = true;
+      } else {
+        // if trace is enabled, print the evaluated "elseif" statement
+        if (mf.GetCMakeInstance()->GetTrace()) {
+          mf.PrintCommandTrace(func);
+        }
+
+        std::string errorString;
+
+        std::vector<cmExpandedCommandArgument> expandedArguments;
+        mf.ExpandArguments(func.Arguments, expandedArguments);
+
+        MessageType messType;
+
+        cmListFileContext conditionContext =
+          cmListFileContext::FromCommandContext(
+            func, this->GetStartingContext().FilePath);
+
+        cmConditionEvaluator conditionEvaluator(mf, conditionContext,
+                                                mf.GetBacktrace(func));
+
+        bool isTrue =
+          conditionEvaluator.IsTrue(expandedArguments, errorString, messType);
+
+        if (!errorString.empty()) {
+          std::string err =
+            cmStrCat(cmIfCommandError(expandedArguments), errorString);
+          cmListFileBacktrace bt = mf.GetBacktrace(func);
+          mf.GetCMakeInstance()->IssueMessage(messType, err, bt);
+          if (messType == MessageType::FATAL_ERROR) {
             cmSystemTools::SetFatalErrorOccured();
             return true;
           }
+        }
 
-          this->IsBlocking = this->HasRun;
+        if (isTrue) {
+          this->IsBlocking = false;
           this->HasRun = true;
-          this->ElseSeen = true;
-
-          // if trace is enabled, print a (trivially) evaluated "else"
-          // statement
-          if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) {
-            mf.PrintCommandTrace(func);
-          }
-        } else if (scopeDepth == 0 && func.Name.Lower == "elseif") {
-          if (this->ElseSeen) {
-            cmListFileBacktrace bt = mf.GetBacktrace(func);
-            mf.GetCMakeInstance()->IssueMessage(
-              MessageType::FATAL_ERROR,
-              "An ELSEIF command was found after an ELSE command.", bt);
-            cmSystemTools::SetFatalErrorOccured();
-            return true;
-          }
-
-          if (this->HasRun) {
-            this->IsBlocking = true;
-          } else {
-            // if trace is enabled, print the evaluated "elseif" statement
-            if (mf.GetCMakeInstance()->GetTrace()) {
-              mf.PrintCommandTrace(func);
-            }
-
-            std::string errorString;
-
-            std::vector<cmExpandedCommandArgument> expandedArguments;
-            mf.ExpandArguments(func.Arguments, expandedArguments);
-
-            MessageType messType;
-
-            cmListFileContext conditionContext =
-              cmListFileContext::FromCommandContext(
-                func, this->GetStartingContext().FilePath);
-
-            cmConditionEvaluator conditionEvaluator(mf, conditionContext,
-                                                    mf.GetBacktrace(func));
-
-            bool isTrue = conditionEvaluator.IsTrue(expandedArguments,
-                                                    errorString, messType);
-
-            if (!errorString.empty()) {
-              std::string err = cmIfCommandError(expandedArguments);
-              err += errorString;
-              cmListFileBacktrace bt = mf.GetBacktrace(func);
-              mf.GetCMakeInstance()->IssueMessage(messType, err, bt);
-              if (messType == MessageType::FATAL_ERROR) {
-                cmSystemTools::SetFatalErrorOccured();
-                return true;
-              }
-            }
-
-            if (isTrue) {
-              this->IsBlocking = false;
-              this->HasRun = true;
-            }
-          }
-        }
-
-        // should we execute?
-        else if (!this->IsBlocking) {
-          status.Clear();
-          mf.ExecuteCommand(func, status);
-          if (status.GetReturnInvoked()) {
-            inStatus.SetReturnInvoked();
-            return true;
-          }
-          if (status.GetBreakInvoked()) {
-            inStatus.SetBreakInvoked();
-            return true;
-          }
-          if (status.GetContinueInvoked()) {
-            inStatus.SetContinueInvoked();
-            return true;
-          }
         }
       }
-      return true;
+    }
+
+    // should we execute?
+    else if (!this->IsBlocking) {
+      cmExecutionStatus status(mf);
+      mf.ExecuteCommand(func, status);
+      if (status.GetReturnInvoked()) {
+        inStatus.SetReturnInvoked();
+        return true;
+      }
+      if (status.GetBreakInvoked()) {
+        inStatus.SetBreakInvoked();
+        return true;
+      }
+      if (status.GetContinueInvoked()) {
+        inStatus.SetContinueInvoked();
+        return true;
+      }
     }
   }
-
-  // record the command
-  this->Functions.push_back(lff);
-
-  // always return true
   return true;
 }
 
 //=========================================================================
-bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
-                                       cmMakefile&)
+bool cmIfCommand(std::vector<cmListFileArgument> const& args,
+                 cmExecutionStatus& inStatus)
 {
-  if (lff.Name.Lower == "endif") {
-    // if the endif has arguments, then make sure
-    // they match the arguments of the matching if
-    if (lff.Arguments.empty() || lff.Arguments == this->Args) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-//=========================================================================
-bool cmIfCommand::InvokeInitialPass(
-  const std::vector<cmListFileArgument>& args, cmExecutionStatus&)
-{
+  cmMakefile& makefile = inStatus.GetMakefile();
   std::string errorString;
 
   std::vector<cmExpandedCommandArgument> expandedArguments;
-  this->Makefile->ExpandArguments(args, expandedArguments);
+  makefile.ExpandArguments(args, expandedArguments);
 
   MessageType status;
 
   cmConditionEvaluator conditionEvaluator(
-    *(this->Makefile), this->Makefile->GetExecutionContext(),
-    this->Makefile->GetBacktrace());
+    makefile, makefile.GetExecutionContext(), makefile.GetBacktrace());
 
   bool isTrue =
     conditionEvaluator.IsTrue(expandedArguments, errorString, status);
 
   if (!errorString.empty()) {
-    std::string err = "if " + cmIfCommandError(expandedArguments);
-    err += errorString;
+    std::string err =
+      cmStrCat("if ", cmIfCommandError(expandedArguments), errorString);
     if (status == MessageType::FATAL_ERROR) {
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err);
+      makefile.IssueMessage(MessageType::FATAL_ERROR, err);
       cmSystemTools::SetFatalErrorOccured();
       return true;
     }
-    this->Makefile->IssueMessage(status, err);
+    makefile.IssueMessage(status, err);
   }
 
-  cmIfFunctionBlocker* f = new cmIfFunctionBlocker();
-  // if is isn't true block the commands
-  f->ScopeDepth = 1;
-  f->IsBlocking = !isTrue;
-  if (isTrue) {
-    f->HasRun = true;
+  {
+    auto fb = cm::make_unique<cmIfFunctionBlocker>();
+    // if is isn't true block the commands
+    fb->IsBlocking = !isTrue;
+    if (isTrue) {
+      fb->HasRun = true;
+    }
+    fb->Args = args;
+    makefile.AddFunctionBlocker(std::move(fb));
   }
-  f->Args = args;
-  this->Makefile->AddFunctionBlocker(f);
 
   return true;
 }
diff --git a/Source/cmIfCommand.h b/Source/cmIfCommand.h
index d34ed02..820ffa4 100644
--- a/Source/cmIfCommand.h
+++ b/Source/cmIfCommand.h
@@ -5,61 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmFunctionBlocker.h"
-#include "cmListFileCache.h"
-
 class cmExecutionStatus;
-class cmExpandedCommandArgument;
-class cmMakefile;
-
-class cmIfFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
-                         cmExecutionStatus&) override;
-  bool ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) override;
-
-  std::vector<cmListFileArgument> Args;
-  std::vector<cmListFileFunction> Functions;
-  bool IsBlocking;
-  bool HasRun = false;
-  bool ElseSeen = false;
-  unsigned int ScopeDepth = 0;
-};
+struct cmListFileArgument;
 
 /// Starts an if block
-class cmIfCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIfCommand; }
-
-  /**
-   * This overrides the default InvokeInitialPass implementation.
-   * It records the arguments before expansion.
-   */
-  bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
-                         cmExecutionStatus&) override;
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const&,
-                   cmExecutionStatus&) override
-  {
-    return false;
-  }
-
-  // Filter the given variable definition based on policy CMP0054.
-  static const char* GetDefinitionIfUnquoted(
-    const cmMakefile* mf, cmExpandedCommandArgument const& argument);
-};
+bool cmIfCommand(std::vector<cmListFileArgument> const& args,
+                 cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx
index 0d608bb..14264f4 100644
--- a/Source/cmIncludeCommand.cxx
+++ b/Source/cmIncludeCommand.cxx
@@ -4,21 +4,21 @@
 
 #include <sstream>
 
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmIncludeCommand
-bool cmIncludeCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool cmIncludeCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.empty() || args.size() > 4) {
-    this->SetError("called with wrong number of arguments.  "
-                   "include() only takes one file.");
+    status.SetError("called with wrong number of arguments.  "
+                    "include() only takes one file.");
     return false;
   }
   bool optional = false;
@@ -29,20 +29,20 @@
   for (unsigned int i = 1; i < args.size(); i++) {
     if (args[i] == "OPTIONAL") {
       if (optional) {
-        this->SetError("called with invalid arguments: OPTIONAL used twice");
+        status.SetError("called with invalid arguments: OPTIONAL used twice");
         return false;
       }
       optional = true;
     } else if (args[i] == "RESULT_VARIABLE") {
       if (!resultVarName.empty()) {
-        this->SetError("called with invalid arguments: "
-                       "only one result variable allowed");
+        status.SetError("called with invalid arguments: "
+                        "only one result variable allowed");
         return false;
       }
       if (++i < args.size()) {
         resultVarName = args[i];
       } else {
-        this->SetError("called with no value for RESULT_VARIABLE.");
+        status.SetError("called with no value for RESULT_VARIABLE.");
         return false;
       }
     } else if (args[i] == "NO_POLICY_SCOPE") {
@@ -50,39 +50,39 @@
     } else if (i > 1) // compat.: in previous cmake versions the second
                       // parameter was ignored if it wasn't "OPTIONAL"
     {
-      std::string errorText = "called with invalid argument: ";
-      errorText += args[i];
-      this->SetError(errorText);
+      std::string errorText =
+        cmStrCat("called with invalid argument: ", args[i]);
+      status.SetError(errorText);
       return false;
     }
   }
 
   if (fname.empty()) {
-    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING,
-                                 "include() given empty file name (ignored).");
+    status.GetMakefile().IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      "include() given empty file name (ignored).");
     return true;
   }
 
   if (!cmSystemTools::FileIsFullPath(fname)) {
     // Not a path. Maybe module.
-    std::string module = fname;
-    module += ".cmake";
-    std::string mfile = this->Makefile->GetModulesFile(module);
+    std::string module = cmStrCat(fname, ".cmake");
+    std::string mfile = status.GetMakefile().GetModulesFile(module);
     if (!mfile.empty()) {
       fname = mfile;
     }
   }
 
   std::string fname_abs = cmSystemTools::CollapseFullPath(
-    fname, this->Makefile->GetCurrentSourceDirectory());
+    fname, status.GetMakefile().GetCurrentSourceDirectory());
 
-  cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
+  cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
   if (gg->IsExportedTargetsFile(fname_abs)) {
     const char* modal = nullptr;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
 
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0024)) {
+    switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0024)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0024) << "\n";
         modal = "should";
@@ -102,7 +102,7 @@
         << " not be used as the argument to the "
            "include() command.  Use ALIAS targets instead to refer to targets "
            "by alternative names.\n";
-      this->Makefile->IssueMessage(messageType, e.str());
+      status.GetMakefile().IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
@@ -112,27 +112,28 @@
   }
 
   std::string listFile = cmSystemTools::CollapseFullPath(
-    fname, this->Makefile->GetCurrentSourceDirectory());
+    fname, status.GetMakefile().GetCurrentSourceDirectory());
   if (optional && !cmSystemTools::FileExists(listFile)) {
     if (!resultVarName.empty()) {
-      this->Makefile->AddDefinition(resultVarName, "NOTFOUND");
+      status.GetMakefile().AddDefinition(resultVarName, "NOTFOUND");
     }
     return true;
   }
 
-  bool readit = this->Makefile->ReadDependentFile(listFile, noPolicyScope);
+  bool readit =
+    status.GetMakefile().ReadDependentFile(listFile, noPolicyScope);
 
   // add the location of the included file if a result variable was given
   if (!resultVarName.empty()) {
-    this->Makefile->AddDefinition(resultVarName,
-                                  readit ? fname_abs.c_str() : "NOTFOUND");
+    status.GetMakefile().AddDefinition(
+      resultVarName, readit ? fname_abs.c_str() : "NOTFOUND");
   }
 
   if (!optional && !readit && !cmSystemTools::GetFatalErrorOccured()) {
-    std::string m = "could not find load file:\n"
-                    "  ";
-    m += fname;
-    this->SetError(m);
+    std::string m = cmStrCat("could not find load file:\n"
+                             "  ",
+                             fname);
+    status.SetError(m);
     return false;
   }
   return true;
diff --git a/Source/cmIncludeCommand.h b/Source/cmIncludeCommand.h
index 3b843b2..b0dd779 100644
--- a/Source/cmIncludeCommand.h
+++ b/Source/cmIncludeCommand.h
@@ -8,30 +8,15 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmIncludeCommand
+/**
  * \brief cmIncludeCommand defines a list of distant
  *  files that can be "included" in the current list file.
  *  In almost every sense, this is identical to a C/C++
  *  #include command.  Arguments are first expended as usual.
  */
-class cmIncludeCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIncludeCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmIncludeCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmIncludeDirectoryCommand.cxx b/Source/cmIncludeDirectoryCommand.cxx
index 62e2abd..170aea1 100644
--- a/Source/cmIncludeDirectoryCommand.cxx
+++ b/Source/cmIncludeDirectoryCommand.cxx
@@ -7,23 +7,28 @@
 #include <utility>
 
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+static void GetIncludes(cmMakefile& mf, const std::string& arg,
+                        std::vector<std::string>& incs);
+static void NormalizeInclude(cmMakefile& mf, std::string& inc);
 
-// cmIncludeDirectoryCommand
-bool cmIncludeDirectoryCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmIncludeDirectoryCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
   if (args.empty()) {
     return true;
   }
 
-  std::vector<std::string>::const_iterator i = args.begin();
+  cmMakefile& mf = status.GetMakefile();
 
-  bool before = this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_BEFORE");
+  auto i = args.begin();
+
+  bool before = mf.IsOn("CMAKE_INCLUDE_DIRECTORIES_BEFORE");
   bool system = false;
 
   if ((*i) == "BEFORE") {
@@ -44,13 +49,13 @@
       continue;
     }
     if (i->empty()) {
-      this->SetError("given empty-string as include directory.");
+      status.SetError("given empty-string as include directory.");
       return false;
     }
 
     std::vector<std::string> includes;
 
-    this->GetIncludes(*i, includes);
+    GetIncludes(mf, *i, includes);
 
     if (before) {
       cmAppend(beforeIncludes, includes);
@@ -63,9 +68,9 @@
   }
   std::reverse(beforeIncludes.begin(), beforeIncludes.end());
 
-  this->Makefile->AddIncludeDirectories(afterIncludes);
-  this->Makefile->AddIncludeDirectories(beforeIncludes, before);
-  this->Makefile->AddSystemIncludeDirectories(systemIncludes);
+  mf.AddIncludeDirectories(afterIncludes);
+  mf.AddIncludeDirectories(beforeIncludes, before);
+  mf.AddSystemIncludeDirectories(systemIncludes);
 
   return true;
 }
@@ -82,8 +87,8 @@
 // output from a program and passing it into a command the cleanup doesn't
 // always happen
 //
-void cmIncludeDirectoryCommand::GetIncludes(const std::string& arg,
-                                            std::vector<std::string>& incs)
+static void GetIncludes(cmMakefile& mf, const std::string& arg,
+                        std::vector<std::string>& incs)
 {
   // break apart any line feed arguments
   std::string::size_type pos = 0;
@@ -91,7 +96,7 @@
   while ((pos = arg.find('\n', lastPos)) != std::string::npos) {
     if (pos) {
       std::string inc = arg.substr(lastPos, pos);
-      this->NormalizeInclude(inc);
+      NormalizeInclude(mf, inc);
       if (!inc.empty()) {
         incs.push_back(std::move(inc));
       }
@@ -99,13 +104,13 @@
     lastPos = pos + 1;
   }
   std::string inc = arg.substr(lastPos);
-  this->NormalizeInclude(inc);
+  NormalizeInclude(mf, inc);
   if (!inc.empty()) {
     incs.push_back(std::move(inc));
   }
 }
 
-void cmIncludeDirectoryCommand::NormalizeInclude(std::string& inc)
+static void NormalizeInclude(cmMakefile& mf, std::string& inc)
 {
   std::string::size_type b = inc.find_first_not_of(" \r");
   std::string::size_type e = inc.find_last_not_of(" \r");
@@ -116,16 +121,11 @@
     return;
   }
 
-  if (!cmSystemTools::IsOff(inc)) {
+  if (!cmIsOff(inc)) {
     cmSystemTools::ConvertToUnixSlashes(inc);
-
-    if (!cmSystemTools::FileIsFullPath(inc)) {
-      if (!cmGeneratorExpression::StartsWithGeneratorExpression(inc)) {
-        std::string tmp = this->Makefile->GetCurrentSourceDirectory();
-        tmp += "/";
-        tmp += inc;
-        inc = tmp;
-      }
+    if (!cmSystemTools::FileIsFullPath(inc) &&
+        !cmGeneratorExpression::StartsWithGeneratorExpression(inc)) {
+      inc = cmStrCat(mf.GetCurrentSourceDirectory(), '/', inc);
     }
   }
 }
diff --git a/Source/cmIncludeDirectoryCommand.h b/Source/cmIncludeDirectoryCommand.h
index 01d98db..66caff7 100644
--- a/Source/cmIncludeDirectoryCommand.h
+++ b/Source/cmIncludeDirectoryCommand.h
@@ -8,35 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmIncludeDirectoryCommand
- * \brief Add include directories to the build.
- *
- * cmIncludeDirectoryCommand is used to specify directory locations
- * to search for included files.
- */
-class cmIncludeDirectoryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIncludeDirectoryCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  // used internally
-  void GetIncludes(const std::string& arg, std::vector<std::string>& incs);
-  void NormalizeInclude(std::string& inc);
-};
+bool cmIncludeDirectoryCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmIncludeExternalMSProjectCommand.cxx b/Source/cmIncludeExternalMSProjectCommand.cxx
index 93134ad..fa1e8bc 100644
--- a/Source/cmIncludeExternalMSProjectCommand.cxx
+++ b/Source/cmIncludeExternalMSProjectCommand.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmIncludeExternalMSProjectCommand.h"
 
+#include "cmExecutionStatus.h"
+
 #ifdef _WIN32
 #  include "cmGlobalGenerator.h"
 #  include "cmMakefile.h"
@@ -11,22 +13,20 @@
 #  include "cmake.h"
 #endif
 
-class cmExecutionStatus;
-
-// cmIncludeExternalMSProjectCommand
-bool cmIncludeExternalMSProjectCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmIncludeExternalMSProjectCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("INCLUDE_EXTERNAL_MSPROJECT called with incorrect "
-                   "number of arguments");
+    status.SetError("INCLUDE_EXTERNAL_MSPROJECT called with incorrect "
+                    "number of arguments");
     return false;
   }
+
 // only compile this for win32 to avoid coverage errors
 #ifdef _WIN32
-  if (this->Makefile->GetDefinition("WIN32") ||
-      this->Makefile->GetGlobalGenerator()
-        ->IsIncludeExternalMSProjectSupported()) {
+  cmMakefile& mf = status.GetMakefile();
+  if (mf.GetDefinition("WIN32") ||
+      mf.GetGlobalGenerator()->IsIncludeExternalMSProjectSupported()) {
     enum Doing
     {
       DoingNone,
@@ -77,15 +77,15 @@
 
     if (!customGuid.empty()) {
       std::string guidVariable = utility_name + "_GUID_CMAKE";
-      this->Makefile->GetCMakeInstance()->AddCacheEntry(
-        guidVariable.c_str(), customGuid.c_str(), "Stored GUID",
-        cmStateEnums::INTERNAL);
+      mf.GetCMakeInstance()->AddCacheEntry(guidVariable.c_str(),
+                                           customGuid.c_str(), "Stored GUID",
+                                           cmStateEnums::INTERNAL);
     }
 
     // Create a target instance for this utility.
-    cmTarget* target = this->Makefile->AddNewTarget(cmStateEnums::UTILITY,
-                                                    utility_name.c_str());
-    if (this->Makefile->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+    cmTarget* target =
+      mf.AddNewTarget(cmStateEnums::UTILITY, utility_name.c_str());
+    if (mf.GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
       target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
     }
 
diff --git a/Source/cmIncludeExternalMSProjectCommand.h b/Source/cmIncludeExternalMSProjectCommand.h
index 945acdc..1013c44 100644
--- a/Source/cmIncludeExternalMSProjectCommand.h
+++ b/Source/cmIncludeExternalMSProjectCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmIncludeExternalMSProjectCommand
- * \brief Specify an external MS project file for inclusion in the workspace.
- *
- * cmIncludeExternalMSProjectCommand is used to specify an externally
- * generated Microsoft project file for inclusion in the default workspace
- * generated by CMake.
- */
-class cmIncludeExternalMSProjectCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIncludeExternalMSProjectCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmIncludeExternalMSProjectCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmIncludeGuardCommand.cxx b/Source/cmIncludeGuardCommand.cxx
index 505b07c..ccb4496 100644
--- a/Source/cmIncludeGuardCommand.cxx
+++ b/Source/cmIncludeGuardCommand.cxx
@@ -21,7 +21,7 @@
 std::string GetIncludeGuardVariableName(std::string const& filePath)
 {
   std::string result = "__INCGUARD_";
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   result += cmSystemTools::ComputeStringMD5(filePath);
 #else
   result += cmSystemTools::MakeCidentifier(filePath);
@@ -50,11 +50,11 @@
 } // anonymous namespace
 
 // cmIncludeGuardCommand
-bool cmIncludeGuardCommand::InitialPass(std::vector<std::string> const& args,
-                                        cmExecutionStatus& status)
+bool cmIncludeGuardCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   if (args.size() > 1) {
-    this->SetError(
+    status.SetError(
       "given an invalid number of arguments. The command takes at "
       "most 1 argument.");
     return false;
@@ -69,15 +69,15 @@
     } else if (arg == "GLOBAL") {
       scope = GLOBAL;
     } else {
-      this->SetError("given an invalid scope: " + arg);
+      status.SetError("given an invalid scope: " + arg);
       return false;
     }
   }
 
   std::string includeGuardVar = GetIncludeGuardVariableName(
-    this->Makefile->GetDefinition("CMAKE_CURRENT_LIST_FILE"));
+    status.GetMakefile().GetDefinition("CMAKE_CURRENT_LIST_FILE"));
 
-  cmMakefile* const mf = this->Makefile;
+  cmMakefile* const mf = &status.GetMakefile();
 
   switch (scope) {
     case VARIABLE:
@@ -85,7 +85,7 @@
         status.SetReturnInvoked();
         return true;
       }
-      mf->AddDefinition(includeGuardVar, true);
+      mf->AddDefinitionBool(includeGuardVar, true);
       break;
     case DIRECTORY:
       if (CheckIncludeGuardIsSet(mf, includeGuardVar)) {
diff --git a/Source/cmIncludeGuardCommand.h b/Source/cmIncludeGuardCommand.h
index eaad9b8..b86b760 100644
--- a/Source/cmIncludeGuardCommand.h
+++ b/Source/cmIncludeGuardCommand.h
@@ -8,30 +8,15 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmIncludeGuardCommand
+/**
  * \brief cmIncludeGuardCommand identical to C++ #pragma_once command
  * Can work in 3 modes: GLOBAL (works on global properties),
  * DIRECTORY(use directory property), VARIABLE(unnamed overload without
  * arguments) define an ordinary variable to be used as include guard checker
  */
-class cmIncludeGuardCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIncludeGuardCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmIncludeGuardCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmIncludeRegularExpressionCommand.cxx b/Source/cmIncludeRegularExpressionCommand.cxx
index 073c95f..655ebd6 100644
--- a/Source/cmIncludeRegularExpressionCommand.cxx
+++ b/Source/cmIncludeRegularExpressionCommand.cxx
@@ -2,22 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmIncludeRegularExpressionCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// cmIncludeRegularExpressionCommand
-bool cmIncludeRegularExpressionCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmIncludeRegularExpressionCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status)
 {
-  if ((args.empty()) || (args.size() > 2)) {
-    this->SetError("called with incorrect number of arguments");
+  if (args.empty() || args.size() > 2) {
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
-  this->Makefile->SetIncludeRegularExpression(args[0].c_str());
+
+  cmMakefile& mf = status.GetMakefile();
+  mf.SetIncludeRegularExpression(args[0].c_str());
 
   if (args.size() > 1) {
-    this->Makefile->SetComplainRegularExpression(args[1]);
+    mf.SetComplainRegularExpression(args[1]);
   }
 
   return true;
diff --git a/Source/cmIncludeRegularExpressionCommand.h b/Source/cmIncludeRegularExpressionCommand.h
index 8da991d..ca152b0 100644
--- a/Source/cmIncludeRegularExpressionCommand.h
+++ b/Source/cmIncludeRegularExpressionCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmIncludeRegularExpressionCommand
- * \brief Set the regular expression for following #includes.
- *
- * cmIncludeRegularExpressionCommand is used to specify the regular expression
- * that determines whether to follow a #include file in dependency checking.
- */
-class cmIncludeRegularExpressionCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmIncludeRegularExpressionCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmIncludeRegularExpressionCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index c9e6923..0c52cc5 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -2,17 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallCommand.h"
 
-#include "cm_static_string_view.hxx"
-#include "cmsys/Glob.hxx"
+#include <cstddef>
 #include <set>
 #include <sstream>
-#include <stddef.h>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+
+#include "cmsys/Glob.hxx"
+
+#include "cm_static_string_view.hxx"
+
 #include "cmArgumentParser.h"
+#include "cmExecutionStatus.h"
 #include "cmExportSet.h"
-#include "cmExportSetMap.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallCommandArguments.h"
@@ -27,13 +30,62 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 
-class cmExecutionStatus;
+namespace {
 
-static cmInstallTargetGenerator* CreateInstallTargetGenerator(
+class Helper
+{
+public:
+  Helper(cmExecutionStatus& status)
+    : Status(status)
+    , Makefile(&status.GetMakefile())
+  {
+    this->DefaultComponentName = this->Makefile->GetSafeDefinition(
+      "CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
+    if (this->DefaultComponentName.empty()) {
+      this->DefaultComponentName = "Unspecified";
+    }
+  }
+
+  void SetError(std::string const& err) { this->Status.SetError(err); }
+
+  bool MakeFilesFullPath(const char* modeName,
+                         const std::vector<std::string>& relFiles,
+                         std::vector<std::string>& absFiles);
+  bool CheckCMP0006(bool& failure);
+
+  std::string GetDestination(const cmInstallCommandArguments* args,
+                             const std::string& varName,
+                             const std::string& guess);
+  std::string GetRuntimeDestination(const cmInstallCommandArguments* args);
+  std::string GetSbinDestination(const cmInstallCommandArguments* args);
+  std::string GetArchiveDestination(const cmInstallCommandArguments* args);
+  std::string GetLibraryDestination(const cmInstallCommandArguments* args);
+  std::string GetIncludeDestination(const cmInstallCommandArguments* args);
+  std::string GetSysconfDestination(const cmInstallCommandArguments* args);
+  std::string GetSharedStateDestination(const cmInstallCommandArguments* args);
+  std::string GetLocalStateDestination(const cmInstallCommandArguments* args);
+  std::string GetRunStateDestination(const cmInstallCommandArguments* args);
+  std::string GetDataRootDestination(const cmInstallCommandArguments* args);
+  std::string GetDataDestination(const cmInstallCommandArguments* args);
+  std::string GetInfoDestination(const cmInstallCommandArguments* args);
+  std::string GetLocaleDestination(const cmInstallCommandArguments* args);
+  std::string GetManDestination(const cmInstallCommandArguments* args);
+  std::string GetDocDestination(const cmInstallCommandArguments* args);
+  std::string GetDestinationForType(const cmInstallCommandArguments* args,
+                                    const std::string& type);
+
+  cmExecutionStatus& Status;
+  cmMakefile* Makefile;
+  std::string DefaultComponentName;
+};
+
+cmInstallTargetGenerator* CreateInstallTargetGenerator(
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
   cmListFileBacktrace const& backtrace, const std::string& destination,
   bool forceOpt = false, bool namelink = false)
@@ -52,7 +104,7 @@
   return g;
 }
 
-static cmInstallTargetGenerator* CreateInstallTargetGenerator(
+cmInstallTargetGenerator* CreateInstallTargetGenerator(
   cmTarget& target, const cmInstallCommandArguments& args, bool impLib,
   cmListFileBacktrace const& backtrace, bool forceOpt = false,
   bool namelink = false)
@@ -62,7 +114,7 @@
                                       namelink);
 }
 
-static cmInstallFilesGenerator* CreateInstallFilesGenerator(
+cmInstallFilesGenerator* CreateInstallFilesGenerator(
   cmMakefile* mf, const std::vector<std::string>& absFiles,
   const cmInstallCommandArguments& args, bool programs,
   const std::string& destination)
@@ -75,7 +127,7 @@
     args.GetExcludeFromAll(), args.GetRename().c_str(), args.GetOptional());
 }
 
-static cmInstallFilesGenerator* CreateInstallFilesGenerator(
+cmInstallFilesGenerator* CreateInstallFilesGenerator(
   cmMakefile* mf, const std::vector<std::string>& absFiles,
   const cmInstallCommandArguments& args, bool programs)
 {
@@ -83,69 +135,18 @@
                                      args.GetDestination());
 }
 
-static const std::set<std::string> allowedTypes{
+std::set<std::string> const allowedTypes{
   "BIN",         "SBIN",       "LIB",      "INCLUDE", "SYSCONF",
   "SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA",    "INFO",
   "LOCALE",      "MAN",        "DOC",
 };
 
-// cmInstallCommand
-bool cmInstallCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool HandleScriptMode(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
-  // Allow calling with no arguments so that arguments may be built up
-  // using a variable that may be left empty.
-  if (args.empty()) {
-    return true;
-  }
+  Helper helper(status);
 
-  // Enable the install target.
-  this->Makefile->GetGlobalGenerator()->EnableInstallTarget();
-
-  this->DefaultComponentName =
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
-  if (this->DefaultComponentName.empty()) {
-    this->DefaultComponentName = "Unspecified";
-  }
-
-  std::string const& mode = args[0];
-
-  // Switch among the command modes.
-  if (mode == "SCRIPT") {
-    return this->HandleScriptMode(args);
-  }
-  if (mode == "CODE") {
-    return this->HandleScriptMode(args);
-  }
-  if (mode == "TARGETS") {
-    return this->HandleTargetsMode(args);
-  }
-  if (mode == "FILES") {
-    return this->HandleFilesMode(args);
-  }
-  if (mode == "PROGRAMS") {
-    return this->HandleFilesMode(args);
-  }
-  if (mode == "DIRECTORY") {
-    return this->HandleDirectoryMode(args);
-  }
-  if (mode == "EXPORT") {
-    return this->HandleExportMode(args);
-  }
-  if (mode == "EXPORT_ANDROID_MK") {
-    return this->HandleExportAndroidMKMode(args);
-  }
-
-  // Unknown mode.
-  std::string e = "called with unknown mode ";
-  e += args[0];
-  this->SetError(e);
-  return false;
-}
-
-bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args)
-{
-  std::string component = this->DefaultComponentName;
+  std::string component = helper.DefaultComponentName;
   int componentCount = 0;
   bool doing_script = false;
   bool doing_code = false;
@@ -165,9 +166,9 @@
   }
 
   if (componentCount > 1) {
-    this->SetError("given more than one COMPONENT for the SCRIPT or CODE "
-                   "signature of the INSTALL command. "
-                   "Use multiple INSTALL commands with one COMPONENT each.");
+    status.SetError("given more than one COMPONENT for the SCRIPT or CODE "
+                    "signature of the INSTALL command. "
+                    "Use multiple INSTALL commands with one COMPONENT each.");
     return false;
   }
 
@@ -188,42 +189,44 @@
       doing_script = false;
       std::string script = arg;
       if (!cmSystemTools::FileIsFullPath(script)) {
-        script = this->Makefile->GetCurrentSourceDirectory();
-        script += "/";
-        script += arg;
+        script =
+          cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg);
       }
       if (cmSystemTools::FileIsDirectory(script)) {
-        this->SetError("given a directory as value of SCRIPT argument.");
+        status.SetError("given a directory as value of SCRIPT argument.");
         return false;
       }
-      this->Makefile->AddInstallGenerator(new cmInstallScriptGenerator(
+      helper.Makefile->AddInstallGenerator(new cmInstallScriptGenerator(
         script.c_str(), false, component.c_str(), exclude_from_all));
     } else if (doing_code) {
       doing_code = false;
       std::string const& code = arg;
-      this->Makefile->AddInstallGenerator(new cmInstallScriptGenerator(
+      helper.Makefile->AddInstallGenerator(new cmInstallScriptGenerator(
         code.c_str(), true, component.c_str(), exclude_from_all));
     }
   }
 
   if (doing_script) {
-    this->SetError("given no value for SCRIPT argument.");
+    status.SetError("given no value for SCRIPT argument.");
     return false;
   }
   if (doing_code) {
-    this->SetError("given no value for CODE argument.");
+    status.SetError("given no value for CODE argument.");
     return false;
   }
 
   // Tell the global generator about any installation component names
   // specified.
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(component);
+  helper.Makefile->GetGlobalGenerator()->AddInstallComponent(component);
 
   return true;
 }
 
-bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
+bool HandleTargetsMode(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
+  Helper helper(status);
+
   // This is the TARGETS mode.
   std::vector<cmTarget*> targets;
 
@@ -263,21 +266,21 @@
   std::vector<std::string> targetList;
   std::string exports;
   std::vector<std::string> unknownArgs;
-  cmInstallCommandArguments genericArgs(this->DefaultComponentName);
+  cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
   genericArgs.Bind("TARGETS"_s, targetList);
   genericArgs.Bind("EXPORT"_s, exports);
   genericArgs.Parse(genericArgVector, &unknownArgs);
   bool success = genericArgs.Finalize();
 
-  cmInstallCommandArguments archiveArgs(this->DefaultComponentName);
-  cmInstallCommandArguments libraryArgs(this->DefaultComponentName);
-  cmInstallCommandArguments runtimeArgs(this->DefaultComponentName);
-  cmInstallCommandArguments objectArgs(this->DefaultComponentName);
-  cmInstallCommandArguments frameworkArgs(this->DefaultComponentName);
-  cmInstallCommandArguments bundleArgs(this->DefaultComponentName);
-  cmInstallCommandArguments privateHeaderArgs(this->DefaultComponentName);
-  cmInstallCommandArguments publicHeaderArgs(this->DefaultComponentName);
-  cmInstallCommandArguments resourceArgs(this->DefaultComponentName);
+  cmInstallCommandArguments archiveArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments libraryArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments objectArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments bundleArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName);
+  cmInstallCommandArguments resourceArgs(helper.DefaultComponentName);
   cmInstallCommandIncludesArgument includesArgs;
 
   // now parse the args for specific parts of the target (e.g. LIBRARY,
@@ -295,9 +298,8 @@
 
   if (!unknownArgs.empty()) {
     // Unknown argument.
-    std::ostringstream e;
-    e << "TARGETS given unknown argument \"" << unknownArgs[0] << "\".";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat("TARGETS given unknown argument \"", unknownArgs[0], "\"."));
     return false;
   }
 
@@ -332,7 +334,7 @@
       objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() ||
       bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() ||
       publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly()) {
-    this->SetError(
+    status.SetError(
       "TARGETS given NAMELINK_ONLY option not in LIBRARY group.  "
       "The NAMELINK_ONLY option may be specified only following LIBRARY.");
     return false;
@@ -341,7 +343,7 @@
       objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() ||
       bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() ||
       publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip()) {
-    this->SetError(
+    status.SetError(
       "TARGETS given NAMELINK_SKIP option not in LIBRARY group.  "
       "The NAMELINK_SKIP option may be specified only following LIBRARY.");
     return false;
@@ -354,15 +356,15 @@
       privateHeaderArgs.HasNamelinkComponent() ||
       publicHeaderArgs.HasNamelinkComponent() ||
       resourceArgs.HasNamelinkComponent()) {
-    this->SetError(
+    status.SetError(
       "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group.  "
       "The NAMELINK_COMPONENT option may be specified only following "
       "LIBRARY.");
     return false;
   }
   if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
-    this->SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP.  "
-                   "At most one of these two options may be specified.");
+    status.SetError("TARGETS given NAMELINK_ONLY and NAMELINK_SKIP.  "
+                    "At most one of these two options may be specified.");
     return false;
   }
   if (!genericArgs.GetType().empty() || !archiveArgs.GetType().empty() ||
@@ -370,10 +372,9 @@
       !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() ||
       !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() ||
       !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty()) {
-    std::ostringstream e;
-    e << "TARGETS given TYPE option. The TYPE option may only be specified in "
-         " install(FILES) and install(DIRECTORIES).";
-    this->SetError(e.str());
+    status.SetError(
+      "TARGETS given TYPE option. The TYPE option may only be specified in "
+      " install(FILES) and install(DIRECTORIES).");
     return false;
   }
 
@@ -391,24 +392,19 @@
     return true;
   }
 
-  // Check whether this is a DLL platform.
-  bool dll_platform =
-    !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty();
-
   for (std::string const& tgt : targetList) {
 
-    if (this->Makefile->IsAlias(tgt)) {
-      std::ostringstream e;
-      e << "TARGETS given target \"" << tgt << "\" which is an alias.";
-      this->SetError(e.str());
+    if (helper.Makefile->IsAlias(tgt)) {
+      status.SetError(
+        cmStrCat("TARGETS given target \"", tgt, "\" which is an alias."));
       return false;
     }
     // Lookup this target in the current directory.
-    cmTarget* target = this->Makefile->FindLocalNonAliasTarget(tgt);
+    cmTarget* target = helper.Makefile->FindLocalNonAliasTarget(tgt);
     if (!target) {
       // If no local target has been found, find it in the global scope.
       cmTarget* const global_target =
-        this->Makefile->GetGlobalGenerator()->FindTarget(tgt, true);
+        helper.Makefile->GetGlobalGenerator()->FindTarget(tgt, true);
       if (global_target && !global_target->IsImported()) {
         target = global_target;
       }
@@ -421,19 +417,17 @@
           target->GetType() != cmStateEnums::MODULE_LIBRARY &&
           target->GetType() != cmStateEnums::OBJECT_LIBRARY &&
           target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
-        std::ostringstream e;
-        e << "TARGETS given target \"" << tgt
-          << "\" which is not an executable, library, or module.";
-        this->SetError(e.str());
+        status.SetError(
+          cmStrCat("TARGETS given target \"", tgt,
+                   "\" which is not an executable, library, or module."));
         return false;
       }
       // Store the target in the list to be installed.
       targets.push_back(target);
     } else {
       // Did not find the target.
-      std::ostringstream e;
-      e << "TARGETS given target \"" << tgt << "\" which does not exist.";
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat("TARGETS given target \"", tgt, "\" which does not exist."));
       return false;
     }
   }
@@ -474,7 +468,7 @@
         // Shared libraries are handled differently on DLL and non-DLL
         // platforms.  All windows platforms are DLL platforms including
         // cygwin.  Currently no other platform is a DLL platform.
-        if (dll_platform) {
+        if (target.IsDLLPlatform()) {
           // When in namelink only mode skip all libraries on Windows.
           if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
             continue;
@@ -484,20 +478,20 @@
           if (!archiveArgs.GetDestination().empty()) {
             // The import library uses the ARCHIVE properties.
             archiveGenerator = CreateInstallTargetGenerator(
-              target, archiveArgs, true, this->Makefile->GetBacktrace());
+              target, archiveArgs, true, helper.Makefile->GetBacktrace());
           }
           if (!runtimeArgs.GetDestination().empty()) {
             // The DLL uses the RUNTIME properties.
             runtimeGenerator = CreateInstallTargetGenerator(
-              target, runtimeArgs, false, this->Makefile->GetBacktrace());
+              target, runtimeArgs, false, helper.Makefile->GetBacktrace());
           }
           if ((archiveGenerator == nullptr) && (runtimeGenerator == nullptr)) {
             archiveGenerator = CreateInstallTargetGenerator(
-              target, archiveArgs, true, this->Makefile->GetBacktrace(),
-              this->GetArchiveDestination(nullptr));
+              target, archiveArgs, true, helper.Makefile->GetBacktrace(),
+              helper.GetArchiveDestination(nullptr));
             runtimeGenerator = CreateInstallTargetGenerator(
-              target, runtimeArgs, false, this->Makefile->GetBacktrace(),
-              this->GetRuntimeDestination(nullptr));
+              target, runtimeArgs, false, helper.Makefile->GetBacktrace(),
+              helper.GetRuntimeDestination(nullptr));
           }
         } else {
           // This is a non-DLL platform.
@@ -512,28 +506,27 @@
             // Use the FRAMEWORK properties.
             if (!frameworkArgs.GetDestination().empty()) {
               frameworkGenerator = CreateInstallTargetGenerator(
-                target, frameworkArgs, false, this->Makefile->GetBacktrace());
+                target, frameworkArgs, false, helper.Makefile->GetBacktrace());
             } else {
-              std::ostringstream e;
-              e << "TARGETS given no FRAMEWORK DESTINATION for shared library "
-                   "FRAMEWORK target \""
-                << target.GetName() << "\".";
-              this->SetError(e.str());
+              status.SetError(
+                cmStrCat("TARGETS given no FRAMEWORK DESTINATION for shared "
+                         "library FRAMEWORK target \"",
+                         target.GetName(), "\"."));
               return false;
             }
           } else {
             // The shared library uses the LIBRARY properties.
             if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
               libraryGenerator = CreateInstallTargetGenerator(
-                target, libraryArgs, false, this->Makefile->GetBacktrace(),
-                this->GetLibraryDestination(&libraryArgs));
+                target, libraryArgs, false, helper.Makefile->GetBacktrace(),
+                helper.GetLibraryDestination(&libraryArgs));
               libraryGenerator->SetNamelinkMode(
                 cmInstallTargetGenerator::NamelinkModeSkip);
             }
             if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
               namelinkGenerator = CreateInstallTargetGenerator(
-                target, libraryArgs, false, this->Makefile->GetBacktrace(),
-                this->GetLibraryDestination(&libraryArgs), false, true);
+                target, libraryArgs, false, helper.Makefile->GetBacktrace(),
+                helper.GetLibraryDestination(&libraryArgs), false, true);
               namelinkGenerator->SetNamelinkMode(
                 cmInstallTargetGenerator::NamelinkModeOnly);
             }
@@ -554,35 +547,34 @@
           // Use the FRAMEWORK properties.
           if (!frameworkArgs.GetDestination().empty()) {
             frameworkGenerator = CreateInstallTargetGenerator(
-              target, frameworkArgs, false, this->Makefile->GetBacktrace());
+              target, frameworkArgs, false, helper.Makefile->GetBacktrace());
           } else {
-            std::ostringstream e;
-            e << "TARGETS given no FRAMEWORK DESTINATION for static library "
-                 "FRAMEWORK target \""
-              << target.GetName() << "\".";
-            this->SetError(e.str());
+            status.SetError(
+              cmStrCat("TARGETS given no FRAMEWORK DESTINATION for static "
+                       "library FRAMEWORK target \"",
+                       target.GetName(), "\"."));
             return false;
           }
         } else {
           // Static libraries use ARCHIVE properties.
           archiveGenerator = CreateInstallTargetGenerator(
-            target, archiveArgs, false, this->Makefile->GetBacktrace(),
-            this->GetArchiveDestination(&archiveArgs));
+            target, archiveArgs, false, helper.Makefile->GetBacktrace(),
+            helper.GetArchiveDestination(&archiveArgs));
         }
       } break;
       case cmStateEnums::MODULE_LIBRARY: {
         // Modules use LIBRARY properties.
         if (!libraryArgs.GetDestination().empty()) {
           libraryGenerator = CreateInstallTargetGenerator(
-            target, libraryArgs, false, this->Makefile->GetBacktrace());
+            target, libraryArgs, false, helper.Makefile->GetBacktrace());
           libraryGenerator->SetNamelinkMode(namelinkMode);
           namelinkOnly =
             (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
         } else {
-          std::ostringstream e;
-          e << "TARGETS given no LIBRARY DESTINATION for module target \""
-            << target.GetName() << "\".";
-          this->SetError(e.str());
+          status.SetError(
+            cmStrCat("TARGETS given no LIBRARY DESTINATION for module "
+                     "target \"",
+                     target.GetName(), "\"."));
           return false;
         }
       } break;
@@ -591,17 +583,16 @@
         if (!objectArgs.GetDestination().empty()) {
           // Verify that we know where the objects are to install them.
           std::string reason;
-          if (!this->Makefile->GetGlobalGenerator()
+          if (!helper.Makefile->GetGlobalGenerator()
                  ->HasKnownObjectFileLocation(&reason)) {
-            std::ostringstream e;
-            e << "TARGETS given OBJECT library \"" << target.GetName()
-              << "\" whose objects may not be installed" << reason << ".";
-            this->SetError(e.str());
+            status.SetError(
+              cmStrCat("TARGETS given OBJECT library \"", target.GetName(),
+                       "\" whose objects may not be installed", reason, "."));
             return false;
           }
 
           objectGenerator = CreateInstallTargetGenerator(
-            target, objectArgs, false, this->Makefile->GetBacktrace());
+            target, objectArgs, false, helper.Makefile->GetBacktrace());
         } else {
           // Installing an OBJECT library without a destination transforms
           // it to an INTERFACE library.  It installs no files but can be
@@ -613,41 +604,40 @@
           // Application bundles use the BUNDLE properties.
           if (!bundleArgs.GetDestination().empty()) {
             bundleGenerator = CreateInstallTargetGenerator(
-              target, bundleArgs, false, this->Makefile->GetBacktrace());
+              target, bundleArgs, false, helper.Makefile->GetBacktrace());
           } else if (!runtimeArgs.GetDestination().empty()) {
             bool failure = false;
-            if (this->CheckCMP0006(failure)) {
+            if (helper.CheckCMP0006(failure)) {
               // For CMake 2.4 compatibility fallback to the RUNTIME
               // properties.
               bundleGenerator = CreateInstallTargetGenerator(
-                target, runtimeArgs, false, this->Makefile->GetBacktrace());
+                target, runtimeArgs, false, helper.Makefile->GetBacktrace());
             } else if (failure) {
               return false;
             }
           }
           if (!bundleGenerator) {
-            std::ostringstream e;
-            e << "TARGETS given no BUNDLE DESTINATION for MACOSX_BUNDLE "
-                 "executable target \""
-              << target.GetName() << "\".";
-            this->SetError(e.str());
+            status.SetError(cmStrCat("TARGETS given no BUNDLE DESTINATION for "
+                                     "MACOSX_BUNDLE executable target \"",
+                                     target.GetName(), "\"."));
             return false;
           }
         } else {
           // Executables use the RUNTIME properties.
           runtimeGenerator = CreateInstallTargetGenerator(
-            target, runtimeArgs, false, this->Makefile->GetBacktrace(),
-            this->GetRuntimeDestination(&runtimeArgs));
+            target, runtimeArgs, false, helper.Makefile->GetBacktrace(),
+            helper.GetRuntimeDestination(&runtimeArgs));
         }
 
         // On DLL platforms an executable may also have an import
         // library.  Install it to the archive destination if it
         // exists.
-        if (dll_platform && !archiveArgs.GetDestination().empty() &&
+        if ((target.IsDLLPlatform() || target.IsAIX()) &&
+            !archiveArgs.GetDestination().empty() &&
             target.IsExecutableWithExports()) {
           // The import library uses the ARCHIVE properties.
           archiveGenerator = CreateInstallTargetGenerator(
-            target, archiveArgs, true, this->Makefile->GetBacktrace(), true);
+            target, archiveArgs, true, helper.Makefile->GetBacktrace(), true);
         }
       } break;
       case cmStateEnums::INTERFACE_LIBRARY:
@@ -675,52 +665,49 @@
     if (createInstallGeneratorsForTargetFileSets && !namelinkOnly) {
       const char* files = target.GetProperty("PRIVATE_HEADER");
       if ((files) && (*files)) {
-        std::vector<std::string> relFiles;
-        cmSystemTools::ExpandListArgument(files, relFiles);
+        std::vector<std::string> relFiles = cmExpandedList(files);
         std::vector<std::string> absFiles;
-        if (!this->MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles)) {
+        if (!helper.MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles)) {
           return false;
         }
 
         // Create the files install generator.
         privateHeaderGenerator = CreateInstallFilesGenerator(
-          this->Makefile, absFiles, privateHeaderArgs, false,
-          this->GetIncludeDestination(&privateHeaderArgs));
+          helper.Makefile, absFiles, privateHeaderArgs, false,
+          helper.GetIncludeDestination(&privateHeaderArgs));
       }
 
       files = target.GetProperty("PUBLIC_HEADER");
       if ((files) && (*files)) {
-        std::vector<std::string> relFiles;
-        cmSystemTools::ExpandListArgument(files, relFiles);
+        std::vector<std::string> relFiles = cmExpandedList(files);
         std::vector<std::string> absFiles;
-        if (!this->MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles)) {
+        if (!helper.MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles)) {
           return false;
         }
 
         // Create the files install generator.
         publicHeaderGenerator = CreateInstallFilesGenerator(
-          this->Makefile, absFiles, publicHeaderArgs, false,
-          this->GetIncludeDestination(&publicHeaderArgs));
+          helper.Makefile, absFiles, publicHeaderArgs, false,
+          helper.GetIncludeDestination(&publicHeaderArgs));
       }
 
       files = target.GetProperty("RESOURCE");
       if ((files) && (*files)) {
-        std::vector<std::string> relFiles;
-        cmSystemTools::ExpandListArgument(files, relFiles);
+        std::vector<std::string> relFiles = cmExpandedList(files);
         std::vector<std::string> absFiles;
-        if (!this->MakeFilesFullPath("RESOURCE", relFiles, absFiles)) {
+        if (!helper.MakeFilesFullPath("RESOURCE", relFiles, absFiles)) {
           return false;
         }
 
         // Create the files install generator.
         if (!resourceArgs.GetDestination().empty()) {
           resourceGenerator = CreateInstallFilesGenerator(
-            this->Makefile, absFiles, resourceArgs, false);
+            helper.Makefile, absFiles, resourceArgs, false);
         } else {
-          std::ostringstream e;
-          e << "INSTALL TARGETS - target " << target.GetName() << " has "
-            << "RESOURCE files but no RESOURCE DESTINATION.";
-          cmSystemTools::Message(e.str(), "Warning");
+          cmSystemTools::Message(
+            cmStrCat("INSTALL TARGETS - target ", target.GetName(),
+                     " has RESOURCE files but no RESOURCE DESTINATION."),
+            "Warning");
         }
       }
     }
@@ -739,21 +726,21 @@
       installsPublicHeader || publicHeaderGenerator != nullptr;
     installsResource = installsResource || resourceGenerator;
 
-    this->Makefile->AddInstallGenerator(archiveGenerator);
-    this->Makefile->AddInstallGenerator(libraryGenerator);
-    this->Makefile->AddInstallGenerator(namelinkGenerator);
-    this->Makefile->AddInstallGenerator(runtimeGenerator);
-    this->Makefile->AddInstallGenerator(objectGenerator);
-    this->Makefile->AddInstallGenerator(frameworkGenerator);
-    this->Makefile->AddInstallGenerator(bundleGenerator);
-    this->Makefile->AddInstallGenerator(privateHeaderGenerator);
-    this->Makefile->AddInstallGenerator(publicHeaderGenerator);
-    this->Makefile->AddInstallGenerator(resourceGenerator);
+    helper.Makefile->AddInstallGenerator(archiveGenerator);
+    helper.Makefile->AddInstallGenerator(libraryGenerator);
+    helper.Makefile->AddInstallGenerator(namelinkGenerator);
+    helper.Makefile->AddInstallGenerator(runtimeGenerator);
+    helper.Makefile->AddInstallGenerator(objectGenerator);
+    helper.Makefile->AddInstallGenerator(frameworkGenerator);
+    helper.Makefile->AddInstallGenerator(bundleGenerator);
+    helper.Makefile->AddInstallGenerator(privateHeaderGenerator);
+    helper.Makefile->AddInstallGenerator(publicHeaderGenerator);
+    helper.Makefile->AddInstallGenerator(resourceGenerator);
 
     // Add this install rule to an export if one was specified and
     // this is not a namelink-only rule.
     if (!exports.empty() && !namelinkOnly) {
-      cmTargetExport* te = new cmTargetExport;
+      auto te = cm::make_unique<cmTargetExport>();
       te->TargetName = target.GetName();
       te->ArchiveGenerator = archiveGenerator;
       te->BundleGenerator = bundleGenerator;
@@ -762,66 +749,69 @@
       te->LibraryGenerator = libraryGenerator;
       te->RuntimeGenerator = runtimeGenerator;
       te->ObjectsGenerator = objectGenerator;
-      this->Makefile->GetGlobalGenerator()
-        ->GetExportSets()[exports]
-        ->AddTargetExport(te);
-
       te->InterfaceIncludeDirectories =
         cmJoin(includesArgs.GetIncludeDirs(), ";");
+
+      helper.Makefile->GetGlobalGenerator()
+        ->GetExportSets()[exports]
+        .AddTargetExport(std::move(te));
     }
   }
 
   // Tell the global generator about any installation component names
   // specified
   if (installsArchive) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       archiveArgs.GetComponent());
   }
   if (installsLibrary) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       libraryArgs.GetComponent());
   }
   if (installsNamelink) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       libraryArgs.GetNamelinkComponent());
   }
   if (installsRuntime) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       runtimeArgs.GetComponent());
   }
   if (installsObject) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       objectArgs.GetComponent());
   }
   if (installsFramework) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       frameworkArgs.GetComponent());
   }
   if (installsBundle) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       bundleArgs.GetComponent());
   }
   if (installsPrivateHeader) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       privateHeaderArgs.GetComponent());
   }
   if (installsPublicHeader) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       publicHeaderArgs.GetComponent());
   }
   if (installsResource) {
-    this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       resourceArgs.GetComponent());
   }
 
   return true;
 }
 
-bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args)
+bool HandleFilesMode(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
 {
+  Helper helper(status);
+
   // This is the FILES mode.
   bool programs = (args[0] == "PROGRAMS");
-  cmInstallCommandArguments ica(this->DefaultComponentName);
+  cmInstallCommandArguments ica(helper.DefaultComponentName);
   std::vector<std::string> files;
   ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files);
   std::vector<std::string> unknownArgs;
@@ -829,17 +819,15 @@
 
   if (!unknownArgs.empty()) {
     // Unknown argument.
-    std::ostringstream e;
-    e << args[0] << " given unknown argument \"" << unknownArgs[0] << "\".";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\"."));
     return false;
   }
 
   std::string type = ica.GetType();
   if (!type.empty() && allowedTypes.count(type) == 0) {
-    std::ostringstream e;
-    e << args[0] << " given non-type \"" << type << "\" with TYPE argument.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given non-type \"", type, "\" with TYPE argument."));
     return false;
   }
 
@@ -852,28 +840,27 @@
 
   if (!ica.GetRename().empty() && filesVector.size() > 1) {
     // The rename option works only with one file.
-    std::ostringstream e;
-    e << args[0] << " given RENAME option with more than one file.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given RENAME option with more than one file."));
     return false;
   }
 
   std::vector<std::string> absFiles;
-  if (!this->MakeFilesFullPath(args[0].c_str(), filesVector, absFiles)) {
+  if (!helper.MakeFilesFullPath(args[0].c_str(), filesVector, absFiles)) {
     return false;
   }
 
-  cmPolicies::PolicyStatus status =
-    this->Makefile->GetPolicyStatus(cmPolicies::CMP0062);
+  cmPolicies::PolicyStatus policyStatus =
+    helper.Makefile->GetPolicyStatus(cmPolicies::CMP0062);
 
-  cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
+  cmGlobalGenerator* gg = helper.Makefile->GetGlobalGenerator();
   for (std::string const& file : filesVector) {
     if (gg->IsExportedTargetsFile(file)) {
       const char* modal = nullptr;
       std::ostringstream e;
       MessageType messageType = MessageType::AUTHOR_WARNING;
 
-      switch (status) {
+      switch (policyStatus) {
         case cmPolicies::WARN:
           e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0062) << "\n";
           modal = "should";
@@ -893,7 +880,7 @@
           << " not be installed with the "
              "install() command.  Use the install(EXPORT) mechanism "
              "instead.  See the cmake-packages(7) manual for more.\n";
-        this->Makefile->IssueMessage(messageType, e.str());
+        helper.Makefile->IssueMessage(messageType, e.str());
         if (messageType == MessageType::FATAL_ERROR) {
           return false;
         }
@@ -906,38 +893,36 @@
   }
 
   if (!type.empty() && !ica.GetDestination().empty()) {
-    std::ostringstream e;
-    e << args[0]
-      << " given both TYPE and DESTINATION arguments. You may only specify "
-         "one.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0],
+                             " given both TYPE and DESTINATION arguments. "
+                             "You may only specify one."));
     return false;
   }
 
-  std::string destination = this->GetDestinationForType(&ica, type);
+  std::string destination = helper.GetDestinationForType(&ica, type);
   if (destination.empty()) {
     // A destination is required.
-    std::ostringstream e;
-    e << args[0] << " given no DESTINATION!";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " given no DESTINATION!"));
     return false;
   }
 
   // Create the files install generator.
-  this->Makefile->AddInstallGenerator(CreateInstallFilesGenerator(
-    this->Makefile, absFiles, ica, programs, destination));
+  helper.Makefile->AddInstallGenerator(CreateInstallFilesGenerator(
+    helper.Makefile, absFiles, ica, programs, destination));
 
   // Tell the global generator about any installation component names
   // specified.
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(
+  helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
     ica.GetComponent());
 
   return true;
 }
 
-bool cmInstallCommand::HandleDirectoryMode(
-  std::vector<std::string> const& args)
+bool HandleDirectoryMode(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
+  Helper helper(status);
+
   enum Doing
   {
     DoingNone,
@@ -962,16 +947,14 @@
   std::string permissions_file;
   std::string permissions_dir;
   std::vector<std::string> configurations;
-  std::string component = this->DefaultComponentName;
+  std::string component = helper.DefaultComponentName;
   std::string literal_args;
   std::string type;
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (args[i] == "DESTINATION") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -979,10 +962,8 @@
       doing = DoingDestination;
     } else if (args[i] == "TYPE") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -990,10 +971,8 @@
       doing = DoingType;
     } else if (args[i] == "OPTIONAL") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1002,10 +981,8 @@
       doing = DoingNone;
     } else if (args[i] == "MESSAGE_NEVER") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1023,20 +1000,16 @@
     } else if (args[i] == "EXCLUDE") {
       // Add this property to the current match rule.
       if (!in_match_mode || doing == DoingPattern || doing == DoingRegex) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" before a PATTERN or REGEX is given.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" before a PATTERN or REGEX is given."));
         return false;
       }
       literal_args += " EXCLUDE";
       doing = DoingNone;
     } else if (args[i] == "PERMISSIONS") {
       if (!in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" before a PATTERN or REGEX is given.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" before a PATTERN or REGEX is given."));
         return false;
       }
 
@@ -1045,10 +1018,8 @@
       doing = DoingPermsMatch;
     } else if (args[i] == "FILE_PERMISSIONS") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1056,10 +1027,8 @@
       doing = DoingPermsFile;
     } else if (args[i] == "DIRECTORY_PERMISSIONS") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1067,10 +1036,8 @@
       doing = DoingPermsDir;
     } else if (args[i] == "USE_SOURCE_PERMISSIONS") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1079,10 +1046,8 @@
       doing = DoingNone;
     } else if (args[i] == "FILES_MATCHING") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1091,10 +1056,8 @@
       doing = DoingNone;
     } else if (args[i] == "CONFIGURATIONS") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1102,10 +1065,8 @@
       doing = DoingConfigurations;
     } else if (args[i] == "COMPONENT") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
 
@@ -1113,10 +1074,8 @@
       doing = DoingComponent;
     } else if (args[i] == "EXCLUDE_FROM_ALL") {
       if (in_match_mode) {
-        std::ostringstream e;
-        e << args[0] << " does not allow \"" << args[i]
-          << "\" after PATTERN or REGEX.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
+                                 "\" after PATTERN or REGEX."));
         return false;
       }
       exclude_from_all = true;
@@ -1126,18 +1085,15 @@
       std::string dir = args[i];
       std::string::size_type gpos = cmGeneratorExpression::Find(dir);
       if (gpos != 0 && !cmSystemTools::FileIsFullPath(dir)) {
-        dir = this->Makefile->GetCurrentSourceDirectory();
-        dir += "/";
-        dir += args[i];
+        dir =
+          cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', args[i]);
       }
 
       // Make sure the name is a directory.
       if (cmSystemTools::FileExists(dir) &&
           !cmSystemTools::FileIsDirectory(dir)) {
-        std::ostringstream e;
-        e << args[0] << " given non-directory \"" << args[i]
-          << "\" to install.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " given non-directory \"", args[i],
+                                 "\" to install."));
         return false;
       }
 
@@ -1150,10 +1106,8 @@
       doing = DoingNone;
     } else if (doing == DoingType) {
       if (allowedTypes.count(args[i]) == 0) {
-        std::ostringstream e;
-        e << args[0] << " given non-type \"" << args[i]
-          << "\" with TYPE argument.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " given non-type \"", args[i],
+                                 "\" with TYPE argument."));
         return false;
       }
 
@@ -1189,36 +1143,30 @@
       // Check the requested permission.
       if (!cmInstallCommandArguments::CheckPermissions(args[i],
                                                        permissions_file)) {
-        std::ostringstream e;
-        e << args[0] << " given invalid file permission \"" << args[i]
-          << "\".";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(args[0], " given invalid file permission \"",
+                                 args[i], "\"."));
         return false;
       }
     } else if (doing == DoingPermsDir) {
       // Check the requested permission.
       if (!cmInstallCommandArguments::CheckPermissions(args[i],
                                                        permissions_dir)) {
-        std::ostringstream e;
-        e << args[0] << " given invalid directory permission \"" << args[i]
-          << "\".";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(
+          args[0], " given invalid directory permission \"", args[i], "\"."));
         return false;
       }
     } else if (doing == DoingPermsMatch) {
       // Check the requested permission.
       if (!cmInstallCommandArguments::CheckPermissions(args[i],
                                                        literal_args)) {
-        std::ostringstream e;
-        e << args[0] << " given invalid permission \"" << args[i] << "\".";
-        this->SetError(e.str());
+        status.SetError(
+          cmStrCat(args[0], " given invalid permission \"", args[i], "\"."));
         return false;
       }
     } else {
       // Unknown argument.
-      std::ostringstream e;
-      e << args[0] << " given unknown argument \"" << args[i] << "\".";
-      this->SetError(e.str());
+      status.SetError(
+        cmStrCat(args[0], " given unknown argument \"", args[i], "\"."));
       return false;
     }
   }
@@ -1236,44 +1184,42 @@
   if (!destination) {
     if (type.empty()) {
       // A destination is required.
-      std::ostringstream e;
-      e << args[0] << " given no DESTINATION!";
-      this->SetError(e.str());
+      status.SetError(cmStrCat(args[0], " given no DESTINATION!"));
       return false;
     }
-    destinationStr = this->GetDestinationForType(nullptr, type);
+    destinationStr = helper.GetDestinationForType(nullptr, type);
     destination = destinationStr.c_str();
   } else if (!type.empty()) {
-    std::ostringstream e;
-    e << args[0]
-      << " given both TYPE and DESTINATION arguments. You may only specify "
-         "one.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0],
+                             " given both TYPE and DESTINATION "
+                             "arguments. You may only specify one."));
     return false;
   }
 
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(this->Makefile, message_never);
+    cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never);
 
   // Create the directory install generator.
-  this->Makefile->AddInstallGenerator(new cmInstallDirectoryGenerator(
+  helper.Makefile->AddInstallGenerator(new cmInstallDirectoryGenerator(
     dirs, destination, permissions_file.c_str(), permissions_dir.c_str(),
     configurations, component.c_str(), message, exclude_from_all,
     literal_args.c_str(), optional));
 
   // Tell the global generator about any installation component names
   // specified.
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(component);
+  helper.Makefile->GetGlobalGenerator()->AddInstallComponent(component);
 
   return true;
 }
 
-bool cmInstallCommand::HandleExportAndroidMKMode(
-  std::vector<std::string> const& args)
+bool HandleExportAndroidMKMode(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
+  Helper helper(status);
+
   // This is the EXPORT mode.
-  cmInstallCommandArguments ica(this->DefaultComponentName);
+  cmInstallCommandArguments ica(helper.DefaultComponentName);
 
   std::string exp;
   std::string name_space;
@@ -1290,9 +1236,8 @@
 
   if (!unknownArgs.empty()) {
     // Unknown argument.
-    std::ostringstream e;
-    e << args[0] << " given unknown argument \"" << unknownArgs[0] << "\".";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\"."));
     return false;
   }
 
@@ -1303,39 +1248,35 @@
   // Make sure there is a destination.
   if (ica.GetDestination().empty()) {
     // A destination is required.
-    std::ostringstream e;
-    e << args[0] << " given no DESTINATION!";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " given no DESTINATION!"));
     return false;
   }
 
   // Check the file name.
   std::string fname = filename;
   if (fname.find_first_of(":/\\") != std::string::npos) {
-    std::ostringstream e;
-    e << args[0] << " given invalid export file name \"" << fname << "\".  "
-      << "The FILE argument may not contain a path.  "
-      << "Specify the path in the DESTINATION argument.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " given invalid export file name \"",
+                             fname,
+                             "\".  The FILE argument may not contain a path.  "
+                             "Specify the path in the DESTINATION argument."));
     return false;
   }
 
   // Check the file extension.
   if (!fname.empty() &&
       cmSystemTools::GetFilenameLastExtension(fname) != ".mk") {
-    std::ostringstream e;
-    e << args[0] << " given invalid export file name \"" << fname << "\".  "
-      << "The FILE argument must specify a name ending in \".mk\".";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(
+      args[0], " given invalid export file name \"", fname,
+      R"(".  The FILE argument must specify a name ending in ".mk".)"));
     return false;
   }
   if (fname.find_first_of(":/\\") != std::string::npos) {
-    std::ostringstream e;
-    e << args[0] << " given export name \"" << exp << "\".  "
-      << "This name cannot be safely converted to a file name.  "
-      << "Specify a different export name or use the FILE option to set "
-      << "a file name explicitly.";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given export name \"", exp,
+               "\".  "
+               "This name cannot be safely converted to a file name.  "
+               "Specify a different export name or use the FILE option to set "
+               "a file name explicitly."));
     return false;
   }
   // Use the default name
@@ -1343,32 +1284,35 @@
     fname = "Android.mk";
   }
 
-  cmExportSet* exportSet =
-    this->Makefile->GetGlobalGenerator()->GetExportSets()[exp];
+  cmExportSet& exportSet =
+    helper.Makefile->GetGlobalGenerator()->GetExportSets()[exp];
 
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(this->Makefile);
+    cmInstallGenerator::SelectMessageLevel(helper.Makefile);
 
   // Create the export install generator.
   cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator(
-    exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
+    &exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
     ica.GetConfigurations(), ica.GetComponent().c_str(), message,
     ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld,
     true);
-  this->Makefile->AddInstallGenerator(exportGenerator);
+  helper.Makefile->AddInstallGenerator(exportGenerator);
 
   return true;
 #else
   static_cast<void>(args);
-  this->SetError("EXPORT_ANDROID_MK not supported in bootstrap cmake");
+  status.SetError("EXPORT_ANDROID_MK not supported in bootstrap cmake");
   return false;
 #endif
 }
 
-bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
+bool HandleExportMode(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
+  Helper helper(status);
+
   // This is the EXPORT mode.
-  cmInstallCommandArguments ica(this->DefaultComponentName);
+  cmInstallCommandArguments ica(helper.DefaultComponentName);
 
   std::string exp;
   std::string name_space;
@@ -1385,9 +1329,8 @@
 
   if (!unknownArgs.empty()) {
     // Unknown argument.
-    std::ostringstream e;
-    e << args[0] << " given unknown argument \"" << unknownArgs[0] << "\".";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\"."));
     return false;
   }
 
@@ -1398,103 +1341,96 @@
   // Make sure there is a destination.
   if (ica.GetDestination().empty()) {
     // A destination is required.
-    std::ostringstream e;
-    e << args[0] << " given no DESTINATION!";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " given no DESTINATION!"));
     return false;
   }
 
   // Check the file name.
   std::string fname = filename;
   if (fname.find_first_of(":/\\") != std::string::npos) {
-    std::ostringstream e;
-    e << args[0] << " given invalid export file name \"" << fname << "\".  "
-      << "The FILE argument may not contain a path.  "
-      << "Specify the path in the DESTINATION argument.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat(args[0], " given invalid export file name \"",
+                             fname,
+                             "\".  "
+                             "The FILE argument may not contain a path.  "
+                             "Specify the path in the DESTINATION argument."));
     return false;
   }
 
   // Check the file extension.
   if (!fname.empty() &&
       cmSystemTools::GetFilenameLastExtension(fname) != ".cmake") {
-    std::ostringstream e;
-    e << args[0] << " given invalid export file name \"" << fname << "\".  "
-      << "The FILE argument must specify a name ending in \".cmake\".";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " given invalid export file name \"", fname,
+               "\".  "
+               "The FILE argument must specify a name ending in \".cmake\"."));
     return false;
   }
 
   // Construct the file name.
   if (fname.empty()) {
-    fname = exp;
-    fname += ".cmake";
+    fname = cmStrCat(exp, ".cmake");
 
     if (fname.find_first_of(":/\\") != std::string::npos) {
-      std::ostringstream e;
-      e << args[0] << " given export name \"" << exp << "\".  "
-        << "This name cannot be safely converted to a file name.  "
-        << "Specify a different export name or use the FILE option to set "
-        << "a file name explicitly.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat(
+        args[0], " given export name \"", exp,
+        "\".  "
+        "This name cannot be safely converted to a file name.  "
+        "Specify a different export name or use the FILE option to set "
+        "a file name explicitly."));
       return false;
     }
   }
 
-  cmExportSet* exportSet =
-    this->Makefile->GetGlobalGenerator()->GetExportSets()[exp];
+  cmExportSet& exportSet =
+    helper.Makefile->GetGlobalGenerator()->GetExportSets()[exp];
   if (exportOld) {
-    for (cmTargetExport* te : *exportSet->GetTargetExports()) {
+    for (auto const& te : exportSet.GetTargetExports()) {
       cmTarget* tgt =
-        this->Makefile->GetGlobalGenerator()->FindTarget(te->TargetName);
+        helper.Makefile->GetGlobalGenerator()->FindTarget(te->TargetName);
       const bool newCMP0022Behavior =
         (tgt && tgt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
          tgt->GetPolicyStatusCMP0022() != cmPolicies::OLD);
 
       if (!newCMP0022Behavior) {
-        std::ostringstream e;
-        e << "INSTALL(EXPORT) given keyword \""
-          << "EXPORT_LINK_INTERFACE_LIBRARIES"
-          << "\", but target \"" << te->TargetName
-          << "\" does not have policy CMP0022 set to NEW.";
-        this->SetError(e.str());
+        status.SetError(cmStrCat(
+          "INSTALL(EXPORT) given keyword \""
+          "EXPORT_LINK_INTERFACE_LIBRARIES\", but target \"",
+          te->TargetName, "\" does not have policy CMP0022 set to NEW."));
         return false;
       }
     }
   }
 
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(this->Makefile);
+    cmInstallGenerator::SelectMessageLevel(helper.Makefile);
 
   // Create the export install generator.
   cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator(
-    exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
+    &exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
     ica.GetConfigurations(), ica.GetComponent().c_str(), message,
     ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld,
     false);
-  this->Makefile->AddInstallGenerator(exportGenerator);
+  helper.Makefile->AddInstallGenerator(exportGenerator);
 
   return true;
 }
 
-bool cmInstallCommand::MakeFilesFullPath(
-  const char* modeName, const std::vector<std::string>& relFiles,
-  std::vector<std::string>& absFiles)
+bool Helper::MakeFilesFullPath(const char* modeName,
+                               const std::vector<std::string>& relFiles,
+                               std::vector<std::string>& absFiles)
 {
   for (std::string const& relFile : relFiles) {
     std::string file = relFile;
     std::string::size_type gpos = cmGeneratorExpression::Find(file);
     if (gpos != 0 && !cmSystemTools::FileIsFullPath(file)) {
-      file = this->Makefile->GetCurrentSourceDirectory();
-      file += "/";
-      file += relFile;
+      file =
+        cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', relFile);
     }
 
     // Make sure the file is not a directory.
     if (gpos == std::string::npos && cmSystemTools::FileIsDirectory(file)) {
-      std::ostringstream e;
-      e << modeName << " given directory \"" << relFile << "\" to install.";
-      this->SetError(e.str());
+      this->SetError(
+        cmStrCat(modeName, " given directory \"", relFile, "\" to install."));
       return false;
     }
     // Store the file for installation.
@@ -1503,7 +1439,7 @@
   return true;
 }
 
-bool cmInstallCommand::CheckCMP0006(bool& failure)
+bool Helper::CheckCMP0006(bool& failure)
 {
   switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0006)) {
     case cmPolicies::WARN:
@@ -1528,9 +1464,9 @@
   return false;
 }
 
-std::string cmInstallCommand::GetDestination(
-  const cmInstallCommandArguments* args, const std::string& varName,
-  const std::string& guess)
+std::string Helper::GetDestination(const cmInstallCommandArguments* args,
+                                   const std::string& varName,
+                                   const std::string& guess)
 {
   if (args && !args->GetDestination().empty()) {
     return args->GetDestination();
@@ -1542,55 +1478,54 @@
   return guess;
 }
 
-std::string cmInstallCommand::GetRuntimeDestination(
+std::string Helper::GetRuntimeDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_BINDIR", "bin");
 }
 
-std::string cmInstallCommand::GetSbinDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetSbinDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_SBINDIR", "sbin");
 }
 
-std::string cmInstallCommand::GetArchiveDestination(
+std::string Helper::GetArchiveDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib");
 }
 
-std::string cmInstallCommand::GetLibraryDestination(
+std::string Helper::GetLibraryDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib");
 }
 
-std::string cmInstallCommand::GetIncludeDestination(
+std::string Helper::GetIncludeDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_INCLUDEDIR", "include");
 }
 
-std::string cmInstallCommand::GetSysconfDestination(
+std::string Helper::GetSysconfDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_SYSCONFDIR", "etc");
 }
 
-std::string cmInstallCommand::GetSharedStateDestination(
+std::string Helper::GetSharedStateDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_SHAREDSTATEDIR", "com");
 }
 
-std::string cmInstallCommand::GetLocalStateDestination(
+std::string Helper::GetLocalStateDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_LOCALSTATEDIR", "var");
 }
 
-std::string cmInstallCommand::GetRunStateDestination(
+std::string Helper::GetRunStateDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_RUNSTATEDIR",
@@ -1598,49 +1533,44 @@
                                 "/run");
 }
 
-std::string cmInstallCommand::GetDataRootDestination(
+std::string Helper::GetDataRootDestination(
   const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_DATAROOTDIR", "share");
 }
 
-std::string cmInstallCommand::GetDataDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetDataDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_DATADIR",
                               this->GetDataRootDestination(nullptr));
 }
 
-std::string cmInstallCommand::GetInfoDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetInfoDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_INFODIR",
                               this->GetDataRootDestination(nullptr) + "/info");
 }
 
-std::string cmInstallCommand::GetLocaleDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetLocaleDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_LOCALEDIR",
                               this->GetDataRootDestination(nullptr) +
                                 "/locale");
 }
 
-std::string cmInstallCommand::GetManDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetManDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_MANDIR",
                               this->GetDataRootDestination(nullptr) + "/man");
 }
 
-std::string cmInstallCommand::GetDocDestination(
-  const cmInstallCommandArguments* args)
+std::string Helper::GetDocDestination(const cmInstallCommandArguments* args)
 {
   return this->GetDestination(args, "CMAKE_INSTALL_DOCDIR",
                               this->GetDataRootDestination(nullptr) + "/doc");
 }
 
-std::string cmInstallCommand::GetDestinationForType(
+std::string Helper::GetDestinationForType(
   const cmInstallCommandArguments* args, const std::string& type)
 {
   if (args && !args->GetDestination().empty()) {
@@ -1687,3 +1617,31 @@
   }
   return "";
 }
+
+} // namespace
+
+bool cmInstallCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
+{
+  // Allow calling with no arguments so that arguments may be built up
+  // using a variable that may be left empty.
+  if (args.empty()) {
+    return true;
+  }
+
+  // Enable the install target.
+  status.GetMakefile().GetGlobalGenerator()->EnableInstallTarget();
+
+  static cmSubcommandTable const subcommand{
+    { "SCRIPT"_s, HandleScriptMode },
+    { "CODE"_s, HandleScriptMode },
+    { "TARGETS"_s, HandleTargetsMode },
+    { "FILES"_s, HandleFilesMode },
+    { "PROGRAMS"_s, HandleFilesMode },
+    { "DIRECTORY"_s, HandleDirectoryMode },
+    { "EXPORT"_s, HandleExportMode },
+    { "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode },
+  };
+
+  return subcommand(args[0], args, status);
+}
diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h
index 202c438..32f00ce 100644
--- a/Source/cmInstallCommand.h
+++ b/Source/cmInstallCommand.h
@@ -8,66 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmInstallCommandArguments;
 
-/** \class cmInstallCommand
- * \brief Specifies where to install some files
- *
- * cmInstallCommand is a general-purpose interface command for
- * specifying install rules.
- */
-class cmInstallCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmInstallCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool HandleScriptMode(std::vector<std::string> const& args);
-  bool HandleTargetsMode(std::vector<std::string> const& args);
-  bool HandleFilesMode(std::vector<std::string> const& args);
-  bool HandleDirectoryMode(std::vector<std::string> const& args);
-  bool HandleExportMode(std::vector<std::string> const& args);
-  bool HandleExportAndroidMKMode(std::vector<std::string> const& args);
-  bool MakeFilesFullPath(const char* modeName,
-                         const std::vector<std::string>& relFiles,
-                         std::vector<std::string>& absFiles);
-  bool CheckCMP0006(bool& failure);
-
-  std::string GetDestination(const cmInstallCommandArguments* args,
-                             const std::string& varName,
-                             const std::string& guess);
-  std::string GetRuntimeDestination(const cmInstallCommandArguments* args);
-  std::string GetSbinDestination(const cmInstallCommandArguments* args);
-  std::string GetArchiveDestination(const cmInstallCommandArguments* args);
-  std::string GetLibraryDestination(const cmInstallCommandArguments* args);
-  std::string GetIncludeDestination(const cmInstallCommandArguments* args);
-  std::string GetSysconfDestination(const cmInstallCommandArguments* args);
-  std::string GetSharedStateDestination(const cmInstallCommandArguments* args);
-  std::string GetLocalStateDestination(const cmInstallCommandArguments* args);
-  std::string GetRunStateDestination(const cmInstallCommandArguments* args);
-  std::string GetDataRootDestination(const cmInstallCommandArguments* args);
-  std::string GetDataDestination(const cmInstallCommandArguments* args);
-  std::string GetInfoDestination(const cmInstallCommandArguments* args);
-  std::string GetLocaleDestination(const cmInstallCommandArguments* args);
-  std::string GetManDestination(const cmInstallCommandArguments* args);
-  std::string GetDocDestination(const cmInstallCommandArguments* args);
-  std::string GetDestinationForType(const cmInstallCommandArguments* args,
-                                    const std::string& type);
-
-  std::string DefaultComponentName;
-};
+bool cmInstallCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx
index 8b33782..31ba63f 100644
--- a/Source/cmInstallCommandArguments.cxx
+++ b/Source/cmInstallCommandArguments.cxx
@@ -2,11 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallCommandArguments.h"
 
-#include "cmRange.h"
-#include "cmSystemTools.h"
+#include <utility>
+
 #include "cm_static_string_view.hxx"
 
-#include <utility>
+#include "cmRange.h"
+#include "cmSystemTools.h"
 
 // Table of valid permissions.
 const char* cmInstallCommandArguments::PermissionsTable[] = {
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx
index 14288f6..259c7f7 100644
--- a/Source/cmInstallDirectoryGenerator.cxx
+++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -6,10 +6,9 @@
 #include "cmInstallType.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <memory> // IWYU pragma: keep
-
 cmInstallDirectoryGenerator::cmInstallDirectoryGenerator(
   std::vector<std::string> const& dirs, const char* dest,
   const char* file_permissions, const char* dir_permissions,
@@ -63,18 +62,16 @@
   std::ostream& os, const std::string& config, Indent indent)
 {
   std::vector<std::string> dirs;
-  cmGeneratorExpression ge;
   for (std::string const& d : this->Directories) {
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(d);
-    cmSystemTools::ExpandListArgument(
-      cge->Evaluate(this->LocalGenerator, config), dirs);
+    cmExpandList(
+      cmGeneratorExpression::Evaluate(d, this->LocalGenerator, config), dirs);
   }
 
   // Make sure all dirs have absolute paths.
   cmMakefile const& mf = *this->LocalGenerator->GetMakefile();
   for (std::string& d : dirs) {
     if (!cmSystemTools::FileIsFullPath(d)) {
-      d = mf.GetCurrentSourceDirectory() + "/" + d;
+      d = cmStrCat(mf.GetCurrentSourceDirectory(), "/", d);
     }
   }
 
@@ -97,6 +94,6 @@
 std::string cmInstallDirectoryGenerator::GetDestination(
   std::string const& config) const
 {
-  cmGeneratorExpression ge;
-  return ge.Parse(this->Destination)->Evaluate(this->LocalGenerator, config);
+  return cmGeneratorExpression::Evaluate(this->Destination,
+                                         this->LocalGenerator, config);
 }
diff --git a/Source/cmInstallDirectoryGenerator.h b/Source/cmInstallDirectoryGenerator.h
index e30849f..84c0694 100644
--- a/Source/cmInstallDirectoryGenerator.h
+++ b/Source/cmInstallDirectoryGenerator.h
@@ -3,15 +3,15 @@
 #ifndef cmInstallDirectoryGenerator_h
 #define cmInstallDirectoryGenerator_h
 
-#include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
-
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
+
 class cmLocalGenerator;
 
 /** \class cmInstallDirectoryGenerator
diff --git a/Source/cmInstallExportAndroidMKGenerator.cxx b/Source/cmInstallExportAndroidMKGenerator.cxx
deleted file mode 100644
index 55d3685..0000000
--- a/Source/cmInstallExportAndroidMKGenerator.cxx
+++ /dev/null
@@ -1,137 +0,0 @@
-
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmInstallExportAndroidMKGenerator.h"
-
-#include <stdio.h>
-
-#include "cmExportInstallFileGenerator.h"
-#include "cmExportSet.h"
-#include "cmGeneratedFileStream.h"
-#include "cmGlobalGenerator.h"
-#include "cmInstallFilesGenerator.h"
-#include "cmInstallTargetGenerator.h"
-#include "cmLocalGenerator.h"
-#include "cmMakefile.h"
-#include "cmMessageType.h"
-
-cmInstallExportAndroidMKGenerator::cmInstallExportAndroidMKGenerator(
-  cmExportSet* exportSet, const char* destination,
-  const char* file_permissions, std::vector<std::string> const& configurations,
-  const char* component, MessageLevel message, bool exclude_from_all,
-  const char* filename, const char* name_space, bool exportOld)
-  : cmInstallExportGenerator(exportSet, destination, file_permissions,
-                             configurations, component, message,
-                             exclude_from_all, filename, name_space, exportOld)
-{
-}
-
-cmInstallExportAndroidMKGenerator::~cmInstallExportAndroidMKGenerator()
-{
-}
-
-bool cmInstallExportAndroidMKGenerator::Compute(cmLocalGenerator* lg)
-{
-  this->LocalGenerator = lg;
-  this->ExportSet->Compute(lg);
-  return true;
-}
-
-void cmInstallExportAndroidMKGenerator::GenerateScript(std::ostream& os)
-{
-  // Skip empty sets.
-  if (ExportSet->GetTargetExports()->empty()) {
-    std::ostringstream e;
-    e << "INSTALL(EXPORT) given unknown export \"" << ExportSet->GetName()
-      << "\"";
-    cmSystemTools::Error(e.str());
-    return;
-  }
-
-  // Create the temporary directory in which to store the files.
-  this->ComputeTempDir();
-  cmSystemTools::MakeDirectory(this->TempDir.c_str());
-
-  // Construct a temporary location for the file.
-  this->MainImportFile = this->TempDir;
-  this->MainImportFile += "/";
-  this->MainImportFile += this->FileName;
-
-  // Generate the import file for this export set.
-  this->EFGen->SetExportFile(this->MainImportFile.c_str());
-  this->EFGen->SetNamespace(this->Namespace);
-  this->EFGen->SetExportOld(this->ExportOld);
-  if (this->ConfigurationTypes->empty()) {
-    if (!this->ConfigurationName.empty()) {
-      this->EFGen->AddConfiguration(this->ConfigurationName);
-    } else {
-      this->EFGen->AddConfiguration("");
-    }
-  } else {
-    for (std::string const& config : this->ConfigurationTypes) {
-      this->EFGen->AddConfiguration(config);
-    }
-  }
-  this->EFGen->GenerateImportFile();
-
-  // Perform the main install script generation.
-  this->cmInstallGenerator::GenerateScript(os);
-}
-
-void cmInstallExportAndroidMKGenerator::GenerateScriptConfigs(
-  std::ostream& os, Indent const& indent)
-{
-  // Create the main install rules first.
-  this->cmInstallGenerator::GenerateScriptConfigs(os, indent);
-
-  // Now create a configuration-specific install rule for the import
-  // file of each configuration.
-  std::vector<std::string> files;
-  for (auto const& pair : this->EFGen->GetConfigImportFiles()) {
-    files.push_back(pair.second);
-    std::string config_test = this->CreateConfigTest(pair.first);
-    os << indent << "if(" << config_test << ")\n";
-    this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
-                         false, this->FilePermissions.c_str(), nullptr,
-                         nullptr, nullptr, indent.Next());
-    os << indent << "endif()\n";
-    files.clear();
-  }
-}
-
-void cmInstallExportAndroidMKGenerator::GenerateScriptActions(
-  std::ostream& os, Indent const& indent)
-{
-  // Remove old per-configuration export files if the main changes.
-  std::string installedDir = "$ENV{DESTDIR}";
-  installedDir += this->ConvertToAbsoluteDestination(this->Destination);
-  installedDir += "/";
-  std::string installedFile = installedDir;
-  installedFile += this->FileName;
-  os << indent << "if(EXISTS \"" << installedFile << "\")\n";
-  Indent indentN = indent.Next();
-  Indent indentNN = indentN.Next();
-  Indent indentNNN = indentNN.Next();
-  /* clang-format off */
-  os << indentN << "file(DIFFERENT EXPORT_FILE_CHANGED FILES\n"
-     << indentN << "     \"" << installedFile << "\"\n"
-     << indentN << "     \"" << this->MainImportFile << "\")\n";
-  os << indentN << "if(EXPORT_FILE_CHANGED)\n";
-  os << indentNN << "file(GLOB OLD_CONFIG_FILES \"" << installedDir
-     << this->EFGen->GetConfigImportFileGlob() << "\")\n";
-  os << indentNN << "if(OLD_CONFIG_FILES)\n";
-  os << indentNNN << "message(STATUS \"Old export file \\\"" << installedFile
-     << "\\\" will be replaced.  Removing files [${OLD_CONFIG_FILES}].\")\n";
-  os << indentNNN << "file(REMOVE ${OLD_CONFIG_FILES})\n";
-  os << indentNN << "endif()\n";
-  os << indentN << "endif()\n";
-  os << indent << "endif()\n";
-  /* clang-format on */
-
-  // Install the main export file.
-  std::vector<std::string> files;
-  files.push_back(this->MainImportFile);
-  this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
-                       false, this->FilePermissions.c_str(), nullptr, nullptr,
-                       nullptr, indent);
-}
diff --git a/Source/cmInstallExportAndroidMKGenerator.h b/Source/cmInstallExportAndroidMKGenerator.h
deleted file mode 100644
index a92ff27..0000000
--- a/Source/cmInstallExportAndroidMKGenerator.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmInstallExportAndroidMKGenerator_h
-#define cmInstallExportAndroidMKGenerator_h
-
-#include "cmInstallExportGenerator.h"
-
-class cmExportInstallFileGenerator;
-class cmInstallFilesGenerator;
-class cmInstallTargetGenerator;
-class cmExportSet;
-class cmMakefile;
-
-/** \class cmInstallExportAndroidMKGenerator
- * \brief Generate rules for creating an export files.
- */
-class cmInstallExportAndroidMKGenerator : public cmInstallExportGenerator
-{
-public:
-  cmInstallExportAndroidMKGenerator(
-    cmExportSet* exportSet, const char* dest, const char* file_permissions,
-    const std::vector<std::string>& configurations, const char* component,
-    MessageLevel message, bool exclude_from_all, const char* filename,
-    const char* name_space, bool exportOld);
-  ~cmInstallExportAndroidMKGenerator();
-
-  bool Compute(cmLocalGenerator* lg) override;
-
-protected:
-  virtual void GenerateScript(std::ostream& os);
-  virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent);
-  virtual void GenerateScriptActions(std::ostream& os, Indent const& indent);
-  void GenerateImportFile(cmExportSet const* exportSet);
-  void GenerateImportFile(const char* config, cmExportSet const* exportSet);
-};
-
-#endif
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index f5bedab..cba68be 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -7,13 +7,14 @@
 #include <sstream>
 #include <utility>
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 #  include "cmExportInstallAndroidMKGenerator.h"
 #endif
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportSet.h"
 #include "cmInstallType.h"
 #include "cmLocalGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmInstallExportGenerator::cmInstallExportGenerator(
@@ -31,7 +32,7 @@
   , LocalGenerator(nullptr)
 {
   if (android) {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
     this->EFGen = new cmExportInstallAndroidMKGenerator(this);
 #endif
   } else {
@@ -56,9 +57,8 @@
 {
   // Choose a temporary directory in which to generate the import
   // files to be installed.
-  this->TempDir = this->LocalGenerator->GetCurrentBinaryDirectory();
-  this->TempDir += "/CMakeFiles";
-  this->TempDir += "/Export";
+  this->TempDir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
+                           "/CMakeFiles/Export");
   if (this->Destination.empty()) {
     return;
   }
@@ -123,7 +123,7 @@
 void cmInstallExportGenerator::GenerateScript(std::ostream& os)
 {
   // Skip empty sets.
-  if (ExportSet->GetTargetExports()->empty()) {
+  if (ExportSet->GetTargetExports().empty()) {
     std::ostringstream e;
     e << "INSTALL(EXPORT) given unknown export \"" << ExportSet->GetName()
       << "\"";
@@ -136,9 +136,7 @@
   cmSystemTools::MakeDirectory(this->TempDir);
 
   // Construct a temporary location for the file.
-  this->MainImportFile = this->TempDir;
-  this->MainImportFile += "/";
-  this->MainImportFile += this->FileName;
+  this->MainImportFile = cmStrCat(this->TempDir, '/', this->FileName);
 
   // Generate the import file for this export set.
   this->EFGen->SetExportFile(this->MainImportFile.c_str());
@@ -186,11 +184,10 @@
                                                      Indent indent)
 {
   // Remove old per-configuration export files if the main changes.
-  std::string installedDir = "$ENV{DESTDIR}";
-  installedDir += this->ConvertToAbsoluteDestination(this->Destination);
-  installedDir += "/";
-  std::string installedFile = installedDir;
-  installedFile += this->FileName;
+  std::string installedDir =
+    cmStrCat("$ENV{DESTDIR}",
+             this->ConvertToAbsoluteDestination(this->Destination), '/');
+  std::string installedFile = cmStrCat(installedDir, this->FileName);
   os << indent << "if(EXISTS \"" << installedFile << "\")\n";
   Indent indentN = indent.Next();
   Indent indentNN = indentN.Next();
@@ -218,3 +215,8 @@
                        false, this->FilePermissions.c_str(), nullptr, nullptr,
                        nullptr, indent);
 }
+
+std::string cmInstallExportGenerator::GetDestinationFile() const
+{
+  return this->Destination + '/' + this->FileName;
+}
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h
index c4d252c..f44127e 100644
--- a/Source/cmInstallExportGenerator.h
+++ b/Source/cmInstallExportGenerator.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
-
+#include <cstddef>
 #include <iosfwd>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
+
 class cmExportInstallFileGenerator;
 class cmExportSet;
 class cmLocalGenerator;
@@ -41,6 +41,7 @@
   const std::string& GetNamespace() const { return this->Namespace; }
 
   std::string const& GetDestination() const { return this->Destination; }
+  std::string GetDestinationFile() const;
 
 protected:
   void GenerateScript(std::ostream& os) override;
diff --git a/Source/cmInstallFilesCommand.cxx b/Source/cmInstallFilesCommand.cxx
index efbcb67..d623943 100644
--- a/Source/cmInstallFilesCommand.cxx
+++ b/Source/cmInstallFilesCommand.cxx
@@ -2,66 +2,72 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallFilesCommand.h"
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+static std::string FindInstallSource(cmMakefile& makefile, const char* name);
+static void CreateInstallGenerator(cmMakefile& makefile,
+                                   std::string const& dest,
+                                   std::vector<std::string> const& files);
+static void FinalAction(cmMakefile& makefile, std::string const& dest,
+                        std::vector<std::string> const& args);
 
-// cmExecutableCommand
-bool cmInstallFilesCommand::InitialPass(std::vector<std::string> const& args,
-                                        cmExecutionStatus&)
+bool cmInstallFilesCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  // Enable the install target.
-  this->Makefile->GetGlobalGenerator()->EnableInstallTarget();
+  cmMakefile& mf = status.GetMakefile();
 
-  this->Destination = args[0];
+  // Enable the install target.
+  mf.GetGlobalGenerator()->EnableInstallTarget();
+
+  std::string const& dest = args[0];
 
   if ((args.size() > 1) && (args[1] == "FILES")) {
-    this->IsFilesForm = true;
+    std::vector<std::string> files;
     for (std::string const& arg : cmMakeRange(args).advance(2)) {
       // Find the source location for each file listed.
-      this->Files.push_back(this->FindInstallSource(arg.c_str()));
+      files.push_back(FindInstallSource(mf, arg.c_str()));
     }
-    this->CreateInstallGenerator();
+    CreateInstallGenerator(mf, dest, files);
   } else {
-    this->IsFilesForm = false;
-    cmAppend(this->FinalArgs, args.begin() + 1, args.end());
+    std::vector<std::string> finalArgs(args.begin() + 1, args.end());
+    mf.AddFinalAction([dest, finalArgs](cmMakefile& makefile) {
+      FinalAction(makefile, dest, finalArgs);
+    });
   }
 
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
+  mf.GetGlobalGenerator()->AddInstallComponent(
+    mf.GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
 
   return true;
 }
 
-void cmInstallFilesCommand::FinalPass()
+static void FinalAction(cmMakefile& makefile, std::string const& dest,
+                        std::vector<std::string> const& args)
 {
-  // No final pass for "FILES" form of arguments.
-  if (this->IsFilesForm) {
-    return;
-  }
-
   std::string testf;
-  std::string const& ext = this->FinalArgs[0];
+  std::string const& ext = args[0];
+  std::vector<std::string> installFiles;
 
   // two different options
-  if (this->FinalArgs.size() > 1) {
+  if (args.size() > 1) {
     // now put the files into the list
-    std::vector<std::string>::iterator s = this->FinalArgs.begin();
+    auto s = args.begin();
     ++s;
     // for each argument, get the files
-    for (; s != this->FinalArgs.end(); ++s) {
+    for (; s != args.end(); ++s) {
       // replace any variables
       std::string const& temps = *s;
       if (!cmSystemTools::GetFilenamePath(temps).empty()) {
@@ -72,30 +78,31 @@
       }
 
       // add to the result
-      this->Files.push_back(this->FindInstallSource(testf.c_str()));
+      installFiles.push_back(FindInstallSource(makefile, testf.c_str()));
     }
   } else // reg exp list
   {
     std::vector<std::string> files;
-    std::string const& regex = this->FinalArgs[0];
-    cmSystemTools::Glob(this->Makefile->GetCurrentSourceDirectory(), regex,
-                        files);
+    std::string const& regex = args[0];
+    cmSystemTools::Glob(makefile.GetCurrentSourceDirectory(), regex, files);
 
-    std::vector<std::string>::iterator s = files.begin();
+    auto s = files.begin();
     // for each argument, get the files
     for (; s != files.end(); ++s) {
-      this->Files.push_back(this->FindInstallSource(s->c_str()));
+      installFiles.push_back(FindInstallSource(makefile, s->c_str()));
     }
   }
 
-  this->CreateInstallGenerator();
+  CreateInstallGenerator(makefile, dest, installFiles);
 }
 
-void cmInstallFilesCommand::CreateInstallGenerator() const
+static void CreateInstallGenerator(cmMakefile& makefile,
+                                   std::string const& dest,
+                                   std::vector<std::string> const& files)
 {
   // Construct the destination.  This command always installs under
   // the prefix.  We skip the leading slash given by the user.
-  std::string destination = this->Destination.substr(1);
+  std::string destination = dest.substr(1);
   cmSystemTools::ConvertToUnixSlashes(destination);
   if (destination.empty()) {
     destination = ".";
@@ -106,12 +113,12 @@
   const char* no_rename = "";
   bool no_exclude_from_all = false;
   std::string no_component =
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
+    makefile.GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
   std::vector<std::string> no_configurations;
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(this->Makefile);
-  this->Makefile->AddInstallGenerator(new cmInstallFilesGenerator(
-    this->Files, destination.c_str(), false, no_permissions, no_configurations,
+    cmInstallGenerator::SelectMessageLevel(&makefile);
+  makefile.AddInstallGenerator(new cmInstallFilesGenerator(
+    files, destination.c_str(), false, no_permissions, no_configurations,
     no_component.c_str(), message, no_exclude_from_all, no_rename));
 }
 
@@ -121,7 +128,7 @@
  * present in the build tree.  If a full path is given, it is just
  * returned.
  */
-std::string cmInstallFilesCommand::FindInstallSource(const char* name) const
+static std::string FindInstallSource(cmMakefile& makefile, const char* name)
 {
   if (cmSystemTools::FileIsFullPath(name) ||
       cmGeneratorExpression::Find(name) == 0) {
@@ -130,12 +137,8 @@
   }
 
   // This is a relative path.
-  std::string tb = this->Makefile->GetCurrentBinaryDirectory();
-  tb += "/";
-  tb += name;
-  std::string ts = this->Makefile->GetCurrentSourceDirectory();
-  ts += "/";
-  ts += name;
+  std::string tb = cmStrCat(makefile.GetCurrentBinaryDirectory(), '/', name);
+  std::string ts = cmStrCat(makefile.GetCurrentSourceDirectory(), '/', name);
 
   if (cmSystemTools::FileExists(tb)) {
     // The file exists in the binary tree.  Use it.
diff --git a/Source/cmInstallFilesCommand.h b/Source/cmInstallFilesCommand.h
index a52f45e..f4ebbde 100644
--- a/Source/cmInstallFilesCommand.h
+++ b/Source/cmInstallFilesCommand.h
@@ -8,49 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmInstallFilesCommand
- * \brief Specifies where to install some files
- *
- * cmInstallFilesCommand specifies the relative path where a list of
- * files should be installed.
- */
-class cmInstallFilesCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmInstallFilesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   * This is called at the end after all the information
-   * specified by the command is accumulated. Most commands do
-   * not implement this method.  At this point, reading and
-   * writing to the cache can be done.
-   */
-  void FinalPass() override;
-  bool HasFinalPass() const override { return !this->IsFilesForm; }
-
-protected:
-  void CreateInstallGenerator() const;
-  std::string FindInstallSource(const char* name) const;
-
-private:
-  std::vector<std::string> FinalArgs;
-  bool IsFilesForm = false;
-  std::string Destination;
-  std::vector<std::string> Files;
-};
+bool cmInstallFilesCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx
index 2ed9f73..f5b69a5 100644
--- a/Source/cmInstallFilesGenerator.cxx
+++ b/Source/cmInstallFilesGenerator.cxx
@@ -4,9 +4,7 @@
 
 #include "cmGeneratorExpression.h"
 #include "cmInstallType.h"
-#include "cmSystemTools.h"
-
-#include <memory> // IWYU pragma: keep
+#include "cmStringAlgorithms.h"
 
 class cmLocalGenerator;
 
@@ -51,8 +49,8 @@
 std::string cmInstallFilesGenerator::GetDestination(
   std::string const& config) const
 {
-  cmGeneratorExpression ge;
-  return ge.Parse(this->Destination)->Evaluate(this->LocalGenerator, config);
+  return cmGeneratorExpression::Evaluate(this->Destination,
+                                         this->LocalGenerator, config);
 }
 
 void cmInstallFilesGenerator::AddFilesInstallRule(
@@ -82,11 +80,9 @@
   std::ostream& os, const std::string& config, Indent indent)
 {
   std::vector<std::string> files;
-  cmGeneratorExpression ge;
   for (std::string const& f : this->Files) {
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(f);
-    cmSystemTools::ExpandListArgument(
-      cge->Evaluate(this->LocalGenerator, config), files);
+    cmExpandList(
+      cmGeneratorExpression::Evaluate(f, this->LocalGenerator, config), files);
   }
   this->AddFilesInstallRule(os, config, indent, files);
 }
diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h
index ac462d4..a680037 100644
--- a/Source/cmInstallFilesGenerator.h
+++ b/Source/cmInstallFilesGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
+
 class cmLocalGenerator;
 
 /** \class cmInstallFilesGenerator
diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx
index 2ffca30..ec17361 100644
--- a/Source/cmInstallGenerator.cxx
+++ b/Source/cmInstallGenerator.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallGenerator.h"
 
+#include <ostream>
+
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
-#include <ostream>
-
 cmInstallGenerator::cmInstallGenerator(
   const char* destination, std::vector<std::string> const& configurations,
   const char* component, MessageLevel message, bool exclude_from_all)
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h
index dbe707d..024027d 100644
--- a/Source/cmInstallGenerator.h
+++ b/Source/cmInstallGenerator.h
@@ -5,13 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallType.h"
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmInstallType.h"
+#include "cmScriptGenerator.h"
+
 class cmLocalGenerator;
 class cmMakefile;
 
diff --git a/Source/cmInstallProgramsCommand.cxx b/Source/cmInstallProgramsCommand.cxx
index a58f875..6bb4409 100644
--- a/Source/cmInstallProgramsCommand.cxx
+++ b/Source/cmInstallProgramsCommand.cxx
@@ -2,73 +2,81 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallProgramsCommand.h"
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+static void FinalAction(cmMakefile& makefile, std::string const& dest,
+                        std::vector<std::string> const& args);
+static std::string FindInstallSource(cmMakefile& makefile, const char* name);
 
-// cmExecutableCommand
-bool cmInstallProgramsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmInstallProgramsCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Enable the install target.
-  this->Makefile->GetGlobalGenerator()->EnableInstallTarget();
+  mf.GetGlobalGenerator()->EnableInstallTarget();
 
-  this->Destination = args[0];
+  mf.GetGlobalGenerator()->AddInstallComponent(
+    mf.GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
 
-  cmAppend(this->FinalArgs, args.begin() + 1, args.end());
-
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
-
+  std::string const& dest = args[0];
+  std::vector<std::string> const finalArgs(args.begin() + 1, args.end());
+  mf.AddFinalAction([dest, finalArgs](cmMakefile& makefile) {
+    FinalAction(makefile, dest, finalArgs);
+  });
   return true;
 }
 
-void cmInstallProgramsCommand::FinalPass()
+static void FinalAction(cmMakefile& makefile, std::string const& dest,
+                        std::vector<std::string> const& args)
 {
   bool files_mode = false;
-  if (!this->FinalArgs.empty() && this->FinalArgs[0] == "FILES") {
+  if (!args.empty() && args[0] == "FILES") {
     files_mode = true;
   }
 
+  std::vector<std::string> files;
+
   // two different options
-  if (this->FinalArgs.size() > 1 || files_mode) {
+  if (args.size() > 1 || files_mode) {
     // for each argument, get the programs
-    std::vector<std::string>::iterator s = this->FinalArgs.begin();
+    auto s = args.begin();
     if (files_mode) {
       // Skip the FILES argument in files mode.
       ++s;
     }
-    for (; s != this->FinalArgs.end(); ++s) {
+    for (; s != args.end(); ++s) {
       // add to the result
-      this->Files.push_back(this->FindInstallSource(s->c_str()));
+      files.push_back(FindInstallSource(makefile, s->c_str()));
     }
   } else // reg exp list
   {
     std::vector<std::string> programs;
-    cmSystemTools::Glob(this->Makefile->GetCurrentSourceDirectory(),
-                        this->FinalArgs[0], programs);
+    cmSystemTools::Glob(makefile.GetCurrentSourceDirectory(), args[0],
+                        programs);
 
-    std::vector<std::string>::iterator s = programs.begin();
+    auto s = programs.begin();
     // for each argument, get the programs
     for (; s != programs.end(); ++s) {
-      this->Files.push_back(this->FindInstallSource(s->c_str()));
+      files.push_back(FindInstallSource(makefile, s->c_str()));
     }
   }
 
   // Construct the destination.  This command always installs under
   // the prefix.  We skip the leading slash given by the user.
-  std::string destination = this->Destination.substr(1);
+  std::string destination = dest.substr(1);
   cmSystemTools::ConvertToUnixSlashes(destination);
   if (destination.empty()) {
     destination = ".";
@@ -79,12 +87,12 @@
   const char* no_rename = "";
   bool no_exclude_from_all = false;
   std::string no_component =
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
+    makefile.GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME");
   std::vector<std::string> no_configurations;
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(this->Makefile);
-  this->Makefile->AddInstallGenerator(new cmInstallFilesGenerator(
-    this->Files, destination.c_str(), true, no_permissions, no_configurations,
+    cmInstallGenerator::SelectMessageLevel(&makefile);
+  makefile.AddInstallGenerator(new cmInstallFilesGenerator(
+    files, destination.c_str(), true, no_permissions, no_configurations,
     no_component.c_str(), message, no_exclude_from_all, no_rename));
 }
 
@@ -94,7 +102,7 @@
  * present in the build tree.  If a full path is given, it is just
  * returned.
  */
-std::string cmInstallProgramsCommand::FindInstallSource(const char* name) const
+static std::string FindInstallSource(cmMakefile& makefile, const char* name)
 {
   if (cmSystemTools::FileIsFullPath(name) ||
       cmGeneratorExpression::Find(name) == 0) {
@@ -103,12 +111,8 @@
   }
 
   // This is a relative path.
-  std::string tb = this->Makefile->GetCurrentBinaryDirectory();
-  tb += "/";
-  tb += name;
-  std::string ts = this->Makefile->GetCurrentSourceDirectory();
-  ts += "/";
-  ts += name;
+  std::string tb = cmStrCat(makefile.GetCurrentBinaryDirectory(), '/', name);
+  std::string ts = cmStrCat(makefile.GetCurrentSourceDirectory(), '/', name);
 
   if (cmSystemTools::FileExists(tb)) {
     // The file exists in the binary tree.  Use it.
diff --git a/Source/cmInstallProgramsCommand.h b/Source/cmInstallProgramsCommand.h
index 5c705eb..c567f3b 100644
--- a/Source/cmInstallProgramsCommand.h
+++ b/Source/cmInstallProgramsCommand.h
@@ -8,48 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmInstallProgramsCommand
- * \brief Specifies where to install some programs
- *
- * cmInstallProgramsCommand specifies the relative path where a list of
- * programs should be installed.
- */
-class cmInstallProgramsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmInstallProgramsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   * This is called at the end after all the information
-   * specified by the command is accumulated. Most commands do
-   * not implement this method.  At this point, reading and
-   * writing to the cache can be done.
-   */
-  void FinalPass() override;
-
-  bool HasFinalPass() const override { return true; }
-
-protected:
-  std::string FindInstallSource(const char* name) const;
-
-private:
-  std::vector<std::string> FinalArgs;
-  std::string Destination;
-  std::vector<std::string> Files;
-};
+bool cmInstallProgramsCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx
index 5832d27..ea29455 100644
--- a/Source/cmInstallScriptGenerator.cxx
+++ b/Source/cmInstallScriptGenerator.cxx
@@ -78,11 +78,9 @@
   std::ostream& os, const std::string& config, Indent indent)
 {
   if (this->AllowGenex) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(this->Script);
     this->AddScriptInstallRule(os, indent,
-                               cge->Evaluate(this->LocalGenerator, config));
+                               cmGeneratorExpression::Evaluate(
+                                 this->Script, this->LocalGenerator, config));
   } else {
     this->AddScriptInstallRule(os, indent, this->Script);
   }
diff --git a/Source/cmInstallScriptGenerator.h b/Source/cmInstallScriptGenerator.h
index 6af7371..7efa321 100644
--- a/Source/cmInstallScriptGenerator.h
+++ b/Source/cmInstallScriptGenerator.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
+
 class cmLocalGenerator;
 
 /** \class cmInstallScriptGenerator
diff --git a/Source/cmInstallSubdirectoryGenerator.cxx b/Source/cmInstallSubdirectoryGenerator.cxx
index 1c0512c..8a0fefa 100644
--- a/Source/cmInstallSubdirectoryGenerator.cxx
+++ b/Source/cmInstallSubdirectoryGenerator.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallSubdirectoryGenerator.h"
 
+#include <sstream>
+#include <vector>
+
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmScriptGenerator.h"
 #include "cmSystemTools.h"
 
-#include <sstream>
-#include <vector>
-
 cmInstallSubdirectoryGenerator::cmInstallSubdirectoryGenerator(
   cmMakefile* makefile, const char* binaryDirectory, bool excludeFromAll)
   : cmInstallGenerator(nullptr, std::vector<std::string>(), nullptr,
diff --git a/Source/cmInstallSubdirectoryGenerator.h b/Source/cmInstallSubdirectoryGenerator.h
index 22759d9..b99bdd5 100644
--- a/Source/cmInstallSubdirectoryGenerator.h
+++ b/Source/cmInstallSubdirectoryGenerator.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallGenerator.h"
-
 #include <iosfwd>
 #include <string>
 
+#include "cmInstallGenerator.h"
+
 class cmLocalGenerator;
 class cmMakefile;
 
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 7c5a55b..0cd04cc 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -2,7 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallTargetGenerator.h"
 
-#include <assert.h>
+#include <cassert>
 #include <map>
 #include <set>
 #include <sstream>
@@ -16,7 +16,10 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmOutputConverter.h"
+#include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
@@ -83,20 +86,18 @@
   std::string fromDirConfig;
   if (this->Target->NeedRelinkBeforeInstall(config)) {
     fromDirConfig =
-      this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory();
-    fromDirConfig += "/CMakeFiles";
-    fromDirConfig += "/CMakeRelink.dir/";
+      cmStrCat(this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
+               "/CMakeFiles/CMakeRelink.dir/");
   } else {
     cmStateEnums::ArtifactType artifact = this->ImportLibrary
       ? cmStateEnums::ImportLibraryArtifact
       : cmStateEnums::RuntimeBinaryArtifact;
-    fromDirConfig = this->Target->GetDirectory(config, artifact);
-    fromDirConfig += "/";
+    fromDirConfig =
+      cmStrCat(this->Target->GetDirectory(config, artifact), '/');
   }
 
-  std::string toDir =
-    this->ConvertToAbsoluteDestination(this->GetDestination(config));
-  toDir += "/";
+  std::string toDir = cmStrCat(
+    this->ConvertToAbsoluteDestination(this->GetDestination(config)), '/');
 
   // Compute the list of files to install for this target.
   std::vector<std::string> filesFrom;
@@ -366,16 +367,15 @@
 {
   this->Target->GetTargetObjectNames(config, objects);
   for (std::string& o : objects) {
-    o = computeInstallObjectDir(this->Target, config) + "/" + o;
+    o = cmStrCat(computeInstallObjectDir(this->Target, config), "/", o);
   }
 }
 
 std::string cmInstallTargetGenerator::GetDestination(
   std::string const& config) const
 {
-  cmGeneratorExpression ge;
-  return ge.Parse(this->Destination)
-    ->Evaluate(this->Target->GetLocalGenerator(), config);
+  return cmGeneratorExpression::Evaluate(
+    this->Destination, this->Target->GetLocalGenerator(), config);
 }
 
 std::string cmInstallTargetGenerator::GetInstallFilename(
@@ -590,8 +590,8 @@
     // on the installed file.
     if (for_build != for_install) {
       // Prepare to refer to the install-tree install_name.
-      new_id = for_install;
-      new_id += this->GetInstallFilename(this->Target, config, NameSO);
+      new_id = cmStrCat(
+        for_install, this->GetInstallFilename(this->Target, config, NameSO));
     }
   }
 
@@ -632,17 +632,34 @@
     return;
   }
 
-  // Get the install RPATH from the link information.
-  std::string newRpath = cli->GetChrpathString();
-
   // Write a rule to remove the installed file if its rpath is not the
   // new rpath.  This is needed for existing build/install trees when
   // the installed rpath changes but the file is not rebuilt.
-  /* clang-format off */
   os << indent << "file(RPATH_CHECK\n"
-     << indent << "     FILE \"" << toDestDirPath << "\"\n"
-     << indent << "     RPATH \"" << newRpath << "\")\n";
-  /* clang-format on */
+     << indent << "     FILE \"" << toDestDirPath << "\"\n";
+
+  // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
+  // CMake install script.
+  switch (this->Target->GetPolicyStatusCMP0095()) {
+    case cmPolicies::WARN:
+      // No author warning needed here, we warn later in
+      // cmInstallTargetGenerator::AddChrpathPatchRule().
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD: {
+      // Get the install RPATH from the link information.
+      std::string newRpath = cli->GetChrpathString();
+      os << indent << "     RPATH \"" << newRpath << "\")\n";
+      break;
+    }
+    default: {
+      // Get the install RPATH from the link information and
+      // escape any CMake syntax in the install RPATH.
+      std::string escapedNewRpath =
+        cmOutputConverter::EscapeForCMake(cli->GetChrpathString());
+      os << indent << "     RPATH " << escapedNewRpath << ")\n";
+      break;
+    }
+  }
 }
 
 void cmInstallTargetGenerator::AddChrpathPatchRule(
@@ -668,7 +685,8 @@
     std::string installNameTool =
       mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
 
-    std::vector<std::string> oldRuntimeDirs, newRuntimeDirs;
+    std::vector<std::string> oldRuntimeDirs;
+    std::vector<std::string> newRuntimeDirs;
     cli->GetRPath(oldRuntimeDirs, false);
     cli->GetRPath(newRuntimeDirs, true);
 
@@ -731,11 +749,34 @@
       return;
     }
 
+    // Escape any CMake syntax in the RPATHs.
+    std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath);
+    std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath);
+
     // Write a rule to run chrpath to set the install-tree RPATH
     os << indent << "file(RPATH_CHANGE\n"
        << indent << "     FILE \"" << toDestDirPath << "\"\n"
-       << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
-       << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
+       << indent << "     OLD_RPATH " << escapedOldRpath << "\n";
+
+    // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
+    // CMake install script.
+    switch (this->Target->GetPolicyStatusCMP0095()) {
+      case cmPolicies::WARN:
+        this->IssueCMP0095Warning(newRpath);
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        os << indent << "     NEW_RPATH \"" << newRpath << "\"";
+        break;
+      default:
+        os << indent << "     NEW_RPATH " << escapedNewRpath;
+        break;
+    }
+
+    if (this->Target->GetPropertyAsBool("INSTALL_REMOVE_ENVIRONMENT_RPATH")) {
+      os << "\n" << indent << "     INSTALL_REMOVE_ENVIRONMENT_RPATH)\n";
+    } else {
+      os << indent << ")\n";
+    }
   }
 }
 
@@ -838,3 +879,26 @@
      << "\"" << this->Target->Target->GetName() << "\" "
      << "\"" << toDestDirPath << "\")\n";
 }
+
+void cmInstallTargetGenerator::IssueCMP0095Warning(
+  const std::string& unescapedRpath)
+{
+  // Reduce warning noise to cases where used RPATHs may actually be affected
+  // by CMP0095. This filter is meant to skip warnings in cases when
+  // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has
+  // worked already before CMP0095. We intend to issue a warning in all cases
+  // with curly-braces syntax, even if the workaround of double-escaping is in
+  // place, since we deprecate the need for it with CMP0095.
+  const bool potentially_affected(unescapedRpath.find("${") !=
+                                  std::string::npos);
+
+  if (potentially_affected) {
+    std::ostringstream w;
+    w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n";
+    w << "RPATH entries for target '" << this->Target->GetName() << "' "
+      << "will not be escaped in the intermediary "
+      << "cmake_install.cmake script.";
+    this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
+      MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());
+  }
+}
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index ed3ab52..8730454 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmInstallGenerator.h"
-#include "cmListFileCache.h"
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmInstallGenerator.h"
+#include "cmListFileCache.h"
+#include "cmScriptGenerator.h"
+
 class cmGeneratorTarget;
 class cmLocalGenerator;
 
@@ -74,9 +74,9 @@
   void GenerateScriptForConfigObjectLibrary(std::ostream& os,
                                             const std::string& config,
                                             Indent indent);
-  typedef void (cmInstallTargetGenerator::*TweakMethod)(std::ostream&, Indent,
-                                                        const std::string&,
-                                                        std::string const&);
+  using TweakMethod = void (cmInstallTargetGenerator::*)(std::ostream&, Indent,
+                                                         const std::string&,
+                                                         const std::string&);
   void AddTweak(std::ostream& os, Indent indent, const std::string& config,
                 std::string const& file, TweakMethod tweak);
   void AddTweak(std::ostream& os, Indent indent, const std::string& config,
@@ -104,6 +104,7 @@
                      const std::string& toDestDirPath);
   void AddUniversalInstallRule(std::ostream& os, Indent indent,
                                const std::string& toDestDirPath);
+  void IssueCMP0095Warning(const std::string& unescapedRpath);
 
   std::string TargetName;
   cmGeneratorTarget* Target;
diff --git a/Source/cmInstallTargetsCommand.cxx b/Source/cmInstallTargetsCommand.cxx
index ef07e2c..44f23a5 100644
--- a/Source/cmInstallTargetsCommand.cxx
+++ b/Source/cmInstallTargetsCommand.cxx
@@ -5,54 +5,54 @@
 #include <unordered_map>
 #include <utility>
 
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
-// cmExecutableCommand
-bool cmInstallTargetsCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmInstallTargetsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  // Enable the install target.
-  this->Makefile->GetGlobalGenerator()->EnableInstallTarget();
+  cmMakefile& mf = status.GetMakefile();
 
-  cmMakefile::cmTargetMap& tgts = this->Makefile->GetTargets();
-  std::vector<std::string>::const_iterator s = args.begin();
+  // Enable the install target.
+  mf.GetGlobalGenerator()->EnableInstallTarget();
+
+  cmMakefile::cmTargetMap& tgts = mf.GetTargets();
+  auto s = args.begin();
   ++s;
   std::string runtime_dir = "/bin";
   for (; s != args.end(); ++s) {
     if (*s == "RUNTIME_DIRECTORY") {
       ++s;
       if (s == args.end()) {
-        this->SetError("called with RUNTIME_DIRECTORY but no actual "
-                       "directory");
+        status.SetError("called with RUNTIME_DIRECTORY but no actual "
+                        "directory");
         return false;
       }
 
       runtime_dir = *s;
     } else {
-      cmMakefile::cmTargetMap::iterator ti = tgts.find(*s);
+      auto ti = tgts.find(*s);
       if (ti != tgts.end()) {
         ti->second.SetInstallPath(args[0]);
         ti->second.SetRuntimeInstallPath(runtime_dir);
         ti->second.SetHaveInstallRule(true);
       } else {
         std::string str = "Cannot find target: \"" + *s + "\" to install.";
-        this->SetError(str);
+        status.SetError(str);
         return false;
       }
     }
   }
 
-  this->Makefile->GetGlobalGenerator()->AddInstallComponent(
-    this->Makefile->GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
+  mf.GetGlobalGenerator()->AddInstallComponent(
+    mf.GetSafeDefinition("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME"));
 
   return true;
 }
diff --git a/Source/cmInstallTargetsCommand.h b/Source/cmInstallTargetsCommand.h
index 9950fb7..0c5850c 100644
--- a/Source/cmInstallTargetsCommand.h
+++ b/Source/cmInstallTargetsCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmInstallTargetsCommand
- * \brief Specifies where to install some targets
- *
- * cmInstallTargetsCommand specifies the relative path where a list of
- * targets should be installed. The targets can be executables or
- * libraries.
- */
-class cmInstallTargetsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmInstallTargetsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmInstallTargetsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmInstalledFile.cxx b/Source/cmInstalledFile.cxx
index 537b4ec..eabe590 100644
--- a/Source/cmInstalledFile.cxx
+++ b/Source/cmInstalledFile.cxx
@@ -2,12 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstalledFile.h"
 
+#include <utility>
+
 #include "cmAlgorithms.h"
+#include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
-#include "cmSystemTools.h"
-
-#include <utility>
+#include "cmStringAlgorithms.h"
 
 cmInstalledFile::cmInstalledFile() = default;
 
@@ -73,7 +74,7 @@
 bool cmInstalledFile::GetProperty(const std::string& prop,
                                   std::string& value) const
 {
-  PropertyMapType::const_iterator i = this->Properties.find(prop);
+  auto i = this->Properties.find(prop);
   if (i == this->Properties.end()) {
     return false;
   }
@@ -97,7 +98,7 @@
 {
   std::string value;
   bool isSet = this->GetProperty(prop, value);
-  return isSet && cmSystemTools::IsOn(value);
+  return isSet && cmIsOn(value);
 }
 
 void cmInstalledFile::GetPropertyAsList(const std::string& prop,
@@ -107,5 +108,5 @@
   this->GetProperty(prop, value);
 
   list.clear();
-  cmSystemTools::ExpandListArgument(value, list);
+  cmExpandList(value, list);
 }
diff --git a/Source/cmInstalledFile.h b/Source/cmInstalledFile.h
index b7d602e..ee809ee 100644
--- a/Source/cmInstalledFile.h
+++ b/Source/cmInstalledFile.h
@@ -5,13 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGeneratorExpression.h"
-
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <vector>
 
+class cmCompiledGeneratorExpression;
 class cmMakefile;
 
 /** \class cmInstalledFile
@@ -22,10 +21,10 @@
 class cmInstalledFile
 {
 public:
-  typedef std::unique_ptr<cmCompiledGeneratorExpression>
-    CompiledGeneratorExpressionPtrType;
+  using CompiledGeneratorExpressionPtrType =
+    std::unique_ptr<cmCompiledGeneratorExpression>;
 
-  typedef std::vector<cmCompiledGeneratorExpression*> ExpressionVectorType;
+  using ExpressionVectorType = std::vector<cmCompiledGeneratorExpression*>;
 
   struct Property
   {
@@ -38,7 +37,7 @@
     ExpressionVectorType ValueExpressions;
   };
 
-  typedef std::map<std::string, Property> PropertyMapType;
+  using PropertyMapType = std::map<std::string, Property>;
 
   cmInstalledFile();
 
diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx
index 636a8e1..b23ab43 100644
--- a/Source/cmJsonObjects.cxx
+++ b/Source/cmJsonObjects.cxx
@@ -2,6 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmJsonObjects.h" // IWYU pragma: keep
 
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -14,30 +26,18 @@
 #include "cmLinkLineComputer.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTest.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <functional>
-#include <limits>
-#include <map>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
 namespace {
 
 std::vector<std::string> getConfigurations(const cmake* cm)
@@ -263,7 +263,7 @@
   std::unordered_map<LanguageData, std::vector<std::string>> fileGroups;
   for (cmSourceFile* file : files) {
     LanguageData fileData;
-    fileData.Language = file->GetLanguage();
+    fileData.Language = file->GetOrDetermineLanguage();
     if (!fileData.Language.empty()) {
       const LanguageData& ld = languageDataMap.at(fileData.Language);
       cmLocalGenerator* lg = target->GetLocalGenerator();
@@ -326,7 +326,7 @@
 
     fileData.IsGenerated = file->GetIsGenerated();
     std::vector<std::string>& groupFileList = fileGroups[fileData];
-    groupFileList.push_back(file->GetFullPath());
+    groupFileList.push_back(file->ResolveFullPath());
   }
 
   const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory();
@@ -356,21 +356,18 @@
   }
 
   // Remove any config specific variables from the output.
-  cmGeneratorExpression ge;
-  auto cge = ge.Parse(command);
-  const std::string& processed = cge->Evaluate(lg, config);
-  result[kCTEST_COMMAND] = processed;
+  result[kCTEST_COMMAND] =
+    cmGeneratorExpression::Evaluate(command, lg, config);
 
   // Build up the list of properties that may have been specified
   Json::Value properties = Json::arrayValue;
-  for (auto& prop : testInfo->GetProperties()) {
+  for (auto& prop : testInfo->GetProperties().GetList()) {
     Json::Value entry = Json::objectValue;
     entry[kKEY_KEY] = prop.first;
 
     // Remove config variables from the value too.
-    auto cge_value = ge.Parse(prop.second.GetValue());
-    const std::string& processed_value = cge_value->Evaluate(lg, config);
-    entry[kVALUE_KEY] = processed_value;
+    entry[kVALUE_KEY] =
+      cmGeneratorExpression::Evaluate(prop.second, lg, config);
     properties.append(entry);
   }
   result[kPROPERTIES_KEY] = properties;
@@ -500,9 +497,9 @@
         if (!dest.empty() && cmSystemTools::FileIsFullPath(dest)) {
           installPath = dest;
         } else {
-          std::string installPrefix =
-            target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
-          installPath = installPrefix + '/' + dest;
+          installPath = cmStrCat(
+            target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"), '/',
+            dest);
         }
 
         installPaths.append(installPath);
@@ -516,9 +513,11 @@
     Json::Value artifacts = Json::arrayValue;
     artifacts.append(
       target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact));
-    if (target->IsDLLPlatform()) {
+    if (target->HasImportLibrary(config)) {
       artifacts.append(
         target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
+    }
+    if (target->IsDLLPlatform()) {
       const cmGeneratorTarget::OutputInfo* output =
         target->GetOutputInfo(config);
       if (output && !output->PdbDir.empty()) {
@@ -539,19 +538,19 @@
     lg->GetTargetFlags(&linkLineComputer, config, linkLibs, linkLanguageFlags,
                        linkFlags, frameworkPath, linkPath, target);
 
-    linkLibs = cmSystemTools::TrimWhitespace(linkLibs);
-    linkFlags = cmSystemTools::TrimWhitespace(linkFlags);
-    linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags);
-    frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath);
-    linkPath = cmSystemTools::TrimWhitespace(linkPath);
+    linkLibs = cmTrimWhitespace(linkLibs);
+    linkFlags = cmTrimWhitespace(linkFlags);
+    linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags);
+    frameworkPath = cmTrimWhitespace(frameworkPath);
+    linkPath = cmTrimWhitespace(linkPath);
 
-    if (!cmSystemTools::TrimWhitespace(linkLibs).empty()) {
+    if (!cmTrimWhitespace(linkLibs).empty()) {
       result[kLINK_LIBRARIES_KEY] = linkLibs;
     }
-    if (!cmSystemTools::TrimWhitespace(linkFlags).empty()) {
+    if (!cmTrimWhitespace(linkFlags).empty()) {
       result[kLINK_FLAGS_KEY] = linkFlags;
     }
-    if (!cmSystemTools::TrimWhitespace(linkLanguageFlags).empty()) {
+    if (!cmTrimWhitespace(linkLanguageFlags).empty()) {
       result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags;
     }
     if (!frameworkPath.empty()) {
diff --git a/Source/cmJsonObjects.h b/Source/cmJsonObjects.h
index cd4da94..64291cc 100644
--- a/Source/cmJsonObjects.h
+++ b/Source/cmJsonObjects.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_jsoncpp_value.h"
-
 #include <string>
 #include <vector>
 
+#include "cm_jsoncpp_value.h"
+
 class cmake;
 class cmGlobalGenerator;
 
diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx
new file mode 100644
index 0000000..cce6178
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.cxx
@@ -0,0 +1,71 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmLDConfigLDConfigTool.h"
+
+#include <istream>
+#include <string>
+#include <vector>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmMakefile.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+cmLDConfigLDConfigTool::cmLDConfigLDConfigTool(
+  cmRuntimeDependencyArchive* archive)
+  : cmLDConfigTool(archive)
+{
+}
+
+bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths)
+{
+  std::string ldConfigPath =
+    this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_COMMAND");
+  if (ldConfigPath.empty()) {
+    ldConfigPath = cmSystemTools::FindProgram(
+      "ldconfig", { "/sbin", "/usr/sbin", "/usr/local/sbin" });
+    if (ldConfigPath.empty()) {
+      this->Archive->SetError("Could not find ldconfig");
+      return false;
+    }
+  }
+
+  std::vector<std::string> ldConfigCommand = cmExpandedList(ldConfigPath);
+  ldConfigCommand.emplace_back("-v");
+  ldConfigCommand.emplace_back("-N"); // Don't rebuild the cache.
+  ldConfigCommand.emplace_back("-X"); // Don't update links.
+
+  cmUVProcessChainBuilder builder;
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .AddCommand(ldConfigCommand);
+  auto process = builder.Start();
+  if (!process.Valid()) {
+    this->Archive->SetError("Failed to start ldconfig process");
+    return false;
+  }
+
+  std::string line;
+  static const cmsys::RegularExpression regex("^([^\t:]*):");
+  while (std::getline(*process.OutputStream(), line)) {
+    cmsys::RegularExpressionMatch match;
+    if (regex.find(line.c_str(), match)) {
+      paths.push_back(match.match(1));
+    }
+  }
+
+  if (!process.Wait()) {
+    this->Archive->SetError("Failed to wait on ldconfig process");
+    return false;
+  }
+  auto status = process.GetStatus();
+  if (!status[0] || status[0]->ExitStatus != 0) {
+    this->Archive->SetError("Failed to run ldconfig");
+    return false;
+  }
+
+  return true;
+}
diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h
new file mode 100644
index 0000000..34bf6c6
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.h
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmLDConfigLDConfigTool_h
+#define cmLDConfigLDConfigTool_h
+
+#include <string>
+#include <vector>
+
+#include "cmLDConfigTool.h"
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigLDConfigTool : public cmLDConfigTool
+{
+public:
+  cmLDConfigLDConfigTool(cmRuntimeDependencyArchive* archive);
+
+  bool GetLDConfigPaths(std::vector<std::string>& paths) override;
+};
+
+#endif
diff --git a/Source/cmLDConfigTool.cxx b/Source/cmLDConfigTool.cxx
new file mode 100644
index 0000000..8d5d563
--- /dev/null
+++ b/Source/cmLDConfigTool.cxx
@@ -0,0 +1,9 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmLDConfigTool.h"
+
+cmLDConfigTool::cmLDConfigTool(cmRuntimeDependencyArchive* archive)
+  : Archive(archive)
+{
+}
diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h
new file mode 100644
index 0000000..c816562
--- /dev/null
+++ b/Source/cmLDConfigTool.h
@@ -0,0 +1,24 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmLDConfigTool_h
+#define cmLDConfigTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigTool
+{
+public:
+  cmLDConfigTool(cmRuntimeDependencyArchive* archive);
+  virtual ~cmLDConfigTool() = default;
+
+  virtual bool GetLDConfigPaths(std::vector<std::string>& paths) = 0;
+
+protected:
+  cmRuntimeDependencyArchive* Archive;
+};
+
+#endif
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 7850977..2914046 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -4,24 +4,26 @@
 
 #include <sstream>
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+static void AddLinkDir(cmMakefile& mf, std::string const& dir,
+                       std::vector<std::string>& directories);
 
-// cmLinkDirectoriesCommand
-bool cmLinkDirectoriesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmLinkDirectoriesCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status)
 {
   if (args.empty()) {
     return true;
   }
 
-  bool before = this->Makefile->IsOn("CMAKE_LINK_DIRECTORIES_BEFORE");
+  cmMakefile& mf = status.GetMakefile();
+  bool before = mf.IsOn("CMAKE_LINK_DIRECTORIES_BEFORE");
 
   auto i = args.cbegin();
   if ((*i) == "BEFORE") {
@@ -34,16 +36,16 @@
 
   std::vector<std::string> directories;
   for (; i != args.cend(); ++i) {
-    this->AddLinkDir(*i, directories);
+    AddLinkDir(mf, *i, directories);
   }
 
-  this->Makefile->AddLinkDirectory(cmJoin(directories, ";"), before);
+  mf.AddLinkDirectory(cmJoin(directories, ";"), before);
 
   return true;
 }
 
-void cmLinkDirectoriesCommand::AddLinkDir(
-  std::string const& dir, std::vector<std::string>& directories)
+static void AddLinkDir(cmMakefile& mf, std::string const& dir,
+                       std::vector<std::string>& directories)
 {
   std::string unixPath = dir;
   cmSystemTools::ConvertToUnixSlashes(unixPath);
@@ -56,10 +58,10 @@
       << "  " << unixPath << "\n"
       << "as a link directory.\n";
     /* clang-format on */
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0015)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0015)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015);
-        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+        mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
         break;
       case cmPolicies::OLD:
         // OLD behavior does not convert
@@ -67,7 +69,7 @@
       case cmPolicies::REQUIRED_IF_USED:
       case cmPolicies::REQUIRED_ALWAYS:
         e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0015);
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        mf.IssueMessage(MessageType::FATAL_ERROR, e.str());
         CM_FALLTHROUGH;
       case cmPolicies::NEW:
         // NEW behavior converts
@@ -75,10 +77,7 @@
         break;
     }
     if (convertToAbsolute) {
-      std::string tmp = this->Makefile->GetCurrentSourceDirectory();
-      tmp += "/";
-      tmp += unixPath;
-      unixPath = tmp;
+      unixPath = cmStrCat(mf.GetCurrentSourceDirectory(), '/', unixPath);
     }
   }
   directories.push_back(unixPath);
diff --git a/Source/cmLinkDirectoriesCommand.h b/Source/cmLinkDirectoriesCommand.h
index ae4fb7f..a7caa5c 100644
--- a/Source/cmLinkDirectoriesCommand.h
+++ b/Source/cmLinkDirectoriesCommand.h
@@ -8,36 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmLinkDirectoriesCommand
- * \brief Define a list of directories containing files to link.
- *
- * cmLinkDirectoriesCommand is used to specify a list
- * of directories containing files to link into executable(s).
- * Note that the command supports the use of CMake built-in variables
- * such as CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR.
- */
-class cmLinkDirectoriesCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmLinkDirectoriesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void AddLinkDir(std::string const& dir,
-                  std::vector<std::string>& directories);
-};
+bool cmLinkDirectoriesCommand(std::vector<std::string> const& args,
+                              cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx
index 9b03ad0..91eb183 100644
--- a/Source/cmLinkItem.cxx
+++ b/Source/cmLinkItem.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLinkItem.h"
 
-#include "cmGeneratorTarget.h"
-
 #include <utility> // IWYU pragma: keep
 
+#include "cmGeneratorTarget.h"
+
 cmLinkItem::cmLinkItem() = default;
 
 cmLinkItem::cmLinkItem(std::string n, cmListFileBacktrace bt)
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index 5b635b5..2d9378b 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <algorithm>
 #include <map>
 #include <ostream>
 #include <string>
 #include <vector>
 
+#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmSystemTools.h"
 #include "cmTargetLinkLibraryType.h"
@@ -58,6 +58,9 @@
 {
   // Libraries listed in the interface.
   std::vector<cmLinkItem> Libraries;
+
+  // Whether the list depends on a genex referencing the head target.
+  bool HadHeadSensitiveCondition = false;
 };
 
 struct cmLinkInterface : public cmLinkInterfaceLibraries
@@ -84,8 +87,7 @@
   bool LibrariesDone = false;
   bool AllDone = false;
   bool Exists = false;
-  bool HadHeadSensitiveCondition = false;
-  const char* ExplicitLibraries = nullptr;
+  bool Explicit = false;
 };
 
 struct cmHeadToLinkInterfaceMap
@@ -118,8 +120,7 @@
 
   // Check if any entry in the list matches this configuration.
   std::string configUpper = cmSystemTools::UpperCase(config);
-  if (std::find(debugConfigs.begin(), debugConfigs.end(), configUpper) !=
-      debugConfigs.end()) {
+  if (cmContains(debugConfigs, configUpper)) {
     return DEBUG_LibraryType;
   }
   // The current configuration is not a debug configuration.
diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx
index 13f6bae..cb63ceb 100644
--- a/Source/cmLinkLibrariesCommand.cxx
+++ b/Source/cmLinkLibrariesCommand.cxx
@@ -2,39 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLinkLibrariesCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// cmLinkLibrariesCommand
-bool cmLinkLibrariesCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.empty()) {
     return true;
   }
+  cmMakefile& mf = status.GetMakefile();
   // add libraries, note that there is an optional prefix
   // of debug and optimized than can be used
-  for (std::vector<std::string>::const_iterator i = args.begin();
-       i != args.end(); ++i) {
+  for (auto i = args.begin(); i != args.end(); ++i) {
     if (*i == "debug") {
       ++i;
       if (i == args.end()) {
-        this->SetError("The \"debug\" argument must be followed by "
-                       "a library");
+        status.SetError("The \"debug\" argument must be followed by "
+                        "a library");
         return false;
       }
-      this->Makefile->AppendProperty("LINK_LIBRARIES", "debug");
+      mf.AppendProperty("LINK_LIBRARIES", "debug");
     } else if (*i == "optimized") {
       ++i;
       if (i == args.end()) {
-        this->SetError("The \"optimized\" argument must be followed by "
-                       "a library");
+        status.SetError("The \"optimized\" argument must be followed by "
+                        "a library");
         return false;
       }
-      this->Makefile->AppendProperty("LINK_LIBRARIES", "optimized");
+      mf.AppendProperty("LINK_LIBRARIES", "optimized");
     }
-    this->Makefile->AppendProperty("LINK_LIBRARIES", i->c_str());
+    mf.AppendProperty("LINK_LIBRARIES", i->c_str());
   }
 
   return true;
diff --git a/Source/cmLinkLibrariesCommand.h b/Source/cmLinkLibrariesCommand.h
index af25fba..3412251 100644
--- a/Source/cmLinkLibrariesCommand.h
+++ b/Source/cmLinkLibrariesCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmLinkLibrariesCommand
- * \brief Specify a list of libraries to link into executables.
- *
- * cmLinkLibrariesCommand is used to specify a list of libraries to link
- * into executable(s) or shared objects. The names of the libraries
- * should be those defined by the LIBRARY(library) command(s).
- */
-class cmLinkLibrariesCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmLinkLibrariesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 469faca..0dc6236 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -4,13 +4,17 @@
 #include "cmLinkLineComputer.h"
 
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmListFileCache.h"
 #include "cmOutputConverter.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmLinkLineComputer::cmLinkLineComputer(cmOutputConverter* outputConverter,
@@ -55,22 +59,49 @@
 std::string cmLinkLineComputer::ComputeLinkLibs(cmComputeLinkInformation& cli)
 {
   std::string linkLibs;
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  std::vector<BT<std::string>> linkLibsList;
+  this->ComputeLinkLibs(cli, linkLibsList);
+  cli.AppendValues(linkLibs, linkLibsList);
+  return linkLibs;
+}
+
+void cmLinkLineComputer::ComputeLinkLibs(
+  cmComputeLinkInformation& cli, std::vector<BT<std::string>>& linkLibraries)
+{
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& items = cli.GetItems();
   for (auto const& item : items) {
     if (item.Target &&
         item.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       continue;
     }
+
+    BT<std::string> linkLib;
     if (item.IsPath) {
-      linkLibs +=
+      linkLib.Value += cli.GetLibLinkFileFlag();
+      linkLib.Value +=
         this->ConvertToOutputFormat(this->ConvertToLinkReference(item.Value));
     } else {
-      linkLibs += item.Value;
+      linkLib.Value += item.Value;
     }
-    linkLibs += " ";
+    linkLib.Value += " ";
+
+    const cmLinkImplementation* linkImpl =
+      cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
+
+    for (const cmLinkImplItem& iter : linkImpl->Libraries) {
+      if (iter.Target != nullptr &&
+          iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+        std::string libPath = iter.Target->GetLocation(cli.GetConfig());
+        if (item.Value == libPath) {
+          linkLib.Backtrace = iter.Backtrace;
+          break;
+        }
+      }
+    }
+
+    linkLibraries.emplace_back(linkLib);
   }
-  return linkLibs;
 }
 
 std::string cmLinkLineComputer::ConvertToOutputFormat(std::string const& input)
@@ -99,8 +130,19 @@
   std::string const& libPathTerminator)
 {
   std::string linkPath;
+  std::vector<BT<std::string>> linkPathList;
+  this->ComputeLinkPath(cli, libPathFlag, libPathTerminator, linkPathList);
+  cli.AppendValues(linkPath, linkPathList);
+  return linkPath;
+}
 
+void cmLinkLineComputer::ComputeLinkPath(
+  cmComputeLinkInformation& cli, std::string const& libPathFlag,
+  std::string const& libPathTerminator, std::vector<BT<std::string>>& linkPath)
+{
   if (cli.GetLinkLanguage() == "Swift") {
+    std::string linkPathNoBT;
+
     for (const cmComputeLinkInformation::Item& item : cli.GetItems()) {
       const cmGeneratorTarget* target = item.Target;
       if (!target) {
@@ -110,24 +152,27 @@
       if (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
           target->GetType() == cmStateEnums::SHARED_LIBRARY) {
         cmStateEnums::ArtifactType type = cmStateEnums::RuntimeBinaryArtifact;
-        if (target->GetType() == cmStateEnums::SHARED_LIBRARY &&
-            target->IsDLLPlatform()) {
+        if (target->HasImportLibrary(cli.GetConfig())) {
           type = cmStateEnums::ImportLibraryArtifact;
         }
 
-        linkPath += " " + libPathFlag +
-          item.Target->GetDirectory(cli.GetConfig(), type) +
-          libPathTerminator + " ";
+        linkPathNoBT += cmStrCat(
+          " ", libPathFlag, item.Target->GetDirectory(cli.GetConfig(), type),
+          libPathTerminator, " ");
       }
     }
+
+    if (!linkPathNoBT.empty()) {
+      linkPath.emplace_back(std::move(linkPathNoBT));
+    }
   }
 
-  for (std::string const& libDir : cli.GetDirectories()) {
-    linkPath += " " + libPathFlag + this->ConvertToOutputForExisting(libDir) +
-      libPathTerminator + " ";
+  for (BT<std::string> libDir : cli.GetDirectoriesWithBacktraces()) {
+    libDir.Value = cmStrCat(" ", libPathFlag,
+                            this->ConvertToOutputForExisting(libDir.Value),
+                            libPathTerminator, " ");
+    linkPath.emplace_back(libDir);
   }
-
-  return linkPath;
 }
 
 std::string cmLinkLineComputer::ComputeRPath(cmComputeLinkInformation& cli)
@@ -177,13 +222,30 @@
 std::string cmLinkLineComputer::ComputeLinkLibraries(
   cmComputeLinkInformation& cli, std::string const& stdLibString)
 {
-  std::ostringstream fout;
-  fout << this->ComputeRPath(cli);
+  std::string linkLibraries;
+  std::vector<BT<std::string>> linkLibrariesList;
+  this->ComputeLinkLibraries(cli, stdLibString, linkLibrariesList);
+  cli.AppendValues(linkLibraries, linkLibrariesList);
+  return linkLibraries;
+}
+
+void cmLinkLineComputer::ComputeLinkLibraries(
+  cmComputeLinkInformation& cli, std::string const& stdLibString,
+  std::vector<BT<std::string>>& linkLibraries)
+{
+  std::ostringstream rpathOut;
+  rpathOut << this->ComputeRPath(cli);
+
+  std::string rpath = rpathOut.str();
+  if (!rpath.empty()) {
+    linkLibraries.emplace_back(std::move(rpath));
+  }
 
   // Write the library flags to the build rule.
-  fout << this->ComputeLinkLibs(cli);
+  this->ComputeLinkLibs(cli, linkLibraries);
 
   // Add the linker runtime search path if any.
+  std::ostringstream fout;
   std::string rpath_link = cli.GetRPathLinkString();
   if (!cli.GetRPathLinkFlag().empty() && !rpath_link.empty()) {
     fout << cli.GetRPathLinkFlag();
@@ -196,7 +258,10 @@
     fout << stdLibString << " ";
   }
 
-  return fout.str();
+  std::string remainingLibs = fout.str();
+  if (!remainingLibs.empty()) {
+    linkLibraries.emplace_back(remainingLibs);
+  }
 }
 
 std::string cmLinkLineComputer::GetLinkerLanguage(cmGeneratorTarget* target,
diff --git a/Source/cmLinkLineComputer.h b/Source/cmLinkLineComputer.h
index 2355c32..f426976 100644
--- a/Source/cmLinkLineComputer.h
+++ b/Source/cmLinkLineComputer.h
@@ -7,12 +7,15 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <string>
+#include <vector>
 
 #include "cmStateDirectory.h"
 
 class cmComputeLinkInformation;
 class cmGeneratorTarget;
 class cmOutputConverter;
+template <typename T>
+class BT;
 
 class cmLinkLineComputer
 {
@@ -34,17 +37,28 @@
                               std::string const& libPathFlag,
                               std::string const& libPathTerminator);
 
+  void ComputeLinkPath(cmComputeLinkInformation& cli,
+                       std::string const& libPathFlag,
+                       std::string const& libPathTerminator,
+                       std::vector<BT<std::string>>& linkPath);
+
   std::string ComputeFrameworkPath(cmComputeLinkInformation& cli,
                                    std::string const& fwSearchFlag);
 
-  virtual std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
-                                           std::string const& stdLibString);
+  std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
+                                   std::string const& stdLibString);
+
+  virtual void ComputeLinkLibraries(
+    cmComputeLinkInformation& cli, std::string const& stdLibString,
+    std::vector<BT<std::string>>& linkLibraries);
 
   virtual std::string GetLinkerLanguage(cmGeneratorTarget* target,
                                         std::string const& config);
 
 protected:
   std::string ComputeLinkLibs(cmComputeLinkInformation& cli);
+  void ComputeLinkLibs(cmComputeLinkInformation& cli,
+                       std::vector<BT<std::string>>& linkLibraries);
   std::string ComputeRPath(cmComputeLinkInformation& cli);
 
   std::string ConvertToOutputFormat(std::string const& input);
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 8d2add6..d845652 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -3,21 +3,21 @@
 
 #include "cmLinkLineDeviceComputer.h"
 
-#include <algorithm>
 #include <set>
-#include <sstream>
 #include <utility>
-#include <vector>
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmLinkItem.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
+#include "cmMakefile.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 
 class cmOutputConverter;
 
@@ -52,7 +52,7 @@
 {
   // Determine if this item might requires device linking.
   // For this we only consider targets
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& items = cli.GetItems();
   std::string config = cli.GetConfig();
   for (auto const& item : items) {
@@ -68,21 +68,22 @@
   return false;
 }
 
-std::string cmLinkLineDeviceComputer::ComputeLinkLibraries(
-  cmComputeLinkInformation& cli, std::string const& stdLibString)
+void cmLinkLineDeviceComputer::ComputeLinkLibraries(
+  cmComputeLinkInformation& cli, std::string const& stdLibString,
+  std::vector<BT<std::string>>& linkLibraries)
 {
-  // Write the library flags to the build rule.
-  std::ostringstream fout;
-
   // Generate the unique set of link items when device linking.
   // The nvcc device linker is designed so that each static library
   // with device symbols only needs to be listed once as it doesn't
   // care about link order.
   std::set<std::string> emitted;
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& items = cli.GetItems();
   std::string config = cli.GetConfig();
   bool skipItemAfterFramework = false;
+  // Note:
+  // Any modification of this algorithm should be reflected also in
+  // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions
   for (auto const& item : items) {
     if (skipItemAfterFramework) {
       skipItemAfterFramework = false;
@@ -92,6 +93,7 @@
     if (item.Target) {
       bool skip = false;
       switch (item.Target->GetType()) {
+        case cmStateEnums::SHARED_LIBRARY:
         case cmStateEnums::MODULE_LIBRARY:
         case cmStateEnums::INTERFACE_LIBRARY:
           skip = true;
@@ -107,7 +109,7 @@
       }
     }
 
-    std::string out;
+    BT<std::string> linkLib;
     if (item.IsPath) {
       // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
       // These should be passed to nvlink.  Other extensions need to be left
@@ -115,7 +117,7 @@
       // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
       if (cmHasLiteralSuffix(item.Value, ".a") ||
           cmHasLiteralSuffix(item.Value, ".lib")) {
-        out += this->ConvertToOutputFormat(
+        linkLib.Value += this->ConvertToOutputFormat(
           this->ConvertToLinkReference(item.Value));
       }
     } else if (item.Value == "-framework") {
@@ -124,19 +126,33 @@
       skipItemAfterFramework = true;
       continue;
     } else if (cmLinkItemValidForDevice(item.Value)) {
-      out += item.Value;
+      linkLib.Value += item.Value;
     }
 
-    if (emitted.insert(out).second) {
-      fout << out << " ";
+    if (emitted.insert(linkLib.Value).second) {
+      linkLib.Value += " ";
+
+      const cmLinkImplementation* linkImpl =
+        cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
+
+      for (const cmLinkImplItem& iter : linkImpl->Libraries) {
+        if (iter.Target != nullptr &&
+            iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+          std::string libPath = iter.Target->GetLocation(cli.GetConfig());
+          if (item.Value == libPath) {
+            linkLib.Backtrace = iter.Backtrace;
+            break;
+          }
+        }
+      }
+
+      linkLibraries.emplace_back(linkLib);
     }
   }
 
   if (!stdLibString.empty()) {
-    fout << stdLibString << " ";
+    linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
   }
-
-  return fout.str();
 }
 
 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
@@ -156,16 +172,20 @@
     return false;
   }
 
+  if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
+    return false;
+  }
+
   if (const char* resolveDeviceSymbols =
         target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
     // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
     // to honor the value no matter what it is.
-    return cmSystemTools::IsOn(resolveDeviceSymbols);
+    return cmIsOn(resolveDeviceSymbols);
   }
 
   if (const char* separableCompilation =
         target.GetProperty("CUDA_SEPARABLE_COMPILATION")) {
-    if (cmSystemTools::IsOn(separableCompilation)) {
+    if (cmIsOn(separableCompilation)) {
       bool doDeviceLinking = false;
       switch (target.GetType()) {
         case cmStateEnums::SHARED_LIBRARY:
@@ -182,14 +202,10 @@
 
   // Determine if we have any dependencies that require
   // us to do a device link step
-  const std::string cuda_lang("CUDA");
   cmGeneratorTarget::LinkClosure const* closure =
     target.GetLinkClosure(config);
 
-  bool closureHasCUDA =
-    (std::find(closure->Languages.begin(), closure->Languages.end(),
-               cuda_lang) != closure->Languages.end());
-  if (closureHasCUDA) {
+  if (cmContains(closure->Languages, "CUDA")) {
     cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
     if (pcli) {
       cmLinkLineDeviceComputer deviceLinkComputer(
diff --git a/Source/cmLinkLineDeviceComputer.h b/Source/cmLinkLineDeviceComputer.h
index 0ea5f69..a9b01cd 100644
--- a/Source/cmLinkLineDeviceComputer.h
+++ b/Source/cmLinkLineDeviceComputer.h
@@ -7,6 +7,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <string>
+#include <vector>
 
 #include "cmLinkLineComputer.h"
 
@@ -15,6 +16,8 @@
 class cmLocalGenerator;
 class cmOutputConverter;
 class cmStateDirectory;
+template <typename T>
+class BT;
 
 class cmLinkLineDeviceComputer : public cmLinkLineComputer
 {
@@ -29,8 +32,9 @@
 
   bool ComputeRequiresDeviceLinking(cmComputeLinkInformation& cli);
 
-  std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
-                                   std::string const& stdLibString) override;
+  void ComputeLinkLibraries(
+    cmComputeLinkInformation& cli, std::string const& stdLibString,
+    std::vector<BT<std::string>>& linkLibraries) override;
 
   std::string GetLinkerLanguage(cmGeneratorTarget* target,
                                 std::string const& config) override;
diff --git a/Source/cmLinkedTree.h b/Source/cmLinkedTree.h
index 099fb6d..c7453ea 100644
--- a/Source/cmLinkedTree.h
+++ b/Source/cmLinkedTree.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <assert.h>
+#include <cassert>
 #include <vector>
 
 /**
@@ -27,9 +27,9 @@
 template <typename T>
 class cmLinkedTree
 {
-  typedef typename std::vector<T>::size_type PositionType;
-  typedef T* PointerType;
-  typedef T& ReferenceType;
+  using PositionType = typename std::vector<T>::size_type;
+  using PointerType = T*;
+  using ReferenceType = T&;
 
 public:
   class iterator
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 1b01ea2..5200a16 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -2,100 +2,49 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmListCommand.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib> // required for atoi
 #include <functional>
 #include <iterator>
 #include <set>
 #include <sstream>
 #include <stdexcept>
-#include <stdio.h>
-#include <stdlib.h> // required for atoi
 #include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_static_string_view.hxx"
 
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
+#include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmListCommand::InitialPass(std::vector<std::string> const& args,
-                                cmExecutionStatus&)
-{
-  if (args.size() < 2) {
-    this->SetError("must be called with at least two arguments.");
-    return false;
-  }
+bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
+                 std::string const& listName,
+                 std::vector<std::string>& varArgsExpanded,
+                 cmExecutionStatus& status);
 
-  const std::string& subCommand = args[0];
-  if (subCommand == "LENGTH") {
-    return this->HandleLengthCommand(args);
-  }
-  if (subCommand == "GET") {
-    return this->HandleGetCommand(args);
-  }
-  if (subCommand == "APPEND") {
-    return this->HandleAppendCommand(args);
-  }
-  if (subCommand == "PREPEND") {
-    return this->HandlePrependCommand(args);
-  }
-  if (subCommand == "POP_BACK") {
-    return this->HandlePopBackCommand(args);
-  }
-  if (subCommand == "POP_FRONT") {
-    return this->HandlePopFrontCommand(args);
-  }
-  if (subCommand == "FIND") {
-    return this->HandleFindCommand(args);
-  }
-  if (subCommand == "INSERT") {
-    return this->HandleInsertCommand(args);
-  }
-  if (subCommand == "JOIN") {
-    return this->HandleJoinCommand(args);
-  }
-  if (subCommand == "REMOVE_AT") {
-    return this->HandleRemoveAtCommand(args);
-  }
-  if (subCommand == "REMOVE_ITEM") {
-    return this->HandleRemoveItemCommand(args);
-  }
-  if (subCommand == "REMOVE_DUPLICATES") {
-    return this->HandleRemoveDuplicatesCommand(args);
-  }
-  if (subCommand == "TRANSFORM") {
-    return this->HandleTransformCommand(args);
-  }
-  if (subCommand == "SORT") {
-    return this->HandleSortCommand(args);
-  }
-  if (subCommand == "SUBLIST") {
-    return this->HandleSublistCommand(args);
-  }
-  if (subCommand == "REVERSE") {
-    return this->HandleReverseCommand(args);
-  }
-  if (subCommand == "FILTER") {
-    return this->HandleFilterCommand(args);
-  }
-
-  std::string e = "does not recognize sub-command " + subCommand;
-  this->SetError(e);
-  return false;
-}
-
-bool cmListCommand::GetListString(std::string& listString,
-                                  const std::string& var)
+bool GetListString(std::string& listString, const std::string& var,
+                   const cmMakefile& makefile)
 {
   // get the old value
-  const char* cacheValue = this->Makefile->GetDefinition(var);
+  const char* cacheValue = makefile.GetDefinition(var);
   if (!cacheValue) {
     return false;
   }
@@ -103,11 +52,11 @@
   return true;
 }
 
-bool cmListCommand::GetList(std::vector<std::string>& list,
-                            const std::string& var)
+bool GetList(std::vector<std::string>& list, const std::string& var,
+             const cmMakefile& makefile)
 {
   std::string listString;
-  if (!this->GetListString(listString, var)) {
+  if (!GetListString(listString, var, makefile)) {
     return false;
   }
   // if the size of the list
@@ -115,25 +64,24 @@
     return true;
   }
   // expand the variable into a list
-  cmSystemTools::ExpandListArgument(listString, list, true);
+  cmExpandList(listString, list, true);
   // if no empty elements then just return
-  if (std::find(list.begin(), list.end(), std::string()) == list.end()) {
+  if (!cmContains(list, std::string())) {
     return true;
   }
   // if we have empty elements we need to check policy CMP0007
-  switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0007)) {
+  switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) {
     case cmPolicies::WARN: {
       // Default is to warn and use old behavior
       // OLD behavior is to allow compatibility, so recall
       // ExpandListArgument without the true which will remove
       // empty values
       list.clear();
-      cmSystemTools::ExpandListArgument(listString, list);
-      std::string warn = cmPolicies::GetPolicyWarning(cmPolicies::CMP0007);
-      warn += " List has value = [";
-      warn += listString;
-      warn += "].";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, warn);
+      cmExpandList(listString, list);
+      std::string warn =
+        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007),
+                 " List has value = [", listString, "].");
+      makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn);
       return true;
     }
     case cmPolicies::OLD:
@@ -141,13 +89,13 @@
       // ExpandListArgument without the true which will remove
       // empty values
       list.clear();
-      cmSystemTools::ExpandListArgument(listString, list);
+      cmExpandList(listString, list);
       return true;
     case cmPolicies::NEW:
       return true;
     case cmPolicies::REQUIRED_IF_USED:
     case cmPolicies::REQUIRED_ALWAYS:
-      this->Makefile->IssueMessage(
+      makefile.IssueMessage(
         MessageType::FATAL_ERROR,
         cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007));
       return false;
@@ -155,10 +103,11 @@
   return true;
 }
 
-bool cmListCommand::HandleLengthCommand(std::vector<std::string> const& args)
+bool HandleLengthCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("sub-command LENGTH requires two arguments.");
+    status.SetError("sub-command LENGTH requires two arguments.");
     return false;
   }
 
@@ -168,19 +117,20 @@
   // do not check the return value here
   // if the list var is not found varArgsExpanded will have size 0
   // and we will return 0
-  this->GetList(varArgsExpanded, listName);
+  GetList(varArgsExpanded, listName, status.GetMakefile());
   size_t length = varArgsExpanded.size();
   char buffer[1024];
   sprintf(buffer, "%d", static_cast<int>(length));
 
-  this->Makefile->AddDefinition(variableName, buffer);
+  status.GetMakefile().AddDefinition(variableName, buffer);
   return true;
 }
 
-bool cmListCommand::HandleGetCommand(std::vector<std::string> const& args)
+bool HandleGetCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.size() < 4) {
-    this->SetError("sub-command GET requires at least three arguments.");
+    status.SetError("sub-command GET requires at least three arguments.");
     return false;
   }
 
@@ -188,13 +138,13 @@
   const std::string& variableName = args.back();
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
-    this->Makefile->AddDefinition(variableName, "NOTFOUND");
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+    status.GetMakefile().AddDefinition(variableName, "NOTFOUND");
     return true;
   }
   // FIXME: Add policy to make non-existing lists an error like empty lists.
   if (varArgsExpanded.empty()) {
-    this->SetError("GET given empty list");
+    status.SetError("GET given empty list");
     return false;
   }
 
@@ -210,20 +160,19 @@
       item = static_cast<int>(nitem) + item;
     }
     if (item < 0 || nitem <= static_cast<size_t>(item)) {
-      std::ostringstream str;
-      str << "index: " << item << " out of range (-" << nitem << ", "
-          << nitem - 1 << ")";
-      this->SetError(str.str());
+      status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
+                               ", ", nitem - 1, ")"));
       return false;
     }
     value += varArgsExpanded[item];
   }
 
-  this->Makefile->AddDefinition(variableName, value.c_str());
+  status.GetMakefile().AddDefinition(variableName, value);
   return true;
 }
 
-bool cmListCommand::HandleAppendCommand(std::vector<std::string> const& args)
+bool HandleAppendCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
 
@@ -232,10 +181,11 @@
     return true;
   }
 
+  cmMakefile& makefile = status.GetMakefile();
   std::string const& listName = args[1];
   // expand the variable
   std::string listString;
-  this->GetListString(listString, listName);
+  GetListString(listString, listName, makefile);
 
   // If `listString` or `args` is empty, no need to append `;`,
   // then index is going to be `1` and points to the end-of-string ";"
@@ -243,11 +193,12 @@
     std::string::size_type(listString.empty() || args.empty());
   listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";");
 
-  this->Makefile->AddDefinition(listName, listString.c_str());
+  makefile.AddDefinition(listName, listString);
   return true;
 }
 
-bool cmListCommand::HandlePrependCommand(std::vector<std::string> const& args)
+bool HandlePrependCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
 
@@ -256,10 +207,11 @@
     return true;
   }
 
+  cmMakefile& makefile = status.GetMakefile();
   std::string const& listName = args[1];
   // expand the variable
   std::string listString;
-  this->GetListString(listString, listName);
+  GetListString(listString, listName, makefile);
 
   // If `listString` or `args` is empty, no need to append `;`,
   // then `offset` is going to be `1` and points to the end-of-string ";"
@@ -268,22 +220,24 @@
   listString.insert(0,
                     cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]);
 
-  this->Makefile->AddDefinition(listName, listString.c_str());
+  makefile.AddDefinition(listName, listString);
   return true;
 }
 
-bool cmListCommand::HandlePopBackCommand(std::vector<std::string> const& args)
+bool HandlePopBackCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
 
+  cmMakefile& makefile = status.GetMakefile();
   auto ai = args.cbegin();
   ++ai; // Skip subcommand name
   std::string const& listName = *ai++;
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, makefile)) {
     // Can't get the list definition... undefine any vars given after.
     for (; ai != args.cend(); ++ai) {
-      this->Makefile->RemoveDefinition(*ai);
+      makefile.RemoveDefinition(*ai);
     }
     return true;
   }
@@ -296,41 +250,42 @@
       // Ok, assign elements to be removed to the given variables
       for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) {
         assert(!ai->empty());
-        this->Makefile->AddDefinition(*ai, varArgsExpanded.back().c_str());
+        makefile.AddDefinition(*ai, varArgsExpanded.back());
         varArgsExpanded.pop_back();
       }
       // Undefine the rest variables if the list gets empty earlier...
       for (; ai != args.cend(); ++ai) {
-        this->Makefile->RemoveDefinition(*ai);
+        makefile.RemoveDefinition(*ai);
       }
     }
 
-    this->Makefile->AddDefinition(listName,
-                                  cmJoin(varArgsExpanded, ";").c_str());
+    makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
 
   } else if (ai !=
              args.cend()) { // The list is empty, but some args were given
     // Need to *undefine* 'em all, cuz there are no items to assign...
     for (; ai != args.cend(); ++ai) {
-      this->Makefile->RemoveDefinition(*ai);
+      makefile.RemoveDefinition(*ai);
     }
   }
 
   return true;
 }
 
-bool cmListCommand::HandlePopFrontCommand(std::vector<std::string> const& args)
+bool HandlePopFrontCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
 
+  cmMakefile& makefile = status.GetMakefile();
   auto ai = args.cbegin();
   ++ai; // Skip subcommand name
   std::string const& listName = *ai++;
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, makefile)) {
     // Can't get the list definition... undefine any vars given after.
     for (; ai != args.cend(); ++ai) {
-      this->Makefile->RemoveDefinition(*ai);
+      makefile.RemoveDefinition(*ai);
     }
     return true;
   }
@@ -344,33 +299,33 @@
       auto vi = varArgsExpanded.begin();
       for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) {
         assert(!ai->empty());
-        this->Makefile->AddDefinition(*ai, vi->c_str());
+        makefile.AddDefinition(*ai, *vi);
       }
       varArgsExpanded.erase(varArgsExpanded.begin(), vi);
       // Undefine the rest variables if the list gets empty earlier...
       for (; ai != args.cend(); ++ai) {
-        this->Makefile->RemoveDefinition(*ai);
+        makefile.RemoveDefinition(*ai);
       }
     }
 
-    this->Makefile->AddDefinition(listName,
-                                  cmJoin(varArgsExpanded, ";").c_str());
+    makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
 
   } else if (ai !=
              args.cend()) { // The list is empty, but some args were given
     // Need to *undefine* 'em all, cuz there are no items to assign...
     for (; ai != args.cend(); ++ai) {
-      this->Makefile->RemoveDefinition(*ai);
+      makefile.RemoveDefinition(*ai);
     }
   }
 
   return true;
 }
 
-bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args)
+bool HandleFindCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() != 4) {
-    this->SetError("sub-command FIND requires three arguments.");
+    status.SetError("sub-command FIND requires three arguments.");
     return false;
   }
 
@@ -378,28 +333,28 @@
   const std::string& variableName = args.back();
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
-    this->Makefile->AddDefinition(variableName, "-1");
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+    status.GetMakefile().AddDefinition(variableName, "-1");
     return true;
   }
 
-  std::vector<std::string>::iterator it =
-    std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
+  auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
   if (it != varArgsExpanded.end()) {
-    std::ostringstream indexStream;
-    indexStream << std::distance(varArgsExpanded.begin(), it);
-    this->Makefile->AddDefinition(variableName, indexStream.str().c_str());
+    status.GetMakefile().AddDefinition(
+      variableName,
+      std::to_string(std::distance(varArgsExpanded.begin(), it)));
     return true;
   }
 
-  this->Makefile->AddDefinition(variableName, "-1");
+  status.GetMakefile().AddDefinition(variableName, "-1");
   return true;
 }
 
-bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args)
+bool HandleInsertCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 4) {
-    this->SetError("sub-command INSERT requires at least three arguments.");
+    status.SetError("sub-command INSERT requires at least three arguments.");
     return false;
   }
 
@@ -408,11 +363,10 @@
   // expand the variable
   int item = atoi(args[2].c_str());
   std::vector<std::string> varArgsExpanded;
-  if ((!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) &&
+  if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
+       varArgsExpanded.empty()) &&
       item != 0) {
-    std::ostringstream str;
-    str << "index: " << item << " out of range (0, 0)";
-    this->SetError(str.str());
+    status.SetError(cmStrCat("index: ", item, " out of range (0, 0)"));
     return false;
   }
 
@@ -422,10 +376,9 @@
       item = static_cast<int>(nitem) + item;
     }
     if (item < 0 || nitem < static_cast<size_t>(item)) {
-      std::ostringstream str;
-      str << "index: " << item << " out of range (-" << varArgsExpanded.size()
-          << ", " << varArgsExpanded.size() << ")";
-      this->SetError(str.str());
+      status.SetError(cmStrCat("index: ", item, " out of range (-",
+                               varArgsExpanded.size(), ", ",
+                               varArgsExpanded.size(), ")"));
       return false;
     }
   }
@@ -434,17 +387,16 @@
                          args.end());
 
   std::string value = cmJoin(varArgsExpanded, ";");
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
-bool cmListCommand::HandleJoinCommand(std::vector<std::string> const& args)
+bool HandleJoinCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() != 4) {
-    std::ostringstream error;
-    error << "sub-command JOIN requires three arguments (" << args.size() - 1
-          << " found).";
-    this->SetError(error.str());
+    status.SetError(cmStrCat("sub-command JOIN requires three arguments (",
+                             args.size() - 1, " found)."));
     return false;
   }
 
@@ -454,95 +406,93 @@
 
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
-    this->Makefile->AddDefinition(variableName, "");
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+    status.GetMakefile().AddDefinition(variableName, "");
     return true;
   }
 
   std::string value =
     cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue);
 
-  this->Makefile->AddDefinition(variableName, value.c_str());
+  status.GetMakefile().AddDefinition(variableName, value);
   return true;
 }
 
-bool cmListCommand::HandleRemoveItemCommand(
-  std::vector<std::string> const& args)
+bool HandleRemoveItemCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("sub-command REMOVE_ITEM requires two or more arguments.");
+    status.SetError("sub-command REMOVE_ITEM requires two or more arguments.");
     return false;
   }
 
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
     return true;
   }
 
   std::vector<std::string> remove(args.begin() + 2, args.end());
   std::sort(remove.begin(), remove.end());
-  std::vector<std::string>::const_iterator remEnd =
-    std::unique(remove.begin(), remove.end());
-  std::vector<std::string>::const_iterator remBegin = remove.begin();
+  auto remEnd = std::unique(remove.begin(), remove.end());
+  auto remBegin = remove.begin();
 
-  std::vector<std::string>::const_iterator argsEnd =
+  auto argsEnd =
     cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd));
-  std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
+  auto argsBegin = varArgsExpanded.cbegin();
   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
-bool cmListCommand::HandleReverseCommand(std::vector<std::string> const& args)
+bool HandleReverseCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
   if (args.size() > 2) {
-    this->SetError("sub-command REVERSE only takes one argument.");
+    status.SetError("sub-command REVERSE only takes one argument.");
     return false;
   }
 
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
     return true;
   }
 
   std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";");
 
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
-bool cmListCommand::HandleRemoveDuplicatesCommand(
-  std::vector<std::string> const& args)
+bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
   if (args.size() > 2) {
-    this->SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
+    status.SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
     return false;
   }
 
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
     return true;
   }
 
-  std::vector<std::string>::const_iterator argsEnd =
-    cmRemoveDuplicates(varArgsExpanded);
-  std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
+  auto argsEnd = cmRemoveDuplicates(varArgsExpanded);
+  auto argsBegin = varArgsExpanded.cbegin();
   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
 
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
 // Helpers for list(TRANSFORM <list> ...)
-namespace {
 using transform_type = std::function<std::string(const std::string&)>;
 
 class transform_error : public std::runtime_error
@@ -641,11 +591,9 @@
       index = static_cast<int>(count) + index;
     }
     if (index < 0 || count <= static_cast<std::size_t>(index)) {
-      std::ostringstream str;
-      str << "sub-command TRANSFORM, selector " << this->Tag
-          << ", index: " << index << " out of range (-" << count << ", "
-          << count - 1 << ").";
-      throw transform_error(str.str());
+      throw transform_error(cmStrCat(
+        "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
+        " out of range (-", count, ", ", count - 1, ")."));
     }
     return index;
   }
@@ -693,7 +641,8 @@
     }
 
     this->Indexes.resize(size);
-    auto start = this->Start, step = this->Step;
+    auto start = this->Start;
+    auto step = this->Step;
     std::generate(this->Indexes.begin(), this->Indexes.end(),
                   [&start, step]() -> int {
                     auto r = start;
@@ -725,17 +674,14 @@
     makefile->ClearMatches();
 
     if (!this->ReplaceHelper.IsRegularExpressionValid()) {
-      std::ostringstream error;
-      error
-        << "sub-command TRANSFORM, action REPLACE: Failed to compile regex \""
-        << arguments[0] << "\".";
-      throw transform_error(error.str());
+      throw transform_error(
+        cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
+                 "regex \"",
+                 arguments[0], "\"."));
     }
     if (!this->ReplaceHelper.IsReplaceExpressionValid()) {
-      std::ostringstream error;
-      error << "sub-command TRANSFORM, action REPLACE: "
-            << this->ReplaceHelper.GetError() << ".";
-      throw transform_error(error.str());
+      throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+                                     this->ReplaceHelper.GetError(), "."));
     }
   }
 
@@ -745,10 +691,8 @@
     std::string output;
 
     if (!this->ReplaceHelper.Replace(input, output)) {
-      std::ostringstream error;
-      error << "sub-command TRANSFORM, action REPLACE: "
-            << this->ReplaceHelper.GetError() << ".";
-      throw transform_error(error.str());
+      throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+                                     this->ReplaceHelper.GetError(), "."));
     }
 
     return output;
@@ -757,13 +701,12 @@
 private:
   cmStringReplaceHelper ReplaceHelper;
 };
-}
 
-bool cmListCommand::HandleTransformCommand(
-  std::vector<std::string> const& args)
+bool HandleTransformCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError(
+    status.SetError(
       "sub-command TRANSFORM requires an action to be specified.");
     return false;
   }
@@ -853,7 +796,7 @@
                   { "STRIP", 0,
                     [&command](const std::string& s) -> std::string {
                       if (command.Selector->InSelection(s)) {
-                        return cmSystemTools::TrimWhitespace(s);
+                        return cmTrimWhitespace(s);
                       }
 
                       return s;
@@ -884,19 +827,17 @@
   auto descriptor = descriptors.find(args[index]);
 
   if (descriptor == descriptors.end()) {
-    std::ostringstream error;
-    error << " sub-command TRANSFORM, " << args[index] << " invalid action.";
-    this->SetError(error.str());
+    status.SetError(
+      cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action."));
     return false;
   }
 
   // Action arguments
   index += 1;
   if (args.size() < index + descriptor->Arity) {
-    std::ostringstream error;
-    error << "sub-command TRANSFORM, action " << descriptor->Name
-          << " expects " << descriptor->Arity << " argument(s).";
-    this->SetError(error.str());
+    status.SetError(cmStrCat("sub-command TRANSFORM, action ",
+                             descriptor->Name, " expects ", descriptor->Arity,
+                             " argument(s)."));
     return false;
   }
 
@@ -909,43 +850,44 @@
 
   if (command.Name == "REPLACE") {
     try {
-      command.Action =
-        cm::make_unique<TransformReplace>(command.Arguments, this->Makefile);
+      command.Action = cm::make_unique<TransformReplace>(
+        command.Arguments, &status.GetMakefile());
     } catch (const transform_error& e) {
-      this->SetError(e.what());
+      status.SetError(e.what());
       return false;
     }
   }
 
-  const std::string REGEX{ "REGEX" }, AT{ "AT" }, FOR{ "FOR" },
-    OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
+  const std::string REGEX{ "REGEX" };
+  const std::string AT{ "AT" };
+  const std::string FOR{ "FOR" };
+  const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
 
   // handle optional arguments
   while (args.size() > index) {
     if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
         command.Selector) {
-      std::ostringstream error;
-      error << "sub-command TRANSFORM, selector already specified ("
-            << command.Selector->Tag << ").";
-      this->SetError(error.str());
+      status.SetError(
+        cmStrCat("sub-command TRANSFORM, selector already specified (",
+                 command.Selector->Tag, ")."));
+
       return false;
     }
 
     // REGEX selector
     if (args[index] == REGEX) {
       if (args.size() == ++index) {
-        this->SetError("sub-command TRANSFORM, selector REGEX expects "
-                       "'regular expression' argument.");
+        status.SetError("sub-command TRANSFORM, selector REGEX expects "
+                        "'regular expression' argument.");
         return false;
       }
 
       command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]);
       if (!command.Selector->Validate()) {
-        std::ostringstream error;
-        error << "sub-command TRANSFORM, selector REGEX failed to compile "
-                 "regex \"";
-        error << args[index] << "\".";
-        this->SetError(error.str());
+        status.SetError(
+          cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
+                   "regex \"",
+                   args[index], "\"."));
         return false;
       }
 
@@ -975,7 +917,7 @@
       }
 
       if (indexes.empty()) {
-        this->SetError(
+        status.SetError(
           "sub-command TRANSFORM, selector AT expects at least one "
           "numeric value.");
         return false;
@@ -990,12 +932,15 @@
     // FOR selector
     if (args[index] == FOR) {
       if (args.size() <= ++index + 1) {
-        this->SetError("sub-command TRANSFORM, selector FOR expects, at least,"
-                       " two arguments.");
+        status.SetError(
+          "sub-command TRANSFORM, selector FOR expects, at least,"
+          " two arguments.");
         return false;
       }
 
-      int start = 0, stop = 0, step = 1;
+      int start = 0;
+      int stop = 0;
+      int step = 1;
       bool valid = true;
       try {
         std::size_t pos;
@@ -1016,8 +961,8 @@
         valid = false;
       }
       if (!valid) {
-        this->SetError("sub-command TRANSFORM, selector FOR expects, "
-                       "at least, two numeric values.");
+        status.SetError("sub-command TRANSFORM, selector FOR expects, "
+                        "at least, two numeric values.");
         return false;
       }
       // try to read a third numeric value for step
@@ -1038,8 +983,8 @@
       }
 
       if (step < 0) {
-        this->SetError("sub-command TRANSFORM, selector FOR expects "
-                       "non negative numeric value for <step>.");
+        status.SetError("sub-command TRANSFORM, selector FOR expects "
+                        "non negative numeric value for <step>.");
       }
 
       command.Selector =
@@ -1051,8 +996,8 @@
     // output variable
     if (args[index] == OUTPUT_VARIABLE) {
       if (args.size() == ++index) {
-        this->SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
-                       "expects variable name argument.");
+        status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
+                        "expects variable name argument.");
         return false;
       }
 
@@ -1060,18 +1005,16 @@
       continue;
     }
 
-    std::ostringstream error;
-    error << "sub-command TRANSFORM, '"
-          << cmJoin(cmMakeRange(args).advance(index), " ")
-          << "': unexpected argument(s).";
-    this->SetError(error.str());
+    status.SetError(cmStrCat("sub-command TRANSFORM, '",
+                             cmJoin(cmMakeRange(args).advance(index), " "),
+                             "': unexpected argument(s)."));
     return false;
   }
 
   // expand the list variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, command.ListName)) {
-    this->Makefile->AddDefinition(command.OutputName, "");
+  if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) {
+    status.GetMakefile().AddDefinition(command.OutputName, "");
     return true;
   }
 
@@ -1083,12 +1026,12 @@
   try {
     command.Selector->Transform(varArgsExpanded, descriptor->Transform);
   } catch (const transform_error& e) {
-    this->SetError(e.what());
+    status.SetError(e.what());
     return false;
   }
 
-  this->Makefile->AddDefinition(command.OutputName,
-                                cmJoin(varArgsExpanded, ";").c_str());
+  status.GetMakefile().AddDefinition(command.OutputName,
+                                     cmJoin(varArgsExpanded, ";"));
 
   return true;
 }
@@ -1117,7 +1060,7 @@
   };
 
 protected:
-  typedef std::string (*StringFilter)(const std::string& in);
+  using StringFilter = std::string (*)(const std::string&);
   StringFilter GetCompareFilter(Compare compare)
   {
     return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName
@@ -1168,11 +1111,12 @@
   bool descending;
 };
 
-bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
+bool HandleSortCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   assert(args.size() >= 2);
   if (args.size() > 8) {
-    this->SetError("sub-command SORT only takes up to six arguments.");
+    status.SetError("sub-command SORT only takes up to six arguments.");
     return false;
   }
 
@@ -1187,9 +1131,9 @@
     const std::string option = args[argumentIndex++];
     if (option == "COMPARE") {
       if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
-        std::string error = messageHint + "option \"" + option +
-          "\" has been specified multiple times.";
-        this->SetError(error);
+        std::string error = cmStrCat(messageHint, "option \"", option,
+                                     "\" has been specified multiple times.");
+        status.SetError(error);
         return false;
       }
       if (argumentIndex < args.size()) {
@@ -1199,23 +1143,22 @@
         } else if (argument == "FILE_BASENAME") {
           sortCompare = cmStringSorter::Compare::FILE_BASENAME;
         } else {
-          std::string error = messageHint + "value \"" + argument +
-            "\" for option \"" + option + "\" is invalid.";
-          this->SetError(error);
+          std::string error =
+            cmStrCat(messageHint, "value \"", argument, "\" for option \"",
+                     option, "\" is invalid.");
+          status.SetError(error);
           return false;
         }
       } else {
-        std::string error =
-          messageHint + "missing argument for option \"" + option + "\".";
-        this->SetError(error);
+        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
+                                 option, "\"."));
         return false;
       }
     } else if (option == "CASE") {
       if (sortCaseSensitivity !=
           cmStringSorter::CaseSensitivity::UNINITIALIZED) {
-        std::string error = messageHint + "option \"" + option +
-          "\" has been specified multiple times.";
-        this->SetError(error);
+        status.SetError(cmStrCat(messageHint, "option \"", option,
+                                 "\" has been specified multiple times."));
         return false;
       }
       if (argumentIndex < args.size()) {
@@ -1225,23 +1168,21 @@
         } else if (argument == "INSENSITIVE") {
           sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
         } else {
-          std::string error = messageHint + "value \"" + argument +
-            "\" for option \"" + option + "\" is invalid.";
-          this->SetError(error);
+          status.SetError(cmStrCat(messageHint, "value \"", argument,
+                                   "\" for option \"", option,
+                                   "\" is invalid."));
           return false;
         }
       } else {
-        std::string error =
-          messageHint + "missing argument for option \"" + option + "\".";
-        this->SetError(error);
+        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
+                                 option, "\"."));
         return false;
       }
     } else if (option == "ORDER") {
 
       if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
-        std::string error = messageHint + "option \"" + option +
-          "\" has been specified multiple times.";
-        this->SetError(error);
+        status.SetError(cmStrCat(messageHint, "option \"", option,
+                                 "\" has been specified multiple times."));
         return false;
       }
       if (argumentIndex < args.size()) {
@@ -1251,21 +1192,19 @@
         } else if (argument == "DESCENDING") {
           sortOrder = cmStringSorter::Order::DESCENDING;
         } else {
-          std::string error = messageHint + "value \"" + argument +
-            "\" for option \"" + option + "\" is invalid.";
-          this->SetError(error);
+          status.SetError(cmStrCat(messageHint, "value \"", argument,
+                                   "\" for option \"", option,
+                                   "\" is invalid."));
           return false;
         }
       } else {
-        std::string error =
-          messageHint + "missing argument for option \"" + option + "\".";
-        this->SetError(error);
+        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
+                                 option, "\"."));
         return false;
       }
     } else {
-      std::string error =
-        messageHint + "option \"" + option + "\" is unknown.";
-      this->SetError(error);
+      status.SetError(
+        cmStrCat(messageHint, "option \"", option, "\" is unknown."));
       return false;
     }
   }
@@ -1283,7 +1222,7 @@
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
     return true;
   }
 
@@ -1297,17 +1236,16 @@
   }
 
   std::string value = cmJoin(varArgsExpanded, ";");
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
-bool cmListCommand::HandleSublistCommand(std::vector<std::string> const& args)
+bool HandleSublistCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() != 5) {
-    std::ostringstream error;
-    error << "sub-command SUBLIST requires four arguments (" << args.size() - 1
-          << " found).";
-    this->SetError(error.str());
+    status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
+                             args.size() - 1, " found)."));
     return false;
   }
 
@@ -1316,8 +1254,9 @@
 
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) {
-    this->Makefile->AddDefinition(variableName, "");
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
+      varArgsExpanded.empty()) {
+    status.GetMakefile().AddDefinition(variableName, "");
     return true;
   }
 
@@ -1327,16 +1266,12 @@
   using size_type = decltype(varArgsExpanded)::size_type;
 
   if (start < 0 || size_type(start) >= varArgsExpanded.size()) {
-    std::ostringstream error;
-    error << "begin index: " << start << " is out of range 0 - "
-          << varArgsExpanded.size() - 1;
-    this->SetError(error.str());
+    status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
+                             varArgsExpanded.size() - 1));
     return false;
   }
   if (length < -1) {
-    std::ostringstream error;
-    error << "length: " << length << " should be -1 or greater";
-    this->SetError(error.str());
+    status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
     return false;
   }
 
@@ -1346,22 +1281,24 @@
     : size_type(start + length);
   std::vector<std::string> sublist(varArgsExpanded.begin() + start,
                                    varArgsExpanded.begin() + end);
-  this->Makefile->AddDefinition(variableName, cmJoin(sublist, ";").c_str());
+  status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";"));
   return true;
 }
 
-bool cmListCommand::HandleRemoveAtCommand(std::vector<std::string> const& args)
+bool HandleRemoveAtCommand(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("sub-command REMOVE_AT requires at least "
-                   "two arguments.");
+    status.SetError("sub-command REMOVE_AT requires at least "
+                    "two arguments.");
     return false;
   }
 
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
+      varArgsExpanded.empty()) {
     std::ostringstream str;
     str << "index: ";
     for (size_t i = 1; i < args.size(); ++i) {
@@ -1371,7 +1308,7 @@
       }
     }
     str << " out of range (0, 0)";
-    this->SetError(str.str());
+    status.SetError(str.str());
     return false;
   }
 
@@ -1384,43 +1321,42 @@
       item = static_cast<int>(nitem) + item;
     }
     if (item < 0 || nitem <= static_cast<size_t>(item)) {
-      std::ostringstream str;
-      str << "index: " << item << " out of range (-" << nitem << ", "
-          << nitem - 1 << ")";
-      this->SetError(str.str());
+      status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
+                               ", ", nitem - 1, ")"));
       return false;
     }
     removed.push_back(static_cast<size_t>(item));
   }
 
   std::sort(removed.begin(), removed.end());
-  std::vector<size_t>::const_iterator remEnd =
-    std::unique(removed.begin(), removed.end());
-  std::vector<size_t>::const_iterator remBegin = removed.begin();
+  auto remEnd = std::unique(removed.begin(), removed.end());
+  auto remBegin = removed.begin();
 
-  std::vector<std::string>::const_iterator argsEnd =
+  auto argsEnd =
     cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
-  std::vector<std::string>::const_iterator argsBegin = varArgsExpanded.begin();
+  auto argsBegin = varArgsExpanded.cbegin();
   std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
 
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
 
-bool cmListCommand::HandleFilterCommand(std::vector<std::string> const& args)
+bool HandleFilterCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command FILTER requires a list to be specified.");
+    status.SetError("sub-command FILTER requires a list to be specified.");
     return false;
   }
 
   if (args.size() < 3) {
-    this->SetError("sub-command FILTER requires an operator to be specified.");
+    status.SetError(
+      "sub-command FILTER requires an operator to be specified.");
     return false;
   }
 
   if (args.size() < 4) {
-    this->SetError("sub-command FILTER requires a mode to be specified.");
+    status.SetError("sub-command FILTER requires a mode to be specified.");
     return false;
   }
 
@@ -1431,28 +1367,29 @@
   } else if (op == "EXCLUDE") {
     includeMatches = false;
   } else {
-    this->SetError("sub-command FILTER does not recognize operator " + op);
+    status.SetError("sub-command FILTER does not recognize operator " + op);
     return false;
   }
 
   const std::string& listName = args[1];
   // expand the variable
   std::vector<std::string> varArgsExpanded;
-  if (!this->GetList(varArgsExpanded, listName)) {
+  if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
     return true;
   }
 
   const std::string& mode = args[3];
   if (mode == "REGEX") {
     if (args.size() != 5) {
-      this->SetError("sub-command FILTER, mode REGEX "
-                     "requires five arguments.");
+      status.SetError("sub-command FILTER, mode REGEX "
+                      "requires five arguments.");
       return false;
     }
-    return this->FilterRegex(args, includeMatches, listName, varArgsExpanded);
+    return FilterRegex(args, includeMatches, listName, varArgsExpanded,
+                       status);
   }
 
-  this->SetError("sub-command FILTER does not recognize mode " + mode);
+  status.SetError("sub-command FILTER does not recognize mode " + mode);
   return false;
 }
 
@@ -1475,28 +1412,60 @@
   const bool includeMatches;
 };
 
-bool cmListCommand::FilterRegex(std::vector<std::string> const& args,
-                                bool includeMatches,
-                                std::string const& listName,
-                                std::vector<std::string>& varArgsExpanded)
+bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
+                 std::string const& listName,
+                 std::vector<std::string>& varArgsExpanded,
+                 cmExecutionStatus& status)
 {
   const std::string& pattern = args[4];
   cmsys::RegularExpression regex(pattern);
   if (!regex.is_valid()) {
-    std::string error = "sub-command FILTER, mode REGEX ";
-    error += "failed to compile regex \"";
-    error += pattern;
-    error += "\".";
-    this->SetError(error);
+    std::string error =
+      cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
+               pattern, "\".");
+    status.SetError(error);
     return false;
   }
 
-  std::vector<std::string>::iterator argsBegin = varArgsExpanded.begin();
-  std::vector<std::string>::iterator argsEnd = varArgsExpanded.end();
-  std::vector<std::string>::iterator newArgsEnd =
+  auto argsBegin = varArgsExpanded.begin();
+  auto argsEnd = varArgsExpanded.end();
+  auto newArgsEnd =
     std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
 
   std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
-  this->Makefile->AddDefinition(listName, value.c_str());
+  status.GetMakefile().AddDefinition(listName, value);
   return true;
 }
+
+} // namespace
+
+bool cmListCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
+{
+  if (args.size() < 2) {
+    status.SetError("must be called with at least two arguments.");
+    return false;
+  }
+
+  static cmSubcommandTable const subcommand{
+    { "LENGTH"_s, HandleLengthCommand },
+    { "GET"_s, HandleGetCommand },
+    { "APPEND"_s, HandleAppendCommand },
+    { "PREPEND"_s, HandlePrependCommand },
+    { "POP_BACK"_s, HandlePopBackCommand },
+    { "POP_FRONT"_s, HandlePopFrontCommand },
+    { "FIND"_s, HandleFindCommand },
+    { "INSERT"_s, HandleInsertCommand },
+    { "JOIN"_s, HandleJoinCommand },
+    { "REMOVE_AT"_s, HandleRemoveAtCommand },
+    { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
+    { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
+    { "TRANSFORM"_s, HandleTransformCommand },
+    { "SORT"_s, HandleSortCommand },
+    { "SUBLIST"_s, HandleSublistCommand },
+    { "REVERSE"_s, HandleReverseCommand },
+    { "FILTER"_s, HandleFilterCommand },
+  };
+
+  return subcommand(args[0], args, status);
+}
diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h
index ea3d643..274d9fd 100644
--- a/Source/cmListCommand.h
+++ b/Source/cmListCommand.h
@@ -8,53 +8,13 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmListCommand
+/**
  * \brief Common list operations
  *
  */
-class cmListCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmListCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  bool HandleLengthCommand(std::vector<std::string> const& args);
-  bool HandleGetCommand(std::vector<std::string> const& args);
-  bool HandleAppendCommand(std::vector<std::string> const& args);
-  bool HandlePrependCommand(std::vector<std::string> const& args);
-  bool HandlePopBackCommand(std::vector<std::string> const& args);
-  bool HandlePopFrontCommand(std::vector<std::string> const& args);
-  bool HandleFindCommand(std::vector<std::string> const& args);
-  bool HandleInsertCommand(std::vector<std::string> const& args);
-  bool HandleJoinCommand(std::vector<std::string> const& args);
-  bool HandleRemoveAtCommand(std::vector<std::string> const& args);
-  bool HandleRemoveItemCommand(std::vector<std::string> const& args);
-  bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
-  bool HandleTransformCommand(std::vector<std::string> const& args);
-  bool HandleSortCommand(std::vector<std::string> const& args);
-  bool HandleSublistCommand(std::vector<std::string> const& args);
-  bool HandleReverseCommand(std::vector<std::string> const& args);
-  bool HandleFilterCommand(std::vector<std::string> const& args);
-  bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
-                   std::string const& listName,
-                   std::vector<std::string>& varArgsExpanded);
-
-  bool GetList(std::vector<std::string>& list, const std::string& var);
-  bool GetListString(std::string& listString, const std::string& var);
-};
+bool cmListCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index df0d00c..47679c9 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -2,18 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmListFileCache.h"
 
+#include <cassert>
+#include <memory>
+#include <sstream>
+#include <utility>
+
 #include "cmListFileLexer.h"
 #include "cmMessageType.h"
 #include "cmMessenger.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <assert.h>
-#include <memory>
-#include <sstream>
-#include <utility>
-
 cmCommandContext::cmCommandName& cmCommandContext::cmCommandName::operator=(
   std::string const& name)
 {
@@ -325,6 +326,7 @@
 {
 }
 
+/* NOLINTNEXTLINE(performance-unnecessary-value-param) */
 cmListFileBacktrace::cmListFileBacktrace(std::shared_ptr<Entry const> parent,
                                          cmListFileContext const& lfc)
   : TopEntry(std::make_shared<Entry const>(std::move(parent), lfc))
@@ -482,8 +484,7 @@
   std::string const& list, cmListFileBacktrace const& bt)
 {
   std::vector<BT<std::string>> result;
-  std::vector<std::string> tmp;
-  cmSystemTools::ExpandListArgument(list, tmp);
+  std::vector<std::string> tmp = cmExpandedList(list);
   result.reserve(tmp.size());
   for (std::string& i : tmp) {
     result.emplace_back(std::move(i), bt);
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h
index 9457415..9cae827 100644
--- a/Source/cmListFileCache.h
+++ b/Source/cmListFileCache.h
@@ -5,9 +5,9 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <iosfwd>
-#include <memory> // IWYU pragma: keep
-#include <stddef.h>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/Source/cmListFileLexer.h b/Source/cmListFileLexer.h
index 8962396..ec6b3cd 100644
--- a/Source/cmListFileLexer.h
+++ b/Source/cmListFileLexer.h
@@ -3,6 +3,11 @@
 #ifndef cmListFileLexer_h
 #define cmListFileLexer_h
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef enum cmListFileLexer_Type_e
 {
   cmListFileLexer_Token_None,
@@ -20,6 +25,7 @@
   cmListFileLexer_Token_BadString
 } cmListFileLexer_Type;
 
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef struct cmListFileLexer_Token_s cmListFileLexer_Token;
 struct cmListFileLexer_Token_s
 {
@@ -40,14 +46,13 @@
   cmListFileLexer_BOM_UTF32BE,
   cmListFileLexer_BOM_UTF32LE
 };
+
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef enum cmListFileLexer_BOM_e cmListFileLexer_BOM;
 
+/* NOLINTNEXTLINE(modernize-use-using) */
 typedef struct cmListFileLexer_s cmListFileLexer;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 cmListFileLexer* cmListFileLexer_New(void);
 int cmListFileLexer_SetFileName(cmListFileLexer*, const char*,
                                 cmListFileLexer_BOM* bom);
diff --git a/Source/cmLoadCacheCommand.cxx b/Source/cmLoadCacheCommand.cxx
index b1fee8d..1184bcb 100644
--- a/Source/cmLoadCacheCommand.cxx
+++ b/Source/cmLoadCacheCommand.cxx
@@ -2,25 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLoadCacheCommand.h"
 
+#include <set>
+
 #include "cmsys/FStream.hxx"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
+static bool ReadWithPrefix(std::vector<std::string> const& args,
+                           cmExecutionStatus& status);
 
-// cmLoadCacheCommand
-bool cmLoadCacheCommand::InitialPass(std::vector<std::string> const& args,
-                                     cmExecutionStatus&)
+static void CheckLine(cmMakefile& mf, std::string const& prefix,
+                      std::set<std::string> const& variablesToRead,
+                      const char* line);
+
+bool cmLoadCacheCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with wrong number of arguments.");
+    status.SetError("called with wrong number of arguments.");
   }
 
   if (args.size() >= 2 && args[1] == "READ_WITH_PREFIX") {
-    return this->ReadWithPrefix(args);
+    return ReadWithPrefix(args, status);
   }
 
   // Cache entries to be excluded from the import list.
@@ -59,24 +66,26 @@
     }
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Loop over each build directory listed in the arguments.  Each
   // directory has a cache file.
   for (std::string const& arg : args) {
     if ((arg == "EXCLUDE") || (arg == "INCLUDE_INTERNALS")) {
       break;
     }
-    this->Makefile->GetCMakeInstance()->LoadCache(arg, false, excludes,
-                                                  includes);
+    mf.GetCMakeInstance()->LoadCache(arg, false, excludes, includes);
   }
 
   return true;
 }
 
-bool cmLoadCacheCommand::ReadWithPrefix(std::vector<std::string> const& args)
+static bool ReadWithPrefix(std::vector<std::string> const& args,
+                           cmExecutionStatus& status)
 {
   // Make sure we have a prefix.
   if (args.size() < 3) {
-    this->SetError("READ_WITH_PREFIX form must specify a prefix.");
+    status.SetError("READ_WITH_PREFIX form must specify a prefix.");
     return false;
   }
 
@@ -84,17 +93,19 @@
   std::string cacheFile = args[0] + "/CMakeCache.txt";
   if (!cmSystemTools::FileExists(cacheFile)) {
     std::string e = "Cannot load cache file from " + cacheFile;
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
   // Prepare the table of variables to read.
-  this->Prefix = args[2];
-  this->VariablesToRead.insert(args.begin() + 3, args.end());
+  std::string const prefix = args[2];
+  std::set<std::string> const variablesToRead(args.begin() + 3, args.end());
 
   // Read the cache file.
   cmsys::ifstream fin(cacheFile.c_str());
 
+  cmMakefile& mf = status.GetMakefile();
+
   // This is a big hack read loop to overcome a buggy ifstream
   // implementation on HP-UX.  This should work on all platforms even
   // for small buffer sizes.
@@ -123,7 +134,7 @@
         }
         if (i != end) {
           // Completed a line.
-          this->CheckLine(line.c_str());
+          CheckLine(mf, prefix, variablesToRead, line.c_str());
           line.clear();
 
           // Skip the newline character.
@@ -134,13 +145,15 @@
   }
   if (!line.empty()) {
     // Partial last line.
-    this->CheckLine(line.c_str());
+    CheckLine(mf, prefix, variablesToRead, line.c_str());
   }
 
   return true;
 }
 
-void cmLoadCacheCommand::CheckLine(const char* line)
+static void CheckLine(cmMakefile& mf, std::string const& prefix,
+                      std::set<std::string> const& variablesToRead,
+                      const char* line)
 {
   // Check one line of the cache file.
   std::string var;
@@ -148,14 +161,14 @@
   cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
   if (cmake::ParseCacheEntry(line, var, value, type)) {
     // Found a real entry.  See if this one was requested.
-    if (this->VariablesToRead.find(var) != this->VariablesToRead.end()) {
+    if (variablesToRead.find(var) != variablesToRead.end()) {
       // This was requested.  Set this variable locally with the given
       // prefix.
-      var = this->Prefix + var;
+      var = prefix + var;
       if (!value.empty()) {
-        this->Makefile->AddDefinition(var, value.c_str());
+        mf.AddDefinition(var, value);
       } else {
-        this->Makefile->RemoveDefinition(var);
+        mf.RemoveDefinition(var);
       }
     }
   }
diff --git a/Source/cmLoadCacheCommand.h b/Source/cmLoadCacheCommand.h
index e0f6e4f..7cee663 100644
--- a/Source/cmLoadCacheCommand.h
+++ b/Source/cmLoadCacheCommand.h
@@ -5,40 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <set>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmLoadCacheCommand
- * \brief load a cache file
- *
- * cmLoadCacheCommand loads the non internal values of a cache file
- */
-class cmLoadCacheCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmLoadCacheCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  std::set<std::string> VariablesToRead;
-  std::string Prefix;
-
-  bool ReadWithPrefix(std::vector<std::string> const& args);
-  void CheckLine(const char* line);
-};
+bool cmLoadCacheCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx
index 69751b6..23ace64 100644
--- a/Source/cmLoadCommandCommand.cxx
+++ b/Source/cmLoadCommandCommand.cxx
@@ -2,49 +2,121 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLoadCommandCommand.h"
 
-#include <signal.h>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <utility>
 
-#include "cmCPluginAPI.cxx"
+#include <cm/memory>
+
 #include "cmCPluginAPI.h"
+#include "cmCommand.h"
 #include "cmDynamicLoader.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+#include "cmCPluginAPI.cxx"
 
 #ifdef __QNX__
 #  include <malloc.h> /* for malloc/free on QNX */
 #endif
 
-extern "C" void TrapsForSignalsCFunction(int sig);
+namespace {
+
+const char* LastName = nullptr;
+
+extern "C" void TrapsForSignals(int sig)
+{
+  fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
+          LastName, sig);
+}
+
+struct SignalHandlerGuard
+{
+  explicit SignalHandlerGuard(const char* name)
+  {
+    LastName = name != nullptr ? name : "????";
+
+    signal(SIGSEGV, TrapsForSignals);
+#ifdef SIGBUS
+    signal(SIGBUS, TrapsForSignals);
+#endif
+    signal(SIGILL, TrapsForSignals);
+  }
+
+  ~SignalHandlerGuard()
+  {
+    signal(SIGSEGV, nullptr);
+#ifdef SIGBUS
+    signal(SIGBUS, nullptr);
+#endif
+    signal(SIGILL, nullptr);
+  }
+
+  SignalHandlerGuard(SignalHandlerGuard const&) = delete;
+  SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
+};
+
+struct LoadedCommandImpl : cmLoadedCommandInfo
+{
+  explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
+    : cmLoadedCommandInfo{ 0,       0,       &cmStaticCAPI, 0,
+                           nullptr, nullptr, nullptr,       nullptr,
+                           nullptr, nullptr, nullptr,       nullptr }
+  {
+    init(this);
+  }
+
+  ~LoadedCommandImpl()
+  {
+    if (this->Destructor) {
+      SignalHandlerGuard guard(this->Name);
+      this->Destructor(this);
+    }
+    if (this->Error != nullptr) {
+      free(this->Error);
+    }
+  }
+
+  LoadedCommandImpl(LoadedCommandImpl const&) = delete;
+  LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
+
+  int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
+  {
+    SignalHandlerGuard guard(this->Name);
+    return this->InitialPass(this, mf, argc, argv);
+  }
+
+  void DoFinalPass(cmMakefile* mf)
+  {
+    SignalHandlerGuard guard(this->Name);
+    this->FinalPass(this, mf);
+  }
+};
 
 // a class for loadabple commands
 class cmLoadedCommand : public cmCommand
 {
 public:
-  cmLoadedCommand()
+  cmLoadedCommand() = default;
+  explicit cmLoadedCommand(CM_INIT_FUNCTION init)
+    : Impl(std::make_shared<LoadedCommandImpl>(init))
   {
-    memset(&this->info, 0, sizeof(this->info));
-    this->info.CAPI = &cmStaticCAPI;
   }
 
-  //! clean up any memory allocated by the plugin
-  ~cmLoadedCommand() override;
-
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override
+  std::unique_ptr<cmCommand> Clone() override
   {
-    cmLoadedCommand* newC = new cmLoadedCommand;
+    auto newC = cm::make_unique<cmLoadedCommand>();
     // we must copy when we clone
-    memcpy(&newC->info, &this->info, sizeof(info));
-    return newC;
+    newC->Impl = this->Impl;
+    return std::unique_ptr<cmCommand>(std::move(newC));
   }
 
   /**
@@ -54,66 +126,20 @@
   bool InitialPass(std::vector<std::string> const& args,
                    cmExecutionStatus&) override;
 
-  /**
-   * This is called at the end after all the information
-   * specified by the command is accumulated. Most commands do
-   * not implement this method.  At this point, reading and
-   * writing to the cache can be done.
-   */
-  void FinalPass() override;
-  bool HasFinalPass() const override
-  {
-    return this->info.FinalPass != nullptr;
-  }
-
-  static const char* LastName;
-  static void TrapsForSignals(int sig)
-  {
-    fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
-            cmLoadedCommand::LastName, sig);
-  }
-  static void InstallSignalHandlers(const char* name, int remove = 0)
-  {
-    cmLoadedCommand::LastName = name;
-    if (!name) {
-      cmLoadedCommand::LastName = "????";
-    }
-
-    if (!remove) {
-      signal(SIGSEGV, TrapsForSignalsCFunction);
-#ifdef SIGBUS
-      signal(SIGBUS, TrapsForSignalsCFunction);
-#endif
-      signal(SIGILL, TrapsForSignalsCFunction);
-    } else {
-      signal(SIGSEGV, nullptr);
-#ifdef SIGBUS
-      signal(SIGBUS, nullptr);
-#endif
-      signal(SIGILL, nullptr);
-    }
-  }
-
-  cmLoadedCommandInfo info;
+private:
+  std::shared_ptr<LoadedCommandImpl> Impl;
 };
 
-extern "C" void TrapsForSignalsCFunction(int sig)
-{
-  cmLoadedCommand::TrapsForSignals(sig);
-}
-
-const char* cmLoadedCommand::LastName = nullptr;
-
 bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
                                   cmExecutionStatus&)
 {
-  if (!info.InitialPass) {
+  if (!this->Impl->InitialPass) {
     return true;
   }
 
   // clear the error string
-  if (this->info.Error) {
-    free(this->info.Error);
+  if (this->Impl->Error) {
+    free(this->Impl->Error);
   }
 
   // create argc and argv and then invoke the command
@@ -126,46 +152,30 @@
   for (i = 0; i < argc; ++i) {
     argv[i] = strdup(args[i].c_str());
   }
-  cmLoadedCommand::InstallSignalHandlers(info.Name);
-  int result = info.InitialPass(&info, this->Makefile, argc, argv);
-  cmLoadedCommand::InstallSignalHandlers(info.Name, 1);
+  int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
   cmFreeArguments(argc, argv);
 
   if (result) {
+    if (this->Impl->FinalPass) {
+      auto impl = this->Impl;
+      this->Makefile->AddFinalAction(
+        [impl](cmMakefile& makefile) { impl->DoFinalPass(&makefile); });
+    }
     return true;
   }
 
   /* Initial Pass must have failed so set the error string */
-  if (this->info.Error) {
-    this->SetError(this->info.Error);
+  if (this->Impl->Error) {
+    this->SetError(this->Impl->Error);
   }
   return false;
 }
 
-void cmLoadedCommand::FinalPass()
-{
-  if (this->info.FinalPass) {
-    cmLoadedCommand::InstallSignalHandlers(info.Name);
-    this->info.FinalPass(&this->info, this->Makefile);
-    cmLoadedCommand::InstallSignalHandlers(info.Name, 1);
-  }
-}
-
-cmLoadedCommand::~cmLoadedCommand()
-{
-  if (this->info.Destructor) {
-    cmLoadedCommand::InstallSignalHandlers(info.Name);
-    this->info.Destructor(&this->info);
-    cmLoadedCommand::InstallSignalHandlers(info.Name, 1);
-  }
-  if (this->info.Error) {
-    free(this->info.Error);
-  }
-}
+} // namespace
 
 // cmLoadCommandCommand
-bool cmLoadCommandCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmLoadCommandCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.empty()) {
     return true;
@@ -173,16 +183,14 @@
 
   // Construct a variable to report what file was loaded, if any.
   // Start by removing the definition in case of failure.
-  std::string reportVar = "CMAKE_LOADED_COMMAND_";
-  reportVar += args[0];
-  this->Makefile->RemoveDefinition(reportVar);
+  std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]);
+  status.GetMakefile().RemoveDefinition(reportVar);
 
   // the file must exist
-  std::string moduleName =
-    this->Makefile->GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX");
-  moduleName += "cm" + args[0];
-  moduleName +=
-    this->Makefile->GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX");
+  std::string moduleName = cmStrCat(
+    status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
+    "cm", args[0],
+    status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
 
   // search for the file
   std::vector<std::string> path;
@@ -198,9 +206,8 @@
   // Try to find the program.
   std::string fullPath = cmSystemTools::FindFile(moduleName, path);
   if (fullPath.empty()) {
-    std::ostringstream e;
-    e << "Attempt to load command failed from file \"" << moduleName << "\"";
-    this->SetError(e.str());
+    status.SetError(cmStrCat("Attempt to load command failed from file \"",
+                             moduleName, "\""));
     return false;
   }
 
@@ -208,41 +215,38 @@
   cmsys::DynamicLoader::LibraryHandle lib =
     cmDynamicLoader::OpenLibrary(fullPath.c_str());
   if (!lib) {
-    std::string err = "Attempt to load the library ";
-    err += fullPath + " failed.";
+    std::string err =
+      cmStrCat("Attempt to load the library ", fullPath, " failed.");
     const char* error = cmsys::DynamicLoader::LastError();
     if (error) {
       err += " Additional error info is:\n";
       err += error;
     }
-    this->SetError(err);
+    status.SetError(err);
     return false;
   }
 
   // Report what file was loaded for this command.
-  this->Makefile->AddDefinition(reportVar, fullPath.c_str());
+  status.GetMakefile().AddDefinition(reportVar, fullPath);
 
   // find the init function
   std::string initFuncName = args[0] + "Init";
   CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
     cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
   if (!initFunction) {
-    initFuncName = "_";
-    initFuncName += args[0];
-    initFuncName += "Init";
+    initFuncName = cmStrCat('_', args[0], "Init");
     initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
       cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
   }
   // if the symbol is found call it to set the name on the
   // function blocker
   if (initFunction) {
-    // create a function blocker and set it up
-    cmLoadedCommand* f = new cmLoadedCommand();
-    (*initFunction)(&f->info);
-    this->Makefile->GetState()->AddScriptedCommand(args[0], f);
+    status.GetMakefile().GetState()->AddScriptedCommand(
+      args[0],
+      cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)));
     return true;
   }
-  this->SetError("Attempt to load command failed. "
-                 "No init function found.");
+  status.SetError("Attempt to load command failed. "
+                  "No init function found.");
   return false;
 }
diff --git a/Source/cmLoadCommandCommand.h b/Source/cmLoadCommandCommand.h
index 021e6c7..f5fd754 100644
--- a/Source/cmLoadCommandCommand.h
+++ b/Source/cmLoadCommandCommand.h
@@ -8,16 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmLoadCommandCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmLoadCommandCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmLoadCommandCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmLocalCommonGenerator.cxx b/Source/cmLocalCommonGenerator.cxx
index 75ad2a6..f86955d 100644
--- a/Source/cmLocalCommonGenerator.cxx
+++ b/Source/cmLocalCommonGenerator.cxx
@@ -8,6 +8,7 @@
 #include "cmGeneratorTarget.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
+#include "cmStringAlgorithms.h"
 
 class cmGlobalGenerator;
 
@@ -34,10 +35,8 @@
   std::string flags;
 
   // Enable module output if necessary.
-  if (const char* modout_flag =
-        this->Makefile->GetDefinition("CMAKE_Fortran_MODOUT_FLAG")) {
-    this->AppendFlags(flags, modout_flag);
-  }
+  this->AppendFlags(
+    flags, this->Makefile->GetSafeDefinition("CMAKE_Fortran_MODOUT_FLAG"));
 
   // Add a module output directory flag if necessary.
   std::string mod_dir =
@@ -51,9 +50,9 @@
       this->Makefile->GetSafeDefinition("CMAKE_Fortran_MODDIR_DEFAULT");
   }
   if (!mod_dir.empty()) {
-    std::string modflag =
-      this->Makefile->GetRequiredDefinition("CMAKE_Fortran_MODDIR_FLAG");
-    modflag += mod_dir;
+    std::string modflag = cmStrCat(
+      this->Makefile->GetRequiredDefinition("CMAKE_Fortran_MODDIR_FLAG"),
+      mod_dir);
     this->AppendFlags(flags, modflag);
   }
 
@@ -65,8 +64,9 @@
     std::vector<std::string> includes;
     this->GetIncludeDirectories(includes, target, "C", config);
     for (std::string const& id : includes) {
-      std::string flg = modpath_flag;
-      flg += this->ConvertToOutputFormat(id, cmOutputConverter::SHELL);
+      std::string flg =
+        cmStrCat(modpath_flag,
+                 this->ConvertToOutputFormat(id, cmOutputConverter::SHELL));
       this->AppendFlags(flags, flg);
     }
   }
diff --git a/Source/cmLocalCommonGenerator.h b/Source/cmLocalCommonGenerator.h
index abebbc2..eaef6ab 100644
--- a/Source/cmLocalCommonGenerator.h
+++ b/Source/cmLocalCommonGenerator.h
@@ -25,7 +25,7 @@
                          std::string wd);
   ~cmLocalCommonGenerator() override;
 
-  std::string const& GetConfigName() { return this->ConfigName; }
+  std::string const& GetConfigName() const { return this->ConfigName; }
 
   std::string GetWorkingDirectory() const { return this->WorkingDirectory; }
 
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 3abf2dd..a2eb1b9 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -2,9 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalGenerator.h"
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
+#include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
@@ -14,34 +19,40 @@
 #include "cmInstallScriptGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
+#include "cmLinkLineDeviceComputer.h"
 #include "cmMakefile.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
+#include "cmSourceFileLocationKind.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTestGenerator.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  define CM_LG_ENCODE_OBJECT_NAMES
 #  include "cmCryptoHash.h"
 #endif
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <initializer_list>
 #include <iterator>
 #include <sstream>
-#include <stdio.h>
-#include <string.h>
 #include <unordered_set>
 #include <utility>
+#include <vector>
+
+#include <cm/string_view>
 
 #if defined(__HAIKU__)
 #  include <FindDirectory.h>
@@ -251,14 +262,6 @@
 
 void cmLocalGenerator::TraceDependencies()
 {
-  std::vector<std::string> configs;
-  this->Makefile->GetConfigurations(configs);
-  if (configs.empty()) {
-    configs.emplace_back();
-  }
-  for (std::string const& c : configs) {
-    this->GlobalGenerator->CreateEvaluationSourceFiles(c);
-  }
   // Generate the rule files for each target.
   const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
   for (cmGeneratorTarget* target : targets) {
@@ -280,9 +283,9 @@
   const std::string& config =
     this->Makefile->GetConfigurations(configurationTypes, false);
 
-  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
-  file += "/";
-  file += "CTestTestfile.cmake";
+  std::string file =
+    cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(),
+             "/CTestTestfile.cmake");
 
   cmGeneratedFileStream fout(file);
   fout.SetCopyIfDifferent(true);
@@ -307,8 +310,7 @@
   const char* testIncludeFiles =
     this->Makefile->GetProperty("TEST_INCLUDE_FILES");
   if (testIncludeFiles) {
-    std::vector<std::string> includesList;
-    cmSystemTools::ExpandListArgument(testIncludeFiles, includesList);
+    std::vector<std::string> includesList = cmExpandedList(testIncludeFiles);
     for (std::string const& i : includesList) {
       fout << "include(\"" << i << "\")" << std::endl;
     }
@@ -321,7 +323,7 @@
     tester->Compute(this);
     tester->Generate(fout, config, configurationTypes);
   }
-  typedef std::vector<cmStateSnapshot> vec_t;
+  using vec_t = std::vector<cmStateSnapshot>;
   vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
   std::string parentBinDir = this->GetCurrentBinaryDirectory();
   for (cmStateSnapshot const& i : children) {
@@ -352,6 +354,15 @@
   }
 }
 
+void cmLocalGenerator::CreateEvaluationFileOutputs()
+{
+  std::vector<std::string> const& configs =
+    this->Makefile->GetGeneratorConfigs();
+  for (std::string const& c : configs) {
+    this->CreateEvaluationFileOutputs(c);
+  }
+}
+
 void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
 {
   std::vector<cmGeneratorExpressionEvaluationFile*> ef =
@@ -645,8 +656,7 @@
 cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
   const std::string& name) const
 {
-  GeneratorTargetMap::const_iterator ti =
-    this->GeneratorTargetSearchIndex.find(name);
+  auto ti = this->GeneratorTargetSearchIndex.find(name);
   if (ti != this->GeneratorTargetSearchIndex.end()) {
     return ti->second;
   }
@@ -742,12 +752,10 @@
   OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL;
   std::ostringstream includeFlags;
 
-  std::string flagVar = "CMAKE_INCLUDE_FLAG_";
-  flagVar += lang;
-  std::string const& includeFlag = this->Makefile->GetSafeDefinition(flagVar);
-  flagVar = "CMAKE_INCLUDE_FLAG_SEP_";
-  flagVar += lang;
-  const char* sep = this->Makefile->GetDefinition(flagVar);
+  std::string const& includeFlag =
+    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
+  const char* sep =
+    this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang));
   bool quotePaths = false;
   if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
     quotePaths = true;
@@ -764,23 +772,16 @@
 
   // Support special system include flag if it is available and the
   // normal flag is repeated for each directory.
-  std::string sysFlagVar = "CMAKE_INCLUDE_SYSTEM_FLAG_";
-  sysFlagVar += lang;
   const char* sysIncludeFlag = nullptr;
   if (repeatFlag) {
-    sysIncludeFlag = this->Makefile->GetDefinition(sysFlagVar);
+    sysIncludeFlag = this->Makefile->GetDefinition(
+      cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
   }
 
-  std::string fwSearchFlagVar = "CMAKE_";
-  fwSearchFlagVar += lang;
-  fwSearchFlagVar += "_FRAMEWORK_SEARCH_FLAG";
-  const char* fwSearchFlag = this->Makefile->GetDefinition(fwSearchFlagVar);
-
-  std::string sysFwSearchFlagVar = "CMAKE_";
-  sysFwSearchFlagVar += lang;
-  sysFwSearchFlagVar += "_SYSTEM_FRAMEWORK_SEARCH_FLAG";
-  const char* sysFwSearchFlag =
-    this->Makefile->GetDefinition(sysFwSearchFlagVar);
+  const char* fwSearchFlag = this->Makefile->GetDefinition(
+    cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
+  const char* sysFwSearchFlag = this->Makefile->GetDefinition(
+    cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
 
   bool flagUsed = false;
   std::set<std::string> emitted;
@@ -790,9 +791,8 @@
   for (std::string const& i : includes) {
     if (fwSearchFlag && *fwSearchFlag && this->Makefile->IsOn("APPLE") &&
         cmSystemTools::IsPathToFramework(i)) {
-      std::string frameworkDir = i;
-      frameworkDir += "/../";
-      frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
+      std::string const frameworkDir =
+        cmSystemTools::CollapseFullPath(cmStrCat(i, "/../"));
       if (emitted.insert(frameworkDir).second) {
         if (sysFwSearchFlag && target &&
             target->IsSystemIncludeDirectory(i, config, lang)) {
@@ -839,29 +839,50 @@
                                          const std::string& lang,
                                          const std::string& config)
 {
+  std::vector<BT<std::string>> tmpFlags;
+  this->AddCompileOptions(tmpFlags, target, lang, config);
+  this->AppendFlags(flags, tmpFlags);
+}
+
+void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags,
+                                         cmGeneratorTarget* target,
+                                         const std::string& lang,
+                                         const std::string& config)
+{
   std::string langFlagRegexVar = std::string("CMAKE_") + lang + "_FLAG_REGEX";
 
   if (const char* langFlagRegexStr =
         this->Makefile->GetDefinition(langFlagRegexVar)) {
     // Filter flags acceptable to this language.
-    std::vector<std::string> opts;
     if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) {
+      std::vector<std::string> opts;
       cmSystemTools::ParseWindowsCommandLine(targetFlags, opts);
+      // Re-escape these flags since COMPILE_FLAGS were already parsed
+      // as a command line above.
+      std::string compileOpts;
+      this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr);
+      if (!compileOpts.empty()) {
+        flags.emplace_back(std::move(compileOpts));
+      }
     }
-    target->GetCompileOptions(opts, config, lang);
-    // (Re-)Escape these flags.  COMPILE_FLAGS were already parsed
-    // as a command line above, and COMPILE_OPTIONS are escaped.
-    this->AppendCompileOptions(flags, opts, langFlagRegexStr);
+    std::vector<BT<std::string>> targetCompileOpts =
+      target->GetCompileOptions(config, lang);
+    // COMPILE_OPTIONS are escaped.
+    this->AppendCompileOptions(flags, targetCompileOpts, langFlagRegexStr);
   } else {
     // Use all flags.
     if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) {
       // COMPILE_FLAGS are not escaped for historical reasons.
-      this->AppendFlags(flags, targetFlags);
+      std::string compileFlags;
+      this->AppendFlags(compileFlags, targetFlags);
+      if (!compileFlags.empty()) {
+        flags.emplace_back(std::move(compileFlags));
+      }
     }
-    std::vector<std::string> opts;
-    target->GetCompileOptions(opts, config, lang);
+    std::vector<BT<std::string>> targetCompileOpts =
+      target->GetCompileOptions(config, lang);
     // COMPILE_OPTIONS are escaped.
-    this->AppendCompileOptions(flags, opts);
+    this->AppendCompileOptions(flags, targetCompileOpts);
   }
 
   for (auto const& it : target->GetMaxLanguageStandards()) {
@@ -888,7 +909,12 @@
       return;
     }
   }
-  this->AddCompilerRequirementFlag(flags, target, lang);
+
+  std::string compReqFlag;
+  this->AddCompilerRequirementFlag(compReqFlag, target, lang);
+  if (!compReqFlag.empty()) {
+    flags.emplace_back(std::move(compReqFlag));
+  }
 
   // Add compile flag for the MSVC compiler only.
   cmMakefile* mf = this->GetMakefile();
@@ -903,14 +929,15 @@
       // to ON
       if (char const* jmcExprGen =
             target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(jmcExprGen);
-        std::string isJMCEnabled = cge->Evaluate(this, config);
-        if (cmSystemTools::IsOn(isJMCEnabled)) {
-          std::vector<std::string> optVec;
-          cmSystemTools::ExpandListArgument(jmc, optVec);
-          this->AppendCompileOptions(flags, optVec);
+        std::string isJMCEnabled =
+          cmGeneratorExpression::Evaluate(jmcExprGen, this, config);
+        if (cmIsOn(isJMCEnabled)) {
+          std::vector<std::string> optVec = cmExpandedList(jmc);
+          std::string jmcFlags;
+          this->AppendCompileOptions(jmcFlags, optVec);
+          if (!jmcFlags.empty()) {
+            flags.emplace_back(std::move(jmcFlags));
+          }
         }
       }
     }
@@ -960,11 +987,9 @@
   // These are intended to simulate additional implicit include directories.
   std::vector<std::string> userStandardDirs;
   {
-    std::string key = "CMAKE_";
-    key += lang;
-    key += "_STANDARD_INCLUDE_DIRECTORIES";
-    std::string const value = this->Makefile->GetSafeDefinition(key);
-    cmSystemTools::ExpandListArgument(value, userStandardDirs);
+    std::string const value = this->Makefile->GetSafeDefinition(
+      cmStrCat("CMAKE_", lang, "_STANDARD_INCLUDE_DIRECTORIES"));
+    cmExpandList(value, userStandardDirs);
     for (std::string& usd : userStandardDirs) {
       cmSystemTools::ConvertToUnixSlashes(usd);
     }
@@ -986,12 +1011,11 @@
     // * Compilers like gfortran do not search their own implicit include
     //   directories for modules ('.mod' files).
     if (lang != "Fortran") {
-      std::string key = "CMAKE_";
-      key += lang;
-      key += "_IMPLICIT_INCLUDE_DIRECTORIES";
-      if (const char* value = this->Makefile->GetDefinition(key)) {
+      const char* value = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "_IMPLICIT_INCLUDE_DIRECTORIES"));
+      if (value != nullptr) {
         size_t const impDirVecOldSize = impDirVec.size();
-        cmSystemTools::ExpandListArgument(value, impDirVec);
+        cmExpandList(value, impDirVec);
         // FIXME: Use cmRange with 'advance()' when it supports non-const.
         for (size_t i = impDirVecOldSize; i < impDirVec.size(); ++i) {
           cmSystemTools::ConvertToUnixSlashes(impDirVec[i]);
@@ -1006,8 +1030,7 @@
     // directly.  In this case adding -I/usr/include can hide SDK headers so we
     // must still exclude it.
     if ((lang == "C" || lang == "CXX" || lang == "CUDA") &&
-        std::find(impDirVec.begin(), impDirVec.end(), "/usr/include") ==
-          impDirVec.end() &&
+        !cmContains(impDirVec, "/usr/include") &&
         std::find_if(impDirVec.begin(), impDirVec.end(),
                      [](std::string const& d) {
                        return cmHasLiteralSuffix(d, "/usr/include");
@@ -1017,7 +1040,7 @@
     }
 
     for (std::string const& i : impDirVec) {
-      if (implicitSet.insert(i).second) {
+      if (implicitSet.insert(cmSystemTools::GetRealPath(i)).second) {
         implicitDirs.emplace_back(i);
       }
     }
@@ -1028,14 +1051,13 @@
                       &lang](std::string const& dir) {
     return (
       // Do not exclude directories that are not in an excluded set.
-      ((implicitSet.find(dir) == implicitSet.end()) &&
-       (implicitExclude.find(dir) == implicitExclude.end()))
+      ((!cmContains(implicitSet, cmSystemTools::GetRealPath(dir))) &&
+       (!cmContains(implicitExclude, dir)))
       // Do not exclude entries of the CPATH environment variable even though
       // they are implicitly searched by the compiler.  They are meant to be
       // user-specified directories that can be re-ordered or converted to
       // -isystem without breaking real compiler builtin headers.
-      || ((lang == "C" || lang == "CXX") &&
-          (this->EnvCPATH.find(dir) != this->EnvCPATH.end())));
+      || ((lang == "C" || lang == "CXX") && cmContains(this->EnvCPATH, dir)));
   };
 
   // Get the target-specific include directories.
@@ -1045,8 +1067,8 @@
   // Support putting all the in-project include directories first if
   // it is requested by the project.
   if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) {
-    std::string const &topSourceDir = this->GetState()->GetSourceDirectory(),
-                      &topBinaryDir = this->GetState()->GetBinaryDirectory();
+    std::string const& topSourceDir = this->GetState()->GetSourceDirectory();
+    std::string const& topBinaryDir = this->GetState()->GetBinaryDirectory();
     for (BT<std::string> const& udr : userDirs) {
       // Emit this directory only if it is a subdirectory of the
       // top-level source or binary tree.
@@ -1082,7 +1104,7 @@
   if (!stripImplicitDirs) {
     // Append implicit directories that were requested by the user only
     for (BT<std::string> const& udr : userDirs) {
-      if (implicitSet.find(udr.Value) != implicitSet.end()) {
+      if (cmContains(implicitSet, cmSystemTools::GetRealPath(udr.Value))) {
         emitBT(udr);
       }
     }
@@ -1130,22 +1152,49 @@
                                              std::string const& linkLanguage,
                                              cmGeneratorTarget* target)
 {
-  this->AppendFlags(
-    flags, this->Makefile->GetSafeDefinition("CMAKE_STATIC_LINKER_FLAGS"));
-  if (!config.empty()) {
-    std::string name = "CMAKE_STATIC_LINKER_FLAGS_" + config;
-    this->AppendFlags(flags, this->Makefile->GetSafeDefinition(name));
-  }
-  this->AppendFlags(flags, target->GetProperty("STATIC_LIBRARY_FLAGS"));
-  if (!config.empty()) {
-    std::string name = "STATIC_LIBRARY_FLAGS_" + config;
-    this->AppendFlags(flags, target->GetProperty(name));
+  std::vector<BT<std::string>> tmpFlags =
+    this->GetStaticLibraryFlags(config, linkLanguage, target);
+  this->AppendFlags(flags, tmpFlags);
+}
+
+std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags(
+  std::string const& config, std::string const& linkLanguage,
+  cmGeneratorTarget* target)
+{
+  std::vector<BT<std::string>> flags;
+  if (linkLanguage != "Swift") {
+    std::string staticLibFlags;
+    this->AppendFlags(
+      staticLibFlags,
+      this->Makefile->GetSafeDefinition("CMAKE_STATIC_LINKER_FLAGS"));
+    if (!config.empty()) {
+      std::string name = "CMAKE_STATIC_LINKER_FLAGS_" + config;
+      this->AppendFlags(staticLibFlags,
+                        this->Makefile->GetSafeDefinition(name));
+    }
+    if (!staticLibFlags.empty()) {
+      flags.emplace_back(std::move(staticLibFlags));
+    }
   }
 
-  std::vector<std::string> options;
-  target->GetStaticLibraryLinkOptions(options, config, linkLanguage);
+  std::string staticLibFlags;
+  this->AppendFlags(staticLibFlags,
+                    target->GetSafeProperty("STATIC_LIBRARY_FLAGS"));
+  if (!config.empty()) {
+    std::string name = "STATIC_LIBRARY_FLAGS_" + config;
+    this->AppendFlags(staticLibFlags, target->GetSafeProperty(name));
+  }
+
+  if (!staticLibFlags.empty()) {
+    flags.emplace_back(std::move(staticLibFlags));
+  }
+
+  std::vector<BT<std::string>> staticLibOpts =
+    target->GetStaticLibraryLinkOptions(config, linkLanguage);
   // STATIC_LIBRARY_OPTIONS are escaped.
-  this->AppendCompileOptions(flags, options);
+  this->AppendCompileOptions(flags, staticLibOpts);
+
+  return flags;
 }
 
 void cmLocalGenerator::GetTargetFlags(
@@ -1153,6 +1202,22 @@
   std::string& linkLibs, std::string& flags, std::string& linkFlags,
   std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target)
 {
+  std::vector<BT<std::string>> linkFlagsList;
+  std::vector<BT<std::string>> linkPathList;
+  std::vector<BT<std::string>> linkLibsList;
+  this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags,
+                       linkFlagsList, frameworkPath, linkPathList, target);
+  this->AppendFlags(linkFlags, linkFlagsList);
+  this->AppendFlags(linkPath, linkPathList);
+  this->AppendFlags(linkLibs, linkLibsList);
+}
+
+void cmLocalGenerator::GetTargetFlags(
+  cmLinkLineComputer* linkLineComputer, const std::string& config,
+  std::vector<BT<std::string>>& linkLibs, std::string& flags,
+  std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
+  std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target)
+{
   const std::string buildType = cmSystemTools::UpperCase(config);
   cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
   const char* libraryLinkVariable =
@@ -1163,21 +1228,26 @@
 
   switch (target->GetType()) {
     case cmStateEnums::STATIC_LIBRARY:
-      this->GetStaticLibraryFlags(linkFlags, buildType, linkLanguage, target);
+      linkFlags = this->GetStaticLibraryFlags(buildType, linkLanguage, target);
+      if (pcli && dynamic_cast<cmLinkLineDeviceComputer*>(linkLineComputer)) {
+        // Compute the required cuda device link libraries when
+        // resolving cuda device symbols
+        this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
+                                  frameworkPath, linkPath);
+      }
       break;
     case cmStateEnums::MODULE_LIBRARY:
       libraryLinkVariable = "CMAKE_MODULE_LINKER_FLAGS";
       CM_FALLTHROUGH;
     case cmStateEnums::SHARED_LIBRARY: {
+      std::string sharedLibFlags;
       if (linkLanguage != "Swift") {
-        linkFlags = this->Makefile->GetSafeDefinition(libraryLinkVariable);
-        linkFlags += " ";
+        sharedLibFlags = cmStrCat(
+          this->Makefile->GetSafeDefinition(libraryLinkVariable), ' ');
         if (!buildType.empty()) {
-          std::string build = libraryLinkVariable;
-          build += "_";
-          build += buildType;
-          linkFlags += this->Makefile->GetSafeDefinition(build);
-          linkFlags += " ";
+          std::string build = cmStrCat(libraryLinkVariable, '_', buildType);
+          sharedLibFlags += this->Makefile->GetSafeDefinition(build);
+          sharedLibFlags += " ";
         }
         if (this->Makefile->IsOn("WIN32") &&
             !(this->Makefile->IsOn("CYGWIN") ||
@@ -1188,10 +1258,10 @@
             this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG");
           for (cmSourceFile* sf : sources) {
             if (sf->GetExtension() == "def") {
-              linkFlags += defFlag;
-              linkFlags += this->ConvertToOutputFormat(
-                cmSystemTools::CollapseFullPath(sf->GetFullPath()), SHELL);
-              linkFlags += " ";
+              sharedLibFlags += defFlag;
+              sharedLibFlags += this->ConvertToOutputFormat(
+                cmSystemTools::CollapseFullPath(sf->ResolveFullPath()), SHELL);
+              sharedLibFlags += " ";
             }
           }
         }
@@ -1199,38 +1269,40 @@
 
       const char* targetLinkFlags = target->GetProperty("LINK_FLAGS");
       if (targetLinkFlags) {
-        linkFlags += targetLinkFlags;
-        linkFlags += " ";
+        sharedLibFlags += targetLinkFlags;
+        sharedLibFlags += " ";
       }
       if (!buildType.empty()) {
-        std::string configLinkFlags = "LINK_FLAGS_";
-        configLinkFlags += buildType;
-        targetLinkFlags = target->GetProperty(configLinkFlags);
+        targetLinkFlags =
+          target->GetProperty(cmStrCat("LINK_FLAGS_", buildType));
         if (targetLinkFlags) {
-          linkFlags += targetLinkFlags;
-          linkFlags += " ";
+          sharedLibFlags += targetLinkFlags;
+          sharedLibFlags += " ";
         }
       }
 
-      std::vector<std::string> opts;
-      target->GetLinkOptions(opts, config, linkLanguage);
+      if (!sharedLibFlags.empty()) {
+        linkFlags.emplace_back(std::move(sharedLibFlags));
+      }
+
+      std::vector<BT<std::string>> linkOpts =
+        target->GetLinkOptions(config, linkLanguage);
       // LINK_OPTIONS are escaped.
-      this->AppendCompileOptions(linkFlags, opts);
+      this->AppendCompileOptions(linkFlags, linkOpts);
       if (pcli) {
         this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
                                   frameworkPath, linkPath);
       }
     } break;
     case cmStateEnums::EXECUTABLE: {
+      std::string exeFlags;
       if (linkLanguage != "Swift") {
-        linkFlags +=
-          this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS");
-        linkFlags += " ";
+        exeFlags = this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS");
+        exeFlags += " ";
         if (!buildType.empty()) {
-          std::string build = "CMAKE_EXE_LINKER_FLAGS_";
-          build += buildType;
-          linkFlags += this->Makefile->GetSafeDefinition(build);
-          linkFlags += " ";
+          exeFlags += this->Makefile->GetSafeDefinition(
+            cmStrCat("CMAKE_EXE_LINKER_FLAGS_", buildType));
+          exeFlags += " ";
         }
         if (linkLanguage.empty()) {
           cmSystemTools::Error(
@@ -1240,22 +1312,19 @@
         }
 
         if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
-          linkFlags +=
+          exeFlags +=
             this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE");
-          linkFlags += " ";
+          exeFlags += " ";
         } else {
-          linkFlags +=
+          exeFlags +=
             this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE");
-          linkFlags += " ";
+          exeFlags += " ";
         }
 
         if (target->IsExecutableWithExports()) {
-          std::string exportFlagVar = "CMAKE_EXE_EXPORTS_";
-          exportFlagVar += linkLanguage;
-          exportFlagVar += "_FLAG";
-
-          linkFlags += this->Makefile->GetSafeDefinition(exportFlagVar);
-          linkFlags += " ";
+          exeFlags += this->Makefile->GetSafeDefinition(
+            cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG"));
+          exeFlags += " ";
         }
       }
 
@@ -1265,48 +1334,55 @@
                                   frameworkPath, linkPath);
       }
 
-      if (cmSystemTools::IsOn(
-            this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) {
+      if (cmIsOn(this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) {
         std::string sFlagVar = std::string("CMAKE_SHARED_BUILD_") +
           linkLanguage + std::string("_FLAGS");
-        linkFlags += this->Makefile->GetSafeDefinition(sFlagVar);
-        linkFlags += " ";
+        exeFlags += this->Makefile->GetSafeDefinition(sFlagVar);
+        exeFlags += " ";
       }
 
       std::string cmp0065Flags =
         this->GetLinkLibsCMP0065(linkLanguage, *target);
       if (!cmp0065Flags.empty()) {
-        linkFlags += cmp0065Flags;
-        linkFlags += " ";
+        exeFlags += cmp0065Flags;
+        exeFlags += " ";
       }
 
       const char* targetLinkFlags = target->GetProperty("LINK_FLAGS");
       if (targetLinkFlags) {
-        linkFlags += targetLinkFlags;
-        linkFlags += " ";
+        exeFlags += targetLinkFlags;
+        exeFlags += " ";
       }
       if (!buildType.empty()) {
-        std::string configLinkFlags = "LINK_FLAGS_";
-        configLinkFlags += buildType;
-        targetLinkFlags = target->GetProperty(configLinkFlags);
+        targetLinkFlags =
+          target->GetProperty(cmStrCat("LINK_FLAGS_", buildType));
         if (targetLinkFlags) {
-          linkFlags += targetLinkFlags;
-          linkFlags += " ";
+          exeFlags += targetLinkFlags;
+          exeFlags += " ";
         }
       }
 
-      std::vector<std::string> opts;
-      target->GetLinkOptions(opts, config, linkLanguage);
+      if (!exeFlags.empty()) {
+        linkFlags.emplace_back(std::move(exeFlags));
+      }
+
+      std::vector<BT<std::string>> linkOpts =
+        target->GetLinkOptions(config, linkLanguage);
       // LINK_OPTIONS are escaped.
-      this->AppendCompileOptions(linkFlags, opts);
+      this->AppendCompileOptions(linkFlags, linkOpts);
     } break;
     default:
       break;
   }
 
-  this->AppendPositionIndependentLinkerFlags(linkFlags, target, config,
+  std::string extraLinkFlags;
+  this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
                                              linkLanguage);
-  this->AppendIPOLinkerFlags(linkFlags, target, config, linkLanguage);
+  this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
+
+  if (!extraLinkFlags.empty()) {
+    linkFlags.emplace_back(std::move(extraLinkFlags));
+  }
 }
 
 void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target,
@@ -1314,26 +1390,45 @@
                                              std::string const& lang,
                                              std::string& flags)
 {
+  std::vector<BT<std::string>> tmpFlags =
+    this->GetTargetCompileFlags(target, config, lang);
+  this->AppendFlags(flags, tmpFlags);
+}
+
+std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
+  cmGeneratorTarget* target, std::string const& config,
+  std::string const& lang)
+{
+  std::vector<BT<std::string>> flags;
+  std::string compileFlags;
+
   cmMakefile* mf = this->GetMakefile();
 
   // Add language-specific flags.
-  this->AddLanguageFlags(flags, target, lang, config);
+  this->AddLanguageFlags(compileFlags, target, lang, config);
 
   if (target->IsIPOEnabled(lang, config)) {
-    this->AppendFeatureOptions(flags, lang, "IPO");
+    this->AppendFeatureOptions(compileFlags, lang, "IPO");
   }
 
-  this->AddArchitectureFlags(flags, target, lang, config);
+  this->AddArchitectureFlags(compileFlags, target, lang, config);
 
   if (lang == "Fortran") {
-    this->AppendFlags(flags, this->GetTargetFortranFlags(target, config));
+    this->AppendFlags(compileFlags,
+                      this->GetTargetFortranFlags(target, config));
   }
 
-  this->AddCMP0018Flags(flags, target, lang, config);
-  this->AddVisibilityPresetFlags(flags, target, lang);
-  this->AppendFlags(flags, mf->GetDefineFlags());
-  this->AppendFlags(flags, this->GetFrameworkFlags(lang, config, target));
+  this->AddCMP0018Flags(compileFlags, target, lang, config);
+  this->AddVisibilityPresetFlags(compileFlags, target, lang);
+  this->AppendFlags(compileFlags, mf->GetDefineFlags());
+  this->AppendFlags(compileFlags,
+                    this->GetFrameworkFlags(lang, config, target));
+
+  if (!compileFlags.empty()) {
+    flags.emplace_back(std::move(compileFlags));
+  }
   this->AddCompileOptions(flags, target, lang, config);
+  return flags;
 }
 
 static std::string GetFrameworkFlags(const std::string& lang,
@@ -1364,8 +1459,7 @@
   // will already have added a -F for the framework
   for (std::string const& include : includes) {
     if (lg->GetGlobalGenerator()->NameResolvesToFramework(include)) {
-      std::string frameworkDir = include;
-      frameworkDir += "/../";
+      std::string frameworkDir = cmStrCat(include, "/../");
       frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
       emitted.insert(frameworkDir);
     }
@@ -1440,6 +1534,19 @@
   std::string& linkLibraries, std::string& frameworkPath,
   std::string& linkPath)
 {
+  std::vector<BT<std::string>> linkLibrariesList;
+  std::vector<BT<std::string>> linkPathList;
+  this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList,
+                            frameworkPath, linkPathList);
+  pcli->AppendValues(linkLibraries, linkLibrariesList);
+  pcli->AppendValues(linkPath, linkPathList);
+}
+
+void cmLocalGenerator::OutputLinkLibraries(
+  cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
+  std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath,
+  std::vector<BT<std::string>>& linkPath)
+{
   cmComputeLinkInformation& cli = *pcli;
 
   std::string linkLanguage = cli.GetLinkLanguage();
@@ -1463,26 +1570,17 @@
   }
 
   // Add standard libraries for this language.
-  std::string standardLibsVar = "CMAKE_";
-  standardLibsVar += cli.GetLinkLanguage();
-  standardLibsVar += "_STANDARD_LIBRARIES";
-  std::string stdLibString;
-  if (const char* stdLibs = this->Makefile->GetDefinition(standardLibsVar)) {
-    stdLibString = stdLibs;
-  }
+  std::string stdLibString = this->Makefile->GetSafeDefinition(
+    cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES"));
 
   // Append the framework search path flags.
-  std::string fwSearchFlagVar = "CMAKE_";
-  fwSearchFlagVar += linkLanguage;
-  fwSearchFlagVar += "_FRAMEWORK_SEARCH_FLAG";
-  std::string fwSearchFlag =
-    this->Makefile->GetSafeDefinition(fwSearchFlagVar);
+  std::string fwSearchFlag = this->Makefile->GetSafeDefinition(
+    cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG"));
 
   frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag);
-  linkPath =
-    linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator);
-
-  linkLibraries = linkLineComputer->ComputeLinkLibraries(cli, stdLibString);
+  linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator,
+                                    linkPath);
+  linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries);
 }
 
 std::string cmLocalGenerator::GetLinkLibsCMP0065(
@@ -1511,8 +1609,10 @@
         }
         CM_FALLTHROUGH;
       case cmPolicies::OLD:
-        // OLD behavior is to always add the flags
-        add_shlib_flags = true;
+        // OLD behavior is to always add the flags, except on AIX where
+        // we compute symbol exports if ENABLE_EXPORTS is on.
+        add_shlib_flags =
+          !(tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"));
         break;
       case cmPolicies::REQUIRED_IF_USED:
       case cmPolicies::REQUIRED_ALWAYS:
@@ -1521,16 +1621,16 @@
           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0065));
         CM_FALLTHROUGH;
       case cmPolicies::NEW:
-        // NEW behavior is to only add the flags if ENABLE_EXPORTS is on
-        add_shlib_flags = tgt.GetPropertyAsBool("ENABLE_EXPORTS");
+        // NEW behavior is to only add the flags if ENABLE_EXPORTS is on,
+        // except on AIX where we compute symbol exports.
+        add_shlib_flags =
+          !tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS");
         break;
     }
 
     if (add_shlib_flags) {
-      std::string linkFlagsVar = "CMAKE_SHARED_LIBRARY_LINK_";
-      linkFlagsVar += linkLanguage;
-      linkFlagsVar += "_FLAGS";
-      linkFlags = this->Makefile->GetSafeDefinition(linkFlagsVar);
+      linkFlags = this->Makefile->GetSafeDefinition(
+        cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", linkLanguage, "_FLAGS"));
     }
   }
   return linkFlags;
@@ -1559,7 +1659,7 @@
     const char* deploymentTargetFlag =
       this->Makefile->GetDefinition(deploymentTargetFlagVar);
     if (!archs.empty() && !lang.empty() &&
-        (lang[0] == 'C' || lang[0] == 'F')) {
+        (lang[0] == 'C' || lang[0] == 'F' || lang[0] == 'O')) {
       for (std::string const& arch : archs) {
         flags += " -arch ";
         flags += arch;
@@ -1588,10 +1688,19 @@
                                         const std::string& config)
 {
   // Add language-specific flags.
-  std::string flagsVar = "CMAKE_";
-  flagsVar += lang;
-  flagsVar += "_FLAGS";
-  this->AddConfigVariableFlags(flags, flagsVar, config);
+  this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"),
+                               config);
+
+  if (lang == "Swift") {
+    if (const char* v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
+      if (cmSystemTools::VersionCompare(
+            cmSystemTools::OP_GREATER_EQUAL,
+            this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
+            "4.2")) {
+        this->AppendFlags(flags, "-swift-version " + std::string(v));
+      }
+    }
+  }
 
   // Add MSVC runtime library flags.  This is activated by the presence
   // of a default selection whether or not it is overridden by a property.
@@ -1603,11 +1712,8 @@
     if (!msvcRuntimeLibraryValue) {
       msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
     }
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(msvcRuntimeLibraryValue);
-    std::string const msvcRuntimeLibrary =
-      cge->Evaluate(this, config, false, target);
+    std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate(
+      msvcRuntimeLibraryValue, this, config, target);
     if (!msvcRuntimeLibrary.empty()) {
       if (const char* msvcRuntimeLibraryOptions =
             this->Makefile->GetDefinition(
@@ -1652,8 +1758,7 @@
 cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse(
   const std::string& name) const
 {
-  GeneratorTargetMap::const_iterator imported =
-    this->ImportedGeneratorTargets.find(name);
+  auto imported = this->ImportedGeneratorTargets.find(name);
   if (imported != this->ImportedGeneratorTargets.end()) {
     return imported->second;
   }
@@ -1743,15 +1848,14 @@
   // Check for a source file in this directory that matches the
   // dependency.
   if (cmSourceFile* sf = this->Makefile->GetSource(inName)) {
-    dep = sf->GetFullPath();
+    dep = sf->ResolveFullPath();
     return true;
   }
 
   // Treat the name as relative to the source directory in which it
   // was given.
-  dep = this->StateSnapshot.GetDirectory().GetCurrentSource();
-  dep += "/";
-  dep += inName;
+  dep = cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentSource(), '/',
+                 inName);
   return true;
 }
 
@@ -1762,10 +1866,9 @@
 
   // Add flags for dealing with shared libraries for this language.
   if (shared) {
-    flagsVar = "CMAKE_SHARED_LIBRARY_";
-    flagsVar += lang;
-    flagsVar += "_FLAGS";
-    this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar));
+    this->AppendFlags(flags,
+                      this->Makefile->GetSafeDefinition(
+                        cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS")));
   }
 }
 
@@ -1784,7 +1887,7 @@
   std::string extProp = lang + "_EXTENSIONS";
   bool ext = true;
   if (const char* extPropValue = target->GetProperty(extProp)) {
-    if (cmSystemTools::IsOff(extPropValue)) {
+    if (cmIsOff(extPropValue)) {
       ext = false;
     }
   }
@@ -1798,8 +1901,7 @@
         "CMAKE_" + lang + "_EXTENSION_COMPILE_OPTION";
       if (const char* opt =
             target->Target->GetMakefile()->GetDefinition(option_flag)) {
-        std::vector<std::string> optVec;
-        cmSystemTools::ExpandListArgument(opt, optVec);
+        std::vector<std::string> optVec = cmExpandedList(opt);
         for (std::string const& i : optVec) {
           this->AppendFlagEscape(flags, i);
         }
@@ -1827,8 +1929,7 @@
            "does not know the compile flags to use to enable it.";
       this->IssueMessage(MessageType::FATAL_ERROR, e.str());
     } else {
-      std::vector<std::string> optVec;
-      cmSystemTools::ExpandListArgument(opt, optVec);
+      std::vector<std::string> optVec = cmExpandedList(opt);
       for (std::string const& i : optVec) {
         this->AppendFlagEscape(flags, i);
       }
@@ -1845,10 +1946,20 @@
     langStdMap["CXX"].emplace_back("11");
     langStdMap["CXX"].emplace_back("98");
 
+    langStdMap["OBJCXX"].emplace_back("20");
+    langStdMap["OBJCXX"].emplace_back("17");
+    langStdMap["OBJCXX"].emplace_back("14");
+    langStdMap["OBJCXX"].emplace_back("11");
+    langStdMap["OBJCXX"].emplace_back("98");
+
     langStdMap["C"].emplace_back("11");
     langStdMap["C"].emplace_back("99");
     langStdMap["C"].emplace_back("90");
 
+    langStdMap["OBJC"].emplace_back("11");
+    langStdMap["OBJC"].emplace_back("99");
+    langStdMap["OBJC"].emplace_back("90");
+
     langStdMap["CUDA"].emplace_back("14");
     langStdMap["CUDA"].emplace_back("11");
     langStdMap["CUDA"].emplace_back("98");
@@ -1858,8 +1969,7 @@
 
   std::vector<std::string>& stds = langStdMap[lang];
 
-  std::vector<std::string>::const_iterator stdIt =
-    std::find(stds.begin(), stds.end(), standard);
+  auto stdIt = std::find(stds.begin(), stds.end(), standard);
   if (stdIt == stds.end()) {
     std::string e =
       lang + "_STANDARD is set to invalid value '" + standard + "'";
@@ -1868,8 +1978,7 @@
     return;
   }
 
-  std::vector<std::string>::const_iterator defaultStdIt =
-    std::find(stds.begin(), stds.end(), defaultStd);
+  auto defaultStdIt = std::find(stds.begin(), stds.end(), defaultStd);
   if (defaultStdIt == stds.end()) {
     std::string e = "CMAKE_" + lang +
       "_STANDARD_DEFAULT is set to invalid value '" + std::string(defaultStd) +
@@ -1888,8 +1997,7 @@
 
     std::string const& opt =
       target->Target->GetMakefile()->GetRequiredDefinition(option_flag);
-    std::vector<std::string> optVec;
-    cmSystemTools::ExpandListArgument(opt, optVec);
+    std::vector<std::string> optVec = cmExpandedList(opt);
     for (std::string const& i : optVec) {
       this->AppendFlagEscape(flags, i);
     }
@@ -1901,12 +2009,11 @@
   // for which a flag is defined.
   for (; stdIt < defaultStdIt; ++stdIt) {
     std::string option_flag =
-      "CMAKE_" + lang + *stdIt + "_" + type + "_COMPILE_OPTION";
+      cmStrCat("CMAKE_", lang, *stdIt, "_", type, "_COMPILE_OPTION");
 
     if (const char* opt =
           target->Target->GetMakefile()->GetDefinition(option_flag)) {
-      std::vector<std::string> optVec;
-      cmSystemTools::ExpandListArgument(opt, optVec);
+      std::vector<std::string> optVec = cmExpandedList(opt);
       for (std::string const& i : optVec) {
         this->AppendFlagEscape(flags, i);
       }
@@ -2054,9 +2161,7 @@
   std::string originalFlags =
     this->GlobalGenerator->GetSharedLibFlagsForLanguage(lang);
   if (shared) {
-    std::string flagsVar = "CMAKE_SHARED_LIBRARY_";
-    flagsVar += lang;
-    flagsVar += "_FLAGS";
+    std::string flagsVar = cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
     std::string const& flags = this->Makefile->GetSafeDefinition(flagsVar);
 
     if (flags != originalFlags) {
@@ -2095,20 +2200,15 @@
   std::string picFlags;
 
   if (targetType == cmStateEnums::EXECUTABLE) {
-    std::string flagsVar = "CMAKE_";
-    flagsVar += lang;
-    flagsVar += "_COMPILE_OPTIONS_PIE";
-    picFlags = this->Makefile->GetSafeDefinition(flagsVar);
+    picFlags = this->Makefile->GetSafeDefinition(
+      cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIE"));
   }
   if (picFlags.empty()) {
-    std::string flagsVar = "CMAKE_";
-    flagsVar += lang;
-    flagsVar += "_COMPILE_OPTIONS_PIC";
-    picFlags = this->Makefile->GetSafeDefinition(flagsVar);
+    picFlags = this->Makefile->GetSafeDefinition(
+      cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIC"));
   }
   if (!picFlags.empty()) {
-    std::vector<std::string> options;
-    cmSystemTools::ExpandListArgument(picFlags, options);
+    std::vector<std::string> options = cmExpandedList(picFlags);
     for (std::string const& o : options) {
       this->AppendFlagEscape(flags, o);
     }
@@ -2120,13 +2220,12 @@
                                               const std::string& config)
 {
   // Add the flags from the variable itself.
-  std::string flagsVar = var;
-  this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar));
+  this->AppendFlags(flags, this->Makefile->GetSafeDefinition(var));
   // Add the flags from the build-type specific variable.
   if (!config.empty()) {
-    flagsVar += "_";
-    flagsVar += cmSystemTools::UpperCase(config);
-    this->AppendFlags(flags, this->Makefile->GetDefinition(flagsVar));
+    const std::string flagsVar =
+      cmStrCat(var, '_', cmSystemTools::UpperCase(config));
+    this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar));
   }
 }
 
@@ -2141,11 +2240,11 @@
   }
 }
 
-void cmLocalGenerator::AppendFlags(std::string& flags,
-                                   const char* newFlags) const
+void cmLocalGenerator::AppendFlags(
+  std::string& flags, const std::vector<BT<std::string>>& newFlags) const
 {
-  if (newFlags && *newFlags) {
-    this->AppendFlags(flags, std::string(newFlags));
+  for (BT<std::string> const& flag : newFlags) {
+    this->AppendFlags(flags, flag.Value);
   }
 }
 
@@ -2155,6 +2254,258 @@
   this->AppendFlags(flags, this->EscapeForShell(rawFlag));
 }
 
+void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
+{
+  // FIXME: Handle all configurations in multi-config generators.
+  std::string config;
+  if (!this->GetGlobalGenerator()->IsMultiConfig()) {
+    config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
+  }
+  const std::string buildType = cmSystemTools::UpperCase(config);
+
+  std::vector<cmSourceFile*> sources;
+  target->GetSourceFiles(sources, buildType);
+
+  for (const std::string& lang : { "C", "CXX" }) {
+    auto langSources =
+      std::count_if(sources.begin(), sources.end(), [lang](cmSourceFile* sf) {
+        return lang == sf->GetLanguage() &&
+          !sf->GetProperty("SKIP_PRECOMPILE_HEADERS");
+      });
+    if (langSources == 0) {
+      continue;
+    }
+
+    const std::string pchSource = target->GetPchSource(config, lang);
+    const std::string pchHeader = target->GetPchHeader(config, lang);
+
+    if (pchSource.empty() || pchHeader.empty()) {
+      continue;
+    }
+
+    const std::string pchExtension =
+      this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+    if (pchExtension.empty()) {
+      continue;
+    }
+
+    const char* pchReuseFrom =
+      target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+
+    auto pch_sf = this->Makefile->GetOrCreateSource(
+      pchSource, false, cmSourceFileLocationKind::Known);
+
+    if (!this->GetGlobalGenerator()->IsXcode()) {
+      if (!pchReuseFrom) {
+        target->AddSource(pchSource, true);
+      }
+
+      const std::string pchFile = target->GetPchFile(config, lang);
+
+      // Exclude the pch files from linking
+      if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+        if (!pchReuseFrom) {
+          pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
+        } else {
+          auto reuseTarget =
+            this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom);
+
+          if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
+
+            const std::string pdb_prefix =
+              this->GetGlobalGenerator()->IsMultiConfig()
+              ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/")
+              : "";
+
+            const std::string target_compile_pdb_dir = cmStrCat(
+              target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/",
+              target->GetName(), ".dir/");
+
+            const std::string copy_script =
+              cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake");
+            cmGeneratedFileStream file(copy_script);
+
+            file << "# CMake generated file\n";
+            for (auto extension : { ".pdb", ".idb" }) {
+              const std::string from_file = cmStrCat(
+                reuseTarget->GetLocalGenerator()->GetCurrentBinaryDirectory(),
+                "/", pchReuseFrom, ".dir/${PDB_PREFIX}", pchReuseFrom,
+                extension);
+
+              const std::string to_dir = cmStrCat(
+                target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/",
+                target->GetName(), ".dir/${PDB_PREFIX}");
+
+              file << "if (EXISTS \"" << from_file << "\")\n";
+              file << "  file(COPY \"" << from_file << "\""
+                   << " DESTINATION \"" << to_dir << "\")\n";
+              file << "endif()\n";
+            }
+
+            cmCustomCommandLines commandLines;
+            cmCustomCommandLine currentLine;
+            currentLine.push_back(cmSystemTools::GetCMakeCommand());
+            currentLine.push_back(cmStrCat("-DPDB_PREFIX=", pdb_prefix));
+            currentLine.push_back("-P");
+            currentLine.push_back(copy_script);
+            commandLines.push_back(std::move(currentLine));
+
+            const std::string no_main_dependency;
+            const std::vector<std::string> no_deps;
+            const char* no_message = "";
+            const char* no_current_dir = nullptr;
+            std::vector<std::string> no_byproducts;
+
+            std::vector<std::string> outputs;
+            outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix,
+                                       pchReuseFrom, ".pdb"));
+
+            if (this->GetGlobalGenerator()->IsMultiConfig()) {
+              this->Makefile->AddCustomCommandToTarget(
+                target->GetName(), outputs, no_deps, commandLines,
+                cmCustomCommandType::PRE_BUILD, no_message, no_current_dir);
+            } else {
+              cmImplicitDependsList no_implicit_depends;
+              cmSourceFile* copy_rule =
+                this->Makefile->AddCustomCommandToOutput(
+                  outputs, no_byproducts, no_deps, no_main_dependency,
+                  no_implicit_depends, commandLines, no_message,
+                  no_current_dir);
+
+              if (copy_rule) {
+                target->AddSource(copy_rule->ResolveFullPath());
+              }
+            }
+
+            target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
+                                        target_compile_pdb_dir.c_str());
+          }
+
+          std::string pchSourceObj =
+            reuseTarget->GetPchFileObject(config, lang);
+
+          // Link to the pch object file
+          target->Target->AppendProperty(
+            "LINK_FLAGS",
+            cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL))
+              .c_str(),
+            true);
+        }
+      } else {
+        pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
+      }
+
+      // Add pchHeader to source files, which will
+      // be grouped as "Precompile Header File"
+      auto pchHeader_sf = this->Makefile->GetOrCreateSource(
+        pchHeader, false, cmSourceFileLocationKind::Known);
+      std::string err;
+      pchHeader_sf->ResolveFullPath(&err);
+      target->AddSource(pchHeader);
+    }
+  }
+}
+
+void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
+{
+  if (!target->GetPropertyAsBool("UNITY_BUILD")) {
+    return;
+  }
+
+  // FIXME: Handle all configurations in multi-config generators.
+  std::string config;
+  if (!this->GetGlobalGenerator()->IsMultiConfig()) {
+    config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
+  }
+
+  const std::string buildType = cmSystemTools::UpperCase(config);
+
+  std::string filename_base =
+    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/",
+             target->GetName(), ".dir/Unity/");
+
+  std::vector<cmSourceFile*> sources;
+  target->GetSourceFiles(sources, buildType);
+
+  auto batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
+  const size_t unityBatchSize =
+    static_cast<size_t>(std::atoi(batchSizeString));
+
+  auto beforeInclude = target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE");
+  auto afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
+
+  for (std::string lang : { "C", "CXX" }) {
+    std::vector<cmSourceFile*> filtered_sources;
+    std::copy_if(sources.begin(), sources.end(),
+                 std::back_inserter(filtered_sources), [&](cmSourceFile* sf) {
+                   return sf->GetLanguage() == lang &&
+                     !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") &&
+                     !sf->GetPropertyAsBool("GENERATED") &&
+                     !sf->GetProperty("COMPILE_OPTIONS") &&
+                     !sf->GetProperty("COMPILE_DEFINITIONS") &&
+                     !sf->GetProperty("COMPILE_FLAGS") &&
+                     !sf->GetProperty("INCLUDE_DIRECTORIES");
+                 });
+
+    size_t batchSize = unityBatchSize;
+    if (unityBatchSize == 0) {
+      batchSize = filtered_sources.size();
+    }
+
+    for (size_t itemsLeft = filtered_sources.size(), chunk = batchSize,
+                batch = 0;
+         itemsLeft > 0; itemsLeft -= chunk, ++batch) {
+
+      chunk = std::min(itemsLeft, batchSize);
+
+      std::string filename = cmStrCat(filename_base, "unity_", batch,
+                                      (lang == "C") ? ".c" : ".cxx");
+
+      const std::string filename_tmp = cmStrCat(filename, ".tmp");
+      {
+        size_t begin = batch * batchSize;
+        size_t end = begin + chunk;
+
+        cmGeneratedFileStream file(
+          filename_tmp, false,
+          this->GetGlobalGenerator()->GetMakefileEncoding());
+        file << "/* generated by CMake */\n\n";
+
+        for (; begin != end; ++begin) {
+          cmSourceFile* sf = filtered_sources[begin];
+
+          // Only in Visual Studio generator we keep the source files
+          // for explicit processing. For the rest the source files will
+          // not be included in the project.
+          if (!this->GetGlobalGenerator()->IsMultiConfig() ||
+              this->GetGlobalGenerator()->IsXcode()) {
+            sf->SetProperty("HEADER_FILE_ONLY", "ON");
+          }
+          sf->SetProperty("UNITY_SOURCE_FILE", filename.c_str());
+
+          if (beforeInclude) {
+            file << beforeInclude << "\n";
+          }
+
+          file << "#include \"" << sf->GetFullPath() << "\"\n";
+
+          if (afterInclude) {
+            file << afterInclude << "\n";
+          }
+        }
+      }
+      cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
+
+      target->AddSource(filename, true);
+
+      auto unity = this->Makefile->GetOrCreateSource(filename);
+      unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
+      unity->SetProperty("UNITY_SOURCE_FILE", filename.c_str());
+    }
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,
@@ -2179,8 +2530,7 @@
     return;
   }
 
-  std::vector<std::string> flagsList;
-  cmSystemTools::ExpandListArgument(rawFlagsList, flagsList);
+  std::vector<std::string> flagsList = cmExpandedList(rawFlagsList);
   for (std::string const& o : flagsList) {
     this->AppendFlagEscape(flags, o);
   }
@@ -2201,10 +2551,10 @@
     return;
   }
 
-  const std::string mode = cmSystemTools::IsOn(PICValue) ? "PIE" : "NO_PIE";
+  const std::string mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE";
 
   std::string supported = "CMAKE_" + lang + "_LINK_" + mode + "_SUPPORTED";
-  if (cmSystemTools::IsOff(this->Makefile->GetDefinition(supported))) {
+  if (cmIsOff(this->Makefile->GetDefinition(supported))) {
     return;
   }
 
@@ -2215,8 +2565,7 @@
     return;
   }
 
-  std::vector<std::string> flagsList;
-  cmSystemTools::ExpandListArgument(pieFlags, flagsList);
+  std::vector<std::string> flagsList = cmExpandedList(pieFlags);
   for (const auto& flag : flagsList) {
     this->AppendFlagEscape(flags, flag);
   }
@@ -2232,8 +2581,7 @@
   }
 
   // Expand the list of options.
-  std::vector<std::string> options_vec;
-  cmSystemTools::ExpandListArgument(options_list, options_vec);
+  std::vector<std::string> options_vec = cmExpandedList(options_list);
   this->AppendCompileOptions(options, options_vec, regex);
 }
 
@@ -2257,6 +2605,30 @@
   }
 }
 
+void cmLocalGenerator::AppendCompileOptions(
+  std::vector<BT<std::string>>& options,
+  const std::vector<BT<std::string>>& options_vec, const char* regex) const
+{
+  if (regex != nullptr) {
+    // Filter flags upon specified regular expressions.
+    cmsys::RegularExpression r(regex);
+
+    for (BT<std::string> const& opt : options_vec) {
+      if (r.find(opt.Value)) {
+        std::string flag;
+        this->AppendFlagEscape(flag, opt.Value);
+        options.emplace_back(std::move(flag), opt.Backtrace);
+      }
+    }
+  } else {
+    for (BT<std::string> const& opt : options_vec) {
+      std::string flag;
+      this->AppendFlagEscape(flag, opt.Value);
+      options.emplace_back(std::move(flag), opt.Backtrace);
+    }
+  }
+}
+
 void cmLocalGenerator::AppendIncludeDirectories(
   std::vector<std::string>& includes, const char* includes_list,
   const cmSourceFile& sourceFile) const
@@ -2267,8 +2639,7 @@
   }
 
   // Expand the list of includes.
-  std::vector<std::string> includes_vec;
-  cmSystemTools::ExpandListArgument(includes_list, includes_vec);
+  std::vector<std::string> includes_vec = cmExpandedList(includes_list);
   this->AppendIncludeDirectories(includes, includes_vec, sourceFile);
 }
 
@@ -2293,7 +2664,7 @@
 
     std::string inc = include;
 
-    if (!cmSystemTools::IsOff(inc)) {
+    if (!cmIsOff(inc)) {
       cmSystemTools::ConvertToUnixSlashes(inc);
     }
 
@@ -2345,10 +2716,8 @@
   // Lookup the define flag for the current language.
   std::string dflag = "-D";
   if (!lang.empty()) {
-    std::string defineFlagVar = "CMAKE_";
-    defineFlagVar += lang;
-    defineFlagVar += "_DEFINE_FLAG";
-    const char* df = this->Makefile->GetDefinition(defineFlagVar);
+    const char* df =
+      this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG"));
     if (df && *df) {
       dflag = df;
     }
@@ -2394,13 +2763,10 @@
                                             const std::string& lang,
                                             const char* feature)
 {
-  std::string optVar = "CMAKE_";
-  optVar += lang;
-  optVar += "_COMPILE_OPTIONS_";
-  optVar += feature;
-  if (const char* optionList = this->Makefile->GetDefinition(optVar)) {
-    std::vector<std::string> options;
-    cmSystemTools::ExpandListArgument(optionList, options);
+  const char* optionList = this->Makefile->GetDefinition(
+    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature));
+  if (optionList != nullptr) {
+    std::vector<std::string> options = cmExpandedList(optionList);
     for (std::string const& o : options) {
       this->AppendFlagEscape(flags, o);
     }
@@ -2555,8 +2921,8 @@
     objName.find('/', objName.size() - max_len + 32);
   if (pos != std::string::npos) {
     cmCryptoHash md5(cmCryptoHash::AlgoMD5);
-    std::string md5name = md5.HashString(objName.substr(0, pos));
-    md5name += objName.substr(pos);
+    std::string md5name = cmStrCat(md5.HashString(objName.substr(0, pos)),
+                                   cm::string_view(objName).substr(pos));
     objName = md5name;
 
     // The object name is now short enough.
@@ -2591,8 +2957,7 @@
   const std::string& sin, std::string const& dir_max)
 {
   // Look for an existing mapped name for this object file.
-  std::map<std::string, std::string>::iterator it =
-    this->UniqueObjectNamesMap.find(sin);
+  auto it = this->UniqueObjectNamesMap.find(sin);
 
   // If no entry exists create one.
   if (it == this->UniqueObjectNamesMap.end()) {
@@ -2751,13 +3116,16 @@
     if (!replaceExt) {
       std::string lang = source.GetLanguage();
       if (!lang.empty()) {
-        std::string repVar = "CMAKE_";
-        repVar += lang;
-        repVar += "_OUTPUT_EXTENSION_REPLACE";
-        replaceExt = this->Makefile->IsOn(repVar);
+        replaceExt = this->Makefile->IsOn(
+          cmStrCat("CMAKE_", lang, "_OUTPUT_EXTENSION_REPLACE"));
       }
     }
 
+    const char* pchExtension = source.GetProperty("PCH_EXTENSION");
+    if (pchExtension) {
+      customOutputExtension = pchExtension;
+    }
+
     // Remove the source extension if it is to be replaced.
     if (replaceExt || customOutputExtension) {
       keptSourceExtension = false;
@@ -2963,7 +3331,7 @@
   // back to the directory-level values set by the user.
   cmMakefile* mf = this->Makefile;
   cmMakefile::ScopePushPop varScope(mf);
-  mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName.c_str());
+  mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName);
   cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING");
   cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE");
   cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER");
@@ -3002,7 +3370,7 @@
   // back to the directory-level values set by the user.
   cmMakefile* mf = this->Makefile;
   cmMakefile::ScopePushPop varScope(mf);
-  mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName.c_str());
+  mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName);
   cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE");
   cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER");
   cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING");
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index f0c6806..12359db 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -5,7 +5,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_kwiml.h"
 #include <iosfwd>
 #include <map>
 #include <set>
@@ -13,6 +12,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include "cm_kwiml.h"
+
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
@@ -121,9 +122,12 @@
   //! Append flags to a string.
   virtual void AppendFlags(std::string& flags,
                            const std::string& newFlags) const;
-  virtual void AppendFlags(std::string& flags, const char* newFlags) const;
+  virtual void AppendFlags(std::string& flags,
+                           const std::vector<BT<std::string>>& newFlags) const;
   virtual void AppendFlagEscape(std::string& flags,
                                 const std::string& rawFlag) const;
+  void AddPchDependencies(cmGeneratorTarget* target);
+  void AddUnityBuild(cmGeneratorTarget* target);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);
@@ -195,6 +199,9 @@
   void AppendCompileOptions(std::string& options,
                             const std::vector<std::string>& options_vec,
                             const char* regex = nullptr) const;
+  void AppendCompileOptions(std::vector<BT<std::string>>& options,
+                            const std::vector<BT<std::string>>& options_vec,
+                            const char* regex = nullptr) const;
 
   /**
    * Join a set of defines into a definesString with a space separator.
@@ -281,6 +288,9 @@
 
   void AddCompileOptions(std::string& flags, cmGeneratorTarget* target,
                          const std::string& lang, const std::string& config);
+  void AddCompileOptions(std::vector<BT<std::string>>& flags,
+                         cmGeneratorTarget* target, const std::string& lang,
+                         const std::string& config);
 
   std::string GetProjectName() const;
 
@@ -361,6 +371,9 @@
   void GetStaticLibraryFlags(std::string& flags, std::string const& config,
                              std::string const& linkLanguage,
                              cmGeneratorTarget* target);
+  std::vector<BT<std::string>> GetStaticLibraryFlags(
+    std::string const& config, std::string const& linkLanguage,
+    cmGeneratorTarget* target);
 
   /** Fill out these strings for the given target.  Libraries to link,
    *  flags, and linkflags. */
@@ -369,6 +382,11 @@
                       std::string& flags, std::string& linkFlags,
                       std::string& frameworkPath, std::string& linkPath,
                       cmGeneratorTarget* target);
+  void GetTargetFlags(
+    cmLinkLineComputer* linkLineComputer, const std::string& config,
+    std::vector<BT<std::string>>& linkLibs, std::string& flags,
+    std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
+    std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target);
   void GetTargetDefines(cmGeneratorTarget const* target,
                         std::string const& config, std::string const& lang,
                         std::set<std::string>& defines) const;
@@ -378,6 +396,9 @@
   void GetTargetCompileFlags(cmGeneratorTarget* target,
                              std::string const& config,
                              std::string const& lang, std::string& flags);
+  std::vector<BT<std::string>> GetTargetCompileFlags(cmGeneratorTarget* target,
+                                                     std::string const& config,
+                                                     std::string const& lang);
 
   std::string GetFrameworkFlags(std::string const& l,
                                 std::string const& config,
@@ -396,6 +417,7 @@
 
   void IssueMessage(MessageType t, std::string const& text) const;
 
+  void CreateEvaluationFileOutputs();
   void CreateEvaluationFileOutputs(const std::string& config);
   void ProcessEvaluationFiles(std::vector<std::string>& generatedFiles);
 
@@ -408,6 +430,11 @@
                            cmLinkLineComputer* linkLineComputer,
                            std::string& linkLibraries,
                            std::string& frameworkPath, std::string& linkPath);
+  void OutputLinkLibraries(cmComputeLinkInformation* pcli,
+                           cmLinkLineComputer* linkLineComputer,
+                           std::vector<BT<std::string>>& linkLibraries,
+                           std::string& frameworkPath,
+                           std::vector<BT<std::string>>& linkPath);
 
   // Handle old-style install rules stored in the targets.
   void GenerateTargetInstallRules(
@@ -431,8 +458,8 @@
 
   std::set<std::string> EnvCPATH;
 
-  typedef std::unordered_map<std::string, cmGeneratorTarget*>
-    GeneratorTargetMap;
+  using GeneratorTargetMap =
+    std::unordered_map<std::string, cmGeneratorTarget*>;
   GeneratorTargetMap GeneratorTargetSearchIndex;
   std::vector<cmGeneratorTarget*> GeneratorTargets;
 
@@ -461,7 +488,7 @@
   void ComputeObjectMaxPath();
 };
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 bool cmLocalGeneratorCheckObjectName(std::string& objName,
                                      std::string::size_type dir_len,
                                      std::string::size_type max_total_len);
diff --git a/Source/cmLocalGhsMultiGenerator.cxx b/Source/cmLocalGhsMultiGenerator.cxx
index bf25f98..4b10798 100644
--- a/Source/cmLocalGhsMultiGenerator.cxx
+++ b/Source/cmLocalGhsMultiGenerator.cxx
@@ -2,16 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalGhsMultiGenerator.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "cmGeneratorTarget.h"
 #include "cmGhsMultiTargetGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <algorithm>
-#include <utility>
-
 cmLocalGhsMultiGenerator::cmLocalGhsMultiGenerator(cmGlobalGenerator* gg,
                                                    cmMakefile* mf)
   : cmLocalGenerator(gg, mf)
@@ -23,9 +24,7 @@
 std::string cmLocalGhsMultiGenerator::GetTargetDirectory(
   cmGeneratorTarget const* target) const
 {
-  std::string dir;
-  dir += target->GetName();
-  dir += ".dir";
+  std::string dir = cmStrCat(target->GetName(), ".dir");
   return dir;
 }
 
@@ -63,11 +62,8 @@
   std::map<cmSourceFile const*, std::string>& mapping,
   cmGeneratorTarget const* gt)
 {
-  std::string dir_max;
-  dir_max += this->GetCurrentBinaryDirectory();
-  dir_max += "/";
-  dir_max += this->GetTargetDirectory(gt);
-  dir_max += "/";
+  std::string dir_max = cmStrCat(this->GetCurrentBinaryDirectory(), '/',
+                                 this->GetTargetDirectory(gt), '/');
 
   // Count the number of object files with each name.  Note that
   // filesystem may not be case sensitive.
@@ -75,9 +71,10 @@
 
   for (auto const& si : mapping) {
     cmSourceFile const* sf = si.first;
-    std::string objectNameLower = cmSystemTools::LowerCase(
-      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()));
-    objectNameLower += this->GlobalGenerator->GetLanguageOutputExtension(*sf);
+    std::string objectNameLower = cmStrCat(
+      cmSystemTools::LowerCase(
+        cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath())),
+      this->GlobalGenerator->GetLanguageOutputExtension(*sf));
     counts[objectNameLower] += 1;
   }
 
@@ -85,9 +82,9 @@
   // object name computation.
   for (auto& si : mapping) {
     cmSourceFile const* sf = si.first;
-    std::string objectName =
-      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath());
-    objectName += this->GlobalGenerator->GetLanguageOutputExtension(*sf);
+    std::string objectName = cmStrCat(
+      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()),
+      this->GlobalGenerator->GetLanguageOutputExtension(*sf));
 
     if (counts[cmSystemTools::LowerCase(objectName)] > 1) {
       const_cast<cmGeneratorTarget*>(gt)->AddExplicitObjectName(sf);
diff --git a/Source/cmLocalGhsMultiGenerator.h b/Source/cmLocalGhsMultiGenerator.h
index b6ccd08..2250e57 100644
--- a/Source/cmLocalGhsMultiGenerator.h
+++ b/Source/cmLocalGhsMultiGenerator.h
@@ -3,12 +3,12 @@
 #ifndef cmLocalGhsMultiGenerator_h
 #define cmLocalGhsMultiGenerator_h
 
-#include "cmLocalGenerator.h"
-
 #include <map>
 #include <string>
 #include <vector>
 
+#include "cmLocalGenerator.h"
+
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmMakefile;
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 90666fc..134bbe1 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -3,13 +3,15 @@
 #include "cmLocalNinjaGenerator.h"
 
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstdio>
 #include <iterator>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <sstream>
-#include <stdio.h>
 #include <utility>
 
+#include "cmsys/FStream.hxx"
+
 #include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
@@ -25,9 +27,9 @@
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
-#include "cmsys/FStream.hxx"
 
 cmLocalNinjaGenerator::cmLocalNinjaGenerator(cmGlobalGenerator* gg,
                                              cmMakefile* mf)
@@ -87,12 +89,6 @@
     auto tg = cmNinjaTargetGenerator::New(target);
     if (tg) {
       tg->Generate();
-      // Add the target to "all" if required.
-      if (!this->GetGlobalNinjaGenerator()->IsExcluded(
-            this->GetGlobalNinjaGenerator()->GetLocalGenerators()[0],
-            target)) {
-        this->GetGlobalNinjaGenerator()->AddDependencyToAll(target);
-      }
     }
   }
 
@@ -104,8 +100,7 @@
 std::string cmLocalNinjaGenerator::GetTargetDirectory(
   cmGeneratorTarget const* target) const
 {
-  std::string dir = "CMakeFiles/";
-  dir += target->GetName();
+  std::string dir = cmStrCat("CMakeFiles/", target->GetName());
 #if defined(__VMS)
   dir += "_dir";
 #else
@@ -223,8 +218,7 @@
   if (jobpools) {
     cmGlobalNinjaGenerator::WriteComment(
       os, "Pools defined by global property JOB_POOLS");
-    std::vector<std::string> pools;
-    cmSystemTools::ExpandListArgument(jobpools, pools);
+    std::vector<std::string> pools = cmExpandedList(jobpools);
     for (std::string const& pool : pools) {
       const std::string::size_type eq = pool.find('=');
       unsigned int jobs;
@@ -302,8 +296,7 @@
   if (target) {
     scriptPath = target->GetSupportDirectory();
   } else {
-    scriptPath = this->GetCurrentBinaryDirectory();
-    scriptPath += "/CMakeFiles";
+    scriptPath = cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles");
   }
   cmSystemTools::MakeDirectory(scriptPath);
   scriptPath += '/';
@@ -390,8 +383,7 @@
   }
 
   std::ostringstream cmd;
-  for (std::vector<std::string>::const_iterator li = cmdLines.begin();
-       li != cmdLines.end(); ++li)
+  for (auto li = cmdLines.begin(); li != cmdLines.end(); ++li)
 #ifdef _WIN32
   {
     if (li != cmdLines.begin()) {
@@ -535,8 +527,7 @@
 void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements()
 {
   for (cmCustomCommand const* customCommand : this->CustomCommands) {
-    CustomCommandTargetMap::iterator i =
-      this->CustomCommandTargets.find(customCommand);
+    auto i = this->CustomCommandTargets.find(customCommand);
     assert(i != this->CustomCommandTargets.end());
 
     // A custom command may appear on multiple targets.  However, some build
@@ -548,7 +539,7 @@
     //
     // FIXME: This won't work in certain obscure scenarios involving indirect
     // dependencies.
-    std::set<cmGeneratorTarget*>::iterator j = i->second.begin();
+    auto j = i->second.begin();
     assert(j != i->second.end());
     std::vector<std::string> ccTargetDeps;
     this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(*j,
@@ -557,7 +548,8 @@
     ++j;
 
     for (; j != i->second.end(); ++j) {
-      std::vector<std::string> jDeps, depsIntersection;
+      std::vector<std::string> jDeps;
+      std::vector<std::string> depsIntersection;
       this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(*j, jDeps);
       std::sort(jDeps.begin(), jDeps.end());
       std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
@@ -613,12 +605,10 @@
         this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     std::vector<std::string> cleanFiles;
     {
-      cmGeneratorExpression ge;
-      auto cge = ge.Parse(prop_value);
-      cmSystemTools::ExpandListArgument(
-        cge->Evaluate(this,
-                      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")),
-        cleanFiles);
+      cmExpandList(cmGeneratorExpression::Evaluate(
+                     prop_value, this,
+                     this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")),
+                   cleanFiles);
     }
     std::string const& binaryDir = this->GetCurrentBinaryDirectory();
     cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator();
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
index 3a30bbb..f64534c 100644
--- a/Source/cmLocalNinjaGenerator.h
+++ b/Source/cmLocalNinjaGenerator.h
@@ -93,7 +93,6 @@
   void WriteProcessedMakefile(std::ostream& os);
   void WritePools(std::ostream& os);
 
-  void WriteCustomCommandRule();
   void WriteCustomCommandBuildStatement(cmCustomCommand const* cc,
                                         const cmNinjaDeps& orderOnlyDeps);
 
@@ -109,8 +108,8 @@
 
   std::string HomeRelativeOutputPath;
 
-  typedef std::map<cmCustomCommand const*, std::set<cmGeneratorTarget*>>
-    CustomCommandTargetMap;
+  using CustomCommandTargetMap =
+    std::map<cmCustomCommand const*, std::set<cmGeneratorTarget*>>;
   CustomCommandTargetMap CustomCommandTargets;
   std::vector<cmCustomCommand const*> CustomCommands;
 };
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index c392e97..4a70248 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -2,13 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalUnixMakefileGenerator3.h"
 
+#include <algorithm>
+#include <cstdio>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Terminal.h"
-#include <algorithm>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h" // IWYU pragma: keep
@@ -31,6 +33,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmake.h"
@@ -38,7 +41,7 @@
 // Include dependency scanners for supported languages.  Only the
 // C/C++ scanner is needed for bootstrapping CMake.
 #include "cmDependsC.h"
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 #  include "cmDependsFortran.h"
 #  include "cmDependsJava.h"
 #endif
@@ -159,14 +162,10 @@
       continue;
     }
     std::vector<cmSourceFile const*> objectSources;
-    gt->GetObjectSources(
-      objectSources, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
+    gt->GetObjectSources(objectSources, this->ConfigName);
     // Compute full path to object file directory for this target.
-    std::string dir;
-    dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
-    dir += "/";
-    dir += this->GetTargetDirectory(gt);
-    dir += "/";
+    std::string dir = cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(),
+                               '/', this->GetTargetDirectory(gt), '/');
     // Compute the name of each object file.
     for (cmSourceFile const* sf : objectSources) {
       bool hasSourceExtension = true;
@@ -363,14 +362,12 @@
       emitted.insert(target->GetName());
 
       // for subdirs add a rule to build this specific target by name.
-      localName = this->GetRelativeTargetDirectory(target);
-      localName += "/rule";
+      localName = cmStrCat(this->GetRelativeTargetDirectory(target), "/rule");
       commands.clear();
       depends.clear();
 
       // Build the target for this pass.
-      std::string makefile2 = "CMakeFiles/";
-      makefile2 += "Makefile2";
+      std::string makefile2 = "CMakeFiles/Makefile2";
       commands.push_back(this->GetRecursiveMakeCall(makefile2, localName));
       this->CreateCDCommand(commands, this->GetBinaryDirectory(),
                             this->GetCurrentBinaryDirectory());
@@ -386,13 +383,12 @@
       }
 
       // Add a fast rule to build the target
-      std::string makefileName = this->GetRelativeTargetDirectory(target);
-      makefileName += "/build.make";
+      std::string makefileName =
+        cmStrCat(this->GetRelativeTargetDirectory(target), "/build.make");
       // make sure the makefile name is suitable for a makefile
-      std::string makeTargetName = this->GetRelativeTargetDirectory(target);
-      makeTargetName += "/build";
-      localName = target->GetName();
-      localName += "/fast";
+      std::string makeTargetName =
+        cmStrCat(this->GetRelativeTargetDirectory(target), "/build");
+      localName = cmStrCat(target->GetName(), "/fast");
       depends.clear();
       commands.clear();
       commands.push_back(
@@ -405,10 +401,9 @@
       // Add a local name for the rule to relink the target before
       // installation.
       if (target->NeedRelinkBeforeInstall(this->ConfigName)) {
-        makeTargetName = this->GetRelativeTargetDirectory(target);
-        makeTargetName += "/preinstall";
-        localName = target->GetName();
-        localName += "/preinstall";
+        makeTargetName =
+          cmStrCat(this->GetRelativeTargetDirectory(target), "/preinstall");
+        localName = cmStrCat(target->GetName(), "/preinstall");
         depends.clear();
         commands.clear();
         commands.push_back(
@@ -425,9 +420,9 @@
 
 void cmLocalUnixMakefileGenerator3::WriteDirectoryInformationFile()
 {
-  std::string infoFileName = this->GetCurrentBinaryDirectory();
-  infoFileName += "/CMakeFiles";
-  infoFileName += "/CMakeDirectoryInformation.cmake";
+  std::string infoFileName =
+    cmStrCat(this->GetCurrentBinaryDirectory(),
+             "/CMakeFiles/CMakeDirectoryInformation.cmake");
 
   // Open the output file.
   cmGeneratedFileStream infoFileStream(infoFileName);
@@ -483,9 +478,8 @@
 std::string cmLocalUnixMakefileGenerator3::ConvertToFullPath(
   const std::string& localPath)
 {
-  std::string dir = this->GetCurrentBinaryDirectory();
-  dir += "/";
-  dir += localPath;
+  std::string dir =
+    cmStrCat(this->GetCurrentBinaryDirectory(), '/', localPath);
   return dir;
 }
 
@@ -766,19 +760,18 @@
     std::vector<std::string> commands;
     cmake* cm = this->GlobalGenerator->GetCMakeInstance();
     if (cm->DoWriteGlobVerifyTarget()) {
-      std::string rescanRule = "$(CMAKE_COMMAND) -P ";
-      rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
-                                                cmOutputConverter::SHELL);
+      std::string rescanRule =
+        cmStrCat("$(CMAKE_COMMAND) -P ",
+                 this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
+                                             cmOutputConverter::SHELL));
       commands.push_back(rescanRule);
     }
-    std::string cmakefileName = "CMakeFiles/";
-    cmakefileName += "Makefile.cmake";
-    std::string runRule =
-      "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
-    runRule += " --check-build-system ";
-    runRule +=
-      this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL);
-    runRule += " 0";
+    std::string cmakefileName = "CMakeFiles/Makefile.cmake";
+    std::string runRule = cmStrCat(
+      "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) "
+      "--check-build-system ",
+      this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL),
+      " 0");
 
     std::vector<std::string> no_depends;
     commands.push_back(std::move(runRule));
@@ -817,10 +810,10 @@
 }
 
 std::string cmLocalUnixMakefileGenerator3::GetRelativeTargetDirectory(
-  cmGeneratorTarget* target)
+  cmGeneratorTarget const* target) const
 {
-  std::string dir = this->HomeRelativeOutputPath;
-  dir += this->GetTargetDirectory(target);
+  std::string dir =
+    cmStrCat(this->HomeRelativeOutputPath, this->GetTargetDirectory(target));
   return dir;
 }
 
@@ -838,12 +831,6 @@
   this->cmLocalGenerator::AppendFlags(flags, newFlags);
 }
 
-void cmLocalUnixMakefileGenerator3::AppendFlags(std::string& flags,
-                                                const char* newFlags) const
-{
-  this->cmLocalGenerator::AppendFlags(flags, newFlags);
-}
-
 void cmLocalUnixMakefileGenerator3::AppendRuleDepend(
   std::vector<std::string>& depends, const char* ruleFileName)
 {
@@ -851,7 +838,7 @@
   // it is specifically enabled by the user or project.
   const char* nodep =
     this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY");
-  if (!nodep || cmSystemTools::IsOff(nodep)) {
+  if (!nodep || cmIsOff(nodep)) {
     depends.emplace_back(ruleFileName);
   }
 }
@@ -961,7 +948,7 @@
         // This command was specified as a path to a file in the
         // current directory.  Add a leading "./" so it can run
         // without the current directory being in the search path.
-        cmd = "./" + cmd;
+        cmd = cmStrCat("./", cmd);
       }
 
       std::string launcher;
@@ -1015,18 +1002,16 @@
           std::string::size_type rcurly = cmd.find('}');
           if (rcurly == std::string::npos || rcurly > lcurly) {
             // The first curly is a left curly.  Use the hack.
-            std::string hack_cmd = cmd.substr(0, lcurly);
-            hack_cmd += "{{}";
-            hack_cmd += cmd.substr(lcurly + 1);
-            cmd = hack_cmd;
+            cmd =
+              cmStrCat(cmd.substr(0, lcurly), "{{}", cmd.substr(lcurly + 1));
           }
         }
       }
       if (launcher.empty()) {
         if (useCall) {
-          cmd = "call " + cmd;
+          cmd = cmStrCat("call ", cmd);
         } else if (this->IsNMake() && cmd[0] == '"') {
-          cmd = "echo >nul && " + cmd;
+          cmd = cmStrCat("echo >nul && ", cmd);
         }
       }
       commands1.push_back(std::move(cmd));
@@ -1045,10 +1030,8 @@
   cmGeneratorTarget* target, const char* filename)
 {
   std::string currentBinDir = this->GetCurrentBinaryDirectory();
-  std::string cleanfile = currentBinDir;
-  cleanfile += "/";
-  cleanfile += this->GetTargetDirectory(target);
-  cleanfile += "/cmake_clean";
+  std::string cleanfile = cmStrCat(
+    currentBinDir, '/', this->GetTargetDirectory(target), "/cmake_clean");
   if (filename) {
     cleanfile += "_";
     cleanfile += filename;
@@ -1068,11 +1051,12 @@
     fout << ")\n";
   }
   {
-    std::string remove = "$(CMAKE_COMMAND) -P ";
-    remove += this->ConvertToOutputFormat(
-      this->MaybeConvertToRelativePath(this->GetCurrentBinaryDirectory(),
-                                       cleanfile),
-      cmOutputConverter::SHELL);
+    std::string remove =
+      cmStrCat("$(CMAKE_COMMAND) -P ",
+               this->ConvertToOutputFormat(
+                 this->MaybeConvertToRelativePath(
+                   this->GetCurrentBinaryDirectory(), cleanfile),
+                 cmOutputConverter::SHELL));
     commands.push_back(std::move(remove));
   }
 
@@ -1100,12 +1084,10 @@
   // Look for additional files registered for cleaning in this directory.
   if (const char* prop_value =
         this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop_value);
-    cmSystemTools::ExpandListArgument(
-      cge->Evaluate(this,
-                    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")),
-      cleanFiles);
+    cmExpandList(cmGeneratorExpression::Evaluate(
+                   prop_value, this,
+                   this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")),
+                 cleanFiles);
   }
   if (cleanFiles.empty()) {
     return;
@@ -1115,8 +1097,8 @@
     this->GetGlobalGenerator()->GetLocalGenerators().at(0);
   std::string const& binaryDir = rootLG->GetCurrentBinaryDirectory();
   std::string const& currentBinaryDir = this->GetCurrentBinaryDirectory();
-  std::string cleanfile = currentBinaryDir;
-  cleanfile += "/CMakeFiles/cmake_directory_clean.cmake";
+  std::string cleanfile =
+    cmStrCat(currentBinaryDir, "/CMakeFiles/cmake_directory_clean.cmake");
   // Write clean script
   {
     std::string cleanfilePath = cmSystemTools::CollapseFullPath(cleanfile);
@@ -1135,10 +1117,11 @@
   }
   // Create command
   {
-    std::string remove = "$(CMAKE_COMMAND) -P ";
-    remove += this->ConvertToOutputFormat(
-      rootLG->MaybeConvertToRelativePath(binaryDir, cleanfile),
-      cmOutputConverter::SHELL);
+    std::string remove =
+      cmStrCat("$(CMAKE_COMMAND) -P ",
+               this->ConvertToOutputFormat(
+                 rootLG->MaybeConvertToRelativePath(binaryDir, cleanfile),
+                 cmOutputConverter::SHELL));
     commands.push_back(std::move(remove));
   }
 }
@@ -1184,12 +1167,12 @@
         std::string cmd;
         if (color_name.empty() && !progress) {
           // Use the native echo command.
-          cmd = "@echo ";
-          cmd += this->EscapeForShell(line, false, true);
+          cmd = cmStrCat("@echo ", this->EscapeForShell(line, false, true));
         } else {
           // Use cmake to echo the text in color.
-          cmd = "@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) ";
-          cmd += color_name;
+          cmd = cmStrCat(
+            "@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) ",
+            color_name);
           if (progress) {
             cmd += "--progress-dir=";
             cmd += this->ConvertToOutputFormat(
@@ -1225,8 +1208,7 @@
 std::string cmLocalUnixMakefileGenerator3::CreateMakeVariable(
   std::string const& s, std::string const& s2)
 {
-  std::string unmodified = s;
-  unmodified += s2;
+  std::string unmodified = cmStrCat(s, s2);
   // if there is no restriction on the length of make variables
   // and there are no "." characters in the string, then return the
   // unmodified combination.
@@ -1241,8 +1223,7 @@
 
   // see if the variable has been defined before and return
   // the modified version of the variable
-  std::map<std::string, std::string>::iterator i =
-    this->MakeVariableMap.find(unmodified);
+  auto i = this->MakeVariableMap.find(unmodified);
   if (i != this->MakeVariableMap.end()) {
     return i->second;
   }
@@ -1344,9 +1325,9 @@
   // may have changed. In this case discard all old dependencies.
   bool needRescanDirInfo = false;
   {
-    std::string dirInfoFile = this->GetCurrentBinaryDirectory();
-    dirInfoFile += "/CMakeFiles";
-    dirInfoFile += "/CMakeDirectoryInformation.cmake";
+    std::string dirInfoFile =
+      cmStrCat(this->GetCurrentBinaryDirectory(),
+               "/CMakeFiles/CMakeDirectoryInformation.cmake");
     int result;
     if (!ftc->Compare(internalDependFile, dirInfoFile, &result) ||
         result < 0) {
@@ -1388,8 +1369,8 @@
     // The dependencies must be regenerated.
     std::string targetName = cmSystemTools::GetFilenameName(targetDir);
     targetName = targetName.substr(0, targetName.length() - 4);
-    std::string message = "Scanning dependencies of target ";
-    message += targetName;
+    std::string message =
+      cmStrCat("Scanning dependencies of target ", targetName);
     cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta |
                                        cmsysTerminal_Color_ForegroundBold,
                                      message.c_str(), true, color);
@@ -1410,9 +1391,9 @@
   cmMakefile* mf = this->Makefile;
   bool haveDirectoryInfo = false;
   {
-    std::string dirInfoFile = this->GetCurrentBinaryDirectory();
-    dirInfoFile += "/CMakeFiles";
-    dirInfoFile += "/CMakeDirectoryInformation.cmake";
+    std::string dirInfoFile =
+      cmStrCat(this->GetCurrentBinaryDirectory(),
+               "/CMakeFiles/CMakeDirectoryInformation.cmake");
     if (mf->ReadListFile(dirInfoFile) &&
         !cmSystemTools::GetErrorOccuredFlag()) {
       haveDirectoryInfo = true;
@@ -1423,7 +1404,7 @@
   if (haveDirectoryInfo) {
     // Test whether we need to force Unix paths.
     if (const char* force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) {
-      if (!cmSystemTools::IsOff(force)) {
+      if (!cmIsOff(force)) {
         cmSystemTools::SetForceUnixPaths(true);
       }
     }
@@ -1465,9 +1446,8 @@
   this->WriteDisclaimer(internalRuleFileStream);
 
   // for each language we need to scan, scan it
-  std::vector<std::string> langs;
-  cmSystemTools::ExpandListArgument(
-    mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES"), langs);
+  std::vector<std::string> langs =
+    cmExpandedList(mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES"));
   for (std::string const& lang : langs) {
     // construct the checker
     // Create the scanner for this language
@@ -1477,7 +1457,7 @@
       // TODO: Handle RC (resource files) dependencies correctly.
       scanner = cm::make_unique<cmDependsC>(this, targetDir, lang, &validDeps);
     }
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
     else if (lang == "Fortran") {
       ruleFileStream << "# Note that incremental build could trigger "
                      << "a call to cmake_copy_f90_mod on each re-build\n";
@@ -1511,10 +1491,8 @@
   }
 
   // Convert the string to a list and preserve empty entries.
-  std::vector<std::string> pairs;
-  cmSystemTools::ExpandListArgument(pairs_string, pairs, true);
-  for (std::vector<std::string>::const_iterator i = pairs.begin();
-       i != pairs.end() && (i + 1) != pairs.end();) {
+  std::vector<std::string> pairs = cmExpandedList(pairs_string, true);
+  for (auto i = pairs.begin(); i != pairs.end() && (i + 1) != pairs.end();) {
     const std::string& depender = *i++;
     const std::string& dependee = *i++;
 
@@ -1625,8 +1603,8 @@
   std::vector<std::string> commands;
 
   // Write the all rule.
-  std::string recursiveTarget = this->GetCurrentBinaryDirectory();
-  recursiveTarget += "/all";
+  std::string recursiveTarget =
+    cmStrCat(this->GetCurrentBinaryDirectory(), "/all");
 
   bool regenerate =
     !this->GlobalGenerator->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
@@ -1634,16 +1612,15 @@
     depends.emplace_back("cmake_check_build_system");
   }
 
-  std::string progressDir = this->GetBinaryDirectory();
-  progressDir += "/CMakeFiles";
+  std::string progressDir =
+    cmStrCat(this->GetBinaryDirectory(), "/CMakeFiles");
   {
     std::ostringstream progCmd;
     progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start ";
     progCmd << this->ConvertToOutputFormat(
       cmSystemTools::CollapseFullPath(progressDir), cmOutputConverter::SHELL);
 
-    std::string progressFile = "/CMakeFiles";
-    progressFile += "/progress.marks";
+    std::string progressFile = "/CMakeFiles/progress.marks";
     std::string progressFileNameFull = this->ConvertToFullPath(progressFile);
     progCmd << " "
             << this->ConvertToOutputFormat(
@@ -1651,8 +1628,7 @@
                  cmOutputConverter::SHELL);
     commands.push_back(progCmd.str());
   }
-  std::string mf2Dir = "CMakeFiles/";
-  mf2Dir += "Makefile2";
+  std::string mf2Dir = "CMakeFiles/Makefile2";
   commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
   this->CreateCDCommand(commands, this->GetBinaryDirectory(),
                         this->GetCurrentBinaryDirectory());
@@ -1668,8 +1644,7 @@
                       commands, true);
 
   // Write the clean rule.
-  recursiveTarget = this->GetCurrentBinaryDirectory();
-  recursiveTarget += "/clean";
+  recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/clean");
   commands.clear();
   depends.clear();
   commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
@@ -1684,13 +1659,12 @@
                       depends, commands, true);
 
   // Write the preinstall rule.
-  recursiveTarget = this->GetCurrentBinaryDirectory();
-  recursiveTarget += "/preinstall";
+  recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/preinstall");
   commands.clear();
   depends.clear();
   const char* noall =
     this->Makefile->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
-  if (!noall || cmSystemTools::IsOff(noall)) {
+  if (!noall || cmIsOff(noall)) {
     // Drive the build before installing.
     depends.emplace_back("all");
   } else if (regenerate) {
@@ -1712,20 +1686,19 @@
     commands.clear();
     cmake* cm = this->GlobalGenerator->GetCMakeInstance();
     if (cm->DoWriteGlobVerifyTarget()) {
-      std::string rescanRule = "$(CMAKE_COMMAND) -P ";
-      rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
-                                                cmOutputConverter::SHELL);
+      std::string rescanRule =
+        cmStrCat("$(CMAKE_COMMAND) -P ",
+                 this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
+                                             cmOutputConverter::SHELL));
       commands.push_back(rescanRule);
     }
-    std::string cmakefileName = "CMakeFiles/";
-    cmakefileName += "Makefile.cmake";
+    std::string cmakefileName = "CMakeFiles/Makefile.cmake";
     {
-      std::string runRule =
-        "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
-      runRule += " --check-build-system ";
-      runRule +=
-        this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL);
-      runRule += " 1";
+      std::string runRule = cmStrCat(
+        "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) "
+        "--check-build-system ",
+        this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL),
+        " 1");
       commands.push_back(std::move(runRule));
     }
     this->CreateCDCommand(commands, this->GetBinaryDirectory(),
@@ -1743,8 +1716,7 @@
   if (!infoDef) {
     return;
   }
-  std::vector<std::string> files;
-  cmSystemTools::ExpandListArgument(infoDef, files);
+  std::vector<std::string> files = cmExpandedList(infoDef);
 
   // Each depend information file corresponds to a target.  Clear the
   // dependencies for that target.
@@ -1846,9 +1818,8 @@
     cmakefileStream << "  )\n";
 
     // Tell the dependency scanner what compiler is used.
-    std::string cidVar = "CMAKE_";
-    cidVar += implicitLang.first;
-    cidVar += "_COMPILER_ID";
+    std::string cidVar =
+      cmStrCat("CMAKE_", implicitLang.first, "_COMPILER_ID");
     const char* cid = this->Makefile->GetDefinition(cidVar);
     if (cid && *cid) {
       cmakefileStream << "set(CMAKE_" << implicitLang.first
@@ -1891,9 +1862,8 @@
                     << "_TARGET_INCLUDE_PATH\n";
     std::vector<std::string> includes;
 
-    const std::string& config =
-      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
-    this->GetIncludeDirectories(includes, target, implicitLang.first, config);
+    this->GetIncludeDirectories(includes, target, implicitLang.first,
+                                this->ConfigName);
     std::string binaryDir = this->GetState()->GetBinaryDirectory();
     if (this->Makefile->IsOn("CMAKE_DEPENDS_IN_PROJECT_ONLY")) {
       std::string const& sourceDir = this->GetState()->GetSourceDirectory();
@@ -1912,11 +1882,11 @@
   std::vector<std::string> transformRules;
   if (const char* xform =
         this->Makefile->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
-    cmSystemTools::ExpandListArgument(xform, transformRules);
+    cmExpandList(xform, transformRules);
   }
   if (const char* xform =
         target->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
-    cmSystemTools::ExpandListArgument(xform, transformRules);
+    cmExpandList(xform, transformRules);
   }
   if (!transformRules.empty()) {
     cmakefileStream << "set(CMAKE_INCLUDE_TRANSFORMS\n";
@@ -1939,10 +1909,9 @@
   const std::string& makefile, const std::string& tgt)
 {
   // Call make on the given file.
-  std::string cmd;
-  cmd += "$(MAKE) -f ";
-  cmd += this->ConvertToOutputFormat(makefile, cmOutputConverter::SHELL);
-  cmd += " ";
+  std::string cmd = cmStrCat(
+    "$(MAKE) -f ",
+    this->ConvertToOutputFormat(makefile, cmOutputConverter::SHELL), ' ');
 
   cmGlobalUnixMakefileGenerator3* gg =
     static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
@@ -2045,10 +2014,9 @@
     if (components.size() > 1) {
       // Now add the rest of the components separated by the proper slash
       // direction for this platform.
-      std::vector<std::string>::const_iterator compEnd = std::remove(
-        components.begin() + 1, components.end() - 1, std::string());
-      std::vector<std::string>::const_iterator compStart =
-        components.begin() + 1;
+      auto compEnd = std::remove(components.begin() + 1, components.end() - 1,
+                                 std::string());
+      auto compStart = components.begin() + 1;
       result += cmJoin(cmMakeRange(compStart, compEnd), slash);
       // Only the last component can be empty to avoid double slashes.
       result += slash;
@@ -2073,8 +2041,7 @@
 std::string cmLocalUnixMakefileGenerator3::GetTargetDirectory(
   cmGeneratorTarget const* target) const
 {
-  std::string dir = "CMakeFiles/";
-  dir += target->GetName();
+  std::string dir = cmStrCat("CMakeFiles/", target->GetName());
 #if defined(__VMS)
   dir += "_dir";
 #else
@@ -2117,13 +2084,12 @@
     // On Windows we must perform each step separately and then change
     // back because the shell keeps the working directory between
     // commands.
-    std::string cmd = cd_cmd;
-    cmd += this->ConvertToOutputForExisting(tgtDir);
+    std::string cmd =
+      cmStrCat(cd_cmd, this->ConvertToOutputForExisting(tgtDir));
     commands.insert(commands.begin(), cmd);
 
     // Change back to the starting directory.
-    cmd = cd_cmd;
-    cmd += this->ConvertToOutputForExisting(relDir);
+    cmd = cmStrCat(cd_cmd, this->ConvertToOutputForExisting(relDir));
     commands.push_back(std::move(cmd));
   } else {
     // On UNIX we must construct a single shell command to change
diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h
index c8e4b0e..f12ae8b 100644
--- a/Source/cmLocalUnixMakefileGenerator3.h
+++ b/Source/cmLocalUnixMakefileGenerator3.h
@@ -5,9 +5,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmDepends.h"
-#include "cmLocalCommonGenerator.h"
-
 #include <iosfwd>
 #include <map>
 #include <set>
@@ -15,6 +12,9 @@
 #include <utility>
 #include <vector>
 
+#include "cmDepends.h"
+#include "cmLocalCommonGenerator.h"
+
 class cmCustomCommand;
 class cmCustomCommandGenerator;
 class cmGeneratorTarget;
@@ -90,7 +90,7 @@
   // append flags to a string
   void AppendFlags(std::string& flags,
                    const std::string& newFlags) const override;
-  void AppendFlags(std::string& flags, const char* newFlags) const override;
+  using cmLocalCommonGenerator::AppendFlags;
 
   // append an echo command
   enum EchoColor
@@ -139,7 +139,8 @@
   void WriteSpecialTargetsTop(std::ostream& makefileStream);
   void WriteSpecialTargetsBottom(std::ostream& makefileStream);
 
-  std::string GetRelativeTargetDirectory(cmGeneratorTarget* target);
+  std::string GetRelativeTargetDirectory(
+    cmGeneratorTarget const* target) const;
 
   // File pairs for implicit dependency scanning.  The key of the map
   // is the depender and the value is the explicit dependee.
diff --git a/Source/cmLocalVisualStudio10Generator.cxx b/Source/cmLocalVisualStudio10Generator.cxx
index d45c335..f3d828b 100644
--- a/Source/cmLocalVisualStudio10Generator.cxx
+++ b/Source/cmLocalVisualStudio10Generator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalVisualStudio10Generator.h"
 
+#include "cm_expat.h"
+
 #include "cmGeneratorTarget.h"
 #include "cmGlobalVisualStudio10Generator.h"
 #include "cmMakefile.h"
@@ -9,8 +11,6 @@
 #include "cmXMLParser.h"
 #include "cmake.h"
 
-#include "cm_expat.h"
-
 class cmVS10XMLParser : public cmXMLParser
 {
 public:
@@ -121,8 +121,7 @@
     return;
   }
 
-  std::string guidStoreName = name;
-  guidStoreName += "_GUID_CMAKE";
+  std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   // save the GUID in the cache
   this->GlobalGenerator->GetCMakeInstance()->AddCacheEntry(
     guidStoreName.c_str(), parser.GUID.c_str(), "Stored GUID",
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 8154f3e..ff1eaec 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -2,8 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalVisualStudio7Generator.h"
 
+#include <windows.h>
+
+#include <ctype.h> // for isspace
+
+#include "cm_expat.h"
+
+#include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalVisualStudio7Generator.h"
 #include "cmMakefile.h"
@@ -11,15 +20,8 @@
 #include "cmSourceFile.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
-#include "cm_expat.h"
 #include "cmake.h"
 
-#include "cmComputeLinkInformation.h"
-#include "cmGeneratedFileStream.h"
-
-#include <ctype.h> // for isspace
-#include <windows.h>
-
 static bool cmLVS7G_IsFAT(const char* dir);
 
 class cmLocalVisualStudio7GeneratorInternals
@@ -29,7 +31,7 @@
     : LocalGenerator(e)
   {
   }
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   void OutputLibraries(std::ostream& fout, ItemVector const& libs);
   void OutputObjects(std::ostream& fout, cmGeneratorTarget* t,
                      std::string const& config, const char* isep = 0);
@@ -91,21 +93,19 @@
   for (cmGeneratorTarget* l : tgts) {
     if (l->GetType() == cmStateEnums::GLOBAL_TARGET) {
       std::vector<std::string> no_depends;
-      cmCustomCommandLine force_command;
-      force_command.push_back("cd");
-      force_command.push_back(".");
-      cmCustomCommandLines force_commands;
-      force_commands.push_back(force_command);
+      cmCustomCommandLines force_commands =
+        cmMakeSingleCommandLine({ "cd", "." });
       std::string no_main_dependency;
-      std::string force = this->GetCurrentBinaryDirectory();
-      force += "/CMakeFiles";
-      force += "/";
-      force += l->GetName();
-      force += "_force";
+      std::string force = cmStrCat(this->GetCurrentBinaryDirectory(),
+                                   "/CMakeFiles/", l->GetName(), "_force");
+      if (cmSourceFile* sf =
+            this->Makefile->GetOrCreateGeneratedSource(force)) {
+        sf->SetProperty("SYMBOLIC", "1");
+      }
       if (cmSourceFile* file = this->Makefile->AddCustomCommandToOutput(
             force.c_str(), no_depends, no_main_dependency, force_commands, " ",
             0, true)) {
-        l->AddSource(file->GetFullPath());
+        l->AddSource(file->ResolveFullPath());
       }
     }
   }
@@ -144,11 +144,10 @@
 {
   // Touch a timestamp file used to determine when the project file is
   // out of date.
-  std::string stampName = this->GetCurrentBinaryDirectory();
-  stampName += "/CMakeFiles";
+  std::string stampName =
+    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles");
   cmSystemTools::MakeDirectory(stampName.c_str());
-  stampName += "/";
-  stampName += "generate.stamp";
+  stampName += "/generate.stamp";
   cmsys::ofstream stamp(stampName.c_str());
   stamp << "# CMake generation timestamp file for this directory.\n";
 
@@ -158,8 +157,7 @@
   // dependencies.  If any file listed in it is newer than itself then
   // CMake must rerun.  Otherwise the project files are up to date and
   // the stamp file can just be touched.
-  std::string depName = stampName;
-  depName += ".depend";
+  std::string depName = cmStrCat(stampName, ".depend");
   cmsys::ofstream depFile(depName.c_str());
   depFile << "# CMake generation dependency list for this directory.\n";
 
@@ -199,9 +197,7 @@
   target->Target->SetProperty("GENERATOR_FILE_NAME", lname.c_str());
   // create the dsp.cmake file
   std::string fname;
-  fname = this->GetCurrentBinaryDirectory();
-  fname += "/";
-  fname += lname;
+  fname = cmStrCat(this->GetCurrentBinaryDirectory(), '/', lname);
   if (this->FortranProject) {
     fname += ".vfproj";
   } else {
@@ -228,9 +224,8 @@
     return nullptr;
   }
 
-  std::string makefileIn = this->GetCurrentSourceDirectory();
-  makefileIn += "/";
-  makefileIn += "CMakeLists.txt";
+  std::string makefileIn =
+    cmStrCat(this->GetCurrentSourceDirectory(), "/CMakeLists.txt");
   makefileIn = cmSystemTools::CollapseFullPath(makefileIn);
   if (cmSourceFile* file = this->Makefile->GetSource(makefileIn)) {
     if (file->GetCustomCommand()) {
@@ -253,26 +248,15 @@
     std::unique(listFiles.begin(), listFiles.end());
   listFiles.erase(new_end, listFiles.end());
 
-  std::string stampName = this->GetCurrentBinaryDirectory();
-  stampName += "/";
-  stampName += "CMakeFiles/";
-  stampName += "generate.stamp";
-  cmCustomCommandLine commandLine;
-  commandLine.push_back(cmSystemTools::GetCMakeCommand());
-  std::string comment = "Building Custom Rule ";
-  comment += makefileIn;
-  std::string args;
-  args = "-S";
-  args += this->GetSourceDirectory();
-  commandLine.push_back(args);
-  args = "-B";
-  args += this->GetBinaryDirectory();
-  commandLine.push_back(args);
-  commandLine.push_back("--check-stamp-file");
-  commandLine.push_back(stampName);
-  cmCustomCommandLines commandLines;
-  commandLines.push_back(commandLine);
-  const char* no_working_directory = 0;
+  std::string argS = cmStrCat("-S", this->GetSourceDirectory());
+  std::string argB = cmStrCat("-B", this->GetBinaryDirectory());
+  std::string stampName =
+    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/generate.stamp");
+  cmCustomCommandLines commandLines =
+    cmMakeSingleCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB,
+                              "--check-stamp-file", stampName });
+  std::string comment = cmStrCat("Building Custom Rule ", makefileIn);
+  const char* no_working_directory = nullptr;
   std::string fullpathStampName =
     cmSystemTools::CollapseFullPath(stampName.c_str());
   this->Makefile->AddCustomCommandToOutput(
@@ -281,7 +265,7 @@
   if (cmSourceFile* file = this->Makefile->GetSource(makefileIn.c_str())) {
     // Finalize the source file path now since we're adding this after
     // the generator validated all project-named sources.
-    file->GetFullPath();
+    file->ResolveFullPath();
     return file;
   } else {
     cmSystemTools::Error("Error adding rule for " + makefileIn);
@@ -721,9 +705,7 @@
     this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE"));
 
   // Add a definition for the configuration name.
-  std::string configDefine = "CMAKE_INTDIR=\"";
-  configDefine += configName;
-  configDefine += "\"";
+  std::string configDefine = cmStrCat("CMAKE_INTDIR=\"", configName, '"');
   targetOptions.AddDefine(configDefine);
 
   // Add the export symbol definition for shared library objects.
@@ -733,9 +715,8 @@
 
   // The intermediate directory name consists of a directory for the
   // target and a subdirectory for the configuration name.
-  std::string intermediateDir = this->GetTargetDirectory(target);
-  intermediateDir += "/";
-  intermediateDir += configName;
+  std::string intermediateDir =
+    cmStrCat(this->GetTargetDirectory(target), '/', configName);
 
   if (target->GetType() < cmStateEnums::UTILITY) {
     std::string const& outDir =
@@ -956,8 +937,7 @@
     extraLinkOptions += targetLinkFlags;
   }
   std::string configTypeUpper = cmSystemTools::UpperCase(configName);
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += configTypeUpper;
+  std::string linkFlagsConfig = cmStrCat("LINK_FLAGS_", configTypeUpper);
   targetLinkFlags = target->GetProperty(linkFlagsConfig.c_str());
   if (targetLinkFlags) {
     extraLinkOptions += " ";
@@ -989,12 +969,9 @@
     case cmStateEnums::UNKNOWN_LIBRARY:
       break;
     case cmStateEnums::OBJECT_LIBRARY: {
-      std::string libpath = this->GetTargetDirectory(target);
-      libpath += "/";
-      libpath += configName;
-      libpath += "/";
-      libpath += target->GetName();
-      libpath += ".lib";
+      std::string libpath =
+        cmStrCat(this->GetTargetDirectory(target), '/', configName, '/',
+                 target->GetName(), ".lib");
       const char* tool =
         this->FortranProject ? "VFLibrarianTool" : "VCLibrarianTool";
       fout << "\t\t\t<Tool\n"
@@ -1005,9 +982,8 @@
     }
     case cmStateEnums::STATIC_LIBRARY: {
       std::string targetNameFull = target->GetFullName(configName);
-      std::string libpath = target->GetDirectory(configName);
-      libpath += "/";
-      libpath += targetNameFull;
+      std::string libpath =
+        cmStrCat(target->GetDirectory(configName), '/', targetNameFull);
       const char* tool = "VCLibrarianTool";
       if (this->FortranProject) {
         tool = "VFLibrarianTool";
@@ -1049,9 +1025,8 @@
 
       // Compute the variable name to lookup standard libraries for this
       // language.
-      std::string standardLibsVar = "CMAKE_";
-      standardLibsVar += linkLanguage;
-      standardLibsVar += "_STANDARD_LIBRARIES";
+      std::string standardLibsVar =
+        cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES");
       const char* tool = "VCLinkerTool";
       if (this->FortranProject) {
         tool = "VFLinkerTool";
@@ -1071,9 +1046,8 @@
       fout << " ";
       this->Internal->OutputLibraries(fout, cli.GetItems());
       fout << "\"\n";
-      temp = target->GetDirectory(configName);
-      temp += "/";
-      temp += targetNames.Output;
+      temp =
+        cmStrCat(target->GetDirectory(configName), '/', targetNames.Output);
       fout << "\t\t\t\tOutputFile=\""
            << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n";
       this->WriteTargetVersionAttribute(fout, target);
@@ -1081,9 +1055,8 @@
       fout << "\t\t\t\tAdditionalLibraryDirectories=\"";
       this->OutputLibraryDirectories(fout, cli.GetDirectories());
       fout << "\"\n";
-      temp = target->GetPDBDirectory(configName);
-      temp += "/";
-      temp += targetNames.PDB;
+      temp =
+        cmStrCat(target->GetPDBDirectory(configName), '/', targetNames.PDB);
       fout << "\t\t\t\tProgramDatabaseFile=\""
            << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n";
       if (targetOptions.IsDebug()) {
@@ -1096,17 +1069,14 @@
           fout << "\t\t\t\tSubSystem=\"8\"\n";
         }
       }
-      std::string stackVar = "CMAKE_";
-      stackVar += linkLanguage;
-      stackVar += "_STACK_SIZE";
+      std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE");
       const char* stackVal = this->Makefile->GetDefinition(stackVar.c_str());
       if (stackVal) {
         fout << "\t\t\t\tStackReserveSize=\"" << stackVal << "\"\n";
       }
-      temp =
-        target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact);
-      temp += "/";
-      temp += targetNames.ImportLibrary;
+      temp = cmStrCat(
+        target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact),
+        '/', targetNames.ImportLibrary);
       fout << "\t\t\t\tImportLibrary=\""
            << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"";
       if (this->FortranProject) {
@@ -1130,9 +1100,8 @@
 
       // Compute the variable name to lookup standard libraries for this
       // language.
-      std::string standardLibsVar = "CMAKE_";
-      standardLibsVar += linkLanguage;
-      standardLibsVar += "_STANDARD_LIBRARIES";
+      std::string standardLibsVar =
+        cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES");
       const char* tool = "VCLinkerTool";
       if (this->FortranProject) {
         tool = "VFLinkerTool";
@@ -1152,9 +1121,8 @@
       fout << " ";
       this->Internal->OutputLibraries(fout, cli.GetItems());
       fout << "\"\n";
-      temp = target->GetDirectory(configName);
-      temp += "/";
-      temp += targetNames.Output;
+      temp =
+        cmStrCat(target->GetDirectory(configName), '/', targetNames.Output);
       fout << "\t\t\t\tOutputFile=\""
            << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n";
       this->WriteTargetVersionAttribute(fout, target);
@@ -1190,17 +1158,14 @@
         fout << "\t\t\t\tSubSystem=\"" << (isWin32Executable ? "2" : "1")
              << "\"\n";
       }
-      std::string stackVar = "CMAKE_";
-      stackVar += linkLanguage;
-      stackVar += "_STACK_SIZE";
+      std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE");
       const char* stackVal = this->Makefile->GetDefinition(stackVar.c_str());
       if (stackVal) {
         fout << "\t\t\t\tStackReserveSize=\"" << stackVal << "\"";
       }
-      temp =
-        target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact);
-      temp += "/";
-      temp += targetNames.ImportLibrary;
+      temp = cmStrCat(
+        target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact),
+        '/', targetNames.ImportLibrary);
       fout << "\t\t\t\tImportLibrary=\""
            << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"/>\n";
       break;
@@ -1489,6 +1454,22 @@
         fc.CompileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
       needfc = true;
     }
+    // Add precompile headers compile options.
+    const std::string pchSource = gt->GetPchSource(config, lang);
+    if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+      std::string pchOptions;
+      if (sf.GetFullPath() == pchSource) {
+        pchOptions = gt->GetPchCreateCompileOptions(config, lang);
+      } else {
+        pchOptions = gt->GetPchUseCompileOptions(config, lang);
+      }
+
+      lg->AppendCompileOptions(
+        fc.CompileFlags,
+        genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
+      needfc = true;
+    }
+
     if (lg->FortranProject) {
       switch (cmOutputConverter::GetFortranFormat(
         sf.GetProperty("Fortran_FORMAT"))) {
@@ -1509,8 +1490,7 @@
       fc.CompileDefs = genexInterpreter.Evaluate(cdefs, COMPILE_DEFINITIONS);
       needfc = true;
     }
-    std::string defPropName = "COMPILE_DEFINITIONS_";
-    defPropName += configUpper;
+    std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
     if (const char* ccdefs = sf.GetProperty(defPropName)) {
       fc.CompileDefsConfig =
         genexInterpreter.Evaluate(ccdefs, COMPILE_DEFINITIONS);
@@ -1525,8 +1505,7 @@
 
     // Check for extra object-file dependencies.
     if (const char* deps = sf.GetProperty("OBJECT_DEPENDS")) {
-      std::vector<std::string> depends;
-      cmSystemTools::ExpandListArgument(deps, depends);
+      std::vector<std::string> depends = cmExpandedList(deps);
       const char* sep = "";
       for (std::vector<std::string>::iterator j = depends.begin();
            j != depends.end(); ++j) {
@@ -1541,8 +1520,10 @@
     // If HEADER_FILE_ONLY is set, we must suppress this generation in
     // the project file
     fc.ExcludedFromBuild = sf.GetPropertyAsBool("HEADER_FILE_ONLY") ||
-      std::find(acs.Configs.begin(), acs.Configs.end(), ci) ==
-        acs.Configs.end();
+      !cmContains(acs.Configs, ci) ||
+      (gt->GetPropertyAsBool("UNITY_BUILD") &&
+       sf.GetProperty("UNITY_SOURCE_FILE") &&
+       !sf.GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION"));
     if (fc.ExcludedFromBuild) {
       needfc = true;
     }
@@ -1586,13 +1567,9 @@
   // Compute the maximum length full path to the intermediate
   // files directory for any configuration.  This is used to construct
   // object file names that do not produce paths that are too long.
-  std::string dir_max;
-  dir_max += this->GetCurrentBinaryDirectory();
-  dir_max += "/";
-  dir_max += this->GetTargetDirectory(target);
-  dir_max += "/";
-  dir_max += config_max;
-  dir_max += "/";
+  std::string dir_max =
+    cmStrCat(this->GetCurrentBinaryDirectory(), '/',
+             this->GetTargetDirectory(target), '/', config_max, '/');
   return dir_max;
 }
 
@@ -2142,8 +2119,7 @@
   if (parser.GUID.empty()) {
     return;
   }
-  std::string guidStoreName = name;
-  guidStoreName += "_GUID_CMAKE";
+  std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   // save the GUID in the cache
   this->GlobalGenerator->GetCMakeInstance()->AddCacheEntry(
     guidStoreName.c_str(), parser.GUID.c_str(), "Stored GUID",
@@ -2153,9 +2129,7 @@
 std::string cmLocalVisualStudio7Generator::GetTargetDirectory(
   cmGeneratorTarget const* target) const
 {
-  std::string dir;
-  dir += target->GetName();
-  dir += ".dir";
+  std::string dir = cmStrCat(target->GetName(), ".dir");
   return dir;
 }
 
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index ce8eceb..671783f 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/Source/cmLocalVisualStudio7Generator.h
@@ -83,8 +83,8 @@
   void CreateSingleVCProj(const std::string& lname, cmGeneratorTarget* tgt);
 
 private:
-  typedef cmVS7GeneratorOptions Options;
-  typedef cmLocalVisualStudio7GeneratorFCInfo FCInfo;
+  using Options = cmVS7GeneratorOptions;
+  using FCInfo = cmLocalVisualStudio7GeneratorFCInfo;
   std::string GetBuildTypeLinkerFlags(std::string rootLinkerFlags,
                                       const std::string& configName);
   void FixGlobalTargets();
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index f3f2042..336e3a5 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalVisualStudioGenerator.h"
 
+#include "windows.h"
+
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmGeneratorTarget.h"
@@ -9,7 +11,6 @@
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 #include "cmSystemTools.h"
-#include "windows.h"
 
 cmLocalVisualStudioGenerator::cmLocalVisualStudioGenerator(
   cmGlobalGenerator* gg, cmMakefile* mf)
@@ -98,16 +99,11 @@
   }
 
   // Add a pre-build event to create the directory.
-  cmCustomCommandLine command;
-  command.push_back(cmSystemTools::GetCMakeCommand());
-  command.push_back("-E");
-  command.push_back("make_directory");
-  command.push_back(impDir);
   std::vector<std::string> no_output;
   std::vector<std::string> no_byproducts;
   std::vector<std::string> no_depends;
-  cmCustomCommandLines commands;
-  commands.push_back(command);
+  cmCustomCommandLines commands = cmMakeSingleCommandLine(
+    { cmSystemTools::GetCMakeCommand(), "-E", "make_directory", impDir });
   pcc.reset(new cmCustomCommand(0, no_output, no_byproducts, no_depends,
                                 commands, 0, 0));
   pcc->SetEscapeOldStyle(false);
diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h
index 3fdafd2..585eb3c 100644
--- a/Source/cmLocalVisualStudioGenerator.h
+++ b/Source/cmLocalVisualStudioGenerator.h
@@ -6,7 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 
 #include "cmGlobalVisualStudioGenerator.h"
diff --git a/Source/cmLocalXCodeGenerator.cxx b/Source/cmLocalXCodeGenerator.cxx
index 9c36627..5a06d4a 100644
--- a/Source/cmLocalXCodeGenerator.cxx
+++ b/Source/cmLocalXCodeGenerator.cxx
@@ -65,9 +65,8 @@
   std::map<std::string, int> counts;
   for (auto& si : mapping) {
     cmSourceFile const* sf = si.first;
-    std::string objectName =
-      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath());
-    objectName += ".o";
+    std::string objectName = cmStrCat(
+      cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()), ".o");
 
     std::string objectNameLower = cmSystemTools::LowerCase(objectName);
     counts[objectNameLower] += 1;
diff --git a/Source/cmLocale.h b/Source/cmLocale.h
index 3580ec8..c44a42d 100644
--- a/Source/cmLocale.h
+++ b/Source/cmLocale.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <locale.h>
+#include <clocale>
 #include <string>
 
 class cmLocaleRAII
diff --git a/Source/cmMachO.cxx b/Source/cmMachO.cxx
index d4af1e0..6cbed36 100644
--- a/Source/cmMachO.cxx
+++ b/Source/cmMachO.cxx
@@ -2,12 +2,16 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMachO.h"
 
-#include "cmsys/FStream.hxx"
-#include <algorithm>
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 #include <vector>
 
+#include <cm/memory>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmAlgorithms.h"
+
 // Include the Mach-O format information system header.
 #include <mach-o/fat.h>
 #include <mach-o/loader.h>
@@ -120,7 +124,7 @@
 
 // Implementation for reading Mach-O header and load commands.
 // This is 32 or 64 bit arch specific.
-template <class T>
+template <typename T>
 class cmMachOHeaderAndLoadCommandsImpl : public cmMachOHeaderAndLoadCommands
 {
 public:
@@ -306,15 +310,11 @@
 // External class implementation.
 
 cmMachO::cmMachO(const char* fname)
-  : Internal(nullptr)
+  : Internal(cm::make_unique<cmMachOInternal>(fname))
 {
-  this->Internal = new cmMachOInternal(fname);
 }
 
-cmMachO::~cmMachO()
-{
-  delete this->Internal;
-}
+cmMachO::~cmMachO() = default;
 
 std::string const& cmMachO::GetErrorMessage() const
 {
diff --git a/Source/cmMachO.h b/Source/cmMachO.h
index 5482465..0c44b55 100644
--- a/Source/cmMachO.h
+++ b/Source/cmMachO.h
@@ -41,7 +41,7 @@
 private:
   friend class cmMachOInternal;
   bool Valid() const;
-  cmMachOInternal* Internal;
+  std::unique_ptr<cmMachOInternal> Internal;
 };
 
 #endif
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index e9c6aea..ba9947a 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -2,48 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMacroCommand.h"
 
-#include <sstream>
-#include <stdio.h>
+#include <cstdio>
 #include <utility>
 
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmExecutionStatus.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
+namespace {
+
 // define the class for macro commands
-class cmMacroHelperCommand : public cmCommand
+class cmMacroHelperCommand
 {
 public:
   /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override
-  {
-    cmMacroHelperCommand* newC = new cmMacroHelperCommand;
-    // we must copy when we clone
-    newC->Args = this->Args;
-    newC->Functions = this->Functions;
-    newC->FilePath = this->FilePath;
-    newC->Policies = this->Policies;
-    return newC;
-  }
-
-  /**
    * This is called when the command is first encountered in
    * the CMakeLists.txt file.
    */
-  bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
-                         cmExecutionStatus&) override;
-
-  bool InitialPass(std::vector<std::string> const&,
-                   cmExecutionStatus&) override
-  {
-    return false;
-  }
+  bool operator()(std::vector<cmListFileArgument> const& args,
+                  cmExecutionStatus& inStatus) const;
 
   std::vector<std::string> Args;
   std::vector<cmListFileFunction> Functions;
@@ -51,33 +40,33 @@
   std::string FilePath;
 };
 
-bool cmMacroHelperCommand::InvokeInitialPass(
-  const std::vector<cmListFileArgument>& args, cmExecutionStatus& inStatus)
+bool cmMacroHelperCommand::operator()(
+  std::vector<cmListFileArgument> const& args,
+  cmExecutionStatus& inStatus) const
 {
+  cmMakefile& makefile = inStatus.GetMakefile();
+
   // Expand the argument list to the macro.
   std::vector<std::string> expandedArgs;
-  this->Makefile->ExpandArguments(args, expandedArgs);
+  makefile.ExpandArguments(args, expandedArgs);
 
   // make sure the number of arguments passed is at least the number
   // required by the signature
   if (expandedArgs.size() < this->Args.size() - 1) {
     std::string errorMsg =
-      "Macro invoked with incorrect arguments for macro named: ";
-    errorMsg += this->Args[0];
-    this->SetError(errorMsg);
+      cmStrCat("Macro invoked with incorrect arguments for macro named: ",
+               this->Args[0]);
+    inStatus.SetError(errorMsg);
     return false;
   }
 
-  cmMakefile::MacroPushPop macroScope(this->Makefile, this->FilePath,
+  cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath,
                                       this->Policies);
 
   // set the value of argc
-  std::ostringstream argcDefStream;
-  argcDefStream << expandedArgs.size();
-  std::string argcDef = argcDefStream.str();
+  std::string argcDef = std::to_string(expandedArgs.size());
 
-  std::vector<std::string>::const_iterator eit =
-    expandedArgs.begin() + (this->Args.size() - 1);
+  auto eit = expandedArgs.begin() + (this->Args.size() - 1);
   std::string expandedArgn = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
   std::string expandedArgv = cmJoin(expandedArgs, ";");
   std::vector<std::string> variables;
@@ -130,9 +119,8 @@
       arg.Line = k.Line;
       newLFF.Arguments.push_back(std::move(arg));
     }
-    cmExecutionStatus status;
-    if (!this->Makefile->ExecuteCommand(newLFF, status) ||
-        status.GetNestedError()) {
+    cmExecutionStatus status(makefile);
+    if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) {
       // The error message should have already included the call stack
       // so we do not need to report an error here.
       macroScope.Quiet();
@@ -151,68 +139,59 @@
   return true;
 }
 
-bool cmMacroFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
-                                               cmMakefile& mf,
-                                               cmExecutionStatus&)
+class cmMacroFunctionBlocker : public cmFunctionBlocker
 {
-  // record commands until we hit the ENDMACRO
-  // at the ENDMACRO call we shift gears and start looking for invocations
-  if (lff.Name.Lower == "macro") {
-    this->Depth++;
-  } else if (lff.Name.Lower == "endmacro") {
-    // if this is the endmacro for this macro then execute
-    if (!this->Depth) {
-      mf.AppendProperty("MACROS", this->Args[0].c_str());
-      // create a new command and add it to cmake
-      cmMacroHelperCommand* f = new cmMacroHelperCommand();
-      f->Args = this->Args;
-      f->Functions = this->Functions;
-      f->FilePath = this->GetStartingContext().FilePath;
-      mf.RecordPolicies(f->Policies);
-      mf.GetState()->AddScriptedCommand(this->Args[0], f);
-      // remove the function blocker now that the macro is defined
-      mf.RemoveFunctionBlocker(this, lff);
-      return true;
-    }
-    // decrement for each nested macro that ends
-    this->Depth--;
-  }
+public:
+  cm::string_view StartCommandName() const override { return "macro"_s; }
+  cm::string_view EndCommandName() const override { return "endmacro"_s; }
 
-  // if it wasn't an endmacro and we are not executing then we must be
-  // recording
-  this->Functions.push_back(lff);
+  bool ArgumentsMatch(cmListFileFunction const&,
+                      cmMakefile& mf) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& status) override;
+
+  std::vector<std::string> Args;
+};
+
+bool cmMacroFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                            cmMakefile& mf) const
+{
+  std::vector<std::string> expandedArguments;
+  mf.ExpandArguments(lff.Arguments, expandedArguments,
+                     this->GetStartingContext().FilePath.c_str());
+  return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
+}
+
+bool cmMacroFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
+                                    cmExecutionStatus& status)
+{
+  cmMakefile& mf = status.GetMakefile();
+  mf.AppendProperty("MACROS", this->Args[0].c_str());
+  // create a new command and add it to cmake
+  cmMacroHelperCommand f;
+  f.Args = this->Args;
+  f.Functions = std::move(functions);
+  f.FilePath = this->GetStartingContext().FilePath;
+  mf.RecordPolicies(f.Policies);
+  mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f));
   return true;
 }
-
-bool cmMacroFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
-                                          cmMakefile& mf)
-{
-  if (lff.Name.Lower == "endmacro") {
-    std::vector<std::string> expandedArguments;
-    mf.ExpandArguments(lff.Arguments, expandedArguments,
-                       this->GetStartingContext().FilePath.c_str());
-    // if the endmacro has arguments make sure they
-    // match the arguments of the macro
-    if ((expandedArguments.empty() ||
-         (expandedArguments[0] == this->Args[0]))) {
-      return true;
-    }
-  }
-
-  return false;
 }
 
-bool cmMacroCommand::InitialPass(std::vector<std::string> const& args,
-                                 cmExecutionStatus&)
+bool cmMacroCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // create a function blocker
-  cmMacroFunctionBlocker* f = new cmMacroFunctionBlocker();
-  cmAppend(f->Args, args);
-  this->Makefile->AddFunctionBlocker(f);
+  {
+    auto fb = cm::make_unique<cmMacroFunctionBlocker>();
+    cmAppend(fb->Args, args);
+    status.GetMakefile().AddFunctionBlocker(std::move(fb));
+  }
   return true;
 }
diff --git a/Source/cmMacroCommand.h b/Source/cmMacroCommand.h
index b54ed66..25091ea 100644
--- a/Source/cmMacroCommand.h
+++ b/Source/cmMacroCommand.h
@@ -8,40 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmFunctionBlocker.h"
-#include "cmListFileCache.h"
-
 class cmExecutionStatus;
-class cmMakefile;
-
-class cmMacroFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  bool IsFunctionBlocked(const cmListFileFunction&, cmMakefile& mf,
-                         cmExecutionStatus&) override;
-  bool ShouldRemove(const cmListFileFunction&, cmMakefile& mf) override;
-
-  std::vector<std::string> Args;
-  std::vector<cmListFileFunction> Functions;
-  int Depth = 0;
-};
 
 /// Starts macro() ... endmacro() block
-class cmMacroCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmMacroCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmMacroCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmMakeDirectoryCommand.cxx b/Source/cmMakeDirectoryCommand.cxx
index aff4ca6..cdde6f9 100644
--- a/Source/cmMakeDirectoryCommand.cxx
+++ b/Source/cmMakeDirectoryCommand.cxx
@@ -2,23 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMakeDirectoryCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmMakeDirectoryCommand
-bool cmMakeDirectoryCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmMakeDirectoryCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() != 1) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
-  if (!this->Makefile->CanIWriteThisFile(args[0])) {
+  if (!status.GetMakefile().CanIWriteThisFile(args[0])) {
     std::string e = "attempted to create a directory: " + args[0] +
       " into a source directory.";
-    this->SetError(e);
+    status.SetError(e);
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
diff --git a/Source/cmMakeDirectoryCommand.h b/Source/cmMakeDirectoryCommand.h
index d2637f3..2474383 100644
--- a/Source/cmMakeDirectoryCommand.h
+++ b/Source/cmMakeDirectoryCommand.h
@@ -8,11 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmMakeDirectoryCommand
+/**
  * \brief Specify auxiliary source code directories.
  *
  * cmMakeDirectoryCommand specifies source code directories
@@ -21,20 +19,7 @@
  * A side effect of this command is to create a subdirectory in the build
  * directory structure.
  */
-class cmMakeDirectoryCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmMakeDirectoryCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmMakeDirectoryCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 3f8bd4e..bf488b1 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -1,22 +1,27 @@
 /* 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 "cmMakefile.h"
 
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <utility>
+
+#include <cm/iterator>
+#include <cm/memory>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
-#include <algorithm>
-#include <assert.h>
-#include <cstring>
-#include <ctype.h>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <utility>
+
+#include "cm_sys_stat.h"
 
 #include "cmAlgorithms.h"
-#include "cmCommand.h"
 #include "cmCommandArgumentParserHelper.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
@@ -24,6 +29,7 @@
 #include "cmExpandedCommandArgument.h" // IWYU pragma: keep
 #include "cmFileLockPool.h"
 #include "cmFunctionBlocker.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
 #include "cmGlobalGenerator.h"
@@ -37,18 +43,17 @@
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
 #include "cmTest.h"
 #include "cmTestGenerator.h" // IWYU pragma: keep
 #include "cmVersion.h"
 #include "cmWorkingDirectory.h"
-#include "cm_sys_stat.h"
 #include "cmake.h"
 
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 #  include "cmVariableWatch.h"
 #endif
 
@@ -98,10 +103,11 @@
   // cmListFileCache in the top level if necessary.
   this->CheckCMP0000 = false;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->AddSourceGroup("", "^.*$");
   this->AddSourceGroup("Source Files", CM_SOURCE_REGEX);
   this->AddSourceGroup("Header Files", CM_HEADER_REGEX);
+  this->AddSourceGroup("Precompile Header File", CM_PCH_REGEX);
   this->AddSourceGroup("CMake Rules", "\\.rule$");
   this->AddSourceGroup("Resources", CM_RESOURCE_REGEX);
   this->AddSourceGroup("Object Files", "\\.(lo|o|obj)$");
@@ -118,8 +124,6 @@
   cmDeleteAll(this->SourceFiles);
   cmDeleteAll(this->Tests);
   cmDeleteAll(this->ImportedTargetsOwned);
-  cmDeleteAll(this->FinalPassCommands);
-  cmDeleteAll(this->FunctionBlockers);
   cmDeleteAll(this->EvaluationFiles);
 }
 
@@ -324,7 +328,13 @@
     msg << " ";
   }
   msg << ")";
-  cmSystemTools::Message(msg.str());
+
+  auto& f = this->GetCMakeInstance()->GetTraceFile();
+  if (f) {
+    f << msg.str() << '\n';
+  } else {
+    cmSystemTools::Message(msg.str());
+  }
 }
 
 // Helper class to make sure the call stack is valid.
@@ -356,6 +366,11 @@
   cmMakefile* Makefile;
 };
 
+void cmMakefile::OnExecuteCommand(std::function<void()> callback)
+{
+  this->ExecuteCommandCallback = std::move(callback);
+}
+
 bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
                                 cmExecutionStatus& status)
 {
@@ -367,6 +382,10 @@
     return result;
   }
 
+  if (this->ExecuteCommandCallback) {
+    this->ExecuteCommandCallback();
+  }
+
   // Place this call on the call stack.
   cmMakefileCall stack_manager(this, lff, status);
   static_cast<void>(stack_manager);
@@ -390,12 +409,8 @@
   }
 
   // Lookup the command prototype.
-  if (cmCommand* proto =
+  if (cmState::Command command =
         this->GetState()->GetCommandByExactName(lff.Name.Lower)) {
-    // Clone the prototype.
-    std::unique_ptr<cmCommand> pcmd(proto->Clone());
-    pcmd->SetMakefile(this);
-
     // Decide whether to invoke the command.
     if (!cmSystemTools::GetFatalErrorOccured()) {
       // if trace is enabled, print out invoke information
@@ -403,29 +418,25 @@
         this->PrintCommandTrace(lff);
       }
       // Try invoking the command.
-      bool invokeSucceeded = pcmd->InvokeInitialPass(lff.Arguments, status);
+      bool invokeSucceeded = command(lff.Arguments, status);
       bool hadNestedError = status.GetNestedError();
       if (!invokeSucceeded || hadNestedError) {
         if (!hadNestedError) {
           // The command invocation requested that we report an error.
           std::string const error =
-            std::string(lff.Name.Original) + " " + pcmd->GetError();
+            std::string(lff.Name.Original) + " " + status.GetError();
           this->IssueMessage(MessageType::FATAL_ERROR, error);
         }
         result = false;
         if (this->GetCMakeInstance()->GetWorkingMode() != cmake::NORMAL_MODE) {
           cmSystemTools::SetFatalErrorOccured();
         }
-      } else if (pcmd->HasFinalPass()) {
-        // use the command
-        this->FinalPassCommands.push_back(pcmd.release());
       }
     }
   } else {
     if (!cmSystemTools::GetFatalErrorOccured()) {
-      std::string error = "Unknown CMake command \"";
-      error += lff.Name.Original;
-      error += "\".";
+      std::string error =
+        cmStrCat("Unknown CMake command \"", lff.Name.Original, "\".");
       this->IssueMessage(MessageType::FATAL_ERROR, error);
       result = false;
       cmSystemTools::SetFatalErrorOccured();
@@ -567,8 +578,9 @@
 bool cmMakefile::ReadDependentFile(const std::string& filename,
                                    bool noPolicyScope)
 {
-  this->AddDefinition("CMAKE_PARENT_LIST_FILE",
-                      this->GetDefinition("CMAKE_CURRENT_LIST_FILE"));
+  if (const char* def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) {
+    this->AddDefinition("CMAKE_PARENT_LIST_FILE", def);
+  }
   std::string filenametoread = cmSystemTools::CollapseFullPath(
     filename, this->GetCurrentSourceDirectory());
 
@@ -651,9 +663,9 @@
     this->GetSafeDefinition("CMAKE_PARENT_LIST_FILE");
   std::string currentFile = this->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE");
 
-  this->AddDefinition("CMAKE_CURRENT_LIST_FILE", filenametoread.c_str());
+  this->AddDefinition("CMAKE_CURRENT_LIST_FILE", filenametoread);
   this->AddDefinition("CMAKE_CURRENT_LIST_DIR",
-                      cmSystemTools::GetFilenamePath(filenametoread).c_str());
+                      cmSystemTools::GetFilenamePath(filenametoread));
 
   this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE");
   this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE");
@@ -662,7 +674,7 @@
   // Run the parsed commands.
   const size_t numberFunctions = listFile.Functions.size();
   for (size_t i = 0; i < numberFunctions; ++i) {
-    cmExecutionStatus status;
+    cmExecutionStatus status(*this);
     this->ExecuteCommand(listFile.Functions[i], status);
     if (cmSystemTools::GetFatalErrorOccured()) {
       break;
@@ -674,10 +686,10 @@
   }
   this->CheckForUnusedVariables();
 
-  this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile.c_str());
-  this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str());
+  this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile);
+  this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile);
   this->AddDefinition("CMAKE_CURRENT_LIST_DIR",
-                      cmSystemTools::GetFilenamePath(currentFile).c_str());
+                      cmSystemTools::GetFilenamePath(currentFile));
   this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE");
   this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE");
   this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR");
@@ -745,9 +757,8 @@
 void cmMakefile::RemoveExportBuildFileGeneratorCMP0024(
   cmExportBuildFileGenerator* gen)
 {
-  std::vector<cmExportBuildFileGenerator*>::iterator it =
-    std::find(this->ExportBuildFileGenerators.begin(),
-              this->ExportBuildFileGenerators.end(), gen);
+  auto it = std::find(this->ExportBuildFileGenerators.begin(),
+                      this->ExportBuildFileGenerators.end(), gen);
   if (it != this->ExportBuildFileGenerators.end()) {
     this->ExportBuildFileGenerators.erase(it);
   }
@@ -769,6 +780,11 @@
 };
 }
 
+void cmMakefile::AddFinalAction(FinalAction action)
+{
+  this->FinalActions.push_back(std::move(action));
+}
+
 void cmMakefile::FinalPass()
 {
   // do all the variable expansions here
@@ -776,8 +792,8 @@
 
   // give all the commands a chance to do something
   // after the file has been parsed before generation
-  for (cmCommand* fpCommand : this->FinalPassCommands) {
-    fpCommand->FinalPass();
+  for (FinalAction& action : this->FinalActions) {
+    action(*this);
   }
 
   // go through all configured files and see which ones still exist.
@@ -809,16 +825,27 @@
   }
 }
 
-void cmMakefile::AddCustomCommandToTarget(
-  const std::string& target, const std::vector<std::string>& byproducts,
-  const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
-  const char* comment, const char* workingDir, bool escapeOldStyle,
-  bool uses_terminal, const std::string& depfile, const std::string& job_pool,
-  bool command_expand_lists, ObjectLibraryCommands objLibraryCommands)
+bool cmMakefile::ValidateCustomCommand(
+  const cmCustomCommandLines& commandLines) const
+{
+  // TODO: More strict?
+  for (cmCustomCommandLine const& cl : commandLines) {
+    if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') {
+      std::ostringstream e;
+      e << "COMMAND may not contain literal quotes:\n  " << cl[0] << "\n";
+      this->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return false;
+    }
+  }
+
+  return true;
+}
+
+cmTarget* cmMakefile::GetCustomCommandTarget(
+  const std::string& target, cmObjectLibraryCommands objLibCommands) const
 {
   // Find the target to which to add the custom command.
-  cmTargetMap::iterator ti = this->Targets.find(target);
+  auto ti = this->Targets.find(target);
 
   if (ti == this->Targets.end()) {
     MessageType messageType = MessageType::AUTHOR_WARNING;
@@ -849,38 +876,67 @@
         e << "No TARGET '" << target
           << "' has been created in this directory.";
       }
-      IssueMessage(messageType, e.str());
+      this->IssueMessage(messageType, e.str());
     }
 
-    return;
+    return nullptr;
   }
 
-  cmTarget& t = ti->second;
-  if (objLibraryCommands == RejectObjectLibraryCommands &&
-      t.GetType() == cmStateEnums::OBJECT_LIBRARY) {
+  cmTarget* t = &ti->second;
+  if (objLibCommands == cmObjectLibraryCommands::Reject &&
+      t->GetType() == cmStateEnums::OBJECT_LIBRARY) {
     std::ostringstream e;
     e << "Target \"" << target
       << "\" is an OBJECT library "
          "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands.";
     this->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
+    return nullptr;
   }
-  if (t.GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+  if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
     std::ostringstream e;
     e << "Target \"" << target
       << "\" is an INTERFACE library "
          "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands.";
     this->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
+    return nullptr;
+  }
+
+  return t;
+}
+
+cmTarget* cmMakefile::AddCustomCommandToTarget(
+  const std::string& target, const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends,
+  const cmCustomCommandLines& commandLines, cmCustomCommandType type,
+  const char* comment, const char* workingDir, bool escapeOldStyle,
+  bool uses_terminal, const std::string& depfile, const std::string& job_pool,
+  bool command_expand_lists, cmObjectLibraryCommands objLibCommands)
+{
+  cmTarget* t = this->GetCustomCommandTarget(target, objLibCommands);
+
+  // Validate custom commands.
+  if (!t || !this->ValidateCustomCommand(commandLines)) {
+    return t;
   }
 
   // Always create the byproduct sources and mark them generated.
-  for (std::string const& o : byproducts) {
-    if (cmSourceFile* out = this->GetOrCreateSource(o, true)) {
-      out->SetProperty("GENERATED", "1");
-    }
-  }
+  this->CreateGeneratedSources(byproducts);
 
+  this->CommitCustomCommandToTarget(
+    t, byproducts, depends, commandLines, type, comment, workingDir,
+    escapeOldStyle, uses_terminal, depfile, job_pool, command_expand_lists);
+
+  return t;
+}
+
+void cmMakefile::CommitCustomCommandToTarget(
+  cmTarget* target, const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends,
+  const cmCustomCommandLines& commandLines, cmCustomCommandType type,
+  const char* comment, const char* workingDir, bool escapeOldStyle,
+  bool uses_terminal, const std::string& depfile, const std::string& job_pool,
+  bool command_expand_lists)
+{
   // Add the command to the appropriate build step for the target.
   std::vector<std::string> no_output;
   cmCustomCommand cc(this, no_output, byproducts, depends, commandLines,
@@ -892,22 +948,42 @@
   cc.SetDepfile(depfile);
   cc.SetJobPool(job_pool);
   switch (type) {
-    case cmTarget::PRE_BUILD:
-      t.AddPreBuildCommand(cc);
+    case cmCustomCommandType::PRE_BUILD:
+      target->AddPreBuildCommand(std::move(cc));
       break;
-    case cmTarget::PRE_LINK:
-      t.AddPreLinkCommand(cc);
+    case cmCustomCommandType::PRE_LINK:
+      target->AddPreLinkCommand(std::move(cc));
       break;
-    case cmTarget::POST_BUILD:
-      t.AddPostBuildCommand(cc);
+    case cmCustomCommandType::POST_BUILD:
+      target->AddPostBuildCommand(std::move(cc));
       break;
   }
+
+  this->AddTargetByproducts(target, byproducts);
+}
+
+cmSourceFile* cmMakefile::AddCustomCommandToOutput(
+  const std::string& output, const std::vector<std::string>& depends,
+  const std::string& main_dependency, const cmCustomCommandLines& commandLines,
+  const char* comment, const char* workingDir, bool replace,
+  bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
+  const std::string& depfile, const std::string& job_pool)
+{
+  std::vector<std::string> outputs;
+  outputs.push_back(output);
+  std::vector<std::string> no_byproducts;
+  cmImplicitDependsList no_implicit_depends;
+  return this->AddCustomCommandToOutput(
+    outputs, no_byproducts, depends, main_dependency, no_implicit_depends,
+    commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
+    command_expand_lists, depfile, job_pool);
 }
 
 cmSourceFile* cmMakefile::AddCustomCommandToOutput(
   const std::vector<std::string>& outputs,
   const std::vector<std::string>& byproducts,
   const std::vector<std::string>& depends, const std::string& main_dependency,
+  const cmImplicitDependsList& implicit_depends,
   const cmCustomCommandLines& commandLines, const char* comment,
   const char* workingDir, bool replace, bool escapeOldStyle,
   bool uses_terminal, bool command_expand_lists, const std::string& depfile,
@@ -919,16 +995,31 @@
     return nullptr;
   }
 
-  // Validate custom commands.  TODO: More strict?
-  for (cmCustomCommandLine const& cl : commandLines) {
-    if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') {
-      std::ostringstream e;
-      e << "COMMAND may not contain literal quotes:\n  " << cl[0] << "\n";
-      this->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return nullptr;
-    }
+  // Validate custom commands.
+  if (!this->ValidateCustomCommand(commandLines)) {
+    return nullptr;
   }
 
+  // Always create the output sources and mark them generated.
+  this->CreateGeneratedSources(outputs);
+  this->CreateGeneratedSources(byproducts);
+
+  return this->CommitCustomCommandToOutput(
+    outputs, byproducts, depends, main_dependency, implicit_depends,
+    commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
+    command_expand_lists, depfile, job_pool);
+}
+
+cmSourceFile* cmMakefile::CommitCustomCommandToOutput(
+  const std::vector<std::string>& outputs,
+  const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends, const std::string& main_dependency,
+  const cmImplicitDependsList& implicit_depends,
+  const cmCustomCommandLines& commandLines, const char* comment,
+  const char* workingDir, bool replace, bool escapeOldStyle,
+  bool uses_terminal, bool command_expand_lists, const std::string& depfile,
+  const std::string& job_pool)
+{
   // Choose a source file on which to store the custom command.
   cmSourceFile* file = nullptr;
   if (!commandLines.empty() && !main_dependency.empty()) {
@@ -976,20 +1067,6 @@
     file->SetProperty("__CMAKE_RULE", "1");
   }
 
-  // Always create the output sources and mark them generated.
-  for (std::string const& o : outputs) {
-    if (cmSourceFile* out =
-          this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) {
-      out->SetProperty("GENERATED", "1");
-    }
-  }
-  for (std::string const& o : byproducts) {
-    if (cmSourceFile* out =
-          this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) {
-      out->SetProperty("GENERATED", "1");
-    }
-  }
-
   // Attach the custom command to the file.
   if (file) {
     // Construct a complete list of dependencies.
@@ -998,62 +1075,22 @@
       depends2.push_back(main_dependency);
     }
 
-    cmCustomCommand* cc = new cmCustomCommand(
+    std::unique_ptr<cmCustomCommand> cc = cm::make_unique<cmCustomCommand>(
       this, outputs, byproducts, depends2, commandLines, comment, workingDir);
     cc->SetEscapeOldStyle(escapeOldStyle);
     cc->SetEscapeAllowMakeVars(true);
+    cc->SetImplicitDepends(implicit_depends);
     cc->SetUsesTerminal(uses_terminal);
     cc->SetCommandExpandLists(command_expand_lists);
     cc->SetDepfile(depfile);
     cc->SetJobPool(job_pool);
-    file->SetCustomCommand(cc);
-    this->UpdateOutputToSourceMap(outputs, file);
+    file->SetCustomCommand(std::move(cc));
+
+    this->AddSourceOutputs(file, outputs, byproducts);
   }
   return file;
 }
 
-void cmMakefile::UpdateOutputToSourceMap(
-  std::vector<std::string> const& outputs, cmSourceFile* source)
-{
-  for (std::string const& o : outputs) {
-    this->UpdateOutputToSourceMap(o, source);
-  }
-}
-
-void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
-                                         cmSourceFile* source)
-{
-  OutputToSourceMap::iterator i = this->OutputToSource.find(output);
-  if (i != this->OutputToSource.end()) {
-    // Multiple custom commands produce the same output but may
-    // be attached to a different source file (MAIN_DEPENDENCY).
-    // LinearGetSourceFileWithOutput would return the first one,
-    // so keep the mapping for the first one.
-    //
-    // TODO: Warn the user about this case.  However, the VS 8 generator
-    // triggers it for separate generate.stamp rules in ZERO_CHECK and
-    // individual targets.
-    return;
-  }
-  this->OutputToSource[output] = source;
-}
-
-cmSourceFile* cmMakefile::AddCustomCommandToOutput(
-  const std::string& output, const std::vector<std::string>& depends,
-  const std::string& main_dependency, const cmCustomCommandLines& commandLines,
-  const char* comment, const char* workingDir, bool replace,
-  bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
-  const std::string& depfile, const std::string& job_pool)
-{
-  std::vector<std::string> outputs;
-  outputs.push_back(output);
-  std::vector<std::string> no_byproducts;
-  return this->AddCustomCommandToOutput(
-    outputs, no_byproducts, depends, main_dependency, commandLines, comment,
-    workingDir, replace, escapeOldStyle, uses_terminal, command_expand_lists,
-    depfile, job_pool);
-}
-
 void cmMakefile::AddCustomCommandOldStyle(
   const std::string& target, const std::vector<std::string>& outputs,
   const std::vector<std::string>& depends, const std::string& source,
@@ -1066,148 +1103,178 @@
     // same then it added a post-build rule to the target.  Preserve
     // this behavior.
     std::vector<std::string> no_byproducts;
-    this->AddCustomCommandToTarget(target, no_byproducts, depends,
-                                   commandLines, cmTarget::POST_BUILD, comment,
-                                   nullptr);
+    this->AddCustomCommandToTarget(
+      target, no_byproducts, depends, commandLines,
+      cmCustomCommandType::POST_BUILD, comment, nullptr);
     return;
   }
 
+  auto ti = this->Targets.find(target);
+  cmTarget* t = ti != this->Targets.end() ? &ti->second : nullptr;
+
+  auto addRuleFileToTarget = [=](cmSourceFile* sf) {
+    // If the rule was added to the source (and not a .rule file),
+    // then add the source to the target to make sure the rule is
+    // included.
+    if (!sf->GetPropertyAsBool("__CMAKE_RULE")) {
+      if (t) {
+        t->AddSource(sf->ResolveFullPath());
+      } else {
+        cmSystemTools::Error("Attempt to add a custom rule to a target "
+                             "that does not exist yet for target " +
+                             target);
+      }
+    }
+  };
+
   // Each output must get its own copy of this rule.
   cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|"
                                        "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
                                        "hm|hpp|hxx|in|txx|inl)$");
-  for (std::string const& oi : outputs) {
-    // Get the name of this output.
-    const char* output = oi.c_str();
-    cmSourceFile* sf;
 
-    // Choose whether to use a main dependency.
-    if (sourceFiles.find(source)) {
-      // The source looks like a real file.  Use it as the main dependency.
-      sf = this->AddCustomCommandToOutput(output, depends, source,
-                                          commandLines, comment, nullptr);
-    } else {
-      // The source may not be a real file.  Do not use a main dependency.
-      std::string no_main_dependency;
-      std::vector<std::string> depends2 = depends;
-      depends2.push_back(source);
-      sf = this->AddCustomCommandToOutput(output, depends2, no_main_dependency,
-                                          commandLines, comment, nullptr);
+  // Choose whether to use a main dependency.
+  if (sourceFiles.find(source)) {
+    // The source looks like a real file.  Use it as the main dependency.
+    for (std::string const& output : outputs) {
+      cmSourceFile* sf = this->AddCustomCommandToOutput(
+        output, depends, source, commandLines, comment, nullptr);
+      if (sf) {
+        addRuleFileToTarget(sf);
+      }
     }
+  } else {
+    std::string no_main_dependency;
+    std::vector<std::string> depends2 = depends;
+    depends2.push_back(source);
 
-    // If the rule was added to the source (and not a .rule file),
-    // then add the source to the target to make sure the rule is
-    // included.
-    if (sf && !sf->GetPropertyAsBool("__CMAKE_RULE")) {
-      cmTargetMap::iterator ti = this->Targets.find(target);
-      if (ti != this->Targets.end()) {
-        ti->second.AddSource(sf->GetFullPath());
-      } else {
-        cmSystemTools::Error("Attempt to add a custom rule to a target "
-                             "that does not exist yet for target " +
-                             target);
-        return;
+    // The source may not be a real file.  Do not use a main dependency.
+    for (std::string const& output : outputs) {
+      cmSourceFile* sf = this->AddCustomCommandToOutput(
+        output, depends2, no_main_dependency, commandLines, comment, nullptr);
+      if (sf) {
+        addRuleFileToTarget(sf);
       }
     }
   }
 }
 
-cmTarget* cmMakefile::AddUtilityCommand(
-  const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-  const std::vector<std::string>& depends, const char* workingDirectory,
-  const char* command, const char* arg1, const char* arg2, const char* arg3,
-  const char* arg4)
+bool cmMakefile::AppendCustomCommandToOutput(
+  const std::string& output, const std::vector<std::string>& depends,
+  const cmImplicitDependsList& implicit_depends,
+  const cmCustomCommandLines& commandLines)
 {
-  // Construct the command line for the custom command.
-  cmCustomCommandLine commandLine;
-  commandLine.push_back(command);
-  if (arg1) {
-    commandLine.push_back(arg1);
+  // Check as good as we can if there will be a command for this output.
+  if (!this->MightHaveCustomCommand(output)) {
+    return false;
   }
-  if (arg2) {
-    commandLine.push_back(arg2);
-  }
-  if (arg3) {
-    commandLine.push_back(arg3);
-  }
-  if (arg4) {
-    commandLine.push_back(arg4);
-  }
-  cmCustomCommandLines commandLines;
-  commandLines.push_back(std::move(commandLine));
 
-  // Call the real signature of this method.
-  return this->AddUtilityCommand(utilityName, origin, excludeFromAll,
-                                 workingDirectory, depends, commandLines);
+  // Validate custom commands.
+  if (this->ValidateCustomCommand(commandLines)) {
+    // Add command factory to allow generator expressions in output.
+    this->CommitAppendCustomCommandToOutput(output, depends, implicit_depends,
+                                            commandLines);
+  }
+
+  return true;
 }
 
-cmTarget* cmMakefile::AddUtilityCommand(
-  const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-  const char* workingDirectory, const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, bool escapeOldStyle,
-  const char* comment, bool uses_terminal, bool command_expand_lists,
-  const std::string& job_pool)
+void cmMakefile::CommitAppendCustomCommandToOutput(
+  const std::string& output, const std::vector<std::string>& depends,
+  const cmImplicitDependsList& implicit_depends,
+  const cmCustomCommandLines& commandLines)
 {
-  std::vector<std::string> no_byproducts;
-  return this->AddUtilityCommand(
-    utilityName, origin, excludeFromAll, workingDirectory, no_byproducts,
-    depends, commandLines, escapeOldStyle, comment, uses_terminal,
-    command_expand_lists, job_pool);
+  // Lookup an existing command.
+  if (cmSourceFile* sf = this->GetSourceFileWithOutput(output)) {
+    if (cmCustomCommand* cc = sf->GetCustomCommand()) {
+      cc->AppendCommands(commandLines);
+      cc->AppendDepends(depends);
+      cc->AppendImplicitDepends(implicit_depends);
+    }
+  }
 }
 
-cmTarget* cmMakefile::AddUtilityCommand(
-  const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-  const char* workingDirectory, const std::vector<std::string>& byproducts,
-  const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, bool escapeOldStyle,
-  const char* comment, bool uses_terminal, bool command_expand_lists,
-  const std::string& job_pool)
+cmUtilityOutput cmMakefile::GetUtilityOutput(cmTarget* target)
 {
-  // Create a target instance for this utility.
-  cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
-  target->SetIsGeneratorProvided(origin == TargetOrigin::Generator);
-  if (excludeFromAll) {
-    target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
-  }
-  if (!comment) {
-    // Use an empty comment to avoid generation of default comment.
-    comment = "";
-  }
-
-  // Store the custom command in the target.
-  if (!commandLines.empty() || !depends.empty()) {
-    std::string force = this->GetCurrentBinaryDirectory();
-    force += "/CMakeFiles";
-    force += "/";
-    force += utilityName;
-    std::vector<std::string> forced;
-    forced.push_back(force);
-    std::string no_main_dependency;
-    bool no_replace = false;
-    this->AddCustomCommandToOutput(
-      forced, byproducts, depends, no_main_dependency, commandLines, comment,
-      workingDirectory, no_replace, escapeOldStyle, uses_terminal,
-      command_expand_lists, /*depfile=*/"", job_pool);
-    cmSourceFile* sf = target->AddSourceCMP0049(force);
-
+  std::string force = cmStrCat(this->GetCurrentBinaryDirectory(),
+                               "/CMakeFiles/", target->GetName());
+  std::string forceCMP0049 = target->GetSourceCMP0049(force);
+  {
+    cmSourceFile* sf = nullptr;
+    if (!forceCMP0049.empty()) {
+      sf = this->GetOrCreateSource(forceCMP0049, false,
+                                   cmSourceFileLocationKind::Known);
+    }
     // The output is not actually created so mark it symbolic.
     if (sf) {
       sf->SetProperty("SYMBOLIC", "1");
     } else {
       cmSystemTools::Error("Could not get source file entry for " + force);
     }
-
-    // Always create the byproduct sources and mark them generated.
-    for (std::string const& byproduct : byproducts) {
-      if (cmSourceFile* out = this->GetOrCreateSource(
-            byproduct, true, cmSourceFileLocationKind::Known)) {
-        out->SetProperty("GENERATED", "1");
-      }
-    }
   }
+  return { std::move(force), std::move(forceCMP0049) };
+}
+
+cmTarget* cmMakefile::AddUtilityCommand(
+  const std::string& utilityName, cmCommandOrigin origin, bool excludeFromAll,
+  const char* workingDirectory, const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends,
+  const cmCustomCommandLines& commandLines, bool escapeOldStyle,
+  const char* comment, bool uses_terminal, bool command_expand_lists,
+  const std::string& job_pool)
+{
+  cmTarget* target =
+    this->AddNewUtilityTarget(utilityName, origin, excludeFromAll);
+
+  // Validate custom commands.
+  if ((commandLines.empty() && depends.empty()) ||
+      !this->ValidateCustomCommand(commandLines)) {
+    return target;
+  }
+
+  // Get the output name of the utility target and mark it generated.
+  cmUtilityOutput force = this->GetUtilityOutput(target);
+  this->GetOrCreateGeneratedSource(force.Name);
+
+  // Always create the byproduct sources and mark them generated.
+  this->CreateGeneratedSources(byproducts);
+
+  if (!comment) {
+    // Use an empty comment to avoid generation of default comment.
+    comment = "";
+  }
+
+  this->CommitUtilityCommand(target, force, workingDirectory, byproducts,
+                             depends, commandLines, escapeOldStyle, comment,
+                             uses_terminal, command_expand_lists, job_pool);
+
   return target;
 }
 
+void cmMakefile::CommitUtilityCommand(
+  cmTarget* target, const cmUtilityOutput& force, const char* workingDirectory,
+  const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends,
+  const cmCustomCommandLines& commandLines, bool escapeOldStyle,
+  const char* comment, bool uses_terminal, bool command_expand_lists,
+  const std::string& job_pool)
+{
+  std::vector<std::string> forced;
+  forced.push_back(force.Name);
+  std::string no_main_dependency;
+  cmImplicitDependsList no_implicit_depends;
+  bool no_replace = false;
+  cmSourceFile* sf = this->AddCustomCommandToOutput(
+    forced, byproducts, depends, no_main_dependency, no_implicit_depends,
+    commandLines, comment, workingDirectory, no_replace, escapeOldStyle,
+    uses_terminal, command_expand_lists, /*depfile=*/"", job_pool);
+  if (!force.NameCMP0049.empty()) {
+    target->AddSource(force.NameCMP0049);
+  }
+  if (sf) {
+    this->AddTargetByproducts(target, byproducts);
+  }
+}
+
 static void s_AddDefineFlag(std::string const& flag, std::string& dflags)
 {
   // remove any \n\r
@@ -1343,13 +1410,11 @@
   if (remove) {
     if (const char* cdefs = this->GetProperty("COMPILE_DEFINITIONS")) {
       // Expand the list.
-      std::vector<std::string> defs;
-      cmSystemTools::ExpandListArgument(cdefs, defs);
+      std::vector<std::string> defs = cmExpandedList(cdefs);
 
       // Recompose the list without the definition.
-      std::vector<std::string>::const_iterator defEnd =
-        std::remove(defs.begin(), defs.end(), define);
-      std::vector<std::string>::const_iterator defBegin = defs.begin();
+      auto defEnd = std::remove(defs.begin(), defs.end(), define);
+      auto defBegin = defs.begin();
       std::string ndefs = cmJoin(cmMakeRange(defBegin, defEnd), ";");
 
       // Store the new list.
@@ -1385,8 +1450,8 @@
     std::vector<std::string> configs;
     this->GetConfigurations(configs);
     for (std::string const& config : configs) {
-      std::string defPropName = "COMPILE_DEFINITIONS_";
-      defPropName += cmSystemTools::UpperCase(config);
+      std::string defPropName =
+        cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
       const char* prop = parent->GetProperty(defPropName);
       this->SetProperty(defPropName, prop);
     }
@@ -1420,7 +1485,7 @@
 
   this->PushLoopBlockBarrier();
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope();
 #endif
 
@@ -1437,7 +1502,7 @@
 
   this->PopFunctionBlockerBarrier(reportError);
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope();
 #endif
 
@@ -1479,8 +1544,8 @@
     , ReportError(true)
   {
     std::string currentStart =
-      this->Makefile->StateSnapshot.GetDirectory().GetCurrentSource();
-    currentStart += "/CMakeLists.txt";
+      cmStrCat(this->Makefile->StateSnapshot.GetDirectory().GetCurrentSource(),
+               "/CMakeLists.txt");
     this->Makefile->StateSnapshot.SetListFile(currentStart);
     this->Makefile->StateSnapshot =
       this->Makefile->StateSnapshot.GetState()->CreatePolicyScopeSnapshot(
@@ -1492,7 +1557,7 @@
     this->Snapshot = this->GG->GetCMakeInstance()->GetCurrentSnapshot();
     this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot);
     this->GG->SetCurrentMakefile(mf);
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
     this->GG->GetFileLockPool().PushFileScope();
 #endif
   }
@@ -1501,7 +1566,7 @@
   {
     this->Makefile->PopFunctionBlockerBarrier(this->ReportError);
     this->Makefile->PopSnapshot(this->ReportError);
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
     this->GG->GetFileLockPool().PopFileScope();
 #endif
     this->GG->SetCurrentMakefile(this->CurrentMakefile);
@@ -1523,9 +1588,8 @@
 
 void cmMakefile::Configure()
 {
-  std::string currentStart =
-    this->StateSnapshot.GetDirectory().GetCurrentSource();
-  currentStart += "/CMakeLists.txt";
+  std::string currentStart = cmStrCat(
+    this->StateSnapshot.GetDirectory().GetCurrentSource(), "/CMakeLists.txt");
 
   // Add the bottom of all backtraces within this directory.
   // We will never pop this scope because it should be available
@@ -1535,12 +1599,12 @@
   BuildsystemFileScope scope(this);
 
   // make sure the CMakeFiles dir is there
-  std::string filesDir = this->StateSnapshot.GetDirectory().GetCurrentBinary();
-  filesDir += "/CMakeFiles";
+  std::string filesDir = cmStrCat(
+    this->StateSnapshot.GetDirectory().GetCurrentBinary(), "/CMakeFiles");
   cmSystemTools::MakeDirectory(filesDir);
 
   assert(cmSystemTools::FileExists(currentStart, true));
-  this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart.c_str());
+  this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart);
 
   cmListFile listFile;
   if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(),
@@ -1578,7 +1642,7 @@
         allowedCommands.insert("message");
         isProblem = false;
         for (cmListFileFunction const& func : listFile.Functions) {
-          if (allowedCommands.find(func.Name.Lower) == allowedCommands.end()) {
+          if (!cmContains(allowedCommands, func.Name.Lower)) {
             isProblem = true;
             break;
           }
@@ -1633,7 +1697,7 @@
   std::vector<cmMakefile*> subdirs = this->UnConfiguredDirectories;
 
   // for each subdir recurse
-  std::vector<cmMakefile*>::iterator sdi = subdirs.begin();
+  auto sdi = subdirs.begin();
   for (; sdi != subdirs.end(); ++sdi) {
     (*sdi)->StateSnapshot.InitializeFromParent_ForSubdirsCommand();
     this->ConfigureSubDirectory(*sdi);
@@ -1647,8 +1711,7 @@
   mf->InitializeFromParent(this);
   std::string currentStart = mf->GetCurrentSourceDirectory();
   if (this->GetCMakeInstance()->GetDebugOutput()) {
-    std::string msg = "   Entering             ";
-    msg += currentStart;
+    std::string msg = cmStrCat("   Entering             ", currentStart);
     cmSystemTools::Message(msg);
   }
 
@@ -1690,8 +1753,8 @@
   mf->Configure();
 
   if (this->GetCMakeInstance()->GetDebugOutput()) {
-    std::string msg = "   Returning to         ";
-    msg += this->GetCurrentSourceDirectory();
+    std::string msg =
+      cmStrCat("   Returning to         ", this->GetCurrentSourceDirectory());
     cmSystemTools::Message(msg);
   }
 }
@@ -1788,26 +1851,27 @@
   }
 }
 
-void cmMakefile::AddDefinition(const std::string& name, const char* value)
+void cmMakefile::AddDefinition(const std::string& name, cm::string_view value)
 {
-  if (!value) {
-    return;
-  }
-
   if (this->VariableInitialized(name)) {
     this->LogUnused("changing definition", name);
   }
   this->StateSnapshot.SetDefinition(name, value);
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmVariableWatch* vv = this->GetVariableWatch();
   if (vv) {
     vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS,
-                         value, this);
+                         value.data(), this);
   }
 #endif
 }
 
+void cmMakefile::AddDefinitionBool(const std::string& name, bool value)
+{
+  this->AddDefinition(name, value ? "ON" : "OFF");
+}
+
 void cmMakefile::AddCacheDefinition(const std::string& name, const char* value,
                                     const char* doc,
                                     cmStateEnums::CacheEntryType type,
@@ -1831,10 +1895,10 @@
       std::vector<std::string> files;
       nvalue = value ? value : "";
 
-      cmSystemTools::ExpandListArgument(nvalue, files);
+      cmExpandList(nvalue, files);
       nvalue.clear();
       for (cc = 0; cc < files.size(); cc++) {
-        if (!cmSystemTools::IsOff(files[cc])) {
+        if (!cmIsOff(files[cc])) {
           files[cc] = cmSystemTools::CollapseFullPath(files[cc]);
         }
         if (cc > 0) {
@@ -1853,23 +1917,6 @@
   this->StateSnapshot.RemoveDefinition(name);
 }
 
-void cmMakefile::AddDefinition(const std::string& name, bool value)
-{
-  if (this->VariableInitialized(name)) {
-    this->LogUnused("changing definition", name);
-  }
-
-  this->StateSnapshot.SetDefinition(name, value ? "ON" : "OFF");
-
-#ifdef CMAKE_BUILD_WITH_CMAKE
-  cmVariableWatch* vv = this->GetVariableWatch();
-  if (vv) {
-    vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS,
-                         value ? "ON" : "OFF", this);
-  }
-#endif
-}
-
 void cmMakefile::CheckForUnusedVariables() const
 {
   if (!this->WarnUnused) {
@@ -1914,8 +1961,7 @@
     if (!this->ExecutionStatusStack.empty()) {
       path = this->GetExecutionContext().FilePath;
     } else {
-      path = this->GetCurrentSourceDirectory();
-      path += "/CMakeLists.txt";
+      path = cmStrCat(this->GetCurrentSourceDirectory(), "/CMakeLists.txt");
     }
 
     if (this->CheckSystemVars || this->IsProjectFile(path.c_str())) {
@@ -1932,7 +1978,7 @@
     this->LogUnused("unsetting", name);
   }
   this->StateSnapshot.RemoveDefinition(name);
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmVariableWatch* vv = this->GetVariableWatch();
   if (vv) {
     vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS,
@@ -1963,11 +2009,9 @@
   }
 
   if (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
-    std::vector<std::string> linkLibs;
-    cmSystemTools::ExpandListArgument(linkLibsProp, linkLibs);
+    std::vector<std::string> linkLibs = cmExpandedList(linkLibsProp);
 
-    for (std::vector<std::string>::iterator j = linkLibs.begin();
-         j != linkLibs.end(); ++j) {
+    for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) {
       std::string libraryName = *j;
       cmTargetLinkLibraryType libType = GENERAL_LibraryType;
       if (libraryName == "optimized") {
@@ -2034,61 +2078,229 @@
 cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type,
                                    const std::string& name)
 {
-  cmTargetMap::iterator it =
+  auto it =
     this->Targets
       .emplace(name, cmTarget(name, type, cmTarget::VisibilityNormal, this))
       .first;
+  this->OrderedTargets.push_back(&it->second);
   this->GetGlobalGenerator()->IndexTarget(&it->second);
   this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name);
   return &it->second;
 }
 
-cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput(
-  const std::string& name) const
+cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName,
+                                          cmCommandOrigin origin,
+                                          bool excludeFromAll)
 {
-  std::string out;
+  cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
+  target->SetIsGeneratorProvided(origin == cmCommandOrigin::Generator);
+  if (excludeFromAll) {
+    target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
+  }
+  return target;
+}
 
-  // look through all the source files that have custom commands
-  // and see if the custom command has the passed source file as an output
+namespace {
+bool AnyOutputMatches(const std::string& name,
+                      const std::vector<std::string>& outputs)
+{
+  for (std::string const& output : outputs) {
+    std::string::size_type pos = output.rfind(name);
+    // If the output matches exactly
+    if (pos != std::string::npos && pos == output.size() - name.size() &&
+        (pos == 0 || output[pos - 1] == '/')) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool AnyTargetCommandOutputMatches(
+  const std::string& name, const std::vector<cmCustomCommand>& commands)
+{
+  for (cmCustomCommand const& command : commands) {
+    if (AnyOutputMatches(name, command.GetByproducts())) {
+      return true;
+    }
+  }
+  return false;
+}
+}
+
+cmTarget* cmMakefile::LinearGetTargetWithOutput(const std::string& name) const
+{
+  // We go through the ordered vector of targets to get reproducible results
+  // should multiple names match.
+  for (cmTarget* t : this->OrderedTargets) {
+    // Does the output of any command match the source file name?
+    if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) {
+      return t;
+    }
+    if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) {
+      return t;
+    }
+    if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) {
+      return t;
+    }
+  }
+  return nullptr;
+}
+
+cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput(
+  const std::string& name, cmSourceOutputKind kind, bool& byproduct) const
+{
+  // Outputs take precedence over byproducts.
+  byproduct = false;
+  cmSourceFile* fallback = nullptr;
+
+  // Look through all the source files that have custom commands and see if the
+  // custom command has the passed source file as an output.
   for (cmSourceFile* src : this->SourceFiles) {
-    // does this source file have a custom command?
+    // Does this source file have a custom command?
     if (src->GetCustomCommand()) {
       // Does the output of the custom command match the source file name?
-      const std::vector<std::string>& outputs =
-        src->GetCustomCommand()->GetOutputs();
-      for (std::string const& output : outputs) {
-        out = output;
-        std::string::size_type pos = out.rfind(name);
-        // If the output matches exactly
-        if (pos != std::string::npos && pos == out.size() - name.size() &&
-            (pos == 0 || out[pos - 1] == '/')) {
-          return src;
+      if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) {
+        // Return the first matching output.
+        return src;
+      }
+      if (kind == cmSourceOutputKind::OutputOrByproduct) {
+        if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) {
+          // Do not return the source yet as there might be a matching output.
+          fallback = src;
         }
       }
     }
   }
 
-  // otherwise return NULL
-  return nullptr;
+  // Did we find a byproduct?
+  byproduct = fallback != nullptr;
+  return fallback;
+}
+
+cmSourcesWithOutput cmMakefile::GetSourcesWithOutput(
+  const std::string& name) const
+{
+  // Linear search?  Also see GetSourceFileWithOutput for detail.
+  if (!cmSystemTools::FileIsFullPath(name)) {
+    cmSourcesWithOutput sources;
+    sources.Target = this->LinearGetTargetWithOutput(name);
+    sources.Source = this->LinearGetSourceFileWithOutput(
+      name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct);
+    return sources;
+  }
+  // Otherwise we use an efficient lookup map.
+  auto o = this->OutputToSource.find(name);
+  if (o != this->OutputToSource.end()) {
+    return o->second.Sources;
+  }
+  return {};
 }
 
 cmSourceFile* cmMakefile::GetSourceFileWithOutput(
-  const std::string& name) const
+  const std::string& name, cmSourceOutputKind kind) const
 {
   // If the queried path is not absolute we use the backward compatible
   // linear-time search for an output with a matching suffix.
   if (!cmSystemTools::FileIsFullPath(name)) {
-    return this->LinearGetSourceFileWithOutput(name);
+    bool byproduct = false;
+    return this->LinearGetSourceFileWithOutput(name, kind, byproduct);
   }
   // Otherwise we use an efficient lookup map.
-  OutputToSourceMap::const_iterator o = this->OutputToSource.find(name);
-  if (o != this->OutputToSource.end()) {
-    return (*o).second;
+  auto o = this->OutputToSource.find(name);
+  if (o != this->OutputToSource.end() &&
+      (!o->second.Sources.SourceIsByproduct ||
+       kind == cmSourceOutputKind::OutputOrByproduct)) {
+    // Source file could also be null pointer for example if we found the
+    // byproduct of a utility target or a PRE_BUILD, PRE_LINK, or POST_BUILD
+    // command of a target.
+    return o->second.Sources.Source;
   }
   return nullptr;
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+bool cmMakefile::MightHaveCustomCommand(const std::string& name) const
+{
+  // This will have to be changed for delaying custom command creation, because
+  // GetSourceFileWithOutput requires the command to be already created.
+  if (cmSourceFile* sf = this->GetSourceFileWithOutput(name)) {
+    if (sf->GetCustomCommand()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void cmMakefile::AddTargetByproducts(
+  cmTarget* target, const std::vector<std::string>& byproducts)
+{
+  for (std::string const& o : byproducts) {
+    this->UpdateOutputToSourceMap(o, target);
+  }
+}
+
+void cmMakefile::AddSourceOutputs(cmSourceFile* source,
+                                  const std::vector<std::string>& outputs,
+                                  const std::vector<std::string>& byproducts)
+{
+  for (std::string const& o : outputs) {
+    this->UpdateOutputToSourceMap(o, source, false);
+  }
+  for (std::string const& o : byproducts) {
+    this->UpdateOutputToSourceMap(o, source, true);
+  }
+}
+
+void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct,
+                                         cmTarget* target)
+{
+  SourceEntry entry;
+  entry.Sources.Target = target;
+
+  auto pr = this->OutputToSource.emplace(byproduct, entry);
+  if (!pr.second) {
+    SourceEntry& current = pr.first->second;
+    // Has the target already been set?
+    if (!current.Sources.Target) {
+      current.Sources.Target = target;
+    } else {
+      // Multiple custom commands/targets produce the same output (source file
+      // or target).  See also comment in other UpdateOutputToSourceMap
+      // overload.
+      //
+      // TODO: Warn the user about this case.
+    }
+  }
+}
+
+void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
+                                         cmSourceFile* source, bool byproduct)
+{
+  SourceEntry entry;
+  entry.Sources.Source = source;
+  entry.Sources.SourceIsByproduct = byproduct;
+
+  auto pr = this->OutputToSource.emplace(output, entry);
+  if (!pr.second) {
+    SourceEntry& current = pr.first->second;
+    // Outputs take precedence over byproducts
+    if (!current.Sources.Source ||
+        (current.Sources.SourceIsByproduct && !byproduct)) {
+      current.Sources.Source = source;
+      current.Sources.SourceIsByproduct = false;
+    } else {
+      // Multiple custom commands produce the same output but may
+      // be attached to a different source file (MAIN_DEPENDENCY).
+      // LinearGetSourceFileWithOutput would return the first one,
+      // so keep the mapping for the first one.
+      //
+      // TODO: Warn the user about this case.  However, the VS 8 generator
+      // triggers it for separate generate.stamp rules in ZERO_CHECK and
+      // individual targets.
+    }
+  }
+}
+
+#if !defined(CMAKE_BOOTSTRAP)
 cmSourceGroup* cmMakefile::GetSourceGroup(
   const std::vector<std::string>& name) const
 {
@@ -2184,8 +2396,7 @@
   if (delimiter == nullptr) {
     delimiter = "\\";
   }
-  return this->GetOrCreateSourceGroup(
-    cmSystemTools::tokenize(name, delimiter));
+  return this->GetOrCreateSourceGroup(cmTokenize(name, delimiter));
 }
 
 /**
@@ -2199,8 +2410,7 @@
   const std::string& source, std::vector<cmSourceGroup>& groups) const
 {
   // First search for a group that lists the file explicitly.
-  for (std::vector<cmSourceGroup>::reverse_iterator sg = groups.rbegin();
-       sg != groups.rend(); ++sg) {
+  for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) {
     cmSourceGroup* result = sg->MatchChildrenFiles(source);
     if (result) {
       return result;
@@ -2208,8 +2418,7 @@
   }
 
   // Now search for a group whose regex matches the file.
-  for (std::vector<cmSourceGroup>::reverse_iterator sg = groups.rbegin();
-       sg != groups.rend(); ++sg) {
+  for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) {
     cmSourceGroup* result = sg->MatchChildrenRegex(source);
     if (result) {
       return result;
@@ -2290,11 +2499,9 @@
   }
 
   if (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
-    std::vector<std::string> linkLibs;
-    cmSystemTools::ExpandListArgument(linkLibsProp, linkLibs);
+    std::vector<std::string> linkLibs = cmExpandedList(linkLibsProp);
 
-    for (std::vector<std::string>::iterator l = linkLibs.begin();
-         l != linkLibs.end(); ++l) {
+    for (auto l = linkLibs.begin(); l != linkLibs.end(); ++l) {
       std::string libName = *l;
       if (libName == "optimized") {
         ++l;
@@ -2334,7 +2541,7 @@
 bool cmMakefile::IsOn(const std::string& name) const
 {
   const char* value = this->GetDefinition(name);
-  return cmSystemTools::IsOn(value);
+  return cmIsOn(value);
 }
 
 bool cmMakefile::IsSet(const std::string& name) const
@@ -2348,7 +2555,7 @@
     return false;
   }
 
-  if (cmSystemTools::IsNOTFOUND(value)) {
+  if (cmIsNOTFOUND(value)) {
     return false;
   }
 
@@ -2470,7 +2677,7 @@
   if (!def) {
     def = this->GetState()->GetInitializedCacheValue(name);
   }
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   if (cmVariableWatch* vv = this->GetVariableWatch()) {
     if (!def) {
       vv->VariableAccessed(
@@ -2487,7 +2694,7 @@
   if (!def) {
     def = this->GetState()->GetInitializedCacheValue(name);
   }
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmVariableWatch* vv = this->GetVariableWatch();
   if (vv && !this->SuppressSideEffects) {
     bool const watch_function_executed =
@@ -2606,8 +2813,8 @@
   }
   // ...otherwise, see if there's a difference that needs to be warned about.
   else if (compareResults && (newResult != source || newError != mtype)) {
-    std::string msg = cmPolicies::GetPolicyWarning(cmPolicies::CMP0053);
-    msg += "\n";
+    std::string msg =
+      cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0053), '\n');
 
     std::string msg_input = original;
     cmSystemTools::ReplaceString(msg_input, "\n", "\n  ");
@@ -2677,7 +2884,7 @@
       if (const char* val = this->GetDefinition(var)) {
         // Store the value in the output escaping as requested.
         if (escapeQuotes) {
-          source.append(cmSystemTools::EscapeQuotes(val));
+          source.append(cmEscapeQuotes(val));
         } else {
           source.append(val);
         }
@@ -2756,12 +2963,13 @@
   return mtype;
 }
 
-typedef enum
+enum t_domain
 {
   NORMAL,
   ENVIRONMENT,
   CACHE
-} t_domain;
+};
+
 struct t_lookup
 {
   t_domain domain = NORMAL;
@@ -2822,9 +3030,7 @@
           switch (var.domain) {
             case NORMAL:
               if (filename && lookup == lineVar) {
-                std::ostringstream ostr;
-                ostr << line;
-                varresult = ostr.str();
+                varresult = std::to_string(line);
               } else {
                 value = this->GetDefinition(lookup);
               }
@@ -2841,7 +3047,7 @@
           // Get the string we're meant to append to.
           if (value) {
             if (escapeQuotes) {
-              varresult = cmSystemTools::EscapeQuotes(value);
+              varresult = cmEscapeQuotes(value);
             } else {
               varresult = value;
             }
@@ -2967,7 +3173,7 @@
             }
 
             if (escapeQuotes) {
-              varresult = cmSystemTools::EscapeQuotes(varresult);
+              varresult = cmEscapeQuotes(varresult);
             }
             // Skip over the variable.
             result.append(last, in - last);
@@ -3060,7 +3266,7 @@
   if (this->GetGlobalGenerator()->IsMultiConfig()) {
     if (const char* configTypes =
           this->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
-      cmSystemTools::ExpandListArgument(configTypes, configs);
+      cmExpandList(configTypes, configs);
     }
     return "";
   }
@@ -3071,23 +3277,25 @@
   return buildType;
 }
 
+std::vector<std::string> cmMakefile::GetGeneratorConfigs() const
+{
+  std::vector<std::string> configs;
+  GetConfigurations(configs);
+  if (configs.empty()) {
+    configs.emplace_back();
+  }
+  return configs;
+}
+
 bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff,
                                    cmExecutionStatus& status)
 {
   // if there are no blockers get out of here
-  if (this->FunctionBlockers.begin() == this->FunctionBlockers.end()) {
+  if (this->FunctionBlockers.empty()) {
     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 (cmFunctionBlocker* pos : cmReverseRange(this->FunctionBlockers)) {
-    if (pos->IsFunctionBlocked(lff, *this, status)) {
-      return true;
-    }
-  }
-
-  return false;
+  return this->FunctionBlockers.top()->IsFunctionBlocked(lff, status);
 }
 
 void cmMakefile::PushFunctionBlockerBarrier()
@@ -3101,8 +3309,9 @@
   FunctionBlockersType::size_type barrier =
     this->FunctionBlockerBarriers.back();
   while (this->FunctionBlockers.size() > barrier) {
-    std::unique_ptr<cmFunctionBlocker> fb(this->FunctionBlockers.back());
-    this->FunctionBlockers.pop_back();
+    std::unique_ptr<cmFunctionBlocker> fb(
+      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();
@@ -3184,7 +3393,7 @@
     if (i.Delim == cmListFileArgument::Quoted) {
       outArgs.push_back(value);
     } else {
-      cmSystemTools::ExpandListArgument(value, outArgs);
+      cmExpandList(value, outArgs);
     }
   }
   return !cmSystemTools::GetFatalErrorOccured();
@@ -3216,8 +3425,7 @@
     if (i.Delim == cmListFileArgument::Quoted) {
       outArgs.emplace_back(value, true);
     } else {
-      std::vector<std::string> stringArgs;
-      cmSystemTools::ExpandListArgument(value, stringArgs);
+      std::vector<std::string> stringArgs = cmExpandedList(value);
       for (std::string const& stringArg : stringArgs) {
         outArgs.emplace_back(stringArg, false);
       }
@@ -3226,54 +3434,25 @@
   return !cmSystemTools::GetFatalErrorOccured();
 }
 
-void cmMakefile::AddFunctionBlocker(cmFunctionBlocker* fb)
+void cmMakefile::AddFunctionBlocker(std::unique_ptr<cmFunctionBlocker> fb)
 {
   if (!this->ExecutionStatusStack.empty()) {
     // Record the context in which the blocker is created.
     fb->SetStartingContext(this->GetExecutionContext());
   }
 
-  this->FunctionBlockers.push_back(fb);
+  this->FunctionBlockers.push(std::move(fb));
 }
 
-std::unique_ptr<cmFunctionBlocker> cmMakefile::RemoveFunctionBlocker(
-  cmFunctionBlocker* fb, const cmListFileFunction& lff)
+std::unique_ptr<cmFunctionBlocker> cmMakefile::RemoveFunctionBlocker()
 {
-  // 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->FunctionBlockerBarriers.empty() ||
+         this->FunctionBlockers.size() > this->FunctionBlockerBarriers.back());
 
-  // Search for the function blocker whose scope this command ends.
-  for (FunctionBlockersType::size_type i = this->FunctionBlockers.size();
-       i > barrier; --i) {
-    std::vector<cmFunctionBlocker*>::iterator pos =
-      this->FunctionBlockers.begin() + (i - 1);
-    if (*pos == 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());
-      }
-      cmFunctionBlocker* b = *pos;
-      this->FunctionBlockers.erase(pos);
-      return std::unique_ptr<cmFunctionBlocker>(b);
-    }
-  }
-
-  return std::unique_ptr<cmFunctionBlocker>();
+  auto b = std::move(this->FunctionBlockers.top());
+  this->FunctionBlockers.pop();
+  return b;
 }
 
 std::string const& cmMakefile::GetHomeDirectory() const
@@ -3288,20 +3467,18 @@
 
 void cmMakefile::SetScriptModeFile(std::string const& scriptfile)
 {
-  this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile.c_str());
+  this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile);
 }
 
 void cmMakefile::SetArgcArgv(const std::vector<std::string>& args)
 {
-  std::ostringstream strStream;
-  strStream << args.size();
-  this->AddDefinition("CMAKE_ARGC", strStream.str().c_str());
+  this->AddDefinition("CMAKE_ARGC", std::to_string(args.size()));
   // this->MarkVariableAsUsed("CMAKE_ARGC");
 
   for (unsigned int t = 0; t < args.size(); ++t) {
     std::ostringstream tmpStream;
     tmpStream << "CMAKE_ARGV" << t;
-    this->AddDefinition(tmpStream.str(), args[t].c_str());
+    this->AddDefinition(tmpStream.str(), args[t]);
     // this->MarkVariableAsUsed(tmpStream.str().c_str());
   }
 }
@@ -3367,23 +3544,41 @@
   return this->CreateSource(sourceName, generated, kind);
 }
 
+cmSourceFile* cmMakefile::GetOrCreateGeneratedSource(
+  const std::string& sourceName)
+{
+  cmSourceFile* sf =
+    this->GetOrCreateSource(sourceName, true, cmSourceFileLocationKind::Known);
+  sf->SetProperty("GENERATED", "1");
+  return sf;
+}
+
+void cmMakefile::CreateGeneratedSources(
+  const std::vector<std::string>& outputs)
+{
+  for (std::string const& output : outputs) {
+    this->GetOrCreateGeneratedSource(output);
+  }
+}
+
 void cmMakefile::AddTargetObject(std::string const& tgtName,
                                  std::string const& objFile)
 {
   cmSourceFile* sf = this->GetOrCreateSource(objFile, true);
   sf->SetObjectLibrary(tgtName);
   sf->SetProperty("EXTERNAL_OBJECT", "1");
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->SourceGroups[this->ObjectLibrariesSourceGroupIndex].AddGroupFile(
-    sf->GetFullPath());
+    sf->ResolveFullPath());
 #endif
 }
 
 void cmMakefile::EnableLanguage(std::vector<std::string> const& lang,
                                 bool optional)
 {
-  this->AddDefinition("CMAKE_CFG_INTDIR",
-                      this->GetGlobalGenerator()->GetCMakeCFGIntDir());
+  if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) {
+    this->AddDefinition("CMAKE_CFG_INTDIR", def);
+  }
   // If RC is explicitly listed we need to do it after other languages.
   // On some platforms we enable RC implicitly while enabling others.
   // Do not let that look like recursive enable_language(RC).
@@ -3554,7 +3749,7 @@
   return this->GlobalGenerator;
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 cmVariableWatch* cmMakefile::GetVariableWatch() const
 {
   if (this->GetCMakeInstance() &&
@@ -3602,8 +3797,7 @@
   // Always search in CMAKE_MODULE_PATH:
   const char* cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH");
   if (cmakeModulePath) {
-    std::vector<std::string> modulePath;
-    cmSystemTools::ExpandListArgument(cmakeModulePath, modulePath);
+    std::vector<std::string> modulePath = cmExpandedList(cmakeModulePath);
 
     // Look through the possible module directories.
     for (std::string itempl : modulePath) {
@@ -3618,9 +3812,8 @@
   }
 
   // Always search in the standard modules location.
-  moduleInCMakeRoot = cmSystemTools::GetCMakeRoot();
-  moduleInCMakeRoot += "/Modules/";
-  moduleInCMakeRoot += filename;
+  moduleInCMakeRoot =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/", filename);
   cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot);
   if (!cmSystemTools::FileExists(moduleInCMakeRoot)) {
     moduleInCMakeRoot.clear();
@@ -3695,7 +3888,7 @@
     // Replace #cmakedefine instances.
     if (this->cmDefineRegex.find(line)) {
       const char* def = this->GetDefinition(this->cmDefineRegex.match(2));
-      if (!cmSystemTools::IsOff(def)) {
+      if (!cmIsOff(def)) {
         const std::string indentation = this->cmDefineRegex.match(1);
         cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine",
                                      "#" + indentation + "define");
@@ -3711,7 +3904,7 @@
       cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine01",
                                    "#" + indentation + "define");
       output += line;
-      if (!cmSystemTools::IsOff(def)) {
+      if (!cmIsOff(def)) {
         output += " 1";
       } else {
         output += " 0";
@@ -3786,8 +3979,7 @@
     } else {
       newLineCharacters = "\n";
     }
-    std::string tempOutputFile = soutfile;
-    tempOutputFile += ".tmp";
+    std::string tempOutputFile = cmStrCat(soutfile, ".tmp");
     cmsys::ofstream fout(tempOutputFile.c_str(), omode);
     if (!fout) {
       cmSystemTools::Error("Could not open file for write in copy operation " +
@@ -3875,7 +4067,7 @@
 
 bool cmMakefile::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmSystemTools::IsOn(this->GetProperty(prop));
+  return cmIsOn(this->GetProperty(prop));
 }
 
 std::vector<std::string> cmMakefile::GetPropertyKeys() const
@@ -3885,7 +4077,7 @@
 
 cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const
 {
-  cmTargetMap::iterator i = this->Targets.find(name);
+  auto i = this->Targets.find(name);
   if (i != this->Targets.end()) {
     return &i->second;
   }
@@ -3906,8 +4098,7 @@
 
 cmTest* cmMakefile::GetTest(const std::string& testName) const
 {
-  std::map<std::string, cmTest*>::const_iterator mi =
-    this->Tests.find(testName);
+  auto mi = this->Tests.find(testName);
   if (mi != this->Tests.end()) {
     return mi->second;
   }
@@ -3928,15 +4119,13 @@
 {
   std::vector<std::string> deps;
   if (const char* deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) {
-    cmSystemTools::ExpandListArgument(deps_str, deps);
+    cmExpandList(deps_str, deps);
   }
   for (std::string const& dep : deps) {
     if (cmSystemTools::FileIsFullPath(dep)) {
       this->AddCMakeDependFile(dep);
     } else {
-      std::string f = this->GetCurrentSourceDirectory();
-      f += "/";
-      f += dep;
+      std::string f = cmStrCat(this->GetCurrentSourceDirectory(), '/', dep);
       this->AddCMakeDependFile(f);
     }
   }
@@ -3954,7 +4143,7 @@
   std::ostringstream tmp;
   size_t depth = listFiles.size();
   if (depth > 0) {
-    std::vector<std::string>::const_iterator it = listFiles.end();
+    auto it = listFiles.end();
     do {
       if (depth != listFiles.size()) {
         tmp << "\n                ";
@@ -3976,14 +4165,14 @@
     this->GetState()->CreateVariableScopeSnapshot(this->StateSnapshot);
   this->PushLoopBlockBarrier();
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope();
 #endif
 }
 
 void cmMakefile::PopScope()
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope();
 #endif
 
@@ -4007,7 +4196,7 @@
     return;
   }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmVariableWatch* vv = this->GetVariableWatch();
   if (vv) {
     vv->VariableAccessed(var, cmVariableWatch::VARIABLE_MODIFIED_ACCESS,
@@ -4041,7 +4230,7 @@
 {
   // Look for an imported target.  These take priority because they
   // are more local in scope and do not have to be globally unique.
-  TargetMap::const_iterator imported = this->ImportedTargets.find(name);
+  auto imported = this->ImportedTargets.find(name);
   if (imported != this->ImportedTargets.end()) {
     return imported->second;
   }
@@ -4057,7 +4246,7 @@
 
 bool cmMakefile::IsAlias(const std::string& name) const
 {
-  if (this->AliasTargets.find(name) != this->AliasTargets.end()) {
+  if (cmContains(this->AliasTargets, name)) {
     return true;
   }
   return this->GetGlobalGenerator()->IsAlias(name);
@@ -4235,7 +4424,7 @@
     std::string const& m = re.match(i);
     if (!m.empty()) {
       std::string const& var = matchVariables[i];
-      this->AddDefinition(var, m.c_str());
+      this->AddDefinition(var, m);
       this->MarkVariableAsUsed(var);
       highest = static_cast<char>('0' + i);
     }
@@ -4265,7 +4454,7 @@
 {
   // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting.
   if (const char* val = this->GetDefinition(var)) {
-    return cmSystemTools::IsOn(val);
+    return cmIsOn(val);
   }
   // Enable optional policy warnings with --debug-output, --trace,
   // or --trace-expand.
@@ -4298,7 +4487,7 @@
 
   // Deprecate old policies, especially those that require a lot
   // of code to maintain the old behavior.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0066 &&
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0069 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
@@ -4368,7 +4557,7 @@
 void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm)
 {
   /* Record the setting of every policy.  */
-  typedef cmPolicies::PolicyID PolicyID;
+  using PolicyID = cmPolicies::PolicyID;
   for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT;
        pid = PolicyID(pid + 1)) {
     pm.Set(pid, this->GetPolicyStatus(pid));
@@ -4422,10 +4611,8 @@
     return false;
   }
 
-  std::vector<std::string> availableFeatures;
-  cmSystemTools::ExpandListArgument(features, availableFeatures);
-  if (std::find(availableFeatures.begin(), availableFeatures.end(), feature) ==
-      availableFeatures.end()) {
+  std::vector<std::string> availableFeatures = cmExpandedList(features);
+  if (!cmContains(availableFeatures, feature)) {
     std::ostringstream e;
     e << "The compiler feature \"" << feature << "\" is not known to " << lang
       << " compiler\n\""
@@ -4443,9 +4630,9 @@
 
   target->AppendProperty("COMPILE_FEATURES", feature.c_str());
 
-  return lang == "C"
-    ? this->AddRequiredTargetCFeature(target, feature, error)
-    : this->AddRequiredTargetCxxFeature(target, feature, error);
+  return lang == "C" || lang == "OBJC"
+    ? this->AddRequiredTargetCFeature(target, feature, lang, error)
+    : this->AddRequiredTargetCxxFeature(target, feature, lang, error);
 }
 
 bool cmMakefile::CompileFeatureKnown(cmTarget const* target,
@@ -4537,30 +4724,33 @@
                                        std::string const& lang,
                                        const std::string& feature) const
 {
-  return lang == "C" ? this->HaveCStandardAvailable(target, feature)
-                     : this->HaveCxxStandardAvailable(target, feature);
+  return lang == "C" || lang == "OBJC"
+    ? this->HaveCStandardAvailable(target, feature, lang)
+    : this->HaveCxxStandardAvailable(target, feature, lang);
 }
 
 bool cmMakefile::HaveCStandardAvailable(cmTarget const* target,
-                                        const std::string& feature) const
+                                        const std::string& feature,
+                                        std::string const& lang) const
 {
   const char* defaultCStandard =
-    this->GetDefinition("CMAKE_C_STANDARD_DEFAULT");
+    this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
   if (!defaultCStandard) {
-    std::ostringstream e;
-    e << "CMAKE_C_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
-         "not fully configured for this compiler.";
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e.str());
+    this->IssueMessage(
+      MessageType::INTERNAL_ERROR,
+      cmStrCat("CMAKE_", lang,
+               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
+               "not fully configured for this compiler."));
     // Return true so the caller does not try to lookup the default standard.
     return true;
   }
   if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
                    cmStrCmp(defaultCStandard)) == cm::cend(C_STANDARDS)) {
-    std::ostringstream e;
-    e << "The CMAKE_C_STANDARD_DEFAULT variable contains an "
-         "invalid value: \""
-      << defaultCStandard << "\".";
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e.str());
+    const std::string e = cmStrCat("The CMAKE_", lang,
+                                   "_STANDARD_DEFAULT variable contains an "
+                                   "invalid value: \"",
+                                   defaultCStandard, "\".");
+    this->IssueMessage(MessageType::INTERNAL_ERROR, e);
     return false;
   }
 
@@ -4568,19 +4758,20 @@
   bool needC99 = false;
   bool needC11 = false;
 
-  this->CheckNeededCLanguage(feature, needC90, needC99, needC11);
+  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
 
-  const char* existingCStandard = target->GetProperty("C_STANDARD");
+  const char* existingCStandard =
+    target->GetProperty(cmStrCat(lang, "_STANDARD"));
   if (!existingCStandard) {
     existingCStandard = defaultCStandard;
   }
 
   if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
                    cmStrCmp(existingCStandard)) == cm::cend(C_STANDARDS)) {
-    std::ostringstream e;
-    e << "The C_STANDARD property on target \"" << target->GetName()
-      << "\" contained an invalid value: \"" << existingCStandard << "\".";
-    this->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    const std::string e = cmStrCat(
+      "The ", lang, "_STANDARD property on target \"", target->GetName(),
+      "\" contained an invalid value: \"", existingCStandard, "\".");
+    this->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
 
@@ -4611,7 +4802,7 @@
                                  std::string const& lhs,
                                  std::string const& rhs)
 {
-  if (lang == "C") {
+  if (lang == "C" || lang == "OBJC") {
     const char* const* rhsIt = std::find_if(
       cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs));
 
@@ -4626,25 +4817,26 @@
 }
 
 bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target,
-                                          const std::string& feature) const
+                                          const std::string& feature,
+                                          std::string const& lang) const
 {
   const char* defaultCxxStandard =
-    this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
+    this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
   if (!defaultCxxStandard) {
-    std::ostringstream e;
-    e << "CMAKE_CXX_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
-         "not fully configured for this compiler.";
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e.str());
+    this->IssueMessage(
+      MessageType::INTERNAL_ERROR,
+      cmStrCat("CMAKE_", lang,
+               "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
+               "not fully configured for this compiler."));
     // Return true so the caller does not try to lookup the default standard.
     return true;
   }
   if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
                    cmStrCmp(defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) {
-    std::ostringstream e;
-    e << "The CMAKE_CXX_STANDARD_DEFAULT variable contains an "
-         "invalid value: \""
-      << defaultCxxStandard << "\".";
-    this->IssueMessage(MessageType::INTERNAL_ERROR, e.str());
+    const std::string e =
+      cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
+               "invalid value: \"", defaultCxxStandard, "\".");
+    this->IssueMessage(MessageType::INTERNAL_ERROR, e);
     return false;
   }
 
@@ -4653,10 +4845,11 @@
   bool needCxx14 = false;
   bool needCxx17 = false;
   bool needCxx20 = false;
-  this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14,
+  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
                                needCxx17, needCxx20);
 
-  const char* existingCxxStandard = target->GetProperty("CXX_STANDARD");
+  const char* existingCxxStandard =
+    target->GetProperty(cmStrCat(lang, "_STANDARD"));
   if (!existingCxxStandard) {
     existingCxxStandard = defaultCxxStandard;
   }
@@ -4665,10 +4858,10 @@
     std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
                  cmStrCmp(existingCxxStandard));
   if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
-    std::ostringstream e;
-    e << "The CXX_STANDARD property on target \"" << target->GetName()
-      << "\" contained an invalid value: \"" << existingCxxStandard << "\".";
-    this->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    const std::string e = cmStrCat(
+      "The ", lang, "_STANDARD property on target \"", target->GetName(),
+      "\" contained an invalid value: \"", existingCxxStandard, "\".");
+    this->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
 
@@ -4686,44 +4879,41 @@
 }
 
 void cmMakefile::CheckNeededCxxLanguage(const std::string& feature,
+                                        std::string const& lang,
                                         bool& needCxx98, bool& needCxx11,
                                         bool& needCxx14, bool& needCxx17,
                                         bool& needCxx20) const
 {
   if (const char* propCxx98 =
-        this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propCxx98, props);
-    needCxx98 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx98);
+    needCxx98 = cmContains(props, feature);
   }
   if (const char* propCxx11 =
-        this->GetDefinition("CMAKE_CXX11_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propCxx11, props);
-    needCxx11 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx11);
+    needCxx11 = cmContains(props, feature);
   }
   if (const char* propCxx14 =
-        this->GetDefinition("CMAKE_CXX14_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propCxx14, props);
-    needCxx14 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx14);
+    needCxx14 = cmContains(props, feature);
   }
   if (const char* propCxx17 =
-        this->GetDefinition("CMAKE_CXX17_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propCxx17, props);
-    needCxx17 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx17);
+    needCxx17 = cmContains(props, feature);
   }
   if (const char* propCxx20 =
-        this->GetDefinition("CMAKE_CXX20_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propCxx20, props);
-    needCxx20 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propCxx20);
+    needCxx20 = cmContains(props, feature);
   }
 }
 
 bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target,
                                              const std::string& feature,
+                                             std::string const& lang,
                                              std::string* error) const
 {
   bool needCxx98 = false;
@@ -4732,13 +4922,14 @@
   bool needCxx17 = false;
   bool needCxx20 = false;
 
-  this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14,
+  this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
                                needCxx17, needCxx20);
 
-  const char* existingCxxStandard = target->GetProperty("CXX_STANDARD");
+  const char* existingCxxStandard =
+    target->GetProperty(cmStrCat(lang, "_STANDARD"));
   if (existingCxxStandard == nullptr) {
     const char* defaultCxxStandard =
-      this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
+      this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
     if (defaultCxxStandard && *defaultCxxStandard) {
       existingCxxStandard = defaultCxxStandard;
     }
@@ -4749,14 +4940,14 @@
       std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
                    cmStrCmp(existingCxxStandard));
     if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
-      std::ostringstream e;
-      e << "The CXX_STANDARD property on target \"" << target->GetName()
-        << "\" contained an invalid value: \"" << existingCxxStandard << "\".";
+      const std::string e = cmStrCat(
+        "The ", lang, "_STANDARD property on target \"", target->GetName(),
+        "\" contained an invalid value: \"", existingCxxStandard, "\".");
       if (error) {
-        *error = e.str();
+        *error = e;
       } else {
-        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
-                                               e.str(), this->Backtrace);
+        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
+                                               this->Backtrace);
       }
       return false;
     }
@@ -4797,7 +4988,7 @@
     // Ensure the C++ language level is high enough to support
     // the needed C++ features.
     if (!existingCxxLevel || existingCxxLevel < needCxxLevel) {
-      target->SetProperty("CXX_STANDARD", *needCxxLevel);
+      target->SetProperty(cmStrCat(lang, "_STANDARD"), *needCxxLevel);
     }
 
     // Ensure the CUDA language level is high enough to support
@@ -4811,43 +5002,42 @@
 }
 
 void cmMakefile::CheckNeededCLanguage(const std::string& feature,
-                                      bool& needC90, bool& needC99,
-                                      bool& needC11) const
+                                      std::string const& lang, bool& needC90,
+                                      bool& needC99, bool& needC11) const
 {
   if (const char* propC90 =
-        this->GetDefinition("CMAKE_C90_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propC90, props);
-    needC90 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC90);
+    needC90 = cmContains(props, feature);
   }
   if (const char* propC99 =
-        this->GetDefinition("CMAKE_C99_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propC99, props);
-    needC99 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC99);
+    needC99 = cmContains(props, feature);
   }
   if (const char* propC11 =
-        this->GetDefinition("CMAKE_C11_COMPILE_FEATURES")) {
-    std::vector<std::string> props;
-    cmSystemTools::ExpandListArgument(propC11, props);
-    needC11 = std::find(props.begin(), props.end(), feature) != props.end();
+        this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
+    std::vector<std::string> props = cmExpandedList(propC11);
+    needC11 = cmContains(props, feature);
   }
 }
 
 bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target,
                                            const std::string& feature,
+                                           std::string const& lang,
                                            std::string* error) const
 {
   bool needC90 = false;
   bool needC99 = false;
   bool needC11 = false;
 
-  this->CheckNeededCLanguage(feature, needC90, needC99, needC11);
+  this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
 
-  const char* existingCStandard = target->GetProperty("C_STANDARD");
+  const char* existingCStandard =
+    target->GetProperty(cmStrCat(lang, "_STANDARD"));
   if (existingCStandard == nullptr) {
     const char* defaultCStandard =
-      this->GetDefinition("CMAKE_C_STANDARD_DEFAULT");
+      this->GetDefinition(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
     if (defaultCStandard && *defaultCStandard) {
       existingCStandard = defaultCStandard;
     }
@@ -4855,14 +5045,14 @@
   if (existingCStandard) {
     if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
                      cmStrCmp(existingCStandard)) == cm::cend(C_STANDARDS)) {
-      std::ostringstream e;
-      e << "The C_STANDARD property on target \"" << target->GetName()
-        << "\" contained an invalid value: \"" << existingCStandard << "\".";
+      const std::string e = cmStrCat(
+        "The ", lang, "_STANDARD property on target \"", target->GetName(),
+        "\" contained an invalid value: \"", existingCStandard, "\".");
       if (error) {
-        *error = e.str();
+        *error = e;
       } else {
-        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
-                                               e.str(), this->Backtrace);
+        this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
+                                               this->Backtrace);
       }
       return false;
     }
@@ -4893,11 +5083,11 @@
   }
 
   if (setC11) {
-    target->SetProperty("C_STANDARD", "11");
+    target->SetProperty(cmStrCat(lang, "_STANDARD"), "11");
   } else if (setC99) {
-    target->SetProperty("C_STANDARD", "99");
+    target->SetProperty(cmStrCat(lang, "_STANDARD"), "99");
   } else if (setC90) {
-    target->SetProperty("C_STANDARD", "90");
+    target->SetProperty(cmStrCat(lang, "_STANDARD"), "90");
   }
   return true;
 }
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index d223347..6e59494 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -5,18 +5,23 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
+#include <cstddef>
 #include <deque>
+#include <functional>
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <set>
 #include <stack>
-#include <stddef.h>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include <cm/string_view>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
+#include "cmCustomCommandTypes.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 #include "cmNewLineStyle.h"
@@ -24,13 +29,16 @@
 #include "cmSourceFileLocationKind.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
-#include "cmTarget.h"
+#include "cmStringAlgorithms.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+// IWYU does not see that 'std::unordered_map<std::string, cmTarget>'
+// will not compile without the complete type.
+#include "cmTarget.h" // IWYU pragma: keep
+
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmSourceGroup.h"
 #endif
 
-class cmCommand;
 class cmCompiledGeneratorExpression;
 class cmCustomCommandLines;
 class cmExecutionStatus;
@@ -39,6 +47,7 @@
 class cmFunctionBlocker;
 class cmGeneratorExpressionEvaluationFile;
 class cmGlobalGenerator;
+class cmImplicitDependsList;
 class cmInstallGenerator;
 class cmMessenger;
 class cmSourceFile;
@@ -48,6 +57,24 @@
 class cmVariableWatch;
 class cmake;
 
+/** Flag if byproducts shall also be considered.  */
+enum class cmSourceOutputKind
+{
+  OutputOnly,
+  OutputOrByproduct
+};
+
+/** Target and source file which have a specific output.  */
+struct cmSourcesWithOutput
+{
+  /** Target with byproduct.  */
+  cmTarget* Target = nullptr;
+
+  /** Source file with output or byproduct.  */
+  cmSourceFile* Source = nullptr;
+  bool SourceIsByproduct = false;
+};
+
 /** A type-safe wrapper for a string representing a directory id.  */
 class cmDirectoryId
 {
@@ -95,7 +122,7 @@
   /**
    * Add a function blocker to this makefile
    */
-  void AddFunctionBlocker(cmFunctionBlocker* fb);
+  void AddFunctionBlocker(std::unique_ptr<cmFunctionBlocker> fb);
 
   /// @return whether we are processing the top CMakeLists.txt file.
   bool IsRootMakefile() const;
@@ -104,8 +131,7 @@
    * Remove the function blocker whose scope ends with the given command.
    * This returns ownership of the function blocker object.
    */
-  std::unique_ptr<cmFunctionBlocker> RemoveFunctionBlocker(
-    cmFunctionBlocker* fb, const cmListFileFunction& lff);
+  std::unique_ptr<cmFunctionBlocker> RemoveFunctionBlocker();
 
   /**
    * Try running cmake and building a file. This is used for dynalically
@@ -125,6 +151,13 @@
   bool EnforceUniqueName(std::string const& name, std::string& msg,
                          bool isCustom = false) const;
 
+  using FinalAction = std::function<void(cmMakefile&)>;
+
+  /**
+   * Register an action that is executed during FinalPass
+   */
+  void AddFinalAction(FinalAction action);
+
   /**
    * Perform FinalPass, Library dependency analysis etc before output of the
    * makefile.
@@ -132,38 +165,38 @@
   void ConfigureFinalPass();
 
   /**
-   * run the final pass on all commands.
+   * run all FinalActions.
    */
   void FinalPass();
 
-  /** How to handle custom commands for object libraries */
-  enum ObjectLibraryCommands
-  {
-    RejectObjectLibraryCommands,
-    AcceptObjectLibraryCommands
-  };
+  /**
+   * Get the target for PRE_BUILD, PRE_LINK, or POST_BUILD commands.
+   */
+  cmTarget* GetCustomCommandTarget(
+    const std::string& target, cmObjectLibraryCommands objLibCommands) const;
 
   /** Add a custom command to the build.  */
-  void AddCustomCommandToTarget(
+  cmTarget* AddCustomCommandToTarget(
     const std::string& target, const std::vector<std::string>& byproducts,
     const std::vector<std::string>& depends,
-    const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
+    const cmCustomCommandLines& commandLines, cmCustomCommandType type,
     const char* comment, const char* workingDir, bool escapeOldStyle = true,
     bool uses_terminal = false, const std::string& depfile = "",
     const std::string& job_pool = "", bool command_expand_lists = false,
-    ObjectLibraryCommands objLibraryCommands = RejectObjectLibraryCommands);
+    cmObjectLibraryCommands objLibCommands = cmObjectLibraryCommands::Reject);
   cmSourceFile* AddCustomCommandToOutput(
-    const std::vector<std::string>& outputs,
-    const std::vector<std::string>& byproducts,
-    const std::vector<std::string>& depends,
+    const std::string& output, const std::vector<std::string>& depends,
     const std::string& main_dependency,
     const cmCustomCommandLines& commandLines, const char* comment,
     const char* workingDir, bool replace = false, bool escapeOldStyle = true,
     bool uses_terminal = false, bool command_expand_lists = false,
     const std::string& depfile = "", const std::string& job_pool = "");
   cmSourceFile* AddCustomCommandToOutput(
-    const std::string& output, const std::vector<std::string>& depends,
+    const std::vector<std::string>& outputs,
+    const std::vector<std::string>& byproducts,
+    const std::vector<std::string>& depends,
     const std::string& main_dependency,
+    const cmImplicitDependsList& implicit_depends,
     const cmCustomCommandLines& commandLines, const char* comment,
     const char* workingDir, bool replace = false, bool escapeOldStyle = true,
     bool uses_terminal = false, bool command_expand_lists = false,
@@ -174,6 +207,23 @@
                                 const std::string& source,
                                 const cmCustomCommandLines& commandLines,
                                 const char* comment);
+  bool AppendCustomCommandToOutput(
+    const std::string& output, const std::vector<std::string>& depends,
+    const cmImplicitDependsList& implicit_depends,
+    const cmCustomCommandLines& commandLines);
+
+  /**
+   * Add target byproducts.
+   */
+  void AddTargetByproducts(cmTarget* target,
+                           const std::vector<std::string>& byproducts);
+
+  /**
+   * Add source file outputs.
+   */
+  void AddSourceOutputs(cmSourceFile* source,
+                        const std::vector<std::string>& outputs,
+                        const std::vector<std::string>& byproducts);
 
   /**
    * Add a define flag to the build.
@@ -192,6 +242,10 @@
   cmTarget* AddNewTarget(cmStateEnums::TargetType type,
                          const std::string& name);
 
+  /** Create a target instance for the utility.  */
+  cmTarget* AddNewUtilityTarget(const std::string& utilityName,
+                                cmCommandOrigin origin, bool excludeFromAll);
+
   /**
    * Add an executable to the build.
    */
@@ -199,34 +253,19 @@
                           const std::vector<std::string>& srcs,
                           bool excludeFromAll = false);
 
-  /** Where the target originated from. */
-  enum class TargetOrigin
-  {
-    Project,
-    Generator
-  };
+  /**
+   * Return the utility target output source file name and the CMP0049 name.
+   */
+  cmUtilityOutput GetUtilityOutput(cmTarget* target);
 
   /**
    * Add a utility to the build.  A utility target is a command that
    * is run every time the target is built.
    */
-  cmTarget* AddUtilityCommand(const std::string& utilityName,
-                              TargetOrigin origin, bool excludeFromAll,
-                              const std::vector<std::string>& depends,
-                              const char* workingDirectory,
-                              const char* command, const char* arg1 = nullptr,
-                              const char* arg2 = nullptr,
-                              const char* arg3 = nullptr,
-                              const char* arg4 = nullptr);
   cmTarget* AddUtilityCommand(
-    const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-    const char* workingDirectory, const std::vector<std::string>& depends,
-    const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
-    const char* comment = nullptr, bool uses_terminal = false,
-    bool command_expand_lists = false, const std::string& job_pool = "");
-  cmTarget* AddUtilityCommand(
-    const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-    const char* workingDirectory, const std::vector<std::string>& byproducts,
+    const std::string& utilityName, cmCommandOrigin origin,
+    bool excludeFromAll, const char* workingDirectory,
+    const std::vector<std::string>& byproducts,
     const std::vector<std::string>& depends,
     const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
     const char* comment = nullptr, bool uses_terminal = false,
@@ -256,18 +295,17 @@
    * Add a variable definition to the build. This variable
    * can be used in CMake to refer to lists, directories, etc.
    */
-  void AddDefinition(const std::string& name, const char* value);
+  void AddDefinition(const std::string& name, cm::string_view value);
+  /**
+   * Add bool variable definition to the build.
+   */
+  void AddDefinitionBool(const std::string& name, bool);
   //! Add a definition to this makefile and the global cmake cache.
   void AddCacheDefinition(const std::string& name, const char* value,
                           const char* doc, cmStateEnums::CacheEntryType type,
                           bool force = false);
 
   /**
-   * Add bool variable definition to the build.
-   */
-  void AddDefinition(const std::string& name, bool);
-
-  /**
    * Remove a variable definition from the build.  This is not valid
    * for cache entries, and will only affect the current makefile.
    */
@@ -284,6 +322,9 @@
   std::string GetConfigurations(std::vector<std::string>& configs,
                                 bool single = true) const;
 
+  /** Get the configurations for dependency checking.  */
+  std::vector<std::string> GetGeneratorConfigs() const;
+
   /**
    * Set the name of the library.
    */
@@ -374,7 +415,7 @@
   }
 
   // -- List of targets
-  typedef std::unordered_map<std::string, cmTarget> cmTargetMap;
+  using cmTargetMap = std::unordered_map<std::string, cmTarget>;
   /** Get the target map */
   cmTargetMap& GetTargets() { return this->Targets; }
   /** Get the target map - const version */
@@ -428,6 +469,12 @@
     const std::string& sourceName, bool generated = false,
     cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous);
 
+  /** Get a cmSourceFile pointer for a given source name and always mark the
+   * file as generated, if the name is not found, then create the source file
+   * and return it.
+   */
+  cmSourceFile* GetOrCreateGeneratedSource(const std::string& sourceName);
+
   void AddTargetObject(std::string const& tgtName, std::string const& objFile);
 
   /**
@@ -495,7 +542,7 @@
    */
   bool CanIWriteThisFile(std::string const& fileName) const;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   /**
    * Get the vector source groups.
    */
@@ -619,6 +666,11 @@
   void PrintCommandTrace(const cmListFileFunction& lff) const;
 
   /**
+   * Set a callback that is invoked whenever ExecuteCommand is called.
+   */
+  void OnExecuteCommand(std::function<void()> callback);
+
+  /**
    * Execute a single CMake command.  Returns true if the command
    * succeeded or false if it failed.
    */
@@ -636,7 +688,7 @@
  * Get the variable watch. This is used to determine when certain variables
  * are accessed.
  */
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmVariableWatch* GetVariableWatch() const;
 #endif
 
@@ -671,10 +723,19 @@
   }
 
   /**
-   * Is there a source file that has the provided source file as an output?
-   * if so then return it
+   * Return the target if the provided source name is a byproduct of a utility
+   * target or a PRE_BUILD, PRE_LINK, or POST_BUILD command.
+   * Return the source file which has the provided source name as output.
    */
-  cmSourceFile* GetSourceFileWithOutput(const std::string& outName) const;
+  cmSourcesWithOutput GetSourcesWithOutput(const std::string& name) const;
+
+  /**
+   * Is there a source file that has the provided source name as an output?
+   * If so then return it.
+   */
+  cmSourceFile* GetSourceFileWithOutput(
+    const std::string& name,
+    cmSourceOutputKind kind = cmSourceOutputKind::OutputOnly) const;
 
   //! Add a new cmTest to the list of tests for this makefile.
   cmTest* CreateTest(const std::string& testName);
@@ -898,7 +959,10 @@
   mutable cmTargetMap Targets;
   std::map<std::string, std::string> AliasTargets;
 
-  typedef std::vector<cmSourceFile*> SourceFileVec;
+  using TargetsVec = std::vector<cmTarget*>;
+  TargetsVec OrderedTargets;
+
+  using SourceFileVec = std::vector<cmSourceFile*>;
   SourceFileVec SourceFiles;
 
   // Because cmSourceFile names are compared in a fuzzy way (see
@@ -907,7 +971,7 @@
   // Name portion of the cmSourceFileLocation and then compare on the list of
   // cmSourceFiles that might match that name.  Note that on platforms which
   // have a case-insensitive filesystem we store the key in all lowercase.
-  typedef std::unordered_map<std::string, SourceFileVec> SourceFileMap;
+  using SourceFileMap = std::unordered_map<std::string, SourceFileVec>;
   SourceFileMap SourceFileSearchIndex;
 
   // For "Known" paths we can store a direct filename to cmSourceFile map
@@ -932,12 +996,12 @@
   // Track the value of the computed DEFINITIONS property.
   std::string DefineFlagsOrig;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   std::vector<cmSourceGroup> SourceGroups;
   size_t ObjectLibrariesSourceGroupIndex;
 #endif
 
-  std::vector<cmCommand*> FinalPassCommands;
+  std::vector<FinalAction> FinalActions;
   cmGlobalGenerator* GlobalGenerator;
   bool IsFunctionBlocked(const cmListFileFunction& lff,
                          cmExecutionStatus& status);
@@ -955,7 +1019,10 @@
   bool EnforceUniqueDir(const std::string& srcPath,
                         const std::string& binPath) const;
 
-  typedef std::vector<cmFunctionBlocker*> FunctionBlockersType;
+  std::function<void()> ExecuteCommandCallback;
+  using FunctionBlockerPtr = std::unique_ptr<cmFunctionBlocker>;
+  using FunctionBlockersType =
+    std::stack<FunctionBlockerPtr, std::vector<FunctionBlockerPtr>>;
   FunctionBlockersType FunctionBlockers;
   std::vector<FunctionBlockersType::size_type> FunctionBlockerBarriers;
   void PushFunctionBlockerBarrier();
@@ -978,7 +1045,7 @@
   friend class cmParseFileScope;
 
   std::vector<cmTarget*> ImportedTargetsOwned;
-  typedef std::unordered_map<std::string, cmTarget*> TargetMap;
+  using TargetMap = std::unordered_map<std::string, cmTarget*>;
   TargetMap ImportedTargets;
 
   // Internal policy stack management.
@@ -986,15 +1053,15 @@
                   cmPolicies::PolicyMap const& pm = cmPolicies::PolicyMap());
   void PopPolicy();
   void PopSnapshot(bool reportError = true);
-  friend class cmCMakePolicyCommand;
+  friend bool cmCMakePolicyCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status);
   class IncludeScope;
-
   friend class IncludeScope;
+
   class ListFileScope;
-
   friend class ListFileScope;
-  class BuildsystemFileScope;
 
+  class BuildsystemFileScope;
   friend class BuildsystemFileScope;
 
   // CMP0053 == old
@@ -1010,40 +1077,98 @@
                                          bool escapeQuotes, bool noEscapes,
                                          bool atOnly, const char* filename,
                                          long line, bool replaceAt) const;
+
+  bool ValidateCustomCommand(const cmCustomCommandLines& commandLines) const;
+
+  void CreateGeneratedSources(const std::vector<std::string>& outputs);
+
+  void CommitCustomCommandToTarget(
+    cmTarget* target, const std::vector<std::string>& byproducts,
+    const std::vector<std::string>& depends,
+    const cmCustomCommandLines& commandLines, cmCustomCommandType type,
+    const char* comment, const char* workingDir, bool escapeOldStyle,
+    bool uses_terminal, const std::string& depfile,
+    const std::string& job_pool, bool command_expand_lists);
+  cmSourceFile* CommitCustomCommandToOutput(
+    const std::vector<std::string>& outputs,
+    const std::vector<std::string>& byproducts,
+    const std::vector<std::string>& depends,
+    const std::string& main_dependency,
+    const cmImplicitDependsList& implicit_depends,
+    const cmCustomCommandLines& commandLines, const char* comment,
+    const char* workingDir, bool replace, bool escapeOldStyle,
+    bool uses_terminal, bool command_expand_lists, const std::string& depfile,
+    const std::string& job_pool);
+  void CommitAppendCustomCommandToOutput(
+    const std::string& output, const std::vector<std::string>& depends,
+    const cmImplicitDependsList& implicit_depends,
+    const cmCustomCommandLines& commandLines);
+
+  void CommitUtilityCommand(cmTarget* target, const cmUtilityOutput& force,
+                            const char* workingDirectory,
+                            const std::vector<std::string>& byproducts,
+                            const std::vector<std::string>& depends,
+                            const cmCustomCommandLines& commandLines,
+                            bool escapeOldStyle, const char* comment,
+                            bool uses_terminal, bool command_expand_lists,
+                            const std::string& job_pool);
+
   /**
-   * Old version of GetSourceFileWithOutput(const std::string&) kept for
-   * backward-compatibility. It implements a linear search and support
-   * relative file paths. It is used as a fall back by
-   * GetSourceFileWithOutput(const std::string&).
+   * See LinearGetSourceFileWithOutput for background information
    */
-  cmSourceFile* LinearGetSourceFileWithOutput(const std::string& cname) const;
+  cmTarget* LinearGetTargetWithOutput(const std::string& name) const;
+
+  /**
+   * Generalized old version of GetSourceFileWithOutput kept for
+   * backward-compatibility. It implements a linear search and supports
+   * relative file paths. It is used as a fall back by GetSourceFileWithOutput
+   * and GetSourcesWithOutput.
+   */
+  cmSourceFile* LinearGetSourceFileWithOutput(const std::string& name,
+                                              cmSourceOutputKind kind,
+                                              bool& byproduct) const;
+
+  struct SourceEntry
+  {
+    cmSourcesWithOutput Sources;
+  };
 
   // A map for fast output to input look up.
-  typedef std::unordered_map<std::string, cmSourceFile*> OutputToSourceMap;
+  using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>;
   OutputToSourceMap OutputToSource;
 
-  void UpdateOutputToSourceMap(std::vector<std::string> const& outputs,
-                               cmSourceFile* source);
-  void UpdateOutputToSourceMap(std::string const& output,
-                               cmSourceFile* source);
+  void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target);
+  void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
+                               bool byproduct);
+
+  /**
+   * Return if the provided source file might have a custom command.
+   */
+  bool MightHaveCustomCommand(const std::string& name) const;
 
   bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature,
+                                 std::string const& lang,
                                  std::string* error = nullptr) const;
 
   bool AddRequiredTargetCxxFeature(cmTarget* target,
                                    const std::string& feature,
+                                   std::string const& lang,
                                    std::string* error = nullptr) const;
 
-  void CheckNeededCLanguage(const std::string& feature, bool& needC90,
+  void CheckNeededCLanguage(const std::string& feature,
+                            std::string const& lang, bool& needC90,
                             bool& needC99, bool& needC11) const;
-  void CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98,
+  void CheckNeededCxxLanguage(const std::string& feature,
+                              std::string const& lang, bool& needCxx98,
                               bool& needCxx11, bool& needCxx14,
                               bool& needCxx17, bool& needCxx20) const;
 
   bool HaveCStandardAvailable(cmTarget const* target,
-                              const std::string& feature) const;
+                              const std::string& feature,
+                              std::string const& lang) const;
   bool HaveCxxStandardAvailable(cmTarget const* target,
-                                const std::string& feature) const;
+                                const std::string& feature,
+                                std::string const& lang) const;
 
   void CheckForUnusedVariables() const;
 
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 6b9b9c7..40265ff 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMakefileExecutableTargetGenerator.h"
 
-#include <memory> // IWYU pragma: keep
 #include <set>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <cm/memory>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
@@ -25,6 +26,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
@@ -81,7 +83,7 @@
 void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
   bool relink)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   const bool requiresDeviceLinking = requireDeviceLinking(
     *this->GeneratorTarget, *this->LocalGenerator, this->ConfigName);
   if (!requiresDeviceLinking) {
@@ -109,14 +111,13 @@
     cmLocalUnixMakefileGenerator3::EchoProgress progress;
     this->MakeEchoProgress(progress);
     // Add the link message.
-    std::string buildEcho = "Linking ";
-    buildEcho += linkLanguage;
-    buildEcho += " device code ";
-    buildEcho += this->LocalGenerator->ConvertToOutputFormat(
-      this->LocalGenerator->MaybeConvertToRelativePath(
-        this->LocalGenerator->GetCurrentBinaryDirectory(),
-        this->DeviceLinkObject),
-      cmOutputConverter::SHELL);
+    std::string buildEcho =
+      cmStrCat("Linking ", linkLanguage, " device code ",
+               this->LocalGenerator->ConvertToOutputFormat(
+                 this->LocalGenerator->MaybeConvertToRelativePath(
+                   this->LocalGenerator->GetCurrentBinaryDirectory(),
+                   this->DeviceLinkObject),
+                 cmOutputConverter::SHELL));
     this->LocalGenerator->AppendEcho(
       commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
   }
@@ -128,11 +129,10 @@
   // Add flags to create an executable.
   // Add symbol export flags if necessary.
   if (this->GeneratorTarget->IsExecutableWithExports()) {
-    std::string export_flag_var = "CMAKE_EXE_EXPORTS_";
-    export_flag_var += linkLanguage;
-    export_flag_var += "_FLAG";
+    std::string export_flag_var =
+      cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG");
     this->LocalGenerator->AppendFlags(
-      linkFlags, this->Makefile->GetDefinition(export_flag_var));
+      linkFlags, this->Makefile->GetSafeDefinition(export_flag_var));
   }
 
   this->LocalGenerator->AppendFlags(linkFlags,
@@ -163,7 +163,7 @@
   const std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE";
   const std::string linkRule = this->GetLinkRule(linkRuleVar);
   std::vector<std::string> commands1;
-  cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
+  cmExpandList(linkRule, real_link_commands);
 
   bool useResponseFileForObjects =
     this->CheckUseResponseFileForObjects(linkLanguage);
@@ -232,8 +232,7 @@
     const char* val = this->LocalGenerator->GetRuleLauncher(
       this->GeneratorTarget, "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -242,7 +241,7 @@
     // Expand placeholders in the commands.
     rulePlaceholderExpander->SetTargetImpLib(targetOutputReal);
     for (std::string& real_link_command : real_link_commands) {
-      real_link_command = launcher + real_link_command;
+      real_link_command = cmStrCat(launcher, real_link_command);
       rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                    real_link_command, vars);
     }
@@ -295,14 +294,13 @@
   if (this->GeneratorTarget->IsAppBundleOnApple()) {
     this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath);
   }
-  outpath += "/";
+  outpath += '/';
   std::string outpathImp;
   if (relink) {
-    outpath = this->Makefile->GetCurrentBinaryDirectory();
-    outpath += "/CMakeFiles";
-    outpath += "/CMakeRelink.dir";
+    outpath = cmStrCat(this->Makefile->GetCurrentBinaryDirectory(),
+                       "/CMakeFiles/CMakeRelink.dir");
     cmSystemTools::MakeDirectory(outpath);
-    outpath += "/";
+    outpath += '/';
     if (!targetNames.ImportLibrary.empty()) {
       outpathImp = outpath;
     }
@@ -312,7 +310,7 @@
       outpathImp = this->GeneratorTarget->GetDirectory(
         this->ConfigName, cmStateEnums::ImportLibraryArtifact);
       cmSystemTools::MakeDirectory(outpathImp);
-      outpathImp += "/";
+      outpathImp += '/';
     }
   }
 
@@ -323,7 +321,7 @@
   std::string pdbOutputPath =
     this->GeneratorTarget->GetPDBDirectory(this->ConfigName);
   cmSystemTools::MakeDirectory(pdbOutputPath);
-  pdbOutputPath += "/";
+  pdbOutputPath += '/';
 
   std::string targetFullPath = outpath + targetNames.Output;
   std::string targetFullPathReal = outpath + targetNames.Real;
@@ -370,10 +368,8 @@
     cmLocalUnixMakefileGenerator3::EchoProgress progress;
     this->MakeEchoProgress(progress);
     // Add the link message.
-    std::string buildEcho = "Linking ";
-    buildEcho += linkLanguage;
-    buildEcho += " executable ";
-    buildEcho += targetOutPath;
+    std::string buildEcho =
+      cmStrCat("Linking ", linkLanguage, " executable ", targetOutPath);
     this->LocalGenerator->AppendEcho(
       commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
   }
@@ -388,19 +384,19 @@
 
   if (this->GeneratorTarget->GetPropertyAsBool("WIN32_EXECUTABLE")) {
     this->LocalGenerator->AppendFlags(
-      linkFlags, this->Makefile->GetDefinition("CMAKE_CREATE_WIN32_EXE"));
+      linkFlags, this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE"));
   } else {
     this->LocalGenerator->AppendFlags(
-      linkFlags, this->Makefile->GetDefinition("CMAKE_CREATE_CONSOLE_EXE"));
+      linkFlags,
+      this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE"));
   }
 
   // Add symbol export flags if necessary.
   if (this->GeneratorTarget->IsExecutableWithExports()) {
-    std::string export_flag_var = "CMAKE_EXE_EXPORTS_";
-    export_flag_var += linkLanguage;
-    export_flag_var += "_FLAG";
+    std::string export_flag_var =
+      cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG");
     this->LocalGenerator->AppendFlags(
-      linkFlags, this->Makefile->GetDefinition(export_flag_var));
+      linkFlags, this->Makefile->GetSafeDefinition(export_flag_var));
   }
 
   this->LocalGenerator->AppendFlags(linkFlags,
@@ -482,20 +478,18 @@
 
   // Construct the main link rule.
   std::vector<std::string> real_link_commands;
-  std::string linkRuleVar = "CMAKE_";
-  linkRuleVar += linkLanguage;
-  linkRuleVar += "_LINK_EXECUTABLE";
+  std::string linkRuleVar = this->GeneratorTarget->GetCreateRuleVariable(
+    linkLanguage, this->ConfigName);
   std::string linkRule = this->GetLinkRule(linkRuleVar);
   std::vector<std::string> commands1;
-  cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
+  cmExpandList(linkRule, real_link_commands);
   if (this->GeneratorTarget->IsExecutableWithExports()) {
     // If a separate rule for creating an import library is specified
     // add it now.
-    std::string implibRuleVar = "CMAKE_";
-    implibRuleVar += linkLanguage;
-    implibRuleVar += "_CREATE_IMPORT_LIBRARY";
+    std::string implibRuleVar =
+      cmStrCat("CMAKE_", linkLanguage, "_CREATE_IMPORT_LIBRARY");
     if (const char* rule = this->Makefile->GetDefinition(implibRuleVar)) {
-      cmSystemTools::ExpandListArgument(rule, real_link_commands);
+      cmExpandList(rule, real_link_commands);
     }
   }
 
@@ -590,10 +584,10 @@
     vars.Manifests = manifests.c_str();
 
     if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-      std::string cmakeCommand = this->LocalGenerator->ConvertToOutputFormat(
-        cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
-      cmakeCommand += " -E __run_co_compile --lwyu=";
-      cmakeCommand += targetOutPathReal;
+      std::string cmakeCommand =
+        cmStrCat(this->LocalGenerator->ConvertToOutputFormat(
+                   cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+                 " -E __run_co_compile --lwyu=", targetOutPathReal);
       real_link_commands.push_back(std::move(cmakeCommand));
     }
 
@@ -602,8 +596,7 @@
     const char* val = this->LocalGenerator->GetRuleLauncher(
       this->GeneratorTarget, "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -612,7 +605,7 @@
     // Expand placeholders in the commands.
     rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
     for (std::string& real_link_command : real_link_commands) {
-      real_link_command = launcher + real_link_command;
+      real_link_command = cmStrCat(launcher, real_link_command);
       rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                    real_link_command, vars);
     }
@@ -639,10 +632,9 @@
 
   // Add a rule to create necessary symlinks for the library.
   if (targetOutPath != targetOutPathReal) {
-    std::string symlink = "$(CMAKE_COMMAND) -E cmake_symlink_executable ";
-    symlink += targetOutPathReal;
-    symlink += " ";
-    symlink += targetOutPath;
+    std::string symlink =
+      cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_executable ",
+               targetOutPathReal, ' ', targetOutPath);
     commands1.push_back(std::move(symlink));
     this->LocalGenerator->CreateCDCommand(
       commands1, this->Makefile->GetCurrentBinaryDirectory(),
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index b9f7c6d..54a6606 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -2,13 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMakefileLibraryTargetGenerator.h"
 
-#include <memory> // IWYU pragma: keep
+#include <cstddef>
 #include <set>
 #include <sstream>
-#include <stddef.h>
 #include <utility>
 #include <vector>
 
+#include <cm/memory>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
@@ -25,6 +26,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
@@ -161,9 +163,8 @@
 
   std::string linkLanguage =
     this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-  std::string linkRuleVar = "CMAKE_";
-  linkRuleVar += linkLanguage;
-  linkRuleVar += "_CREATE_SHARED_LIBRARY";
+  std::string linkRuleVar =
+    cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_LIBRARY");
 
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
@@ -196,9 +197,8 @@
 
   std::string linkLanguage =
     this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-  std::string linkRuleVar = "CMAKE_";
-  linkRuleVar += linkLanguage;
-  linkRuleVar += "_CREATE_SHARED_MODULE";
+  std::string linkRuleVar =
+    cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_MODULE");
 
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
@@ -219,9 +219,8 @@
 {
   std::string linkLanguage =
     this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-  std::string linkRuleVar = "CMAKE_";
-  linkRuleVar += linkLanguage;
-  linkRuleVar += "_CREATE_MACOSX_FRAMEWORK";
+  std::string linkRuleVar =
+    cmStrCat("CMAKE_", linkLanguage, "_CREATE_MACOSX_FRAMEWORK");
 
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
@@ -234,7 +233,7 @@
 void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
   const std::string& linkRuleVar, bool relink)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   // TODO: Merge the methods that call this method to avoid
   // code duplication.
   std::vector<std::string> commands;
@@ -262,12 +261,13 @@
     cmLocalUnixMakefileGenerator3::EchoProgress progress;
     this->MakeEchoProgress(progress);
     // Add the link message.
-    std::string buildEcho = "Linking " + linkLanguage + " device code ";
-    buildEcho += this->LocalGenerator->ConvertToOutputFormat(
-      this->LocalGenerator->MaybeConvertToRelativePath(
-        this->LocalGenerator->GetCurrentBinaryDirectory(),
-        this->DeviceLinkObject),
-      cmOutputConverter::SHELL);
+    std::string buildEcho =
+      cmStrCat("Linking ", linkLanguage, " device code ",
+               this->LocalGenerator->ConvertToOutputFormat(
+                 this->LocalGenerator->MaybeConvertToRelativePath(
+                   this->LocalGenerator->GetCurrentBinaryDirectory(),
+                   this->DeviceLinkObject),
+                 cmOutputConverter::SHELL));
     this->LocalGenerator->AppendEcho(
       commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
   }
@@ -298,19 +298,16 @@
 
     // Collect up flags to link in needed libraries.
     std::string linkLibs;
-    if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {
+    std::unique_ptr<cmLinkLineComputer> linkLineComputer(
+      new cmLinkLineDeviceComputer(
+        this->LocalGenerator,
+        this->LocalGenerator->GetStateSnapshot().GetDirectory()));
+    linkLineComputer->SetForResponse(useResponseFileForLibs);
+    linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
+    linkLineComputer->SetRelink(relink);
 
-      std::unique_ptr<cmLinkLineComputer> linkLineComputer(
-        new cmLinkLineDeviceComputer(
-          this->LocalGenerator,
-          this->LocalGenerator->GetStateSnapshot().GetDirectory()));
-      linkLineComputer->SetForResponse(useResponseFileForLibs);
-      linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
-      linkLineComputer->SetRelink(relink);
-
-      this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
-                           useResponseFileForLibs, depends);
-    }
+    this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
+                         useResponseFileForLibs, depends);
 
     // Construct object file lists that may be needed to expand the
     // rule.
@@ -358,8 +355,7 @@
     const char* val = this->LocalGenerator->GetRuleLauncher(
       this->GeneratorTarget, "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -368,11 +364,11 @@
     // Construct the main link rule and expand placeholders.
     rulePlaceholderExpander->SetTargetImpLib(targetOutputReal);
     std::string linkRule = this->GetLinkRule(linkRuleVar);
-    cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
+    cmExpandList(linkRule, real_link_commands);
 
     // Expand placeholders.
     for (std::string& real_link_command : real_link_commands) {
-      real_link_command = launcher + real_link_command;
+      real_link_command = cmStrCat(launcher, real_link_command);
       rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                    real_link_command, vars);
     }
@@ -463,30 +459,29 @@
     outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
     this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
                                               outpath);
-    outpath += "/";
+    outpath += '/';
   } else if (this->GeneratorTarget->IsCFBundleOnApple()) {
     outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
     this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output,
                                              outpath);
-    outpath += "/";
+    outpath += '/';
   } else if (relink) {
-    outpath = this->Makefile->GetCurrentBinaryDirectory();
-    outpath += "/CMakeFiles";
-    outpath += "/CMakeRelink.dir";
+    outpath = cmStrCat(this->Makefile->GetCurrentBinaryDirectory(),
+                       "/CMakeFiles/CMakeRelink.dir");
     cmSystemTools::MakeDirectory(outpath);
-    outpath += "/";
+    outpath += '/';
     if (!this->TargetNames.ImportLibrary.empty()) {
       outpathImp = outpath;
     }
   } else {
     outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
     cmSystemTools::MakeDirectory(outpath);
-    outpath += "/";
+    outpath += '/';
     if (!this->TargetNames.ImportLibrary.empty()) {
       outpathImp = this->GeneratorTarget->GetDirectory(
         this->ConfigName, cmStateEnums::ImportLibraryArtifact);
       cmSystemTools::MakeDirectory(outpathImp);
-      outpathImp += "/";
+      outpathImp += '/';
     }
   }
 
@@ -535,8 +530,7 @@
     cmLocalUnixMakefileGenerator3::EchoProgress progress;
     this->MakeEchoProgress(progress);
     // Add the link message.
-    std::string buildEcho = "Linking ";
-    buildEcho += linkLanguage;
+    std::string buildEcho = cmStrCat("Linking ", linkLanguage);
     switch (this->GeneratorTarget->GetType()) {
       case cmStateEnums::STATIC_LIBRARY:
         buildEcho += " static library ";
@@ -640,35 +634,32 @@
   std::string::size_type archiveCommandLimit = std::string::npos;
   if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
     haveStaticLibraryRule = this->Makefile->IsDefinitionSet(linkRuleVar);
-    std::string arCreateVar = "CMAKE_";
-    arCreateVar += linkLanguage;
-    arCreateVar += "_ARCHIVE_CREATE";
+    std::string arCreateVar =
+      cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_CREATE");
 
     arCreateVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
       arCreateVar, linkLanguage, this->ConfigName);
 
     if (const char* rule = this->Makefile->GetDefinition(arCreateVar)) {
-      cmSystemTools::ExpandListArgument(rule, archiveCreateCommands);
+      cmExpandList(rule, archiveCreateCommands);
     }
-    std::string arAppendVar = "CMAKE_";
-    arAppendVar += linkLanguage;
-    arAppendVar += "_ARCHIVE_APPEND";
+    std::string arAppendVar =
+      cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_APPEND");
 
     arAppendVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
       arAppendVar, linkLanguage, this->ConfigName);
 
     if (const char* rule = this->Makefile->GetDefinition(arAppendVar)) {
-      cmSystemTools::ExpandListArgument(rule, archiveAppendCommands);
+      cmExpandList(rule, archiveAppendCommands);
     }
-    std::string arFinishVar = "CMAKE_";
-    arFinishVar += linkLanguage;
-    arFinishVar += "_ARCHIVE_FINISH";
+    std::string arFinishVar =
+      cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_FINISH");
 
     arFinishVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
       arFinishVar, linkLanguage, this->ConfigName);
 
     if (const char* rule = this->Makefile->GetDefinition(arFinishVar)) {
-      cmSystemTools::ExpandListArgument(rule, archiveFinishCommands);
+      cmExpandList(rule, archiveFinishCommands);
     }
   }
 
@@ -820,8 +811,7 @@
     const char* val = this->LocalGenerator->GetRuleLauncher(
       this->GeneratorTarget, "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -844,7 +834,7 @@
       }
 
       // Create the archive with the first set of objects.
-      std::vector<std::string>::iterator osi = object_strings.begin();
+      auto osi = object_strings.begin();
       {
         vars.Objects = osi->c_str();
         for (std::string const& acc : archiveCreateCommands) {
@@ -878,19 +868,19 @@
     } else {
       // Get the set of commands.
       std::string linkRule = this->GetLinkRule(linkRuleVar);
-      cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
+      cmExpandList(linkRule, real_link_commands);
       if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
           (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY)) {
-        std::string cmakeCommand = this->LocalGenerator->ConvertToOutputFormat(
-          cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
-        cmakeCommand += " -E __run_co_compile --lwyu=";
-        cmakeCommand += targetOutPathReal;
+        std::string cmakeCommand = cmStrCat(
+          this->LocalGenerator->ConvertToOutputFormat(
+            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+          " -E __run_co_compile --lwyu=", targetOutPathReal);
         real_link_commands.push_back(std::move(cmakeCommand));
       }
 
       // Expand placeholders.
       for (std::string& real_link_command : real_link_commands) {
-        real_link_command = launcher + real_link_command;
+        real_link_command = cmStrCat(launcher, real_link_command);
         rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                      real_link_command, vars);
       }
@@ -920,12 +910,9 @@
   // Frameworks are handled by cmOSXBundleGenerator.
   if (targetOutPath != targetOutPathReal &&
       !this->GeneratorTarget->IsFrameworkOnApple()) {
-    std::string symlink = "$(CMAKE_COMMAND) -E cmake_symlink_library ";
-    symlink += targetOutPathReal;
-    symlink += " ";
-    symlink += targetOutPathSO;
-    symlink += " ";
-    symlink += targetOutPath;
+    std::string symlink =
+      cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_library ", targetOutPathReal,
+               ' ', targetOutPathSO, ' ', targetOutPath);
     commands1.push_back(std::move(symlink));
     this->LocalGenerator->CreateCDCommand(
       commands1, this->Makefile->GetCurrentBinaryDirectory(),
diff --git a/Source/cmMakefileLibraryTargetGenerator.h b/Source/cmMakefileLibraryTargetGenerator.h
index 35e4327..ca22b09 100644
--- a/Source/cmMakefileLibraryTargetGenerator.h
+++ b/Source/cmMakefileLibraryTargetGenerator.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmMakefileTargetGenerator.h"
-
 #include <string>
 
+#include "cmMakefileTargetGenerator.h"
+
 class cmGeneratorTarget;
 
 class cmMakefileLibraryTargetGenerator : public cmMakefileTargetGenerator
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index b3bab4b..51804d2 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -2,9 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMakefileTargetGenerator.h"
 
-#include <memory> // IWYU pragma: keep
+#include <cstdio>
+#include <memory>
 #include <sstream>
-#include <stdio.h>
 #include <utility>
 
 #include "cmAlgorithms.h"
@@ -15,6 +15,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalUnixMakefileGenerator3.h"
+#include "cmLocalCommonGenerator.h"
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmMakefile.h"
 #include "cmMakefileExecutableTargetGenerator.h"
@@ -28,6 +29,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -48,7 +50,7 @@
   this->NoRuleMessages = false;
   if (const char* ruleStatus =
         cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
-    this->NoRuleMessages = cmSystemTools::IsOff(ruleStatus);
+    this->NoRuleMessages = cmIsOff(ruleStatus);
   }
   MacOSXContentGenerator = new MacOSXContentGeneratorType(this);
 }
@@ -87,12 +89,12 @@
   std::string& flags, const std::string& linkLanguage)
 {
   this->LocalGenerator->AppendFlags(
-    flags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
+    flags, this->GeneratorTarget->GetSafeProperty("LINK_FLAGS"));
 
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
+  std::string linkFlagsConfig =
+    cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(this->ConfigName));
   this->LocalGenerator->AppendFlags(
-    flags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+    flags, this->GeneratorTarget->GetSafeProperty(linkFlagsConfig));
 
   std::vector<std::string> opts;
   this->GeneratorTarget->GetLinkOptions(opts, this->ConfigName, linkLanguage);
@@ -113,14 +115,13 @@
   cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull);
 
   // Construct the rule file name.
-  this->BuildFileName = this->TargetBuildDirectory;
-  this->BuildFileName += "/build.make";
-  this->BuildFileNameFull = this->TargetBuildDirectoryFull;
-  this->BuildFileNameFull += "/build.make";
+  this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make");
+  this->BuildFileNameFull =
+    cmStrCat(this->TargetBuildDirectoryFull, "/build.make");
 
   // Construct the rule file name.
-  this->ProgressFileNameFull = this->TargetBuildDirectoryFull;
-  this->ProgressFileNameFull += "/progress.make";
+  this->ProgressFileNameFull =
+    cmStrCat(this->TargetBuildDirectoryFull, "/progress.make");
 
   // reset the progress count
   this->NumberOfProgressActions = 0;
@@ -130,10 +131,10 @@
   this->BuildFileStream =
     new cmGeneratedFileStream(this->BuildFileNameFull, false,
                               this->GlobalGenerator->GetMakefileEncoding());
-  this->BuildFileStream->SetCopyIfDifferent(true);
   if (!this->BuildFileStream) {
     return;
   }
+  this->BuildFileStream->SetCopyIfDifferent(true);
   this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream);
   if (this->GlobalGenerator->AllowDeleteOnError()) {
     std::vector<std::string> no_depends;
@@ -149,18 +150,13 @@
 {
   // -- Write the custom commands for this target
 
-  const std::string& config =
-    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
-
   // Evaluates generator expressions and expands prop_value
   auto evaluatedFiles =
-    [this, &config](const char* prop_value) -> std::vector<std::string> {
+    [this](const char* prop_value) -> std::vector<std::string> {
     std::vector<std::string> files;
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop_value);
-    cmSystemTools::ExpandListArgument(
-      cge->Evaluate(this->LocalGenerator, config, false, this->GeneratorTarget,
-                    nullptr, nullptr),
+    cmExpandList(
+      cmGeneratorExpression::Evaluate(prop_value, this->LocalGenerator,
+                                      this->ConfigName, this->GeneratorTarget),
       files);
     return files;
   };
@@ -186,12 +182,12 @@
 
   // add custom commands to the clean rules?
   const char* clean_no_custom = this->Makefile->GetProperty("CLEAN_NO_CUSTOM");
-  bool clean = cmSystemTools::IsOff(clean_no_custom);
+  bool clean = cmIsOff(clean_no_custom);
 
   // First generate the object rule files.  Save a list of all object
   // files for this target.
   std::vector<cmSourceFile const*> customCommands;
-  this->GeneratorTarget->GetCustomCommands(customCommands, config);
+  this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName);
   std::string currentBinDir =
     this->LocalGenerator->GetCurrentBinaryDirectory();
   for (cmSourceFile const* sf : customCommands) {
@@ -224,7 +220,8 @@
              this->GeneratorTarget->GetPostBuildCommands());
 
     for (const auto& be : buildEventCommands) {
-      const std::vector<std::string>& byproducts = be.GetByproducts();
+      cmCustomCommandGenerator beg(be, this->ConfigName, this->LocalGenerator);
+      const std::vector<std::string>& byproducts = beg.GetByproducts();
       for (std::string const& byproduct : byproducts) {
         this->CleanFiles.insert(
           this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir,
@@ -233,20 +230,25 @@
     }
   }
   std::vector<cmSourceFile const*> headerSources;
-  this->GeneratorTarget->GetHeaderSources(headerSources, config);
+  this->GeneratorTarget->GetHeaderSources(headerSources, this->ConfigName);
   this->OSXBundleGenerator->GenerateMacOSXContentStatements(
     headerSources, this->MacOSXContentGenerator);
   std::vector<cmSourceFile const*> extraSources;
-  this->GeneratorTarget->GetExtraSources(extraSources, config);
+  this->GeneratorTarget->GetExtraSources(extraSources, this->ConfigName);
   this->OSXBundleGenerator->GenerateMacOSXContentStatements(
     extraSources, this->MacOSXContentGenerator);
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
   std::vector<cmSourceFile const*> externalObjects;
-  this->GeneratorTarget->GetExternalObjects(externalObjects, config);
+  this->GeneratorTarget->GetExternalObjects(externalObjects, this->ConfigName);
   for (cmSourceFile const* sf : externalObjects) {
-    this->ExternalObjects.push_back(sf->GetFullPath());
+    auto const& objectFileName = sf->GetFullPath();
+    if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+      this->ExternalObjects.push_back(objectFileName);
+    }
   }
   std::vector<cmSourceFile const*> objectSources;
-  this->GeneratorTarget->GetObjectSources(objectSources, config);
+  this->GeneratorTarget->GetObjectSources(objectSources, this->ConfigName);
   for (cmSourceFile const* sf : objectSources) {
     // Generate this object file's rule file.
     this->WriteObjectRuleFiles(*sf);
@@ -260,8 +262,8 @@
                         : "");
 
   // Include the dependencies for the target.
-  std::string dependFileNameFull = this->TargetBuildDirectoryFull;
-  dependFileNameFull += "/depend.make";
+  std::string dependFileNameFull =
+    cmStrCat(this->TargetBuildDirectoryFull, "/depend.make");
   *this->BuildFileStream
     << "# Include any dependencies generated for this target.\n"
     << this->GlobalGenerator->IncludeDirective << " " << root
@@ -295,15 +297,15 @@
 
   // Open the flags file.  This should be copy-if-different because the
   // rules may depend on this file itself.
-  this->FlagFileNameFull = this->TargetBuildDirectoryFull;
-  this->FlagFileNameFull += "/flags.make";
+  this->FlagFileNameFull =
+    cmStrCat(this->TargetBuildDirectoryFull, "/flags.make");
   this->FlagFileStream =
     new cmGeneratedFileStream(this->FlagFileNameFull, false,
                               this->GlobalGenerator->GetMakefileEncoding());
-  this->FlagFileStream->SetCopyIfDifferent(true);
   if (!this->FlagFileStream) {
     return;
   }
+  this->FlagFileStream->SetCopyIfDifferent(true);
   this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream);
 
   // Include the flags for the target.
@@ -325,9 +327,7 @@
   // put the compiler in the rules.make file so that if it changes
   // things rebuild
   for (std::string const& language : languages) {
-    std::string compiler = "CMAKE_";
-    compiler += language;
-    compiler += "_COMPILER";
+    std::string compiler = cmStrCat("CMAKE_", language, "_COMPILER");
     *this->FlagFileStream << "# compile " << language << " with "
                           << this->Makefile->GetSafeDefinition(compiler)
                           << "\n";
@@ -362,9 +362,8 @@
   std::string const& input = source.GetFullPath();
 
   // Get the output file location.
-  std::string output = macdir;
-  output += "/";
-  output += cmSystemTools::GetFilenameName(input);
+  std::string output =
+    cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
   this->Generator->CleanFiles.insert(
     this->Generator->LocalGenerator->MaybeConvertToRelativePath(
       this->Generator->LocalGenerator->GetCurrentBinaryDirectory(), output));
@@ -375,16 +374,16 @@
   std::vector<std::string> depends;
   std::vector<std::string> commands;
   depends.push_back(input);
-  std::string copyEcho = "Copying OS X content ";
-  copyEcho += output;
+  std::string copyEcho = cmStrCat("Copying OS X content ", output);
   this->Generator->LocalGenerator->AppendEcho(
     commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
-  std::string copyCommand = "$(CMAKE_COMMAND) -E copy ";
-  copyCommand += this->Generator->LocalGenerator->ConvertToOutputFormat(
-    input, cmOutputConverter::SHELL);
-  copyCommand += " ";
-  copyCommand += this->Generator->LocalGenerator->ConvertToOutputFormat(
-    output, cmOutputConverter::SHELL);
+  std::string copyCommand =
+    cmStrCat("$(CMAKE_COMMAND) -E copy ",
+             this->Generator->LocalGenerator->ConvertToOutputFormat(
+               input, cmOutputConverter::SHELL),
+             ' ',
+             this->Generator->LocalGenerator->ConvertToOutputFormat(
+               output, cmOutputConverter::SHELL));
   commands.push_back(std::move(copyCommand));
   this->Generator->LocalGenerator->WriteMakeRule(
     *this->Generator->BuildFileStream, nullptr, output, depends, commands,
@@ -407,9 +406,8 @@
   std::string const& objectName =
     this->GeneratorTarget->GetObjectName(&source);
   std::string obj =
-    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-  obj += "/";
-  obj += objectName;
+    cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+             '/', objectName);
 
   // Avoid generating duplicate rules.
   if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) {
@@ -445,9 +443,8 @@
   this->WriteObjectBuildFile(obj, lang, source, depends);
 
   // The object file should be checked for dependency integrity.
-  std::string objFullPath = this->LocalGenerator->GetCurrentBinaryDirectory();
-  objFullPath += "/";
-  objFullPath += obj;
+  std::string objFullPath =
+    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj);
   objFullPath = cmSystemTools::CollapseFullPath(objFullPath);
   std::string srcFullPath =
     cmSystemTools::CollapseFullPath(source.GetFullPath());
@@ -467,21 +464,30 @@
   // generate the depend scanning rule
   this->WriteObjectDependRules(source, depends);
 
-  std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath();
-  relativeObj += obj;
+  std::string config = this->LocalGenerator->GetConfigName();
+  std::string configUpper = cmSystemTools::UpperCase(config);
+
+  // Add precompile headers dependencies
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(config, lang);
+  if (!pchSource.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    depends.push_back(this->GeneratorTarget->GetPchHeader(config, lang));
+    if (source.GetFullPath() != pchSource) {
+      depends.push_back(this->GeneratorTarget->GetPchFile(config, lang));
+    }
+  }
+
+  std::string relativeObj =
+    cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj);
   // Write the build rule.
 
   // Build the set of compiler flags.
   std::string flags;
 
   // Add language-specific flags.
-  std::string langFlags = "$(";
-  langFlags += lang;
-  langFlags += "_FLAGS)";
+  std::string langFlags = cmStrCat("$(", lang, "_FLAGS)");
   this->LocalGenerator->AppendFlags(flags, langFlags);
 
-  std::string config = this->LocalGenerator->GetConfigName();
-  std::string configUpper = cmSystemTools::UpperCase(config);
   cmGeneratorExpressionInterpreter genexInterpreter(
     this->LocalGenerator, config, this->GeneratorTarget, lang);
 
@@ -511,6 +517,26 @@
                           << "\n";
   }
 
+  // Add precompile headers compile options.
+  if (!pchSource.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (source.GetFullPath() == pchSource) {
+      pchOptions =
+        this->GeneratorTarget->GetPchCreateCompileOptions(config, lang);
+    } else {
+      pchOptions =
+        this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
+    }
+
+    const std::string& evaluatedFlags =
+      genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS);
+
+    this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags);
+    *this->FlagFileStream << "# PCH options: " << relativeObj
+                          << "_OPTIONS = " << evaluatedFlags << "\n"
+                          << "\n";
+  }
+
   // Add include directories from source file properties.
   std::vector<std::string> includes;
 
@@ -539,8 +565,7 @@
                           << "_DEFINES = " << evaluatedDefs << "\n"
                           << "\n";
   }
-  std::string defPropName = "COMPILE_DEFINITIONS_";
-  defPropName += configUpper;
+  std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
   if (const char* config_compile_defs = source.GetProperty(defPropName)) {
     const std::string& evaluatedDefs =
       genexInterpreter.Evaluate(config_compile_defs, COMPILE_DEFINITIONS);
@@ -564,10 +589,8 @@
   if (!this->NoRuleMessages) {
     cmLocalUnixMakefileGenerator3::EchoProgress progress;
     this->MakeEchoProgress(progress);
-    std::string buildEcho = "Building ";
-    buildEcho += lang;
-    buildEcho += " object ";
-    buildEcho += relativeObj;
+    std::string buildEcho =
+      cmStrCat("Building ", lang, " object ", relativeObj);
     this->LocalGenerator->AppendEcho(commands, buildEcho,
                                      cmLocalUnixMakefileGenerator3::EchoBuild,
                                      &progress);
@@ -587,9 +610,8 @@
       targetFullPathReal = this->GeneratorTarget->GetFullPath(
         this->ConfigName, cmStateEnums::RuntimeBinaryArtifact, true);
       targetFullPathPDB =
-        this->GeneratorTarget->GetPDBDirectory(this->ConfigName);
-      targetFullPathPDB += "/";
-      targetFullPathPDB += this->GeneratorTarget->GetPDBName(this->ConfigName);
+        cmStrCat(this->GeneratorTarget->GetPDBDirectory(this->ConfigName), '/',
+                 this->GeneratorTarget->GetPDBName(this->ConfigName));
     }
 
     targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
@@ -638,9 +660,7 @@
   vars.ObjectFileDir = objectFileDir.c_str();
   vars.Flags = flags.c_str();
 
-  std::string definesString = "$(";
-  definesString += lang;
-  definesString += "_DEFINES)";
+  std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
 
   this->LocalGenerator->JoinDefines(defines, definesString, lang);
 
@@ -679,12 +699,12 @@
       }
       const std::string& compileRule =
         this->Makefile->GetRequiredDefinition(cmdVar);
-      cmSystemTools::ExpandListArgument(compileRule, compileCommands);
+      cmExpandList(compileRule, compileCommands);
     } else {
       const std::string cmdVar = "CMAKE_" + lang + "_COMPILE_OBJECT";
       const std::string& compileRule =
         this->Makefile->GetRequiredDefinition(cmdVar);
-      cmSystemTools::ExpandListArgument(compileRule, compileCommands);
+      cmExpandList(compileRule, compileCommands);
     }
 
     if (this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS") &&
@@ -781,8 +801,7 @@
     // If compiler launcher was specified and not consumed above, it
     // goes to the beginning of the command line.
     if (!compileCommands.empty() && !compilerLauncher.empty()) {
-      std::vector<std::string> args;
-      cmSystemTools::ExpandListArgument(compilerLauncher, args, true);
+      std::vector<std::string> args = cmExpandedList(compilerLauncher, true);
       if (!args.empty()) {
         args[0] = this->LocalGenerator->ConvertToOutputFormat(
           args[0], cmOutputConverter::SHELL);
@@ -798,14 +817,13 @@
       const char* val = this->LocalGenerator->GetRuleLauncher(
         this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
       if (val && *val) {
-        launcher = val;
-        launcher += " ";
+        launcher = cmStrCat(val, ' ');
       }
     }
 
     // Expand placeholders in the commands.
     for (std::string& compileCommand : compileCommands) {
-      compileCommand = launcher + compileCommand;
+      compileCommand = cmStrCat(launcher, compileCommand);
       rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                    compileCommand, vars);
     }
@@ -821,7 +839,7 @@
   std::vector<std::string> outputs(1, relativeObj);
   if (const char* extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
     // Register these as extra files to clean.
-    cmSystemTools::ExpandListArgument(extra_outputs_str, outputs);
+    cmExpandList(extra_outputs_str, outputs);
     this->CleanFiles.insert(outputs.begin() + 1, outputs.end());
   }
 
@@ -846,20 +864,17 @@
       std::string relativeObjI = relativeObjBase + ".i";
       std::string objI = objBase + ".i";
 
-      std::string preprocessEcho = "Preprocessing ";
-      preprocessEcho += lang;
-      preprocessEcho += " source to ";
-      preprocessEcho += objI;
+      std::string preprocessEcho =
+        cmStrCat("Preprocessing ", lang, " source to ", objI);
       this->LocalGenerator->AppendEcho(
         commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
 
-      std::string preprocessRuleVar = "CMAKE_";
-      preprocessRuleVar += lang;
-      preprocessRuleVar += "_CREATE_PREPROCESSED_SOURCE";
+      std::string preprocessRuleVar =
+        cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE");
       if (const char* preprocessRule =
             this->Makefile->GetDefinition(preprocessRuleVar)) {
-        std::vector<std::string> preprocessCommands;
-        cmSystemTools::ExpandListArgument(preprocessRule, preprocessCommands);
+        std::vector<std::string> preprocessCommands =
+          cmExpandedList(preprocessRule);
 
         std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat(
           objI, cmOutputConverter::SHELL);
@@ -878,8 +893,9 @@
           this->LocalGenerator->GetBinaryDirectory());
         cmAppend(commands, preprocessCommands);
       } else {
-        std::string cmd = "$(CMAKE_COMMAND) -E cmake_unimplemented_variable ";
-        cmd += preprocessRuleVar;
+        std::string cmd =
+          cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
+                   preprocessRuleVar);
         commands.push_back(std::move(cmd));
       }
 
@@ -893,20 +909,17 @@
       std::string relativeObjS = relativeObjBase + ".s";
       std::string objS = objBase + ".s";
 
-      std::string assemblyEcho = "Compiling ";
-      assemblyEcho += lang;
-      assemblyEcho += " source to assembly ";
-      assemblyEcho += objS;
+      std::string assemblyEcho =
+        cmStrCat("Compiling ", lang, " source to assembly ", objS);
       this->LocalGenerator->AppendEcho(
         commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
 
-      std::string assemblyRuleVar = "CMAKE_";
-      assemblyRuleVar += lang;
-      assemblyRuleVar += "_CREATE_ASSEMBLY_SOURCE";
+      std::string assemblyRuleVar =
+        cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE");
       if (const char* assemblyRule =
             this->Makefile->GetDefinition(assemblyRuleVar)) {
-        std::vector<std::string> assemblyCommands;
-        cmSystemTools::ExpandListArgument(assemblyRule, assemblyCommands);
+        std::vector<std::string> assemblyCommands =
+          cmExpandedList(assemblyRule);
 
         std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat(
           objS, cmOutputConverter::SHELL);
@@ -924,8 +937,9 @@
           this->LocalGenerator->GetBinaryDirectory());
         cmAppend(commands, assemblyCommands);
       } else {
-        std::string cmd = "$(CMAKE_COMMAND) -E cmake_unimplemented_variable ";
-        cmd += assemblyRuleVar;
+        std::string cmd =
+          cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
+                   assemblyRuleVar);
         commands.push_back(std::move(cmd));
       }
 
@@ -942,9 +956,9 @@
   std::vector<std::string> commands;
 
   // Construct the clean target name.
-  std::string cleanTarget =
-    this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
-  cleanTarget += "/clean";
+  std::string cleanTarget = cmStrCat(
+    this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
+    "/clean");
 
   // Construct the clean command.
   this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles,
@@ -1028,15 +1042,14 @@
   // must write the targets depend info file
   std::string dir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-  this->InfoFileNameFull = dir;
-  this->InfoFileNameFull += "/DependInfo.cmake";
+  this->InfoFileNameFull = cmStrCat(dir, "/DependInfo.cmake");
   this->InfoFileNameFull =
     this->LocalGenerator->ConvertToFullPath(this->InfoFileNameFull);
   this->InfoFileStream = new cmGeneratedFileStream(this->InfoFileNameFull);
-  this->InfoFileStream->SetCopyIfDifferent(true);
-  if (!*this->InfoFileStream) {
+  if (!this->InfoFileStream) {
     return;
   }
+  this->InfoFileStream->SetCopyIfDifferent(true);
   this->LocalGenerator->WriteDependLanguageInfo(*this->InfoFileStream,
                                                 this->GeneratorTarget);
 
@@ -1088,9 +1101,9 @@
   std::vector<std::string> commands;
 
   // Construct the name of the dependency generation target.
-  std::string depTarget =
-    this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
-  depTarget += "/depend";
+  std::string depTarget = cmStrCat(
+    this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
+    "/depend");
 
   // Add a command to call CMake to scan dependencies.  CMake will
   // touch the corresponding depends file after scanning dependencies.
@@ -1181,7 +1194,7 @@
   // shared between the object file and dependency scanning rule.
   depends.push_back(source.GetFullPath());
   if (const char* objectDeps = source.GetProperty("OBJECT_DEPENDS")) {
-    cmSystemTools::ExpandListArgument(objectDeps, depends);
+    cmExpandList(objectDeps, depends);
   }
 }
 
@@ -1235,8 +1248,8 @@
 void cmMakefileTargetGenerator::MakeEchoProgress(
   cmLocalUnixMakefileGenerator3::EchoProgress& progress) const
 {
-  progress.Dir = this->LocalGenerator->GetBinaryDirectory();
-  progress.Dir += "/CMakeFiles";
+  progress.Dir =
+    cmStrCat(this->LocalGenerator->GetBinaryDirectory(), "/CMakeFiles");
   std::ostringstream progressArg;
   progressArg << "$(CMAKE_PROGRESS_" << this->NumberOfProgressActions << ")";
   progress.Arg = progressArg.str();
@@ -1259,7 +1272,14 @@
   if (!lineContinue) {
     lineContinue = "\\";
   }
+
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     *this->BuildFileStream << " " << lineContinue << "\n";
     *this->BuildFileStream
       << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
@@ -1352,10 +1372,16 @@
 void cmMakefileTargetGenerator::WriteObjectsStrings(
   std::vector<std::string>& objStrings, std::string::size_type limit)
 {
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   cmMakefileTargetGeneratorObjectStrings helper(
     objStrings, this->LocalGenerator,
     this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     helper.Feed(obj);
   }
   for (std::string const& obj : this->ExternalObjects) {
@@ -1370,8 +1396,8 @@
   // Compute the name of the driver target.
   std::string dir =
     this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
-  std::string buildTargetRuleName = dir;
-  buildTargetRuleName += relink ? "/preinstall" : "/build";
+  std::string buildTargetRuleName =
+    cmStrCat(dir, relink ? "/preinstall" : "/build");
   buildTargetRuleName = this->LocalGenerator->MaybeConvertToRelativePath(
     this->LocalGenerator->GetBinaryDirectory(), buildTargetRuleName);
 
@@ -1426,8 +1452,7 @@
   std::string const& relPath =
     this->LocalGenerator->GetHomeRelativeOutputPath();
   for (std::string const& obj : this->Objects) {
-    std::string objTarget = relPath;
-    objTarget += obj;
+    std::string objTarget = cmStrCat(relPath, obj);
     depends.push_back(std::move(objTarget));
   }
 
@@ -1473,9 +1498,9 @@
 {
   std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar);
   if (this->GeneratorTarget->HasImplibGNUtoMS(this->ConfigName)) {
-    std::string ruleVar = "CMAKE_";
-    ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-    ruleVar += "_GNUtoMS_RULE";
+    std::string ruleVar = cmStrCat(
+      "CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+      "_GNUtoMS_RULE");
     if (const char* rule = this->Makefile->GetDefinition(ruleVar)) {
       linkRule += rule;
     }
@@ -1496,9 +1521,8 @@
   std::vector<std::string>& makefile_depends)
 {
   // Create the link script file.
-  std::string linkScriptName = this->TargetBuildDirectoryFull;
-  linkScriptName += "/";
-  linkScriptName += name;
+  std::string linkScriptName =
+    cmStrCat(this->TargetBuildDirectoryFull, '/', name);
   cmGeneratedFileStream linkScriptStream(linkScriptName);
   linkScriptStream.SetCopyIfDifferent(true);
   for (std::string const& link_command : link_commands) {
@@ -1510,12 +1534,13 @@
   }
 
   // Create the makefile command to invoke the link script.
-  std::string link_command = "$(CMAKE_COMMAND) -E cmake_link_script ";
-  link_command += this->LocalGenerator->ConvertToOutputFormat(
-    this->LocalGenerator->MaybeConvertToRelativePath(
-      this->LocalGenerator->GetCurrentBinaryDirectory(), linkScriptName),
-    cmOutputConverter::SHELL);
-  link_command += " --verbose=$(VERBOSE)";
+  std::string link_command = cmStrCat(
+    "$(CMAKE_COMMAND) -E cmake_link_script ",
+    this->LocalGenerator->ConvertToOutputFormat(
+      this->LocalGenerator->MaybeConvertToRelativePath(
+        this->LocalGenerator->GetCurrentBinaryDirectory(), linkScriptName),
+      cmOutputConverter::SHELL),
+    " --verbose=$(VERBOSE)");
   makefile_commands.push_back(std::move(link_command));
   makefile_depends.push_back(std::move(linkScriptName));
 }
@@ -1528,7 +1553,7 @@
     "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS";
   if (const char* val = this->Makefile->GetDefinition(responseVar)) {
     if (*val) {
-      return cmSystemTools::IsOn(val);
+      return cmIsOn(val);
     }
   }
 
@@ -1567,7 +1592,7 @@
     "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES";
   if (const char* val = this->Makefile->GetDefinition(responseVar)) {
     if (*val) {
-      return cmSystemTools::IsOn(val);
+      return cmIsOn(val);
     }
   }
 
@@ -1580,9 +1605,8 @@
   std::vector<std::string>& makefile_depends)
 {
   // Create the response file.
-  std::string responseFileNameFull = this->TargetBuildDirectoryFull;
-  responseFileNameFull += "/";
-  responseFileNameFull += name;
+  std::string responseFileNameFull =
+    cmStrCat(this->TargetBuildDirectoryFull, '/', name);
   cmGeneratedFileStream responseStream(responseFileNameFull);
   responseStream.SetCopyIfDifferent(true);
   responseStream << options << "\n";
@@ -1592,9 +1616,8 @@
   makefile_depends.push_back(std::move(responseFileNameFull));
 
   // Construct the name to be used on the command line.
-  std::string responseFileName = this->TargetBuildDirectory;
-  responseFileName += "/";
-  responseFileName += name;
+  std::string responseFileName =
+    cmStrCat(this->TargetBuildDirectory, '/', name);
   return responseFileName;
 }
 
@@ -1615,10 +1638,8 @@
 {
   std::string frameworkPath;
   std::string linkPath;
-  const std::string& config =
-    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   cmComputeLinkInformation* pcli =
-    this->GeneratorTarget->GetLinkInformation(config);
+    this->GeneratorTarget->GetLinkInformation(this->ConfigName);
   this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
                                             frameworkPath, linkPath);
   linkLibs = frameworkPath + linkPath + linkLibs;
@@ -1626,10 +1647,9 @@
   if (useResponseFile &&
       linkLibs.find_first_not_of(' ') != std::string::npos) {
     // Lookup the response file reference flag.
-    std::string responseFlagVar = "CMAKE_";
-    responseFlagVar +=
-      this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-    responseFlagVar += "_RESPONSE_FILE_LINK_FLAG";
+    std::string responseFlagVar = cmStrCat(
+      "CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+      "_RESPONSE_FILE_LINK_FLAG");
     const char* responseFlag = this->Makefile->GetDefinition(responseFlagVar);
     if (!responseFlag) {
       responseFlag = "@";
@@ -1640,9 +1660,9 @@
       this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends);
 
     // Reference the response file.
-    linkLibs = responseFlag;
-    linkLibs += this->LocalGenerator->ConvertToOutputFormat(
-      link_rsp, cmOutputConverter::SHELL);
+    linkLibs = cmStrCat(responseFlag,
+                        this->LocalGenerator->ConvertToOutputFormat(
+                          link_rsp, cmOutputConverter::SHELL));
   }
 }
 
@@ -1664,10 +1684,9 @@
     this->WriteObjectsStrings(object_strings, responseFileLimit);
 
     // Lookup the response file reference flag.
-    std::string responseFlagVar = "CMAKE_";
-    responseFlagVar +=
-      this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-    responseFlagVar += "_RESPONSE_FILE_LINK_FLAG";
+    std::string responseFlagVar = cmStrCat(
+      "CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+      "_RESPONSE_FILE_LINK_FLAG");
     const char* responseFlag = this->Makefile->GetDefinition(responseFlagVar);
     if (!responseFlag) {
       responseFlag = "@";
@@ -1700,30 +1719,25 @@
       buildObjs = objStrings[0];
     }
   } else {
-    buildObjs = "$(";
-    buildObjs += variableName;
-    buildObjs += ") $(";
-    buildObjs += variableNameExternal;
-    buildObjs += ")";
+    buildObjs =
+      cmStrCat("$(", variableName, ") $(", variableNameExternal, ')');
   }
 }
 
 void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
                                                 const std::string& lang)
 {
-  std::string responseVar = "CMAKE_";
-  responseVar += lang;
-  responseVar += "_USE_RESPONSE_FILE_FOR_INCLUDES";
+  std::string responseVar =
+    cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES");
   bool useResponseFile = this->Makefile->IsOn(responseVar);
 
   std::vector<std::string> includes;
-  const std::string& config =
-    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
-                                              lang, config);
+                                              lang, this->ConfigName);
 
   std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
-    includes, this->GeneratorTarget, lang, false, useResponseFile, config);
+    includes, this->GeneratorTarget, lang, false, useResponseFile,
+    this->ConfigName);
   if (includeFlags.empty()) {
     return;
   }
@@ -1736,9 +1750,7 @@
     if (responseFlag.empty()) {
       responseFlag = "@";
     }
-    std::string name = "includes_";
-    name += lang;
-    name += ".rsp";
+    std::string name = cmStrCat("includes_", lang, ".rsp");
     std::string arg = std::move(responseFlag) +
       this->CreateResponseFile(name.c_str(), includeFlags,
                                this->FlagFileDepends[lang]);
@@ -1757,19 +1769,25 @@
     return;
   }
   std::string cmd = cmSystemTools::GetCMakeCommand();
-  cmd =
-    this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL);
-  cmd += " -E __create_def ";
-  cmd += this->LocalGenerator->ConvertToOutputFormat(
-    this->LocalGenerator->MaybeConvertToRelativePath(
-      this->LocalGenerator->GetCurrentBinaryDirectory(), mdi->DefFile),
-    cmOutputConverter::SHELL);
-  cmd += " ";
+  cmd = cmStrCat(
+    this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL),
+    " -E __create_def ",
+    this->LocalGenerator->ConvertToOutputFormat(
+      this->LocalGenerator->MaybeConvertToRelativePath(
+        this->LocalGenerator->GetCurrentBinaryDirectory(), mdi->DefFile),
+      cmOutputConverter::SHELL),
+    ' ');
   std::string objlist_file = mdi->DefFile + ".objs";
   cmd += this->LocalGenerator->ConvertToOutputFormat(
     this->LocalGenerator->MaybeConvertToRelativePath(
       this->LocalGenerator->GetCurrentBinaryDirectory(), objlist_file),
     cmOutputConverter::SHELL);
+  const char* nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
+  if (nm_executable && *nm_executable) {
+    cmd += " --nm=";
+    cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
+      nm_executable, cmOutputConverter::SHELL);
+  }
   real_link_commands.insert(real_link_commands.begin(), cmd);
   // create a list of obj files for the -E __create_def to read
   cmGeneratedFileStream fout(objlist_file);
diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h
index c570a7c..f464128 100644
--- a/Source/cmMakefileTargetGenerator.h
+++ b/Source/cmMakefileTargetGenerator.h
@@ -7,6 +7,7 @@
 
 #include <iosfwd>
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
@@ -222,7 +223,7 @@
   // Set of extra output files to be driven by the build.
   std::set<std::string> ExtraFiles;
 
-  typedef std::map<std::string, std::string> MultipleOutputPairsType;
+  using MultipleOutputPairsType = std::map<std::string, std::string>;
   MultipleOutputPairsType MultipleOutputPairs;
   bool WriteMakeRule(std::ostream& os, const char* comment,
                      const std::vector<std::string>& outputs,
diff --git a/Source/cmMakefileUtilityTargetGenerator.cxx b/Source/cmMakefileUtilityTargetGenerator.cxx
index 4236995..1625e4f 100644
--- a/Source/cmMakefileUtilityTargetGenerator.cxx
+++ b/Source/cmMakefileUtilityTargetGenerator.cxx
@@ -7,7 +7,8 @@
 #include <utility>
 #include <vector>
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalUnixMakefileGenerator3.h"
diff --git a/Source/cmMarkAsAdvancedCommand.cxx b/Source/cmMarkAsAdvancedCommand.cxx
index 45c59b9..ca46e14 100644
--- a/Source/cmMarkAsAdvancedCommand.cxx
+++ b/Source/cmMarkAsAdvancedCommand.cxx
@@ -2,20 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMarkAsAdvancedCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
 // cmMarkAsAdvancedCommand
-bool cmMarkAsAdvancedCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmMarkAsAdvancedCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -31,9 +30,9 @@
   }
   for (; i < args.size(); ++i) {
     std::string const& variable = args[i];
-    cmState* state = this->Makefile->GetState();
+    cmState* state = status.GetMakefile().GetState();
     if (!state->GetCacheEntryValue(variable)) {
-      this->Makefile->GetCMakeInstance()->AddCacheEntry(
+      status.GetMakefile().GetCMakeInstance()->AddCacheEntry(
         variable, nullptr, nullptr, cmStateEnums::UNINITIALIZED);
       overwrite = true;
     }
diff --git a/Source/cmMarkAsAdvancedCommand.h b/Source/cmMarkAsAdvancedCommand.h
index 5dd198f..de7bf08 100644
--- a/Source/cmMarkAsAdvancedCommand.h
+++ b/Source/cmMarkAsAdvancedCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmMarkAsAdvancedCommand
+/**
  * \brief mark_as_advanced command
  *
  * cmMarkAsAdvancedCommand implements the mark_as_advanced CMake command
  */
-class cmMarkAsAdvancedCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmMarkAsAdvancedCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmMarkAsAdvancedCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmMathCommand.cxx b/Source/cmMathCommand.cxx
index 48b9a27..f11b906 100644
--- a/Source/cmMathCommand.cxx
+++ b/Source/cmMathCommand.cxx
@@ -2,35 +2,42 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMathCommand.h"
 
+#include <cstdio>
+
+#include "cm_kwiml.h"
+
+#include "cmExecutionStatus.h"
 #include "cmExprParserHelper.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cm_kwiml.h"
 
-#include <stdio.h>
+namespace {
+bool HandleExprCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
+}
 
-class cmExecutionStatus;
-
-bool cmMathCommand::InitialPass(std::vector<std::string> const& args,
-                                cmExecutionStatus&)
+bool cmMathCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("must be called with at least one argument.");
+    status.SetError("must be called with at least one argument.");
     return false;
   }
   const std::string& subCommand = args[0];
   if (subCommand == "EXPR") {
-    return this->HandleExprCommand(args);
+    return HandleExprCommand(args, status);
   }
   std::string e = "does not recognize sub-command " + subCommand;
-  this->SetError(e);
+  status.SetError(e);
   return false;
 }
 
-bool cmMathCommand::HandleExprCommand(std::vector<std::string> const& args)
+namespace {
+bool HandleExprCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if ((args.size() != 3) && (args.size() != 5)) {
-    this->SetError("EXPR called with incorrect arguments.");
+    status.SetError("EXPR called with incorrect arguments.");
     return false;
   }
 
@@ -46,7 +53,7 @@
   size_t argumentIndex = 3;
   NumericFormat outputFormat = NumericFormat::UNINITIALIZED;
 
-  this->Makefile->AddDefinition(outputVariable, "ERROR");
+  status.GetMakefile().AddDefinition(outputVariable, "ERROR");
 
   if (argumentIndex < args.size()) {
     const std::string messageHint = "sub-command EXPR ";
@@ -61,19 +68,19 @@
         } else {
           std::string error = messageHint + "value \"" + argument +
             "\" for option \"" + option + "\" is invalid.";
-          this->SetError(error);
+          status.SetError(error);
           return false;
         }
       } else {
         std::string error =
           messageHint + "missing argument for option \"" + option + "\".";
-        this->SetError(error);
+        status.SetError(error);
         return false;
       }
     } else {
       std::string error =
         messageHint + "option \"" + option + "\" is unknown.";
-      this->SetError(error);
+      status.SetError(error);
       return false;
     }
   }
@@ -84,7 +91,7 @@
 
   cmExprParserHelper helper;
   if (!helper.ParseString(expression.c_str(), 0)) {
-    this->SetError(helper.GetError());
+    status.SetError(helper.GetError());
     return false;
   }
 
@@ -104,9 +111,10 @@
 
   std::string const& w = helper.GetWarning();
   if (!w.empty()) {
-    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w);
+    status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, w);
   }
 
-  this->Makefile->AddDefinition(outputVariable, buffer);
+  status.GetMakefile().AddDefinition(outputVariable, buffer);
   return true;
 }
+}
diff --git a/Source/cmMathCommand.h b/Source/cmMathCommand.h
index 0c6c76b..ac1957c 100644
--- a/Source/cmMathCommand.h
+++ b/Source/cmMathCommand.h
@@ -8,28 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
 /// Mathematical expressions: math(EXPR ...) command.
-class cmMathCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmMathCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  bool HandleExprCommand(std::vector<std::string> const& args);
-};
+bool cmMathCommand(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx
index 5320ec5..96a6386 100644
--- a/Source/cmMessageCommand.cxx
+++ b/Source/cmMessageCommand.cxx
@@ -2,30 +2,28 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmMessageCommand.h"
 
-#include "cmAlgorithms.h"
+#include <cassert>
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmMessenger.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <cassert>
-
-class cmExecutionStatus;
-
 // cmLibraryCommand
-bool cmMessageCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool cmMessageCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   auto i = args.cbegin();
 
   auto type = MessageType::MESSAGE;
-  auto status = false;
   auto fatal = false;
   auto level = cmake::LogLevel::LOG_UNDEFINED;
   if (*i == "SEND_ERROR") {
@@ -42,12 +40,13 @@
     level = cmake::LogLevel::LOG_WARNING;
     ++i;
   } else if (*i == "AUTHOR_WARNING") {
-    if (this->Makefile->IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
-        !this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
+    if (status.GetMakefile().IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
+        !status.GetMakefile().IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
       fatal = true;
       type = MessageType::AUTHOR_ERROR;
       level = cmake::LogLevel::LOG_ERROR;
-    } else if (!this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
+    } else if (!status.GetMakefile().IsOn(
+                 "CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
       type = MessageType::AUTHOR_WARNING;
       level = cmake::LogLevel::LOG_WARNING;
     } else {
@@ -55,28 +54,24 @@
     }
     ++i;
   } else if (*i == "STATUS") {
-    status = true;
     level = cmake::LogLevel::LOG_STATUS;
     ++i;
   } else if (*i == "VERBOSE") {
-    status = true;
     level = cmake::LogLevel::LOG_VERBOSE;
     ++i;
   } else if (*i == "DEBUG") {
-    status = true;
     level = cmake::LogLevel::LOG_DEBUG;
     ++i;
   } else if (*i == "TRACE") {
-    status = true;
     level = cmake::LogLevel::LOG_TRACE;
     ++i;
   } else if (*i == "DEPRECATION") {
-    if (this->Makefile->IsOn("CMAKE_ERROR_DEPRECATED")) {
+    if (status.GetMakefile().IsOn("CMAKE_ERROR_DEPRECATED")) {
       fatal = true;
       type = MessageType::DEPRECATION_ERROR;
       level = cmake::LogLevel::LOG_ERROR;
-    } else if ((!this->Makefile->IsSet("CMAKE_WARN_DEPRECATED") ||
-                this->Makefile->IsOn("CMAKE_WARN_DEPRECATED"))) {
+    } else if (!status.GetMakefile().IsSet("CMAKE_WARN_DEPRECATED") ||
+               status.GetMakefile().IsOn("CMAKE_WARN_DEPRECATED")) {
       type = MessageType::DEPRECATION_WARNING;
       level = cmake::LogLevel::LOG_WARNING;
     } else {
@@ -94,7 +89,7 @@
   assert("Message log level expected to be set" &&
          level != cmake::LogLevel::LOG_UNDEFINED);
 
-  auto desiredLevel = this->Makefile->GetCMakeInstance()->GetLogLevel();
+  auto desiredLevel = status.GetMakefile().GetCMakeInstance()->GetLogLevel();
   assert("Expected a valid log level here" &&
          desiredLevel != cmake::LogLevel::LOG_UNDEFINED);
 
@@ -105,17 +100,45 @@
 
   auto message = cmJoin(cmMakeRange(i, args.cend()), "");
 
-  if (type != MessageType::MESSAGE) {
-    // we've overridden the message type, above, so display it directly
-    cmMessenger* m = this->Makefile->GetMessenger();
-    m->DisplayMessage(type, message, this->Makefile->GetBacktrace());
-  } else {
-    if (status) {
-      this->Makefile->DisplayStatus(message, -1);
-    } else {
-      cmSystemTools::Message(message);
-    }
+  if (cmake::LogLevel::LOG_NOTICE <= level) {
+    // Check if any indentation has requested:
+    // `CMAKE_MESSAGE_INDENT` is a list of "padding" pieces
+    // to be joined and prepended to the message lines.
+    auto indent = cmJoin(cmExpandedList(status.GetMakefile().GetSafeDefinition(
+                           "CMAKE_MESSAGE_INDENT")),
+                         "");
+    // Make every line of the `message` indented
+    // NOTE Can't reuse `cmDocumentationFormatter::PrintPreformatted`
+    // here cuz it appends `\n` to the EOM ;-(
+    cmSystemTools::ReplaceString(message, "\n", "\n" + indent);
+    message = indent + message;
   }
+
+  switch (level) {
+    case cmake::LogLevel::LOG_ERROR:
+    case cmake::LogLevel::LOG_WARNING:
+      // we've overridden the message type, above, so display it directly
+      status.GetMakefile().GetMessenger()->DisplayMessage(
+        type, message, status.GetMakefile().GetBacktrace());
+      break;
+
+    case cmake::LogLevel::LOG_NOTICE:
+      cmSystemTools::Message(message);
+      break;
+
+    case cmake::LogLevel::LOG_STATUS:
+    case cmake::LogLevel::LOG_VERBOSE:
+    case cmake::LogLevel::LOG_DEBUG:
+    case cmake::LogLevel::LOG_TRACE:
+      status.GetMakefile().DisplayStatus(message, -1);
+      break;
+
+    default:
+      assert("Unexpected log level! Review the `cmMessageCommand.cxx`." &&
+             false);
+      break;
+  }
+
   if (fatal) {
     cmSystemTools::SetFatalErrorOccured();
   }
diff --git a/Source/cmMessageCommand.h b/Source/cmMessageCommand.h
index 819ebda..7d544c4 100644
--- a/Source/cmMessageCommand.h
+++ b/Source/cmMessageCommand.h
@@ -8,28 +8,13 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmMessageCommand
+/**
  * \brief Displays a message to the user
  *
  */
-class cmMessageCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmMessageCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmMessageCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx
index 1d790e2..af83478 100644
--- a/Source/cmMessenger.cxx
+++ b/Source/cmMessenger.cxx
@@ -2,11 +2,11 @@
    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)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmsys/SystemInformation.hxx"
 #endif
 
@@ -100,14 +100,13 @@
            "it.";
   } else if (t == MessageType::AUTHOR_ERROR) {
     msg << "This error is for project developers. Use -Wno-error=dev to "
-           "suppress "
-           "it.";
+           "suppress it.";
   }
 
   // Add a terminating blank line.
   msg << "\n";
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   // Add a C++ stack trace to internal errors.
   if (t == MessageType::INTERNAL_ERROR) {
     std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0);
diff --git a/Source/cmMessenger.h b/Source/cmMessenger.h
index cf15adf..8c09782 100644
--- a/Source/cmMessenger.h
+++ b/Source/cmMessenger.h
@@ -5,11 +5,11 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 
-#include <string>
-
 class cmMessenger
 {
 public:
diff --git a/Source/cmNewLineStyle.cxx b/Source/cmNewLineStyle.cxx
index 3f6523e..a121332 100644
--- a/Source/cmNewLineStyle.cxx
+++ b/Source/cmNewLineStyle.cxx
@@ -2,7 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmNewLineStyle.h"
 
-#include <stddef.h>
+#include <cstddef>
 
 cmNewLineStyle::cmNewLineStyle() = default;
 
@@ -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/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index b7b822a..beedef4 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -3,15 +3,17 @@
 #include "cmNinjaNormalTargetGenerator.h"
 
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
 #include <iterator>
 #include <map>
-#include <memory> // IWYU pragma: keep
 #include <set>
 #include <sstream>
 #include <utility>
 
+#include <cm/memory>
+
 #include "cmAlgorithms.h"
+#include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h" // IWYU pragma: keep
 #include "cmCustomCommandGenerator.h"
 #include "cmGeneratedFileStream.h"
@@ -19,6 +21,7 @@
 #include "cmGlobalNinjaGenerator.h"
 #include "cmLinkLineComputer.h"
 #include "cmLinkLineDeviceComputer.h"
+#include "cmLocalCommonGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalNinjaGenerator.h"
 #include "cmMakefile.h"
@@ -32,6 +35,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
@@ -217,8 +221,7 @@
     const char* val = this->GetLocalGenerator()->GetRuleLauncher(
       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -227,7 +230,7 @@
     // Rule for linking library/executable.
     std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
     for (std::string& linkCmd : linkCmds) {
-      linkCmd = launcher + linkCmd;
+      linkCmd = cmStrCat(launcher, linkCmd);
       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
                                                    linkCmd, vars);
     }
@@ -238,16 +241,10 @@
     rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
 
     // Write the linker rule with response file if needed.
-    rule.Comment = "Rule for linking ";
-    rule.Comment += this->TargetLinkLanguage;
-    rule.Comment += " ";
-    rule.Comment += this->GetVisibleTypeName();
-    rule.Comment += ".";
-    rule.Description = "Linking ";
-    rule.Description += this->TargetLinkLanguage;
-    rule.Description += " ";
-    rule.Description += this->GetVisibleTypeName();
-    rule.Description += " $TARGET_FILE";
+    rule.Comment = cmStrCat("Rule for linking ", this->TargetLinkLanguage, ' ',
+                            this->GetVisibleTypeName(), '.');
+    rule.Description = cmStrCat("Linking ", this->TargetLinkLanguage, ' ',
+                                this->GetVisibleTypeName(), " $TARGET_FILE");
     rule.Restat = "$RESTAT";
 
     this->GetGlobalGenerator()->AddRule(rule);
@@ -281,8 +278,7 @@
 
     std::string responseFlag;
 
-    std::string cmakeVarLang = "CMAKE_";
-    cmakeVarLang += this->TargetLinkLanguage;
+    std::string cmakeVarLang = cmStrCat("CMAKE_", this->TargetLinkLanguage);
 
     // build response file name
     std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
@@ -356,8 +352,7 @@
     const char* val = this->GetLocalGenerator()->GetRuleLauncher(
       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
     if (val && *val) {
-      launcher = val;
-      launcher += " ";
+      launcher = cmStrCat(val, ' ');
     }
 
     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -366,7 +361,7 @@
     // Rule for linking library/executable.
     std::vector<std::string> linkCmds = this->ComputeLinkCmd();
     for (std::string& linkCmd : linkCmds) {
-      linkCmd = launcher + linkCmd;
+      linkCmd = cmStrCat(launcher, linkCmd);
       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
                                                    linkCmd, vars);
     }
@@ -379,16 +374,10 @@
     rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
 
     // Write the linker rule with response file if needed.
-    rule.Comment = "Rule for linking ";
-    rule.Comment += this->TargetLinkLanguage;
-    rule.Comment += " ";
-    rule.Comment += this->GetVisibleTypeName();
-    rule.Comment += ".";
-    rule.Description = "Linking ";
-    rule.Description += this->TargetLinkLanguage;
-    rule.Description += " ";
-    rule.Description += this->GetVisibleTypeName();
-    rule.Description += " $TARGET_FILE";
+    rule.Comment = cmStrCat("Rule for linking ", this->TargetLinkLanguage, ' ',
+                            this->GetVisibleTypeName(), '.');
+    rule.Description = cmStrCat("Linking ", this->TargetLinkLanguage, ' ',
+                                this->GetVisibleTypeName(), " $TARGET_FILE");
     rule.Restat = "$RESTAT";
     this->GetGlobalGenerator()->AddRule(rule);
   }
@@ -439,12 +428,12 @@
     case cmStateEnums::MODULE_LIBRARY: {
       const std::string cudaLinkCmd(
         this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_LIBRARY"));
-      cmSystemTools::ExpandListArgument(cudaLinkCmd, linkCmds);
+      cmExpandList(cudaLinkCmd, linkCmds);
     } break;
     case cmStateEnums::EXECUTABLE: {
       const std::string cudaLinkCmd(this->GetMakefile()->GetDefinition(
         "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE"));
-      cmSystemTools::ExpandListArgument(cudaLinkCmd, linkCmds);
+      cmExpandList(cudaLinkCmd, linkCmds);
     } break;
     default:
       break;
@@ -466,19 +455,19 @@
     if (linkCmd) {
       std::string linkCmdStr = linkCmd;
       if (this->GetGeneratorTarget()->HasImplibGNUtoMS(this->ConfigName)) {
-        std::string ruleVar = "CMAKE_";
-        ruleVar += this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
-        ruleVar += "_GNUtoMS_RULE";
+        std::string ruleVar = cmStrCat(
+          "CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+          "_GNUtoMS_RULE");
         if (const char* rule = this->Makefile->GetDefinition(ruleVar)) {
           linkCmdStr += rule;
         }
       }
-      cmSystemTools::ExpandListArgument(linkCmdStr, linkCmds);
+      cmExpandList(linkCmdStr, linkCmds);
       if (this->GetGeneratorTarget()->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-        std::string cmakeCommand =
+        std::string cmakeCommand = cmStrCat(
           this->GetLocalGenerator()->ConvertToOutputFormat(
-            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
-        cmakeCommand += " -E __run_co_compile --lwyu=";
+            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+          " -E __run_co_compile --lwyu=");
         cmGeneratorTarget& gt = *this->GetGeneratorTarget();
         const std::string cfgName = this->GetConfigName();
         std::string targetOutputReal = this->ConvertToNinjaPath(
@@ -501,26 +490,24 @@
       }
       // TODO: Use ARCHIVE_APPEND for archives over a certain size.
       {
-        std::string linkCmdVar = "CMAKE_";
-        linkCmdVar += this->TargetLinkLanguage;
-        linkCmdVar += "_ARCHIVE_CREATE";
+        std::string linkCmdVar =
+          cmStrCat("CMAKE_", this->TargetLinkLanguage, "_ARCHIVE_CREATE");
 
         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
           linkCmdVar, this->TargetLinkLanguage, this->GetConfigName());
 
         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
-        cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
+        cmExpandList(linkCmd, linkCmds);
       }
       {
-        std::string linkCmdVar = "CMAKE_";
-        linkCmdVar += this->TargetLinkLanguage;
-        linkCmdVar += "_ARCHIVE_FINISH";
+        std::string linkCmdVar =
+          cmStrCat("CMAKE_", this->TargetLinkLanguage, "_ARCHIVE_FINISH");
 
         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
           linkCmdVar, this->TargetLinkLanguage, this->GetConfigName());
 
         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
-        cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
+        cmExpandList(linkCmd, linkCmds);
       }
 #ifdef __APPLE__
       // On macOS ranlib truncates the fractional part of the static archive
@@ -589,10 +576,8 @@
 
   // Compute the comment.
   cmNinjaBuild build(this->LanguageLinkerDeviceRule());
-  build.Comment = "Link the ";
-  build.Comment += this->GetVisibleTypeName();
-  build.Comment += " ";
-  build.Comment += targetOutputReal;
+  build.Comment =
+    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
 
   cmNinjaVars& vars = build.Variables;
 
@@ -727,13 +712,9 @@
                                               outpath);
 
     // Calculate the output path
-    targetOutput = outpath;
-    targetOutput += "/";
-    targetOutput += this->TargetNames.Output;
+    targetOutput = cmStrCat(outpath, '/', this->TargetNames.Output);
     targetOutput = this->ConvertToNinjaPath(targetOutput);
-    targetOutputReal = outpath;
-    targetOutputReal += "/";
-    targetOutputReal += this->TargetNames.Real;
+    targetOutputReal = cmStrCat(outpath, '/', this->TargetNames.Real);
     targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
   } else if (gt->IsFrameworkOnApple()) {
     // Create the library framework.
@@ -756,10 +737,8 @@
   cmNinjaVars& vars = linkBuild.Variables;
 
   // Compute the comment.
-  linkBuild.Comment = "Link the ";
-  linkBuild.Comment += this->GetVisibleTypeName();
-  linkBuild.Comment += " ";
-  linkBuild.Comment += targetOutputReal;
+  linkBuild.Comment =
+    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
 
   // Compute outputs.
   linkBuild.Outputs.push_back(targetOutputReal);
@@ -966,7 +945,8 @@
     &gt->GetPostBuildCommands()
   };
 
-  std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
+  std::vector<std::string> preLinkCmdLines;
+  std::vector<std::string> postBuildCmdLines;
   std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
                                                 &preLinkCmdLines,
                                                 &postBuildCmdLines };
@@ -988,14 +968,21 @@
     std::string cmakeCommand =
       this->GetLocalGenerator()->ConvertToOutputFormat(
         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
-    std::string cmd = cmakeCommand;
-    cmd += " -E __create_def ";
-    cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
-      mdi->DefFile, cmOutputConverter::SHELL);
-    cmd += " ";
+    std::string cmd =
+      cmStrCat(cmakeCommand, " -E __create_def ",
+               this->GetLocalGenerator()->ConvertToOutputFormat(
+                 mdi->DefFile, cmOutputConverter::SHELL),
+               ' ');
     std::string obj_list_file = mdi->DefFile + ".objs";
     cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
       obj_list_file, cmOutputConverter::SHELL);
+
+    const char* nm_executable = GetMakefile()->GetDefinition("CMAKE_NM");
+    if (nm_executable && *nm_executable) {
+      cmd += " --nm=";
+      cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
+        nm_executable, cmOutputConverter::SHELL);
+    }
     preLinkCmdLines.push_back(std::move(cmd));
 
     // create a list of obj files for the -E __create_def to read
@@ -1037,8 +1024,7 @@
     symlinkVars["POST_BUILD"] = postBuildCmdLine;
   }
 
-  std::string cmakeVarLang = "CMAKE_";
-  cmakeVarLang += this->TargetLinkLanguage;
+  std::string cmakeVarLang = cmStrCat("CMAKE_", this->TargetLinkLanguage);
 
   // build response file name
   std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
@@ -1061,6 +1047,26 @@
   // Gather order-only dependencies.
   this->GetLocalGenerator()->AppendTargetDepends(gt, linkBuild.OrderOnlyDeps);
 
+  // Add order-only dependencies on versioning symlinks of shared libs we link.
+  if (!this->GeneratorTarget->IsDLLPlatform()) {
+    if (cmComputeLinkInformation* cli =
+          this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) {
+      for (auto const& item : cli->GetItems()) {
+        if (item.Target &&
+            item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
+            !item.Target->IsFrameworkOnApple()) {
+          std::string const& lib = this->ConvertToNinjaPath(
+            item.Target->GetFullPath(this->GetConfigName()));
+          if (std::find(linkBuild.ImplicitDeps.begin(),
+                        linkBuild.ImplicitDeps.end(),
+                        lib) == linkBuild.ImplicitDeps.end()) {
+            linkBuild.OrderOnlyDeps.emplace_back(lib);
+          }
+        }
+      }
+    }
+  }
+
   // Ninja should restat after linking if and only if there are byproducts.
   vars["RESTAT"] = byproducts.empty() ? "" : "1";
 
diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h
index 14991a2..ebc1268 100644
--- a/Source/cmNinjaNormalTargetGenerator.h
+++ b/Source/cmNinjaNormalTargetGenerator.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmGeneratorTarget.h"
-#include "cmNinjaTargetGenerator.h"
-
 #include <string>
 #include <vector>
 
+#include "cmGeneratorTarget.h"
+#include "cmNinjaTargetGenerator.h"
+
 class cmNinjaNormalTargetGenerator : public cmNinjaTargetGenerator
 {
 public:
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 2139a45..919a5db 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -2,16 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmNinjaTargetGenerator.h"
 
-#include "cm_jsoncpp_value.h"
-#include "cm_jsoncpp_writer.h"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
 #include <iterator>
 #include <map>
-#include <memory> // IWYU pragma: keep
 #include <ostream>
 #include <utility>
 
+#include <cm/memory>
+
+#include "cm_jsoncpp_value.h"
+#include "cm_jsoncpp_writer.h"
+
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommandGenerator.h"
@@ -30,6 +32,7 @@
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -106,6 +109,13 @@
   return lang == "Fortran";
 }
 
+bool cmNinjaTargetGenerator::CompilePreprocessedSourceWithDefines(
+  std::string const& lang) const
+{
+  return this->Makefile->IsOn(
+    cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES"));
+}
+
 std::string cmNinjaTargetGenerator::LanguageDyndepRule(
   const std::string& lang) const
 {
@@ -131,6 +141,7 @@
   cmSourceFile const* source, const std::string& language)
 {
   std::string flags = this->GetFlags(language);
+  const std::string configName = this->LocalGenerator->GetConfigName();
 
   // Add Fortran format flags.
   if (language == "Fortran") {
@@ -139,8 +150,7 @@
 
   // Add source file specific flags.
   cmGeneratorExpressionInterpreter genexInterpreter(
-    this->LocalGenerator, this->LocalGenerator->GetConfigName(),
-    this->GeneratorTarget, language);
+    this->LocalGenerator, configName, this->GeneratorTarget, language);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
   if (const char* cflags = source->GetProperty(COMPILE_FLAGS)) {
@@ -154,6 +164,24 @@
       flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
   }
 
+  // Add precompile headers compile options.
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(configName, language);
+
+  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (source->GetFullPath() == pchSource) {
+      pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
+        configName, language);
+    } else {
+      pchOptions =
+        this->GeneratorTarget->GetPchUseCompileOptions(configName, language);
+    }
+
+    this->LocalGenerator->AppendCompileOptions(
+      flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
+  }
+
   return flags;
 }
 
@@ -217,8 +245,8 @@
       defines, genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS));
   }
 
-  std::string defPropName = "COMPILE_DEFINITIONS_";
-  defPropName += cmSystemTools::UpperCase(config);
+  std::string defPropName =
+    cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
   if (const char* config_compile_defs = source->GetProperty(defPropName)) {
     this->LocalGenerator->AppendDefines(
       defines,
@@ -372,10 +400,10 @@
 std::string cmNinjaTargetGenerator::GetTargetDependInfoPath(
   std::string const& lang) const
 {
-  std::string path = this->Makefile->GetCurrentBinaryDirectory();
-  path += "/";
-  path += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-  path += "/" + lang + "DependInfo.json";
+  std::string path =
+    cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/',
+             this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+             '/', lang, "DependInfo.json");
   return path;
 }
 
@@ -414,9 +442,9 @@
         this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
         this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
         this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
-      pdbPath = this->GeneratorTarget->GetPDBDirectory(this->GetConfigName());
-      pdbPath += "/";
-      pdbPath += this->GeneratorTarget->GetPDBName(this->GetConfigName());
+      pdbPath = cmStrCat(
+        this->GeneratorTarget->GetPDBDirectory(this->GetConfigName()), '/',
+        this->GeneratorTarget->GetPDBName(this->GetConfigName()));
     }
 
     vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
@@ -456,12 +484,14 @@
   vars.ObjectDir = "$OBJECT_DIR";
   vars.ObjectFileDir = "$OBJECT_FILE_DIR";
 
+  cmMakefile* mf = this->GetMakefile();
+
   // For some cases we do an explicit preprocessor invocation.
   bool const explicitPP = this->NeedExplicitPreprocessing(lang);
+  bool const compilePPWithDefines = this->UsePreprocessedSource(lang) &&
+    this->CompilePreprocessedSourceWithDefines(lang);
   bool const needDyndep = this->NeedDyndep(lang);
 
-  cmMakefile* mf = this->GetMakefile();
-
   std::string flags = "$FLAGS";
 
   std::string responseFlag;
@@ -486,8 +516,7 @@
   const char* val = this->GetLocalGenerator()->GetRuleLauncher(
     this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE");
   if (val && *val) {
-    launcher = val;
-    launcher += " ";
+    launcher = cmStrCat(val, ' ');
   }
 
   std::string const cmakeCmd =
@@ -516,9 +545,14 @@
     // Preprocessing and compilation use the same flags.
     std::string ppFlags = flags;
 
-    // Move preprocessor definitions to the preprocessor rule.
-    ppVars.Defines = vars.Defines;
-    vars.Defines = "";
+    if (!compilePPWithDefines) {
+      // Move preprocessor definitions to the preprocessor rule.
+      ppVars.Defines = vars.Defines;
+      vars.Defines = "";
+    } else {
+      // Copy preprocessor definitions to the preprocessor rule.
+      ppVars.Defines = vars.Defines;
+    }
 
     // Copy include directories to the preprocessor rule.  The Fortran
     // compilation rule still needs them for the INCLUDE directive.
@@ -527,12 +561,8 @@
     // If using a response file, move defines, includes, and flags into it.
     if (!responseFlag.empty()) {
       rule.RspFile = "$RSP_FILE";
-      rule.RspContent = " ";
-      rule.RspContent += ppVars.Defines;
-      rule.RspContent += " ";
-      rule.RspContent += ppVars.Includes;
-      rule.RspContent += " ";
-      rule.RspContent += ppFlags;
+      rule.RspContent =
+        cmStrCat(' ', ppVars.Defines, ' ', ppVars.Includes, ' ', ppFlags);
       ppFlags = responseFlag + rule.RspFile;
       ppVars.Defines = "";
       ppVars.Includes = "";
@@ -544,26 +574,21 @@
     std::vector<std::string> ppCmds;
     {
       // Lookup the explicit preprocessing rule.
-      std::string ppVar = "CMAKE_" + lang;
-      ppVar += "_PREPROCESS_SOURCE";
-      cmSystemTools::ExpandListArgument(
-        this->GetMakefile()->GetRequiredDefinition(ppVar), ppCmds);
+      std::string ppVar = cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE");
+      cmExpandList(this->GetMakefile()->GetRequiredDefinition(ppVar), ppCmds);
     }
 
     for (std::string& i : ppCmds) {
-      i = launcher + i;
+      i = cmStrCat(launcher, i);
       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
                                                    i, ppVars);
     }
 
     // Run CMake dependency scanner on preprocessed output.
     {
-      std::string ccmd = cmakeCmd;
-      ccmd += " -E cmake_ninja_depends --tdi=";
-      ccmd += tdi;
-      ccmd += " --lang=";
-      ccmd += lang;
-      ccmd += " --pp=$out --dep=$DEP_FILE";
+      std::string ccmd =
+        cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi,
+                 " --lang=", lang, " --pp=$out --dep=$DEP_FILE");
       if (needDyndep) {
         ccmd += " --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE";
       }
@@ -572,12 +597,8 @@
     rule.Command = this->GetLocalGenerator()->BuildCommandLine(ppCmds);
 
     // Write the rule for preprocessing file of the given language.
-    rule.Comment = "Rule for preprocessing ";
-    rule.Comment += lang;
-    rule.Comment += " files.";
-    rule.Description = "Building ";
-    rule.Description += lang;
-    rule.Description += " preprocessed $out";
+    rule.Comment = cmStrCat("Rule for preprocessing ", lang, " files.");
+    rule.Description = cmStrCat("Building ", lang, " preprocessed $out");
     this->GetGlobalGenerator()->AddRule(rule);
   }
 
@@ -594,24 +615,16 @@
     {
       std::vector<std::string> ddCmds;
       {
-        std::string ccmd = cmakeCmd;
-        ccmd += " -E cmake_ninja_dyndep --tdi=";
-        ccmd += tdi;
-        ccmd += " --lang=";
-        ccmd += lang;
-        ccmd += " --dd=$out ";
-        ccmd += "@";
-        ccmd += rule.RspFile;
+        std::string ccmd =
+          cmStrCat(cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi,
+                   " --lang=", lang, " --dd=$out @", rule.RspFile);
         ddCmds.emplace_back(std::move(ccmd));
       }
       rule.Command = this->GetLocalGenerator()->BuildCommandLine(ddCmds);
     }
-    rule.Comment = "Rule to generate ninja dyndep files for ";
-    rule.Comment += lang;
-    rule.Comment += ".";
-    rule.Description = "Generating ";
-    rule.Description += lang;
-    rule.Description += " dyndep file $out";
+    rule.Comment =
+      cmStrCat("Rule to generate ninja dyndep files for ", lang, '.');
+    rule.Description = cmStrCat("Generating ", lang, " dyndep file $out");
     this->GetGlobalGenerator()->AddRule(rule);
   }
 
@@ -619,12 +632,8 @@
   // If using a response file, move defines, includes, and flags into it.
   if (!responseFlag.empty()) {
     rule.RspFile = "$RSP_FILE";
-    rule.RspContent = " ";
-    rule.RspContent += vars.Defines;
-    rule.RspContent += " ";
-    rule.RspContent += vars.Includes;
-    rule.RspContent += " ";
-    rule.RspContent += flags;
+    rule.RspContent =
+      cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags);
     flags = responseFlag + rule.RspFile;
     vars.Defines = "";
     vars.Includes = "";
@@ -647,11 +656,10 @@
       const std::string cl = mf->GetDefinition("CMAKE_C_COMPILER")
         ? mf->GetSafeDefinition("CMAKE_C_COMPILER")
         : mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
-      cldeps = "\"";
-      cldeps += cmSystemTools::GetCMClDepsCommand();
-      cldeps += "\" " + lang + " " + vars.Source + " $DEP_FILE $out \"";
-      cldeps += mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX");
-      cldeps += "\" \"" + cl + "\" ";
+      cldeps = cmStrCat('"', cmSystemTools::GetCMClDepsCommand(), "\" ", lang,
+                        ' ', vars.Source, " $DEP_FILE $out \"",
+                        mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"),
+                        "\" \"", cl, "\" ");
     }
   } else {
     rule.DepType = "gcc";
@@ -684,11 +692,11 @@
       cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION";
     }
     const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar);
-    cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
+    cmExpandList(compileCmd, compileCmds);
   } else {
     const std::string cmdVar = "CMAKE_" + lang + "_COMPILE_OBJECT";
     const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar);
-    cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
+    cmExpandList(compileCmd, compileCmds);
   }
 
   // See if we need to use a compiler launcher like ccache or distcc
@@ -714,8 +722,7 @@
     const char* cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
     if ((iwyu && *iwyu) || (tidy && *tidy) || (cpplint && *cpplint) ||
         (cppcheck && *cppcheck)) {
-      std::string run_iwyu = cmakeCmd;
-      run_iwyu += " -E __run_co_compile";
+      std::string run_iwyu = cmStrCat(cmakeCmd, " -E __run_co_compile");
       if (!compilerLauncher.empty()) {
         // In __run_co_compile case the launcher command is supplied
         // via --launcher=<maybe-list> and consumed
@@ -751,8 +758,7 @@
   // If compiler launcher was specified and not consumed above, it
   // goes to the beginning of the command line.
   if (!compileCmds.empty() && !compilerLauncher.empty()) {
-    std::vector<std::string> args;
-    cmSystemTools::ExpandListArgument(compilerLauncher, args, true);
+    std::vector<std::string> args = cmExpandedList(compilerLauncher, true);
     if (!args.empty()) {
       args[0] = this->LocalGenerator->ConvertToOutputFormat(
         args[0], cmOutputConverter::SHELL);
@@ -768,7 +774,7 @@
   }
 
   for (std::string& i : compileCmds) {
-    i = launcher + i;
+    i = cmStrCat(launcher, i);
     rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i,
                                                  vars);
   }
@@ -776,12 +782,8 @@
   rule.Command = this->GetLocalGenerator()->BuildCommandLine(compileCmds);
 
   // Write the rule for compiling file of the given language.
-  rule.Comment = "Rule for compiling ";
-  rule.Comment += lang;
-  rule.Comment += " files.";
-  rule.Description = "Building ";
-  rule.Description += lang;
-  rule.Description += " object $out";
+  rule.Comment = cmStrCat("Rule for compiling ", lang, " files.");
+  rule.Description = cmStrCat("Building ", lang, " object $out");
   this->GetGlobalGenerator()->AddRule(rule);
 }
 
@@ -794,11 +796,9 @@
     << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
     << " target " << this->GetTargetName() << "\n\n";
 
-  const std::string& config =
-    this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   {
     std::vector<cmSourceFile const*> customCommands;
-    this->GeneratorTarget->GetCustomCommands(customCommands, config);
+    this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName);
     for (cmSourceFile const* sf : customCommands) {
       cmCustomCommand const* cc = sf->GetCustomCommand();
       this->GetLocalGenerator()->AddCustomCommandTarget(
@@ -810,21 +810,28 @@
   }
   {
     std::vector<cmSourceFile const*> headerSources;
-    this->GeneratorTarget->GetHeaderSources(headerSources, config);
+    this->GeneratorTarget->GetHeaderSources(headerSources, this->ConfigName);
     this->OSXBundleGenerator->GenerateMacOSXContentStatements(
       headerSources, this->MacOSXContentGenerator.get());
   }
   {
     std::vector<cmSourceFile const*> extraSources;
-    this->GeneratorTarget->GetExtraSources(extraSources, config);
+    this->GeneratorTarget->GetExtraSources(extraSources, this->ConfigName);
     this->OSXBundleGenerator->GenerateMacOSXContentStatements(
       extraSources, this->MacOSXContentGenerator.get());
   }
   {
+    const char* pchExtension =
+      GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+
     std::vector<cmSourceFile const*> externalObjects;
-    this->GeneratorTarget->GetExternalObjects(externalObjects, config);
+    this->GeneratorTarget->GetExternalObjects(externalObjects,
+                                              this->ConfigName);
     for (cmSourceFile const* sf : externalObjects) {
-      this->Objects.push_back(this->GetSourceFilePath(sf));
+      const auto objectFileName = this->GetSourceFilePath(sf);
+      if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+        this->Objects.push_back(objectFileName);
+      }
     }
   }
 
@@ -863,11 +870,9 @@
     if (orderOnlyDeps.empty()) {
       // Any path that always exists will work here.  It would be nice to
       // use just "." but that is not supported by Ninja < 1.7.
-      std::string tgtDir;
-      tgtDir += this->LocalGenerator->GetCurrentBinaryDirectory();
-      tgtDir += "/";
-      tgtDir +=
-        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
+      std::string tgtDir = cmStrCat(
+        this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
       orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir));
     }
 
@@ -876,7 +881,7 @@
 
   {
     std::vector<cmSourceFile const*> objectSources;
-    this->GeneratorTarget->GetObjectSources(objectSources, config);
+    this->GeneratorTarget->GetObjectSources(objectSources, this->ConfigName);
     for (cmSourceFile const* sf : objectSources) {
       this->WriteObjectBuildStatement(sf);
     }
@@ -909,8 +914,8 @@
   this->GetBuildFileStream() << "\n";
 
   if (!this->SwiftOutputMap.empty()) {
-    std::string const mapFilePath = this->ConvertToNinjaPath(
-      this->GeneratorTarget->GetSupportDirectory() + "/output-file-map.json");
+    std::string const mapFilePath =
+      this->GeneratorTarget->GetSupportDirectory() + "/output-file-map.json";
     std::string const targetSwiftDepsPath = [this]() -> std::string {
       cmGeneratorTarget const* target = this->GeneratorTarget;
       if (const char* name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
@@ -944,8 +949,7 @@
   std::string const objectFileDir =
     cmSystemTools::GetFilenamePath(objectFileName);
 
-  std::string cmakeVarLang = "CMAKE_";
-  cmakeVarLang += language;
+  std::string cmakeVarLang = cmStrCat("CMAKE_", language);
 
   // build response file name
   std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_FLAG";
@@ -966,9 +970,8 @@
   if (!this->NeedDepTypeMSVC(language)) {
     bool replaceExt(false);
     if (!language.empty()) {
-      std::string repVar = "CMAKE_";
-      repVar += language;
-      repVar += "_DEPFILE_EXTENSION_REPLACE";
+      std::string repVar =
+        cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE");
       replaceExt = this->Makefile->IsOn(repVar);
     }
     if (!replaceExt) {
@@ -990,14 +993,36 @@
     vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]);
 
   objBuild.Outputs.push_back(objectFileName);
-  // Add this object to the list of object files.
-  this->Objects.push_back(objectFileName);
+  const char* pchExtension =
+    this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+  if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+    // Add this object to the list of object files.
+    this->Objects.push_back(objectFileName);
+  }
 
   objBuild.ExplicitDeps.push_back(sourceFileName);
 
+  // Add precompile headers dependencies
+  std::vector<std::string> depList;
+
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(this->GetConfigName(), language);
+  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    depList.push_back(
+      this->GeneratorTarget->GetPchHeader(this->GetConfigName(), language));
+    if (source->GetFullPath() != pchSource) {
+      depList.push_back(
+        this->GeneratorTarget->GetPchFile(this->GetConfigName(), language));
+    }
+  }
+
   if (const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
-    std::vector<std::string> depList =
-      cmSystemTools::ExpandedListArgument(objectDeps);
+    std::vector<std::string> objDepList = cmExpandedList(objectDeps);
+    std::copy(objDepList.begin(), objDepList.end(),
+              std::back_inserter(depList));
+  }
+
+  if (!depList.empty()) {
     for (std::string& odi : depList) {
       if (cmSystemTools::FileIsFullPath(odi)) {
         odi = cmSystemTools::CollapseFullPath(odi);
@@ -1037,6 +1062,8 @@
     ppBuild.RspFile = ppFileName + ".rsp";
 
     bool const compilePP = this->UsePreprocessedSource(language);
+    bool const compilePPWithDefines =
+      compilePP && this->CompilePreprocessedSourceWithDefines(language);
     if (compilePP) {
       // Move compilation dependencies to the preprocessing build statement.
       std::swap(ppBuild.ExplicitDeps, objBuild.ExplicitDeps);
@@ -1065,7 +1092,7 @@
       this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag);
     }
 
-    if (compilePP) {
+    if (compilePP && !compilePPWithDefines) {
       // Move preprocessor definitions to the preprocessor build statement.
       std::swap(ppBuild.Variables["DEFINES"], vars["DEFINES"]);
     } else {
@@ -1150,7 +1177,7 @@
   if (const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
     cmNinjaBuild build("phony");
     build.Comment = "Additional output files.";
-    build.Outputs = cmSystemTools::ExpandedListArgument(objectOutputs);
+    build.Outputs = cmExpandedList(objectOutputs);
     std::transform(build.Outputs.begin(), build.Outputs.end(),
                    build.Outputs.begin(), MapToNinjaPath());
     build.ExplicitDeps = objBuild.Outputs;
@@ -1299,12 +1326,12 @@
     }
     const std::string& compileCmd =
       this->GetMakefile()->GetRequiredDefinition(cmdVar);
-    cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
+    cmExpandList(compileCmd, compileCmds);
   } else {
     const std::string cmdVar = "CMAKE_" + language + "_COMPILE_OBJECT";
     const std::string& compileCmd =
       this->GetMakefile()->GetRequiredDefinition(cmdVar);
-    cmSystemTools::ExpandListArgument(compileCmd, compileCmds);
+    cmExpandList(compileCmd, compileCmds);
   }
 
   std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -1328,15 +1355,11 @@
         this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     cmLocalNinjaGenerator* lg = this->LocalGenerator;
     std::vector<std::string> cleanFiles;
-    {
-      cmGeneratorExpression ge;
-      auto cge = ge.Parse(prop_value);
-      cmSystemTools::ExpandListArgument(
-        cge->Evaluate(lg,
-                      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"),
-                      false, this->GeneratorTarget, nullptr, nullptr),
-        cleanFiles);
-    }
+    cmExpandList(cmGeneratorExpression::Evaluate(
+                   prop_value, lg,
+                   this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"),
+                   this->GeneratorTarget),
+                 cleanFiles);
     std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
     cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator();
     for (std::string const& cleanFile : cleanFiles) {
@@ -1384,9 +1407,8 @@
   input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input);
 
   // Get the output file location.
-  std::string output = macdir;
-  output += "/";
-  output += cmSystemTools::GetFilenameName(input);
+  std::string output =
+    cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
   output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output);
 
   // Write a build statement to copy the content into the bundle.
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 3055e18..4627bcd 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -5,6 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
 #include "cm_jsoncpp_value.h"
 
 #include "cmCommonTargetGenerator.h"
@@ -12,12 +18,6 @@
 #include "cmNinjaTypes.h"
 #include "cmOSXBundleGenerator.h"
 
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <set>
-#include <string>
-#include <vector>
-
 class cmCustomCommand;
 class cmGeneratedFileStream;
 class cmGeneratorTarget;
@@ -70,6 +70,7 @@
   std::string LanguageDyndepRule(std::string const& lang) const;
   bool NeedDyndep(std::string const& lang) const;
   bool UsePreprocessedSource(std::string const& lang) const;
+  bool CompilePreprocessedSourceWithDefines(std::string const& lang) const;
 
   std::string OrderDependsTargetForTarget();
 
diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h
index 52c05b6..bd0e83f 100644
--- a/Source/cmNinjaTypes.h
+++ b/Source/cmNinjaTypes.h
@@ -17,9 +17,9 @@
   DependOnTargetOrdering
 };
 
-typedef std::vector<std::string> cmNinjaDeps;
-typedef std::set<std::string> cmNinjaOuts;
-typedef std::map<std::string, std::string> cmNinjaVars;
+using cmNinjaDeps = std::vector<std::string>;
+using cmNinjaOuts = std::set<std::string>;
+using cmNinjaVars = std::map<std::string, std::string>;
 
 class cmNinjaRule
 {
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
index e774b53..5259037 100644
--- a/Source/cmNinjaUtilityTargetGenerator.cxx
+++ b/Source/cmNinjaUtilityTargetGenerator.cxx
@@ -2,6 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmNinjaUtilityTargetGenerator.h"
 
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmGeneratedFileStream.h"
@@ -13,15 +20,9 @@
 #include "cmOutputConverter.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <algorithm>
-#include <array>
-#include <iterator>
-#include <string>
-#include <utility>
-#include <vector>
-
 cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator(
   cmGeneratorTarget* target)
   : cmNinjaTargetGenerator(target)
@@ -36,15 +37,15 @@
   cmLocalNinjaGenerator* lg = this->GetLocalGenerator();
   cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
 
-  std::string utilCommandName = lg->GetCurrentBinaryDirectory();
-  utilCommandName += "/CMakeFiles";
-  utilCommandName += "/";
-  utilCommandName += this->GetTargetName() + ".util";
+  std::string utilCommandName =
+    cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles/",
+             this->GetTargetName(), ".util");
   utilCommandName = this->ConvertToNinjaPath(utilCommandName);
 
   cmNinjaBuild phonyBuild("phony");
   std::vector<std::string> commands;
-  cmNinjaDeps deps, util_outputs(1, utilCommandName);
+  cmNinjaDeps deps;
+  cmNinjaDeps util_outputs(1, utilCommandName);
 
   bool uses_terminal = false;
   {
diff --git a/Source/cmOSXBundleGenerator.cxx b/Source/cmOSXBundleGenerator.cxx
index 47a8df4..a6f4e51 100644
--- a/Source/cmOSXBundleGenerator.cxx
+++ b/Source/cmOSXBundleGenerator.cxx
@@ -2,16 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmOSXBundleGenerator.h"
 
+#include <cassert>
+#include <utility>
+
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-#include <cassert>
-#include <utility>
-
 class cmSourceFile;
 
 cmOSXBundleGenerator::cmOSXBundleGenerator(cmGeneratorTarget* target,
@@ -40,20 +41,20 @@
   }
 
   // Compute bundle directory names.
-  std::string out = outpath;
-  out += "/";
-  out += this->GT->GetAppBundleDirectory(this->ConfigName,
-                                         cmGeneratorTarget::FullLevel);
+  std::string out =
+    cmStrCat(outpath, '/',
+             this->GT->GetAppBundleDirectory(this->ConfigName,
+                                             cmGeneratorTarget::FullLevel));
   cmSystemTools::MakeDirectory(out);
   this->Makefile->AddCMakeOutputFile(out);
 
   // Configure the Info.plist file.  Note that it needs the executable name
   // to be set.
-  std::string plist = outpath;
-  plist += "/";
-  plist += this->GT->GetAppBundleDirectory(this->ConfigName,
-                                           cmGeneratorTarget::ContentLevel);
-  plist += "/Info.plist";
+  std::string plist =
+    cmStrCat(outpath, '/',
+             this->GT->GetAppBundleDirectory(this->ConfigName,
+                                             cmGeneratorTarget::ContentLevel),
+             "/Info.plist");
   this->LocalGenerator->GenerateAppleInfoPList(this->GT, targetName, plist);
   this->Makefile->AddCMakeOutputFile(plist);
   outpath = out;
@@ -69,10 +70,11 @@
   assert(this->MacContentFolders);
 
   // Compute the location of the top-level foo.framework directory.
-  std::string contentdir = outpath + "/" +
-    this->GT->GetFrameworkDirectory(this->ConfigName,
-                                    cmGeneratorTarget::ContentLevel);
-  contentdir += "/";
+  std::string contentdir =
+    cmStrCat(outpath, '/',
+             this->GT->GetFrameworkDirectory(this->ConfigName,
+                                             cmGeneratorTarget::ContentLevel),
+             '/');
 
   std::string newoutpath = outpath + "/" +
     this->GT->GetFrameworkDirectory(this->ConfigName,
@@ -102,8 +104,7 @@
   std::string newName;
 
   // Make foo.framework/Versions
-  std::string versions = contentdir;
-  versions += "Versions";
+  std::string versions = cmStrCat(contentdir, "Versions");
   cmSystemTools::MakeDirectory(versions);
 
   // Make foo.framework/Versions/version
@@ -111,17 +112,14 @@
 
   // Current -> version
   oldName = frameworkVersion;
-  newName = versions;
-  newName += "/Current";
+  newName = cmStrCat(versions, "/Current");
   cmSystemTools::RemoveFile(newName);
   cmSystemTools::CreateSymlink(oldName, newName);
   this->Makefile->AddCMakeOutputFile(newName);
 
   // foo -> Versions/Current/foo
-  oldName = "Versions/Current/";
-  oldName += name;
-  newName = contentdir;
-  newName += name;
+  oldName = cmStrCat("Versions/Current/", name);
+  newName = cmStrCat(contentdir, name);
   cmSystemTools::RemoveFile(newName);
   cmSystemTools::CreateSymlink(oldName, newName);
   this->Makefile->AddCMakeOutputFile(newName);
@@ -130,8 +128,7 @@
   if (this->MacContentFolders->find("Resources") !=
       this->MacContentFolders->end()) {
     oldName = "Versions/Current/Resources";
-    newName = contentdir;
-    newName += "Resources";
+    newName = cmStrCat(contentdir, "Resources");
     cmSystemTools::RemoveFile(newName);
     cmSystemTools::CreateSymlink(oldName, newName);
     this->Makefile->AddCMakeOutputFile(newName);
@@ -141,8 +138,7 @@
   if (this->MacContentFolders->find("Headers") !=
       this->MacContentFolders->end()) {
     oldName = "Versions/Current/Headers";
-    newName = contentdir;
-    newName += "Headers";
+    newName = cmStrCat(contentdir, "Headers");
     cmSystemTools::RemoveFile(newName);
     cmSystemTools::CreateSymlink(oldName, newName);
     this->Makefile->AddCMakeOutputFile(newName);
@@ -152,8 +148,7 @@
   if (this->MacContentFolders->find("PrivateHeaders") !=
       this->MacContentFolders->end()) {
     oldName = "Versions/Current/PrivateHeaders";
-    newName = contentdir;
-    newName += "PrivateHeaders";
+    newName = cmStrCat(contentdir, "PrivateHeaders");
     cmSystemTools::RemoveFile(newName);
     cmSystemTools::CreateSymlink(oldName, newName);
     this->Makefile->AddCMakeOutputFile(newName);
@@ -168,19 +163,20 @@
   }
 
   // Compute bundle directory names.
-  std::string out = root;
-  out += "/";
-  out += this->GT->GetCFBundleDirectory(this->ConfigName,
-                                        cmGeneratorTarget::FullLevel);
+  std::string out =
+    cmStrCat(root, '/',
+             this->GT->GetCFBundleDirectory(this->ConfigName,
+                                            cmGeneratorTarget::FullLevel));
   cmSystemTools::MakeDirectory(out);
   this->Makefile->AddCMakeOutputFile(out);
 
   // Configure the Info.plist file.  Note that it needs the executable name
   // to be set.
-  std::string plist = root + "/" +
-    this->GT->GetCFBundleDirectory(this->ConfigName,
-                                   cmGeneratorTarget::ContentLevel);
-  plist += "/Info.plist";
+  std::string plist =
+    cmStrCat(root, '/',
+             this->GT->GetCFBundleDirectory(this->ConfigName,
+                                            cmGeneratorTarget::ContentLevel),
+             "/Info.plist");
   std::string name = cmSystemTools::GetFilenameName(targetName);
   this->LocalGenerator->GenerateAppleInfoPList(this->GT, name, plist);
   this->Makefile->AddCMakeOutputFile(plist);
@@ -208,10 +204,10 @@
 {
   // Construct the full path to the content subdirectory.
 
-  std::string macdir = this->GT->GetMacContentDirectory(
-    this->ConfigName, cmStateEnums::RuntimeBinaryArtifact);
-  macdir += "/";
-  macdir += pkgloc;
+  std::string macdir =
+    cmStrCat(this->GT->GetMacContentDirectory(
+               this->ConfigName, cmStateEnums::RuntimeBinaryArtifact),
+             '/', pkgloc);
   cmSystemTools::MakeDirectory(macdir);
 
   // Record use of this content location.  Only the first level
diff --git a/Source/cmOptionCommand.cxx b/Source/cmOptionCommand.cxx
index 52f63a3..22e59ac 100644
--- a/Source/cmOptionCommand.cxx
+++ b/Source/cmOptionCommand.cxx
@@ -2,38 +2,35 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmOptionCommand.h"
 
-#include <sstream>
-
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
-#include "cmSystemTools.h"
-
-class cmExecutionStatus;
+#include "cmStringAlgorithms.h"
 
 // cmOptionCommand
-bool cmOptionCommand::InitialPass(std::vector<std::string> const& args,
-                                  cmExecutionStatus&)
+bool cmOptionCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
 {
   const bool argError = (args.size() < 2) || (args.size() > 3);
   if (argError) {
-    std::string m = "called with incorrect number of arguments: ";
-    m += cmJoin(args, " ");
-    this->SetError(m);
+    std::string m = cmStrCat("called with incorrect number of arguments: ",
+                             cmJoin(args, " "));
+    status.SetError(m);
     return false;
   }
 
   // Determine the state of the option policy
   bool checkAndWarn = false;
   {
-    auto status = this->Makefile->GetPolicyStatus(cmPolicies::CMP0077);
+    auto policyStatus =
+      status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0077);
     const auto* existsBeforeSet =
-      this->Makefile->GetStateSnapshot().GetDefinition(args[0]);
-    switch (status) {
+      status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
+    switch (policyStatus) {
       case cmPolicies::WARN:
         checkAndWarn = (existsBeforeSet != nullptr);
         break;
@@ -54,7 +51,7 @@
 
   // See if a cache variable with this name already exists
   // If so just make sure the doc state is correct
-  cmState* state = this->Makefile->GetState();
+  cmState* state = status.GetMakefile().GetState();
   const char* existingValue = state->GetCacheEntryValue(args[0]);
   if (existingValue &&
       (state->GetCacheEntryType(args[0]) != cmStateEnums::UNINITIALIZED)) {
@@ -67,21 +64,21 @@
   if (args.size() == 3) {
     initialValue = args[2];
   }
-  bool init = cmSystemTools::IsOn(initialValue);
-  this->Makefile->AddCacheDefinition(args[0], init ? "ON" : "OFF",
-                                     args[1].c_str(), cmStateEnums::BOOL);
+  bool init = cmIsOn(initialValue);
+  status.GetMakefile().AddCacheDefinition(args[0], init ? "ON" : "OFF",
+                                          args[1].c_str(), cmStateEnums::BOOL);
 
   if (checkAndWarn) {
     const auto* existsAfterSet =
-      this->Makefile->GetStateSnapshot().GetDefinition(args[0]);
+      status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
     if (!existsAfterSet) {
-      std::ostringstream w;
-      w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0077)
-        << "\n"
-           "For compatibility with older versions of CMake, option "
-           "is clearing the normal variable '"
-        << args[0] << "'.";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+      status.GetMakefile().IssueMessage(
+        MessageType::AUTHOR_WARNING,
+        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0077),
+                 "\n"
+                 "For compatibility with older versions of CMake, option "
+                 "is clearing the normal variable '",
+                 args[0], "'."));
     }
   }
   return true;
diff --git a/Source/cmOptionCommand.h b/Source/cmOptionCommand.h
index 34e0e6f..cbd1cb8 100644
--- a/Source/cmOptionCommand.h
+++ b/Source/cmOptionCommand.h
@@ -8,29 +8,13 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmOptionCommand
+/**
  * \brief Provide an option to the user
  *
  * cmOptionCommand provides an option for the user to select
  */
-class cmOptionCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmOptionCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
-
+bool cmOptionCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 #endif
diff --git a/Source/cmOrderDirectories.cxx b/Source/cmOrderDirectories.cxx
index 585db42..073222c 100644
--- a/Source/cmOrderDirectories.cxx
+++ b/Source/cmOrderDirectories.cxx
@@ -2,18 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmOrderDirectories.h"
 
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <sstream>
+#include <vector>
+
 #include "cmAlgorithms.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <assert.h>
-#include <functional>
-#include <sstream>
-
 /*
 Directory ordering computation.
   - Useful to compute a safe runtime library path order
@@ -116,9 +118,7 @@
                                                    std::string const& name)
 {
   // Check if the file exists on disk.
-  std::string file = dir;
-  file += "/";
-  file += name;
+  std::string file = cmStrCat(dir, '/', name);
   if (cmSystemTools::FileExists(file, true)) {
     // The file conflicts only if it is not the same as the original
     // file due to a symlink or hardlink.
@@ -128,7 +128,7 @@
   // Check if the file will be built by cmake.
   std::set<std::string> const& files =
     (this->GlobalGenerator->GetDirectoryContent(dir, false));
-  std::set<std::string>::const_iterator fi = files.find(name);
+  auto fi = files.find(name);
   return fi != files.end();
 }
 
@@ -186,9 +186,9 @@
     // know the soname just look at all files that start with the
     // file name.  Usually the soname starts with the library name.
     std::string base = this->FileName;
-    std::set<std::string>::const_iterator first = files.lower_bound(base);
+    auto first = files.lower_bound(base);
     ++base.back();
-    std::set<std::string>::const_iterator last = files.upper_bound(base);
+    auto last = files.upper_bound(base);
     if (first != last) {
       return true;
     }
@@ -228,8 +228,7 @@
     std::string ext = this->OD->RemoveLibraryExtension.match(2);
     for (std::string const& LinkExtension : this->OD->LinkExtensions) {
       if (LinkExtension != ext) {
-        std::string fname = lib;
-        fname += LinkExtension;
+        std::string fname = cmStrCat(lib, LinkExtension);
         if (this->FileMayConflict(dir, fname)) {
           return true;
         }
@@ -381,7 +380,7 @@
 int cmOrderDirectories::AddOriginalDirectory(std::string const& dir)
 {
   // Add the runtime directory with a unique index.
-  std::map<std::string, int>::iterator i = this->DirectoryIndex.find(dir);
+  auto i = this->DirectoryIndex.find(dir);
   if (i == this->DirectoryIndex.end()) {
     std::map<std::string, int>::value_type entry(
       dir, static_cast<int>(this->OriginalDirectories.size()));
@@ -413,7 +412,7 @@
 
 struct cmOrderDirectoriesCompare
 {
-  typedef std::pair<int, int> ConflictPair;
+  using ConflictPair = std::pair<int, int>;
 
   // The conflict pair is unique based on just the directory
   // (first).  The second element is only used for displaying
@@ -442,8 +441,7 @@
     std::sort(cl.begin(), cl.end());
 
     // Make the edge list unique so cycle detection will be reliable.
-    ConflictList::iterator last =
-      std::unique(cl.begin(), cl.end(), cmOrderDirectoriesCompare());
+    auto last = std::unique(cl.begin(), cl.end(), cmOrderDirectoriesCompare());
     cl.erase(last, cl.end());
   }
 
@@ -467,14 +465,14 @@
   }
 
   // Warn about the conflicts.
-  std::ostringstream w;
-  w << "Cannot generate a safe " << this->Purpose << " for target "
-    << this->Target->GetName()
-    << " because files in some directories may conflict with "
-    << " libraries in implicit directories:\n"
-    << text << "Some of these libraries may not be found correctly.";
   this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
-    MessageType::WARNING, w.str(), this->Target->GetBacktrace());
+    MessageType::WARNING,
+    cmStrCat("Cannot generate a safe ", this->Purpose, " for target ",
+             this->Target->GetName(),
+             " because files in some directories may "
+             "conflict with  libraries in implicit directories:\n",
+             text, "Some of these libraries may not be found correctly."),
+    this->Target->GetBacktrace());
 }
 
 void cmOrderDirectories::OrderDirectories()
@@ -554,11 +552,10 @@
 
 std::string const& cmOrderDirectories::GetRealPath(std::string const& dir)
 {
-  std::map<std::string, std::string>::iterator i =
-    this->RealPaths.lower_bound(dir);
+  auto i = this->RealPaths.lower_bound(dir);
   if (i == this->RealPaths.end() ||
       this->RealPaths.key_comp()(dir, i->first)) {
-    typedef std::map<std::string, std::string>::value_type value_type;
+    using value_type = std::map<std::string, std::string>::value_type;
     i = this->RealPaths.insert(
       i, value_type(dir, cmSystemTools::GetRealPath(dir)));
   }
diff --git a/Source/cmOrderDirectories.h b/Source/cmOrderDirectories.h
index 23e61d6..23c5145 100644
--- a/Source/cmOrderDirectories.h
+++ b/Source/cmOrderDirectories.h
@@ -5,13 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
 #include <map>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmOrderDirectoriesConstraint;
@@ -75,7 +76,7 @@
   // the index of the directory that must come first.  The second
   // element is the index of the runtime library that added the
   // constraint.
-  typedef std::pair<int, int> ConflictPair;
+  using ConflictPair = std::pair<int, int>;
   struct ConflictList : public std::vector<ConflictPair>
   {
   };
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx
index 7d88b08..e602a6d 100644
--- a/Source/cmOutputConverter.cxx
+++ b/Source/cmOutputConverter.cxx
@@ -3,13 +3,13 @@
 #include "cmOutputConverter.h"
 
 #include <algorithm>
-#include <assert.h>
-#include <ctype.h>
+#include <cassert>
+#include <cctype>
 #include <set>
-#include <string.h>
 #include <vector>
 
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot)
@@ -38,10 +38,10 @@
   return this->ConvertToOutputFormat(remote, format);
 }
 
-std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
+std::string cmOutputConverter::ConvertToOutputFormat(cm::string_view source,
                                                      OutputFormat output) const
 {
-  std::string result = source;
+  std::string result(source);
   // Convert it to an output path.
   if (output == SHELL || output == WATCOMQUOTE) {
     result = this->ConvertDirectorySeparatorsForShell(source);
@@ -53,9 +53,9 @@
 }
 
 std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
-  const std::string& source) const
+  cm::string_view source) const
 {
-  std::string result = source;
+  std::string result(source);
   // For the MSYS shell convert drive letters to posix paths, so
   // that c:/some/path becomes /c/some/path.  This is needed to
   // avoid problems with the shell path translation.
@@ -71,33 +71,21 @@
   return result;
 }
 
-static bool cmOutputConverterIsShellOperator(const std::string& str)
+static bool cmOutputConverterIsShellOperator(cm::string_view str)
 {
-  static std::set<std::string> shellOperators;
-  if (shellOperators.empty()) {
-    shellOperators.insert("<");
-    shellOperators.insert(">");
-    shellOperators.insert("<<");
-    shellOperators.insert(">>");
-    shellOperators.insert("|");
-    shellOperators.insert("||");
-    shellOperators.insert("&&");
-    shellOperators.insert("&>");
-    shellOperators.insert("1>");
-    shellOperators.insert("2>");
-    shellOperators.insert("2>&1");
-    shellOperators.insert("1>&2");
-  }
-  return shellOperators.count(str) > 0;
+  static std::set<cm::string_view> const shellOperators{
+    "<", ">", "<<", ">>", "|", "||", "&&", "&>", "1>", "2>", "2>&1", "1>&2"
+  };
+  return (shellOperators.count(str) != 0);
 }
 
-std::string cmOutputConverter::EscapeForShell(const std::string& str,
+std::string cmOutputConverter::EscapeForShell(cm::string_view str,
                                               bool makeVars, bool forEcho,
                                               bool useWatcomQuote) const
 {
   // Do not escape shell operators.
   if (cmOutputConverterIsShellOperator(str)) {
-    return str;
+    return std::string(str);
   }
 
   // Compute the flags for the target shell environment.
@@ -129,46 +117,44 @@
     flags |= Shell_Flag_IsUnix;
   }
 
-  return Shell__GetArgument(str.c_str(), flags);
+  return Shell__GetArgument(str, flags);
 }
 
-std::string cmOutputConverter::EscapeForCMake(const std::string& str)
+std::string cmOutputConverter::EscapeForCMake(cm::string_view str)
 {
   // Always double-quote the argument to take care of most escapes.
   std::string result = "\"";
-  for (const char* c = str.c_str(); *c; ++c) {
-    if (*c == '"') {
+  for (const char c : str) {
+    if (c == '"') {
       // Escape the double quote to avoid ending the argument.
       result += "\\\"";
-    } else if (*c == '$') {
+    } else if (c == '$') {
       // Escape the dollar to avoid expanding variables.
       result += "\\$";
-    } else if (*c == '\\') {
+    } else if (c == '\\') {
       // Escape the backslash to avoid other escapes.
       result += "\\\\";
     } else {
       // Other characters will be parsed correctly.
-      result += *c;
+      result += c;
     }
   }
   result += "\"";
   return result;
 }
 
-std::string cmOutputConverter::EscapeWindowsShellArgument(const char* arg,
+std::string cmOutputConverter::EscapeWindowsShellArgument(cm::string_view arg,
                                                           int shell_flags)
 {
   return Shell__GetArgument(arg, shell_flags);
 }
 
 cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat(
-  const char* value)
+  cm::string_view value)
 {
   FortranFormat format = FortranFormatNone;
-  if (value && *value) {
-    std::vector<std::string> fmt;
-    cmSystemTools::ExpandListArgument(value, fmt);
-    for (std::string const& fi : fmt) {
+  if (!value.empty()) {
+    for (std::string const& fi : cmExpandedList(value)) {
       if (fi == "FIXED") {
         format = FortranFormatFixed;
       }
@@ -180,6 +166,15 @@
   return format;
 }
 
+cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat(
+  const char* value)
+{
+  if (!value) {
+    return FortranFormatNone;
+  }
+  return GetFortranFormat(cm::string_view(value));
+}
+
 void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell)
 {
   this->LinkScriptShell = linkScriptShell;
@@ -225,12 +220,12 @@
 */
 
 /* Some helpers to identify character classes */
-static int Shell__CharIsWhitespace(char c)
+static bool Shell__CharIsWhitespace(char c)
 {
   return ((c == ' ') || (c == '\t'));
 }
 
-static int Shell__CharNeedsQuotesOnUnix(char c)
+static bool Shell__CharNeedsQuotesOnUnix(char c)
 {
   return ((c == '\'') || (c == '`') || (c == ';') || (c == '#') ||
           (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
@@ -238,51 +233,52 @@
           (c == '\\'));
 }
 
-static int Shell__CharNeedsQuotesOnWindows(char c)
+static bool Shell__CharNeedsQuotesOnWindows(char c)
 {
   return ((c == '\'') || (c == '#') || (c == '&') || (c == '<') ||
           (c == '>') || (c == '|') || (c == '^'));
 }
 
-static int Shell__CharIsMakeVariableName(char c)
+static bool Shell__CharIsMakeVariableName(char c)
 {
   return c && (c == '_' || isalpha((static_cast<int>(c))));
 }
 
-int cmOutputConverter::Shell__CharNeedsQuotes(char c, int flags)
+bool cmOutputConverter::Shell__CharNeedsQuotes(char c, int flags)
 {
   /* On Windows the built-in command shell echo never needs quotes.  */
   if (!(flags & Shell_Flag_IsUnix) && (flags & Shell_Flag_EchoWindows)) {
-    return 0;
+    return false;
   }
 
   /* On all platforms quotes are needed to preserve whitespace.  */
   if (Shell__CharIsWhitespace(c)) {
-    return 1;
+    return true;
   }
 
   if (flags & Shell_Flag_IsUnix) {
     /* On UNIX several special characters need quotes to preserve them.  */
     if (Shell__CharNeedsQuotesOnUnix(c)) {
-      return 1;
+      return true;
     }
   } else {
     /* On Windows several special characters need quotes to preserve them.  */
     if (Shell__CharNeedsQuotesOnWindows(c)) {
-      return 1;
+      return true;
     }
   }
-  return 0;
+  return false;
 }
 
-const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c)
+cm::string_view::iterator cmOutputConverter::Shell__SkipMakeVariables(
+  cm::string_view::iterator c, cm::string_view::iterator end)
 {
-  while (*c == '$' && *(c + 1) == '(') {
-    const char* skip = c + 2;
-    while (Shell__CharIsMakeVariableName(*skip)) {
+  while ((c != end && (c + 1) != end) && (*c == '$' && *(c + 1) == '(')) {
+    cm::string_view::iterator skip = c + 2;
+    while ((skip != end) && Shell__CharIsMakeVariableName(*skip)) {
       ++skip;
     }
-    if (*skip == ')') {
+    if ((skip != end) && *skip == ')') {
       c = skip + 1;
     } else {
       break;
@@ -314,63 +310,60 @@
 */
 #define KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES 0
 
-int cmOutputConverter::Shell__ArgumentNeedsQuotes(const char* in, int flags)
+bool cmOutputConverter::Shell__ArgumentNeedsQuotes(cm::string_view in,
+                                                   int flags)
 {
   /* The empty string needs quotes.  */
-  if (!*in) {
-    return 1;
+  if (in.empty()) {
+    return true;
   }
 
   /* Scan the string for characters that require quoting.  */
-  {
-    const char* c;
-    for (c = in; *c; ++c) {
-      /* Look for $(MAKEVAR) syntax if requested.  */
-      if (flags & Shell_Flag_AllowMakeVariables) {
+  for (cm::string_view::iterator cit = in.begin(), cend = in.end();
+       cit != cend; ++cit) {
+    /* Look for $(MAKEVAR) syntax if requested.  */
+    if (flags & Shell_Flag_AllowMakeVariables) {
 #if KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES
-        const char* skip = Shell__SkipMakeVariables(c);
-        if (skip != c) {
-          /* We need to quote make variable references to preserve the
-             string with contents substituted in its place.  */
-          return 1;
-        }
+      cm::string_view::iterator skip = Shell__SkipMakeVariables(cit, cend);
+      if (skip != cit) {
+        /* We need to quote make variable references to preserve the
+           string with contents substituted in its place.  */
+        return true;
+      }
 #else
-        /* Skip over the make variable references if any are present.  */
-        c = Shell__SkipMakeVariables(c);
+      /* Skip over the make variable references if any are present.  */
+      cit = Shell__SkipMakeVariables(cit, cend);
 
-        /* Stop if we have reached the end of the string.  */
-        if (!*c) {
-          break;
-        }
+      /* Stop if we have reached the end of the string.  */
+      if (cit == cend) {
+        break;
+      }
 #endif
-      }
+    }
 
-      /* Check whether this character needs quotes.  */
-      if (Shell__CharNeedsQuotes(*c, flags)) {
-        return 1;
-      }
+    /* Check whether this character needs quotes.  */
+    if (Shell__CharNeedsQuotes(*cit, flags)) {
+      return true;
     }
   }
 
   /* On Windows some single character arguments need quotes.  */
-  if (flags & Shell_Flag_IsUnix && *in && !*(in + 1)) {
-    char c = *in;
+  if (flags & Shell_Flag_IsUnix && in.size() == 1) {
+    char c = in[0];
     if ((c == '?') || (c == '&') || (c == '^') || (c == '|') || (c == '#')) {
-      return 1;
+      return true;
     }
   }
 
-  return 0;
+  return false;
 }
 
-std::string cmOutputConverter::Shell__GetArgument(const char* in, int flags)
+std::string cmOutputConverter::Shell__GetArgument(cm::string_view in,
+                                                  int flags)
 {
   /* Output will be at least as long as input string.  */
   std::string out;
-  out.reserve(strlen(in));
-
-  /* String iterator.  */
-  const char* c;
+  out.reserve(in.size());
 
   /* Keep track of how many backslashes have been encountered in a row.  */
   int windows_backslashes = 0;
@@ -390,14 +383,15 @@
   }
 
   /* Scan the string for characters that require escaping or quoting.  */
-  for (c = in; *c; ++c) {
+  for (cm::string_view::iterator cit = in.begin(), cend = in.end();
+       cit != cend; ++cit) {
     /* Look for $(MAKEVAR) syntax if requested.  */
     if (flags & Shell_Flag_AllowMakeVariables) {
-      const char* skip = Shell__SkipMakeVariables(c);
-      if (skip != c) {
+      cm::string_view::iterator skip = Shell__SkipMakeVariables(cit, cend);
+      if (skip != cit) {
         /* Copy to the end of the make variable references.  */
-        while (c != skip) {
-          out += *c++;
+        while (cit != skip) {
+          out += *cit++;
         }
 
         /* The make variable reference eliminates any escaping needed
@@ -405,7 +399,7 @@
         windows_backslashes = 0;
 
         /* Stop if we have reached the end of the string.  */
-        if (!*c) {
+        if (cit == cend) {
           break;
         }
       }
@@ -415,7 +409,7 @@
     if (flags & Shell_Flag_IsUnix) {
       /* On Unix a few special characters need escaping even inside a
          quoted argument.  */
-      if (*c == '\\' || *c == '"' || *c == '`' || *c == '$') {
+      if (*cit == '\\' || *cit == '"' || *cit == '`' || *cit == '$') {
         /* This character needs a backslash to escape it.  */
         out += '\\';
       }
@@ -423,10 +417,10 @@
       /* On Windows the built-in command shell echo never needs escaping.  */
     } else {
       /* On Windows only backslashes and double-quotes need escaping.  */
-      if (*c == '\\') {
+      if (*cit == '\\') {
         /* Found a backslash.  It may need to be escaped later.  */
         ++windows_backslashes;
-      } else if (*c == '"') {
+      } else if (*cit == '"') {
         /* Found a double-quote.  Escape all immediately preceding
            backslashes.  */
         while (windows_backslashes > 0) {
@@ -444,7 +438,7 @@
     }
 
     /* Check whether this character needs escaping for a make tool.  */
-    if (*c == '$') {
+    if (*cit == '$') {
       if (flags & Shell_Flag_Make) {
         /* In Makefiles a dollar is written $$.  The make tool will
            replace it with just $ before passing it to the shell.  */
@@ -461,7 +455,7 @@
         /* Otherwise a dollar is written just $. */
         out += '$';
       }
-    } else if (*c == '#') {
+    } else if (*cit == '#') {
       if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
         /* In Watcom WMake makefiles a pound is written $#.  The make
            tool will replace it with just # before passing it to the
@@ -471,7 +465,7 @@
         /* Otherwise a pound is written just #. */
         out += '#';
       }
-    } else if (*c == '%') {
+    } else if (*cit == '%') {
       if ((flags & Shell_Flag_VSIDE) ||
           ((flags & Shell_Flag_Make) &&
            ((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
@@ -481,7 +475,7 @@
         /* Otherwise a percent is written just %. */
         out += '%';
       }
-    } else if (*c == ';') {
+    } else if (*cit == ';') {
       if (flags & Shell_Flag_VSIDE) {
         /* In a VS IDE a semicolon is written ";".  If this is written
            in an un-quoted argument it starts a quoted segment,
@@ -495,7 +489,7 @@
       }
     } else {
       /* Store this character.  */
-      out += *c;
+      out += *cit;
     }
   }
 
diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h
index deca767..349a069 100644
--- a/Source/cmOutputConverter.h
+++ b/Source/cmOutputConverter.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include <cm/string_view>
+
 #include "cmStateSnapshot.h"
 
 class cmState;
@@ -22,10 +24,9 @@
     WATCOMQUOTE,
     RESPONSE
   };
-  std::string ConvertToOutputFormat(const std::string& source,
+  std::string ConvertToOutputFormat(cm::string_view source,
                                     OutputFormat output) const;
-  std::string ConvertDirectorySeparatorsForShell(
-    const std::string& source) const;
+  std::string ConvertDirectorySeparatorsForShell(cm::string_view source) const;
 
   //! for existing files convert to output path and short path if spaces
   std::string ConvertToOutputForExisting(const std::string& remote,
@@ -72,15 +73,15 @@
     Shell_Flag_IsUnix = (1 << 8)
   };
 
-  std::string EscapeForShell(const std::string& str, bool makeVars = false,
+  std::string EscapeForShell(cm::string_view str, bool makeVars = false,
                              bool forEcho = false,
                              bool useWatcomQuote = false) const;
 
-  static std::string EscapeForCMake(const std::string& str);
+  static std::string EscapeForCMake(cm::string_view str);
 
   /** Compute an escaped version of the given argument for use in a
       windows shell.  */
-  static std::string EscapeWindowsShellArgument(const char* arg,
+  static std::string EscapeWindowsShellArgument(cm::string_view arg,
                                                 int shell_flags);
 
   enum FortranFormat
@@ -89,15 +90,17 @@
     FortranFormatFixed,
     FortranFormatFree
   };
+  static FortranFormat GetFortranFormat(cm::string_view value);
   static FortranFormat GetFortranFormat(const char* value);
 
 private:
   cmState* GetState() const;
 
-  static int Shell__CharNeedsQuotes(char c, int flags);
-  static const char* Shell__SkipMakeVariables(const char* c);
-  static int Shell__ArgumentNeedsQuotes(const char* in, int flags);
-  static std::string Shell__GetArgument(const char* in, int flags);
+  static bool Shell__CharNeedsQuotes(char c, int flags);
+  static cm::string_view::iterator Shell__SkipMakeVariables(
+    cm::string_view::iterator begin, cm::string_view::iterator end);
+  static bool Shell__ArgumentNeedsQuotes(cm::string_view in, int flags);
+  static std::string Shell__GetArgument(cm::string_view in, int flags);
 
 private:
   cmStateSnapshot StateSnapshot;
diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx
index f3276ec..e093be0 100644
--- a/Source/cmOutputRequiredFilesCommand.cxx
+++ b/Source/cmOutputRequiredFilesCommand.cxx
@@ -2,20 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmOutputRequiredFilesCommand.h"
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/RegularExpression.hxx"
+#include <cstdio>
 #include <map>
+#include <set>
 #include <utility>
 
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
-
+namespace {
 /** \class cmDependInformation
  * \brief Store dependency information for a single source file.
  *
@@ -33,7 +37,7 @@
   /**
    * The set of files on which this one depends.
    */
-  typedef std::set<cmDependInformation*> DependencySetType;
+  using DependencySetType = std::set<cmDependInformation*>;
   DependencySetType DependencySet;
 
   /**
@@ -121,8 +125,7 @@
       std::string incDirs = cmGeneratorExpression::Preprocess(
         incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
 
-      std::vector<std::string> includes;
-      cmSystemTools::ExpandListArgument(incDirs, includes);
+      std::vector<std::string> includes = cmExpandedList(incDirs);
 
       for (std::string& path : includes) {
         this->Makefile->ExpandVariablesInString(path);
@@ -192,10 +195,8 @@
         // see if the include matches the regular expression
         if (!this->IncludeFileRegularExpression.find(includeFile)) {
           if (this->Verbose) {
-            std::string message = "Skipping ";
-            message += includeFile;
-            message += " for file ";
-            message += info->FullPath;
+            std::string message =
+              cmStrCat("Skipping ", includeFile, " for file ", info->FullPath);
             cmSystemTools::Error(message);
           }
           continue;
@@ -214,10 +215,8 @@
           if (cmSystemTools::FileExists(cxxFile)) {
             found = true;
           }
-          for (std::string path : this->IncludeDirectories) {
-            path = path + "/";
-            path = path + cxxFile;
-            if (cmSystemTools::FileExists(path)) {
+          for (std::string const& path : this->IncludeDirectories) {
+            if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
               found = true;
             }
           }
@@ -226,10 +225,8 @@
             if (cmSystemTools::FileExists(cxxFile)) {
               found = true;
             }
-            for (std::string path : this->IncludeDirectories) {
-              path = path + "/";
-              path = path + cxxFile;
-              if (cmSystemTools::FileExists(path)) {
+            for (std::string const& path : this->IncludeDirectories) {
+              if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
                 found = true;
               }
             }
@@ -239,10 +236,8 @@
             if (cmSystemTools::FileExists(cxxFile)) {
               found = true;
             }
-            for (std::string path : this->IncludeDirectories) {
-              path = path + "/";
-              path = path + cxxFile;
-              if (cmSystemTools::FileExists(path)) {
+            for (std::string const& path : this->IncludeDirectories) {
+              if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
                 found = true;
               }
             }
@@ -252,10 +247,8 @@
             if (cmSystemTools::FileExists(cxxFile)) {
               found = true;
             }
-            for (std::string path : this->IncludeDirectories) {
-              path = path + "/";
-              path = path + cxxFile;
-              if (cmSystemTools::FileExists(path)) {
+            for (std::string const& path : this->IncludeDirectories) {
+              if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
                 found = true;
               }
             }
@@ -333,16 +326,16 @@
       cmSourceFile* srcFile = this->Makefile->GetSource(
         cmSystemTools::GetFilenameWithoutExtension(path));
       if (srcFile) {
-        if (srcFile->GetFullPath() == path) {
+        if (srcFile->ResolveFullPath() == path) {
           found = true;
         } else {
           // try to guess which include path to use
           for (std::string incpath : this->IncludeDirectories) {
             if (!incpath.empty() && incpath.back() != '/') {
-              incpath = incpath + "/";
+              incpath += "/";
             }
-            incpath = incpath + path;
-            if (srcFile->GetFullPath() == incpath) {
+            incpath += path;
+            if (srcFile->ResolveFullPath() == incpath) {
               // set the path to the guessed path
               info->FullPath = incpath;
               found = true;
@@ -375,8 +368,7 @@
     std::string fullPath = this->FullPath(file, extraPath);
 
     // Try to find the file's instance of cmDependInformation.
-    DependInformationMapType::const_iterator result =
-      this->DependInformationMap.find(fullPath);
+    auto result = this->DependInformationMap.find(fullPath);
     if (result != this->DependInformationMap.end()) {
       // Found an instance, return it.
       return result->second;
@@ -406,7 +398,7 @@
 
     if (m != this->DirectoryToFileToPathMap.end()) {
       FileToPathMapType& map = m->second;
-      FileToPathMapType::iterator p = map.find(fname);
+      auto p = map.find(fname);
       if (p != map.end()) {
         return p->second;
       }
@@ -420,9 +412,9 @@
 
     for (std::string path : this->IncludeDirectories) {
       if (!path.empty() && path.back() != '/') {
-        path = path + "/";
+        path += "/";
       }
-      path = path + fname;
+      path += fname;
       if (cmSystemTools::FileExists(path, true) &&
           !cmSystemTools::FileIsDirectory(path)) {
         std::string fp = cmSystemTools::CollapseFullPath(path);
@@ -454,53 +446,55 @@
   cmsys::RegularExpression IncludeFileRegularExpression;
   cmsys::RegularExpression ComplainFileRegularExpression;
   std::vector<std::string> IncludeDirectories;
-  typedef std::map<std::string, std::string> FileToPathMapType;
-  typedef std::map<std::string, FileToPathMapType>
-    DirectoryToFileToPathMapType;
-  typedef std::map<std::string, cmDependInformation*> DependInformationMapType;
+  using FileToPathMapType = std::map<std::string, std::string>;
+  using DirectoryToFileToPathMapType =
+    std::map<std::string, FileToPathMapType>;
+  using DependInformationMapType = std::map<std::string, cmDependInformation*>;
   DependInformationMapType DependInformationMap;
   DirectoryToFileToPathMapType DirectoryToFileToPathMap;
 };
 
+void ListDependencies(cmDependInformation const* info, FILE* fout,
+                      std::set<cmDependInformation const*>* visited);
+}
+
 // cmOutputRequiredFilesCommand
-bool cmOutputRequiredFilesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmOutputRequiredFilesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status)
 {
   if (args.size() != 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // store the arg for final pass
-  this->File = args[0];
-  this->OutputFile = args[1];
+  const std::string& file = args[0];
+  const std::string& outputFile = args[1];
 
   // compute the list of files
   cmLBDepend md;
-  md.SetMakefile(this->Makefile);
-  md.AddSearchPath(this->Makefile->GetCurrentSourceDirectory());
+  md.SetMakefile(&status.GetMakefile());
+  md.AddSearchPath(status.GetMakefile().GetCurrentSourceDirectory());
   // find the depends for a file
-  const cmDependInformation* info = md.FindDependencies(this->File.c_str());
+  const cmDependInformation* info = md.FindDependencies(file.c_str());
   if (info) {
     // write them out
-    FILE* fout = cmsys::SystemTools::Fopen(this->OutputFile, "w");
+    FILE* fout = cmsys::SystemTools::Fopen(outputFile, "w");
     if (!fout) {
-      std::string err = "Can not open output file: ";
-      err += this->OutputFile;
-      this->SetError(err);
+      status.SetError(cmStrCat("Can not open output file: ", outputFile));
       return false;
     }
     std::set<cmDependInformation const*> visited;
-    this->ListDependencies(info, fout, &visited);
+    ListDependencies(info, fout, &visited);
     fclose(fout);
   }
 
   return true;
 }
 
-void cmOutputRequiredFilesCommand::ListDependencies(
-  cmDependInformation const* info, FILE* fout,
-  std::set<cmDependInformation const*>* visited)
+namespace {
+void ListDependencies(cmDependInformation const* info, FILE* fout,
+                      std::set<cmDependInformation const*>* visited)
 {
   // add info to the visited set
   visited->insert(info);
@@ -515,7 +509,8 @@
           fprintf(fout, "%s\n", d->FullPath.c_str());
         }
       }
-      this->ListDependencies(d, fout, visited);
+      ListDependencies(d, fout, visited);
     }
   }
 }
+}
diff --git a/Source/cmOutputRequiredFilesCommand.h b/Source/cmOutputRequiredFilesCommand.h
index 09e622b..4c11894 100644
--- a/Source/cmOutputRequiredFilesCommand.h
+++ b/Source/cmOutputRequiredFilesCommand.h
@@ -5,29 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <set>
-#include <stdio.h>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
-class cmDependInformation;
 class cmExecutionStatus;
 
-class cmOutputRequiredFilesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmOutputRequiredFilesCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  void ListDependencies(cmDependInformation const* info, FILE* fout,
-                        std::set<cmDependInformation const*>* visited);
-
-private:
-  std::string File;
-  std::string OutputFile;
-};
+bool cmOutputRequiredFilesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 5213432..d712edb 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -7,15 +7,15 @@
 #include <sstream>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include <cm/string_view>
+
 #include "cmArgumentParser.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cm_string_view.hxx"
-
-class cmExecutionStatus;
 
 static std::string EscapeArg(const std::string& arg)
 {
@@ -38,10 +38,10 @@
 
 namespace {
 
-typedef std::map<std::string, bool> options_map;
-typedef std::map<std::string, std::string> single_map;
-typedef std::map<std::string, std::vector<std::string>> multi_map;
-typedef std::set<std::string> options_set;
+using options_map = std::map<std::string, bool>;
+using single_map = std::map<std::string, std::string>;
+using multi_map = std::map<std::string, std::vector<std::string>>;
+using options_set = std::set<std::string>;
 
 struct UserArgumentParser : public cmArgumentParser<void>
 {
@@ -75,7 +75,7 @@
 
   for (auto const& iter : singleValArgs) {
     if (!iter.second.empty()) {
-      makefile.AddDefinition(prefix + iter.first, iter.second.c_str());
+      makefile.AddDefinition(prefix + iter.first, iter.second);
     } else {
       makefile.RemoveDefinition(prefix + iter.first);
     }
@@ -84,7 +84,7 @@
   for (auto const& iter : multiValArgs) {
     if (!iter.second.empty()) {
       makefile.AddDefinition(prefix + iter.first,
-                             JoinList(iter.second, parseFromArgV).c_str());
+                             JoinList(iter.second, parseFromArgV));
     } else {
       makefile.RemoveDefinition(prefix + iter.first);
     }
@@ -92,39 +92,38 @@
 
   if (!unparsed.empty()) {
     makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS",
-                           JoinList(unparsed, parseFromArgV).c_str());
+                           JoinList(unparsed, parseFromArgV));
   } else {
     makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
   }
 
   if (!keywordsMissingValues.empty()) {
-    makefile.AddDefinition(
-      prefix + "KEYWORDS_MISSING_VALUES",
-      cmJoin(cmMakeRange(keywordsMissingValues), ";").c_str());
+    makefile.AddDefinition(prefix + "KEYWORDS_MISSING_VALUES",
+                           cmJoin(cmMakeRange(keywordsMissingValues), ";"));
   } else {
     makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES");
   }
 }
 
-bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmParseArgumentsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   // cmake_parse_arguments(prefix options single multi <ARGN>)
   //                         1       2      3      4
   // or
   // cmake_parse_arguments(PARSE_ARGV N prefix options single multi)
   if (args.size() < 4) {
-    this->SetError("must be called with at least 4 arguments.");
+    status.SetError("must be called with at least 4 arguments.");
     return false;
   }
 
-  std::vector<std::string>::const_iterator argIter = args.begin(),
-                                           argEnd = args.end();
+  auto argIter = args.begin();
+  auto argEnd = args.end();
   bool parseFromArgV = false;
   unsigned long argvStart = 0;
   if (*argIter == "PARSE_ARGV") {
     if (args.size() != 6) {
-      this->Makefile->IssueMessage(
+      status.GetMakefile().IssueMessage(
         MessageType::FATAL_ERROR,
         "PARSE_ARGV must be called with exactly 6 arguments.");
       cmSystemTools::SetFatalErrorOccured();
@@ -132,10 +131,10 @@
     }
     parseFromArgV = true;
     argIter++; // move past PARSE_ARGV
-    if (!cmSystemTools::StringToULong(argIter->c_str(), &argvStart)) {
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                   "PARSE_ARGV index '" + *argIter +
-                                     "' is not an unsigned integer");
+    if (!cmStrToULong(*argIter, &argvStart)) {
+      status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR,
+                                        "PARSE_ARGV index '" + *argIter +
+                                          "' is not an unsigned integer");
       cmSystemTools::SetFatalErrorOccured();
       return true;
     }
@@ -155,24 +154,23 @@
   // anything else is put into a vector of unparsed strings
   std::vector<std::string> unparsed;
 
-  auto const duplicateKey = [this](std::string const& key) {
-    this->GetMakefile()->IssueMessage(
+  auto const duplicateKey = [&status](std::string const& key) {
+    status.GetMakefile().IssueMessage(
       MessageType::WARNING, "keyword defined more than once: " + key);
   };
 
   // the second argument is a (cmake) list of options without argument
-  std::vector<std::string> list;
-  cmSystemTools::ExpandListArgument(*argIter++, list);
+  std::vector<std::string> list = cmExpandedList(*argIter++);
   parser.Bind(list, options, duplicateKey);
 
   // the third argument is a (cmake) list of single argument options
   list.clear();
-  cmSystemTools::ExpandListArgument(*argIter++, list);
+  cmExpandList(*argIter++, list);
   parser.Bind(list, singleValArgs, duplicateKey);
 
   // the fourth argument is a (cmake) list of multi argument options
   list.clear();
-  cmSystemTools::ExpandListArgument(*argIter++, list);
+  cmExpandList(*argIter++, list);
   parser.Bind(list, multiValArgs, duplicateKey);
 
   list.clear();
@@ -180,27 +178,28 @@
     // Flatten ;-lists in the arguments into a single list as was done
     // by the original function(CMAKE_PARSE_ARGUMENTS).
     for (; argIter != argEnd; ++argIter) {
-      cmSystemTools::ExpandListArgument(*argIter, list);
+      cmExpandList(*argIter, list);
     }
   } else {
     // in the PARSE_ARGV move read the arguments from ARGC and ARGV#
-    std::string argc = this->Makefile->GetSafeDefinition("ARGC");
+    std::string argc = status.GetMakefile().GetSafeDefinition("ARGC");
     unsigned long count;
-    if (!cmSystemTools::StringToULong(argc.c_str(), &count)) {
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                   "PARSE_ARGV called with ARGC='" + argc +
-                                     "' that is not an unsigned integer");
+    if (!cmStrToULong(argc, &count)) {
+      status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR,
+                                        "PARSE_ARGV called with ARGC='" +
+                                          argc +
+                                          "' that is not an unsigned integer");
       cmSystemTools::SetFatalErrorOccured();
       return true;
     }
     for (unsigned long i = argvStart; i < count; ++i) {
       std::ostringstream argName;
       argName << "ARGV" << i;
-      const char* arg = this->Makefile->GetDefinition(argName.str());
+      const char* arg = status.GetMakefile().GetDefinition(argName.str());
       if (!arg) {
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                     "PARSE_ARGV called with " +
-                                       argName.str() + " not set");
+        status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR,
+                                          "PARSE_ARGV called with " +
+                                            argName.str() + " not set");
         cmSystemTools::SetFatalErrorOccured();
         return true;
       }
@@ -213,7 +212,8 @@
   parser.Parse(list, &unparsed, &keywordsMissingValues);
 
   PassParsedArguments(
-    prefix, *this->Makefile, options, singleValArgs, multiValArgs, unparsed,
+    prefix, status.GetMakefile(), options, singleValArgs, multiValArgs,
+    unparsed,
     options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()),
     parseFromArgV);
 
diff --git a/Source/cmParseArgumentsCommand.h b/Source/cmParseArgumentsCommand.h
index b8ba61d..b2e436d 100644
--- a/Source/cmParseArgumentsCommand.h
+++ b/Source/cmParseArgumentsCommand.h
@@ -8,27 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmParseArgumentsCommand
- *
- */
-class cmParseArgumentsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmParseArgumentsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmParseArgumentsCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmPipeConnection.h b/Source/cmPipeConnection.h
index e67f15c..81f8a49 100644
--- a/Source/cmPipeConnection.h
+++ b/Source/cmPipeConnection.h
@@ -4,12 +4,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmUVHandlePtr.h"
 #include <string>
 
-#include "cmConnection.h"
 #include "cm_uv.h"
 
+#include "cmConnection.h"
+#include "cmUVHandlePtr.h"
+
 class cmPipeConnection : public cmEventBasedConnection
 {
 public:
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index ec40136..5c8bc98 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -1,20 +1,20 @@
 #include "cmPolicies.h"
 
-#include "cmAlgorithms.h"
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+#include <vector>
+
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
-#include <assert.h>
-#include <ctype.h>
-#include <sstream>
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-
 static bool stringToId(const char* input, cmPolicies::PolicyID& pid)
 {
   assert(input);
@@ -34,7 +34,7 @@
     }
   }
   long id;
-  if (!cmSystemTools::StringToLong(input + 3, &id)) {
+  if (!cmStrToLong(input + 3, &id)) {
     return false;
   }
   if (id >= cmPolicies::CMPCOUNT) {
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index b705119..92c80bb 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -279,7 +279,18 @@
   SELECT(POLICY, CMP0094,                                                     \
          "FindPython3,  FindPython2 and FindPyton use "                       \
          "LOCATION for lookup strategy.",                                     \
-         3, 15, 0, cmPolicies::WARN)
+         3, 15, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0095,                                                     \
+         "RPATH entries are properly escaped in the intermediary CMake "      \
+         "install script.",                                                   \
+         3, 16, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0096,                                                     \
+         "project() preserves leading zeros in version components.", 3, 16,   \
+         0, cmPolicies::WARN)                                                 \
+  SELECT(POLICY, CMP0097,                                                     \
+         "ExternalProject_Add with GIT_SUBMODULES \"\" initializes no "       \
+         "submodules.",                                                       \
+         3, 16, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -307,7 +318,8 @@
   F(CMP0073)                                                                  \
   F(CMP0076)                                                                  \
   F(CMP0081)                                                                  \
-  F(CMP0083)
+  F(CMP0083)                                                                  \
+  F(CMP0095)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies
diff --git a/Source/cmProcessOutput.h b/Source/cmProcessOutput.h
index 400354c..3db47a4 100644
--- a/Source/cmProcessOutput.h
+++ b/Source/cmProcessOutput.h
@@ -5,7 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stddef.h>
+#include <cstddef>
 #include <string>
 #include <vector>
 
diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx
index a2bc16f..9ebf5b7 100644
--- a/Source/cmProcessTools.cxx
+++ b/Source/cmProcessTools.cxx
@@ -1,10 +1,12 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcessTools.h"
-#include "cmProcessOutput.h"
+
+#include <ostream>
 
 #include "cmsys/Process.h"
-#include <ostream>
+
+#include "cmProcessOutput.h"
 
 void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
                                 OutputParser* err, Encoding encoding)
diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h
index 7616316..21d59c4 100644
--- a/Source/cmProcessTools.h
+++ b/Source/cmProcessTools.h
@@ -4,12 +4,13 @@
 #define cmProcessTools_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmProcessOutput.h"
 
+#include <cstring>
 #include <iosfwd>
-#include <string.h>
 #include <string>
 
+#include "cmProcessOutput.h"
+
 /** \class cmProcessTools
  * \brief Helper classes for process output parsing
  *
@@ -17,7 +18,7 @@
 class cmProcessTools
 {
 public:
-  typedef cmProcessOutput::Encoding Encoding;
+  using Encoding = cmProcessOutput::Encoding;
   /** Abstract interface for process output parsers.  */
   class OutputParser
   {
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 8615ecc..eb59b4f 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -2,58 +2,56 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProjectCommand.h"
 
-#include "cmsys/RegularExpression.hxx"
+#include <array>
+#include <cstddef>
+#include <cstdio>
 #include <functional>
-#include <sstream>
-#include <stdio.h>
+#include <limits>
+#include <utility>
 
-#include "cmAlgorithms.h"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+static bool IncludeByVariable(cmExecutionStatus& status,
+                              const std::string& variable);
+static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name,
+                                    std::string const& value);
 
-// cmProjectCommand
-bool cmProjectCommand::InitialPass(std::vector<std::string> const& args,
-                                   cmExecutionStatus&)
+bool cmProjectCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("PROJECT called with incorrect number of arguments");
+    status.SetError("PROJECT called with incorrect number of arguments");
     return false;
   }
 
-  if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE_BEFORE")) {
+  cmMakefile& mf = status.GetMakefile();
+  if (!IncludeByVariable(status, "CMAKE_PROJECT_INCLUDE_BEFORE")) {
     return false;
   }
 
   std::string const& projectName = args[0];
 
-  this->Makefile->SetProjectName(projectName);
+  mf.SetProjectName(projectName);
 
-  std::string bindir = projectName;
-  bindir += "_BINARY_DIR";
-  std::string srcdir = projectName;
-  srcdir += "_SOURCE_DIR";
+  mf.AddCacheDefinition(projectName + "_BINARY_DIR",
+                        mf.GetCurrentBinaryDirectory().c_str(),
+                        "Value Computed by CMake", cmStateEnums::STATIC);
+  mf.AddCacheDefinition(projectName + "_SOURCE_DIR",
+                        mf.GetCurrentSourceDirectory().c_str(),
+                        "Value Computed by CMake", cmStateEnums::STATIC);
 
-  this->Makefile->AddCacheDefinition(
-    bindir, this->Makefile->GetCurrentBinaryDirectory().c_str(),
-    "Value Computed by CMake", cmStateEnums::STATIC);
-  this->Makefile->AddCacheDefinition(
-    srcdir, this->Makefile->GetCurrentSourceDirectory().c_str(),
-    "Value Computed by CMake", cmStateEnums::STATIC);
+  mf.AddDefinition("PROJECT_BINARY_DIR", mf.GetCurrentBinaryDirectory());
+  mf.AddDefinition("PROJECT_SOURCE_DIR", mf.GetCurrentSourceDirectory());
 
-  bindir = "PROJECT_BINARY_DIR";
-  srcdir = "PROJECT_SOURCE_DIR";
-
-  this->Makefile->AddDefinition(
-    bindir, this->Makefile->GetCurrentBinaryDirectory().c_str());
-  this->Makefile->AddDefinition(
-    srcdir, this->Makefile->GetCurrentSourceDirectory().c_str());
-
-  this->Makefile->AddDefinition("PROJECT_NAME", projectName.c_str());
+  mf.AddDefinition("PROJECT_NAME", projectName);
 
   // Set the CMAKE_PROJECT_NAME variable to be the highest-level
   // project name in the tree. If there are two project commands
@@ -61,12 +59,10 @@
   // CMakeLists.txt file, then go with the last one, so that
   // CMAKE_PROJECT_NAME will match PROJECT_NAME, and cmake --build
   // will work.
-  if (!this->Makefile->GetDefinition("CMAKE_PROJECT_NAME") ||
-      (this->Makefile->IsRootMakefile())) {
-    this->Makefile->AddDefinition("CMAKE_PROJECT_NAME", projectName.c_str());
-    this->Makefile->AddCacheDefinition(
-      "CMAKE_PROJECT_NAME", projectName.c_str(), "Value Computed by CMake",
-      cmStateEnums::STATIC);
+  if (!mf.GetDefinition("CMAKE_PROJECT_NAME") || mf.IsRootMakefile()) {
+    mf.AddDefinition("CMAKE_PROJECT_NAME", projectName);
+    mf.AddCacheDefinition("CMAKE_PROJECT_NAME", projectName.c_str(),
+                          "Value Computed by CMake", cmStateEnums::STATIC);
   }
 
   bool haveVersion = false;
@@ -93,9 +89,8 @@
   for (size_t i = 1; i < args.size(); ++i) {
     if (args[i] == "LANGUAGES") {
       if (haveLanguages) {
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          "LANGUAGES may be specified at most once.");
+        mf.IssueMessage(MessageType::FATAL_ERROR,
+                        "LANGUAGES may be specified at most once.");
         cmSystemTools::SetFatalErrorOccured();
         return true;
       }
@@ -105,17 +100,16 @@
       }
       doing = DoingLanguages;
       if (!languages.empty()) {
-        std::string msg =
+        std::string msg = cmStrCat(
           "the following parameters must be specified after LANGUAGES "
-          "keyword: ";
-        msg += cmJoin(languages, ", ");
-        msg += '.';
-        this->Makefile->IssueMessage(MessageType::WARNING, msg);
+          "keyword: ",
+          cmJoin(languages, ", "), '.');
+        mf.IssueMessage(MessageType::WARNING, msg);
       }
     } else if (args[i] == "VERSION") {
       if (haveVersion) {
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                     "VERSION may be specified at most once.");
+        mf.IssueMessage(MessageType::FATAL_ERROR,
+                        "VERSION may be specified at most once.");
         cmSystemTools::SetFatalErrorOccured();
         return true;
       }
@@ -124,8 +118,8 @@
         missedValueReporter();
       }
       doing = DoingVersion;
-      missedValueReporter = [this, &resetReporter]() {
-        this->Makefile->IssueMessage(
+      missedValueReporter = [&mf, &resetReporter]() {
+        mf.IssueMessage(
           MessageType::WARNING,
           "VERSION keyword not followed by a value or was followed by a "
           "value that expanded to nothing.");
@@ -133,9 +127,8 @@
       };
     } else if (args[i] == "DESCRIPTION") {
       if (haveDescription) {
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          "DESCRIPTION may be specified at most once.");
+        mf.IssueMessage(MessageType::FATAL_ERROR,
+                        "DESCRIPTION may be specified at most once.");
         cmSystemTools::SetFatalErrorOccured();
         return true;
       }
@@ -144,8 +137,8 @@
         missedValueReporter();
       }
       doing = DoingDescription;
-      missedValueReporter = [this, &resetReporter]() {
-        this->Makefile->IssueMessage(
+      missedValueReporter = [&mf, &resetReporter]() {
+        mf.IssueMessage(
           MessageType::WARNING,
           "DESCRIPTION keyword not followed by a value or was followed "
           "by a value that expanded to nothing.");
@@ -153,16 +146,15 @@
       };
     } else if (args[i] == "HOMEPAGE_URL") {
       if (haveHomepage) {
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          "HOMEPAGE_URL may be specified at most once.");
+        mf.IssueMessage(MessageType::FATAL_ERROR,
+                        "HOMEPAGE_URL may be specified at most once.");
         cmSystemTools::SetFatalErrorOccured();
         return true;
       }
       haveHomepage = true;
       doing = DoingHomepage;
-      missedValueReporter = [this, &resetReporter]() {
-        this->Makefile->IssueMessage(
+      missedValueReporter = [&mf, &resetReporter]() {
+        mf.IssueMessage(
           MessageType::WARNING,
           "HOMEPAGE_URL keyword not followed by a value or was followed "
           "by a value that expanded to nothing.");
@@ -194,10 +186,9 @@
 
   if ((haveVersion || haveDescription || haveHomepage) && !haveLanguages &&
       !languages.empty()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "project with VERSION, DESCRIPTION or HOMEPAGE_URL must "
-      "use LANGUAGES before language names.");
+    mf.IssueMessage(MessageType::FATAL_ERROR,
+                    "project with VERSION, DESCRIPTION or HOMEPAGE_URL must "
+                    "use LANGUAGES before language names.");
     cmSystemTools::SetFatalErrorOccured();
     return true;
   }
@@ -205,14 +196,13 @@
     languages.emplace_back("NONE");
   }
 
-  cmPolicies::PolicyStatus cmp0048 =
-    this->Makefile->GetPolicyStatus(cmPolicies::CMP0048);
+  cmPolicies::PolicyStatus const cmp0048 =
+    mf.GetPolicyStatus(cmPolicies::CMP0048);
   if (haveVersion) {
     // Set project VERSION variables to given values
     if (cmp0048 == cmPolicies::OLD || cmp0048 == cmPolicies::WARN) {
-      this->Makefile->IssueMessage(
-        MessageType::FATAL_ERROR,
-        "VERSION not allowed unless CMP0048 is set to NEW");
+      mf.IssueMessage(MessageType::FATAL_ERROR,
+                      "VERSION not allowed unless CMP0048 is set to NEW");
       cmSystemTools::SetFatalErrorOccured();
       return true;
     }
@@ -220,65 +210,84 @@
     cmsys::RegularExpression vx(
       R"(^([0-9]+(\.[0-9]+(\.[0-9]+(\.[0-9]+)?)?)?)?$)");
     if (!vx.find(version)) {
-      std::string e = "VERSION \"" + version + "\" format invalid.";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+      std::string e = R"(VERSION ")" + version + R"(" format invalid.)";
+      mf.IssueMessage(MessageType::FATAL_ERROR, e);
       cmSystemTools::SetFatalErrorOccured();
       return true;
     }
 
-    std::string vs;
-    const char* sep = "";
-    char vb[4][64];
-    unsigned int v[4] = { 0, 0, 0, 0 };
-    int vc =
-      sscanf(version.c_str(), "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]);
-    for (int i = 0; i < 4; ++i) {
-      if (i < vc) {
-        sprintf(vb[i], "%u", v[i]);
-        vs += sep;
-        vs += vb[i];
-        sep = ".";
-      } else {
-        vb[i][0] = 0;
+    cmPolicies::PolicyStatus const cmp0096 =
+      mf.GetPolicyStatus(cmPolicies::CMP0096);
+
+    constexpr std::size_t MAX_VERSION_COMPONENTS = 4u;
+    std::string version_string;
+    std::array<std::string, MAX_VERSION_COMPONENTS> version_components;
+
+    if (cmp0096 == cmPolicies::OLD || cmp0096 == cmPolicies::WARN) {
+      char vb[MAX_VERSION_COMPONENTS][std::numeric_limits<unsigned>::digits10];
+      unsigned v[MAX_VERSION_COMPONENTS] = { 0, 0, 0, 0 };
+      const int vc = std::sscanf(version.c_str(), "%u.%u.%u.%u", &v[0], &v[1],
+                                 &v[2], &v[3]);
+      for (auto i = 0u; i < MAX_VERSION_COMPONENTS; ++i) {
+        if (int(i) < vc) {
+          std::sprintf(vb[i], "%u", v[i]);
+          version_string += &"."[std::size_t(i == 0)];
+          version_string += vb[i];
+          version_components[i] = vb[i];
+        } else {
+          vb[i][0] = '\x00';
+        }
+      }
+    } else {
+      // The regex above verified that we have a .-separated string of
+      // non-negative integer components.  Keep the original string.
+      version_string = std::move(version);
+      // Split the integer components.
+      auto components = cmSystemTools::SplitString(version_string, '.');
+      for (auto i = 0u; i < components.size(); ++i) {
+        version_components[i] = std::move(components[i]);
       }
     }
 
     std::string vv;
     vv = projectName + "_VERSION";
-    this->Makefile->AddDefinition("PROJECT_VERSION", vs.c_str());
-    this->Makefile->AddDefinition(vv, vs.c_str());
+    mf.AddDefinition("PROJECT_VERSION", version_string);
+    mf.AddDefinition(vv, version_string);
     vv = projectName + "_VERSION_MAJOR";
-    this->Makefile->AddDefinition("PROJECT_VERSION_MAJOR", vb[0]);
-    this->Makefile->AddDefinition(vv, vb[0]);
+    mf.AddDefinition("PROJECT_VERSION_MAJOR", version_components[0]);
+    mf.AddDefinition(vv, version_components[0]);
     vv = projectName + "_VERSION_MINOR";
-    this->Makefile->AddDefinition("PROJECT_VERSION_MINOR", vb[1]);
-    this->Makefile->AddDefinition(vv, vb[1]);
+    mf.AddDefinition("PROJECT_VERSION_MINOR", version_components[1]);
+    mf.AddDefinition(vv, version_components[1]);
     vv = projectName + "_VERSION_PATCH";
-    this->Makefile->AddDefinition("PROJECT_VERSION_PATCH", vb[2]);
-    this->Makefile->AddDefinition(vv, vb[2]);
+    mf.AddDefinition("PROJECT_VERSION_PATCH", version_components[2]);
+    mf.AddDefinition(vv, version_components[2]);
     vv = projectName + "_VERSION_TWEAK";
-    this->Makefile->AddDefinition("PROJECT_VERSION_TWEAK", vb[3]);
-    this->Makefile->AddDefinition(vv, vb[3]);
+    mf.AddDefinition("PROJECT_VERSION_TWEAK", version_components[3]);
+    mf.AddDefinition(vv, version_components[3]);
     // Also, try set top level variables
-    TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION", vs.c_str());
-    TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MAJOR", vb[0]);
-    TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_MINOR", vb[1]);
-    TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_PATCH", vb[2]);
-    TopLevelCMakeVarCondSet("CMAKE_PROJECT_VERSION_TWEAK", vb[3]);
+    TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION", version_string);
+    TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_MAJOR",
+                            version_components[0]);
+    TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_MINOR",
+                            version_components[1]);
+    TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_PATCH",
+                            version_components[2]);
+    TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_VERSION_TWEAK",
+                            version_components[3]);
   } else if (cmp0048 != cmPolicies::OLD) {
     // Set project VERSION variables to empty
-    std::vector<std::string> vv;
-    vv.emplace_back("PROJECT_VERSION");
-    vv.emplace_back("PROJECT_VERSION_MAJOR");
-    vv.emplace_back("PROJECT_VERSION_MINOR");
-    vv.emplace_back("PROJECT_VERSION_PATCH");
-    vv.emplace_back("PROJECT_VERSION_TWEAK");
-    vv.push_back(projectName + "_VERSION");
-    vv.push_back(projectName + "_VERSION_MAJOR");
-    vv.push_back(projectName + "_VERSION_MINOR");
-    vv.push_back(projectName + "_VERSION_PATCH");
-    vv.push_back(projectName + "_VERSION_TWEAK");
-    if (this->Makefile->IsRootMakefile()) {
+    std::vector<std::string> vv = { "PROJECT_VERSION",
+                                    "PROJECT_VERSION_MAJOR",
+                                    "PROJECT_VERSION_MINOR",
+                                    "PROJECT_VERSION_PATCH",
+                                    "PROJECT_VERSION_TWEAK",
+                                    projectName + "_VERSION",
+                                    projectName + "_VERSION_MAJOR",
+                                    projectName + "_VERSION_MINOR",
+                                    projectName + "_VERSION_PATCH",
+                                    projectName + "_VERSION_TWEAK" };
+    if (mf.IsRootMakefile()) {
       vv.emplace_back("CMAKE_PROJECT_VERSION");
       vv.emplace_back("CMAKE_PROJECT_VERSION_MAJOR");
       vv.emplace_back("CMAKE_PROJECT_VERSION_MINOR");
@@ -287,7 +296,7 @@
     }
     std::string vw;
     for (std::string const& i : vv) {
-      const char* v = this->Makefile->GetDefinition(i);
+      const char* const v = mf.GetDefinition(i);
       if (v && *v) {
         if (cmp0048 == cmPolicies::WARN) {
           if (!injectedProjectCommand) {
@@ -295,54 +304,54 @@
             vw += i;
           }
         } else {
-          this->Makefile->AddDefinition(i, "");
+          mf.AddDefinition(i, "");
         }
       }
     }
     if (!vw.empty()) {
-      std::ostringstream w;
-      w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0048)
-        << "\nThe following variable(s) would be set to empty:" << vw;
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+      mf.IssueMessage(
+        MessageType::AUTHOR_WARNING,
+        cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0048),
+                 "\nThe following variable(s) would be set to empty:", vw));
     }
   }
 
-  this->Makefile->AddDefinition("PROJECT_DESCRIPTION", description.c_str());
-  this->Makefile->AddDefinition(projectName + "_DESCRIPTION",
-                                description.c_str());
-  TopLevelCMakeVarCondSet("CMAKE_PROJECT_DESCRIPTION", description.c_str());
+  mf.AddDefinition("PROJECT_DESCRIPTION", description);
+  mf.AddDefinition(projectName + "_DESCRIPTION", description);
+  TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_DESCRIPTION", description);
 
-  this->Makefile->AddDefinition("PROJECT_HOMEPAGE_URL", homepage.c_str());
-  this->Makefile->AddDefinition(projectName + "_HOMEPAGE_URL",
-                                homepage.c_str());
-  TopLevelCMakeVarCondSet("CMAKE_PROJECT_HOMEPAGE_URL", homepage.c_str());
+  mf.AddDefinition("PROJECT_HOMEPAGE_URL", homepage);
+  mf.AddDefinition(projectName + "_HOMEPAGE_URL", homepage);
+  TopLevelCMakeVarCondSet(mf, "CMAKE_PROJECT_HOMEPAGE_URL", homepage);
 
   if (languages.empty()) {
     // if no language is specified do c and c++
-    languages.emplace_back("C");
-    languages.emplace_back("CXX");
+    languages = { "C", "CXX" };
   }
-  this->Makefile->EnableLanguage(languages, false);
+  mf.EnableLanguage(languages, false);
 
-  if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE")) {
+  if (!IncludeByVariable(status, "CMAKE_PROJECT_INCLUDE")) {
     return false;
   }
 
-  if (!this->IncludeByVariable("CMAKE_PROJECT_" + projectName + "_INCLUDE")) {
+  if (!IncludeByVariable(status,
+                         "CMAKE_PROJECT_" + projectName + "_INCLUDE")) {
     return false;
   }
 
   return true;
 }
 
-bool cmProjectCommand::IncludeByVariable(const std::string& variable)
+static bool IncludeByVariable(cmExecutionStatus& status,
+                              const std::string& variable)
 {
-  const char* include = this->Makefile->GetDefinition(variable);
+  cmMakefile& mf = status.GetMakefile();
+  const char* const include = mf.GetDefinition(variable);
   if (!include) {
     return true;
   }
 
-  const bool readit = this->Makefile->ReadDependentFile(include);
+  const bool readit = mf.ReadDependentFile(include);
   if (readit) {
     return true;
   }
@@ -351,24 +360,20 @@
     return true;
   }
 
-  std::string m = "could not find file:\n"
-                  "  ";
-  m += include;
-  this->SetError(m);
+  status.SetError(cmStrCat("could not find file:\n  ", include));
   return false;
 }
 
-void cmProjectCommand::TopLevelCMakeVarCondSet(const char* const name,
-                                               const char* const value)
+static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name,
+                                    std::string const& value)
 {
   // Set the CMAKE_PROJECT_XXX variable to be the highest-level
   // project name in the tree. If there are two project commands
   // in the same CMakeLists.txt file, and it is the top level
   // CMakeLists.txt file, then go with the last one.
-  if (!this->Makefile->GetDefinition(name) ||
-      (this->Makefile->IsRootMakefile())) {
-    this->Makefile->AddDefinition(name, value);
-    this->Makefile->AddCacheDefinition(name, value, "Value Computed by CMake",
-                                       cmStateEnums::STATIC);
+  if (!mf.GetDefinition(name) || mf.IsRootMakefile()) {
+    mf.AddDefinition(name, value);
+    mf.AddCacheDefinition(name, value.c_str(), "Value Computed by CMake",
+                          cmStateEnums::STATIC);
   }
 }
diff --git a/Source/cmProjectCommand.h b/Source/cmProjectCommand.h
index f1d03e7..c06b459 100644
--- a/Source/cmProjectCommand.h
+++ b/Source/cmProjectCommand.h
@@ -8,36 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmProjectCommand
- * \brief Specify the name for this build project.
- *
- * cmProjectCommand is used to specify a name for this build project.
- * It is defined once per set of CMakeList.txt files (including
- * all subdirectories). Currently it just sets the name of the workspace
- * file for Microsoft Visual C++
- */
-class cmProjectCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmProjectCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  bool IncludeByVariable(const std::string& variable);
-  void TopLevelCMakeVarCondSet(const char* name, const char* value);
-};
+bool cmProjectCommand(std::vector<std::string> const& args,
+                      cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx
deleted file mode 100644
index 27f0ecd..0000000
--- a/Source/cmProperty.cxx
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmProperty.h"
-
-void cmProperty::Set(const char* value)
-{
-  this->Value = value;
-  this->ValueHasBeenSet = true;
-}
-
-void cmProperty::Append(const char* value, bool asString)
-{
-  if (!this->Value.empty() && *value && !asString) {
-    this->Value += ";";
-  }
-  this->Value += value;
-  this->ValueHasBeenSet = true;
-}
-
-const char* cmProperty::GetValue() const
-{
-  if (this->ValueHasBeenSet) {
-    return this->Value.c_str();
-  }
-  return nullptr;
-}
diff --git a/Source/cmProperty.h b/Source/cmProperty.h
index d11c5ef..80f131a 100644
--- a/Source/cmProperty.h
+++ b/Source/cmProperty.h
@@ -5,8 +5,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
-
 class cmProperty
 {
 public:
@@ -22,22 +20,6 @@
     CACHED_VARIABLE,
     INSTALL
   };
-
-  // set this property
-  void Set(const char* value);
-
-  // append to this property
-  void Append(const char* value, bool asString = false);
-
-  // get the value
-  const char* GetValue() const;
-
-  // construct with the value not set
-  cmProperty() { this->ValueHasBeenSet = false; }
-
-protected:
-  std::string Value;
-  bool ValueHasBeenSet;
 };
 
 #endif
diff --git a/Source/cmPropertyDefinition.h b/Source/cmPropertyDefinition.h
index 9adff49..0d68c32 100644
--- a/Source/cmPropertyDefinition.h
+++ b/Source/cmPropertyDefinition.h
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmProperty.h"
-
 #include <string>
 
+#include "cmProperty.h"
+
 /** \class cmPropertyDefinition
  * \brief Property meta-information
  *
diff --git a/Source/cmPropertyDefinitionMap.cxx b/Source/cmPropertyDefinitionMap.cxx
index 5daaf9b..f752ed7 100644
--- a/Source/cmPropertyDefinitionMap.cxx
+++ b/Source/cmPropertyDefinitionMap.cxx
@@ -10,7 +10,7 @@
                                              const char* FullDescription,
                                              bool chain)
 {
-  cmPropertyDefinitionMap::iterator it = this->find(name);
+  auto it = this->find(name);
   cmPropertyDefinition* prop;
   if (it == this->end()) {
     prop = &(*this)[name];
@@ -26,7 +26,7 @@
 
 bool cmPropertyDefinitionMap::IsPropertyChained(const std::string& name) const
 {
-  cmPropertyDefinitionMap::const_iterator it = this->find(name);
+  auto it = this->find(name);
   if (it == this->end()) {
     return false;
   }
diff --git a/Source/cmPropertyDefinitionMap.h b/Source/cmPropertyDefinitionMap.h
index 97ba553..8ec7910 100644
--- a/Source/cmPropertyDefinitionMap.h
+++ b/Source/cmPropertyDefinitionMap.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmProperty.h"
-#include "cmPropertyDefinition.h"
-
 #include <map>
 #include <string>
 
+#include "cmProperty.h"
+#include "cmPropertyDefinition.h"
+
 class cmPropertyDefinitionMap
   : public std::map<std::string, cmPropertyDefinition>
 {
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 3f6d7c8..a3d4946 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -3,40 +3,21 @@
 #include "cmPropertyMap.h"
 
 #include <algorithm>
-#include <assert.h>
 #include <utility>
 
-cmProperty* cmPropertyMap::GetOrCreateProperty(const std::string& name)
+void cmPropertyMap::Clear()
 {
-  cmPropertyMap::iterator it = this->find(name);
-  cmProperty* prop;
-  if (it == this->end()) {
-    prop = &(*this)[name];
-  } else {
-    prop = &(it->second);
-  }
-  return prop;
-}
-
-std::vector<std::string> cmPropertyMap::GetPropertyList() const
-{
-  std::vector<std::string> keyList;
-  for (auto const& i : *this) {
-    keyList.push_back(i.first);
-  }
-  std::sort(keyList.begin(), keyList.end());
-  return keyList;
+  Map_.clear();
 }
 
 void cmPropertyMap::SetProperty(const std::string& name, const char* value)
 {
   if (!value) {
-    this->erase(name);
+    Map_.erase(name);
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Set(value);
+  Map_[name] = value;
 }
 
 void cmPropertyMap::AppendProperty(const std::string& name, const char* value,
@@ -47,17 +28,53 @@
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Append(value, asString);
+  {
+    std::string& pVal = Map_[name];
+    if (!pVal.empty() && !asString) {
+      pVal += ';';
+    }
+    pVal += value;
+  }
+}
+
+void cmPropertyMap::RemoveProperty(const std::string& name)
+{
+  Map_.erase(name);
 }
 
 const char* cmPropertyMap::GetPropertyValue(const std::string& name) const
 {
-  assert(!name.empty());
-
-  cmPropertyMap::const_iterator it = this->find(name);
-  if (it == this->end()) {
-    return nullptr;
+  {
+    auto it = Map_.find(name);
+    if (it != Map_.end()) {
+      return it->second.c_str();
+    }
   }
-  return it->second.GetValue();
+  return nullptr;
+}
+
+std::vector<std::string> cmPropertyMap::GetKeys() const
+{
+  std::vector<std::string> keyList;
+  keyList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    keyList.push_back(item.first);
+  }
+  std::sort(keyList.begin(), keyList.end());
+  return keyList;
+}
+
+std::vector<std::pair<std::string, std::string>> cmPropertyMap::GetList() const
+{
+  using StringPair = std::pair<std::string, std::string>;
+  std::vector<StringPair> kvList;
+  kvList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    kvList.emplace_back(item.first, item.second);
+  }
+  std::sort(kvList.begin(), kvList.end(),
+            [](StringPair const& a, StringPair const& b) {
+              return a.first < b.first;
+            });
+  return kvList;
 }
diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h
index 5a05150..9aed349 100644
--- a/Source/cmPropertyMap.h
+++ b/Source/cmPropertyMap.h
@@ -5,25 +5,47 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmProperty.h"
-
-#include <map>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
-class cmPropertyMap : public std::map<std::string, cmProperty>
+/** \class cmPropertyMap
+ * \brief String property map.
+ */
+class cmPropertyMap
 {
 public:
-  cmProperty* GetOrCreateProperty(const std::string& name);
+  // -- General
 
-  std::vector<std::string> GetPropertyList() const;
+  //! Clear property list
+  void Clear();
 
+  // -- Properties
+
+  //! Set the property value
   void SetProperty(const std::string& name, const char* value);
 
+  //! Append to the property value
   void AppendProperty(const std::string& name, const char* value,
                       bool asString = false);
 
+  //! Get the property value
   const char* GetPropertyValue(const std::string& name) const;
+
+  //! Remove the property @a name from the map
+  void RemoveProperty(const std::string& name);
+
+  // -- Lists
+
+  //! Get a sorted list of property keys
+  std::vector<std::string> GetKeys() const;
+
+  //! Get a sorted by key list of property key,value pairs
+  std::vector<std::pair<std::string, std::string>> GetList() const;
+
+private:
+  std::unordered_map<std::string, std::string> Map_;
 };
 
 #endif
diff --git a/Source/cmQTWrapCPPCommand.cxx b/Source/cmQTWrapCPPCommand.cxx
index 9a764c6..cc4df8f 100644
--- a/Source/cmQTWrapCPPCommand.cxx
+++ b/Source/cmQTWrapCPPCommand.cxx
@@ -3,45 +3,41 @@
 #include "cmQTWrapCPPCommand.h"
 
 #include "cmCustomCommandLines.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <utility>
-
-class cmExecutionStatus;
-
-// cmQTWrapCPPCommand
-bool cmQTWrapCPPCommand::InitialPass(std::vector<std::string> const& args,
-                                     cmExecutionStatus&)
+bool cmQTWrapCPPCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Get the moc executable to run in the custom command.
-  std::string const& moc_exe =
-    this->Makefile->GetRequiredDefinition("QT_MOC_EXECUTABLE");
+  std::string const& moc_exe = mf.GetRequiredDefinition("QT_MOC_EXECUTABLE");
 
   // Get the variable holding the list of sources.
   std::string const& sourceList = args[1];
-  std::string sourceListValue = this->Makefile->GetSafeDefinition(sourceList);
+  std::string sourceListValue = mf.GetSafeDefinition(sourceList);
 
   // Create a rule for all sources listed.
   for (std::string const& arg : cmMakeRange(args).advance(2)) {
-    cmSourceFile* curr = this->Makefile->GetSource(arg);
+    cmSourceFile* curr = mf.GetSource(arg);
     // if we should wrap the class
     if (!(curr && curr->GetPropertyAsBool("WRAP_EXCLUDE"))) {
       // Compute the name of the file to generate.
       std::string srcName =
         cmSystemTools::GetFilenameWithoutLastExtension(arg);
-      std::string newName = this->Makefile->GetCurrentBinaryDirectory();
-      newName += "/moc_";
-      newName += srcName;
-      newName += ".cxx";
-      cmSourceFile* sf = this->Makefile->GetOrCreateSource(newName, true);
+      std::string newName =
+        cmStrCat(mf.GetCurrentBinaryDirectory(), "/moc_", srcName, ".cxx");
+      cmSourceFile* sf = mf.GetOrCreateSource(newName, true);
       if (curr) {
         sf->SetProperty("ABSTRACT", curr->GetProperty("ABSTRACT"));
       }
@@ -52,9 +48,9 @@
         hname = arg;
       } else {
         if (curr && curr->GetIsGenerated()) {
-          hname = this->Makefile->GetCurrentBinaryDirectory();
+          hname = mf.GetCurrentBinaryDirectory();
         } else {
-          hname = this->Makefile->GetCurrentSourceDirectory();
+          hname = mf.GetCurrentSourceDirectory();
         }
         hname += "/";
         hname += arg;
@@ -67,14 +63,8 @@
       sourceListValue += newName;
 
       // Create the custom command to generate the file.
-      cmCustomCommandLine commandLine;
-      commandLine.push_back(moc_exe);
-      commandLine.push_back("-o");
-      commandLine.push_back(newName);
-      commandLine.push_back(hname);
-
-      cmCustomCommandLines commandLines;
-      commandLines.push_back(std::move(commandLine));
+      cmCustomCommandLines commandLines =
+        cmMakeSingleCommandLine({ moc_exe, "-o", newName, hname });
 
       std::vector<std::string> depends;
       depends.push_back(moc_exe);
@@ -82,13 +72,13 @@
 
       std::string no_main_dependency;
       const char* no_working_dir = nullptr;
-      this->Makefile->AddCustomCommandToOutput(
-        newName, depends, no_main_dependency, commandLines, "Qt Wrapped File",
-        no_working_dir);
+      mf.AddCustomCommandToOutput(newName, depends, no_main_dependency,
+                                  commandLines, "Qt Wrapped File",
+                                  no_working_dir);
     }
   }
 
   // Store the final list of source files.
-  this->Makefile->AddDefinition(sourceList, sourceListValue.c_str());
+  mf.AddDefinition(sourceList, sourceListValue);
   return true;
 }
diff --git a/Source/cmQTWrapCPPCommand.h b/Source/cmQTWrapCPPCommand.h
index c1dcd54..75fa180 100644
--- a/Source/cmQTWrapCPPCommand.h
+++ b/Source/cmQTWrapCPPCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmQTWrapCPPCommand
- * \brief Create moc file rules for Qt classes
- *
- * cmQTWrapCPPCommand is used to create wrappers for Qt classes into
- * normal C++
- */
-class cmQTWrapCPPCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmQTWrapCPPCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmQTWrapCPPCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmQTWrapUICommand.cxx b/Source/cmQTWrapUICommand.cxx
index 2223e2d..66c0228 100644
--- a/Source/cmQTWrapUICommand.cxx
+++ b/Source/cmQTWrapUICommand.cxx
@@ -3,56 +3,47 @@
 #include "cmQTWrapUICommand.h"
 
 #include "cmCustomCommandLines.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#include <utility>
-
-class cmExecutionStatus;
-
-// cmQTWrapUICommand
-bool cmQTWrapUICommand::InitialPass(std::vector<std::string> const& args,
-                                    cmExecutionStatus&)
+bool cmQTWrapUICommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() < 4) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // Get the uic and moc executables to run in the custom commands.
-  std::string const& uic_exe =
-    this->Makefile->GetRequiredDefinition("QT_UIC_EXECUTABLE");
-  std::string const& moc_exe =
-    this->Makefile->GetRequiredDefinition("QT_MOC_EXECUTABLE");
+  std::string const& uic_exe = mf.GetRequiredDefinition("QT_UIC_EXECUTABLE");
+  std::string const& moc_exe = mf.GetRequiredDefinition("QT_MOC_EXECUTABLE");
 
   // Get the variable holding the list of sources.
   std::string const& headerList = args[1];
   std::string const& sourceList = args[2];
-  std::string headerListValue = this->Makefile->GetSafeDefinition(headerList);
-  std::string sourceListValue = this->Makefile->GetSafeDefinition(sourceList);
+  std::string headerListValue = mf.GetSafeDefinition(headerList);
+  std::string sourceListValue = mf.GetSafeDefinition(sourceList);
 
   // Create rules for all sources listed.
   for (std::string const& arg : cmMakeRange(args).advance(3)) {
-    cmSourceFile* curr = this->Makefile->GetSource(arg);
+    cmSourceFile* curr = mf.GetSource(arg);
     // if we should wrap the class
     if (!(curr && curr->GetPropertyAsBool("WRAP_EXCLUDE"))) {
       // Compute the name of the files to generate.
       std::string srcName =
         cmSystemTools::GetFilenameWithoutLastExtension(arg);
-      std::string hName = this->Makefile->GetCurrentBinaryDirectory();
-      hName += "/";
-      hName += srcName;
-      hName += ".h";
-      std::string cxxName = this->Makefile->GetCurrentBinaryDirectory();
-      cxxName += "/";
-      cxxName += srcName;
-      cxxName += ".cxx";
-      std::string mocName = this->Makefile->GetCurrentBinaryDirectory();
-      mocName += "/moc_";
-      mocName += srcName;
-      mocName += ".cxx";
+      std::string hName =
+        cmStrCat(mf.GetCurrentBinaryDirectory(), '/', srcName, ".h");
+      std::string cxxName =
+        cmStrCat(mf.GetCurrentBinaryDirectory(), '/', srcName, ".cxx");
+      std::string mocName =
+        cmStrCat(mf.GetCurrentBinaryDirectory(), "/moc_", srcName, ".cxx");
 
       // Compute the name of the ui file from which to generate others.
       std::string uiName;
@@ -60,9 +51,9 @@
         uiName = arg;
       } else {
         if (curr && curr->GetIsGenerated()) {
-          uiName = this->Makefile->GetCurrentBinaryDirectory();
+          uiName = mf.GetCurrentBinaryDirectory();
         } else {
-          uiName = this->Makefile->GetCurrentSourceDirectory();
+          uiName = mf.GetCurrentSourceDirectory();
         }
         uiName += "/";
         uiName += arg;
@@ -83,56 +74,34 @@
       sourceListValue += mocName;
 
       // set up .ui to .h and .cxx command
-      cmCustomCommandLine hCommand;
-      hCommand.push_back(uic_exe);
-      hCommand.push_back("-o");
-      hCommand.push_back(hName);
-      hCommand.push_back(uiName);
-      cmCustomCommandLines hCommandLines;
-      hCommandLines.push_back(std::move(hCommand));
-
-      cmCustomCommandLine cxxCommand;
-      cxxCommand.push_back(uic_exe);
-      cxxCommand.push_back("-impl");
-      cxxCommand.push_back(hName);
-      cxxCommand.push_back("-o");
-      cxxCommand.push_back(cxxName);
-      cxxCommand.push_back(uiName);
-      cmCustomCommandLines cxxCommandLines;
-      cxxCommandLines.push_back(std::move(cxxCommand));
-
-      cmCustomCommandLine mocCommand;
-      mocCommand.push_back(moc_exe);
-      mocCommand.push_back("-o");
-      mocCommand.push_back(mocName);
-      mocCommand.push_back(hName);
-      cmCustomCommandLines mocCommandLines;
-      mocCommandLines.push_back(std::move(mocCommand));
+      cmCustomCommandLines hCommandLines =
+        cmMakeSingleCommandLine({ uic_exe, "-o", hName, uiName });
+      cmCustomCommandLines cxxCommandLines = cmMakeSingleCommandLine(
+        { uic_exe, "-impl", hName, "-o", cxxName, uiName });
+      cmCustomCommandLines mocCommandLines =
+        cmMakeSingleCommandLine({ moc_exe, "-o", mocName, hName });
 
       std::vector<std::string> depends;
       depends.push_back(uiName);
       std::string no_main_dependency;
       const char* no_comment = nullptr;
       const char* no_working_dir = nullptr;
-      this->Makefile->AddCustomCommandToOutput(
-        hName, depends, no_main_dependency, hCommandLines, no_comment,
-        no_working_dir);
+      mf.AddCustomCommandToOutput(hName, depends, no_main_dependency,
+                                  hCommandLines, no_comment, no_working_dir);
 
       depends.push_back(hName);
-      this->Makefile->AddCustomCommandToOutput(
-        cxxName, depends, no_main_dependency, cxxCommandLines, no_comment,
-        no_working_dir);
+      mf.AddCustomCommandToOutput(cxxName, depends, no_main_dependency,
+                                  cxxCommandLines, no_comment, no_working_dir);
 
       depends.clear();
       depends.push_back(hName);
-      this->Makefile->AddCustomCommandToOutput(
-        mocName, depends, no_main_dependency, mocCommandLines, no_comment,
-        no_working_dir);
+      mf.AddCustomCommandToOutput(mocName, depends, no_main_dependency,
+                                  mocCommandLines, no_comment, no_working_dir);
     }
   }
 
   // Store the final list of source files and headers.
-  this->Makefile->AddDefinition(sourceList, sourceListValue.c_str());
-  this->Makefile->AddDefinition(headerList, headerListValue.c_str());
+  mf.AddDefinition(sourceList, sourceListValue);
+  mf.AddDefinition(headerList, headerListValue);
   return true;
 }
diff --git a/Source/cmQTWrapUICommand.h b/Source/cmQTWrapUICommand.h
index 15cab40..a17ef54 100644
--- a/Source/cmQTWrapUICommand.h
+++ b/Source/cmQTWrapUICommand.h
@@ -8,29 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmQTWrapUICommand
- * \brief Create .h and .cxx files rules for Qt user interfaces files
- *
- * cmQTWrapUICommand is used to create wrappers for Qt classes into normal C++
- */
-class cmQTWrapUICommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmQTWrapUICommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmQTWrapUICommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 3683edd..eb7c900 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -2,17 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGen.h"
 
-#include "cmAlgorithms.h"
-#include "cmDuration.h"
-#include "cmProcessOutput.h"
-#include "cmSystemTools.h"
+#include <algorithm>
+#include <array>
+#include <initializer_list>
+#include <sstream>
+#include <utility>
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
-#include <algorithm>
-#include <array>
-#include <sstream>
-#include <utility>
+#include "cmAlgorithms.h"
+#include "cmDuration.h"
+#include "cmProcessOutput.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 // - Static functions
 
@@ -20,10 +23,8 @@
 /// @arg valueOpts list of options that accept a value
 void MergeOptions(std::vector<std::string>& baseOpts,
                   std::vector<std::string> const& newOpts,
-                  std::vector<std::string> const& valueOpts, bool isQt5)
+                  std::initializer_list<cm::string_view> valueOpts, bool isQt5)
 {
-  typedef std::vector<std::string>::iterator Iter;
-  typedef std::vector<std::string>::const_iterator CIter;
   if (newOpts.empty()) {
     return;
   }
@@ -33,10 +34,10 @@
   }
 
   std::vector<std::string> extraOpts;
-  for (CIter fit = newOpts.begin(), fitEnd = newOpts.end(); fit != fitEnd;
+  for (auto fit = newOpts.begin(), fitEnd = newOpts.end(); fit != fitEnd;
        ++fit) {
     std::string const& newOpt = *fit;
-    Iter existIt = std::find(baseOpts.begin(), baseOpts.end(), newOpt);
+    auto existIt = std::find(baseOpts.begin(), baseOpts.end(), newOpt);
     if (existIt != baseOpts.end()) {
       if (newOpt.size() >= 2) {
         // Acquire the option name
@@ -52,11 +53,9 @@
           }
         }
         // Test if this is a value option and change the existing value
-        if (!optName.empty() &&
-            (std::find(valueOpts.begin(), valueOpts.end(), optName) !=
-             valueOpts.end())) {
-          const Iter existItNext(existIt + 1);
-          const CIter fitNext(fit + 1);
+        if (!optName.empty() && cmContains(valueOpts, optName)) {
+          const auto existItNext(existIt + 1);
+          const auto fitNext(fit + 1);
           if ((existItNext != baseOpts.end()) && (fitNext != fitEnd)) {
             *existItNext = *fitNext;
             ++fit;
@@ -74,104 +73,75 @@
 // - Class definitions
 
 unsigned int const cmQtAutoGen::ParallelMax = 64;
-std::string const cmQtAutoGen::ListSep = "<<<S>>>";
 
-std::string const& cmQtAutoGen::GeneratorName(GenT genType)
+cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
 {
-  static const std::string AutoGen("AutoGen");
-  static const std::string AutoMoc("AutoMoc");
-  static const std::string AutoUic("AutoUic");
-  static const std::string AutoRcc("AutoRcc");
-
   switch (genType) {
     case GenT::GEN:
-      return AutoGen;
+      return "AutoGen";
     case GenT::MOC:
-      return AutoMoc;
+      return "AutoMoc";
     case GenT::UIC:
-      return AutoUic;
+      return "AutoUic";
     case GenT::RCC:
-      return AutoRcc;
+      return "AutoRcc";
   }
-  return AutoGen;
+  return "AutoGen";
 }
 
-std::string const& cmQtAutoGen::GeneratorNameUpper(GenT genType)
+cm::string_view cmQtAutoGen::GeneratorNameUpper(GenT genType)
 {
-  static const std::string AUTOGEN("AUTOGEN");
-  static const std::string AUTOMOC("AUTOMOC");
-  static const std::string AUTOUIC("AUTOUIC");
-  static const std::string AUTORCC("AUTORCC");
-
   switch (genType) {
     case GenT::GEN:
-      return AUTOGEN;
+      return "AUTOGEN";
     case GenT::MOC:
-      return AUTOMOC;
+      return "AUTOMOC";
     case GenT::UIC:
-      return AUTOUIC;
+      return "AUTOUIC";
     case GenT::RCC:
-      return AUTORCC;
+      return "AUTORCC";
   }
-  return AUTOGEN;
+  return "AUTOGEN";
 }
 
 std::string cmQtAutoGen::Tools(bool moc, bool uic, bool rcc)
 {
-  std::string res;
-  std::vector<std::string> lst;
+  std::array<cm::string_view, 3> lst;
+  decltype(lst)::size_type num = 0;
   if (moc) {
-    lst.emplace_back("AUTOMOC");
+    lst.at(num++) = "AUTOMOC";
   }
   if (uic) {
-    lst.emplace_back("AUTOUIC");
+    lst.at(num++) = "AUTOUIC";
   }
   if (rcc) {
-    lst.emplace_back("AUTORCC");
+    lst.at(num++) = "AUTORCC";
   }
-  switch (lst.size()) {
+  switch (num) {
     case 1:
-      res += lst.at(0);
-      break;
+      return std::string(lst[0]);
     case 2:
-      res += lst.at(0);
-      res += " and ";
-      res += lst.at(1);
-      break;
+      return cmStrCat(lst[0], " and ", lst[1]);
     case 3:
-      res += lst.at(0);
-      res += ", ";
-      res += lst.at(1);
-      res += " and ";
-      res += lst.at(2);
-      break;
+      return cmStrCat(lst[0], ", ", lst[1], " and ", lst[2]);
     default:
       break;
   }
-  return res;
+  return std::string();
 }
 
-std::string cmQtAutoGen::Quoted(std::string const& text)
+std::string cmQtAutoGen::Quoted(cm::string_view text)
 {
-  const std::array<std::pair<const char*, const char*>, 9> replaces = {
-    { { "\\", "\\\\" },
-      { "\"", "\\\"" },
-      { "\a", "\\a" },
-      { "\b", "\\b" },
-      { "\f", "\\f" },
-      { "\n", "\\n" },
-      { "\r", "\\r" },
-      { "\t", "\\t" },
-      { "\v", "\\v" } }
-  };
+  static std::initializer_list<std::pair<const char*, const char*>> const
+    replacements = { { "\\", "\\\\" }, { "\"", "\\\"" }, { "\a", "\\a" },
+                     { "\b", "\\b" },  { "\f", "\\f" },  { "\n", "\\n" },
+                     { "\r", "\\r" },  { "\t", "\\t" },  { "\v", "\\v" } };
 
-  std::string res = text;
-  for (auto const& pair : replaces) {
+  std::string res(text);
+  for (auto const& pair : replacements) {
     cmSystemTools::ReplaceString(res, pair.first, pair.second);
   }
-  res = '"' + res;
-  res += '"';
-  return res;
+  return cmStrCat('"', res, '"');
 }
 
 std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
@@ -192,37 +162,50 @@
   return res;
 }
 
-std::string cmQtAutoGen::SubDirPrefix(std::string const& filename)
+std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename)
 {
-  std::string::size_type slash_pos = filename.rfind('/');
-  if (slash_pos == std::string::npos) {
-    return std::string();
+  auto slashPos = filename.rfind('/');
+  if (slashPos != cm::string_view::npos) {
+    filename.remove_prefix(slashPos + 1);
   }
-  return filename.substr(0, slash_pos + 1);
+  auto dotPos = filename.rfind('.');
+  return std::string(filename.substr(0, dotPos));
 }
 
-std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename,
-                                              std::string const& suffix)
+std::string cmQtAutoGen::ParentDir(cm::string_view filename)
 {
-  std::string res;
-  auto pos = filename.rfind('.');
-  if (pos != std::string::npos) {
-    const auto it_dot = filename.begin() + pos;
-    res.assign(filename.begin(), it_dot);
-    res.append(suffix);
-    res.append(it_dot, filename.end());
-  } else {
-    res = filename;
-    res.append(suffix);
+  auto slashPos = filename.rfind('/');
+  if (slashPos == cm::string_view::npos) {
+    return std::string();
   }
-  return res;
+  return std::string(filename.substr(0, slashPos));
+}
+
+std::string cmQtAutoGen::SubDirPrefix(cm::string_view filename)
+{
+  auto slashPos = filename.rfind('/');
+  if (slashPos == cm::string_view::npos) {
+    return std::string();
+  }
+  return std::string(filename.substr(0, slashPos + 1));
+}
+
+std::string cmQtAutoGen::AppendFilenameSuffix(cm::string_view filename,
+                                              cm::string_view suffix)
+{
+  auto dotPos = filename.rfind('.');
+  if (dotPos == cm::string_view::npos) {
+    return cmStrCat(filename, suffix);
+  }
+  return cmStrCat(filename.substr(0, dotPos), suffix,
+                  filename.substr(dotPos, filename.size() - dotPos));
 }
 
 void cmQtAutoGen::UicMergeOptions(std::vector<std::string>& baseOpts,
                                   std::vector<std::string> const& newOpts,
                                   bool isQt5)
 {
-  static std::vector<std::string> const valueOpts = {
+  static std::initializer_list<cm::string_view> const valueOpts = {
     "tr",      "translate", "postfix", "generator",
     "include", // Since Qt 5.3
     "g"
@@ -234,9 +217,9 @@
                                   std::vector<std::string> const& newOpts,
                                   bool isQt5)
 {
-  static std::vector<std::string> const valueOpts = { "name", "root",
-                                                      "compress",
-                                                      "threshold" };
+  static std::initializer_list<cm::string_view> const valueOpts = {
+    "name", "root", "compress", "threshold"
+  };
   MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
 }
 
@@ -293,9 +276,8 @@
 
         std::string::size_type pos = eline.find(searchString);
         if (pos == std::string::npos) {
-          error = "rcc lists unparsable output:\n";
-          error += cmQtAutoGen::Quoted(eline);
-          error += "\n";
+          error = cmStrCat("rcc lists unparsable output:\n",
+                           cmQtAutoGen::Quoted(eline), '\n');
           return false;
         }
         pos += searchString.length();
@@ -324,9 +306,8 @@
   error.clear();
 
   if (!cmSystemTools::FileExists(qrcFile, true)) {
-    error = "The resource file ";
-    error += Quoted(qrcFile);
-    error += " does not exist.";
+    error =
+      cmStrCat("The resource file ", Quoted(qrcFile), " does not exist.");
     return false;
   }
 
@@ -352,10 +333,8 @@
 
       // Log command
       if (verbose) {
-        std::string msg = "Running command:\n";
-        msg += QuotedCommand(cmd);
-        msg += '\n';
-        cmSystemTools::Stdout(msg);
+        cmSystemTools::Stdout(
+          cmStrCat("Running command:\n", QuotedCommand(cmd), '\n'));
       }
 
       result = cmSystemTools::RunSingleCommand(
@@ -363,16 +342,13 @@
         cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
     }
     if (!result || retVal) {
-      error = "The rcc list process failed for ";
-      error += Quoted(qrcFile);
-      error += "\n";
+      error =
+        cmStrCat("The rcc list process failed for ", Quoted(qrcFile), '\n');
       if (!rccStdOut.empty()) {
-        error += rccStdOut;
-        error += "\n";
+        error += cmStrCat(rccStdOut, '\n');
       }
       if (!rccStdErr.empty()) {
-        error += rccStdErr;
-        error += "\n";
+        error += cmStrCat(rccStdErr, '\n');
       }
       return false;
     }
@@ -391,9 +367,8 @@
           osst << ifs.rdbuf();
           qrcContents = osst.str();
         } else {
-          error = "The resource file ";
-          error += Quoted(qrcFile);
-          error += " is not readable\n";
+          error = cmStrCat("The resource file ", Quoted(qrcFile),
+                           " is not readable\n");
           return false;
         }
       }
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index 9c52129..a740ba3 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -5,17 +5,19 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 /** \class cmQtAutoGen
  * \brief Common base class for QtAutoGen classes
  */
 class cmQtAutoGen
 {
 public:
-  /// @brief Integer version
+  /** Integer version.  */
   struct IntegerVersion
   {
     unsigned int Major = 0;
@@ -41,6 +43,7 @@
     }
   };
 
+  /** Compiler features.  */
   class CompilerFeatures
   {
   public:
@@ -48,9 +51,9 @@
     std::string HelpOutput;
     std::vector<std::string> ListOptions;
   };
-  typedef std::shared_ptr<CompilerFeatures> CompilerFeaturesHandle;
+  using CompilerFeaturesHandle = std::shared_ptr<CompilerFeatures>;
 
-  /// @brief AutoGen generator type
+  /** AutoGen generator type.  */
   enum class GenT
   {
     GEN, // AUTOGEN
@@ -59,31 +62,35 @@
     RCC  // AUTORCC
   };
 
-  /// @brief Nested lists separator
-  static std::string const ListSep;
   /// @brief Maximum number of parallel threads/processes in a generator
   static unsigned int const ParallelMax;
 
 public:
   /// @brief Returns the generator name
-  static std::string const& GeneratorName(GenT genType);
+  static cm::string_view GeneratorName(GenT genType);
   /// @brief Returns the generator name in upper case
-  static std::string const& GeneratorNameUpper(GenT genType);
+  static cm::string_view GeneratorNameUpper(GenT genType);
 
   /// @brief Returns a string with the requested tool names
   static std::string Tools(bool moc, bool uic, bool rcc);
 
   /// @brief Returns the string escaped and enclosed in quotes
-  static std::string Quoted(std::string const& text);
+  static std::string Quoted(cm::string_view text);
 
   static std::string QuotedCommand(std::vector<std::string> const& command);
 
+  /// @brief Returns the file name without path and extension (thread safe)
+  static std::string FileNameWithoutLastExtension(cm::string_view filename);
+
+  /// @brief Returns the parent directory of the file (thread safe)
+  static std::string ParentDir(cm::string_view filename);
+
   /// @brief Returns the parent directory of the file with a "/" suffix
-  static std::string SubDirPrefix(std::string const& filename);
+  static std::string SubDirPrefix(cm::string_view filename);
 
   /// @brief Appends the suffix to the filename before the last dot
-  static std::string AppendFilenameSuffix(std::string const& filename,
-                                          std::string const& suffix);
+  static std::string AppendFilenameSuffix(cm::string_view filename,
+                                          cm::string_view suffix);
 
   /// @brief Merges newOpts into baseOpts
   static void UicMergeOptions(std::vector<std::string>& baseOpts,
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index ef8a56b..ef6b886 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -1,25 +1,27 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenGlobalInitializer.h"
-#include "cmQtAutoGen.h"
-#include "cmQtAutoGenInitializer.h"
 
-#include "cmAlgorithms.h"
+#include <utility>
+
+#include <cm/memory>
+
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmDuration.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmProcessOutput.h"
+#include "cmQtAutoGen.h"
+#include "cmQtAutoGenInitializer.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-#include <memory>
-#include <utility>
-
 cmQtAutoGenGlobalInitializer::Keywords::Keywords()
   : AUTOMOC("AUTOMOC")
   , AUTOUIC("AUTOUIC")
@@ -48,8 +50,7 @@
     {
       cmMakefile* makefile = localGen->GetMakefile();
       // Detect global autogen target name
-      if (cmSystemTools::IsOn(
-            makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET"))) {
+      if (cmIsOn(makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET"))) {
         std::string targetName =
           makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET_NAME");
         if (targetName.empty()) {
@@ -60,8 +61,7 @@
       }
 
       // Detect global autorcc target name
-      if (cmSystemTools::IsOn(
-            makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET"))) {
+      if (cmIsOn(makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET"))) {
         std::string targetName =
           makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET_NAME");
         if (targetName.empty()) {
@@ -119,23 +119,17 @@
         bool const uicDisabled = (uic && !uicAvailable);
         bool const rccDisabled = (rcc && !rccAvailable);
         if (mocDisabled || uicDisabled || rccDisabled) {
-          std::string msg = "AUTOGEN: No valid Qt version found for target ";
-          msg += target->GetName();
-          msg += ". ";
-          msg += cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled);
-          msg += " disabled.  Consider adding:\n";
-          {
-            std::string version = (qtVersion.second == 0)
-              ? std::string("<QTVERSION>")
-              : std::to_string(qtVersion.second);
-            std::string comp = uicDisabled ? "Widgets" : "Core";
-            msg += "  find_package(Qt";
-            msg += version;
-            msg += " COMPONENTS ";
-            msg += comp;
-            msg += ")\n";
-          }
-          msg += "to your CMakeLists.txt file.";
+          cmAlphaNum version = (qtVersion.second == 0)
+            ? cmAlphaNum("<QTVERSION>")
+            : cmAlphaNum(qtVersion.second);
+          cmAlphaNum component = uicDisabled ? "Widgets" : "Core";
+
+          std::string const msg = cmStrCat(
+            "AUTOGEN: No valid Qt version found for target ",
+            target->GetName(), ".  ",
+            cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled),
+            " disabled.  Consider adding:\n", "  find_package(Qt", version,
+            " COMPONENTS ", component, ")\n", "to your CMakeLists.txt file.");
           target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
         }
         if (mocIsValid || uicIsValid || rccIsValid) {
@@ -161,7 +155,7 @@
 
     // Create utility target
     cmTarget* target = makefile->AddUtilityCommand(
-      name, cmMakefile::TargetOrigin::Generator, true,
+      name, cmCommandOrigin::Generator, true,
       makefile->GetHomeOutputDirectory().c_str() /*work dir*/,
       std::vector<std::string>() /*output*/,
       std::vector<std::string>() /*depends*/, cmCustomCommandLines(), false,
@@ -218,11 +212,8 @@
 
   // Check if the executable exists
   if (!cmSystemTools::FileExists(executable, true)) {
-    error = "The \"";
-    error += generator;
-    error += "\" executable ";
-    error += cmQtAutoGen::Quoted(executable);
-    error += " does not exist.";
+    error = cmStrCat("The \"", generator, "\" executable ",
+                     cmQtAutoGen::Quoted(executable), " does not exist.");
     return cmQtAutoGen::CompilerFeaturesHandle();
   }
 
@@ -238,15 +229,10 @@
       command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
       cmDuration::zero(), cmProcessOutput::Auto);
     if (!runResult) {
-      error = "Test run of \"";
-      error += generator;
-      error += "\" executable ";
-      error += cmQtAutoGen::Quoted(executable) + " failed.\n";
-      error += cmQtAutoGen::QuotedCommand(command);
-      error += "\n";
-      error += stdOut;
-      error += "\n";
-      error += stdErr;
+      error = cmStrCat("Test run of \"", generator, "\" executable ",
+                       cmQtAutoGen::Quoted(executable), " failed.\n",
+                       cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
+                       stdErr);
       return cmQtAutoGen::CompilerFeaturesHandle();
     }
   }
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index d56153a..806725a 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -5,14 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmQtAutoGen.h"
-
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include "cmQtAutoGen.h"
+
 class cmLocalGenerator;
 class cmQtAutoGenInitializer;
 
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 9985f93..a20f106 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -1,13 +1,32 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenInitializer.h"
-#include "cmQtAutoGen.h"
-#include "cmQtAutoGenGlobalInitializer.h"
+
+#include <cstddef>
+#include <deque>
+#include <initializer_list>
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include <cm/algorithm>
+#include <cm/iterator>
+#include <cm/memory>
+
+#include "cmsys/SystemInformation.hxx"
+
+#include "cm_jsoncpp_value.h"
+#include "cm_jsoncpp_writer.h"
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
-#include "cmFilePathChecksum.h"
+#include "cmCustomCommandTypes.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -16,44 +35,36 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmOutputConverter.h"
 #include "cmPolicies.h"
+#include "cmQtAutoGen.h"
+#include "cmQtAutoGenGlobalInitializer.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmSourceGroup.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmake.h"
-#include "cmsys/SystemInformation.hxx"
 
-#include <algorithm>
-#include <array>
-#include <deque>
-#include <map>
-#include <set>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
+namespace {
 
-static std::size_t GetParallelCPUCount()
+unsigned int GetParallelCPUCount()
 {
-  static std::size_t count = 0;
+  static unsigned int count = 0;
   // Detect only on the first call
   if (count == 0) {
     cmsys::SystemInformation info;
     info.RunCPUCheck();
-    count = info.GetNumberOfPhysicalCPU();
-    count = std::max<std::size_t>(count, 1);
-    count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
+    count =
+      cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax);
   }
   return count;
 }
 
-static std::string FileProjectRelativePath(cmMakefile* makefile,
-                                           std::string const& fileName)
+std::string FileProjectRelativePath(cmMakefile* makefile,
+                                    std::string const& fileName)
 {
   std::string res;
   {
@@ -77,9 +88,9 @@
  * recursive STATIC_LIBRARY dependencies depends on targetOrigin
  * (STATIC_LIBRARY cycle).
  */
-static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
-                               cmGeneratorTarget const* targetDepend,
-                               std::string const& config)
+bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
+                        cmGeneratorTarget const* targetDepend,
+                        std::string const& config)
 {
   bool cycle = false;
   if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
@@ -117,109 +128,180 @@
   return cycle;
 }
 
-cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename)
+/** Sanitizes file search paths.  */
+class SearchPathSanitizer
 {
-  Ofs_.SetCopyIfDifferent(true);
-  Ofs_.Open(filename, false, true);
-}
+public:
+  SearchPathSanitizer(cmMakefile* makefile)
+    : SourcePath_(makefile->GetCurrentSourceDirectory())
+  {
+  }
+  std::vector<std::string> operator()(
+    std::vector<std::string> const& paths) const;
 
-template <class IT>
-std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin,
-                                                         IT it_end)
+private:
+  std::string SourcePath_;
+};
+
+std::vector<std::string> SearchPathSanitizer::operator()(
+  std::vector<std::string> const& paths) const
 {
-  std::string res;
-  for (IT it = it_begin; it != it_end; ++it) {
-    if (it != it_begin) {
-      res += ';';
+  std::vector<std::string> res;
+  res.reserve(paths.size());
+  for (std::string const& srcPath : paths) {
+    // Collapse relative paths
+    std::string path = cmSystemTools::CollapseFullPath(srcPath, SourcePath_);
+    // Remove suffix slashes
+    while (cmHasSuffix(path, '/')) {
+      path.pop_back();
     }
-    for (const char* c = it->c_str(); *c; ++c) {
-      if (*c == '"') {
-        // Escape the double quote to avoid ending the argument.
-        res += "\\\"";
-      } else if (*c == '$') {
-        // Escape the dollar to avoid expanding variables.
-        res += "\\$";
-      } else if (*c == '\\') {
-        // Escape the backslash to avoid other escapes.
-        res += "\\\\";
-      } else if (*c == ';') {
-        // Escape the semicolon to avoid list expansion.
-        res += "\\;";
-      } else {
-        // Other characters will be parsed correctly.
-        res += *c;
-      }
+    // Accept only non empty paths
+    if (!path.empty()) {
+      res.emplace_back(std::move(path));
     }
   }
   return res;
 }
 
-std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey(
-  const char* key, std::string const& config)
+/** @brief Writes a CMake info file.  */
+class InfoWriter
 {
-  std::string ckey = key;
-  ckey += '_';
-  ckey += config;
-  return ckey;
-}
-
-void cmQtAutoGenInitializer::InfoWriter::Write(const char* key,
-                                               std::string const& value)
-{
-  Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
-       << ")\n";
-};
-
-void cmQtAutoGenInitializer::InfoWriter::WriteUInt(const char* key,
-                                                   unsigned int value)
-{
-  Ofs_ << "set(" << key << " " << value << ")\n";
-};
-
-template <class C>
-void cmQtAutoGenInitializer::InfoWriter::WriteStrings(const char* key,
-                                                      C const& container)
-{
-  Ofs_ << "set(" << key << " \""
-       << ListJoin(container.begin(), container.end()) << "\")\n";
-}
-
-void cmQtAutoGenInitializer::InfoWriter::WriteConfig(
-  const char* key, std::map<std::string, std::string> const& map)
-{
-  for (auto const& item : map) {
-    Write(ConfigKey(key, item.first).c_str(), item.second);
+public:
+  // -- Single value
+  void Set(std::string const& key, std::string const& value)
+  {
+    Value_[key] = value;
   }
+  void SetConfig(std::string const& key,
+                 cmQtAutoGenInitializer::ConfigString const& cfgStr);
+  void SetBool(std::string const& key, bool value) { Value_[key] = value; }
+  void SetUInt(std::string const& key, unsigned int value)
+  {
+    Value_[key] = value;
+  }
+
+  // -- Array utility
+  template <typename CONT>
+  static bool MakeArray(Json::Value& jval, CONT const& container);
+
+  template <typename CONT>
+  static void MakeStringArray(Json::Value& jval, CONT const& container);
+
+  // -- Array value
+  template <typename CONT>
+  void SetArray(std::string const& key, CONT const& container);
+  template <typename CONT>
+  void SetConfigArray(
+    std::string const& key,
+    cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr);
+
+  // -- Array of arrays
+  template <typename CONT, typename FUNC>
+  void SetArrayArray(std::string const& key, CONT const& container, FUNC func);
+
+  // -- Save to json file
+  bool Save(std::string const& filename);
+
+private:
+  Json::Value Value_;
 };
 
-template <class C>
-void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings(
-  const char* key, std::map<std::string, C> const& map)
+void InfoWriter::SetConfig(std::string const& key,
+                           cmQtAutoGenInitializer::ConfigString const& cfgStr)
 {
-  for (auto const& item : map) {
-    WriteStrings(ConfigKey(key, item.first).c_str(), item.second);
+  Set(key, cfgStr.Default);
+  for (auto const& item : cfgStr.Config) {
+    Set(cmStrCat(key, '_', item.first), item.second);
   }
 }
 
-void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists(
-  const char* key, std::vector<std::vector<std::string>> const& lists)
+template <typename CONT>
+bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container)
 {
-  std::vector<std::string> seplist;
-  for (const std::vector<std::string>& list : lists) {
-    std::string blist = "{";
-    blist += ListJoin(list.begin(), list.end());
-    blist += "}";
-    seplist.push_back(std::move(blist));
+  jval = Json::arrayValue;
+  std::size_t const listSize = cm::size(container);
+  if (listSize == 0) {
+    return false;
   }
-  Write(key, cmJoin(seplist, cmQtAutoGen::ListSep));
-};
+  jval.resize(static_cast<unsigned int>(listSize));
+  return true;
+}
+
+template <typename CONT>
+void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container)
+{
+  if (MakeArray(jval, container)) {
+    Json::ArrayIndex ii = 0;
+    for (std::string const& item : container) {
+      jval[ii++] = item;
+    }
+  }
+}
+
+template <typename CONT>
+void InfoWriter::SetArray(std::string const& key, CONT const& container)
+{
+  MakeStringArray(Value_[key], container);
+}
+
+template <typename CONT, typename FUNC>
+void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
+                               FUNC func)
+{
+  Json::Value& jval = Value_[key];
+  if (MakeArray(jval, container)) {
+    Json::ArrayIndex ii = 0;
+    for (auto const& citem : container) {
+      Json::Value& aval = jval[ii++];
+      aval = Json::arrayValue;
+      func(aval, citem);
+    }
+  }
+}
+
+template <typename CONT>
+void InfoWriter::SetConfigArray(
+  std::string const& key,
+  cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr)
+{
+  SetArray(key, cfgStr.Default);
+  for (auto const& item : cfgStr.Config) {
+    SetArray(cmStrCat(key, '_', item.first), item.second);
+  }
+}
+
+bool InfoWriter::Save(std::string const& filename)
+{
+  cmGeneratedFileStream fileStream;
+  fileStream.SetCopyIfDifferent(true);
+  fileStream.Open(filename, false, true);
+  if (!fileStream) {
+    return false;
+  }
+
+  Json::StyledStreamWriter jsonWriter;
+  try {
+    jsonWriter.write(fileStream, Value_);
+  } catch (...) {
+    return false;
+  }
+
+  return fileStream.Close();
+}
+
+} // End of unnamed namespace
 
 cmQtAutoGenInitializer::cmQtAutoGenInitializer(
-  cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target,
-  IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled,
-  bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget)
+  cmQtAutoGenGlobalInitializer* globalInitializer,
+  cmGeneratorTarget* genTarget, IntegerVersion const& qtVersion,
+  bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget,
+  bool globalAutoRccTarget)
   : GlobalInitializer(globalInitializer)
-  , Target(target)
+  , GenTarget(genTarget)
+  , GlobalGen(genTarget->GetGlobalGenerator())
+  , LocalGen(genTarget->GetLocalGenerator())
+  , Makefile(genTarget->Makefile)
+  , PathCheckSum(genTarget->Makefile)
   , QtVersion(qtVersion)
 {
   AutogenTarget.GlobalTarget = globalAutogenTarget;
@@ -231,38 +313,42 @@
 
 bool cmQtAutoGenInitializer::InitCustomTargets()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
-  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
-
   // Configurations
-  this->MultiConfig = globalGen->IsMultiConfig();
-  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
+  this->MultiConfig = this->GlobalGen->IsMultiConfig();
+  this->ConfigDefault = this->Makefile->GetConfigurations(this->ConfigsList);
   if (this->ConfigsList.empty()) {
     this->ConfigsList.push_back(this->ConfigDefault);
   }
 
   // Verbosity
-  this->Verbosity = makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
-  if (!this->Verbosity.empty()) {
-    unsigned long iVerb = 0;
-    if (!cmSystemTools::StringToULong(this->Verbosity.c_str(), &iVerb)) {
-      // Non numeric verbosity
-      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
+  {
+    std::string def =
+      this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
+    if (!def.empty()) {
+      unsigned long iVerb = 0;
+      if (cmStrToULong(def, &iVerb)) {
+        // Numeric verbosity
+        this->Verbosity = static_cast<unsigned int>(iVerb);
+      } else {
+        // Non numeric verbosity
+        if (cmIsOn(def)) {
+          this->Verbosity = 1;
+        }
+      }
     }
   }
 
   // Targets FOLDER
   {
     const char* folder =
-      makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
+      this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
     if (folder == nullptr) {
-      folder =
-        makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
+      folder = this->Makefile->GetState()->GetGlobalProperty(
+        "AUTOGEN_TARGETS_FOLDER");
     }
     // Inherit FOLDER property from target (#13688)
     if (folder == nullptr) {
-      folder = this->Target->GetProperty("FOLDER");
+      folder = this->GenTarget->GetProperty("FOLDER");
     }
     if (folder != nullptr) {
       this->TargetsFolder = folder;
@@ -272,7 +358,7 @@
   // Check status of policy CMP0071
   {
     cmPolicies::PolicyStatus const CMP0071_status =
-      makefile->GetPolicyStatus(cmPolicies::CMP0071);
+      this->Makefile->GetPolicyStatus(cmPolicies::CMP0071);
     switch (CMP0071_status) {
       case cmPolicies::WARN:
         this->CMP0071Warn = true;
@@ -293,24 +379,18 @@
   {
     // Collapsed current binary directory
     std::string const cbd = cmSystemTools::CollapseFullPath(
-      std::string(), makefile->GetCurrentBinaryDirectory());
+      std::string(), this->Makefile->GetCurrentBinaryDirectory());
 
     // Info directory
-    this->Dir.Info = cbd;
-    this->Dir.Info += "/CMakeFiles";
-    this->Dir.Info += '/';
-    this->Dir.Info += this->Target->GetName();
-    this->Dir.Info += "_autogen";
-    this->Dir.Info += ".dir";
+    this->Dir.Info = cmStrCat(cbd, "/CMakeFiles/", this->GenTarget->GetName(),
+                              "_autogen.dir");
     cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);
 
     // Build directory
-    this->Dir.Build = this->Target->GetSafeProperty("AUTOGEN_BUILD_DIR");
+    this->Dir.Build = this->GenTarget->GetSafeProperty("AUTOGEN_BUILD_DIR");
     if (this->Dir.Build.empty()) {
-      this->Dir.Build = cbd;
-      this->Dir.Build += '/';
-      this->Dir.Build += this->Target->GetName();
-      this->Dir.Build += "_autogen";
+      this->Dir.Build =
+        cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen");
     }
     cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
     // Cleanup build directory
@@ -321,19 +401,11 @@
     cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
 
     // Include directory
-    this->Dir.Include = this->Dir.Build;
-    this->Dir.Include += "/include";
+    ConfigFileNames(this->Dir.Include, cmStrCat(this->Dir.Build, "/include"),
+                    "");
+    this->Dir.IncludeGenExp = this->Dir.Include.Default;
     if (this->MultiConfig) {
-      this->Dir.Include += "_$<CONFIG>";
-    }
-    // Per config include directories
-    if (this->MultiConfig) {
-      for (std::string const& cfg : this->ConfigsList) {
-        std::string& dir = this->Dir.ConfigInclude[cfg];
-        dir = this->Dir.Build;
-        dir += "/include_";
-        dir += cfg;
-      }
+      this->Dir.IncludeGenExp += "_$<CONFIG>";
     }
   }
 
@@ -350,55 +422,48 @@
     }
 
     // Autogen target name
-    this->AutogenTarget.Name = this->Target->GetName();
-    this->AutogenTarget.Name += "_autogen";
+    this->AutogenTarget.Name =
+      cmStrCat(this->GenTarget->GetName(), "_autogen");
 
     // Autogen target parallel processing
-    this->AutogenTarget.Parallel =
-      this->Target->GetSafeProperty("AUTOGEN_PARALLEL");
-    if (this->AutogenTarget.Parallel.empty() ||
-        (this->AutogenTarget.Parallel == "AUTO")) {
-      // Autodetect number of CPUs
-      this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount());
+    {
+      std::string prop = this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
+      if (prop.empty() || (prop == "AUTO")) {
+        // Autodetect number of CPUs
+        this->AutogenTarget.Parallel = GetParallelCPUCount();
+      } else {
+        this->AutogenTarget.Parallel = 1;
+      }
     }
 
     // Autogen target info and settings files
     {
-      this->AutogenTarget.InfoFile = this->Dir.Info;
-      this->AutogenTarget.InfoFile += "/AutogenInfo.cmake";
+      // Info file
+      this->AutogenTarget.InfoFile =
+        cmStrCat(this->Dir.Info, "/AutogenInfo.json");
 
-      this->AutogenTarget.SettingsFile = this->Dir.Info;
-      this->AutogenTarget.SettingsFile += "/AutogenOldSettings.txt";
+      // Used settings file
+      ConfigFileNames(this->AutogenTarget.SettingsFile,
+                      cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
+      ConfigFileClean(this->AutogenTarget.SettingsFile);
 
-      if (this->MultiConfig) {
-        for (std::string const& cfg : this->ConfigsList) {
-          std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg];
-          filename =
-            AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg);
-          this->AddCleanFile(filename);
-        }
-      } else {
-        this->AddCleanFile(this->AutogenTarget.SettingsFile);
-      }
-
-      this->AutogenTarget.ParseCacheFile = this->Dir.Info;
-      this->AutogenTarget.ParseCacheFile += "/ParseCache.txt";
-      this->AddCleanFile(this->AutogenTarget.ParseCacheFile);
+      // Parse cache file
+      ConfigFileNames(this->AutogenTarget.ParseCacheFile,
+                      cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
+      ConfigFileClean(this->AutogenTarget.ParseCacheFile);
     }
 
     // Autogen target: Compute user defined dependencies
     {
       this->AutogenTarget.DependOrigin =
-        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");
+        this->GenTarget->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");
 
       std::string const deps =
-        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
+        this->GenTarget->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
       if (!deps.empty()) {
-        std::vector<std::string> extraDeps;
-        cmSystemTools::ExpandListArgument(deps, extraDeps);
-        for (std::string const& depName : extraDeps) {
+        for (std::string const& depName : cmExpandedList(deps)) {
           // Allow target and file dependencies
-          auto* depTarget = makefile->FindTargetToUse(depName);
+          auto* depTarget = this->Makefile->FindTargetToUse(depName);
           if (depTarget != nullptr) {
             this->AutogenTarget.DependTargets.insert(depTarget);
           } else {
@@ -408,16 +473,47 @@
       }
     }
 
-    // CMAKE_AUTOMOC_RELAXED_MODE deprecation warning
     if (this->Moc.Enabled) {
-      if (cmSystemTools::IsOn(
-            makefile->GetDefinition("CMAKE_AUTOMOC_RELAXED_MODE"))) {
-        std::string msg = "AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
-                          "deprecated an will be removed in the future.  ";
-        msg += "Consider disabling it and converting the target ";
-        msg += this->Target->GetName();
-        msg += " to regular mode.";
-        makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
+      // Path prefix
+      if (cmIsOn(this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"))) {
+        this->Moc.PathPrefix = true;
+      }
+
+      // CMAKE_AUTOMOC_RELAXED_MODE
+      if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) {
+        this->Moc.RelaxedMode = true;
+        this->Makefile->IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
+                   "deprecated an will be removed in the future.  Consider "
+                   "disabling it and converting the target ",
+                   this->GenTarget->GetName(), " to regular mode."));
+      }
+
+      // Options
+      cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"),
+                   this->Moc.Options);
+      // Filters
+      cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
+                   this->Moc.MacroNames);
+      {
+        auto filterList = cmExpandedList(
+          this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
+        if ((filterList.size() % 2) != 0) {
+          cmSystemTools::Error(
+            cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ",
+                     filterList.size(), " is not a multiple of 2."));
+          return false;
+        }
+        this->Moc.DependFilters.reserve(1 + (filterList.size() / 2));
+        this->Moc.DependFilters.emplace_back(
+          "Q_PLUGIN_METADATA",
+          "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+          "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+        for (std::size_t ii = 0; ii != filterList.size(); ii += 2) {
+          this->Moc.DependFilters.emplace_back(filterList[ii],
+                                               filterList[ii + 1]);
+        }
       }
     }
   }
@@ -429,7 +525,7 @@
 
   // Add autogen include directory to the origin target INCLUDE_DIRECTORIES
   if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) {
-    this->Target->AddIncludeDirectory(this->Dir.Include, true);
+    this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true);
   }
 
   // Scan files
@@ -452,54 +548,57 @@
 
 bool cmQtAutoGenInitializer::InitMoc()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
-
   // Mocs compilation file
-  this->Moc.MocsCompilation = this->Dir.Build;
-  this->Moc.MocsCompilation += "/mocs_compilation.cpp";
+  this->Moc.CompilationFile =
+    cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
 
-  // Moc predefs command
-  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
+  // Moc predefs
+  if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
       (this->QtVersion >= IntegerVersion(5, 8))) {
-    this->Moc.PredefsCmd =
-      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
+    // Command
+    cmExpandList(this->Makefile->GetSafeDefinition(
+                   "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"),
+                 this->Moc.PredefsCmd);
+    // Header
+    if (!this->Moc.PredefsCmd.empty()) {
+      ConfigFileNames(this->Moc.PredefsFile,
+                      cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
+    }
   }
 
   // Moc includes
   {
-    bool const appendImplicit = (this->QtVersion.Major >= 5);
-    auto GetIncludeDirs =
-      [this, localGen,
-       appendImplicit](std::string const& cfg) -> std::vector<std::string> {
+    SearchPathSanitizer sanitizer(this->Makefile);
+    auto getDirs =
+      [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
       // Get the include dirs for this target, without stripping the implicit
-      // include dirs off, see
-      // https://gitlab.kitware.com/cmake/cmake/issues/13667
+      // include dirs off, see issue #13667.
       std::vector<std::string> dirs;
-      localGen->GetIncludeDirectoriesImplicit(dirs, this->Target, "CXX", cfg,
-                                              false, appendImplicit);
-      return dirs;
+      bool const appendImplicit = (this->QtVersion.Major >= 5);
+      this->LocalGen->GetIncludeDirectoriesImplicit(
+        dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
+      return sanitizer(dirs);
     };
 
     // Default configuration include directories
-    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
+    this->Moc.Includes.Default = getDirs(this->ConfigDefault);
     // Other configuration settings
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::vector<std::string> dirs = GetIncludeDirs(cfg);
-        if (dirs != this->Moc.Includes) {
-          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
+        std::vector<std::string> dirs = getDirs(cfg);
+        if (dirs == this->Moc.Includes.Default) {
+          continue;
         }
+        this->Moc.Includes.Config[cfg] = std::move(dirs);
       }
     }
   }
 
   // Moc compile definitions
   {
-    auto GetCompileDefinitions =
-      [this, localGen](std::string const& cfg) -> std::set<std::string> {
+    auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
       std::set<std::string> defines;
-      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
+      this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines);
 #ifdef _WIN32
       if (this->Moc.PredefsCmd.empty()) {
         // Add WIN32 definition if we don't have a moc_predefs.h
@@ -510,14 +609,15 @@
     };
 
     // Default configuration defines
-    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
+    this->Moc.Defines.Default = getDefs(this->ConfigDefault);
     // Other configuration defines
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::set<std::string> defines = GetCompileDefinitions(cfg);
-        if (defines != this->Moc.Defines) {
-          this->Moc.ConfigDefines[cfg] = std::move(defines);
+        std::set<std::string> defines = getDefs(cfg);
+        if (defines == this->Moc.Defines.Default) {
+          continue;
         }
+        this->Moc.Defines.Config[cfg] = std::move(defines);
       }
     }
   }
@@ -539,39 +639,33 @@
 
 bool cmQtAutoGenInitializer::InitUic()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-
   // Uic search paths
   {
     std::string const usp =
-      this->Target->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
+      this->GenTarget->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
     if (!usp.empty()) {
-      cmSystemTools::ExpandListArgument(usp, this->Uic.SearchPaths);
-      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
-      for (std::string& path : this->Uic.SearchPaths) {
-        path = cmSystemTools::CollapseFullPath(path, srcDir);
-      }
+      this->Uic.SearchPaths =
+        SearchPathSanitizer(this->Makefile)(cmExpandedList(usp));
     }
   }
   // Uic target options
   {
-    auto UicGetOpts =
-      [this](std::string const& cfg) -> std::vector<std::string> {
+    auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> {
       std::vector<std::string> opts;
-      this->Target->GetAutoUicOptions(opts, cfg);
+      this->GenTarget->GetAutoUicOptions(opts, cfg);
       return opts;
     };
 
-    // Default settings
-    this->Uic.Options = UicGetOpts(this->ConfigDefault);
-
-    // Configuration specific settings
+    // Default options
+    this->Uic.Options.Default = getOpts(this->ConfigDefault);
+    // Configuration specific options
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::vector<std::string> options = UicGetOpts(cfg);
-        if (options != this->Uic.Options) {
-          this->Uic.ConfigOptions[cfg] = std::move(options);
+        std::vector<std::string> options = getOpts(cfg);
+        if (options == this->Uic.Options.Default) {
+          continue;
         }
+        this->Uic.Options.Config[cfg] = std::move(options);
       }
     }
   }
@@ -619,13 +713,13 @@
 
 bool cmQtAutoGenInitializer::InitScanFiles()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
+  cmake const* cm = this->Makefile->GetCMakeInstance();
   auto const& kw = this->GlobalInitializer->kw();
 
   auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath,
                                 bool muIt) -> MUFileHandle {
     MUFileHandle muf = cm::make_unique<MUFile>();
-    muf->RealPath = cmSystemTools::GetRealPath(fullPath);
+    muf->FullPath = fullPath;
     muf->SF = sf;
     muf->Generated = sf->GetIsGenerated();
     bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
@@ -655,39 +749,35 @@
   {
     // Scan through target files
     std::vector<cmSourceFile*> srcFiles;
-    this->Target->GetConfigCommonSourceFiles(srcFiles);
+    this->GenTarget->GetConfigCommonSourceFiles(srcFiles);
     for (cmSourceFile* sf : srcFiles) {
-      // sf->GetExtension() is only valid after sf->GetFullPath() ...
+      // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
       // Since we're iterating over source files that might be not in the
       // target we need to check for path errors (not existing files).
       std::string pathError;
-      std::string const& fullPath = sf->GetFullPath(&pathError);
+      std::string const& fullPath = sf->ResolveFullPath(&pathError);
       if (!pathError.empty() || fullPath.empty()) {
         continue;
       }
-      std::string const& ext = sf->GetExtension();
+      std::string const& extLower =
+        cmSystemTools::LowerCase(sf->GetExtension());
 
       // Register files that will be scanned by moc or uic
       if (this->MocOrUicEnabled()) {
-        switch (cmSystemTools::GetFileFormat(ext)) {
-          case cmSystemTools::HEADER_FILE_FORMAT:
-            addMUFile(makeMUFile(sf, fullPath, true), true);
-            break;
-          case cmSystemTools::CXX_FILE_FORMAT:
-            addMUFile(makeMUFile(sf, fullPath, true), false);
-            break;
-          default:
-            break;
+        if (cm->IsHeaderExtension(extLower)) {
+          addMUFile(makeMUFile(sf, fullPath, true), true);
+        } else if (cm->IsSourceExtension(extLower)) {
+          addMUFile(makeMUFile(sf, fullPath, true), false);
         }
       }
 
       // Register rcc enabled files
       if (this->Rcc.Enabled) {
-        if ((ext == kw.qrc) && !sf->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
+        if ((extLower == kw.qrc) && !sf->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
             !sf->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
           // Register qrc file
           Qrc qrc;
-          qrc.QrcFile = cmSystemTools::GetRealPath(fullPath);
+          qrc.QrcFile = fullPath;
           qrc.QrcName =
             cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
           qrc.Generated = sf->GetIsGenerated();
@@ -695,7 +785,7 @@
           {
             std::string const opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS);
             if (!opts.empty()) {
-              cmSystemTools::ExpandListArgument(opts, qrc.Options);
+              cmExpandList(opts, qrc.Options);
             }
           }
           this->Rcc.Qrcs.push_back(std::move(qrc));
@@ -707,36 +797,35 @@
   // sources meta data cache. Clear it so that OBJECT library targets that
   // are AUTOGEN initialized after this target get their added
   // mocs_compilation.cpp source acknowledged by this target.
-  this->Target->ClearSourcesCache();
+  this->GenTarget->ClearSourcesCache();
 
   // For source files find additional headers and private headers
   if (this->MocOrUicEnabled()) {
     std::vector<MUFileHandle> extraHeaders;
     extraHeaders.reserve(this->AutogenTarget.Sources.size() * 2);
     // Header search suffixes and extensions
-    std::array<std::string, 2> const suffixes{ { "", "_p" } };
-    auto const& exts = makefile->GetCMakeInstance()->GetHeaderExtensions();
+    static std::initializer_list<cm::string_view> const suffixes{ "", "_p" };
+    auto const& exts = cm->GetHeaderExtensions();
     // Scan through sources
     for (auto const& pair : this->AutogenTarget.Sources) {
       MUFile const& muf = *pair.second;
       if (muf.MocIt || muf.UicIt) {
         // Search for the default header file and a private header
-        std::string const& srcPath = muf.SF->GetFullPath();
-        std::string basePath = cmQtAutoGen::SubDirPrefix(srcPath);
-        basePath += cmSystemTools::GetFilenameWithoutLastExtension(srcPath);
+        std::string const& srcFullPath = muf.SF->ResolveFullPath();
+        std::string basePath = cmStrCat(
+          cmQtAutoGen::SubDirPrefix(srcFullPath),
+          cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath));
         for (auto const& suffix : suffixes) {
-          std::string const suffixedPath = basePath + suffix;
+          std::string const suffixedPath = cmStrCat(basePath, suffix);
           for (auto const& ext : exts) {
-            std::string fullPath = suffixedPath;
-            fullPath += '.';
-            fullPath += ext;
+            std::string fullPath = cmStrCat(suffixedPath, '.', ext);
 
             auto constexpr locationKind = cmSourceFileLocationKind::Known;
-            cmSourceFile* sf = makefile->GetSource(fullPath, locationKind);
+            cmSourceFile* sf =
+              this->Makefile->GetSource(fullPath, locationKind);
             if (sf != nullptr) {
               // Check if we know about this header already
-              if (this->AutogenTarget.Headers.find(sf) !=
-                  this->AutogenTarget.Headers.end()) {
+              if (cmContains(this->AutogenTarget.Headers, sf)) {
                 continue;
               }
               // We only accept not-GENERATED files that do exist.
@@ -746,7 +835,7 @@
               }
             } else if (cmSystemTools::FileExists(fullPath)) {
               // Create a new source file for the existing file
-              sf = makefile->CreateSource(fullPath, false, locationKind);
+              sf = this->Makefile->CreateSource(fullPath, false, locationKind);
             }
 
             if (sf != nullptr) {
@@ -775,37 +864,34 @@
   // The reason is that their file names might be discovered from source files
   // at generation time.
   if (this->MocOrUicEnabled()) {
-    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
-      // sf->GetExtension() is only valid after sf->GetFullPath() ...
+    for (cmSourceFile* sf : this->Makefile->GetSourceFiles()) {
+      // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
       // Since we're iterating over source files that might be not in the
       // target we need to check for path errors (not existing files).
       std::string pathError;
-      std::string const& fullPath = sf->GetFullPath(&pathError);
+      std::string const& fullPath = sf->ResolveFullPath(&pathError);
       if (!pathError.empty() || fullPath.empty()) {
         continue;
       }
-      std::string const& ext = sf->GetExtension();
+      std::string const& extLower =
+        cmSystemTools::LowerCase(sf->GetExtension());
 
-      auto const fileFormat = cmSystemTools::GetFileFormat(ext);
-      if (fileFormat == cmSystemTools::HEADER_FILE_FORMAT) {
-        if (this->AutogenTarget.Headers.find(sf) ==
-            this->AutogenTarget.Headers.end()) {
+      if (cm->IsHeaderExtension(extLower)) {
+        if (!cmContains(this->AutogenTarget.Headers, sf)) {
           auto muf = makeMUFile(sf, fullPath, false);
           if (muf->SkipMoc || muf->SkipUic) {
             this->AutogenTarget.Headers.emplace(sf, std::move(muf));
           }
         }
-      } else if (fileFormat == cmSystemTools::CXX_FILE_FORMAT) {
-        if (this->AutogenTarget.Sources.find(sf) ==
-            this->AutogenTarget.Sources.end()) {
+      } else if (cm->IsSourceExtension(extLower)) {
+        if (!cmContains(this->AutogenTarget.Headers, sf)) {
           auto muf = makeMUFile(sf, fullPath, false);
           if (muf->SkipMoc || muf->SkipUic) {
             this->AutogenTarget.Sources.emplace(sf, std::move(muf));
           }
         }
-      } else if (this->Uic.Enabled && (ext == kw.ui)) {
+      } else if (this->Uic.Enabled && (extLower == kw.ui)) {
         // .ui file
-        std::string realPath = cmSystemTools::GetRealPath(fullPath);
         bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
         bool const skipUic =
           (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
@@ -813,14 +899,11 @@
           // Check if the .ui file has uic options
           std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
           if (!uicOpts.empty()) {
-            this->Uic.FileFiles.push_back(std::move(realPath));
-            std::vector<std::string> optsVec;
-            cmSystemTools::ExpandListArgument(uicOpts, optsVec);
-            this->Uic.FileOptions.push_back(std::move(optsVec));
+            this->Uic.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts));
           }
         } else {
           // Register skipped .ui file
-          this->Uic.SkipUi.insert(std::move(realPath));
+          this->Uic.SkipUi.insert(fullPath);
         }
       }
     }
@@ -831,37 +914,34 @@
     if (this->CMP0071Accept) {
       // Let the autogen target depend on the GENERATED files
       for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
-        this->AutogenTarget.DependFiles.insert(muf->RealPath);
+        this->AutogenTarget.DependFiles.insert(muf->FullPath);
       }
     } else if (this->CMP0071Warn) {
-      std::string msg;
-      msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
-      msg += '\n';
-      std::string property;
+      cm::string_view property;
       if (this->Moc.Enabled && this->Uic.Enabled) {
-        property = kw.SKIP_AUTOGEN;
+        property = "SKIP_AUTOGEN";
       } else if (this->Moc.Enabled) {
-        property = kw.SKIP_AUTOMOC;
+        property = "SKIP_AUTOMOC";
       } else if (this->Uic.Enabled) {
-        property = kw.SKIP_AUTOUIC;
+        property = "SKIP_AUTOUIC";
       }
-      msg += "For compatibility, CMake is excluding the GENERATED source "
-             "file(s):\n";
+      std::string files;
       for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
-        msg += "  ";
-        msg += Quoted(muf->RealPath);
-        msg += '\n';
+        files += cmStrCat("  ", Quoted(muf->FullPath), '\n');
       }
-      msg += "from processing by ";
-      msg += cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false);
-      msg += ". If any of the files should be processed, set CMP0071 to NEW. "
-             "If any of the files should not be processed, "
-             "explicitly exclude them by setting the source file property ";
-      msg += property;
-      msg += ":\n  set_property(SOURCE file.h PROPERTY ";
-      msg += property;
-      msg += " ON)\n";
-      makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
+      this->Makefile->IssueMessage(
+        MessageType::AUTHOR_WARNING,
+        cmStrCat(
+          cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), '\n',
+          "For compatibility, CMake is excluding the GENERATED source "
+          "file(s):\n",
+          files, "from processing by ",
+          cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
+          ".  If any of the files should be processed, set CMP0071 to NEW.  "
+          "If any of the files should not be processed, "
+          "explicitly exclude them by setting the source file property ",
+          property, ":\n  set_property(SOURCE file.h PROPERTY ", property,
+          " ON)\n"));
     }
   }
 
@@ -869,9 +949,8 @@
   if (!this->Rcc.Qrcs.empty()) {
     const bool modernQt = (this->QtVersion.Major >= 5);
     // Target rcc options
-    std::vector<std::string> optionsTarget;
-    cmSystemTools::ExpandListArgument(
-      this->Target->GetSafeProperty(kw.AUTORCC_OPTIONS), optionsTarget);
+    std::vector<std::string> optionsTarget =
+      cmExpandedList(this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS));
 
     // Check if file name is unique
     for (Qrc& qrc : this->Rcc.Qrcs) {
@@ -884,46 +963,19 @@
       }
     }
     // Path checksum and file names
-    {
-      cmFilePathChecksum const fpathCheckSum(makefile);
-      for (Qrc& qrc : this->Rcc.Qrcs) {
-        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
-        // RCC output file name
-        {
-          std::string rccFile = this->Dir.Build + "/";
-          rccFile += qrc.PathChecksum;
-          rccFile += "/qrc_";
-          rccFile += qrc.QrcName;
-          rccFile += ".cpp";
-          qrc.RccFile = std::move(rccFile);
-        }
-        {
-          std::string base = this->Dir.Info;
-          base += "/RCC";
-          base += qrc.QrcName;
-          if (!qrc.Unique) {
-            base += qrc.PathChecksum;
-          }
-
-          qrc.LockFile = base;
-          qrc.LockFile += ".lock";
-
-          qrc.InfoFile = base;
-          qrc.InfoFile += "Info.cmake";
-
-          qrc.SettingsFile = base;
-          qrc.SettingsFile += "Settings.txt";
-
-          if (this->MultiConfig) {
-            for (std::string const& cfg : this->ConfigsList) {
-              qrc.ConfigSettingsFile[cfg] =
-                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
-            }
-          }
-        }
-      }
+    for (Qrc& qrc : this->Rcc.Qrcs) {
+      // Path checksum
+      qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
+      // Output file name
+      qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                "/qrc_", qrc.QrcName, ".cpp");
+      std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
+                                        qrc.QrcName, '_', qrc.QrcPathChecksum);
+      qrc.LockFile = cmStrCat(base, "_Lock.lock");
+      qrc.InfoFile = cmStrCat(base, "_Info.json");
+      ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt");
     }
-    // RCC options
+    // rcc options
     for (Qrc& qrc : this->Rcc.Qrcs) {
       // Target options
       std::vector<std::string> opts = optionsTarget;
@@ -933,8 +985,7 @@
         // Replace '-' with '_'. The former is not valid for symbol names.
         std::replace(name.begin(), name.end(), '-', '_');
         if (!qrc.Unique) {
-          name += "_";
-          name += qrc.PathChecksum;
+          name += cmStrCat('_', qrc.QrcPathChecksum);
         }
         std::vector<std::string> nameOpts;
         nameOpts.emplace_back("-name");
@@ -945,7 +996,7 @@
       RccMergeOptions(opts, qrc.Options, modernQt);
       qrc.Options = std::move(opts);
     }
-    // RCC resources
+    // rcc resources
     for (Qrc& qrc : this->Rcc.Qrcs) {
       if (!qrc.Generated) {
         std::string error;
@@ -964,18 +1015,14 @@
 
 bool cmQtAutoGenInitializer::InitAutogenTarget()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
-  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
-
   // Register info file as generated by CMake
-  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
+  this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
 
   // Files provided by the autogen target
   std::vector<std::string> autogenProvides;
   if (this->Moc.Enabled) {
-    this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true);
-    autogenProvides.push_back(this->Moc.MocsCompilation);
+    this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
+    autogenProvides.push_back(this->Moc.CompilationFile);
   }
 
   // Compose target comment
@@ -991,27 +1038,18 @@
       }
       tools += "UIC";
     }
-    autogenComment = "Automatic ";
-    autogenComment += tools;
-    autogenComment += " for target ";
-    autogenComment += this->Target->GetName();
+    autogenComment = cmStrCat("Automatic ", tools, " for target ",
+                              this->GenTarget->GetName());
   }
 
   // Compose command lines
-  cmCustomCommandLines commandLines;
-  {
-    cmCustomCommandLine currentLine;
-    currentLine.push_back(cmSystemTools::GetCMakeCommand());
-    currentLine.push_back("-E");
-    currentLine.push_back("cmake_autogen");
-    currentLine.push_back(this->AutogenTarget.InfoFile);
-    currentLine.push_back("$<CONFIGURATION>");
-    commandLines.push_back(std::move(currentLine));
-  }
+  cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+    { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
+      this->AutogenTarget.InfoFile, "$<CONFIGURATION>" });
 
   // Use PRE_BUILD on demand
   bool usePRE_BUILD = false;
-  if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
+  if (this->GlobalGen->GetName().find("Visual Studio") != std::string::npos) {
     // Under VS use a PRE_BUILD event instead of a separate target to
     // reduce the number of targets loaded into the IDE.
     // This also works around a VS 11 bug that may skip updating the target:
@@ -1033,7 +1071,8 @@
   if (usePRE_BUILD) {
     // Add additional autogen target dependencies to origin target
     for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
-      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
+      this->GenTarget->Target->AddUtility(depTarget->GetName(),
+                                          this->Makefile);
     }
 
     // Add the pre-build command directly to bypass the OBJECT_LIBRARY
@@ -1043,12 +1082,12 @@
     // PRE_BUILD does not support file dependencies!
     const std::vector<std::string> no_output;
     const std::vector<std::string> no_deps;
-    cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps,
+    cmCustomCommand cc(this->Makefile, no_output, autogenProvides, no_deps,
                        commandLines, autogenComment.c_str(),
                        this->Dir.Work.c_str());
     cc.SetEscapeOldStyle(false);
     cc.SetEscapeAllowMakeVars(true);
-    this->Target->Target->AddPreBuildCommand(cc);
+    this->GenTarget->Target->AddPreBuildCommand(std::move(cc));
   } else {
 
     // Add link library target dependencies to the autogen target
@@ -1059,12 +1098,12 @@
       std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
       for (std::string const& config : this->ConfigsList) {
         cmLinkImplementationLibraries const* libs =
-          this->Target->GetLinkImplementationLibraries(config);
+          this->GenTarget->GetLinkImplementationLibraries(config);
         if (libs != nullptr) {
           for (cmLinkItem const& item : libs->Libraries) {
             cmGeneratorTarget const* libTarget = item.Target;
             if ((libTarget != nullptr) &&
-                !StaticLibraryCycle(this->Target, libTarget, config)) {
+                !StaticLibraryCycle(this->GenTarget, libTarget, config)) {
               // Increment target config count
               commonTargets[libTarget]++;
             }
@@ -1079,25 +1118,25 @@
     }
 
     // Create autogen target
-    cmTarget* autogenTarget = makefile->AddUtilityCommand(
-      this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true,
+    cmTarget* autogenTarget = this->Makefile->AddUtilityCommand(
+      this->AutogenTarget.Name, cmCommandOrigin::Generator, true,
       this->Dir.Work.c_str(), /*byproducts=*/autogenProvides,
       std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
                                this->AutogenTarget.DependFiles.end()),
       commandLines, false, autogenComment.c_str());
     // Create autogen generator target
-    localGen->AddGeneratorTarget(
-      new cmGeneratorTarget(autogenTarget, localGen));
+    this->LocalGen->AddGeneratorTarget(
+      new cmGeneratorTarget(autogenTarget, this->LocalGen));
 
     // Forward origin utilities to autogen target
     if (this->AutogenTarget.DependOrigin) {
-      for (BT<std::string> const& depName : this->Target->GetUtilities()) {
-        autogenTarget->AddUtility(depName.Value, makefile);
+      for (BT<std::string> const& depName : this->GenTarget->GetUtilities()) {
+        autogenTarget->AddUtility(depName.Value, this->Makefile);
       }
     }
     // Add additional autogen target dependencies to autogen target
     for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
-      autogenTarget->AddUtility(depTarget->GetName(), makefile);
+      autogenTarget->AddUtility(depTarget->GetName(), this->Makefile);
     }
 
     // Set FOLDER property in autogen target
@@ -1106,11 +1145,12 @@
     }
 
     // Add autogen target to the origin target dependencies
-    this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile);
+    this->GenTarget->Target->AddUtility(this->AutogenTarget.Name,
+                                        this->Makefile);
 
     // Add autogen target to the global autogen target dependencies
     if (this->AutogenTarget.GlobalTarget) {
-      this->GlobalInitializer->AddToGlobalAutoGen(localGen,
+      this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
                                                   this->AutogenTarget.Name);
     }
   }
@@ -1120,17 +1160,14 @@
 
 bool cmQtAutoGenInitializer::InitRccTargets()
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
-
   for (Qrc const& qrc : this->Rcc.Qrcs) {
     // Register info file as generated by CMake
-    makefile->AddCMakeOutputFile(qrc.InfoFile);
+    this->Makefile->AddCMakeOutputFile(qrc.InfoFile);
     // Register file at target
-    this->AddGeneratedSource(qrc.RccFile, this->Rcc);
+    this->AddGeneratedSource(qrc.OutputFile, this->Rcc);
 
     std::vector<std::string> ccOutput;
-    ccOutput.push_back(qrc.RccFile);
+    ccOutput.push_back(qrc.OutputFile);
 
     std::vector<std::string> ccDepends;
     // Add the .qrc and info file to the custom command dependencies
@@ -1141,61 +1178,51 @@
     if (this->MultiConfig) {
       // Build for all configurations
       for (std::string const& config : this->ConfigsList) {
-        cmCustomCommandLine currentLine;
-        currentLine.push_back(cmSystemTools::GetCMakeCommand());
-        currentLine.push_back("-E");
-        currentLine.push_back("cmake_autorcc");
-        currentLine.push_back(qrc.InfoFile);
-        currentLine.push_back(config);
-        commandLines.push_back(std::move(currentLine));
+        commandLines.push_back(
+          cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
+                              "cmake_autorcc", qrc.InfoFile, config }));
       }
     } else {
-      cmCustomCommandLine currentLine;
-      currentLine.push_back(cmSystemTools::GetCMakeCommand());
-      currentLine.push_back("-E");
-      currentLine.push_back("cmake_autorcc");
-      currentLine.push_back(qrc.InfoFile);
-      currentLine.push_back("$<CONFIG>");
-      commandLines.push_back(std::move(currentLine));
+      commandLines.push_back(
+        cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
+                            "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
     }
-    std::string ccComment = "Automatic RCC for ";
-    ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
+    std::string ccComment =
+      cmStrCat("Automatic RCC for ",
+               FileProjectRelativePath(this->Makefile, qrc.QrcFile));
 
     if (qrc.Generated || this->Rcc.GlobalTarget) {
       // Create custom rcc target
       std::string ccName;
       {
-        ccName = this->Target->GetName();
-        ccName += "_arcc_";
-        ccName += qrc.QrcName;
+        ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName);
         if (!qrc.Unique) {
-          ccName += "_";
-          ccName += qrc.PathChecksum;
+          ccName += cmStrCat('_', qrc.QrcPathChecksum);
         }
 
-        cmTarget* autoRccTarget = makefile->AddUtilityCommand(
-          ccName, cmMakefile::TargetOrigin::Generator, true,
-          this->Dir.Work.c_str(), ccOutput, ccDepends, commandLines, false,
-          ccComment.c_str());
+        cmTarget* autoRccTarget = this->Makefile->AddUtilityCommand(
+          ccName, cmCommandOrigin::Generator, true, this->Dir.Work.c_str(),
+          ccOutput, ccDepends, commandLines, false, ccComment.c_str());
 
         // Create autogen generator target
-        localGen->AddGeneratorTarget(
-          new cmGeneratorTarget(autoRccTarget, localGen));
+        this->LocalGen->AddGeneratorTarget(
+          new cmGeneratorTarget(autoRccTarget, this->LocalGen));
 
         // Set FOLDER property in autogen target
         if (!this->TargetsFolder.empty()) {
           autoRccTarget->SetProperty("FOLDER", this->TargetsFolder.c_str());
         }
         if (!this->Rcc.ExecutableTargetName.empty()) {
-          autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, makefile);
+          autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName,
+                                    this->Makefile);
         }
       }
       // Add autogen target to the origin target dependencies
-      this->Target->Target->AddUtility(ccName, makefile);
+      this->GenTarget->Target->AddUtility(ccName, this->Makefile);
 
       // Add autogen target to the global autogen target dependencies
       if (this->Rcc.GlobalTarget) {
-        this->GlobalInitializer->AddToGlobalAutoRcc(localGen, ccName);
+        this->GlobalInitializer->AddToGlobalAutoRcc(this->LocalGen, ccName);
       }
     } else {
       // Create custom rcc command
@@ -1210,13 +1237,15 @@
         if (!this->Rcc.ExecutableTargetName.empty()) {
           ccDepends.push_back(this->Rcc.ExecutableTargetName);
         }
-        makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
-                                           /*main_dependency*/ std::string(),
-                                           commandLines, ccComment.c_str(),
-                                           this->Dir.Work.c_str());
+        std::string no_main_dependency;
+        cmImplicitDependsList no_implicit_depends;
+        this->Makefile->AddCustomCommandToOutput(
+          ccOutput, ccByproducts, ccDepends, no_main_dependency,
+          no_implicit_depends, commandLines, ccComment.c_str(),
+          this->Dir.Work.c_str());
       }
       // Reconfigure when .qrc file changes
-      makefile->AddCMakeDependFile(qrc.QrcFile);
+      this->Makefile->AddCMakeDependFile(qrc.QrcFile);
     }
   }
 
@@ -1227,9 +1256,8 @@
 {
   // Create info directory on demand
   if (!cmSystemTools::MakeDirectory(this->Dir.Info)) {
-    std::string emsg = ("AutoGen: Could not create directory: ");
-    emsg += Quoted(this->Dir.Info);
-    cmSystemTools::Error(emsg);
+    cmSystemTools::Error(cmStrCat("AutoGen: Could not create directory: ",
+                                  Quoted(this->Dir.Info)));
     return false;
   }
 
@@ -1247,232 +1275,185 @@
 
 bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
 {
-  InfoWriter ofs(this->AutogenTarget.InfoFile);
-  if (ofs) {
-    // Utility lambdas
-    cmMakefile* makefile = this->Target->Target->GetMakefile();
-    auto MfDef = [makefile](const char* key) {
-      return makefile->GetSafeDefinition(key);
-    };
+  // Utility lambdas
+  auto MfDef = [this](std::string const& key) {
+    return this->Makefile->GetSafeDefinition(key);
+  };
 
-    // Write common settings
-    ofs.Write("# Meta\n");
-    ofs.Write("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
-    ofs.Write("AM_PARALLEL", this->AutogenTarget.Parallel);
-    ofs.Write("AM_VERBOSITY", this->Verbosity);
+  // Filtered headers and sources
+  std::set<std::string> moc_skip;
+  std::set<std::string> uic_skip;
+  std::vector<MUFile const*> headers;
+  std::vector<MUFile const*> sources;
 
-    ofs.Write("# Directories\n");
-    ofs.Write("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
-    ofs.Write("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
-    ofs.Write("AM_CMAKE_CURRENT_SOURCE_DIR",
-              MfDef("CMAKE_CURRENT_SOURCE_DIR"));
-    ofs.Write("AM_CMAKE_CURRENT_BINARY_DIR",
-              MfDef("CMAKE_CURRENT_BINARY_DIR"));
-    ofs.Write("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE",
-              MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"));
-    ofs.Write("AM_BUILD_DIR", this->Dir.Build);
-    ofs.Write("AM_INCLUDE_DIR", this->Dir.Include);
-    ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude);
-
-    std::vector<std::string> headers;
-    std::vector<std::string> headersFlags;
-    std::vector<std::string> headersBuildPaths;
-    std::vector<std::string> sources;
-    std::vector<std::string> sourcesFlags;
-    std::set<std::string> moc_skip;
-    std::set<std::string> uic_skip;
-
-    // Filter headers
-    {
-      auto headerCount = this->AutogenTarget.Headers.size();
-      headers.reserve(headerCount);
-      headersFlags.reserve(headerCount);
-
-      std::vector<MUFile const*> sortedHeaders;
-      {
-        sortedHeaders.reserve(headerCount);
-        for (auto const& pair : this->AutogenTarget.Headers) {
-          sortedHeaders.emplace_back(pair.second.get());
-        }
-        std::sort(sortedHeaders.begin(), sortedHeaders.end(),
-                  [](MUFile const* a, MUFile const* b) {
-                    return (a->RealPath < b->RealPath);
-                  });
+  // Filter headers
+  {
+    headers.reserve(this->AutogenTarget.Headers.size());
+    for (auto const& pair : this->AutogenTarget.Headers) {
+      MUFile const* const muf = pair.second.get();
+      if (muf->Generated && !this->CMP0071Accept) {
+        continue;
       }
-
-      for (MUFile const* const muf : sortedHeaders) {
-        if (muf->Generated && !this->CMP0071Accept) {
-          continue;
-        }
-        if (muf->SkipMoc) {
-          moc_skip.insert(muf->RealPath);
-        }
-        if (muf->SkipUic) {
-          uic_skip.insert(muf->RealPath);
-        }
-        if (muf->MocIt || muf->UicIt) {
-          headers.emplace_back(muf->RealPath);
-          std::string flags;
-          flags += muf->MocIt ? 'M' : 'm';
-          flags += muf->UicIt ? 'U' : 'u';
-          headersFlags.emplace_back(std::move(flags));
-        }
+      if (muf->SkipMoc) {
+        moc_skip.insert(muf->FullPath);
+      }
+      if (muf->SkipUic) {
+        uic_skip.insert(muf->FullPath);
+      }
+      if (muf->MocIt || muf->UicIt) {
+        headers.emplace_back(muf);
       }
     }
-    // Header build paths
-    {
-      cmFilePathChecksum const fpathCheckSum(makefile);
-      std::unordered_set<std::string> emitted;
-      for (std::string const& hdr : headers) {
-        std::string basePath = fpathCheckSum.getPart(hdr);
-        basePath += "/moc_";
-        basePath += cmSystemTools::GetFilenameWithoutLastExtension(hdr);
-        for (unsigned int ii = 1; ii != 1024; ++ii) {
-          std::string path = basePath;
-          if (ii > 1) {
-            path += '_';
-            path += std::to_string(ii);
-          }
-          path += ".cpp";
-          if (emitted.emplace(path).second) {
-            headersBuildPaths.emplace_back(std::move(path));
-            break;
-          }
-        }
-      }
-    }
-
-    // Filter sources
-    {
-      auto sourcesCount = this->AutogenTarget.Sources.size();
-      sources.reserve(sourcesCount);
-      sourcesFlags.reserve(sourcesCount);
-
-      std::vector<MUFile const*> sorted;
-      sorted.reserve(sourcesCount);
-      for (auto const& pair : this->AutogenTarget.Sources) {
-        sorted.emplace_back(pair.second.get());
-      }
-      std::sort(sorted.begin(), sorted.end(),
-                [](MUFile const* a, MUFile const* b) {
-                  return (a->RealPath < b->RealPath);
-                });
-
-      for (MUFile const* const muf : sorted) {
-        if (muf->Generated && !this->CMP0071Accept) {
-          continue;
-        }
-        if (muf->SkipMoc) {
-          moc_skip.insert(muf->RealPath);
-        }
-        if (muf->SkipUic) {
-          uic_skip.insert(muf->RealPath);
-        }
-        if (muf->MocIt || muf->UicIt) {
-          sources.emplace_back(muf->RealPath);
-          std::string flags;
-          flags += muf->MocIt ? 'M' : 'm';
-          flags += muf->UicIt ? 'U' : 'u';
-          sourcesFlags.emplace_back(std::move(flags));
-        }
-      }
-    }
-
-    ofs.Write("# Qt\n");
-    ofs.WriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major);
-    ofs.Write("AM_QT_MOC_EXECUTABLE", this->Moc.Executable);
-    ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable);
-
-    ofs.Write("# Files\n");
-    ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
-    ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile);
-    ofs.WriteConfig("AM_SETTINGS_FILE",
-                    this->AutogenTarget.ConfigSettingsFile);
-    ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
-    ofs.WriteStrings("AM_HEADERS", headers);
-    ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags);
-    ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths);
-    ofs.WriteStrings("AM_SOURCES", sources);
-    ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags);
-
-    // Write moc settings
-    if (this->Moc.Enabled) {
-      ofs.Write("# MOC settings\n");
-      ofs.WriteStrings("AM_MOC_SKIP", moc_skip);
-      ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines);
-      ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines);
-      ofs.WriteStrings("AM_MOC_INCLUDES", this->Moc.Includes);
-      ofs.WriteConfigStrings("AM_MOC_INCLUDES", this->Moc.ConfigIncludes);
-      ofs.Write("AM_MOC_OPTIONS",
-                this->Target->GetSafeProperty("AUTOMOC_MOC_OPTIONS"));
-      ofs.Write("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE"));
-      ofs.Write("AM_MOC_MACRO_NAMES",
-                this->Target->GetSafeProperty("AUTOMOC_MACRO_NAMES"));
-      ofs.Write("AM_MOC_DEPEND_FILTERS",
-                this->Target->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
-      ofs.Write("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
-    }
-
-    // Write uic settings
-    if (this->Uic.Enabled) {
-      // Add skipped .ui files
-      uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
-
-      ofs.Write("# UIC settings\n");
-      ofs.WriteStrings("AM_UIC_SKIP", uic_skip);
-      ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options);
-      ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions);
-      ofs.WriteStrings("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles);
-      ofs.WriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions);
-      ofs.WriteStrings("AM_UIC_SEARCH_PATHS", this->Uic.SearchPaths);
-    }
-  } else {
-    std::string err = "AutoGen: Could not write file ";
-    err += this->AutogenTarget.InfoFile;
-    cmSystemTools::Error(err);
-    return false;
+    std::sort(headers.begin(), headers.end(),
+              [](MUFile const* a, MUFile const* b) {
+                return (a->FullPath < b->FullPath);
+              });
   }
 
+  // Filter sources
+  {
+    sources.reserve(this->AutogenTarget.Sources.size());
+    for (auto const& pair : this->AutogenTarget.Sources) {
+      MUFile const* const muf = pair.second.get();
+      if (muf->Generated && !this->CMP0071Accept) {
+        continue;
+      }
+      if (muf->SkipMoc) {
+        moc_skip.insert(muf->FullPath);
+      }
+      if (muf->SkipUic) {
+        uic_skip.insert(muf->FullPath);
+      }
+      if (muf->MocIt || muf->UicIt) {
+        sources.emplace_back(muf);
+      }
+    }
+    std::sort(sources.begin(), sources.end(),
+              [](MUFile const* a, MUFile const* b) {
+                return (a->FullPath < b->FullPath);
+              });
+  }
+
+  // Info writer
+  InfoWriter info;
+
+  // General
+  info.SetBool("MULTI_CONFIG", this->MultiConfig);
+  info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
+  info.SetUInt("VERBOSITY", this->Verbosity);
+
+  // Directories
+  info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
+  info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
+  info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
+  info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
+  info.Set("BUILD_DIR", this->Dir.Build);
+  info.SetConfig("INCLUDE_DIR", this->Dir.Include);
+
+  info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
+  info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
+  info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
+
+  info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
+  info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
+  info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
+  info.SetArray("HEADER_EXTENSIONS",
+                this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
+  info.SetArrayArray(
+    "HEADERS", headers, [this](Json::Value& jval, MUFile const* muf) {
+      jval.resize(3u);
+      jval[0u] = muf->FullPath;
+      jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
+      jval[2u] = this->GetMocBuildPath(*muf);
+    });
+  info.SetArrayArray(
+    "SOURCES", sources, [](Json::Value& jval, MUFile const* muf) {
+      jval.resize(2u);
+      jval[0u] = muf->FullPath;
+      jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
+    });
+
+  // Write moc settings
+  if (this->Moc.Enabled) {
+    info.SetArray("MOC_SKIP", moc_skip);
+    info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines);
+    info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes);
+    info.SetArray("MOC_OPTIONS", this->Moc.Options);
+    info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
+    info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
+    info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
+    info.SetArrayArray(
+      "MOC_DEPEND_FILTERS", this->Moc.DependFilters,
+      [](Json::Value& jval, std::pair<std::string, std::string> const& pair) {
+        jval.resize(2u);
+        jval[0u] = pair.first;
+        jval[1u] = pair.second;
+      });
+    info.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
+    info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
+    info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
+  }
+
+  // Write uic settings
+  if (this->Uic.Enabled) {
+    // Add skipped .ui files
+    uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
+
+    info.SetArray("UIC_SKIP", uic_skip);
+    info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFiles,
+                       [](Json::Value& jval, UicT::UiFileT const& uiFile) {
+                         jval.resize(2u);
+                         jval[0u] = uiFile.first;
+                         InfoWriter::MakeStringArray(jval[1u], uiFile.second);
+                       });
+    info.SetConfigArray("UIC_OPTIONS", this->Uic.Options);
+    info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths);
+  }
+
+  info.Save(this->AutogenTarget.InfoFile);
+
   return true;
 }
 
 bool cmQtAutoGenInitializer::SetupWriteRccInfo()
 {
   for (Qrc const& qrc : this->Rcc.Qrcs) {
-    InfoWriter ofs(qrc.InfoFile);
-    if (ofs) {
-      // Write
-      ofs.Write("# Configurations\n");
-      ofs.Write("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
-      ofs.Write("ARCC_VERBOSITY", this->Verbosity);
-      ofs.Write("# Settings file\n");
-      ofs.Write("ARCC_SETTINGS_FILE", qrc.SettingsFile);
-      ofs.WriteConfig("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile);
+    // Utility lambdas
+    auto MfDef = [this](std::string const& key) {
+      return this->Makefile->GetSafeDefinition(key);
+    };
 
-      ofs.Write("# Directories\n");
-      ofs.Write("ARCC_BUILD_DIR", this->Dir.Build);
-      ofs.Write("ARCC_INCLUDE_DIR", this->Dir.Include);
-      ofs.WriteConfig("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude);
+    InfoWriter info;
 
-      ofs.Write("# Rcc executable\n");
-      ofs.Write("ARCC_RCC_EXECUTABLE", this->Rcc.Executable);
-      ofs.WriteStrings("ARCC_RCC_LIST_OPTIONS",
-                       this->Rcc.ExecutableFeatures->ListOptions);
+    // General
+    info.SetBool("MULTI_CONFIG", this->MultiConfig);
+    info.SetUInt("VERBOSITY", this->Verbosity);
 
-      ofs.Write("# Rcc job\n");
-      ofs.Write("ARCC_LOCK_FILE", qrc.LockFile);
-      ofs.Write("ARCC_SOURCE", qrc.QrcFile);
-      ofs.Write("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum);
-      ofs.Write("ARCC_OUTPUT_NAME",
-                cmSystemTools::GetFilenameName(qrc.RccFile));
-      ofs.WriteStrings("ARCC_OPTIONS", qrc.Options);
-      ofs.WriteStrings("ARCC_INPUTS", qrc.Resources);
-    } else {
-      std::string err = "AutoRcc: Could not write file ";
-      err += qrc.InfoFile;
-      cmSystemTools::Error(err);
-      return false;
-    }
+    // Files
+    info.Set("LOCK_FILE", qrc.LockFile);
+    info.SetConfig("SETTINGS_FILE", qrc.SettingsFile);
+
+    // Directories
+    info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
+    info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
+    info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
+    info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
+    info.Set("BUILD_DIR", this->Dir.Build);
+    info.SetConfig("INCLUDE_DIR", this->Dir.Include);
+
+    // rcc executable
+    info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
+    info.SetArray("RCC_LIST_OPTIONS",
+                  this->Rcc.ExecutableFeatures->ListOptions);
+
+    // qrc file
+    info.Set("SOURCE", qrc.QrcFile);
+    info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
+    info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
+    info.SetArray("OPTIONS", qrc.Options);
+    info.SetArray("INPUTS", qrc.Resources);
+
+    info.Save(qrc.InfoFile);
   }
 
   return true;
@@ -1481,8 +1462,7 @@
 void cmQtAutoGenInitializer::RegisterGeneratedSource(
   std::string const& filename)
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
-  cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true);
+  cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
   gFile->SetProperty("GENERATED", "1");
   gFile->SetProperty("SKIP_AUTOGEN", "1");
 }
@@ -1494,15 +1474,14 @@
   // Register source at makefile
   this->RegisterGeneratedSource(filename);
   // Add source file to target
-  this->Target->AddSource(filename, prepend);
+  this->GenTarget->AddSource(filename, prepend);
   // Add source file to source group
   return this->AddToSourceGroup(filename, genVars.GenNameUpper);
 }
 
 bool cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
-                                              std::string const& genNameUpper)
+                                              cm::string_view genNameUpper)
 {
-  cmMakefile* makefile = this->Target->Target->GetMakefile();
   cmSourceGroup* sourceGroup = nullptr;
   // Acquire source group
   {
@@ -1510,28 +1489,27 @@
     std::string groupName;
     {
       // Prefer generator specific source group name
-      std::array<std::string, 2> props{ { genNameUpper + "_SOURCE_GROUP",
-                                          "AUTOGEN_SOURCE_GROUP" } };
-      for (std::string& prop : props) {
-        const char* propName = makefile->GetState()->GetGlobalProperty(prop);
+      std::initializer_list<std::string> const props{
+        cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
+      };
+      for (std::string const& prop : props) {
+        const char* propName =
+          this->Makefile->GetState()->GetGlobalProperty(prop);
         if ((propName != nullptr) && (*propName != '\0')) {
           groupName = propName;
-          property = std::move(prop);
+          property = prop;
           break;
         }
       }
     }
     // Generate a source group on demand
     if (!groupName.empty()) {
-      sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
+      sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
       if (sourceGroup == nullptr) {
-        std::string err;
-        err += genNameUpper;
-        err += " error in ";
-        err += property;
-        err += ": Could not find or create the source group ";
-        err += cmQtAutoGen::Quoted(groupName);
-        cmSystemTools::Error(err);
+        cmSystemTools::Error(
+          cmStrCat(genNameUpper, " error in ", property,
+                   ": Could not find or create the source group ",
+                   cmQtAutoGen::Quoted(groupName)));
         return false;
       }
     }
@@ -1544,62 +1522,86 @@
 
 void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
 {
-  Target->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName.c_str(),
-                                 false);
+  this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES",
+                                          fileName.c_str(), false);
 }
 
-static unsigned int CharPtrToUInt(const char* const input)
+void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
+                                             cm::string_view prefix,
+                                             cm::string_view suffix)
 {
-  unsigned long tmp = 0;
-  if (input != nullptr && cmSystemTools::StringToULong(input, &tmp)) {
-    return static_cast<unsigned int>(tmp);
-  }
-  return 0;
-}
-
-static std::vector<cmQtAutoGen::IntegerVersion> GetKnownQtVersions(
-  cmGeneratorTarget const* target)
-{
-  // Qt version variable prefixes
-  static std::array<std::string, 3> const prefixes{ { "Qt6Core", "Qt5Core",
-                                                      "QT" } };
-
-  std::vector<cmQtAutoGen::IntegerVersion> result;
-  result.reserve(prefixes.size() * 2);
-  // Adds a version to the result (nullptr safe)
-  auto addVersion = [&result](const char* major, const char* minor) {
-    cmQtAutoGen::IntegerVersion ver(CharPtrToUInt(major),
-                                    CharPtrToUInt(minor));
-    if (ver.Major != 0) {
-      result.emplace_back(ver);
+  configString.Default = cmStrCat(prefix, suffix);
+  if (this->MultiConfig) {
+    for (auto const& cfg : this->ConfigsList) {
+      configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix);
     }
-  };
-  cmMakefile* makefile = target->Target->GetMakefile();
-
-  // Read versions from variables
-  for (const std::string& prefix : prefixes) {
-    addVersion(makefile->GetDefinition(prefix + "_VERSION_MAJOR"),
-               makefile->GetDefinition(prefix + "_VERSION_MINOR"));
   }
+}
 
-  // Read versions from directory properties
-  for (const std::string& prefix : prefixes) {
-    addVersion(makefile->GetProperty(prefix + "_VERSION_MAJOR"),
-               makefile->GetProperty(prefix + "_VERSION_MINOR"));
+void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
+{
+  this->AddCleanFile(configString.Default);
+  if (this->MultiConfig) {
+    for (auto const& pair : configString.Config) {
+      this->AddCleanFile(pair.second);
+    }
   }
-
-  return result;
 }
 
 std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
 cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
 {
+  // Converts a char ptr to an unsigned int value
+  auto toUInt = [](const char* const input) -> unsigned int {
+    unsigned long tmp = 0;
+    if (input != nullptr && cmStrToULong(input, &tmp)) {
+      return static_cast<unsigned int>(tmp);
+    }
+    return 0u;
+  };
+
+  // Initialize return value to a default
   std::pair<IntegerVersion, unsigned int> res(
     IntegerVersion(),
-    CharPtrToUInt(target->GetLinkInterfaceDependentStringProperty(
-      "QT_MAJOR_VERSION", "")));
+    toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
+                                                           "")));
 
-  auto knownQtVersions = GetKnownQtVersions(target);
+  // Acquire known Qt versions
+  std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions;
+  {
+    // Qt version variable prefixes
+    static std::initializer_list<
+      std::pair<cm::string_view, cm::string_view>> const keys{
+      { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" },
+      { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" },
+      { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" },
+    };
+
+    knownQtVersions.reserve(keys.size() * 2);
+
+    // Adds a version to the result (nullptr safe)
+    auto addVersion = [&knownQtVersions, &toUInt](const char* major,
+                                                  const char* minor) {
+      cmQtAutoGen::IntegerVersion ver(toUInt(major), toUInt(minor));
+      if (ver.Major != 0) {
+        knownQtVersions.emplace_back(ver);
+      }
+    };
+
+    // Read versions from variables
+    for (auto const& keyPair : keys) {
+      addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)),
+                 target->Makefile->GetDefinition(std::string(keyPair.second)));
+    }
+
+    // Read versions from directory properties
+    for (auto const& keyPair : keys) {
+      addVersion(target->Makefile->GetProperty(std::string(keyPair.first)),
+                 target->Makefile->GetProperty(std::string(keyPair.second)));
+    }
+  }
+
+  // Evaluate known Qt versions
   if (!knownQtVersions.empty()) {
     if (res.second == 0) {
       // No specific version was requested by the target:
@@ -1618,32 +1620,50 @@
   return res;
 }
 
+std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf)
+{
+  std::string res;
+  if (!muf.MocIt) {
+    return res;
+  }
+  {
+    std::string const basePath =
+      cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_",
+               FileNameWithoutLastExtension(muf.FullPath));
+    std::string suffix;
+    constexpr std::size_t num_tries_max = 256;
+    for (std::size_t ii = 0; ii != num_tries_max; ++ii) {
+      res = cmStrCat(basePath, suffix, ".cpp");
+      if (this->Moc.EmittedBuildPaths.emplace(res).second) {
+        break;
+      }
+      // Compute new suffix
+      suffix = cmStrCat('_', ii + 1);
+    }
+  }
+  return res;
+}
+
 bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
                                              const std::string& executable,
                                              bool ignoreMissingTarget) const
 {
   auto print_err = [this, &genVars](std::string const& err) {
-    std::string msg = genVars.GenNameUpper;
-    msg += " for target ";
-    msg += this->Target->GetName();
-    msg += ": ";
-    msg += err;
-    cmSystemTools::Error(msg);
+    cmSystemTools::Error(cmStrCat(genVars.GenNameUpper, " for target ",
+                                  this->GenTarget->GetName(), ": ", err));
   };
 
   // Custom executable
   {
-    std::string const prop = genVars.GenNameUpper + "_EXECUTABLE";
-    std::string const val = this->Target->Target->GetSafeProperty(prop);
+    std::string const prop = cmStrCat(genVars.GenNameUpper, "_EXECUTABLE");
+    std::string const val = this->GenTarget->Target->GetSafeProperty(prop);
     if (!val.empty()) {
       // Evaluate generator expression
       {
-        cmListFileBacktrace lfbt =
-          this->Target->Target->GetMakefile()->GetBacktrace();
+        cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
         cmGeneratorExpression ge(lfbt);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
-        genVars.Executable =
-          cge->Evaluate(this->Target->GetLocalGenerator(), "");
+        genVars.Executable = cge->Evaluate(this->LocalGen, "");
       }
       if (genVars.Executable.empty() && !ignoreMissingTarget) {
         print_err(prop + " evaluates to an empty value");
@@ -1660,26 +1680,26 @@
   // Find executable target
   {
     // Find executable target name
-    std::string targetName;
+    cm::string_view prefix;
     if (this->QtVersion.Major == 4) {
-      targetName = "Qt4::";
+      prefix = "Qt4::";
     } else if (this->QtVersion.Major == 5) {
-      targetName = "Qt5::";
+      prefix = "Qt5::";
     } else if (this->QtVersion.Major == 6) {
-      targetName = "Qt6::";
+      prefix = "Qt6::";
     }
-    targetName += executable;
+    std::string const targetName = cmStrCat(prefix, executable);
 
     // Find target
-    cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
-    cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(targetName);
-    if (target != nullptr) {
+    cmGeneratorTarget* genTarget =
+      this->LocalGen->FindGeneratorTargetToUse(targetName);
+    if (genTarget != nullptr) {
       genVars.ExecutableTargetName = targetName;
-      genVars.ExecutableTarget = target;
-      if (target->IsImported()) {
-        genVars.Executable = target->ImportedGetLocation("");
+      genVars.ExecutableTarget = genTarget;
+      if (genTarget->IsImported()) {
+        genVars.Executable = genTarget->ImportedGetLocation("");
       } else {
-        genVars.Executable = target->GetLocation("");
+        genVars.Executable = genTarget->GetLocation("");
       }
     } else {
       if (ignoreMissingTarget) {
@@ -1688,11 +1708,8 @@
           std::make_shared<cmQtAutoGen::CompilerFeatures>();
         return true;
       }
-      std::string err = "Could not find ";
-      err += executable;
-      err += " executable target ";
-      err += targetName;
-      print_err(err);
+      print_err(cmStrCat("Could not find ", executable, " executable target ",
+                         targetName));
       return false;
     }
   }
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index aa073d1..486dab7 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -4,49 +4,72 @@
 #define cmQtAutoGenInitializer_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmGeneratedFileStream.h"
-#include "cmQtAutoGen.h"
 
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <ostream>
+#include <memory>
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include <cm/string_view>
+
+#include "cmFilePathChecksum.h"
+#include "cmQtAutoGen.h"
+
 class cmGeneratorTarget;
-class cmTarget;
+class cmGlobalGenerator;
+class cmLocalGenerator;
+class cmMakefile;
 class cmQtAutoGenGlobalInitializer;
 class cmSourceFile;
+class cmTarget;
 
-/// @brief Initializes the QtAutoGen generators
+/** \class cmQtAutoGenerator
+ * \brief Initializes the QtAutoGen generators
+ */
 class cmQtAutoGenInitializer : public cmQtAutoGen
 {
 public:
-  /// @brief Rcc job information
+  /** String value with per configuration variants.  */
+  class ConfigString
+  {
+  public:
+    std::string Default;
+    std::unordered_map<std::string, std::string> Config;
+  };
+
+  /** String values with per configuration variants.  */
+  template <typename C>
+  class ConfigStrings
+  {
+  public:
+    C Default;
+    std::unordered_map<std::string, C> Config;
+  };
+
+  /** rcc job.  */
   class Qrc
   {
   public:
     std::string LockFile;
     std::string QrcFile;
     std::string QrcName;
-    std::string PathChecksum;
+    std::string QrcPathChecksum;
     std::string InfoFile;
-    std::string SettingsFile;
-    std::map<std::string, std::string> ConfigSettingsFile;
-    std::string RccFile;
+    ConfigString SettingsFile;
+    std::string OutputFile;
     bool Generated = false;
     bool Unique = false;
     std::vector<std::string> Options;
     std::vector<std::string> Resources;
   };
 
-  /// @brief Moc/Uic file
+  /** moc and/or uic file.  */
   struct MUFile
   {
-    std::string RealPath;
+    std::string FullPath;
     cmSourceFile* SF = nullptr;
     bool Generated = false;
     bool SkipMoc = false;
@@ -54,67 +77,33 @@
     bool MocIt = false;
     bool UicIt = false;
   };
-  typedef std::unique_ptr<MUFile> MUFileHandle;
+  using MUFileHandle = std::unique_ptr<MUFile>;
 
-  /// @brief Abstract moc/uic/rcc generator variables base class
+  /** Abstract moc/uic/rcc generator variables base class.  */
   struct GenVarsT
   {
     bool Enabled = false;
     // Generator type/name
     GenT Gen;
-    std::string const& GenNameUpper;
+    cm::string_view GenNameUpper;
     // Executable
     std::string ExecutableTargetName;
     cmGeneratorTarget* ExecutableTarget = nullptr;
     std::string Executable;
     CompilerFeaturesHandle ExecutableFeatures;
 
-    /// @brief Constructor
     GenVarsT(GenT gen)
       : Gen(gen)
       , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){};
   };
 
-  /// @brief Writes a CMake info file
-  class InfoWriter
-  {
-  public:
-    /// @brief Open the given file
-    InfoWriter(std::string const& filename);
-
-    /// @return True if the file is open
-    explicit operator bool() const { return static_cast<bool>(Ofs_); }
-
-    void Write(const char* text) { Ofs_ << text; }
-    void Write(const char* key, std::string const& value);
-    void WriteUInt(const char* key, unsigned int value);
-
-    template <class C>
-    void WriteStrings(const char* key, C const& container);
-    void WriteConfig(const char* key,
-                     std::map<std::string, std::string> const& map);
-    template <class C>
-    void WriteConfigStrings(const char* key,
-                            std::map<std::string, C> const& map);
-    void WriteNestedLists(const char* key,
-                          std::vector<std::vector<std::string>> const& lists);
-
-  private:
-    template <class IT>
-    static std::string ListJoin(IT it_begin, IT it_end);
-    static std::string ConfigKey(const char* key, std::string const& config);
-
-  private:
-    cmGeneratedFileStream Ofs_;
-  };
-
 public:
-  /// @return The detected Qt version and the required Qt major version
+  /** @return The detected Qt version and the required Qt major version.  */
   static std::pair<IntegerVersion, unsigned int> GetQtVersion(
-    cmGeneratorTarget const* target);
+    cmGeneratorTarget const* genTarget);
 
   cmQtAutoGenInitializer(cmQtAutoGenGlobalInitializer* globalInitializer,
-                         cmGeneratorTarget* target,
+                         cmGeneratorTarget* genTarget,
                          IntegerVersion const& qtVersion, bool mocEnabled,
                          bool uicEnabled, bool rccEnabled,
                          bool globalAutogenTarget, bool globalAutoRccTarget);
@@ -123,7 +112,7 @@
   bool SetupCustomTargets();
 
 private:
-  /// @brief If moc or uic is enabled, the autogen target will be generated
+  /** If moc or uic is enabled, the autogen target will be generated.  */
   bool MocOrUicEnabled() const
   {
     return (this->Moc.Enabled || this->Uic.Enabled);
@@ -144,48 +133,57 @@
   bool AddGeneratedSource(std::string const& filename, GenVarsT const& genVars,
                           bool prepend = false);
   bool AddToSourceGroup(std::string const& fileName,
-                        std::string const& genNameUpper);
+                        cm::string_view genNameUpper);
   void AddCleanFile(std::string const& fileName);
 
+  void ConfigFileNames(ConfigString& configString, cm::string_view prefix,
+                       cm::string_view suffix);
+  void ConfigFileClean(ConfigString& configString);
+
+  std::string GetMocBuildPath(MUFile const& muf);
+
   bool GetQtExecutable(GenVarsT& genVars, const std::string& executable,
                        bool ignoreMissingTarget) const;
 
 private:
-  cmQtAutoGenGlobalInitializer* GlobalInitializer;
-  cmGeneratorTarget* Target;
+  cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr;
+  cmGeneratorTarget* GenTarget = nullptr;
+  cmGlobalGenerator* GlobalGen = nullptr;
+  cmLocalGenerator* LocalGen = nullptr;
+  cmMakefile* Makefile = nullptr;
+  cmFilePathChecksum const PathCheckSum;
 
-  // Configuration
+  // -- Configuration
   IntegerVersion QtVersion;
+  unsigned int Verbosity = 0;
   bool MultiConfig = false;
-  std::string ConfigDefault;
-  std::vector<std::string> ConfigsList;
-  std::string Verbosity;
-  std::string TargetsFolder;
   bool CMP0071Accept = false;
   bool CMP0071Warn = false;
+  std::string ConfigDefault;
+  std::vector<std::string> ConfigsList;
+  std::string TargetsFolder;
 
-  /// @brief Common directories
+  /** Common directories.  */
   struct
   {
     std::string Info;
     std::string Build;
     std::string Work;
-    std::string Include;
-    std::map<std::string, std::string> ConfigInclude;
+    ConfigString Include;
+    std::string IncludeGenExp;
   } Dir;
 
-  /// @brief Autogen target variables
+  /** Autogen target variables.  */
   struct
   {
     std::string Name;
     bool GlobalTarget = false;
     // Settings
-    std::string Parallel;
+    unsigned int Parallel = 1;
     // Configuration files
     std::string InfoFile;
-    std::string SettingsFile;
-    std::string ParseCacheFile;
-    std::map<std::string, std::string> ConfigSettingsFile;
+    ConfigString SettingsFile;
+    ConfigString ParseCacheFile;
     // Dependencies
     bool DependOrigin = false;
     std::set<std::string> DependFiles;
@@ -196,45 +194,53 @@
     std::vector<MUFile*> FilesGenerated;
   } AutogenTarget;
 
-  /// @brief Moc only variables
+  /** moc variables.  */
   struct MocT : public GenVarsT
   {
-    std::string PredefsCmd;
-    std::vector<std::string> Includes;
-    std::map<std::string, std::vector<std::string>> ConfigIncludes;
-    std::set<std::string> Defines;
-    std::map<std::string, std::set<std::string>> ConfigDefines;
-    std::string MocsCompilation;
-
-    /// @brief Constructor
     MocT()
       : GenVarsT(GenT::MOC){};
+
+    bool RelaxedMode = false;
+    bool PathPrefix = false;
+    std::string CompilationFile;
+    // Compiler implicit pre defines
+    std::vector<std::string> PredefsCmd;
+    ConfigString PredefsFile;
+    // Defines
+    ConfigStrings<std::set<std::string>> Defines;
+    // Includes
+    ConfigStrings<std::vector<std::string>> Includes;
+    // Options
+    std::vector<std::string> Options;
+    // Filters
+    std::vector<std::string> MacroNames;
+    std::vector<std::pair<std::string, std::string>> DependFilters;
+    // Utility
+    std::unordered_set<std::string> EmittedBuildPaths;
   } Moc;
 
-  /// @brief Uic only variables
+  /** uic variables.  */
   struct UicT : public GenVarsT
   {
-    std::set<std::string> SkipUi;
-    std::vector<std::string> SearchPaths;
-    std::vector<std::string> Options;
-    std::map<std::string, std::vector<std::string>> ConfigOptions;
-    std::vector<std::string> FileFiles;
-    std::vector<std::vector<std::string>> FileOptions;
+    using UiFileT = std::pair<std::string, std::vector<std::string>>;
 
-    /// @brief Constructor
     UicT()
       : GenVarsT(GenT::UIC){};
+
+    std::set<std::string> SkipUi;
+    std::vector<UiFileT> UiFiles;
+    ConfigStrings<std::vector<std::string>> Options;
+    std::vector<std::string> SearchPaths;
   } Uic;
 
-  /// @brief Rcc only variables
+  /** rcc variables.  */
   struct RccT : public GenVarsT
   {
-    bool GlobalTarget = false;
-    std::vector<Qrc> Qrcs;
-
-    /// @brief Constructor
     RccT()
       : GenVarsT(GenT::RCC){};
+
+    bool GlobalTarget = false;
+    std::vector<Qrc> Qrcs;
   } Rcc;
 };
 
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index e1c435b..da96305 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -1,18 +1,14 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenerator.h"
-#include "cmQtAutoGen.h"
 
 #include "cmsys/FStream.hxx"
 
-#include "cmAlgorithms.h"
-#include "cmGlobalGenerator.h"
-#include "cmMakefile.h"
-#include "cmState.h"
-#include "cmStateDirectory.h"
-#include "cmStateSnapshot.h"
+#include "cm_jsoncpp_reader.h"
+
+#include "cmQtAutoGen.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmake.h"
 
 cmQtAutoGenerator::Logger::Logger()
 {
@@ -21,11 +17,11 @@
     std::string verbose;
     if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) {
       unsigned long iVerbose = 0;
-      if (cmSystemTools::StringToULong(verbose.c_str(), &iVerbose)) {
+      if (cmStrToULong(verbose, &iVerbose)) {
         SetVerbosity(static_cast<unsigned int>(iVerbose));
       } else {
         // Non numeric verbosity
-        SetVerbose(cmSystemTools::IsOn(verbose));
+        SetVerbose(cmIsOn(verbose));
       }
     }
   }
@@ -33,7 +29,7 @@
     std::string colorEnv;
     cmSystemTools::GetEnv("COLOR", colorEnv);
     if (!colorEnv.empty()) {
-      SetColorOutput(cmSystemTools::IsOn(colorEnv));
+      SetColorOutput(cmIsOn(colorEnv));
     } else {
       SetColorOutput(true);
     }
@@ -42,13 +38,10 @@
 
 cmQtAutoGenerator::Logger::~Logger() = default;
 
-void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value)
+void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value)
 {
-  unsigned long verbosity = 0;
-  if (cmSystemTools::StringToULong(value.c_str(), &verbosity)) {
-    if (this->Verbosity_ < verbosity) {
-      this->Verbosity_ = static_cast<unsigned int>(verbosity);
-    }
+  if (this->Verbosity_ < value) {
+    this->Verbosity_ = value;
   }
 }
 
@@ -57,24 +50,16 @@
   ColorOutput_ = value;
 }
 
-std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
+std::string cmQtAutoGenerator::Logger::HeadLine(cm::string_view title)
 {
-  std::string head = title;
-  head += '\n';
-  head.append(head.size() - 1, '-');
-  head += '\n';
-  return head;
+  return cmStrCat(title, '\n', std::string(title.size(), '-'), '\n');
 }
 
 void cmQtAutoGenerator::Logger::Info(GenT genType,
-                                     std::string const& message) const
+                                     cm::string_view message) const
 {
-  std::string msg = GeneratorName(genType);
-  msg += ": ";
-  msg += message;
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
+  std::string msg = cmStrCat(GeneratorName(genType), ": ", message,
+                             cmHasSuffix(message, '\n') ? "" : "\n");
   {
     std::lock_guard<std::mutex> lock(Mutex_);
     cmSystemTools::Stdout(msg);
@@ -82,94 +67,46 @@
 }
 
 void cmQtAutoGenerator::Logger::Warning(GenT genType,
-                                        std::string const& message) const
+                                        cm::string_view message) const
 {
   std::string msg;
   if (message.find('\n') == std::string::npos) {
     // Single line message
-    msg += GeneratorName(genType);
-    msg += " warning: ";
+    msg = cmStrCat(GeneratorName(genType), " warning: ", message,
+                   cmHasSuffix(message, '\n') ? "\n" : "\n\n");
   } else {
     // Multi line message
-    msg += HeadLine(GeneratorName(genType) + " warning");
+    msg = cmStrCat(HeadLine(cmStrCat(GeneratorName(genType), " warning")),
+                   message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
   }
-  // Message
-  msg += message;
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
-  msg.push_back('\n');
   {
     std::lock_guard<std::mutex> lock(Mutex_);
     cmSystemTools::Stdout(msg);
   }
 }
 
-void cmQtAutoGenerator::Logger::WarningFile(GenT genType,
-                                            std::string const& filename,
-                                            std::string const& message) const
-{
-  std::string msg = "  ";
-  msg += Quoted(filename);
-  msg.push_back('\n');
-  // Message
-  msg += message;
-  Warning(genType, msg);
-}
-
 void cmQtAutoGenerator::Logger::Error(GenT genType,
-                                      std::string const& message) const
+                                      cm::string_view message) const
 {
-  std::string msg;
-  msg += HeadLine(GeneratorName(genType) + " error");
-  // Message
-  msg += message;
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
-  msg.push_back('\n');
+  std::string msg =
+    cmStrCat('\n', HeadLine(cmStrCat(GeneratorName(genType), " error")),
+             message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
   {
     std::lock_guard<std::mutex> lock(Mutex_);
     cmSystemTools::Stderr(msg);
   }
 }
 
-void cmQtAutoGenerator::Logger::ErrorFile(GenT genType,
-                                          std::string const& filename,
-                                          std::string const& message) const
-{
-  std::string emsg = "  ";
-  emsg += Quoted(filename);
-  emsg += '\n';
-  // Message
-  emsg += message;
-  Error(genType, emsg);
-}
-
 void cmQtAutoGenerator::Logger::ErrorCommand(
-  GenT genType, std::string const& message,
+  GenT genType, cm::string_view message,
   std::vector<std::string> const& command, std::string const& output) const
 {
-  std::string msg;
-  msg.push_back('\n');
-  msg += HeadLine(GeneratorName(genType) + " subprocess error");
-  msg += message;
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
-  msg.push_back('\n');
-  msg += HeadLine("Command");
-  msg += QuotedCommand(command);
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
-  msg.push_back('\n');
-  msg += HeadLine("Output");
-  msg += output;
-  if (msg.back() != '\n') {
-    msg.push_back('\n');
-  }
-  msg.push_back('\n');
+  std::string msg = cmStrCat(
+    '\n', HeadLine(cmStrCat(GeneratorName(genType), " subprocess error")),
+    message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
+  msg += cmStrCat(HeadLine("Command"), QuotedCommand(command), "\n\n");
+  msg += cmStrCat(HeadLine("Output"), output,
+                  cmHasSuffix(output, '\n') ? "\n" : "\n\n");
   {
     std::lock_guard<std::mutex> lock(Mutex_);
     cmSystemTools::Stderr(msg);
@@ -210,7 +147,7 @@
       return false;
     }
     content.reserve(length);
-    typedef std::istreambuf_iterator<char> IsIt;
+    using IsIt = std::istreambuf_iterator<char>;
     content.assign(IsIt{ ifs }, IsIt{});
     if (!ifs) {
       content.clear();
@@ -268,65 +205,288 @@
   return differs;
 }
 
-cmQtAutoGenerator::cmQtAutoGenerator() = default;
+cmQtAutoGenerator::cmQtAutoGenerator(GenT genType)
+  : GenType_(genType)
+{
+}
 
 cmQtAutoGenerator::~cmQtAutoGenerator() = default;
 
-bool cmQtAutoGenerator::Run(std::string const& infoFile,
-                            std::string const& config)
+bool cmQtAutoGenerator::InfoT::Read(std::istream& istr)
 {
-  // Info settings
-  InfoFile_ = infoFile;
-  cmSystemTools::ConvertToUnixSlashes(InfoFile_);
-  if (!InfoFileTime_.Load(InfoFile_)) {
-    std::string msg = "AutoGen: The info file ";
-    msg += Quoted(InfoFile_);
-    msg += " is not readable\n";
-    cmSystemTools::Stderr(msg);
+  try {
+    istr >> Json_;
+  } catch (...) {
     return false;
   }
-  InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
-  InfoConfig_ = config;
-
-  bool success = false;
-  {
-    cmake cm(cmake::RoleScript, cmState::Unknown);
-    cm.SetHomeOutputDirectory(InfoDir());
-    cm.SetHomeDirectory(InfoDir());
-    cm.GetCurrentSnapshot().SetDefaultDefinitions();
-    cmGlobalGenerator gg(&cm);
-
-    cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
-    snapshot.GetDirectory().SetCurrentBinary(InfoDir());
-    snapshot.GetDirectory().SetCurrentSource(InfoDir());
-
-    auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
-    // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
-    // https://gitlab.kitware.com/cmake/cmake/issues/17570
-    makefile->SetPolicyVersion("3.9", std::string());
-    gg.SetCurrentMakefile(makefile.get());
-    success = this->Init(makefile.get());
-  }
-  if (success) {
-    success = this->Process();
-  }
-  return success;
+  return true;
 }
 
-std::string cmQtAutoGenerator::SettingsFind(std::string const& content,
-                                            const char* key)
+bool cmQtAutoGenerator::InfoT::GetJsonArray(std::vector<std::string>& list,
+                                            Json::Value const& jval)
 {
-  std::string prefix(key);
-  prefix += ':';
-  std::string::size_type pos = content.find(prefix);
-  if (pos != std::string::npos) {
+  Json::ArrayIndex const arraySize = jval.size();
+  if (arraySize == 0) {
+    return false;
+  }
+
+  bool picked = false;
+  list.reserve(list.size() + arraySize);
+  for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+    Json::Value const& ival = jval[ii];
+    if (ival.isString()) {
+      list.emplace_back(ival.asString());
+      picked = true;
+    }
+  }
+  return picked;
+}
+
+bool cmQtAutoGenerator::InfoT::GetJsonArray(
+  std::unordered_set<std::string>& list, Json::Value const& jval)
+{
+  Json::ArrayIndex const arraySize = jval.size();
+  if (arraySize == 0) {
+    return false;
+  }
+
+  bool picked = false;
+  list.reserve(list.size() + arraySize);
+  for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+    Json::Value const& ival = jval[ii];
+    if (ival.isString()) {
+      list.emplace(ival.asString());
+      picked = true;
+    }
+  }
+  return picked;
+}
+
+std::string cmQtAutoGenerator::InfoT::ConfigKey(cm::string_view key) const
+{
+  return cmStrCat(key, '_', Gen_.InfoConfig());
+}
+
+bool cmQtAutoGenerator::InfoT::GetString(std::string const& key,
+                                         std::string& value,
+                                         bool required) const
+{
+  Json::Value const& jval = Json_[key];
+  if (!jval.isString()) {
+    if (!jval.isNull() || required) {
+      return LogError(cmStrCat(key, " is not a string."));
+    }
+  } else {
+    value = jval.asString();
+    if (value.empty() && required) {
+      return LogError(cmStrCat(key, " is empty."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoT::GetStringConfig(std::string const& key,
+                                               std::string& value,
+                                               bool required) const
+{
+  { // Try config
+    std::string const configKey = ConfigKey(key);
+    Json::Value const& jval = Json_[configKey];
+    if (!jval.isNull()) {
+      if (!jval.isString()) {
+        return LogError(cmStrCat(configKey, " is not a string."));
+      }
+      value = jval.asString();
+      if (required && value.empty()) {
+        return LogError(cmStrCat(configKey, " is empty."));
+      }
+      return true;
+    }
+  }
+  // Try plain
+  return GetString(key, value, required);
+}
+
+bool cmQtAutoGenerator::InfoT::GetBool(std::string const& key, bool& value,
+                                       bool required) const
+{
+  Json::Value const& jval = Json_[key];
+  if (jval.isBool()) {
+    value = jval.asBool();
+  } else {
+    if (!jval.isNull() || required) {
+      return LogError(cmStrCat(key, " is not a boolean."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoT::GetUInt(std::string const& key,
+                                       unsigned int& value,
+                                       bool required) const
+{
+  Json::Value const& jval = Json_[key];
+  if (jval.isUInt()) {
+    value = jval.asUInt();
+  } else {
+    if (!jval.isNull() || required) {
+      return LogError(cmStrCat(key, " is not an unsigned integer."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
+                                        std::vector<std::string>& list,
+                                        bool required) const
+{
+  Json::Value const& jval = Json_[key];
+  if (!jval.isArray()) {
+    if (!jval.isNull() || required) {
+      return LogError(cmStrCat(key, " is not an array."));
+    }
+  }
+  return GetJsonArray(list, jval) || !required;
+}
+
+bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
+                                        std::unordered_set<std::string>& list,
+                                        bool required) const
+{
+  Json::Value const& jval = Json_[key];
+  if (!jval.isArray()) {
+    if (!jval.isNull() || required) {
+      return LogError(cmStrCat(key, " is not an array."));
+    }
+  }
+  return GetJsonArray(list, jval) || !required;
+}
+
+bool cmQtAutoGenerator::InfoT::GetArrayConfig(std::string const& key,
+                                              std::vector<std::string>& list,
+                                              bool required) const
+{
+  { // Try config
+    std::string const configKey = ConfigKey(key);
+    Json::Value const& jval = Json_[configKey];
+    if (!jval.isNull()) {
+      if (!jval.isArray()) {
+        return LogError(cmStrCat(configKey, " is not an array string."));
+      }
+      if (!GetJsonArray(list, jval) && required) {
+        return LogError(cmStrCat(configKey, " is empty."));
+      }
+      return true;
+    }
+  }
+  // Try plain
+  return GetArray(key, list, required);
+}
+
+bool cmQtAutoGenerator::InfoT::LogError(GenT genType,
+                                        cm::string_view message) const
+{
+  Gen_.Log().Error(genType,
+                   cmStrCat("Info error in info file\n",
+                            Quoted(Gen_.InfoFile()), ":\n", message));
+  return false;
+}
+
+bool cmQtAutoGenerator::InfoT::LogError(cm::string_view message) const
+{
+  return LogError(Gen_.GenType_, message);
+}
+
+std::string cmQtAutoGenerator::SettingsFind(cm::string_view content,
+                                            cm::string_view key)
+{
+  cm::string_view res;
+  std::string const prefix = cmStrCat(key, ':');
+  cm::string_view::size_type pos = content.find(prefix);
+  if (pos != cm::string_view::npos) {
     pos += prefix.size();
     if (pos < content.size()) {
-      std::string::size_type posE = content.find('\n', pos);
-      if ((posE != std::string::npos) && (posE != pos)) {
-        return content.substr(pos, posE - pos);
+      cm::string_view::size_type posE = content.find('\n', pos);
+      if ((posE != cm::string_view::npos) && (posE != pos)) {
+        res = content.substr(pos, posE - pos);
       }
     }
   }
-  return std::string();
+  return std::string(res);
+}
+
+std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const
+{
+  std::string res;
+  if (cmHasPrefix(path, ProjectDirs().Source)) {
+    res = cmStrCat("SRC:", path.substr(ProjectDirs().Source.size()));
+  } else if (cmHasPrefix(path, ProjectDirs().Binary)) {
+    res = cmStrCat("BIN:", path.substr(ProjectDirs().Binary.size()));
+  } else {
+    res = std::string(path);
+  }
+  return cmQtAutoGen::Quoted(res);
+}
+
+bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
+{
+  // Info config
+  InfoConfig_ = std::string(config);
+
+  // Info file
+  InfoFile_ = std::string(infoFile);
+  cmSystemTools::CollapseFullPath(InfoFile_);
+  InfoDir_ = cmSystemTools::GetFilenamePath(InfoFile_);
+
+  // Load info file time
+  if (!InfoFileTime_.Load(InfoFile_)) {
+    cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ",
+                                   Quoted(InfoFile_), " is not readable\n"));
+    return false;
+  }
+
+  {
+    InfoT info(*this);
+
+    // Read info file
+    {
+      cmsys::ifstream ifs(InfoFile_.c_str(),
+                          (std::ios::in | std::ios::binary));
+      if (!ifs) {
+        Log().Error(
+          GenType_,
+          cmStrCat("Could not to open info file ", Quoted(InfoFile_)));
+        return false;
+      }
+      if (!info.Read(ifs)) {
+        Log().Error(GenType_,
+                    cmStrCat("Could not read info file ", Quoted(InfoFile_)));
+        return false;
+      }
+    }
+
+    // -- Read common info settings
+    {
+      unsigned int verbosity = 0;
+      // Info: setup project directories
+      if (!info.GetUInt("VERBOSITY", verbosity, false) ||
+          !info.GetString("CMAKE_SOURCE_DIR", ProjectDirs_.Source, true) ||
+          !info.GetString("CMAKE_BINARY_DIR", ProjectDirs_.Binary, true) ||
+          !info.GetString("CMAKE_CURRENT_SOURCE_DIR",
+                          ProjectDirs_.CurrentSource, true) ||
+          !info.GetString("CMAKE_CURRENT_BINARY_DIR",
+                          ProjectDirs_.CurrentBinary, true)) {
+        return false;
+      }
+      Logger_.RaiseVerbosity(verbosity);
+    }
+
+    // -- Call virtual init from info method.
+    if (!this->InitFromInfo(info)) {
+      return false;
+    }
+  }
+
+  // Call virtual process method.
+  return this->Process();
 }
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index ff4c4c9..bbe6dd0 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -5,14 +5,18 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileTime.h"
-#include "cmQtAutoGen.h"
-
+#include <istream>
 #include <mutex>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
-class cmMakefile;
+#include <cm/string_view>
+
+#include "cm_jsoncpp_value.h"
+
+#include "cmFileTime.h"
+#include "cmQtAutoGen.h"
 
 /** \class cmQtAutoGenerator
  * \brief Base class for QtAutoGen generators
@@ -22,9 +26,7 @@
 public:
   // -- Types
 
-  /**
-   * Thread safe logger
-   */
+  /** Thread safe logger.  */
   class Logger
   {
   public:
@@ -34,28 +36,24 @@
     // -- Verbosity
     unsigned int Verbosity() const { return this->Verbosity_; }
     void SetVerbosity(unsigned int value) { this->Verbosity_ = value; }
-    void RaiseVerbosity(std::string const& value);
+    void RaiseVerbosity(unsigned int value);
     bool Verbose() const { return (this->Verbosity_ != 0); }
     void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; }
     // -- Color output
     bool ColorOutput() const { return this->ColorOutput_; }
     void SetColorOutput(bool value);
     // -- Log info
-    void Info(GenT genType, std::string const& message) const;
+    void Info(GenT genType, cm::string_view message) const;
     // -- Log warning
-    void Warning(GenT genType, std::string const& message) const;
-    void WarningFile(GenT genType, std::string const& filename,
-                     std::string const& message) const;
+    void Warning(GenT genType, cm::string_view message) const;
     // -- Log error
-    void Error(GenT genType, std::string const& message) const;
-    void ErrorFile(GenT genType, std::string const& filename,
-                   std::string const& message) const;
-    void ErrorCommand(GenT genType, std::string const& message,
+    void Error(GenT genType, cm::string_view message) const;
+    void ErrorCommand(GenT genType, cm::string_view message,
                       std::vector<std::string> const& command,
                       std::string const& output) const;
 
   private:
-    static std::string HeadLine(std::string const& title);
+    static std::string HeadLine(cm::string_view title);
 
   private:
     mutable std::mutex Mutex_;
@@ -63,6 +61,15 @@
     bool ColorOutput_ = false;
   };
 
+  /** Project directories.  */
+  struct ProjectDirsT
+  {
+    std::string Source;
+    std::string Binary;
+    std::string CurrentSource;
+    std::string CurrentBinary;
+  };
+
   // -- File system methods
   static bool MakeParentDirectory(std::string const& filename);
   static bool FileRead(std::string& content, std::string const& filename,
@@ -75,35 +82,100 @@
 
 public:
   // -- Constructors
-  cmQtAutoGenerator();
+  cmQtAutoGenerator(GenT genType);
   virtual ~cmQtAutoGenerator();
 
   cmQtAutoGenerator(cmQtAutoGenerator const&) = delete;
   cmQtAutoGenerator& operator=(cmQtAutoGenerator const&) = delete;
 
-  // -- Run
-  bool Run(std::string const& infoFile, std::string const& config);
-
-  // -- InfoFile
+  // -- Info options
   std::string const& InfoFile() const { return InfoFile_; }
-  cmFileTime const& InfoFileTime() const { return InfoFileTime_; }
   std::string const& InfoDir() const { return InfoDir_; }
+  cmFileTime const& InfoFileTime() const { return InfoFileTime_; }
   std::string const& InfoConfig() const { return InfoConfig_; }
 
-  // -- Utility
-  static std::string SettingsFind(std::string const& content, const char* key);
+  // -- Info file parsing
+  /** Info file reader class. */
+  class InfoT
+  {
+  public:
+    InfoT(cmQtAutoGenerator& gen)
+      : Gen_(gen)
+    {
+    }
+
+    /** Read json data from a stream.  */
+    bool Read(std::istream& istr);
+
+    /** Returns false if the JSON value isn't a string.  */
+    bool GetString(std::string const& key, std::string& value,
+                   bool required) const;
+    bool GetStringConfig(std::string const& key, std::string& value,
+                         bool required) const;
+    bool GetBool(std::string const& key, bool& value, bool required) const;
+    bool GetUInt(std::string const& key, unsigned int& value,
+                 bool required) const;
+    /** Returns false if the JSON value isn't an array.  */
+    bool GetArray(std::string const& key, std::vector<std::string>& list,
+                  bool required) const;
+    bool GetArray(std::string const& key,
+                  std::unordered_set<std::string>& list, bool required) const;
+    bool GetArrayConfig(std::string const& key, std::vector<std::string>& list,
+                        bool required) const;
+
+    Json::Value const& GetValue(std::string const& key) const
+    {
+      return Json_[key];
+    }
+
+    /** Returns true if strings were appended to the list.  */
+    static bool GetJsonArray(std::vector<std::string>& list,
+                             Json::Value const& jval);
+    /** Returns true if strings were found in the JSON array.  */
+    static bool GetJsonArray(std::unordered_set<std::string>& list,
+                             Json::Value const& jval);
+
+    bool LogError(GenT genType, cm::string_view message) const;
+    bool LogError(cm::string_view message) const;
+
+  private:
+    std::string ConfigKey(cm::string_view key) const;
+
+  private:
+    Json::Value Json_;
+    cmQtAutoGenerator& Gen_;
+  };
+
+  // -- Settings file
+  static std::string SettingsFind(cm::string_view content,
+                                  cm::string_view key);
+
+  // -- Directories
+  ProjectDirsT const& ProjectDirs() const { return ProjectDirs_; }
+  std::string MessagePath(cm::string_view path) const;
+
+  // -- Run
+  bool Run(cm::string_view infoFile, cm::string_view config);
 
 protected:
   // -- Abstract processing interface
-  virtual bool Init(cmMakefile* makefile) = 0;
+  virtual bool InitFromInfo(InfoT const& info) = 0;
   virtual bool Process() = 0;
+  // - Utility classes
+  Logger const& Log() const { return Logger_; }
 
 private:
-  // -- Info settings
+  // -- Generator type
+  GenT GenType_;
+  // -- Logging
+  Logger Logger_;
+  // -- Info file
   std::string InfoFile_;
-  cmFileTime InfoFileTime_;
   std::string InfoDir_;
+  cmFileTime InfoFileTime_;
   std::string InfoConfig_;
+  // -- Directories
+  ProjectDirsT ProjectDirs_;
 };
 
 #endif
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 641d8aa..127421a 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -3,31 +3,571 @@
 #include "cmQtAutoMocUic.h"
 
 #include <algorithm>
-#include <array>
-#include <list>
-#include <memory>
+#include <atomic>
+#include <cstddef>
+#include <map>
+#include <mutex>
 #include <set>
-#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
+#include <vector>
+
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_jsoncpp_value.h"
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
+#include "cmFileTime.h"
 #include "cmGeneratedFileStream.h"
-#include "cmMakefile.h"
 #include "cmQtAutoGen.h"
+#include "cmQtAutoGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmake.h"
-#include "cmsys/FStream.hxx"
+#include "cmWorkerPool.h"
 
 #if defined(__APPLE__)
 #  include <unistd.h>
 #endif
 
-static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
-static constexpr std::size_t UiUnderscoreLength = 3;  // Length of "ui_"
+namespace {
 
-cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key,
-                                         std::size_t basePrefixLength)
+constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
+constexpr std::size_t UiUnderscoreLength = 3;  // Length of "ui_"
+
+/** \class cmQtAutoMocUicT
+ * \brief AUTOMOC and AUTOUIC generator
+ */
+class cmQtAutoMocUicT : public cmQtAutoGenerator
+{
+public:
+  cmQtAutoMocUicT();
+  ~cmQtAutoMocUicT() override;
+
+  cmQtAutoMocUicT(cmQtAutoMocUicT const&) = delete;
+  cmQtAutoMocUicT& operator=(cmQtAutoMocUicT const&) = delete;
+
+public:
+  // -- Types
+
+  /** Include string with sub parts.  */
+  struct IncludeKeyT
+  {
+    IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
+
+    std::string Key;  // Full include string
+    std::string Dir;  // Include directory
+    std::string Base; // Base part of the include file name
+  };
+
+  /** Search key plus regular expression pair.  */
+  struct KeyExpT
+  {
+    KeyExpT(std::string key, std::string const& exp)
+      : Key(std::move(key))
+      , Exp(exp)
+    {
+    }
+
+    std::string Key;
+    cmsys::RegularExpression Exp;
+  };
+
+  /** Source file parsing cache.  */
+  class ParseCacheT
+  {
+  public:
+    // -- Types
+
+    /** Entry of the file parsing cache.  */
+    struct FileT
+    {
+      void Clear();
+
+      struct MocT
+      {
+        std::string Macro;
+        struct IncludeT
+        {
+          std::vector<IncludeKeyT> Underscore;
+          std::vector<IncludeKeyT> Dot;
+        } Include;
+        std::vector<std::string> Depends;
+      } Moc;
+
+      struct UicT
+      {
+        std::vector<IncludeKeyT> Include;
+        std::vector<std::string> Depends;
+      } Uic;
+    };
+    using FileHandleT = std::shared_ptr<FileT>;
+    using GetOrInsertT = std::pair<FileHandleT, bool>;
+
+  public:
+    ParseCacheT();
+    ~ParseCacheT();
+
+    bool ReadFromFile(std::string const& fileName);
+    bool WriteToFile(std::string const& fileName);
+
+    //! Always returns a valid handle
+    GetOrInsertT GetOrInsert(std::string const& fileName);
+
+  private:
+    std::unordered_map<std::string, FileHandleT> Map_;
+  };
+
+  /** Source file data.  */
+  class SourceFileT
+  {
+  public:
+    SourceFileT(std::string fileName)
+      : FileName(std::move(fileName))
+    {
+    }
+
+  public:
+    std::string FileName;
+    cmFileTime FileTime;
+    ParseCacheT::FileHandleT ParseData;
+    std::string BuildPath;
+    bool IsHeader = false;
+    bool Moc = false;
+    bool Uic = false;
+  };
+  using SourceFileHandleT = std::shared_ptr<SourceFileT>;
+  using SourceFileMapT = std::map<std::string, SourceFileHandleT>;
+
+  /** Meta compiler file mapping information.  */
+  struct MappingT
+  {
+    SourceFileHandleT SourceFile;
+    std::string OutputFile;
+    std::string IncludeString;
+    std::vector<SourceFileHandleT> IncluderFiles;
+  };
+  using MappingHandleT = std::shared_ptr<MappingT>;
+  using MappingMapT = std::map<std::string, MappingHandleT>;
+
+  /** Common settings.  */
+  class BaseSettingsT
+  {
+  public:
+    // -- Constructors
+    BaseSettingsT();
+    ~BaseSettingsT();
+
+    BaseSettingsT(BaseSettingsT const&) = delete;
+    BaseSettingsT& operator=(BaseSettingsT const&) = delete;
+
+    // -- Attributes
+    // - Config
+    bool MultiConfig = false;
+    unsigned int QtVersionMajor = 4;
+    unsigned int ThreadCount = 0;
+    // - Directories
+    std::string AutogenBuildDir;
+    std::string AutogenIncludeDir;
+    // - Files
+    std::string CMakeExecutable;
+    cmFileTime CMakeExecutableTime;
+    std::string ParseCacheFile;
+    std::vector<std::string> HeaderExtensions;
+  };
+
+  /** Shared common variables.  */
+  class BaseEvalT
+  {
+  public:
+    // -- Parse Cache
+    bool ParseCacheChanged = false;
+    cmFileTime ParseCacheTime;
+    ParseCacheT ParseCache;
+
+    // -- Sources
+    SourceFileMapT Headers;
+    SourceFileMapT Sources;
+  };
+
+  /** Moc settings.  */
+  class MocSettingsT
+  {
+  public:
+    // -- Constructors
+    MocSettingsT();
+    ~MocSettingsT();
+
+    MocSettingsT(MocSettingsT const&) = delete;
+    MocSettingsT& operator=(MocSettingsT const&) = delete;
+
+    // -- Const methods
+    bool skipped(std::string const& fileName) const;
+    std::string MacrosString() const;
+
+    // -- Attributes
+    bool Enabled = false;
+    bool SettingsChanged = false;
+    bool RelaxedMode = false;
+    bool PathPrefix = false;
+    cmFileTime ExecutableTime;
+    std::string Executable;
+    std::string CompFileAbs;
+    std::string PredefsFileAbs;
+    std::unordered_set<std::string> SkipList;
+    std::vector<std::string> IncludePaths;
+    std::vector<std::string> Definitions;
+    std::vector<std::string> OptionsIncludes;
+    std::vector<std::string> OptionsDefinitions;
+    std::vector<std::string> OptionsExtra;
+    std::vector<std::string> PredefsCmd;
+    std::vector<KeyExpT> DependFilters;
+    std::vector<KeyExpT> MacroFilters;
+    cmsys::RegularExpression RegExpInclude;
+  };
+
+  /** Moc shared variables.  */
+  class MocEvalT
+  {
+  public:
+    // -- predefines file
+    cmFileTime PredefsTime;
+    // -- Mappings
+    MappingMapT HeaderMappings;
+    MappingMapT SourceMappings;
+    MappingMapT Includes;
+    // -- Discovered files
+    SourceFileMapT HeadersDiscovered;
+    // -- Output directories
+    std::unordered_set<std::string> OutputDirs;
+    // -- Mocs compilation
+    bool CompUpdated = false;
+    std::vector<std::string> CompFiles;
+  };
+
+  /** Uic settings.  */
+  class UicSettingsT
+  {
+  public:
+    struct UiFile
+    {
+      std::vector<std::string> Options;
+    };
+
+  public:
+    UicSettingsT();
+    ~UicSettingsT();
+
+    UicSettingsT(UicSettingsT const&) = delete;
+    UicSettingsT& operator=(UicSettingsT const&) = delete;
+
+    // -- Const methods
+    bool skipped(std::string const& fileName) const;
+
+    // -- Attributes
+    bool Enabled = false;
+    bool SettingsChanged = false;
+    cmFileTime ExecutableTime;
+    std::string Executable;
+    std::unordered_set<std::string> SkipList;
+    std::vector<std::string> Options;
+    std::unordered_map<std::string, UiFile> UiFiles;
+    std::vector<std::string> SearchPaths;
+    cmsys::RegularExpression RegExpInclude;
+  };
+
+  /** Uic shared variables.  */
+  class UicEvalT
+  {
+  public:
+    // -- Discovered files
+    SourceFileMapT UiFiles;
+    // -- Mappings
+    MappingMapT Includes;
+    // -- Output directories
+    std::unordered_set<std::string> OutputDirs;
+  };
+
+  /** Abstract job class for concurrent job processing.  */
+  class JobT : public cmWorkerPool::JobT
+  {
+  protected:
+    /** Protected default constructor.  */
+    JobT(bool fence = false)
+      : cmWorkerPool::JobT(fence)
+    {
+    }
+
+    //! Get the generator. Only valid during Process() call!
+    cmQtAutoMocUicT* Gen() const
+    {
+      return static_cast<cmQtAutoMocUicT*>(UserData());
+    };
+
+    // -- Accessors. Only valid during Process() call!
+    Logger const& Log() const { return Gen()->Log(); }
+    BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); }
+    BaseEvalT& BaseEval() const { return Gen()->BaseEval(); }
+    MocSettingsT const& MocConst() const { return Gen()->MocConst(); }
+    MocEvalT& MocEval() const { return Gen()->MocEval(); }
+    UicSettingsT const& UicConst() const { return Gen()->UicConst(); }
+    UicEvalT& UicEval() const { return Gen()->UicEval(); }
+
+    // -- Logging
+    std::string MessagePath(cm::string_view path) const
+    {
+      return Gen()->MessagePath(path);
+    }
+    // - Error logging with automatic abort
+    void LogError(GenT genType, cm::string_view message) const;
+    void LogCommandError(GenT genType, cm::string_view message,
+                         std::vector<std::string> const& command,
+                         std::string const& output) const;
+
+    /** @brief Run an external process. Use only during Process() call!  */
+    bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
+                    std::vector<std::string> const& command,
+                    std::string* infoMessage = nullptr);
+  };
+
+  /** Fence job utility class.  */
+  class JobFenceT : public JobT
+  {
+  public:
+    JobFenceT()
+      : JobT(true)
+    {
+    }
+    void Process() override{};
+  };
+
+  /** Generate moc_predefs.h.  */
+  class JobMocPredefsT : public JobFenceT
+  {
+    void Process() override;
+    bool Update(std::string* reason) const;
+  };
+
+  /** File parse job base class.  */
+  class JobParseT : public JobT
+  {
+  public:
+    JobParseT(SourceFileHandleT fileHandle)
+      : FileHandle(std::move(fileHandle))
+    {
+    }
+
+  protected:
+    bool ReadFile();
+    void CreateKeys(std::vector<IncludeKeyT>& container,
+                    std::set<std::string> const& source,
+                    std::size_t basePrefixLength);
+    void MocMacro();
+    void MocDependecies();
+    void MocIncludes();
+    void UicIncludes();
+
+  protected:
+    SourceFileHandleT FileHandle;
+    std::string Content;
+  };
+
+  /** Header file parse job.  */
+  class JobParseHeaderT : public JobParseT
+  {
+  public:
+    using JobParseT::JobParseT;
+    void Process() override;
+  };
+
+  /** Source file parse job.  */
+  class JobParseSourceT : public JobParseT
+  {
+  public:
+    using JobParseT::JobParseT;
+    void Process() override;
+  };
+
+  /** Evaluate cached file parse data - moc.  */
+  class JobEvalCacheT : public JobT
+  {
+  protected:
+    std::string MessageSearchLocations() const;
+    std::vector<std::string> SearchLocations;
+  };
+
+  /** Evaluate cached file parse data - moc.  */
+  class JobEvalCacheMocT : public JobEvalCacheT
+  {
+    void Process() override;
+    bool EvalHeader(SourceFileHandleT source);
+    bool EvalSource(SourceFileHandleT const& source);
+    bool FindIncludedHeader(SourceFileHandleT& headerHandle,
+                            cm::string_view includerDir,
+                            cm::string_view includeBase);
+    bool RegisterIncluded(std::string const& includeString,
+                          SourceFileHandleT includerFileHandle,
+                          SourceFileHandleT sourceFileHandle) const;
+    void RegisterMapping(MappingHandleT mappingHandle) const;
+    std::string MessageHeader(cm::string_view headerBase) const;
+  };
+
+  /** Evaluate cached file parse data - uic.  */
+  class JobEvalCacheUicT : public JobEvalCacheT
+  {
+    void Process() override;
+    bool EvalFile(SourceFileHandleT const& sourceFileHandle);
+    bool FindIncludedUi(cm::string_view sourceDirPrefix,
+                        cm::string_view includePrefix);
+    bool RegisterMapping(std::string const& includeString,
+                         SourceFileHandleT includerFileHandle);
+
+    std::string UiName;
+    SourceFileHandleT UiFileHandle;
+  };
+
+  /** Evaluate cached file parse data - finish  */
+  class JobEvalCacheFinishT : public JobFenceT
+  {
+    void Process() override;
+  };
+
+  /** Dependency probing base job.  */
+  class JobProbeDepsT : public JobT
+  {
+  };
+
+  /** Probes file dependencies and generates moc compile jobs.  */
+  class JobProbeDepsMocT : public JobProbeDepsT
+  {
+    void Process() override;
+    bool Generate(MappingHandleT const& mapping, bool compFile) const;
+    bool Probe(MappingT const& mapping, std::string* reason) const;
+    std::pair<std::string, cmFileTime> FindDependency(
+      std::string const& sourceDir, std::string const& includeString) const;
+  };
+
+  /** Probes file dependencies and generates uic compile jobs.  */
+  class JobProbeDepsUicT : public JobProbeDepsT
+  {
+    void Process() override;
+    bool Probe(MappingT const& mapping, std::string* reason) const;
+  };
+
+  /** Dependency probing finish job.  */
+  class JobProbeDepsFinishT : public JobFenceT
+  {
+    void Process() override;
+  };
+
+  /** Meta compiler base job.  */
+  class JobCompileT : public JobT
+  {
+  public:
+    JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason)
+      : Mapping(std::move(uicMapping))
+      , Reason(std::move(reason))
+    {
+    }
+
+  protected:
+    MappingHandleT Mapping;
+    std::unique_ptr<std::string> Reason;
+  };
+
+  /** moc compiles a file.  */
+  class JobCompileMocT : public JobCompileT
+  {
+  public:
+    using JobCompileT::JobCompileT;
+    void Process() override;
+  };
+
+  /** uic compiles a file.  */
+  class JobCompileUicT : public JobCompileT
+  {
+  public:
+    using JobCompileT::JobCompileT;
+    void Process() override;
+  };
+
+  /** Generate mocs_compilation.cpp.  */
+  class JobMocsCompilationT : public JobFenceT
+  {
+  private:
+    void Process() override;
+  };
+
+  /** @brief The last job.  */
+  class JobFinishT : public JobFenceT
+  {
+  private:
+    void Process() override;
+  };
+
+  // -- Const settings interface
+  BaseSettingsT const& BaseConst() const { return this->BaseConst_; }
+  BaseEvalT& BaseEval() { return this->BaseEval_; }
+  MocSettingsT const& MocConst() const { return this->MocConst_; }
+  MocEvalT& MocEval() { return this->MocEval_; }
+  UicSettingsT const& UicConst() const { return this->UicConst_; }
+  UicEvalT& UicEval() { return this->UicEval_; }
+
+  // -- Parallel job processing interface
+  cmWorkerPool& WorkerPool() { return WorkerPool_; }
+  void AbortError() { Abort(true); }
+  void AbortSuccess() { Abort(false); }
+
+  // -- Utility
+  std::string AbsoluteBuildPath(cm::string_view relativePath) const;
+  std::string AbsoluteIncludePath(cm::string_view relativePath) const;
+  template <class JOBTYPE>
+  void CreateParseJobs(SourceFileMapT const& sourceMap);
+  std::string CollapseFullPathTS(std::string const& path) const;
+
+private:
+  // -- Abstract processing interface
+  bool InitFromInfo(InfoT const& info) override;
+  void InitJobs();
+  bool Process() override;
+  // -- Settings file
+  void SettingsFileRead();
+  bool SettingsFileWrite();
+  // -- Parse cache
+  void ParseCacheRead();
+  bool ParseCacheWrite();
+  // -- Thread processing
+  void Abort(bool error);
+  // -- Generation
+  bool CreateDirectories();
+
+private:
+  // -- Settings
+  BaseSettingsT BaseConst_;
+  BaseEvalT BaseEval_;
+  MocSettingsT MocConst_;
+  MocEvalT MocEval_;
+  UicSettingsT UicConst_;
+  UicEvalT UicEval_;
+  // -- Settings file
+  std::string SettingsFile_;
+  std::string SettingsStringMoc_;
+  std::string SettingsStringUic_;
+  // -- Worker thread pool
+  std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
+  cmWorkerPool WorkerPool_;
+  // -- Concurrent processing
+  mutable std::mutex CMakeLibMutex_;
+};
+
+cmQtAutoMocUicT::IncludeKeyT::IncludeKeyT(std::string const& key,
+                                          std::size_t basePrefixLength)
   : Key(key)
   , Dir(SubDirPrefix(key))
   , Base(cmSystemTools::GetFilenameWithoutLastExtension(key))
@@ -37,7 +577,7 @@
   }
 }
 
-void cmQtAutoMocUic::ParseCacheT::FileT::Clear()
+void cmQtAutoMocUicT::ParseCacheT::FileT::Clear()
 {
   Moc.Macro.clear();
   Moc.Include.Underscore.clear();
@@ -48,18 +588,8 @@
   Uic.Depends.clear();
 }
 
-cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get(
-  std::string const& fileName) const
-{
-  auto it = Map_.find(fileName);
-  if (it != Map_.end()) {
-    return it->second;
-  }
-  return FileHandleT();
-}
-
-cmQtAutoMocUic::ParseCacheT::GetOrInsertT
-cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName)
+cmQtAutoMocUicT::ParseCacheT::GetOrInsertT
+cmQtAutoMocUicT::ParseCacheT::GetOrInsert(std::string const& fileName)
 {
   // Find existing entry
   {
@@ -75,15 +605,10 @@
   };
 }
 
-cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default;
-cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default;
+cmQtAutoMocUicT::ParseCacheT::ParseCacheT() = default;
+cmQtAutoMocUicT::ParseCacheT::~ParseCacheT() = default;
 
-void cmQtAutoMocUic::ParseCacheT::Clear()
-{
-  Map_.clear();
-}
-
-bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName)
+bool cmQtAutoMocUicT::ParseCacheT::ReadFromFile(std::string const& fileName)
 {
   cmsys::ifstream fin(fileName.c_str());
   if (!fin) {
@@ -146,7 +671,7 @@
   return true;
 }
 
-bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName)
+bool cmQtAutoMocUicT::ParseCacheT::WriteToFile(std::string const& fileName)
 {
   cmGeneratedFileStream ofs(fileName);
   if (!ofs) {
@@ -178,24 +703,24 @@
   return ofs.Close();
 }
 
-cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default;
-cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default;
+cmQtAutoMocUicT::BaseSettingsT::BaseSettingsT() = default;
+cmQtAutoMocUicT::BaseSettingsT::~BaseSettingsT() = default;
 
-cmQtAutoMocUic::MocSettingsT::MocSettingsT()
+cmQtAutoMocUicT::MocSettingsT::MocSettingsT()
 {
   RegExpInclude.compile(
     "(^|\n)[ \t]*#[ \t]*include[ \t]+"
     "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
 }
 
-cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default;
+cmQtAutoMocUicT::MocSettingsT::~MocSettingsT() = default;
 
-bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+bool cmQtAutoMocUicT::MocSettingsT::skipped(std::string const& fileName) const
 {
   return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
 }
 
-std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
+std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const
 {
   std::string res;
   const auto itB = MacroFilters.cbegin();
@@ -217,65 +742,56 @@
   return res;
 }
 
-cmQtAutoMocUic::UicSettingsT::UicSettingsT()
+cmQtAutoMocUicT::UicSettingsT::UicSettingsT()
 {
   RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
                         "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
 }
 
-cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default;
+cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default;
 
-bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
+bool cmQtAutoMocUicT::UicSettingsT::skipped(std::string const& fileName) const
 {
   return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
 }
 
-void cmQtAutoMocUic::JobT::LogError(GenT genType,
-                                    std::string const& message) const
+void cmQtAutoMocUicT::JobT::LogError(GenT genType,
+                                     cm::string_view message) const
 {
   Gen()->AbortError();
   Gen()->Log().Error(genType, message);
 }
 
-void cmQtAutoMocUic::JobT::LogFileError(GenT genType,
-                                        std::string const& filename,
-                                        std::string const& message) const
-{
-  Gen()->AbortError();
-  Gen()->Log().ErrorFile(genType, filename, message);
-}
-
-void cmQtAutoMocUic::JobT::LogCommandError(
-  GenT genType, std::string const& message,
+void cmQtAutoMocUicT::JobT::LogCommandError(
+  GenT genType, cm::string_view message,
   std::vector<std::string> const& command, std::string const& output) const
 {
   Gen()->AbortError();
   Gen()->Log().ErrorCommand(genType, message, command, output);
 }
 
-bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
-                                      cmWorkerPool::ProcessResultT& result,
-                                      std::vector<std::string> const& command,
-                                      std::string* infoMessage)
+bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType,
+                                       cmWorkerPool::ProcessResultT& result,
+                                       std::vector<std::string> const& command,
+                                       std::string* infoMessage)
 {
   // Log command
   if (Log().Verbose()) {
-    std::string msg;
-    if ((infoMessage != nullptr) && !infoMessage->empty()) {
-      msg = *infoMessage;
-      if (msg.back() != '\n') {
-        msg += '\n';
-      }
+    cm::string_view info;
+    if (infoMessage != nullptr) {
+      info = *infoMessage;
     }
-    msg += QuotedCommand(command);
-    msg += '\n';
-    Log().Info(genType, msg);
+    Log().Info(genType,
+               cmStrCat(info,
+                        info.empty() || cmHasSuffix(info, '\n') ? "" : "\n",
+                        QuotedCommand(command), '\n'));
   }
+  // Run command
   return cmWorkerPool::JobT::RunProcess(result, command,
                                         BaseConst().AutogenBuildDir);
 }
 
-void cmQtAutoMocUic::JobMocPredefsT::Process()
+void cmQtAutoMocUicT::JobMocPredefsT::Process()
 {
   // (Re)generate moc_predefs.h on demand
   std::unique_ptr<std::string> reason;
@@ -285,26 +801,23 @@
   if (!Update(reason.get())) {
     return;
   }
-  std::string const& predefsFileRel = MocConst().PredefsFileRel;
   std::string const& predefsFileAbs = MocConst().PredefsFileAbs;
   {
     cmWorkerPool::ProcessResultT result;
     {
       // Compose command
       std::vector<std::string> cmd = MocConst().PredefsCmd;
-      // Add includes
-      cmAppend(cmd, MocConst().Includes);
       // Add definitions
-      for (std::string const& def : MocConst().Definitions) {
-        cmd.emplace_back("-D" + def);
-      }
+      cmAppend(cmd, MocConst().OptionsDefinitions);
+      // Add includes
+      cmAppend(cmd, MocConst().OptionsIncludes);
       // Execute command
       if (!RunProcess(GenT::MOC, result, cmd, reason.get())) {
-        std::string msg = "The content generation command for ";
-        msg += Quoted(predefsFileRel);
-        msg += " failed.\n";
-        msg += result.ErrorMessage;
-        LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
+        LogCommandError(GenT::MOC,
+                        cmStrCat("The content generation command for ",
+                                 MessagePath(predefsFileAbs), " failed.\n",
+                                 result.ErrorMessage),
+                        cmd, result.StdOut);
         return;
       }
     }
@@ -312,22 +825,20 @@
     // (Re)write predefs file only on demand
     if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) {
       if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) {
-        std::string msg = "Writing ";
-        msg += Quoted(predefsFileRel);
-        msg += " failed.";
-        LogFileError(GenT::MOC, predefsFileAbs, msg);
+        LogError(
+          GenT::MOC,
+          cmStrCat("Writing ", MessagePath(predefsFileAbs), " failed."));
         return;
       }
     } else {
       // Touch to update the time stamp
       if (Log().Verbose()) {
-        Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel));
+        Log().Info(GenT::MOC, "Touching " + MessagePath(predefsFileAbs));
       }
       if (!cmSystemTools::Touch(predefsFileAbs, false)) {
-        std::string msg = "Touching ";
-        msg += Quoted(predefsFileAbs);
-        msg += " failed.";
-        LogFileError(GenT::MOC, predefsFileAbs, msg);
+        LogError(
+          GenT::MOC,
+          cmStrCat("Touching ", MessagePath(predefsFileAbs), " failed."));
         return;
       }
     }
@@ -335,19 +846,20 @@
 
   // Read file time afterwards
   if (!MocEval().PredefsTime.Load(predefsFileAbs)) {
-    LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed.");
+    LogError(GenT::MOC,
+             cmStrCat("Reading the file time of ", MessagePath(predefsFileAbs),
+                      " failed."));
     return;
   }
 }
 
-bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const
+bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const
 {
   // Test if the file exists
   if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(MocConst().PredefsFileRel);
-      *reason += ", because it doesn't exist.";
+      *reason = cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
+                         ", because it doesn't exist.");
     }
     return true;
   }
@@ -355,9 +867,8 @@
   // Test if the settings changed
   if (MocConst().SettingsChanged) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(MocConst().PredefsFileRel);
-      *reason += ", because the moc settings changed.";
+      *reason = cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
+                         ", because the moc settings changed.");
     }
     return true;
   }
@@ -369,11 +880,9 @@
     if (execTime.Load(exec)) {
       if (MocEval().PredefsTime.Older(execTime)) {
         if (reason != nullptr) {
-          *reason = "Generating ";
-          *reason += Quoted(MocConst().PredefsFileRel);
-          *reason += " because it is older than ";
-          *reason += Quoted(exec);
-          *reason += ".";
+          *reason =
+            cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
+                     " because it is older than ", MessagePath(exec), '.');
         }
         return true;
       }
@@ -383,34 +892,36 @@
   return false;
 }
 
-bool cmQtAutoMocUic::JobParseT::ReadFile()
+bool cmQtAutoMocUicT::JobParseT::ReadFile()
 {
   // Clear old parse information
   FileHandle->ParseData->Clear();
   std::string const& fileName = FileHandle->FileName;
   // Write info
   if (Log().Verbose()) {
-    Log().Info(GenT::GEN, "Parsing " + Quoted(fileName));
+    Log().Info(GenT::GEN, cmStrCat("Parsing ", MessagePath(fileName)));
   }
   // Read file content
   {
     std::string error;
     if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) {
-      LogFileError(GenT::GEN, fileName, "Could not read the file: " + error);
+      LogError(
+        GenT::GEN,
+        cmStrCat("Could not read ", MessagePath(fileName), ".\n", error));
       return false;
     }
   }
   // Warn if empty
   if (Content.empty()) {
-    Log().WarningFile(GenT::GEN, fileName, "The file is empty.");
+    Log().Warning(GenT::GEN, cmStrCat(MessagePath(fileName), " is empty."));
     return false;
   }
   return true;
 }
 
-void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container,
-                                           std::set<std::string> const& source,
-                                           std::size_t basePrefixLength)
+void cmQtAutoMocUicT::JobParseT::CreateKeys(
+  std::vector<IncludeKeyT>& container, std::set<std::string> const& source,
+  std::size_t basePrefixLength)
 {
   if (source.empty()) {
     return;
@@ -421,7 +932,7 @@
   }
 }
 
-void cmQtAutoMocUic::JobParseT::MocMacro()
+void cmQtAutoMocUicT::JobParseT::MocMacro()
 {
   for (KeyExpT const& filter : MocConst().MacroFilters) {
     // Run a simple find string check
@@ -438,7 +949,7 @@
   }
 }
 
-void cmQtAutoMocUic::JobParseT::MocDependecies()
+void cmQtAutoMocUicT::JobParseT::MocDependecies()
 {
   if (MocConst().DependFilters.empty()) {
     return;
@@ -479,7 +990,7 @@
   }
 }
 
-void cmQtAutoMocUic::JobParseT::MocIncludes()
+void cmQtAutoMocUicT::JobParseT::MocIncludes()
 {
   if (Content.find("moc") == std::string::npos) {
     return;
@@ -512,7 +1023,7 @@
   CreateKeys(Include.Dot, dot, 0);
 }
 
-void cmQtAutoMocUic::JobParseT::UicIncludes()
+void cmQtAutoMocUicT::JobParseT::UicIncludes()
 {
   if (Content.find("ui_") == std::string::npos) {
     return;
@@ -532,7 +1043,7 @@
   CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength);
 }
 
-void cmQtAutoMocUic::JobParseHeaderT::Process()
+void cmQtAutoMocUicT::JobParseHeaderT::Process()
 {
   if (!ReadFile()) {
     return;
@@ -548,7 +1059,7 @@
   }
 }
 
-void cmQtAutoMocUic::JobParseSourceT::Process()
+void cmQtAutoMocUicT::JobParseSourceT::Process()
 {
   if (!ReadFile()) {
     return;
@@ -565,37 +1076,35 @@
   }
 }
 
-void cmQtAutoMocUic::JobEvaluateT::Process()
+std::string cmQtAutoMocUicT::JobEvalCacheT::MessageSearchLocations() const
 {
-  // Evaluate for moc
-  if (MocConst().Enabled) {
-    // Evaluate headers
-    for (auto const& pair : BaseEval().Headers) {
-      if (!MocEvalHeader(pair.second)) {
-        return;
-      }
-    }
-    // Evaluate sources
-    for (auto const& pair : BaseEval().Sources) {
-      if (!MocEvalSource(pair.second)) {
-        return;
-      }
-    }
+  std::string res;
+  res.reserve(512);
+  for (std::string const& path : SearchLocations) {
+    res += "  ";
+    res += MessagePath(path);
+    res += '\n';
   }
-  // Evaluate for uic
-  if (UicConst().Enabled) {
-    if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) {
+  return res;
+}
+
+void cmQtAutoMocUicT::JobEvalCacheMocT::Process()
+{
+  // Evaluate headers
+  for (auto const& pair : BaseEval().Headers) {
+    if (!EvalHeader(pair.second)) {
       return;
     }
   }
-
-  // Add discovered header parse jobs
-  Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered);
-  // Add generate job after
-  Gen()->WorkerPool().EmplaceJob<JobGenerateT>();
+  // Evaluate sources
+  for (auto const& pair : BaseEval().Sources) {
+    if (!EvalSource(pair.second)) {
+      return;
+    }
+  }
 }
 
-bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source)
+bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalHeader(SourceFileHandleT source)
 {
   SourceFileT const& sourceFile = *source;
   auto const& parseData = sourceFile.ParseData->Moc;
@@ -616,13 +1125,13 @@
     }
 
     // Register mapping in headers map
-    MocRegisterMapping(handle, true);
+    RegisterMapping(handle);
   }
 
   return true;
 }
 
-bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource(
+bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalSource(
   SourceFileHandleT const& source)
 {
   SourceFileT const& sourceFile = *source;
@@ -633,7 +1142,7 @@
     return true;
   }
 
-  std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
+  std::string const sourceDirPrefix = SubDirPrefix(sourceFile.FileName);
   std::string const sourceBase =
     cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName);
 
@@ -660,33 +1169,30 @@
   // Check if this source needs to be moc processed but doesn't.
   if (!sourceIncludesDotMoc && !parseData.Macro.empty() &&
       !(relaxedMode && sourceIncludesMocUnderscore)) {
-    {
-      std::string emsg = "The file contains a ";
-      emsg += Quoted(parseData.Macro);
-      emsg += " macro, but does not include ";
-      emsg += Quoted(sourceBase + ".moc");
-      emsg += "!\nConsider to\n  - add #include \"";
-      emsg += sourceBase;
-      emsg += ".moc\"\n  - enable SKIP_AUTOMOC for this file";
-      LogFileError(GenT::MOC, sourceFile.FileName, emsg);
-    }
+    LogError(GenT::MOC,
+             cmStrCat(MessagePath(sourceFile.FileName), "\ncontains a ",
+                      Quoted(parseData.Macro), " macro, but does not include ",
+                      MessagePath(sourceBase + ".moc"),
+                      "!\nConsider to\n  - add #include \"", sourceBase,
+                      ".moc\"\n  - enable SKIP_AUTOMOC for this file"));
     return false;
   }
 
   // Evaluate "moc_" includes
   for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
-    std::string const headerBase = incKey.Dir + incKey.Base;
-    SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
-    if (!header) {
-      {
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += ",\nbut the header could not be found "
-               "in the following locations\n";
-        msg += MocMessageTestHeaders(headerBase);
-        LogFileError(GenT::MOC, sourceFile.FileName, msg);
+    SourceFileHandleT headerHandle;
+    {
+      std::string const headerBase = cmStrCat(incKey.Dir, incKey.Base);
+      if (!FindIncludedHeader(headerHandle, sourceDirPrefix, headerBase)) {
+        LogError(GenT::MOC,
+                 cmStrCat(MessagePath(sourceFile.FileName),
+                          "\nincludes the moc file ", MessagePath(incKey.Key),
+                          ",\nbut a header ", MessageHeader(headerBase),
+                          "\ncould not be found "
+                          "in the following directories\n",
+                          MessageSearchLocations()));
+        return false;
       }
-      return false;
     }
     // The include might be handled differently in relaxed mode
     if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() &&
@@ -696,35 +1202,32 @@
       // be generated from <BASE>.cpp instead of <BASE>.h, because otherwise
       // it won't build. But warn, since this is not how it is supposed to be
       // used. This is for KDE4 compatibility.
-      {
-        // Issue a warning
-        std::string msg = "The file contains a ";
-        msg += Quoted(parseData.Macro);
-        msg += " macro, but does not include ";
-        msg += Quoted(sourceBase + ".moc");
-        msg += ".\nInstead it includes ";
-        msg += Quoted(incKey.Key);
-        msg += ".\nRunning moc on the source\n  ";
-        msg += Quoted(sourceFile.FileName);
-        msg += "!\nBetter include ";
-        msg += Quoted(sourceBase + ".moc");
-        msg += " for compatibility with regular mode.\n";
-        msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
-        Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
-      }
+
+      // Issue a warning
+      Log().Warning(
+        GenT::MOC,
+        cmStrCat(MessagePath(sourceFile.FileName), "\ncontains a ",
+                 Quoted(parseData.Macro), " macro, but does not include ",
+                 MessagePath(sourceBase + ".moc"), ".\nInstead it includes ",
+                 MessagePath(incKey.Key), ".\nRunning moc on the source\n  ",
+                 MessagePath(sourceFile.FileName), "!\nBetter include ",
+                 MessagePath(sourceBase + ".moc"),
+                 " for compatibility with regular mode.\n",
+                 "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
+
       // Create mapping
-      if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
+      if (!RegisterIncluded(incKey.Key, source, source)) {
         return false;
       }
       continue;
     }
 
     // Check if header is skipped
-    if (MocConst().skipped(header->FileName)) {
+    if (MocConst().skipped(headerHandle->FileName)) {
       continue;
     }
     // Create mapping
-    if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
+    if (!RegisterIncluded(incKey.Key, source, std::move(headerHandle))) {
       return false;
     }
   }
@@ -737,57 +1240,60 @@
       bool const ownMoc = (incKey.Base == sourceBase);
       if (ownMoc && !parseData.Macro.empty()) {
         // Create mapping for the regular use case
-        if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
+        if (!RegisterIncluded(incKey.Key, source, source)) {
           return false;
         }
         continue;
       }
       // Try to find a header instead but issue a warning.
       // This is for KDE4 compatibility.
-      std::string const headerBase = incKey.Dir + incKey.Base;
-      SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
-      if (!header) {
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += ",\nwhich seems to be the moc file from a different source "
-               "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header"
-               "could not be found in the following locations\n";
-        msg += MocMessageTestHeaders(headerBase);
-        LogFileError(GenT::MOC, sourceFile.FileName, msg);
-        return false;
+      SourceFileHandleT headerHandle;
+      {
+        std::string const headerBase = cmStrCat(incKey.Dir, incKey.Base);
+        if (!FindIncludedHeader(headerHandle, sourceDirPrefix, headerBase)) {
+          LogError(
+            GenT::MOC,
+            cmStrCat(
+              MessagePath(sourceFile.FileName), "\nincludes the moc file ",
+              MessagePath(incKey.Key),
+              ",\nwhich seems to be the moc file from a different source "
+              "file.\nCMAKE_AUTOMOC_RELAXED_MODE:\nAlso a matching header ",
+              MessageHeader(headerBase),
+              "\ncould not be found in the following directories\n",
+              MessageSearchLocations()));
+          return false;
+        }
       }
       // Check if header is skipped
-      if (MocConst().skipped(header->FileName)) {
+      if (MocConst().skipped(headerHandle->FileName)) {
         continue;
       }
       // Issue a warning
       if (ownMoc && parseData.Macro.empty()) {
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += ", but does not contain a\n";
-        msg += MocConst().MacrosString();
-        msg += " macro.\nRunning moc on the header\n  ";
-        msg += Quoted(header->FileName);
-        msg += "!\nBetter include ";
-        msg += Quoted("moc_" + incKey.Base + ".cpp");
-        msg += " for a compatibility with regular mode.\n";
-        msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
-        Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
+        Log().Warning(
+          GenT::MOC,
+          cmStrCat(MessagePath(sourceFile.FileName),
+                   "\nincludes the moc file ", MessagePath(incKey.Key),
+                   ", but does not contain a\n", MocConst().MacrosString(),
+                   " macro.\nRunning moc on the header\n  ",
+                   MessagePath(headerHandle->FileName), "!\nBetter include ",
+                   MessagePath("moc_" + incKey.Base + ".cpp"),
+                   " for a compatibility with regular mode.\n",
+                   "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
       } else {
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += " instead of ";
-        msg += Quoted("moc_" + incKey.Base + ".cpp");
-        msg += ".\nRunning moc on the header\n  ";
-        msg += Quoted(header->FileName);
-        msg += "!\nBetter include ";
-        msg += Quoted("moc_" + incKey.Base + ".cpp");
-        msg += " for compatibility with regular mode.\n";
-        msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
-        Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
+        Log().Warning(
+          GenT::MOC,
+          cmStrCat(MessagePath(sourceFile.FileName),
+                   "\nincludes the moc file ", MessagePath(incKey.Key),
+                   " instead of ", MessagePath("moc_" + incKey.Base + ".cpp"),
+                   ".\nRunning moc on the header\n  ",
+                   MessagePath(headerHandle->FileName), "!\nBetter include ",
+                   MessagePath("moc_" + incKey.Base + ".cpp"),
+                   " for compatibility with regular mode.\n",
+                   "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
       }
       // Create mapping
-      if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
+      if (!RegisterIncluded(incKey.Key, source, std::move(headerHandle))) {
         return false;
       }
     }
@@ -798,26 +1304,26 @@
       bool const ownMoc = (incKey.Base == sourceBase);
       if (!ownMoc) {
         // Don't allow <BASE>.moc include other than own in regular mode
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += ",\nwhich seems to be the moc file from a different "
-               "source file.\nThis is not supported.  Include ";
-        msg += Quoted(sourceBase + ".moc");
-        msg += " to run moc on this source file.";
-        LogFileError(GenT::MOC, sourceFile.FileName, msg);
+        LogError(GenT::MOC,
+                 cmStrCat(MessagePath(sourceFile.FileName),
+                          "\nincludes the moc file ", MessagePath(incKey.Key),
+                          ",\nwhich seems to be the moc file from a different "
+                          "source file.\nThis is not supported.  Include ",
+                          MessagePath(sourceBase + ".moc"),
+                          " to run moc on this source file."));
         return false;
       }
       // Accept but issue a warning if moc isn't required
       if (parseData.Macro.empty()) {
-        std::string msg = "The file includes the moc file ";
-        msg += Quoted(incKey.Key);
-        msg += ", but does not contain a ";
-        msg += MocConst().MacrosString();
-        msg += " macro.";
-        Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
+        Log().Warning(GenT::MOC,
+                      cmStrCat(MessagePath(sourceFile.FileName),
+                               "\nincludes the moc file ",
+                               MessagePath(incKey.Key),
+                               ", but does not contain a ",
+                               MocConst().MacrosString(), " macro."));
       }
       // Create mapping
-      if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
+      if (!RegisterIncluded(incKey.Key, source, source)) {
         return false;
       }
     }
@@ -826,113 +1332,99 @@
   return true;
 }
 
-cmQtAutoMocUic::SourceFileHandleT
-cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader(
-  std::string const& includerDir, std::string const& includeBase) const
+bool cmQtAutoMocUicT::JobEvalCacheMocT::FindIncludedHeader(
+  SourceFileHandleT& headerHandle, cm::string_view includerDir,
+  cm::string_view includeBase)
 {
-  // Search in vicinity of the source
-  {
-    SourceFileHandleT res = MocFindHeader(includerDir + includeBase);
-    if (res) {
-      return res;
-    }
-  }
-  // Search in include directories
-  for (std::string const& path : MocConst().IncludePaths) {
-    std::string testPath = path;
-    testPath += '/';
-    testPath += includeBase;
-    SourceFileHandleT res = MocFindHeader(testPath);
-    if (res) {
-      return res;
-    }
-  }
-  // Return without success
-  return SourceFileHandleT();
-}
+  // Clear search locations
+  SearchLocations.clear();
 
-cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader(
-  std::string const& basePath) const
-{
-  std::string testPath;
-  testPath.reserve(basePath.size() + 8);
-  for (std::string const& ext : BaseConst().HeaderExtensions) {
-    testPath.clear();
-    testPath += basePath;
-    testPath += '.';
-    testPath += ext;
-    cmFileTime fileTime;
-    if (fileTime.Load(testPath)) {
-      // Compute real path of the file
-      testPath = cmSystemTools::GetRealPath(testPath);
+  auto findHeader = [this,
+                     &headerHandle](std::string const& basePath) -> bool {
+    bool found = false;
+    std::string const baseCollapsed =
+      this->Gen()->CollapseFullPathTS(cmStrCat(basePath, '.'));
+    for (std::string const& ext : this->BaseConst().HeaderExtensions) {
+      std::string const testPath = cmStrCat(baseCollapsed, ext);
+      cmFileTime fileTime;
+      if (!fileTime.Load(testPath)) {
+        // File not found
+        continue;
+      }
+
       // Return a known file if it exists already
       {
         auto it = BaseEval().Headers.find(testPath);
         if (it != BaseEval().Headers.end()) {
-          return it->second;
+          headerHandle = it->second;
+          found = true;
+          break;
         }
       }
+
       // Created and return discovered file entry
-      SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath];
-      if (!res) {
-        res = std::make_shared<SourceFileT>(testPath);
-        res->FileTime = fileTime;
-        res->Moc = true;
+      {
+        SourceFileHandleT& handle = MocEval().HeadersDiscovered[testPath];
+        if (!handle) {
+          handle = std::make_shared<SourceFileT>(testPath);
+          handle->FileTime = fileTime;
+          handle->IsHeader = true;
+          handle->Moc = true;
+        }
+        headerHandle = handle;
+        found = true;
+        break;
       }
-      return res;
+    }
+    if (!found) {
+      this->SearchLocations.emplace_back(
+        cmQtAutoGen::ParentDir(baseCollapsed));
+    }
+    return found;
+  };
+
+  // Search in vicinity of the source
+  if (findHeader(cmStrCat(includerDir, includeBase))) {
+    return true;
+  }
+  // Search in include directories
+  for (std::string const& path : MocConst().IncludePaths) {
+    if (findHeader(cmStrCat(path, '/', includeBase))) {
+      return true;
     }
   }
   // Return without success
-  return SourceFileHandleT();
+  return false;
 }
 
-std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders(
-  std::string const& fileBase) const
-{
-  std::ostringstream res;
-  {
-    std::string exts = ".{";
-    exts += cmJoin(BaseConst().HeaderExtensions, ",");
-    exts += '}';
-    // Compose result string
-    res << "  " << fileBase << exts << '\n';
-    for (std::string const& path : MocConst().IncludePaths) {
-      res << "  " << path << '/' << fileBase << exts << '\n';
-    }
-  }
-  return res.str();
-}
-
-bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded(
+bool cmQtAutoMocUicT::JobEvalCacheMocT::RegisterIncluded(
   std::string const& includeString, SourceFileHandleT includerFileHandle,
-  SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const
+  SourceFileHandleT sourceFileHandle) const
 {
   // Check if this file is already included
   MappingHandleT& handle = MocEval().Includes[includeString];
   if (handle) {
     // Check if the output file would be generated from different source files
     if (handle->SourceFile != sourceFileHandle) {
-      std::string msg = "The source files\n  ";
-      msg += Quoted(includerFileHandle->FileName);
-      msg += '\n';
+      std::string files =
+        cmStrCat("  ", MessagePath(includerFileHandle->FileName), '\n');
       for (auto const& item : handle->IncluderFiles) {
-        msg += "  ";
-        msg += Quoted(item->FileName);
-        msg += '\n';
+        files += cmStrCat("  ", MessagePath(item->FileName), '\n');
       }
-      msg += "contain the same include string ";
-      msg += Quoted(includeString);
-      msg += ", but\nthe moc file would be generated from different "
-             "source files\n  ";
-      msg += Quoted(sourceFileHandle->FileName);
-      msg += " and\n  ";
-      msg += Quoted(handle->SourceFile->FileName);
-      msg += ".\nConsider to\n"
-             "  - not include the \"moc_<NAME>.cpp\" file\n"
-             "  - add a directory prefix to a \"<NAME>.moc\" include "
-             "(e.g \"sub/<NAME>.moc\")\n"
-             "  - rename the source file(s)\n";
-      LogError(GenT::MOC, msg);
+      LogError(
+        GenT::MOC,
+        cmStrCat("The source files\n", files,
+                 "contain the same include string ",
+                 MessagePath(includeString),
+                 ", but\nthe moc file would be generated from different "
+                 "source files\n  ",
+                 MessagePath(sourceFileHandle->FileName), " and\n  ",
+                 MessagePath(handle->SourceFile->FileName),
+                 ".\nConsider to\n"
+                 "  - not include the \"moc_<NAME>.cpp\" file\n"
+                 "  - add a directory prefix to a \"<NAME>.moc\" include "
+                 "(e.g \"sub/<NAME>.moc\")\n"
+                 "  - rename the source file(s)\n"));
       return false;
     }
 
@@ -946,18 +1438,19 @@
   handle->IncludeString = includeString;
   handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
   handle->SourceFile = std::move(sourceFileHandle);
-  handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
+  handle->OutputFile = Gen()->AbsoluteIncludePath(includeString);
 
   // Register mapping in sources/headers map
-  MocRegisterMapping(handle, sourceIsHeader);
+  RegisterMapping(handle);
   return true;
 }
 
-void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping(
-  MappingHandleT mappingHandle, bool sourceIsHeader) const
+void cmQtAutoMocUicT::JobEvalCacheMocT::RegisterMapping(
+  MappingHandleT mappingHandle) const
 {
-  auto& regMap =
-    sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings;
+  auto& regMap = mappingHandle->SourceFile->IsHeader
+    ? MocEval().HeaderMappings
+    : MocEval().SourceMappings;
   // Check if source file already gets mapped
   auto& regHandle = regMap[mappingHandle->SourceFile->FileName];
   if (!regHandle) {
@@ -971,17 +1464,33 @@
   }
 }
 
-bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap)
+std::string cmQtAutoMocUicT::JobEvalCacheMocT::MessageHeader(
+  cm::string_view headerBase) const
 {
-  for (auto const& pair : fileMap) {
-    if (!UicEvalFile(pair.second)) {
-      return false;
-    }
-  }
-  return true;
+  return MessagePath(cmStrCat(
+    headerBase, ".{", cmJoin(this->BaseConst().HeaderExtensions, ","), '}'));
 }
 
-bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile(
+void cmQtAutoMocUicT::JobEvalCacheUicT::Process()
+{
+  // Prepare buffers
+  SearchLocations.reserve((UicConst().SearchPaths.size() + 1) * 2);
+
+  // Evaluate headers
+  for (auto const& pair : BaseEval().Headers) {
+    if (!EvalFile(pair.second)) {
+      return;
+    }
+  }
+  // Evaluate sources
+  for (auto const& pair : BaseEval().Sources) {
+    if (!EvalFile(pair.second)) {
+      return;
+    }
+  }
+}
+
+bool cmQtAutoMocUicT::JobEvalCacheUicT::EvalFile(
   SourceFileHandleT const& sourceFileHandle)
 {
   SourceFileT const& sourceFile = *sourceFileHandle;
@@ -990,17 +1499,25 @@
     return true;
   }
 
-  std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
+  std::string const sourceDirPrefix = SubDirPrefix(sourceFile.FileName);
   for (IncludeKeyT const& incKey : Include) {
-    // Find .ui file name
-    SourceFileHandleT uiFileHandle =
-      UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey);
-    if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) {
+    // Find .ui file
+    UiName = cmStrCat(incKey.Base, ".ui");
+    if (!FindIncludedUi(sourceDirPrefix, incKey.Dir)) {
+      LogError(GenT::UIC,
+               cmStrCat(MessagePath(sourceFile.FileName),
+                        "\nincludes the uic file ", MessagePath(incKey.Key),
+                        ",\nbut the user interface file ", MessagePath(UiName),
+                        "\ncould not be found in the following directories\n",
+                        MessageSearchLocations()));
+      return false;
+    }
+    // Check if the file is skipped
+    if (UicConst().skipped(UiFileHandle->FileName)) {
       continue;
     }
     // Register mapping
-    if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle),
-                            sourceFileHandle)) {
+    if (!RegisterMapping(incKey.Key, sourceFileHandle)) {
       return false;
     }
   }
@@ -1008,37 +1525,88 @@
   return true;
 }
 
-bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping(
-  std::string const& includeString, SourceFileHandleT uiFileHandle,
-  SourceFileHandleT includerFileHandle)
+bool cmQtAutoMocUicT::JobEvalCacheUicT::FindIncludedUi(
+  cm::string_view sourceDirPrefix, cm::string_view includePrefix)
+{
+  // Clear locations buffer
+  SearchLocations.clear();
+
+  auto findUi = [this](std::string const& testPath) -> bool {
+    std::string const fullPath = this->Gen()->CollapseFullPathTS(testPath);
+    cmFileTime fileTime;
+    if (!fileTime.Load(fullPath)) {
+      this->SearchLocations.emplace_back(cmQtAutoGen::ParentDir(fullPath));
+      return false;
+    }
+    // .ui file found in files system!
+    // Get or create .ui file handle
+    SourceFileHandleT& handle = this->UicEval().UiFiles[fullPath];
+    if (!handle) {
+      // The file wasn't registered, yet
+      handle = std::make_shared<SourceFileT>(fullPath);
+      handle->FileTime = fileTime;
+    }
+    this->UiFileHandle = handle;
+    return true;
+  };
+
+  // Vicinity of the source
+  if (findUi(cmStrCat(sourceDirPrefix, UiName))) {
+    return true;
+  }
+  if (!includePrefix.empty()) {
+    if (findUi(cmStrCat(sourceDirPrefix, includePrefix, UiName))) {
+      return true;
+    }
+  }
+  // Additional AUTOUIC search paths
+  auto const& searchPaths = UicConst().SearchPaths;
+  if (!searchPaths.empty()) {
+    for (std::string const& sPath : searchPaths) {
+      if (findUi(cmStrCat(sPath, '/', UiName))) {
+        return true;
+      }
+    }
+    if (!includePrefix.empty()) {
+      for (std::string const& sPath : searchPaths) {
+        if (findUi(cmStrCat(sPath, '/', includePrefix, UiName))) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+bool cmQtAutoMocUicT::JobEvalCacheUicT::RegisterMapping(
+  std::string const& includeString, SourceFileHandleT includerFileHandle)
 {
   auto& Includes = Gen()->UicEval().Includes;
   auto it = Includes.find(includeString);
   if (it != Includes.end()) {
     MappingHandleT const& handle = it->second;
-    if (handle->SourceFile != uiFileHandle) {
+    if (handle->SourceFile != UiFileHandle) {
       // The output file already gets generated - from a different .ui file!
-      std::string msg = "The source files\n  ";
-      msg += Quoted(includerFileHandle->FileName);
-      msg += '\n';
+      std::string files =
+        cmStrCat("  ", MessagePath(includerFileHandle->FileName), '\n');
       for (auto const& item : handle->IncluderFiles) {
-        msg += "  ";
-        msg += Quoted(item->FileName);
-        msg += '\n';
+        files += cmStrCat("  ", MessagePath(item->FileName), '\n');
       }
-      msg += "contain the same include string ";
-      msg += Quoted(includeString);
-      msg += ", but\nthe uic file would be generated from different "
-             "user interface files\n  ";
-      msg += Quoted(uiFileHandle->FileName);
-      msg += " and\n  ";
-      msg += Quoted(handle->SourceFile->FileName);
-      msg += ".\nConsider to\n"
-             "  - add a directory prefix to a \"ui_<NAME>.h\" include "
-             "(e.g \"sub/ui_<NAME>.h\")\n"
-             "  - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
-             "include(s)\n";
-      LogError(GenT::UIC, msg);
+      LogError(
+        GenT::UIC,
+        cmStrCat(
+          "The source files\n", files, "contain the same include string ",
+          Quoted(includeString),
+          ", but\nthe uic file would be generated from different "
+          "user interface files\n  ",
+          MessagePath(UiFileHandle->FileName), " and\n  ",
+          MessagePath(handle->SourceFile->FileName),
+          ".\nConsider to\n"
+          "  - add a directory prefix to a \"ui_<NAME>.h\" include "
+          "(e.g \"sub/ui_<NAME>.h\")\n"
+          "  - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+          "include(s)\n"));
       return false;
     }
     // Add includer file to existing mapping
@@ -1048,143 +1616,68 @@
     MappingHandleT handle = std::make_shared<MappingT>();
     handle->IncludeString = includeString;
     handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
-    handle->SourceFile = std::move(uiFileHandle);
-    handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
+    handle->SourceFile = UiFileHandle;
+    handle->OutputFile = Gen()->AbsoluteIncludePath(includeString);
     // Register mapping
     Includes.emplace(includeString, std::move(handle));
   }
   return true;
 }
 
-cmQtAutoMocUic::SourceFileHandleT
-cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi(
-  std::string const& sourceFile, std::string const& sourceDir,
-  IncludeKeyT const& incKey) const
+void cmQtAutoMocUicT::JobEvalCacheFinishT::Process()
 {
-  std::string searchFileName = incKey.Base;
-  searchFileName += ".ui";
-  // Collect search paths list
-  std::vector<std::string> testFiles;
+  // Add discovered header parse jobs
+  Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered);
+
+  // Add dependency probing jobs
   {
-    auto& searchPaths = UicConst().SearchPaths;
-    testFiles.reserve((searchPaths.size() + 1) * 2);
-
-    // Vicinity of the source
-    testFiles.emplace_back(sourceDir + searchFileName);
-    if (!incKey.Dir.empty()) {
-      std::string path = sourceDir;
-      path += incKey.Dir;
-      path += searchFileName;
-      testFiles.emplace_back(path);
+    // Add fence job to ensure all parsing has finished
+    Gen()->WorkerPool().EmplaceJob<JobFenceT>();
+    if (MocConst().Enabled) {
+      Gen()->WorkerPool().EmplaceJob<JobProbeDepsMocT>();
     }
-    // AUTOUIC search paths
-    if (!searchPaths.empty()) {
-      for (std::string const& sPath : searchPaths) {
-        std::string path = sPath;
-        path += '/';
-        path += searchFileName;
-        testFiles.emplace_back(std::move(path));
-      }
-      if (!incKey.Dir.empty()) {
-        for (std::string const& sPath : searchPaths) {
-          std::string path = sPath;
-          path += '/';
-          path += incKey.Dir;
-          path += searchFileName;
-          testFiles.emplace_back(std::move(path));
-        }
-      }
+    if (UicConst().Enabled) {
+      Gen()->WorkerPool().EmplaceJob<JobProbeDepsUicT>();
     }
+    // Add probe finish job
+    Gen()->WorkerPool().EmplaceJob<JobProbeDepsFinishT>();
   }
-
-  // Search for the .ui file!
-  for (std::string const& testFile : testFiles) {
-    cmFileTime fileTime;
-    if (fileTime.Load(testFile)) {
-      // .ui file found in files system!
-      std::string realPath = cmSystemTools::GetRealPath(testFile);
-      // Get or create .ui file handle
-      SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath];
-      if (!handle) {
-        // The file wasn't registered, yet
-        handle = std::make_shared<SourceFileT>(realPath);
-        handle->FileTime = fileTime;
-      }
-      return handle;
-    }
-  }
-
-  // Log error
-  {
-    std::string msg = "The file includes the uic file ";
-    msg += Quoted(incKey.Key);
-    msg += ",\nbut the user interface file ";
-    msg += Quoted(searchFileName);
-    msg += "\ncould not be found in the following locations\n";
-    for (std::string const& testFile : testFiles) {
-      msg += "  ";
-      msg += Quoted(testFile);
-      msg += '\n';
-    }
-    LogFileError(GenT::UIC, sourceFile, msg);
-  }
-
-  return SourceFileHandleT();
 }
 
-void cmQtAutoMocUic::JobGenerateT::Process()
+void cmQtAutoMocUicT::JobProbeDepsMocT::Process()
 {
-  // Add moc compile jobs
-  if (MocConst().Enabled) {
-    for (auto const& pair : MocEval().HeaderMappings) {
-      // Register if this mapping is a candidate for mocs_compilation.cpp
-      bool const compFile = pair.second->IncludeString.empty();
-      if (compFile) {
-        MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath);
-      }
-      if (!MocGenerate(pair.second, compFile)) {
-        return;
-      }
+  // Create moc header jobs
+  for (auto const& pair : MocEval().HeaderMappings) {
+    // Register if this mapping is a candidate for mocs_compilation.cpp
+    bool const compFile = pair.second->IncludeString.empty();
+    if (compFile) {
+      MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath);
     }
-    for (auto const& pair : MocEval().SourceMappings) {
-      if (!MocGenerate(pair.second, false)) {
-        return;
-      }
-    }
-
-    // Add mocs compilations job on demand
-    Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
-  }
-
-  // Add uic compile jobs
-  if (UicConst().Enabled) {
-    for (auto const& pair : Gen()->UicEval().Includes) {
-      if (!UicGenerate(pair.second)) {
-        return;
-      }
+    if (!Generate(pair.second, compFile)) {
+      return;
     }
   }
 
-  // Add finish job
-  Gen()->WorkerPool().EmplaceJob<JobFinishT>();
+  // Create moc source jobs
+  for (auto const& pair : MocEval().SourceMappings) {
+    if (!Generate(pair.second, false)) {
+      return;
+    }
+  }
 }
 
-bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping,
-                                               bool compFile) const
+bool cmQtAutoMocUicT::JobProbeDepsMocT::Generate(MappingHandleT const& mapping,
+                                                 bool compFile) const
 {
   std::unique_ptr<std::string> reason;
   if (Log().Verbose()) {
     reason = cm::make_unique<std::string>();
   }
-  if (MocUpdate(*mapping, reason.get())) {
-    // Create the parent directory
-    if (!MakeParentDirectory(mapping->OutputFile)) {
-      LogFileError(GenT::MOC, mapping->OutputFile,
-                   "Could not create parent directory.");
-      return false;
-    }
+  if (Probe(*mapping, reason.get())) {
+    // Register the parent directory for creation
+    MocEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile));
     // Add moc job
-    Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason));
+    Gen()->WorkerPool().EmplaceJob<JobCompileMocT>(mapping, std::move(reason));
     // Check if a moc job for a mocs_compilation.cpp entry was generated
     if (compFile) {
       MocEval().CompUpdated = true;
@@ -1193,8 +1686,8 @@
   return true;
 }
 
-bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping,
-                                             std::string* reason) const
+bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping,
+                                              std::string* reason) const
 {
   std::string const& sourceFile = mapping.SourceFile->FileName;
   std::string const& outputFile = mapping.OutputFile;
@@ -1203,10 +1696,9 @@
   cmFileTime outputFileTime;
   if (!outputFileTime.Load(outputFile)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because it doesn't exist, from ";
-      *reason += Quoted(sourceFile);
+      *reason =
+        cmStrCat("Generating ", MessagePath(outputFile),
+                 ", because it doesn't exist, from ", MessagePath(sourceFile));
     }
     return true;
   }
@@ -1214,10 +1706,9 @@
   // Test if any setting changed
   if (MocConst().SettingsChanged) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because the uic settings changed, from ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         ", because the uic settings changed, from ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1225,10 +1716,9 @@
   // Test if the source file is newer
   if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because it's older than its source file, from ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         ", because it's older than its source file, from ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1237,12 +1727,10 @@
   if (!MocConst().PredefsFileAbs.empty()) {
     if (outputFileTime.Older(MocEval().PredefsTime)) {
       if (reason != nullptr) {
-        *reason = "Generating ";
-        *reason += Quoted(outputFile);
-        *reason += ", because it's older than ";
-        *reason += Quoted(MocConst().PredefsFileAbs);
-        *reason += ", from ";
-        *reason += Quoted(sourceFile);
+        *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                           ", because it's older than ",
+                           MessagePath(MocConst().PredefsFileAbs), ", from ",
+                           MessagePath(sourceFile));
       }
       return true;
     }
@@ -1251,10 +1739,9 @@
   // Test if the moc executable is newer
   if (outputFileTime.Older(MocConst().ExecutableTime)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because it's older than the moc executable, from ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         ", because it's older than the moc executable, from ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1265,21 +1752,21 @@
     std::string const sourceDir = SubDirPrefix(sourceFile);
     for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) {
       // Find dependency file
-      auto const depMatch = MocFindDependency(sourceDir, dep);
+      auto const depMatch = FindDependency(sourceDir, dep);
       if (depMatch.first.empty()) {
-        Log().WarningFile(GenT::MOC, sourceFile,
-                          "Could not find dependency file " + Quoted(dep));
+        Log().Warning(GenT::MOC,
+                      cmStrCat(MessagePath(sourceFile), " depends on ",
+                               MessagePath(dep),
+                               " but the file does not exist."));
         continue;
       }
       // Test if dependency file is older
       if (outputFileTime.Older(depMatch.second)) {
         if (reason != nullptr) {
-          *reason = "Generating ";
-          *reason += Quoted(outputFile);
-          *reason += ", because it's older than its dependency file ";
-          *reason += Quoted(depMatch.first);
-          *reason += ", from ";
-          *reason += Quoted(sourceFile);
+          *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                             ", because it's older than its dependency file ",
+                             MessagePath(depMatch.first), ", from ",
+                             MessagePath(sourceFile));
         }
         return true;
       }
@@ -1290,10 +1777,10 @@
 }
 
 std::pair<std::string, cmFileTime>
-cmQtAutoMocUic::JobGenerateT::MocFindDependency(
+cmQtAutoMocUicT::JobProbeDepsMocT::FindDependency(
   std::string const& sourceDir, std::string const& includeString) const
 {
-  typedef std::pair<std::string, cmFileTime> ResPair;
+  using ResPair = std::pair<std::string, cmFileTime>;
   // Search in vicinity of the source
   {
     ResPair res{ sourceDir + includeString, {} };
@@ -1303,9 +1790,7 @@
   }
   // Search in include directories
   for (std::string const& includePath : MocConst().IncludePaths) {
-    ResPair res{ includePath, {} };
-    res.first += '/';
-    res.first += includeString;
+    ResPair res{ cmStrCat(includePath, '/', includeString), {} };
     if (res.second.Load(res.first)) {
       return res;
     }
@@ -1314,28 +1799,27 @@
   return ResPair();
 }
 
-bool cmQtAutoMocUic::JobGenerateT::UicGenerate(
-  MappingHandleT const& mapping) const
+void cmQtAutoMocUicT::JobProbeDepsUicT::Process()
 {
-  std::unique_ptr<std::string> reason;
-  if (Log().Verbose()) {
-    reason = cm::make_unique<std::string>();
-  }
-  if (UicUpdate(*mapping, reason.get())) {
-    // Create the parent directory
-    if (!MakeParentDirectory(mapping->OutputFile)) {
-      LogFileError(GenT::UIC, mapping->OutputFile,
-                   "Could not create parent directory.");
-      return false;
+  for (auto const& pair : Gen()->UicEval().Includes) {
+    MappingHandleT const& mapping = pair.second;
+    std::unique_ptr<std::string> reason;
+    if (Log().Verbose()) {
+      reason = cm::make_unique<std::string>();
     }
+    if (!Probe(*mapping, reason.get())) {
+      continue;
+    }
+
+    // Register the parent directory for creation
+    UicEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile));
     // Add uic job
-    Gen()->WorkerPool().EmplaceJob<JobUicT>(mapping, std::move(reason));
+    Gen()->WorkerPool().EmplaceJob<JobCompileUicT>(mapping, std::move(reason));
   }
-  return true;
 }
 
-bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping,
-                                             std::string* reason) const
+bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping,
+                                              std::string* reason) const
 {
   std::string const& sourceFile = mapping.SourceFile->FileName;
   std::string const& outputFile = mapping.OutputFile;
@@ -1344,10 +1828,9 @@
   cmFileTime outputFileTime;
   if (!outputFileTime.Load(outputFile)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because it doesn't exist, from ";
-      *reason += Quoted(sourceFile);
+      *reason =
+        cmStrCat("Generating ", MessagePath(outputFile),
+                 ", because it doesn't exist, from ", MessagePath(sourceFile));
     }
     return true;
   }
@@ -1355,10 +1838,9 @@
   // Test if the uic settings changed
   if (UicConst().SettingsChanged) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because the uic settings changed, from ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         ", because the uic settings changed, from ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1366,10 +1848,9 @@
   // Test if the source file is newer
   if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += " because it's older than the source file ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         " because it's older than the source file ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1377,10 +1858,9 @@
   // Test if the uic executable is newer
   if (outputFileTime.Older(UicConst().ExecutableTime)) {
     if (reason != nullptr) {
-      *reason = "Generating ";
-      *reason += Quoted(outputFile);
-      *reason += ", because it's older than the uic executable, from ";
-      *reason += Quoted(sourceFile);
+      *reason = cmStrCat("Generating ", MessagePath(outputFile),
+                         ", because it's older than the uic executable, from ",
+                         MessagePath(sourceFile));
     }
     return true;
   }
@@ -1388,24 +1868,93 @@
   return false;
 }
 
-void cmQtAutoMocUic::JobMocT::Process()
+void cmQtAutoMocUicT::JobProbeDepsFinishT::Process()
+{
+  // Create output directories
+  {
+    using StringSet = std::unordered_set<std::string>;
+    auto createDirs = [this](GenT genType, StringSet const& dirSet) {
+      for (std::string const& dirName : dirSet) {
+        if (!cmSystemTools::MakeDirectory(dirName)) {
+          this->LogError(
+            genType,
+            cmStrCat("Creating directory ", MessagePath(dirName), " failed."));
+          return;
+        }
+      }
+    };
+    if (MocConst().Enabled && UicConst().Enabled) {
+      StringSet outputDirs = MocEval().OutputDirs;
+      outputDirs.insert(UicEval().OutputDirs.begin(),
+                        UicEval().OutputDirs.end());
+      createDirs(GenT::GEN, outputDirs);
+    } else if (MocConst().Enabled) {
+      createDirs(GenT::MOC, MocEval().OutputDirs);
+    } else if (UicConst().Enabled) {
+      createDirs(GenT::UIC, UicEval().OutputDirs);
+    }
+  }
+
+  if (MocConst().Enabled) {
+    // Add mocs compilations job
+    Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
+  }
+
+  // Add finish job
+  Gen()->WorkerPool().EmplaceJob<JobFinishT>();
+}
+
+void cmQtAutoMocUicT::JobCompileMocT::Process()
 {
   std::string const& sourceFile = Mapping->SourceFile->FileName;
   std::string const& outputFile = Mapping->OutputFile;
 
   // Compose moc command
   std::vector<std::string> cmd;
-  cmd.push_back(MocConst().Executable);
-  // Add options
-  cmAppend(cmd, MocConst().AllOptions);
-  // Add predefs include
-  if (!MocConst().PredefsFileAbs.empty()) {
-    cmd.emplace_back("--include");
-    cmd.push_back(MocConst().PredefsFileAbs);
+  {
+    // Reserve large enough
+    cmd.reserve(MocConst().OptionsDefinitions.size() +
+                MocConst().OptionsIncludes.size() +
+                MocConst().OptionsExtra.size() + 16);
+    cmd.push_back(MocConst().Executable);
+    // Add definitions
+    cmAppend(cmd, MocConst().OptionsDefinitions);
+    // Add includes
+    cmAppend(cmd, MocConst().OptionsIncludes);
+    // Add predefs include
+    if (!MocConst().PredefsFileAbs.empty()) {
+      cmd.emplace_back("--include");
+      cmd.push_back(MocConst().PredefsFileAbs);
+    }
+    // Add path prefix on demand
+    if (MocConst().PathPrefix && Mapping->SourceFile->IsHeader) {
+      for (std::string const& dir : MocConst().IncludePaths) {
+        cm::string_view prefix = sourceFile;
+        if (cmHasPrefix(prefix, dir)) {
+          prefix.remove_prefix(dir.size());
+          if (cmHasPrefix(prefix, '/')) {
+            prefix.remove_prefix(1);
+            auto slashPos = prefix.rfind('/');
+            if (slashPos != cm::string_view::npos) {
+              cmd.emplace_back("-p");
+              cmd.emplace_back(prefix.substr(0, slashPos));
+            } else {
+              cmd.emplace_back("-p");
+              cmd.emplace_back("./");
+            }
+            break;
+          }
+        }
+      }
+    }
+    // Add extra options
+    cmAppend(cmd, MocConst().OptionsExtra);
+    // Add output file
+    cmd.emplace_back("-o");
+    cmd.push_back(outputFile);
+    // Add source file
+    cmd.push_back(sourceFile);
   }
-  cmd.emplace_back("-o");
-  cmd.push_back(outputFile);
-  cmd.push_back(sourceFile);
 
   // Execute moc command
   cmWorkerPool::ProcessResultT result;
@@ -1416,26 +1965,23 @@
     }
   } else {
     // Moc command failed
-    std::string msg = "The moc process failed to compile\n  ";
-    msg += Quoted(sourceFile);
-    msg += "\ninto\n  ";
-    msg += Quoted(outputFile);
-    if (Mapping->IncluderFiles.empty()) {
-      msg += ".\n";
-    } else {
-      msg += "\nincluded by\n";
+    std::string includers;
+    if (!Mapping->IncluderFiles.empty()) {
+      includers = "included by\n";
       for (auto const& item : Mapping->IncluderFiles) {
-        msg += "  ";
-        msg += Quoted(item->FileName);
-        msg += '\n';
+        includers += cmStrCat("  ", MessagePath(item->FileName), '\n');
       }
     }
-    msg += result.ErrorMessage;
-    LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
+    LogCommandError(GenT::MOC,
+                    cmStrCat("The moc process failed to compile\n  ",
+                             MessagePath(sourceFile), "\ninto\n  ",
+                             MessagePath(outputFile), '\n', includers,
+                             result.ErrorMessage),
+                    cmd, result.StdOut);
   }
 }
 
-void cmQtAutoMocUic::JobUicT::Process()
+void cmQtAutoMocUicT::JobCompileUicT::Process()
 {
   std::string const& sourceFile = Mapping->SourceFile->FileName;
   std::string const& outputFile = Mapping->OutputFile;
@@ -1444,10 +1990,10 @@
   std::vector<std::string> cmd;
   cmd.push_back(UicConst().Executable);
   {
-    std::vector<std::string> allOpts = UicConst().TargetOptions;
-    auto optionIt = UicConst().Options.find(sourceFile);
-    if (optionIt != UicConst().Options.end()) {
-      UicMergeOptions(allOpts, optionIt->second,
+    std::vector<std::string> allOpts = UicConst().Options;
+    auto optionIt = UicConst().UiFiles.find(sourceFile);
+    if (optionIt != UicConst().UiFiles.end()) {
+      UicMergeOptions(allOpts, optionIt->second.Options,
                       (BaseConst().QtVersionMajor == 5));
     }
     cmAppend(cmd, allOpts);
@@ -1465,22 +2011,20 @@
     }
   } else {
     // Uic command failed
-    std::string msg = "The uic process failed to compile\n  ";
-    msg += Quoted(sourceFile);
-    msg += "\ninto\n  ";
-    msg += Quoted(outputFile);
-    msg += "\nincluded by\n";
+    std::string includers;
     for (auto const& item : Mapping->IncluderFiles) {
-      msg += "  ";
-      msg += Quoted(item->FileName);
-      msg += '\n';
+      includers += cmStrCat("  ", MessagePath(item->FileName), '\n');
     }
-    msg += result.ErrorMessage;
-    LogCommandError(GenT::UIC, msg, cmd, result.StdOut);
+    LogCommandError(GenT::UIC,
+                    cmStrCat("The uic process failed to compile\n  ",
+                             MessagePath(sourceFile), "\ninto\n  ",
+                             MessagePath(outputFile), "\nincluded by\n",
+                             includers, result.ErrorMessage),
+                    cmd, result.StdOut);
   }
 }
 
-void cmQtAutoMocUic::JobMocsCompilationT::Process()
+void cmQtAutoMocUicT::JobMocsCompilationT::Process()
 {
   // Compose mocs compilation file content
   std::string content =
@@ -1489,427 +2033,354 @@
   if (MocEval().CompFiles.empty()) {
     // Placeholder content
     content += "// No files found that require moc or the moc files are "
-               "included\n";
-    content += "enum some_compilers { need_more_than_nothing };\n";
+               "included\n"
+               "enum some_compilers { need_more_than_nothing };\n";
   } else {
     // Valid content
-    char const clampB = BaseConst().MultiConfig ? '<' : '"';
-    char const clampE = BaseConst().MultiConfig ? '>' : '"';
-    for (std::string const& mocfile : MocEval().CompFiles) {
-      content += "#include ";
-      content += clampB;
-      content += mocfile;
-      content += clampE;
-      content += '\n';
-    }
+    const bool mc = BaseConst().MultiConfig;
+    cm::string_view const wrapFront = mc ? "#include <" : "#include \"";
+    cm::string_view const wrapBack = mc ? ">\n" : "\"\n";
+    content += cmWrap(wrapFront, MocEval().CompFiles, wrapBack, "");
   }
 
   std::string const& compAbs = MocConst().CompFileAbs;
   if (cmQtAutoGenerator::FileDiffers(compAbs, content)) {
     // Actually write mocs compilation file
     if (Log().Verbose()) {
-      Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
+      Log().Info(GenT::MOC,
+                 "Generating MOC compilation " + MessagePath(compAbs));
     }
     if (!FileWrite(compAbs, content)) {
-      LogFileError(GenT::MOC, compAbs,
-                   "mocs compilation file writing failed.");
+      LogError(GenT::MOC,
+               cmStrCat("Writing MOC compilation ", MessagePath(compAbs),
+                        " failed."));
     }
   } else if (MocEval().CompUpdated) {
     // Only touch mocs compilation file
     if (Log().Verbose()) {
-      Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+      Log().Info(GenT::MOC,
+                 "Touching MOC compilation " + MessagePath(compAbs));
     }
     if (!cmSystemTools::Touch(compAbs, false)) {
-      LogFileError(GenT::MOC, compAbs,
-                   "mocs compilation file touching failed.");
+      LogError(GenT::MOC,
+               cmStrCat("Touching MOC compilation ", MessagePath(compAbs),
+                        " failed."));
     }
   }
 }
 
-void cmQtAutoMocUic::JobFinishT::Process()
+void cmQtAutoMocUicT::JobFinishT::Process()
 {
   Gen()->AbortSuccess();
 }
 
-cmQtAutoMocUic::cmQtAutoMocUic() = default;
-cmQtAutoMocUic::~cmQtAutoMocUic() = default;
-
-bool cmQtAutoMocUic::Init(cmMakefile* makefile)
+cmQtAutoMocUicT::cmQtAutoMocUicT()
+  : cmQtAutoGenerator(GenT::GEN)
 {
-  // Utility lambdas
-  auto InfoGet = [makefile](const char* key) {
-    return makefile->GetSafeDefinition(key);
-  };
-  auto InfoGetBool = [makefile](const char* key) {
-    return makefile->IsOn(key);
-  };
-  auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
-    return list;
-  };
-  auto InfoGetLists =
-    [makefile](const char* key) -> std::vector<std::vector<std::string>> {
-    std::vector<std::vector<std::string>> lists;
-    {
-      std::string const value = makefile->GetSafeDefinition(key);
-      std::string::size_type pos = 0;
-      while (pos < value.size()) {
-        std::string::size_type next = value.find(ListSep, pos);
-        std::string::size_type length =
-          (next != std::string::npos) ? next - pos : value.size() - pos;
-        // Remove enclosing braces
-        if (length >= 2) {
-          std::string::const_iterator itBeg = value.begin() + (pos + 1);
-          std::string::const_iterator itEnd = itBeg + (length - 2);
-          {
-            std::string subValue(itBeg, itEnd);
-            std::vector<std::string> list;
-            cmSystemTools::ExpandListArgument(subValue, list);
-            lists.push_back(std::move(list));
-          }
-        }
-        pos += length;
-        pos += ListSep.size();
-      }
-    }
-    return lists;
-  };
-  auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
-    const char* valueConf = nullptr;
-    {
-      std::string keyConf = key;
-      keyConf += '_';
-      keyConf += InfoConfig();
-      valueConf = makefile->GetDefinition(keyConf);
-    }
-    if (valueConf == nullptr) {
-      return makefile->GetSafeDefinition(key);
-    }
-    return std::string(valueConf);
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](const char* key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
-    return list;
-  };
-  auto LogInfoError = [this](std::string const& msg) -> bool {
-    std::ostringstream err;
-    err << "In " << Quoted(this->InfoFile()) << ":\n" << msg;
-    this->Log().Error(GenT::GEN, err.str());
+}
+cmQtAutoMocUicT::~cmQtAutoMocUicT() = default;
+
+bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
+{
+  // -- Required settings
+  if (!info.GetBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) ||
+      !info.GetUInt("QT_VERSION_MAJOR", BaseConst_.QtVersionMajor, true) ||
+      !info.GetUInt("PARALLEL", BaseConst_.ThreadCount, false) ||
+      !info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) ||
+      !info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir,
+                            true) ||
+      !info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
+      !info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile,
+                            true) ||
+      !info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
+      !info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
+      !info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
+      !info.GetString("QT_UIC_EXECUTABLE", UicConst_.Executable, false)) {
     return false;
-  };
-  auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB,
-                                    std::size_t sizeA,
-                                    std::size_t sizeB) -> bool {
-    if (sizeA == sizeB) {
-      return true;
-    }
-    std::ostringstream err;
-    err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB
-        << '(' << sizeB << ')';
-    return LogInfoError(err.str());
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    return LogInfoError("File processing failed");
   }
 
-  // -- Meta
-  Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY"));
-  BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
-  {
-    unsigned long num = 1;
-    if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
-      num = std::max<unsigned long>(num, 1);
-      num = std::min<unsigned long>(num, ParallelMax);
-    }
-    WorkerPool_.SetThreadCount(static_cast<unsigned int>(num));
-  }
-  BaseConst_.HeaderExtensions =
-    makefile->GetCMakeInstance()->GetHeaderExtensions();
-
-  // - Files and directories
-  BaseConst_.IncludeProjectDirsBefore =
-    InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
-  BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
-  BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
-  BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
-  BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
-  BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
-  if (BaseConst_.AutogenBuildDir.empty()) {
-    return LogInfoError("Autogen build directory missing.");
-  }
-  BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
-  if (BaseConst_.AutogenIncludeDir.empty()) {
-    return LogInfoError("Autogen include directory missing.");
-  }
-  BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE");
-  if (BaseConst_.CMakeExecutable.empty()) {
-    return LogInfoError("CMake executable file name missing.");
-  }
+  // -- Checks
   if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) {
-    std::string error = "The CMake executable ";
-    error += Quoted(BaseConst_.CMakeExecutable);
-    error += " does not exist.";
-    return LogInfoError(error);
-  }
-  BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE");
-  if (BaseConst_.ParseCacheFile.empty()) {
-    return LogInfoError("Parse cache file name missing.");
+    return info.LogError(cmStrCat("The CMake executable ",
+                                  MessagePath(BaseConst_.CMakeExecutable),
+                                  " does not exist."));
   }
 
-  // - Settings file
-  SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
-  if (SettingsFile_.empty()) {
-    return LogInfoError("Settings file name missing.");
-  }
+  // -- Evaluate values
+  BaseConst_.ThreadCount = std::min(BaseConst_.ThreadCount, ParallelMax);
+  WorkerPool_.SetThreadCount(BaseConst_.ThreadCount);
 
-  // - Qt environment
-  {
-    unsigned long qtv = BaseConst_.QtVersionMajor;
-    if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
-                                     &qtv)) {
-      BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv);
-    }
-  }
-
-  // - Moc
-  MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
-  if (!MocConst().Executable.empty()) {
+  // -- Moc
+  if (!MocConst_.Executable.empty()) {
+    // -- Moc is enabled
     MocConst_.Enabled = true;
-    // Load the executable file time
-    if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
-      std::string error = "The moc executable ";
-      error += Quoted(MocConst_.Executable);
-      error += " does not exist.";
-      return LogInfoError(error);
+
+    // -- Temporary buffers
+    struct
+    {
+      std::vector<std::string> MacroNames;
+      std::vector<std::string> DependFilters;
+    } tmp;
+
+    // -- Required settings
+    if (!info.GetBool("MOC_RELAXED_MODE", MocConst_.RelaxedMode, false) ||
+        !info.GetBool("MOC_PATH_PREFIX", MocConst_.PathPrefix, true) ||
+        !info.GetArray("MOC_SKIP", MocConst_.SkipList, false) ||
+        !info.GetArrayConfig("MOC_DEFINITIONS", MocConst_.Definitions,
+                             false) ||
+        !info.GetArrayConfig("MOC_INCLUDES", MocConst_.IncludePaths, false) ||
+        !info.GetArray("MOC_OPTIONS", MocConst_.OptionsExtra, false) ||
+        !info.GetStringConfig("MOC_COMPILATION_FILE", MocConst_.CompFileAbs,
+                              true) ||
+        !info.GetArray("MOC_PREDEFS_CMD", MocConst_.PredefsCmd, false) ||
+        !info.GetStringConfig("MOC_PREDEFS_FILE", MocConst_.PredefsFileAbs,
+                              !MocConst_.PredefsCmd.empty()) ||
+        !info.GetArray("MOC_MACRO_NAMES", tmp.MacroNames, true) ||
+        !info.GetArray("MOC_DEPEND_FILTERS", tmp.DependFilters, false)) {
+      return false;
     }
-    for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
-      MocConst_.SkipList.insert(std::move(sfl));
-    }
-    MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
-    MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
-    MocConst_.Options = InfoGetList("AM_MOC_OPTIONS");
-    MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
-    for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+
+    // -- Evaluate settings
+    for (std::string const& item : tmp.MacroNames) {
       MocConst_.MacroFilters.emplace_back(
         item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
     }
+    // Dependency filters
     {
-      auto addFilter = [this, &LogInfoError](std::string const& key,
-                                             std::string const& exp) -> bool {
-        auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool {
-          std::ostringstream ferr;
-          ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n';
-          ferr << "  Key: " << Quoted(key) << '\n';
-          ferr << "  Exp: " << Quoted(exp) << '\n';
-          return LogInfoError(ferr.str());
+      Json::Value const& val = info.GetValue("MOC_DEPEND_FILTERS");
+      if (!val.isArray()) {
+        return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array.");
+      }
+      Json::ArrayIndex const arraySize = val.size();
+      for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+        // Test entry closure
+        auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
+          if (!test) {
+            info.LogError(
+              cmStrCat("MOC_DEPEND_FILTERS filter ", ii, ": ", msg));
+          }
+          return !test;
         };
-        if (key.empty()) {
-          return filterErr("Key is empty");
+
+        Json::Value const& pairVal = val[ii];
+
+        if (testEntry(pairVal.isArray(), "JSON value is not an array.") ||
+            testEntry(pairVal.size() == 2, "JSON array size invalid.")) {
+          return false;
         }
-        if (exp.empty()) {
-          return filterErr("Regular expression is empty");
+
+        Json::Value const& keyVal = pairVal[0u];
+        Json::Value const& expVal = pairVal[1u];
+        if (testEntry(keyVal.isString(),
+                      "JSON value for keyword is not a string.") ||
+            testEntry(expVal.isString(),
+                      "JSON value for regular expression is not a string.")) {
+          return false;
         }
+
+        std::string const key = keyVal.asString();
+        std::string const exp = expVal.asString();
+        if (testEntry(!key.empty(), "Keyword is empty.") ||
+            testEntry(!exp.empty(), "Regular expression is empty.")) {
+          return false;
+        }
+
         this->MocConst_.DependFilters.emplace_back(key, exp);
-        if (!this->MocConst_.DependFilters.back().Exp.is_valid()) {
-          return filterErr("Regular expression compiling failed");
+        if (testEntry(
+              this->MocConst_.DependFilters.back().Exp.is_valid(),
+              cmStrCat("Regular expression compilation failed.\nKeyword: ",
+                       Quoted(key), "\nExpression: ", Quoted(exp)))) {
+          return false;
         }
-        return true;
+      }
+    }
+    // Check if moc executable exists (by reading the file time)
+    if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
+      return info.LogError(cmStrCat("The moc executable ",
+                                    MessagePath(MocConst_.Executable),
+                                    " does not exist."));
+    }
+  }
+
+  // -- Uic
+  if (!UicConst_.Executable.empty()) {
+    // Uic is enabled
+    UicConst_.Enabled = true;
+
+    // -- Required settings
+    if (!info.GetArray("UIC_SKIP", UicConst_.SkipList, false) ||
+        !info.GetArray("UIC_SEARCH_PATHS", UicConst_.SearchPaths, false) ||
+        !info.GetArrayConfig("UIC_OPTIONS", UicConst_.Options, false)) {
+      return false;
+    }
+    // .ui files
+    {
+      Json::Value const& val = info.GetValue("UIC_UI_FILES");
+      if (!val.isArray()) {
+        return info.LogError("UIC_UI_FILES JSON value is not an array.");
+      }
+      Json::ArrayIndex const arraySize = val.size();
+      for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+        // Test entry closure
+        auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
+          if (!test) {
+            info.LogError(cmStrCat("UIC_UI_FILES entry ", ii, ": ", msg));
+          }
+          return !test;
+        };
+
+        Json::Value const& entry = val[ii];
+        if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+            testEntry(entry.size() == 2, "JSON array size invalid.")) {
+          return false;
+        }
+
+        Json::Value const& entryName = entry[0u];
+        Json::Value const& entryOptions = entry[1u];
+        if (testEntry(entryName.isString(),
+                      "JSON value for name is not a string.") ||
+            testEntry(entryOptions.isArray(),
+                      "JSON value for options is not an array.")) {
+          return false;
+        }
+
+        auto& uiFile = UicConst_.UiFiles[entryName.asString()];
+        InfoT::GetJsonArray(uiFile.Options, entryOptions);
+      }
+    }
+
+    // -- Evaluate settings
+    // Check if uic executable exists (by reading the file time)
+    if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
+      return info.LogError(cmStrCat("The uic executable ",
+                                    MessagePath(UicConst_.Executable),
+                                    " does not exist."));
+    }
+  }
+
+  // -- Headers
+  {
+    Json::Value const& val = info.GetValue("HEADERS");
+    if (!val.isArray()) {
+      return info.LogError("HEADERS JSON value is not an array.");
+    }
+    Json::ArrayIndex const arraySize = val.size();
+    for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+      // Test entry closure
+      auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
+        if (!test) {
+          info.LogError(cmStrCat("HEADERS entry ", ii, ": ", msg));
+        }
+        return !test;
       };
 
-      // Insert default filter for Q_PLUGIN_METADATA
-      if (BaseConst().QtVersionMajor != 4) {
-        if (!addFilter("Q_PLUGIN_METADATA",
-                       "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
-                       "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) {
-          return false;
-        }
-      }
-      // Insert user defined dependency filters
-      std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
-      if ((flts.size() % 2) != 0) {
-        return LogInfoError(
-          "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
-      }
-      for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) {
-        if (!addFilter(*itC, *(itC + 1))) {
-          return false;
-        }
-      }
-    }
-    MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
-  }
-
-  // - Uic
-  UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
-  if (!UicConst().Executable.empty()) {
-    UicConst_.Enabled = true;
-    // Load the executable file time
-    if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
-      std::string error = "The uic executable ";
-      error += Quoted(UicConst_.Executable);
-      error += " does not exist.";
-      return LogInfoError(error);
-    }
-    for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
-      UicConst_.SkipList.insert(std::move(sfl));
-    }
-    UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
-    UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
-    {
-      const char* keyFiles = "AM_UIC_OPTIONS_FILES";
-      const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS";
-      auto sources = InfoGetList(keyFiles);
-      auto options = InfoGetLists(keyOpts);
-      if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) {
+      Json::Value const& entry = val[ii];
+      if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+          testEntry(entry.size() == 3, "JSON array size invalid.")) {
         return false;
       }
-      auto fitEnd = sources.cend();
-      auto fit = sources.begin();
-      auto oit = options.begin();
-      while (fit != fitEnd) {
-        UicConst_.Options[*fit] = std::move(*oit);
-        ++fit;
-        ++oit;
-      }
-    }
-  }
 
-  // - Headers and sources
-  {
-    auto makeSource =
-      [&LogInfoError](std::string const& fileName,
-                      std::string const& fileFlags) -> SourceFileHandleT {
-      if (fileFlags.size() != 2) {
-        LogInfoError("Invalid file flags string size");
-        return SourceFileHandleT();
+      Json::Value const& entryName = entry[0u];
+      Json::Value const& entryFlags = entry[1u];
+      Json::Value const& entryBuild = entry[2u];
+      if (testEntry(entryName.isString(),
+                    "JSON value for name is not a string.") ||
+          testEntry(entryFlags.isString(),
+                    "JSON value for flags is not a string.") ||
+          testEntry(entryBuild.isString(),
+                    "JSON value for build path is not a string.")) {
+        return false;
       }
+
+      std::string name = entryName.asString();
+      std::string flags = entryFlags.asString();
+      std::string build = entryBuild.asString();
+      if (testEntry(flags.size() == 2, "Invalid flags string size")) {
+        return false;
+      }
+
       cmFileTime fileTime;
-      if (!fileTime.Load(fileName)) {
-        LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) +
-                     " does not exist.");
-        return SourceFileHandleT();
+      if (!fileTime.Load(name)) {
+        return info.LogError(cmStrCat(
+          "The header file ", this->MessagePath(name), " does not exist."));
       }
-      SourceFileHandleT sfh = std::make_shared<SourceFileT>(fileName);
-      sfh->FileTime = fileTime;
-      sfh->Moc = (fileFlags[0] == 'M');
-      sfh->Uic = (fileFlags[1] == 'U');
-      return sfh;
-    };
 
-    // Headers
-    {
-      // Get file lists
-      const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS";
-      std::vector<std::string> files = InfoGetList(keyFiles);
-      std::vector<std::string> flags = InfoGetList(keyFlags);
-      std::vector<std::string> builds;
-      if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
-        return false;
-      }
-      if (MocConst().Enabled) {
-        const char* keyPaths = "AM_HEADERS_BUILD_PATHS";
-        builds = InfoGetList(keyPaths);
-        if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) {
-          return false;
+      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
+      sourceHandle->FileTime = fileTime;
+      sourceHandle->IsHeader = true;
+      sourceHandle->Moc = (flags[0] == 'M');
+      sourceHandle->Uic = (flags[1] == 'U');
+      if (sourceHandle->Moc && MocConst().Enabled) {
+        if (build.empty()) {
+          return info.LogError(
+            cmStrCat("Header file ", ii, " build path is empty"));
         }
+        sourceHandle->BuildPath = std::move(build);
       }
-      // Process file lists
-      for (std::size_t ii = 0; ii != files.size(); ++ii) {
-        std::string& fileName(files[ii]);
-        SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
-        if (!sfh) {
-          return false;
-        }
-        if (MocConst().Enabled) {
-          sfh->BuildPath = std::move(builds[ii]);
-          if (sfh->BuildPath.empty()) {
-            Log().ErrorFile(GenT::GEN, this->InfoFile(),
-                            "Header file build path is empty");
-            return false;
-          }
-        }
-        BaseEval().Headers.emplace(std::move(fileName), std::move(sfh));
-      }
-    }
-
-    // Sources
-    {
-      const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS";
-      std::vector<std::string> files = InfoGetList(keyFiles);
-      std::vector<std::string> flags = InfoGetList(keyFlags);
-      if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
-        return false;
-      }
-      // Process file lists
-      for (std::size_t ii = 0; ii != files.size(); ++ii) {
-        std::string& fileName(files[ii]);
-        SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
-        if (!sfh) {
-          return false;
-        }
-        BaseEval().Sources.emplace(std::move(fileName), std::move(sfh));
-      }
+      BaseEval().Headers.emplace(std::move(name), std::move(sourceHandle));
     }
   }
 
-  // Init derived information
-  // ------------------------
+  // -- Sources
+  {
+    Json::Value const& val = info.GetValue("SOURCES");
+    if (!val.isArray()) {
+      return info.LogError("SOURCES JSON value is not an array.");
+    }
+    Json::ArrayIndex const arraySize = val.size();
+    for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+      // Test entry closure
+      auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
+        if (!test) {
+          info.LogError(cmStrCat("SOURCES entry ", ii, ": ", msg));
+        }
+        return !test;
+      };
 
+      Json::Value const& entry = val[ii];
+      if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+          testEntry(entry.size() == 2, "JSON array size invalid.")) {
+        return false;
+      }
+
+      Json::Value const& entryName = entry[0u];
+      Json::Value const& entryFlags = entry[1u];
+      if (testEntry(entryName.isString(),
+                    "JSON value for name is not a string.") ||
+          testEntry(entryFlags.isString(),
+                    "JSON value for flags is not a string.")) {
+        return false;
+      }
+
+      std::string name = entryName.asString();
+      std::string flags = entryFlags.asString();
+      if (testEntry(flags.size() == 2, "Invalid flags string size")) {
+        return false;
+      }
+
+      cmFileTime fileTime;
+      if (!fileTime.Load(name)) {
+        return info.LogError(cmStrCat(
+          "The source file ", this->MessagePath(name), " does not exist."));
+      }
+
+      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
+      sourceHandle->FileTime = fileTime;
+      sourceHandle->IsHeader = false;
+      sourceHandle->Moc = (flags[0] == 'M');
+      sourceHandle->Uic = (flags[1] == 'U');
+      BaseEval().Sources.emplace(std::move(name), std::move(sourceHandle));
+    }
+  }
+
+  // -- Init derived information
   // Moc variables
   if (MocConst().Enabled) {
-    // Mocs compilation file
-    MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp");
-
-    // Moc predefs file
-    if (!MocConst_.PredefsCmd.empty()) {
-      MocConst_.PredefsFileRel = "moc_predefs";
-      if (BaseConst_.MultiConfig) {
-        MocConst_.PredefsFileRel += '_';
-        MocConst_.PredefsFileRel += InfoConfig();
-      }
-      MocConst_.PredefsFileRel += ".h";
-      MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel);
-    }
-
-    // Sort include directories on demand
-    if (BaseConst().IncludeProjectDirsBefore) {
-      // Move strings to temporary list
-      std::list<std::string> includes(MocConst().IncludePaths.begin(),
-                                      MocConst().IncludePaths.end());
-      MocConst_.IncludePaths.clear();
-      MocConst_.IncludePaths.reserve(includes.size());
-      // Append project directories only
-      {
-        std::array<std::string const*, 2> const movePaths = {
-          { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir }
-        };
-        for (std::string const* ppath : movePaths) {
-          std::list<std::string>::iterator it = includes.begin();
-          while (it != includes.end()) {
-            std::string const& path = *it;
-            if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
-              MocConst_.IncludePaths.push_back(path);
-              it = includes.erase(it);
-            } else {
-              ++it;
-            }
-          }
-        }
-      }
-      // Append remaining directories
-      MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(),
-                                    includes.begin(), includes.end());
-    }
     // Compose moc includes list
     {
+      // Compute framework paths
       std::set<std::string> frameworkPaths;
       for (std::string const& path : MocConst().IncludePaths) {
-        MocConst_.Includes.push_back("-I" + path);
         // Extract framework path
         if (cmHasLiteralSuffix(path, ".framework/Headers")) {
           // Go up twice to get to the framework root
@@ -1919,26 +2390,26 @@
             pathComponents.begin(), pathComponents.end() - 2));
         }
       }
+      // Reserve options
+      MocConst_.OptionsIncludes.reserve(MocConst().IncludePaths.size() +
+                                        frameworkPaths.size() * 2);
+      // Append includes
+      for (std::string const& path : MocConst().IncludePaths) {
+        MocConst_.OptionsIncludes.emplace_back("-I" + path);
+      }
       // Append framework includes
       for (std::string const& path : frameworkPaths) {
-        MocConst_.Includes.emplace_back("-F");
-        MocConst_.Includes.push_back(path);
+        MocConst_.OptionsIncludes.emplace_back("-F");
+        MocConst_.OptionsIncludes.push_back(path);
       }
     }
-    // Setup single list with all options
+
+    // Compose moc definitions list
     {
-      // Add includes
-      MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
-                                  MocConst().Includes.begin(),
-                                  MocConst().Includes.end());
-      // Add definitions
+      MocConst_.OptionsDefinitions.reserve(MocConst().Definitions.size());
       for (std::string const& def : MocConst().Definitions) {
-        MocConst_.AllOptions.push_back("-D" + def);
+        MocConst_.OptionsDefinitions.emplace_back("-D" + def);
       }
-      // Add options
-      MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
-                                  MocConst().Options.begin(),
-                                  MocConst().Options.end());
     }
   }
 
@@ -1946,7 +2417,7 @@
 }
 
 template <class JOBTYPE>
-void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap)
+void cmQtAutoMocUicT::CreateParseJobs(SourceFileMapT const& sourceMap)
 {
   cmFileTime const parseCacheTime = BaseEval().ParseCacheTime;
   ParseCacheT& parseCache = BaseEval().ParseCache;
@@ -1962,21 +2433,41 @@
   }
 }
 
-void cmQtAutoMocUic::InitJobs()
+/** Concurrently callable implementation of cmSystemTools::CollapseFullPath */
+std::string cmQtAutoMocUicT::CollapseFullPathTS(std::string const& path) const
+{
+  std::lock_guard<std::mutex> guard(CMakeLibMutex_);
+  return cmSystemTools::CollapseFullPath(path, ProjectDirs().CurrentSource);
+}
+
+void cmQtAutoMocUicT::InitJobs()
 {
   // Add moc_predefs.h job
   if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) {
     WorkerPool().EmplaceJob<JobMocPredefsT>();
   }
+
   // Add header parse jobs
   CreateParseJobs<JobParseHeaderT>(BaseEval().Headers);
   // Add source parse jobs
   CreateParseJobs<JobParseSourceT>(BaseEval().Sources);
-  // Add evaluate job
-  WorkerPool().EmplaceJob<JobEvaluateT>();
+
+  // Add parse cache evaluations jobs
+  {
+    // Add a fence job to ensure all parsing has finished
+    WorkerPool().EmplaceJob<JobFenceT>();
+    if (MocConst().Enabled) {
+      WorkerPool().EmplaceJob<JobEvalCacheMocT>();
+    }
+    if (UicConst().Enabled) {
+      WorkerPool().EmplaceJob<JobEvalCacheUicT>();
+    }
+    // Add evaluate job
+    WorkerPool().EmplaceJob<JobEvalCacheFinishT>();
+  }
 }
 
-bool cmQtAutoMocUic::Process()
+bool cmQtAutoMocUicT::Process()
 {
   SettingsFileRead();
   ParseCacheRead();
@@ -1999,26 +2490,30 @@
   return true;
 }
 
-void cmQtAutoMocUic::SettingsFileRead()
+void cmQtAutoMocUicT::SettingsFileRead()
 {
   // Compose current settings strings
   {
     cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256);
-    std::string const sep(";");
-    auto cha = [&cryptoHash, &sep](std::string const& value) {
+    auto cha = [&cryptoHash](cm::string_view value) {
       cryptoHash.Append(value);
-      cryptoHash.Append(sep);
+      cryptoHash.Append(";");
     };
 
     if (MocConst_.Enabled) {
       cryptoHash.Initialize();
       cha(MocConst().Executable);
-      for (auto const& value : MocConst().AllOptions) {
-        cha(value);
+      for (auto const& item : MocConst().OptionsDefinitions) {
+        cha(item);
       }
-      cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE");
-      for (auto const& value : MocConst().PredefsCmd) {
-        cha(value);
+      for (auto const& item : MocConst().OptionsIncludes) {
+        cha(item);
+      }
+      for (auto const& item : MocConst().OptionsExtra) {
+        cha(item);
+      }
+      for (auto const& item : MocConst().PredefsCmd) {
+        cha(item);
       }
       for (auto const& filter : MocConst().DependFilters) {
         cha(filter.Key);
@@ -2032,14 +2527,11 @@
     if (UicConst().Enabled) {
       cryptoHash.Initialize();
       cha(UicConst().Executable);
-      for (auto const& value : UicConst().TargetOptions) {
-        cha(value);
-      }
-      for (const auto& item : UicConst().Options) {
+      std::for_each(UicConst().Options.begin(), UicConst().Options.end(), cha);
+      for (const auto& item : UicConst().UiFiles) {
         cha(item.first);
-        for (auto const& svalue : item.second) {
-          cha(svalue);
-        }
+        auto const& opts = item.second.Options;
+        std::for_each(opts.begin(), opts.end(), cha);
       }
       SettingsStringUic_ = cryptoHash.FinalizeHex();
     }
@@ -2077,23 +2569,22 @@
   }
 }
 
-bool cmQtAutoMocUic::SettingsFileWrite()
+bool cmQtAutoMocUicT::SettingsFileWrite()
 {
   // Only write if any setting changed
   if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
     if (Log().Verbose()) {
-      Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
+      Log().Info(
+        GenT::GEN,
+        cmStrCat("Writing the settings file ", MessagePath(SettingsFile_)));
     }
     // Compose settings file content
     std::string content;
     {
-      auto SettingAppend = [&content](const char* key,
-                                      std::string const& value) {
+      auto SettingAppend = [&content](cm::string_view key,
+                                      cm::string_view value) {
         if (!value.empty()) {
-          content += key;
-          content += ':';
-          content += value;
-          content += '\n';
+          content += cmStrCat(key, ':', value, '\n');
         }
       };
       SettingAppend("moc", SettingsStringMoc_);
@@ -2102,8 +2593,9 @@
     // Write settings file
     std::string error;
     if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) {
-      Log().ErrorFile(GenT::GEN, SettingsFile_,
-                      "Settings file writing failed. " + error);
+      Log().Error(GenT::GEN,
+                  cmStrCat("Writing the settings file ",
+                           MessagePath(SettingsFile_), " failed.\n", error));
       // Remove old settings file to trigger a full rebuild on the next run
       cmSystemTools::RemoveFile(SettingsFile_);
       return false;
@@ -2112,9 +2604,9 @@
   return true;
 }
 
-void cmQtAutoMocUic::ParseCacheRead()
+void cmQtAutoMocUicT::ParseCacheRead()
 {
-  const char* reason = nullptr;
+  cm::string_view reason;
   // Don't read the cache if it is invalid
   if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) {
     reason = "Refreshing parse cache because it doesn't exist.";
@@ -2126,7 +2618,7 @@
       "Refreshing parse cache because it is older than the CMake executable.";
   }
 
-  if (reason != nullptr) {
+  if (!reason.empty()) {
     // Don't read but refresh the complete parse cache
     if (Log().Verbose()) {
       Log().Info(GenT::GEN, reason);
@@ -2138,35 +2630,39 @@
   }
 }
 
-bool cmQtAutoMocUic::ParseCacheWrite()
+bool cmQtAutoMocUicT::ParseCacheWrite()
 {
   if (BaseEval().ParseCacheChanged) {
     if (Log().Verbose()) {
       Log().Info(GenT::GEN,
-                 "Writing parse cache file " +
-                   Quoted(BaseConst().ParseCacheFile));
+                 cmStrCat("Writing the parse cache file ",
+                          MessagePath(BaseConst().ParseCacheFile)));
     }
     if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) {
-      Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile,
-                      "Parse cache file writing failed.");
+      Log().Error(GenT::GEN,
+                  cmStrCat("Writing the parse cache file ",
+                           MessagePath(BaseConst().ParseCacheFile),
+                           " failed."));
       return false;
     }
   }
   return true;
 }
 
-bool cmQtAutoMocUic::CreateDirectories()
+bool cmQtAutoMocUicT::CreateDirectories()
 {
   // Create AUTOGEN include directory
   if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) {
-    Log().ErrorFile(GenT::GEN, BaseConst().AutogenIncludeDir,
-                    "Could not create directory.");
+    Log().Error(GenT::GEN,
+                cmStrCat("Creating the AUTOGEN include directory ",
+                         MessagePath(BaseConst().AutogenIncludeDir),
+                         " failed."));
     return false;
   }
   return true;
 }
 
-void cmQtAutoMocUic::Abort(bool error)
+void cmQtAutoMocUicT::Abort(bool error)
 {
   if (error) {
     JobError_.store(true);
@@ -2174,20 +2670,21 @@
   WorkerPool_.Abort();
 }
 
-std::string cmQtAutoMocUic::AbsoluteBuildPath(
-  std::string const& relativePath) const
+std::string cmQtAutoMocUicT::AbsoluteBuildPath(
+  cm::string_view relativePath) const
 {
-  std::string res(BaseConst().AutogenBuildDir);
-  res += '/';
-  res += relativePath;
-  return res;
+  return cmStrCat(BaseConst().AutogenBuildDir, '/', relativePath);
 }
 
-std::string cmQtAutoMocUic::AbsoluteIncludePath(
-  std::string const& relativePath) const
+std::string cmQtAutoMocUicT::AbsoluteIncludePath(
+  cm::string_view relativePath) const
 {
-  std::string res(BaseConst().AutogenIncludeDir);
-  res += '/';
-  res += relativePath;
-  return res;
+  return cmStrCat(BaseConst().AutogenIncludeDir, '/', relativePath);
+}
+
+} // End of unnamed namespace
+
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config)
+{
+  return cmQtAutoMocUicT().Run(infoFile, config);
 }
diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h
index 81546cc..ffcc2db 100644
--- a/Source/cmQtAutoMocUic.h
+++ b/Source/cmQtAutoMocUic.h
@@ -5,572 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileTime.h"
-#include "cmQtAutoGen.h"
-#include "cmQtAutoGenerator.h"
-#include "cmWorkerPool.h"
-#include "cmsys/RegularExpression.hxx"
+#include <cm/string_view>
 
-#include <atomic>
-#include <cstddef>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-class cmMakefile;
-
-/** \class cmQtAutoMocUic
- * \brief AUTOMOC and AUTOUIC generator
+/**
+ * Process AUTOMOC and AUTOUIC
+ * @return true on success
  */
-class cmQtAutoMocUic : public cmQtAutoGenerator
-{
-public:
-  cmQtAutoMocUic();
-  ~cmQtAutoMocUic() override;
-
-  cmQtAutoMocUic(cmQtAutoMocUic const&) = delete;
-  cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete;
-
-public:
-  // -- Types
-
-  /**
-   * Search key plus regular expression pair
-   */
-  struct KeyExpT
-  {
-    KeyExpT() = default;
-
-    KeyExpT(const char* key, const char* exp)
-      : Key(key)
-      , Exp(exp)
-    {
-    }
-
-    KeyExpT(std::string key, std::string const& exp)
-      : Key(std::move(key))
-      , Exp(exp)
-    {
-    }
-
-    std::string Key;
-    cmsys::RegularExpression Exp;
-  };
-
-  /**
-   * Include string with sub parts
-   */
-  struct IncludeKeyT
-  {
-    IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
-
-    std::string Key;  // Full include string
-    std::string Dir;  // Include directory
-    std::string Base; // Base part of the include file name
-  };
-
-  /**
-   * Source file parsing cache
-   */
-  class ParseCacheT
-  {
-  public:
-    // -- Types
-    /**
-     * Entry of the file parsing cache
-     */
-    struct FileT
-    {
-      void Clear();
-
-      struct MocT
-      {
-        std::string Macro;
-        struct IncludeT
-        {
-          std::vector<IncludeKeyT> Underscore;
-          std::vector<IncludeKeyT> Dot;
-        } Include;
-        std::vector<std::string> Depends;
-      } Moc;
-
-      struct UicT
-      {
-        std::vector<IncludeKeyT> Include;
-        std::vector<std::string> Depends;
-      } Uic;
-    };
-    typedef std::shared_ptr<FileT> FileHandleT;
-    typedef std::pair<FileHandleT, bool> GetOrInsertT;
-
-  public:
-    ParseCacheT();
-    ~ParseCacheT();
-
-    void Clear();
-
-    bool ReadFromFile(std::string const& fileName);
-    bool WriteToFile(std::string const& fileName);
-
-    //! Might return an invalid handle
-    FileHandleT Get(std::string const& fileName) const;
-    //! Always returns a valid handle
-    GetOrInsertT GetOrInsert(std::string const& fileName);
-
-  private:
-    std::unordered_map<std::string, FileHandleT> Map_;
-  };
-
-  /**
-   * Source file data
-   */
-  class SourceFileT
-  {
-  public:
-    SourceFileT(std::string fileName)
-      : FileName(std::move(fileName))
-    {
-    }
-
-  public:
-    std::string FileName;
-    cmFileTime FileTime;
-    ParseCacheT::FileHandleT ParseData;
-    std::string BuildPath;
-    bool Moc = false;
-    bool Uic = false;
-  };
-  typedef std::shared_ptr<SourceFileT> SourceFileHandleT;
-  typedef std::map<std::string, SourceFileHandleT> SourceFileMapT;
-
-  /**
-   * Meta compiler file mapping information
-   */
-  struct MappingT
-  {
-    SourceFileHandleT SourceFile;
-    std::string OutputFile;
-    std::string IncludeString;
-    std::vector<SourceFileHandleT> IncluderFiles;
-  };
-  typedef std::shared_ptr<MappingT> MappingHandleT;
-  typedef std::map<std::string, MappingHandleT> MappingMapT;
-
-  /**
-   * Common settings
-   */
-  class BaseSettingsT
-  {
-  public:
-    // -- Constructors
-    BaseSettingsT();
-    ~BaseSettingsT();
-
-    BaseSettingsT(BaseSettingsT const&) = delete;
-    BaseSettingsT& operator=(BaseSettingsT const&) = delete;
-
-    // -- Attributes
-    // - Config
-    bool MultiConfig = false;
-    bool IncludeProjectDirsBefore = false;
-    unsigned int QtVersionMajor = 4;
-    // - Directories
-    std::string ProjectSourceDir;
-    std::string ProjectBinaryDir;
-    std::string CurrentSourceDir;
-    std::string CurrentBinaryDir;
-    std::string AutogenBuildDir;
-    std::string AutogenIncludeDir;
-    // - Files
-    std::string CMakeExecutable;
-    cmFileTime CMakeExecutableTime;
-    std::string ParseCacheFile;
-    std::vector<std::string> HeaderExtensions;
-  };
-
-  /**
-   * Shared common variables
-   */
-  class BaseEvalT
-  {
-  public:
-    // -- Parse Cache
-    bool ParseCacheChanged = false;
-    cmFileTime ParseCacheTime;
-    ParseCacheT ParseCache;
-
-    // -- Sources
-    SourceFileMapT Headers;
-    SourceFileMapT Sources;
-  };
-
-  /**
-   * Moc settings
-   */
-  class MocSettingsT
-  {
-  public:
-    // -- Constructors
-    MocSettingsT();
-    ~MocSettingsT();
-
-    MocSettingsT(MocSettingsT const&) = delete;
-    MocSettingsT& operator=(MocSettingsT const&) = delete;
-
-    // -- Const methods
-    bool skipped(std::string const& fileName) const;
-    std::string MacrosString() const;
-
-    // -- Attributes
-    bool Enabled = false;
-    bool SettingsChanged = false;
-    bool RelaxedMode = false;
-    cmFileTime ExecutableTime;
-    std::string Executable;
-    std::string CompFileAbs;
-    std::string PredefsFileRel;
-    std::string PredefsFileAbs;
-    std::unordered_set<std::string> SkipList;
-    std::vector<std::string> IncludePaths;
-    std::vector<std::string> Includes;
-    std::vector<std::string> Definitions;
-    std::vector<std::string> Options;
-    std::vector<std::string> AllOptions;
-    std::vector<std::string> PredefsCmd;
-    std::vector<KeyExpT> DependFilters;
-    std::vector<KeyExpT> MacroFilters;
-    cmsys::RegularExpression RegExpInclude;
-  };
-
-  /**
-   * Moc shared variables
-   */
-  class MocEvalT
-  {
-  public:
-    // -- predefines file
-    cmFileTime PredefsTime;
-    // -- Mappings
-    MappingMapT HeaderMappings;
-    MappingMapT SourceMappings;
-    MappingMapT Includes;
-    // -- Discovered files
-    SourceFileMapT HeadersDiscovered;
-    // -- Mocs compilation
-    bool CompUpdated = false;
-    std::vector<std::string> CompFiles;
-  };
-
-  /**
-   * Uic settings
-   */
-  class UicSettingsT
-  {
-  public:
-    UicSettingsT();
-    ~UicSettingsT();
-
-    UicSettingsT(UicSettingsT const&) = delete;
-    UicSettingsT& operator=(UicSettingsT const&) = delete;
-
-    // -- Const methods
-    bool skipped(std::string const& fileName) const;
-
-    // -- Attributes
-    bool Enabled = false;
-    bool SettingsChanged = false;
-    cmFileTime ExecutableTime;
-    std::string Executable;
-    std::unordered_set<std::string> SkipList;
-    std::vector<std::string> TargetOptions;
-    std::map<std::string, std::vector<std::string>> Options;
-    std::vector<std::string> SearchPaths;
-    cmsys::RegularExpression RegExpInclude;
-  };
-
-  /**
-   * Uic shared variables
-   */
-  class UicEvalT
-  {
-  public:
-    SourceFileMapT UiFiles;
-    MappingMapT Includes;
-  };
-
-  /**
-   * Abstract job class for concurrent job processing
-   */
-  class JobT : public cmWorkerPool::JobT
-  {
-  protected:
-    /**
-     * @brief Protected default constructor
-     */
-    JobT(bool fence = false)
-      : cmWorkerPool::JobT(fence)
-    {
-    }
-
-    //! Get the generator. Only valid during Process() call!
-    cmQtAutoMocUic* Gen() const
-    {
-      return static_cast<cmQtAutoMocUic*>(UserData());
-    };
-
-    // -- Accessors. Only valid during Process() call!
-    Logger const& Log() const { return Gen()->Log(); }
-    BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); }
-    BaseEvalT& BaseEval() const { return Gen()->BaseEval(); }
-    MocSettingsT const& MocConst() const { return Gen()->MocConst(); }
-    MocEvalT& MocEval() const { return Gen()->MocEval(); }
-    UicSettingsT const& UicConst() const { return Gen()->UicConst(); }
-    UicEvalT& UicEval() const { return Gen()->UicEval(); }
-
-    // -- Error logging with automatic abort
-    void LogError(GenT genType, std::string const& message) const;
-    void LogFileError(GenT genType, std::string const& filename,
-                      std::string const& message) const;
-    void LogCommandError(GenT genType, std::string const& message,
-                         std::vector<std::string> const& command,
-                         std::string const& output) const;
-
-    /**
-     * @brief Run an external process. Use only during Process() call!
-     */
-    bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
-                    std::vector<std::string> const& command,
-                    std::string* infoMessage = nullptr);
-  };
-
-  /**
-   * Fence job utility class
-   */
-  class JobFenceT : public JobT
-  {
-  public:
-    JobFenceT()
-      : JobT(true)
-    {
-    }
-    void Process() override{};
-  };
-
-  /**
-   * Generate moc_predefs.h
-   */
-  class JobMocPredefsT : public JobFenceT
-  {
-    void Process() override;
-    bool Update(std::string* reason) const;
-  };
-
-  /**
-   * File parse job base class
-   */
-  class JobParseT : public JobT
-  {
-  public:
-    JobParseT(SourceFileHandleT fileHandle)
-      : FileHandle(std::move(fileHandle))
-    {
-    }
-
-  protected:
-    bool ReadFile();
-    void CreateKeys(std::vector<IncludeKeyT>& container,
-                    std::set<std::string> const& source,
-                    std::size_t basePrefixLength);
-    void MocMacro();
-    void MocDependecies();
-    void MocIncludes();
-    void UicIncludes();
-
-  protected:
-    SourceFileHandleT FileHandle;
-    std::string Content;
-  };
-
-  /**
-   * Header file parse job
-   */
-  class JobParseHeaderT : public JobParseT
-  {
-  public:
-    using JobParseT::JobParseT;
-    void Process() override;
-  };
-
-  /**
-   * Source file parse job
-   */
-  class JobParseSourceT : public JobParseT
-  {
-  public:
-    using JobParseT::JobParseT;
-    void Process() override;
-  };
-
-  /**
-   * Evaluate parsed files
-   */
-  class JobEvaluateT : public JobFenceT
-  {
-    void Process() override;
-
-    // -- Moc
-    bool MocEvalHeader(SourceFileHandleT source);
-    bool MocEvalSource(SourceFileHandleT const& source);
-    SourceFileHandleT MocFindIncludedHeader(
-      std::string const& includerDir, std::string const& includeBase) const;
-    SourceFileHandleT MocFindHeader(std::string const& basePath) const;
-    std::string MocMessageTestHeaders(std::string const& fileBase) const;
-    bool MocRegisterIncluded(std::string const& includeString,
-                             SourceFileHandleT includerFileHandle,
-                             SourceFileHandleT sourceFileHandle,
-                             bool sourceIsHeader) const;
-    void MocRegisterMapping(MappingHandleT mappingHandle,
-                            bool sourceIsHeader) const;
-
-    // -- Uic
-    bool UicEval(SourceFileMapT const& fileMap);
-    bool UicEvalFile(SourceFileHandleT const& sourceFileHandle);
-    SourceFileHandleT UicFindIncludedUi(std::string const& sourceFile,
-                                        std::string const& sourceDir,
-                                        IncludeKeyT const& incKey) const;
-    bool UicRegisterMapping(std::string const& includeString,
-                            SourceFileHandleT uiFileHandle,
-                            SourceFileHandleT includerFileHandle);
-  };
-
-  /**
-   * Generates moc/uic jobs
-   */
-  class JobGenerateT : public JobFenceT
-  {
-    void Process() override;
-    // -- Moc
-    bool MocGenerate(MappingHandleT const& mapping, bool compFile) const;
-    bool MocUpdate(MappingT const& mapping, std::string* reason) const;
-    std::pair<std::string, cmFileTime> MocFindDependency(
-      std::string const& sourceDir, std::string const& includeString) const;
-    // -- Uic
-    bool UicGenerate(MappingHandleT const& mapping) const;
-    bool UicUpdate(MappingT const& mapping, std::string* reason) const;
-  };
-
-  /**
-   * File compiling base job
-   */
-  class JobCompileT : public JobT
-  {
-  public:
-    JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason)
-      : Mapping(std::move(uicMapping))
-      , Reason(std::move(reason))
-    {
-    }
-
-  protected:
-    MappingHandleT Mapping;
-    std::unique_ptr<std::string> Reason;
-  };
-
-  /**
-   * moc compiles a file
-   */
-  class JobMocT : public JobCompileT
-  {
-  public:
-    using JobCompileT::JobCompileT;
-    void Process() override;
-  };
-
-  /**
-   * uic compiles a file
-   */
-  class JobUicT : public JobCompileT
-  {
-  public:
-    using JobCompileT::JobCompileT;
-    void Process() override;
-  };
-
-  /// @brief Generate mocs_compilation.cpp
-  ///
-  class JobMocsCompilationT : public JobFenceT
-  {
-  private:
-    void Process() override;
-  };
-
-  /// @brief The last job
-  ///
-  class JobFinishT : public JobFenceT
-  {
-  private:
-    void Process() override;
-  };
-
-  // -- Const settings interface
-  BaseSettingsT const& BaseConst() const { return this->BaseConst_; }
-  BaseEvalT& BaseEval() { return this->BaseEval_; }
-  MocSettingsT const& MocConst() const { return this->MocConst_; }
-  MocEvalT& MocEval() { return this->MocEval_; }
-  UicSettingsT const& UicConst() const { return this->UicConst_; }
-  UicEvalT& UicEval() { return this->UicEval_; }
-
-  // -- Parallel job processing interface
-  cmWorkerPool& WorkerPool() { return WorkerPool_; }
-  void AbortError() { Abort(true); }
-  void AbortSuccess() { Abort(false); }
-
-  // -- Utility
-  std::string AbsoluteBuildPath(std::string const& relativePath) const;
-  std::string AbsoluteIncludePath(std::string const& relativePath) const;
-  template <class JOBTYPE>
-  void CreateParseJobs(SourceFileMapT const& sourceMap);
-
-private:
-  // -- Utility accessors
-  Logger const& Log() const { return Logger_; }
-  // -- Abstract processing interface
-  bool Init(cmMakefile* makefile) override;
-  void InitJobs();
-  bool Process() override;
-  // -- Settings file
-  void SettingsFileRead();
-  bool SettingsFileWrite();
-  // -- Parse cache
-  void ParseCacheRead();
-  bool ParseCacheWrite();
-  // -- Thread processing
-  void Abort(bool error);
-  // -- Generation
-  bool CreateDirectories();
-
-private:
-  // -- Utility
-  Logger Logger_;
-  // -- Settings
-  BaseSettingsT BaseConst_;
-  BaseEvalT BaseEval_;
-  MocSettingsT MocConst_;
-  MocEvalT MocEval_;
-  UicSettingsT UicConst_;
-  UicEvalT UicEval_;
-  // -- Settings file
-  std::string SettingsFile_;
-  std::string SettingsStringMoc_;
-  std::string SettingsStringUic_;
-  // -- Worker thread pool
-  std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
-  cmWorkerPool WorkerPool_;
-};
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config);
 
 #endif
diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index 20885df..3af81ad 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -1,148 +1,135 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoRcc.h"
-#include "cmQtAutoGen.h"
 
-#include <sstream>
+#include <algorithm>
+#include <string>
+#include <vector>
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmDuration.h"
+#include "cmFileLock.h"
 #include "cmFileLockResult.h"
-#include "cmMakefile.h"
+#include "cmFileTime.h"
 #include "cmProcessOutput.h"
+#include "cmQtAutoGen.h"
+#include "cmQtAutoGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-// -- Class methods
+namespace {
 
-cmQtAutoRcc::cmQtAutoRcc() = default;
-
-cmQtAutoRcc::~cmQtAutoRcc() = default;
-
-bool cmQtAutoRcc::Init(cmMakefile* makefile)
+/** \class cmQtAutoRccT
+ * \brief AUTORCC generator
+ */
+class cmQtAutoRccT : public cmQtAutoGenerator
 {
-  // -- Utility lambdas
-  auto InfoGet = [makefile](std::string const& key) {
-    return makefile->GetSafeDefinition(key);
-  };
-  auto InfoGetList =
-    [makefile](std::string const& key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
-    return list;
-  };
-  auto InfoGetConfig = [makefile,
-                        this](std::string const& key) -> std::string {
-    const char* valueConf = nullptr;
-    {
-      std::string keyConf = key;
-      keyConf += '_';
-      keyConf += InfoConfig();
-      valueConf = makefile->GetDefinition(keyConf);
-    }
-    if (valueConf == nullptr) {
-      return makefile->GetSafeDefinition(key);
-    }
-    return std::string(valueConf);
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](std::string const& key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
-    return list;
-  };
-  auto LogInfoError = [this](std::string const& msg) -> bool {
-    std::ostringstream err;
-    err << "In " << Quoted(this->InfoFile()) << ":\n" << msg;
-    this->Log().Error(GenT::RCC, err.str());
+public:
+  cmQtAutoRccT();
+  ~cmQtAutoRccT() override;
+
+  cmQtAutoRccT(cmQtAutoRccT const&) = delete;
+  cmQtAutoRccT& operator=(cmQtAutoRccT const&) = delete;
+
+private:
+  // -- Utility
+  bool IsMultiConfig() const { return MultiConfig_; }
+  std::string MultiConfigOutput() const;
+
+  // -- Abstract processing interface
+  bool InitFromInfo(InfoT const& info) override;
+  bool Process() override;
+  // -- Settings file
+  bool SettingsFileRead();
+  bool SettingsFileWrite();
+  // -- Tests
+  bool TestQrcRccFiles(bool& generate);
+  bool TestResources(bool& generate);
+  bool TestInfoFile();
+  // -- Generation
+  bool GenerateRcc();
+  bool GenerateWrapper();
+
+private:
+  // -- Config settings
+  bool MultiConfig_ = false;
+  // -- Directories
+  std::string AutogenBuildDir_;
+  std::string IncludeDir_;
+  // -- Qt environment
+  std::string RccExecutable_;
+  cmFileTime RccExecutableTime_;
+  std::vector<std::string> RccListOptions_;
+  // -- Job
+  std::string LockFile_;
+  cmFileLock LockFileLock_;
+  std::string QrcFile_;
+  std::string QrcFileName_;
+  std::string QrcFileDir_;
+  cmFileTime QrcFileTime_;
+  std::string RccPathChecksum_;
+  std::string RccFileName_;
+  std::string RccFileOutput_;
+  std::string RccFilePublic_;
+  cmFileTime RccFileTime_;
+  std::string Reason;
+  std::vector<std::string> Options_;
+  std::vector<std::string> Inputs_;
+  // -- Settings file
+  std::string SettingsFile_;
+  std::string SettingsString_;
+  bool SettingsChanged_ = false;
+  bool BuildFileChanged_ = false;
+};
+
+cmQtAutoRccT::cmQtAutoRccT()
+  : cmQtAutoGenerator(GenT::RCC)
+{
+}
+cmQtAutoRccT::~cmQtAutoRccT() = default;
+
+bool cmQtAutoRccT::InitFromInfo(InfoT const& info)
+{
+  // -- Required settings
+  if (!info.GetBool("MULTI_CONFIG", MultiConfig_, true) ||
+      !info.GetString("BUILD_DIR", AutogenBuildDir_, true) ||
+      !info.GetStringConfig("INCLUDE_DIR", IncludeDir_, true) ||
+      !info.GetString("RCC_EXECUTABLE", RccExecutable_, true) ||
+      !info.GetArray("RCC_LIST_OPTIONS", RccListOptions_, false) ||
+      !info.GetString("LOCK_FILE", LockFile_, true) ||
+      !info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
+      !info.GetString("SOURCE", QrcFile_, true) ||
+      !info.GetString("OUTPUT_CHECKSUM", RccPathChecksum_, true) ||
+      !info.GetString("OUTPUT_NAME", RccFileName_, true) ||
+      !info.GetArray("OPTIONS", Options_, false) ||
+      !info.GetArray("INPUTS", Inputs_, false)) {
     return false;
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    return LogInfoError("File processing failed.");
   }
 
-  // - Configurations
-  Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
-  MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
-
-  // - Directories
-  AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
-  if (AutogenBuildDir_.empty()) {
-    return LogInfoError("Build directory empty.");
-  }
-
-  IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
-  if (IncludeDir_.empty()) {
-    return LogInfoError("Include directory empty.");
-  }
-
-  // - Rcc executable
-  RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
-  if (!RccExecutableTime_.Load(RccExecutable_)) {
-    std::string error = "The rcc executable ";
-    error += Quoted(RccExecutable_);
-    error += " does not exist.";
-    return LogInfoError(error);
-  }
-  RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
-
-  // - Job
-  LockFile_ = InfoGet("ARCC_LOCK_FILE");
-  QrcFile_ = InfoGet("ARCC_SOURCE");
+  // -- Derive information
   QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_);
   QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_);
-  RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM");
-  RccFileName_ = InfoGet("ARCC_OUTPUT_NAME");
-  Options_ = InfoGetConfigList("ARCC_OPTIONS");
-  Inputs_ = InfoGetList("ARCC_INPUTS");
+  RccFilePublic_ =
+    cmStrCat(AutogenBuildDir_, '/', RccPathChecksum_, '/', RccFileName_);
 
-  // - Settings file
-  SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE");
-
-  // - Validity checks
-  if (LockFile_.empty()) {
-    return LogInfoError("Lock file name missing.");
-  }
-  if (SettingsFile_.empty()) {
-    return LogInfoError("Settings file name missing.");
-  }
-  if (AutogenBuildDir_.empty()) {
-    return LogInfoError("Autogen build directory missing.");
-  }
-  if (RccExecutable_.empty()) {
-    return LogInfoError("rcc executable missing.");
-  }
-  if (QrcFile_.empty()) {
-    return LogInfoError("rcc input file missing.");
-  }
-  if (RccFileName_.empty()) {
-    return LogInfoError("rcc output file missing.");
-  }
-
-  // Init derived information
-  // ------------------------
-
-  RccFilePublic_ = AutogenBuildDir_;
-  RccFilePublic_ += '/';
-  RccFilePublic_ += RccPathChecksum_;
-  RccFilePublic_ += '/';
-  RccFilePublic_ += RccFileName_;
-
-  // Compute rcc output file name
+  // rcc output file name
   if (IsMultiConfig()) {
-    RccFileOutput_ = IncludeDir_;
-    RccFileOutput_ += '/';
-    RccFileOutput_ += MultiConfigOutput();
+    RccFileOutput_ = cmStrCat(IncludeDir_, '/', MultiConfigOutput());
   } else {
     RccFileOutput_ = RccFilePublic_;
   }
 
+  // -- Checks
+  if (!RccExecutableTime_.Load(RccExecutable_)) {
+    return info.LogError(cmStrCat(
+      "The rcc executable ", MessagePath(RccExecutable_), " does not exist."));
+  }
+
   return true;
 }
 
-bool cmQtAutoRcc::Process()
+bool cmQtAutoRccT::Process()
 {
   if (!SettingsFileRead()) {
     return false;
@@ -175,48 +162,38 @@
   return SettingsFileWrite();
 }
 
-std::string cmQtAutoRcc::MultiConfigOutput() const
+std::string cmQtAutoRccT::MultiConfigOutput() const
 {
-  static std::string const suffix = "_CMAKE_";
-  std::string res;
-  res += RccPathChecksum_;
-  res += '/';
-  res += AppendFilenameSuffix(RccFileName_, suffix);
-  return res;
+  return cmStrCat(RccPathChecksum_, '/',
+                  AppendFilenameSuffix(RccFileName_, "_CMAKE_"));
 }
 
-bool cmQtAutoRcc::SettingsFileRead()
+bool cmQtAutoRccT::SettingsFileRead()
 {
   // Compose current settings strings
   {
-    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
-    std::string const sep(" ~~~ ");
-    {
-      std::string str;
-      str += RccExecutable_;
-      str += sep;
-      str += cmJoin(RccListOptions_, ";");
-      str += sep;
-      str += QrcFile_;
-      str += sep;
-      str += RccPathChecksum_;
-      str += sep;
-      str += RccFileName_;
-      str += sep;
-      str += cmJoin(Options_, ";");
-      str += sep;
-      str += cmJoin(Inputs_, ";");
-      str += sep;
-      SettingsString_ = crypt.HashString(str);
-    }
+    cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256);
+    auto cha = [&cryptoHash](cm::string_view value) {
+      cryptoHash.Append(value);
+      cryptoHash.Append(";");
+    };
+    cha(RccExecutable_);
+    std::for_each(RccListOptions_.begin(), RccListOptions_.end(), cha);
+    cha(QrcFile_);
+    cha(RccPathChecksum_);
+    cha(RccFileName_);
+    std::for_each(Options_.begin(), Options_.end(), cha);
+    std::for_each(Inputs_.begin(), Inputs_.end(), cha);
+    SettingsString_ = cryptoHash.FinalizeHex();
   }
 
   // Make sure the settings file exists
   if (!cmSystemTools::FileExists(SettingsFile_, true)) {
     // Touch the settings file to make sure it exists
     if (!cmSystemTools::Touch(SettingsFile_, true)) {
-      Log().ErrorFile(GenT::RCC, SettingsFile_,
-                      "Settings file creation failed.");
+      Log().Error(GenT::RCC,
+                  cmStrCat("Touching the settings file ",
+                           MessagePath(SettingsFile_), " failed."));
       return false;
     }
   }
@@ -226,7 +203,9 @@
     // Make sure the lock file exists
     if (!cmSystemTools::FileExists(LockFile_, true)) {
       if (!cmSystemTools::Touch(LockFile_, true)) {
-        Log().ErrorFile(GenT::RCC, LockFile_, "Lock file creation failed.");
+        Log().Error(GenT::RCC,
+                    cmStrCat("Touching the lock file ", MessagePath(LockFile_),
+                             " failed."));
         return false;
       }
     }
@@ -234,8 +213,9 @@
     cmFileLockResult lockResult =
       LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1));
     if (!lockResult.IsOk()) {
-      Log().ErrorFile(GenT::RCC, LockFile_,
-                      "File lock failed: " + lockResult.GetOutputMessage());
+      Log().Error(GenT::RCC,
+                  cmStrCat("Locking of the lock file ", MessagePath(LockFile_),
+                           " failed.\n", lockResult.GetOutputMessage()));
       return false;
     }
   }
@@ -251,8 +231,10 @@
       if (SettingsChanged_) {
         std::string error;
         if (!FileWrite(SettingsFile_, "", &error)) {
-          Log().ErrorFile(GenT::RCC, SettingsFile_,
-                          "Settings file clearing failed. " + error);
+          Log().Error(GenT::RCC,
+                      cmStrCat("Clearing of the settings file ",
+                               MessagePath(SettingsFile_), " failed.\n",
+                               error));
           return false;
         }
       }
@@ -264,21 +246,21 @@
   return true;
 }
 
-bool cmQtAutoRcc::SettingsFileWrite()
+bool cmQtAutoRccT::SettingsFileWrite()
 {
   // Only write if any setting changed
   if (SettingsChanged_) {
     if (Log().Verbose()) {
-      Log().Info(GenT::RCC, "Writing settings file " + Quoted(SettingsFile_));
+      Log().Info(GenT::RCC,
+                 "Writing settings file " + MessagePath(SettingsFile_));
     }
     // Write settings file
-    std::string content = "rcc:";
-    content += SettingsString_;
-    content += '\n';
+    std::string content = cmStrCat("rcc:", SettingsString_, '\n');
     std::string error;
     if (!FileWrite(SettingsFile_, content, &error)) {
-      Log().ErrorFile(GenT::RCC, SettingsFile_,
-                      "Settings file writing failed. " + error);
+      Log().Error(GenT::RCC,
+                  cmStrCat("Writing of the settings file ",
+                           MessagePath(SettingsFile_), " failed.\n", error));
       // Remove old settings file to trigger a full rebuild on the next run
       cmSystemTools::RemoveFile(SettingsFile_);
       return false;
@@ -291,25 +273,22 @@
 }
 
 /// Do basic checks if rcc generation is required
-bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
+bool cmQtAutoRccT::TestQrcRccFiles(bool& generate)
 {
   // Test if the rcc input file exists
   if (!QrcFileTime_.Load(QrcFile_)) {
-    std::string error;
-    error = "The resources file ";
-    error += Quoted(QrcFile_);
-    error += " does not exist";
-    Log().ErrorFile(GenT::RCC, QrcFile_, error);
+    Log().Error(GenT::RCC,
+                cmStrCat("The resources file ", MessagePath(QrcFile_),
+                         " does not exist"));
     return false;
   }
 
   // Test if the rcc output file exists
   if (!RccFileTime_.Load(RccFileOutput_)) {
     if (Log().Verbose()) {
-      Reason = "Generating ";
-      Reason += Quoted(RccFileOutput_);
-      Reason += ", because it doesn't exist, from ";
-      Reason += Quoted(QrcFile_);
+      Reason =
+        cmStrCat("Generating ", MessagePath(RccFileOutput_),
+                 ", because it doesn't exist, from ", MessagePath(QrcFile_));
     }
     generate = true;
     return true;
@@ -318,10 +297,9 @@
   // Test if the settings changed
   if (SettingsChanged_) {
     if (Log().Verbose()) {
-      Reason = "Generating ";
-      Reason += Quoted(RccFileOutput_);
-      Reason += ", because the rcc settings changed, from ";
-      Reason += Quoted(QrcFile_);
+      Reason = cmStrCat("Generating ", MessagePath(RccFileOutput_),
+                        ", because the rcc settings changed, from ",
+                        MessagePath(QrcFile_));
     }
     generate = true;
     return true;
@@ -330,12 +308,9 @@
   // Test if the rcc output file is older than the .qrc file
   if (RccFileTime_.Older(QrcFileTime_)) {
     if (Log().Verbose()) {
-      Reason = "Generating ";
-      Reason += Quoted(RccFileOutput_);
-      Reason += ", because it is older than ";
-      Reason += Quoted(QrcFile_);
-      Reason += ", from ";
-      Reason += Quoted(QrcFile_);
+      Reason = cmStrCat("Generating ", MessagePath(RccFileOutput_),
+                        ", because it is older than ", MessagePath(QrcFile_),
+                        ", from ", MessagePath(QrcFile_));
     }
     generate = true;
     return true;
@@ -344,10 +319,9 @@
   // Test if the rcc output file is older than the rcc executable
   if (RccFileTime_.Older(RccExecutableTime_)) {
     if (Log().Verbose()) {
-      Reason = "Generating ";
-      Reason += Quoted(RccFileOutput_);
-      Reason += ", because it is older than the rcc executable, from ";
-      Reason += Quoted(QrcFile_);
+      Reason = cmStrCat("Generating ", MessagePath(RccFileOutput_),
+                        ", because it is older than the rcc executable, from ",
+                        MessagePath(QrcFile_));
     }
     generate = true;
     return true;
@@ -356,14 +330,16 @@
   return true;
 }
 
-bool cmQtAutoRcc::TestResources(bool& generate)
+bool cmQtAutoRccT::TestResources(bool& generate)
 {
   // Read resource files list
   if (Inputs_.empty()) {
     std::string error;
     RccLister const lister(RccExecutable_, RccListOptions_);
     if (!lister.list(QrcFile_, Inputs_, error, Log().Verbose())) {
-      Log().ErrorFile(GenT::RCC, QrcFile_, error);
+      Log().Error(
+        GenT::RCC,
+        cmStrCat("Listing of ", MessagePath(QrcFile_), " failed.\n", error));
       return false;
     }
   }
@@ -373,22 +349,18 @@
     // Check if the resource file exists
     cmFileTime fileTime;
     if (!fileTime.Load(resFile)) {
-      std::string error;
-      error = "Could not find the resource file\n  ";
-      error += Quoted(resFile);
-      error += '\n';
-      Log().ErrorFile(GenT::RCC, QrcFile_, error);
+      Log().Error(GenT::RCC,
+                  cmStrCat("The resource file ", MessagePath(resFile),
+                           " listed in ", MessagePath(QrcFile_),
+                           " does not exist."));
       return false;
     }
     // Check if the resource file is newer than the rcc output file
     if (RccFileTime_.Older(fileTime)) {
       if (Log().Verbose()) {
-        Reason = "Generating ";
-        Reason += Quoted(RccFileOutput_);
-        Reason += ", because it is older than ";
-        Reason += Quoted(resFile);
-        Reason += ", from ";
-        Reason += Quoted(QrcFile_);
+        Reason = cmStrCat("Generating ", MessagePath(RccFileOutput_),
+                          ", because it is older than ", MessagePath(resFile),
+                          ", from ", MessagePath(QrcFile_));
       }
       generate = true;
       break;
@@ -397,20 +369,21 @@
   return true;
 }
 
-bool cmQtAutoRcc::TestInfoFile()
+bool cmQtAutoRccT::TestInfoFile()
 {
   // Test if the rcc output file is older than the info file
   if (RccFileTime_.Older(InfoFileTime())) {
     if (Log().Verbose()) {
-      std::string reason = "Touching ";
-      reason += Quoted(RccFileOutput_);
-      reason += " because it is older than ";
-      reason += Quoted(InfoFile());
-      Log().Info(GenT::RCC, reason);
+      Log().Info(GenT::RCC,
+                 cmStrCat("Touching ", MessagePath(RccFileOutput_),
+                          " because it is older than ",
+                          MessagePath(InfoFile())));
     }
     // Touch build file
     if (!cmSystemTools::Touch(RccFileOutput_, false)) {
-      Log().ErrorFile(GenT::RCC, RccFileOutput_, "Build file touch failed");
+      Log().Error(
+        GenT::RCC,
+        cmStrCat("Touching ", MessagePath(RccFileOutput_), " failed."));
       return false;
     }
     BuildFileChanged_ = true;
@@ -419,12 +392,13 @@
   return true;
 }
 
-bool cmQtAutoRcc::GenerateRcc()
+bool cmQtAutoRccT::GenerateRcc()
 {
   // Make parent directory
   if (!MakeParentDirectory(RccFileOutput_)) {
-    Log().ErrorFile(GenT::RCC, RccFileOutput_,
-                    "Could not create parent directory");
+    Log().Error(GenT::RCC,
+                cmStrCat("Could not create parent directory of ",
+                         MessagePath(RccFileOutput_)));
     return false;
   }
 
@@ -438,13 +412,9 @@
 
   // Log reason and command
   if (Log().Verbose()) {
-    std::string msg = Reason;
-    if (!msg.empty() && (msg.back() != '\n')) {
-      msg += '\n';
-    }
-    msg += QuotedCommand(cmd);
-    msg += '\n';
-    Log().Info(GenT::RCC, msg);
+    Log().Info(GenT::RCC,
+               cmStrCat(Reason, cmHasSuffix(Reason, '\n') ? "" : "\n",
+                        QuotedCommand(cmd), '\n'));
   }
 
   std::string rccStdOut;
@@ -455,13 +425,11 @@
     cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
   if (!result || (retVal != 0)) {
     // rcc process failed
-    {
-      std::string err = "The rcc process failed to compile\n  ";
-      err += Quoted(QrcFile_);
-      err += "\ninto\n  ";
-      err += Quoted(RccFileOutput_);
-      Log().ErrorCommand(GenT::RCC, err, cmd, rccStdOut + rccStdErr);
-    }
+    Log().ErrorCommand(GenT::RCC,
+                       cmStrCat("The rcc process failed to compile\n  ",
+                                MessagePath(QrcFile_), "\ninto\n  ",
+                                MessagePath(RccFileOutput_)),
+                       cmd, rccStdOut + rccStdErr);
     cmSystemTools::RemoveFile(RccFileOutput_);
     return false;
   }
@@ -476,17 +444,15 @@
   return true;
 }
 
-bool cmQtAutoRcc::GenerateWrapper()
+bool cmQtAutoRccT::GenerateWrapper()
 {
   // Generate a wrapper source file on demand
   if (IsMultiConfig()) {
     // Wrapper file content
-    std::string content;
-    content += "// This is an autogenerated configuration wrapper file.\n";
-    content += "// Changes will be overwritten.\n";
-    content += "#include <";
-    content += MultiConfigOutput();
-    content += ">\n";
+    std::string content =
+      cmStrCat("// This is an autogenerated configuration wrapper file.\n",
+               "// Changes will be overwritten.\n", "#include <",
+               MultiConfigOutput(), ">\n");
 
     // Compare with existing file content
     bool fileDiffers = true;
@@ -499,25 +465,39 @@
     if (fileDiffers) {
       // Write new wrapper file
       if (Log().Verbose()) {
-        Log().Info(GenT::RCC, "Generating RCC wrapper file " + RccFilePublic_);
+        Log().Info(GenT::RCC,
+                   cmStrCat("Generating RCC wrapper file ",
+                            MessagePath(RccFilePublic_)));
       }
       std::string error;
       if (!FileWrite(RccFilePublic_, content, &error)) {
-        Log().ErrorFile(GenT::RCC, RccFilePublic_,
-                        "RCC wrapper file writing failed. " + error);
+        Log().Error(GenT::RCC,
+                    cmStrCat("Generating RCC wrapper file ",
+                             MessagePath(RccFilePublic_), " failed.\n",
+                             error));
         return false;
       }
     } else if (BuildFileChanged_) {
       // Just touch the wrapper file
       if (Log().Verbose()) {
-        Log().Info(GenT::RCC, "Touching RCC wrapper file " + RccFilePublic_);
+        Log().Info(
+          GenT::RCC,
+          cmStrCat("Touching RCC wrapper file ", MessagePath(RccFilePublic_)));
       }
       if (!cmSystemTools::Touch(RccFilePublic_, false)) {
-        Log().ErrorFile(GenT::RCC, RccFilePublic_,
-                        "RCC wrapper file touch failed.");
+        Log().Error(GenT::RCC,
+                    cmStrCat("Touching RCC wrapper file ",
+                             MessagePath(RccFilePublic_), " failed."));
         return false;
       }
     }
   }
   return true;
 }
+
+} // End of unnamed namespace
+
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config)
+{
+  return cmQtAutoRccT().Run(infoFile, config);
+}
diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h
index 636a667..a74b33a 100644
--- a/Source/cmQtAutoRcc.h
+++ b/Source/cmQtAutoRcc.h
@@ -5,77 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFileLock.h"
-#include "cmFileTime.h"
-#include "cmQtAutoGenerator.h"
+#include <cm/string_view>
 
-#include <string>
-#include <vector>
-
-class cmMakefile;
-
-// @brief AUTORCC generator
-class cmQtAutoRcc : public cmQtAutoGenerator
-{
-public:
-  cmQtAutoRcc();
-  ~cmQtAutoRcc() override;
-
-  cmQtAutoRcc(cmQtAutoRcc const&) = delete;
-  cmQtAutoRcc& operator=(cmQtAutoRcc const&) = delete;
-
-private:
-  // -- Utility
-  Logger const& Log() const { return Logger_; }
-  bool IsMultiConfig() const { return MultiConfig_; }
-  std::string MultiConfigOutput() const;
-
-  // -- Abstract processing interface
-  bool Init(cmMakefile* makefile) override;
-  bool Process() override;
-  // -- Settings file
-  bool SettingsFileRead();
-  bool SettingsFileWrite();
-  // -- Tests
-  bool TestQrcRccFiles(bool& generate);
-  bool TestResources(bool& generate);
-  bool TestInfoFile();
-  // -- Generation
-  bool GenerateRcc();
-  bool GenerateWrapper();
-
-private:
-  // -- Logging
-  Logger Logger_;
-  // -- Config settings
-  bool MultiConfig_ = false;
-  // -- Directories
-  std::string AutogenBuildDir_;
-  std::string IncludeDir_;
-  // -- Qt environment
-  std::string RccExecutable_;
-  cmFileTime RccExecutableTime_;
-  std::vector<std::string> RccListOptions_;
-  // -- Job
-  std::string LockFile_;
-  cmFileLock LockFileLock_;
-  std::string QrcFile_;
-  std::string QrcFileName_;
-  std::string QrcFileDir_;
-  cmFileTime QrcFileTime_;
-  std::string RccPathChecksum_;
-  std::string RccFileName_;
-  std::string RccFileOutput_;
-  std::string RccFilePublic_;
-  cmFileTime RccFileTime_;
-  std::string Reason;
-  std::vector<std::string> Options_;
-  std::vector<std::string> Inputs_;
-  // -- Settings file
-  std::string SettingsFile_;
-  std::string SettingsString_;
-  bool SettingsChanged_ = false;
-  bool BuildFileChanged_ = false;
-};
+/**
+ * Process AUTORCC
+ * @return true on success
+ */
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config);
 
 #endif
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index 2064275..7f4abf9 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -2,17 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmRST.h"
 
-#include "cmAlgorithms.h"
-#include "cmRange.h"
-#include "cmSystemTools.h"
-#include "cmVersion.h"
+#include <algorithm>
+#include <cctype>
+#include <cstddef>
+#include <iterator>
+#include <utility>
 
 #include "cmsys/FStream.hxx"
-#include <algorithm>
-#include <ctype.h>
-#include <iterator>
-#include <stddef.h>
-#include <utility>
+
+#include "cmAlgorithms.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmVersion.h"
 
 cmRST::cmRST(std::ostream& os, std::string docroot)
   : OS(os)
@@ -319,8 +321,7 @@
     std::string::size_type start = this->Substitution.start(2);
     std::string::size_type end = this->Substitution.end(2);
     std::string substitute = this->Substitution.match(3);
-    std::map<std::string, std::string>::iterator replace =
-      this->Replace.find(substitute);
+    auto replace = this->Replace.find(substitute);
     if (replace != this->Replace.end()) {
       std::pair<std::set<std::string>::iterator, bool> replaced =
         this->Replaced.insert(substitute);
@@ -341,7 +342,7 @@
 {
   for (auto line : this->MarkupLines) {
     if (!line.empty()) {
-      line = " " + line;
+      line = cmStrCat(" ", line);
     }
     this->OutputLine(line, inlineMarkup);
   }
@@ -450,10 +451,10 @@
     }
   }
 
-  std::vector<std::string>::const_iterator it = lines.begin();
+  auto it = lines.cbegin();
   size_t leadingEmpty = std::distance(it, cmFindNot(lines, std::string()));
 
-  std::vector<std::string>::const_reverse_iterator rit = lines.rbegin();
+  auto rit = lines.crbegin();
   size_t trailingEmpty =
     std::distance(rit, cmFindNot(cmReverseRange(lines), std::string()));
 
@@ -463,7 +464,7 @@
     return;
   }
 
-  std::vector<std::string>::iterator contentEnd = cmRotate(
-    lines.begin(), lines.begin() + leadingEmpty, lines.end() - trailingEmpty);
+  auto contentEnd = cmRotate(lines.begin(), lines.begin() + leadingEmpty,
+                             lines.end() - trailingEmpty);
   lines.erase(contentEnd, lines.end());
 }
diff --git a/Source/cmRST.h b/Source/cmRST.h
index d8d2a0b..6b5d416 100644
--- a/Source/cmRST.h
+++ b/Source/cmRST.h
@@ -5,13 +5,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
 #include <iosfwd>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 /** \class cmRST
  * \brief Perform basic .rst processing for command-line help
  *
diff --git a/Source/cmRemoveCommand.cxx b/Source/cmRemoveCommand.cxx
index a64ad8c..457b708 100644
--- a/Source/cmRemoveCommand.cxx
+++ b/Source/cmRemoveCommand.cxx
@@ -2,14 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmRemoveCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmSystemTools.h"
-
-class cmExecutionStatus;
+#include "cmStringAlgorithms.h"
 
 // cmRemoveCommand
-bool cmRemoveCommand::InitialPass(std::vector<std::string> const& args,
-                                  cmExecutionStatus&)
+bool cmRemoveCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
 {
   if (args.empty()) {
     return true;
@@ -17,7 +16,7 @@
 
   std::string const& variable = args[0]; // VAR is always first
   // get the old value
-  const char* cacheValue = this->Makefile->GetDefinition(variable);
+  const char* cacheValue = status.GetMakefile().GetDefinition(variable);
 
   // if there is no old value then return
   if (!cacheValue) {
@@ -25,13 +24,12 @@
   }
 
   // expand the variable
-  std::vector<std::string> const varArgsExpanded =
-    cmSystemTools::ExpandedListArgument(cacheValue);
+  std::vector<std::string> const varArgsExpanded = cmExpandedList(cacheValue);
 
   // expand the args
   // check for REMOVE(VAR v1 v2 ... vn)
   std::vector<std::string> const argsExpanded =
-    cmSystemTools::ExpandedLists(args.begin() + 1, args.end());
+    cmExpandedLists(args.begin() + 1, args.end());
 
   // now create the new value
   std::string value;
@@ -52,7 +50,7 @@
   }
 
   // add the definition
-  this->Makefile->AddDefinition(variable, value.c_str());
+  status.GetMakefile().AddDefinition(variable, value);
 
   return true;
 }
diff --git a/Source/cmRemoveCommand.h b/Source/cmRemoveCommand.h
index 7b11849..fb72ab5 100644
--- a/Source/cmRemoveCommand.h
+++ b/Source/cmRemoveCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmRemoveCommand
+/**
  * \brief remove command
  *
  * cmRemoveCommand implements the remove CMake command
  */
-class cmRemoveCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmRemoveCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmRemoveCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmRemoveDefinitionsCommand.cxx b/Source/cmRemoveDefinitionsCommand.cxx
index 8d3f688..339ff9d 100644
--- a/Source/cmRemoveDefinitionsCommand.cxx
+++ b/Source/cmRemoveDefinitionsCommand.cxx
@@ -2,21 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmRemoveDefinitionsCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
-
-// cmRemoveDefinitionsCommand
-bool cmRemoveDefinitionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmRemoveDefinitionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
-  // it is OK to have no arguments
-  if (args.empty()) {
-    return true;
-  }
-
+  cmMakefile& mf = status.GetMakefile();
   for (std::string const& i : args) {
-    this->Makefile->RemoveDefineFlag(i);
+    mf.RemoveDefineFlag(i);
   }
   return true;
 }
diff --git a/Source/cmRemoveDefinitionsCommand.h b/Source/cmRemoveDefinitionsCommand.h
index a5cb204..868416b 100644
--- a/Source/cmRemoveDefinitionsCommand.h
+++ b/Source/cmRemoveDefinitionsCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmRemoveDefinitionsCommand
- * \brief Specify a list of compiler defines
- *
- * cmRemoveDefinitionsCommand specifies a list of compiler defines.
- * These defines will
- * be removed from the compile command.
- */
-class cmRemoveDefinitionsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmRemoveDefinitionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmRemoveDefinitionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmReturnCommand.cxx b/Source/cmReturnCommand.cxx
index ceea8b4..5905669 100644
--- a/Source/cmReturnCommand.cxx
+++ b/Source/cmReturnCommand.cxx
@@ -5,8 +5,8 @@
 #include "cmExecutionStatus.h"
 
 // cmReturnCommand
-bool cmReturnCommand::InitialPass(std::vector<std::string> const&,
-                                  cmExecutionStatus& status)
+bool cmReturnCommand(std::vector<std::string> const&,
+                     cmExecutionStatus& status)
 {
   status.SetReturnInvoked();
   return true;
diff --git a/Source/cmReturnCommand.h b/Source/cmReturnCommand.h
index ef39614..2404a36 100644
--- a/Source/cmReturnCommand.h
+++ b/Source/cmReturnCommand.h
@@ -8,29 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmReturnCommand
- * \brief Return from a directory or function
- *
- * cmReturnCommand returns from a directory or function
- */
-class cmReturnCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmReturnCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+/// Return from a directory or function
+bool cmReturnCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 33389ca..0a1d109 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmRulePlaceholderExpander.h"
 
-#include <ctype.h>
-#include <string.h>
+#include <cctype>
+#include <cstring>
 #include <utility>
 
 #include "cmOutputConverter.h"
@@ -235,8 +235,7 @@
       cmOutputConverter::SHELL);
   }
 
-  std::map<std::string, std::string>::iterator compIt =
-    this->Compilers.find(variable);
+  auto compIt = this->Compilers.find(variable);
 
   if (compIt != this->Compilers.end()) {
     std::string ret = outputConverter->ConvertToOutputForExisting(
@@ -292,8 +291,7 @@
     return ret;
   }
 
-  std::map<std::string, std::string>::iterator mapIt =
-    this->VariableMappings.find(variable);
+  auto mapIt = this->VariableMappings.find(variable);
   if (mapIt != this->VariableMappings.end()) {
     if (variable.find("_FLAG") == std::string::npos) {
       return outputConverter->ConvertToOutputForExisting(mapIt->second);
diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx
new file mode 100644
index 0000000..7a987c2
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.cxx
@@ -0,0 +1,378 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmRuntimeDependencyArchive.h"
+
+#include "cmBinUtilsLinuxELFLinker.h"
+#include "cmBinUtilsMacOSMachOLinker.h"
+#include "cmBinUtilsWindowsPELinker.h"
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+#if defined(_WIN32)
+#  include "cmGlobalGenerator.h"
+#  ifndef CMAKE_BOOTSTRAP
+#    include "cmGlobalVisualStudioVersionedGenerator.h"
+#  endif
+#  include "cmsys/Glob.hxx"
+
+#  include "cmVSSetupHelper.h"
+#endif
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#if defined(_WIN32)
+static void AddVisualStudioPath(std::vector<std::string>& paths,
+                                const std::string& prefix,
+                                unsigned int version, cmGlobalGenerator* gg)
+{
+  // If generating for the VS IDE, use the same instance.
+  std::string vsloc;
+  bool found = false;
+#  ifndef CMAKE_BOOTSTRAP
+  if (gg->GetName().find(prefix) == 0) {
+    cmGlobalVisualStudioVersionedGenerator* vsgen =
+      static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
+    if (vsgen->GetVSInstance(vsloc)) {
+      found = true;
+    }
+  }
+#  endif
+
+  // Otherwise, find a VS instance ourselves.
+  if (!found) {
+    cmVSSetupAPIHelper vsSetupAPIHelper(version);
+    if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) {
+      cmSystemTools::ConvertToUnixSlashes(vsloc);
+      found = true;
+    }
+  }
+
+  if (found) {
+    cmsys::Glob glob;
+    glob.SetListDirs(true);
+    glob.FindFiles(vsloc + "/VC/Tools/MSVC/*");
+    for (auto const& vcdir : glob.GetFiles()) {
+      paths.push_back(vcdir + "/bin/Hostx64/x64");
+      paths.push_back(vcdir + "/bin/Hostx86/x64");
+      paths.push_back(vcdir + "/bin/Hostx64/x86");
+      paths.push_back(vcdir + "/bin/Hostx86/x86");
+    }
+  }
+}
+
+static void AddRegistryPath(std::vector<std::string>& paths,
+                            const std::string& path, cmMakefile* mf)
+{
+  // We should view the registry as the target application would view
+  // it.
+  cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
+  cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
+  if (mf->PlatformIs64Bit()) {
+    view = cmSystemTools::KeyWOW64_64;
+    other_view = cmSystemTools::KeyWOW64_32;
+  }
+
+  // Expand using the view of the target application.
+  std::string expanded = path;
+  cmSystemTools::ExpandRegistryValues(expanded, view);
+  cmSystemTools::GlobDirs(expanded, paths);
+
+  // Executables can be either 32-bit or 64-bit, so expand using the
+  // alternative view.
+  expanded = path;
+  cmSystemTools::ExpandRegistryValues(expanded, other_view);
+  cmSystemTools::GlobDirs(expanded, paths);
+}
+
+static void AddEnvPath(std::vector<std::string>& paths, const std::string& var,
+                       const std::string& suffix)
+{
+  std::string value;
+  if (cmSystemTools::GetEnv(var, value)) {
+    paths.push_back(value + suffix);
+  }
+}
+#endif
+
+static cmsys::RegularExpression TransformCompile(const std::string& str)
+{
+  return cmsys::RegularExpression(str);
+}
+
+cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
+  cmExecutionStatus& status, std::vector<std::string> searchDirectories,
+  std::string bundleExecutable,
+  const std::vector<std::string>& preIncludeRegexes,
+  const std::vector<std::string>& preExcludeRegexes,
+  const std::vector<std::string>& postIncludeRegexes,
+  const std::vector<std::string>& postExcludeRegexes)
+  : Status(status)
+  , SearchDirectories(std::move(searchDirectories))
+  , BundleExecutable(std::move(bundleExecutable))
+  , PreIncludeRegexes(preIncludeRegexes.size())
+  , PreExcludeRegexes(preExcludeRegexes.size())
+  , PostIncludeRegexes(postIncludeRegexes.size())
+  , PostExcludeRegexes(postExcludeRegexes.size())
+{
+  std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
+                 this->PreIncludeRegexes.begin(), TransformCompile);
+  std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
+                 this->PreExcludeRegexes.begin(), TransformCompile);
+  std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
+                 this->PostIncludeRegexes.begin(), TransformCompile);
+  std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
+                 this->PostExcludeRegexes.begin(), TransformCompile);
+}
+
+bool cmRuntimeDependencyArchive::Prepare()
+{
+  std::string platform = this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
+  if (platform.empty()) {
+    std::string systemName =
+      this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+    if (systemName == "Windows") {
+      platform = "windows+pe";
+    } else if (systemName == "Darwin") {
+      platform = "macos+macho";
+    } else if (systemName == "Linux") {
+      platform = "linux+elf";
+    }
+  }
+  if (platform == "linux+elf") {
+    this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
+  } else if (platform == "windows+pe") {
+    this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
+  } else if (platform == "macos+macho") {
+    this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
+  } else {
+    std::ostringstream e;
+    e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
+      << platform;
+    this->SetError(e.str());
+    return false;
+  }
+
+  return this->Linker->Prepare();
+}
+
+bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
+  const std::vector<std::string>& executables,
+  const std::vector<std::string>& libraries,
+  const std::vector<std::string>& modules)
+{
+  for (auto const& exe : executables) {
+    if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
+      return false;
+    }
+  }
+  for (auto const& lib : libraries) {
+    if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
+      return false;
+    }
+  }
+  for (auto const& mod : modules) {
+    if (!this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void cmRuntimeDependencyArchive::SetError(const std::string& e)
+{
+  this->Status.SetError(e);
+}
+
+std::string cmRuntimeDependencyArchive::GetBundleExecutable()
+{
+  return this->BundleExecutable;
+}
+
+const std::vector<std::string>&
+cmRuntimeDependencyArchive::GetSearchDirectories()
+{
+  return this->SearchDirectories;
+}
+
+std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
+{
+  return this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
+}
+
+bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
+  const std::string& search, std::vector<std::string>& command)
+{
+  // First see if it was supplied by the user
+  std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
+    "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
+  if (!toolCommand.empty()) {
+    cmExpandList(toolCommand, command);
+    return true;
+  }
+
+  // Now go searching for it
+  std::vector<std::string> paths;
+#ifdef _WIN32
+  cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
+
+  // Add newer Visual Studio paths
+  AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
+  AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
+
+  // Add older Visual Studio paths
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
+  paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
+  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
+    "../../VC/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
+  paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
+  paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
+  AddRegistryPath(
+    paths,
+    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
+    "../../VC7/bin",
+    this->GetMakefile());
+  AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
+  paths.push_back(
+    "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
+  paths.push_back(
+    "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
+#endif
+
+  std::string program = cmSystemTools::FindProgram(search, paths);
+  if (!program.empty()) {
+    command = { program };
+    return true;
+  }
+
+  // Couldn't find it
+  return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name)
+{
+  cmsys::RegularExpressionMatch match;
+
+  for (auto const& regex : this->PreIncludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return false;
+    }
+  }
+
+  for (auto const& regex : this->PreExcludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name)
+{
+  cmsys::RegularExpressionMatch match;
+
+  for (auto const& regex : this->PostIncludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return false;
+    }
+  }
+
+  for (auto const& regex : this->PostExcludeRegexes) {
+    if (regex.find(name.c_str(), match)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name,
+                                                 const std::string& path,
+                                                 bool& unique)
+{
+  auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
+  unique = true;
+  for (auto const& other : it->second) {
+    if (cmSystemTools::SameFile(path, other)) {
+      unique = false;
+      break;
+    }
+  }
+  it->second.insert(path);
+}
+
+void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
+{
+  this->UnresolvedPaths.insert(name);
+}
+
+cmMakefile* cmRuntimeDependencyArchive::GetMakefile()
+{
+  return &this->Status.GetMakefile();
+}
+
+const std::map<std::string, std::set<std::string>>&
+cmRuntimeDependencyArchive::GetResolvedPaths()
+{
+  return this->ResolvedPaths;
+}
+
+const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
+{
+  return this->UnresolvedPaths;
+}
diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h
new file mode 100644
index 0000000..9e2dfb6
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.h
@@ -0,0 +1,70 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#ifndef cmRuntimeDependencyArchive_h
+#define cmRuntimeDependencyArchive_h
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmBinUtilsLinker.h"
+
+class cmExecutionStatus;
+class cmMakefile;
+
+class cmRuntimeDependencyArchive
+{
+public:
+  explicit cmRuntimeDependencyArchive(
+    cmExecutionStatus& status, std::vector<std::string> searchDirectories,
+    std::string bundleExecutable,
+    const std::vector<std::string>& preIncludeRegexes,
+    const std::vector<std::string>& preExcludeRegexes,
+    const std::vector<std::string>& postIncludeRegexes,
+    const std::vector<std::string>& postExcludeRegexes);
+  bool Prepare();
+  bool GetRuntimeDependencies(const std::vector<std::string>& executables,
+                              const std::vector<std::string>& libraries,
+                              const std::vector<std::string>& modules);
+
+  void SetError(const std::string& e);
+
+  std::string GetBundleExecutable();
+  const std::vector<std::string>& GetSearchDirectories();
+  std::string GetGetRuntimeDependenciesTool();
+  bool GetGetRuntimeDependenciesCommand(const std::string& search,
+                                        std::vector<std::string>& command);
+  bool IsPreExcluded(const std::string& name);
+  bool IsPostExcluded(const std::string& name);
+
+  void AddResolvedPath(const std::string& name, const std::string& path,
+                       bool& unique);
+  void AddUnresolvedPath(const std::string& name);
+
+  cmMakefile* GetMakefile();
+  const std::map<std::string, std::set<std::string>>& GetResolvedPaths();
+  const std::set<std::string>& GetUnresolvedPaths();
+
+private:
+  cmExecutionStatus& Status;
+  std::unique_ptr<cmBinUtilsLinker> Linker;
+
+  std::string GetRuntimeDependenciesTool;
+  std::vector<std::string> GetRuntimeDependenciesCommand;
+
+  std::vector<std::string> SearchDirectories;
+  std::string BundleExecutable;
+  std::vector<cmsys::RegularExpression> PreIncludeRegexes;
+  std::vector<cmsys::RegularExpression> PreExcludeRegexes;
+  std::vector<cmsys::RegularExpression> PostIncludeRegexes;
+  std::vector<cmsys::RegularExpression> PostExcludeRegexes;
+  std::map<std::string, std::set<std::string>> ResolvedPaths;
+  std::set<std::string> UnresolvedPaths;
+};
+
+#endif // cmRuntimeDependencyArchive_h
diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx
index 9182b41..adc0679 100644
--- a/Source/cmScriptGenerator.cxx
+++ b/Source/cmScriptGenerator.cxx
@@ -2,10 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmScriptGenerator.h"
 
-#include "cmSystemTools.h"
-
 #include <utility>
 
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 cmScriptGenerator::cmScriptGenerator(std::string config_var,
                                      std::vector<std::string> configurations)
   : RuntimeConfigVariable(std::move(config_var))
@@ -51,9 +52,8 @@
 
 std::string cmScriptGenerator::CreateConfigTest(const std::string& config)
 {
-  std::string result = "\"${";
-  result += this->RuntimeConfigVariable;
-  result += "}\" MATCHES \"^(";
+  std::string result =
+    cmStrCat("\"${", this->RuntimeConfigVariable, "}\" MATCHES \"^(");
   if (!config.empty()) {
     cmScriptGeneratorEncodeConfig(config, result);
   }
@@ -64,9 +64,8 @@
 std::string cmScriptGenerator::CreateConfigTest(
   std::vector<std::string> const& configs)
 {
-  std::string result = "\"${";
-  result += this->RuntimeConfigVariable;
-  result += "}\" MATCHES \"^(";
+  std::string result =
+    cmStrCat("\"${", this->RuntimeConfigVariable, "}\" MATCHES \"^(");
   const char* sep = "";
   for (std::string const& config : configs) {
     result += sep;
diff --git a/Source/cmScriptGenerator.h b/Source/cmScriptGenerator.h
index e334d5b..c8bb1ab 100644
--- a/Source/cmScriptGenerator.h
+++ b/Source/cmScriptGenerator.h
@@ -25,7 +25,7 @@
   }
   cmScriptGeneratorIndent Next(int step = 2) const
   {
-    return cmScriptGeneratorIndent(this->Level + step);
+    return { this->Level + step };
   }
 
 private:
@@ -56,7 +56,7 @@
                 std::vector<std::string> const& configurationTypes);
 
 protected:
-  typedef cmScriptGeneratorIndent Indent;
+  using Indent = cmScriptGeneratorIndent;
   virtual void GenerateScript(std::ostream& os);
   virtual void GenerateScriptConfigs(std::ostream& os, Indent indent);
   virtual void GenerateScriptActions(std::ostream& os, Indent indent);
diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx
index f98984e..d15ce57 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)
@@ -78,8 +78,7 @@
 
   // Get a path from a CMake variable.
   if (const char* value = this->FC->Makefile->GetDefinition(variable)) {
-    std::vector<std::string> expanded;
-    cmSystemTools::ExpandListArgument(value, expanded);
+    std::vector<std::string> expanded = cmExpandedList(value);
 
     for (std::string const& p : expanded) {
       this->AddPathInternal(
@@ -103,8 +102,7 @@
 
   // Get a path from a CMake variable.
   if (const char* value = this->FC->Makefile->GetDefinition(variable)) {
-    std::vector<std::string> expanded;
-    cmSystemTools::ExpandListArgument(value, expanded);
+    std::vector<std::string> expanded = cmExpandedList(value);
 
     this->AddPrefixPaths(
       expanded, this->FC->Makefile->GetCurrentSourceDirectory().c_str());
diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx
index 28cbdc0..52bde7c 100644
--- a/Source/cmSeparateArgumentsCommand.cxx
+++ b/Source/cmSeparateArgumentsCommand.cxx
@@ -3,19 +3,18 @@
 #include "cmSeparateArgumentsCommand.h"
 
 #include <algorithm>
-#include <sstream>
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmSeparateArgumentsCommand
-bool cmSeparateArgumentsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmSeparateArgumentsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("must be given at least one argument.");
+    status.SetError("must be given at least one argument.");
     return false;
   }
 
@@ -57,19 +56,17 @@
       command = arg;
       doing = DoingNone;
     } else {
-      std::ostringstream e;
-      e << "given unknown argument " << arg;
-      this->SetError(e.str());
+      status.SetError(cmStrCat("given unknown argument ", arg));
       return false;
     }
   }
 
   if (mode == ModeOld) {
     // Original space-replacement version of command.
-    if (const char* def = this->Makefile->GetDefinition(var)) {
+    if (const char* def = status.GetMakefile().GetDefinition(var)) {
       std::string value = def;
       std::replace(value.begin(), value.end(), ' ', ';');
-      this->Makefile->AddDefinition(var, value.c_str());
+      status.GetMakefile().AddDefinition(var, value);
     }
   } else {
     // Parse the command line.
@@ -97,7 +94,7 @@
         value += si;
       }
     }
-    this->Makefile->AddDefinition(var, value.c_str());
+    status.GetMakefile().AddDefinition(var, value);
   }
 
   return true;
diff --git a/Source/cmSeparateArgumentsCommand.h b/Source/cmSeparateArgumentsCommand.h
index 988ad23..e000c51 100644
--- a/Source/cmSeparateArgumentsCommand.h
+++ b/Source/cmSeparateArgumentsCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmSeparateArgumentsCommand
+/**
  * \brief separate_arguments command
  *
  * cmSeparateArgumentsCommand implements the separate_arguments CMake command
  */
-class cmSeparateArgumentsCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmSeparateArgumentsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmSeparateArgumentsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx
index 1903fd9..3b2e5f3 100644
--- a/Source/cmServer.cxx
+++ b/Source/cmServer.cxx
@@ -2,25 +2,28 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmServer.h"
 
-#include "cmAlgorithms.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <mutex>
+#include <utility>
+
+#include <cm/memory>
+#include <cm/shared_mutex>
+
+#include "cmsys/FStream.hxx"
+
+#include "cm_jsoncpp_reader.h"
+#include "cm_jsoncpp_writer.h"
+
 #include "cmConnection.h"
 #include "cmFileMonitor.h"
 #include "cmJsonObjectDictionary.h"
 #include "cmServerDictionary.h"
 #include "cmServerProtocol.h"
 #include "cmSystemTools.h"
-#include "cm_jsoncpp_reader.h"
-#include "cm_jsoncpp_writer.h"
 #include "cmake.h"
-#include "cmsys/FStream.hxx"
-
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <memory>
-#include <mutex>
-#include <utility>
 
 void on_signal(uv_signal_t* signal, int signum)
 {
diff --git a/Source/cmServer.h b/Source/cmServer.h
index aba4924..3d7027b 100644
--- a/Source/cmServer.h
+++ b/Source/cmServer.h
@@ -4,16 +4,17 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cm/shared_mutex>
+
 #include "cm_jsoncpp_value.h"
-#include "cm_thread.hxx"
 #include "cm_uv.h"
 
 #include "cmUVHandlePtr.h"
 
-#include <memory> // IWYU pragma: keep
-#include <string>
-#include <vector>
-
 class cmConnection;
 class cmFileMonitor;
 class cmServerProtocol;
diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx
index a878890..2791972 100644
--- a/Source/cmServerConnection.cxx
+++ b/Source/cmServerConnection.cxx
@@ -1,11 +1,13 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmConfigure.h"
+
 #include "cmServerConnection.h"
 
-#include "cmConfigure.h"
+#include "cm_uv.h"
+
 #include "cmServer.h"
 #include "cmServerDictionary.h"
-#include "cm_uv.h"
 
 #ifdef _WIN32
 #  include "io.h"
diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx
index 558391f..56003df 100644
--- a/Source/cmServerProtocol.cxx
+++ b/Source/cmServerProtocol.cxx
@@ -2,6 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmServerProtocol.h"
 
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+
+#include "cm_uv.h"
+
 #include "cmAlgorithms.h"
 #include "cmExternalMakefileProjectGenerator.h"
 #include "cmFileMonitor.h"
@@ -13,17 +24,8 @@
 #include "cmServerDictionary.h"
 #include "cmState.h"
 #include "cmSystemTools.h"
-#include "cm_uv.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <cassert>
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
 // Get rid of some windows macros:
 #undef max
 
@@ -166,7 +168,7 @@
 
 std::pair<int, int> cmServerProtocol1::ProtocolVersion() const
 {
-  return std::make_pair(1, 2);
+  return { 1, 2 };
 }
 
 static void setErrorMessage(std::string* errorMessage, const std::string& text)
@@ -378,8 +380,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);
 
@@ -434,7 +435,7 @@
     keys = allKeys;
   } else {
     for (auto const& i : keys) {
-      if (std::find(allKeys.begin(), allKeys.end(), i) == allKeys.end()) {
+      if (!cmContains(allKeys, i)) {
         return request.ReportError("Key \"" + i + "\" not found in cache.");
       }
     }
diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h
index 2f55a20..8446c3e 100644
--- a/Source/cmServerProtocol.h
+++ b/Source/cmServerProtocol.h
@@ -4,13 +4,14 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_jsoncpp_value.h"
-#include "cmake.h"
-
 #include <memory>
 #include <string>
 #include <utility>
 
+#include "cm_jsoncpp_value.h"
+
+#include "cmake.h"
+
 class cmConnection;
 class cmFileMonitor;
 class cmServer;
@@ -80,7 +81,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 +107,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/cmSetCommand.cxx b/Source/cmSetCommand.cxx
index 41555e8..8c3a4cb 100644
--- a/Source/cmSetCommand.cxx
+++ b/Source/cmSetCommand.cxx
@@ -2,22 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSetCommand.h"
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmSetCommand
-bool cmSetCommand::InitialPass(std::vector<std::string> const& args,
-                               cmExecutionStatus&)
+bool cmSetCommand(std::vector<std::string> const& args,
+                  cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -45,7 +44,7 @@
         std::string m = "Only the first value argument is used when setting "
                         "an environment variable.  Argument '" +
           args[2] + "' and later are unused.";
-        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
+        status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
       }
       return true;
     }
@@ -59,13 +58,13 @@
 
   // SET (VAR) // Removes the definition of VAR.
   if (args.size() == 1) {
-    this->Makefile->RemoveDefinition(variable);
+    status.GetMakefile().RemoveDefinition(variable);
     return true;
   }
   // SET (VAR PARENT_SCOPE) // Removes the definition of VAR
   // in the parent scope.
   if (args.size() == 2 && args.back() == "PARENT_SCOPE") {
-    this->Makefile->RaiseScope(variable, nullptr);
+    status.GetMakefile().RaiseScope(variable, nullptr);
     return true;
   }
 
@@ -106,7 +105,7 @@
   value = cmJoin(cmMakeRange(args).advance(1).retreat(ignoreLastArgs), ";");
 
   if (parentScope) {
-    this->Makefile->RaiseScope(variable, value.c_str());
+    status.GetMakefile().RaiseScope(variable, value.c_str());
     return true;
   }
 
@@ -116,7 +115,7 @@
   if ((args.back() == "CACHE") ||
       (args.size() > 1 && args[args.size() - 2] == "CACHE") ||
       (force && !cache)) {
-    this->SetError("given invalid arguments for CACHE mode.");
+    status.SetError("given invalid arguments for CACHE mode.");
     return false;
   }
 
@@ -125,7 +124,7 @@
     if (!cmState::StringToCacheEntryType(args[cacheStart + 1].c_str(), type)) {
       std::string m = "implicitly converting '" + args[cacheStart + 1] +
         "' to 'STRING' type.";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
+      status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, m);
       // Setting this may not be required, since it's
       // initialized as a string. Keeping this here to
       // ensure that the type is actually converting to a string.
@@ -135,7 +134,7 @@
   }
 
   // see if this is already in the cache
-  cmState* state = this->Makefile->GetState();
+  cmState* state = status.GetMakefile().GetState();
   const char* existingValue = state->GetCacheEntryValue(variable);
   if (existingValue &&
       (state->GetCacheEntryType(variable) != cmStateEnums::UNINITIALIZED)) {
@@ -150,11 +149,11 @@
 
   // if it is meant to be in the cache then define it in the cache
   if (cache) {
-    this->Makefile->AddCacheDefinition(variable, value.c_str(), docstring,
-                                       type, force);
+    status.GetMakefile().AddCacheDefinition(variable, value.c_str(), docstring,
+                                            type, force);
   } else {
     // add the definition
-    this->Makefile->AddDefinition(variable, value.c_str());
+    status.GetMakefile().AddDefinition(variable, value);
   }
   return true;
 }
diff --git a/Source/cmSetCommand.h b/Source/cmSetCommand.h
index 76e3eae..0973d33 100644
--- a/Source/cmSetCommand.h
+++ b/Source/cmSetCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmSetCommand
+/**
  * \brief Set a CMAKE variable
  *
  * cmSetCommand sets a variable to a value with expansion.
  */
-class cmSetCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmSetCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmSetCommand(std::vector<std::string> const& args,
+                  cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSetDirectoryPropertiesCommand.cxx b/Source/cmSetDirectoryPropertiesCommand.cxx
index 8d3961a..35daca6 100644
--- a/Source/cmSetDirectoryPropertiesCommand.cxx
+++ b/Source/cmSetDirectoryPropertiesCommand.cxx
@@ -2,31 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSetDirectoryPropertiesCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 
-class cmExecutionStatus;
+namespace {
+bool RunCommand(cmMakefile& mf, std::vector<std::string>::const_iterator ait,
+                std::vector<std::string>::const_iterator aitend,
+                std::string& errors);
+}
 
 // cmSetDirectoryPropertiesCommand
-bool cmSetDirectoryPropertiesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args,
+                                     cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   std::string errors;
-  bool ret = cmSetDirectoryPropertiesCommand::RunCommand(
-    this->Makefile, args.begin() + 1, args.end(), errors);
+  bool ret =
+    RunCommand(status.GetMakefile(), args.begin() + 1, args.end(), errors);
   if (!ret) {
-    this->SetError(errors);
+    status.SetError(errors);
   }
   return ret;
 }
 
-bool cmSetDirectoryPropertiesCommand::RunCommand(
-  cmMakefile* mf, std::vector<std::string>::const_iterator ait,
-  std::vector<std::string>::const_iterator aitend, std::string& errors)
+namespace {
+bool RunCommand(cmMakefile& mf, std::vector<std::string>::const_iterator ait,
+                std::vector<std::string>::const_iterator aitend,
+                std::string& errors)
 {
   for (; ait != aitend; ait += 2) {
     if (ait + 1 == aitend) {
@@ -43,8 +49,9 @@
       errors = "Commands and macros cannot be set using SET_CMAKE_PROPERTIES";
       return false;
     }
-    mf->SetProperty(prop, value.c_str());
+    mf.SetProperty(prop, value.c_str());
   }
 
   return true;
 }
+}
diff --git a/Source/cmSetDirectoryPropertiesCommand.h b/Source/cmSetDirectoryPropertiesCommand.h
index 473347c..c243dd7 100644
--- a/Source/cmSetDirectoryPropertiesCommand.h
+++ b/Source/cmSetDirectoryPropertiesCommand.h
@@ -8,30 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmMakefile;
 
-class cmSetDirectoryPropertiesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmSetDirectoryPropertiesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   * Static entry point for use by other commands
-   */
-  static bool RunCommand(cmMakefile* mf,
-                         std::vector<std::string>::const_iterator ait,
-                         std::vector<std::string>::const_iterator aitend,
-                         std::string& errors);
-};
+bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args,
+                                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx
index e9343c7..112d832 100644
--- a/Source/cmSetPropertyCommand.cxx
+++ b/Source/cmSetPropertyCommand.cxx
@@ -2,8 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSetPropertyCommand.h"
 
+#include <set>
 #include <sstream>
 
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
 #include "cmMakefile.h"
@@ -11,25 +13,72 @@
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTest.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
-cmSetPropertyCommand::cmSetPropertyCommand()
-{
-  this->AppendMode = false;
-  this->AppendAsString = false;
-  this->Remove = true;
+namespace {
+bool HandleGlobalMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue, bool appendAsString,
+                      bool appendMode, bool remove);
+bool HandleDirectoryMode(cmExecutionStatus& status,
+                         const std::set<std::string>& names,
+                         const std::string& propertyName,
+                         const std::string& propertyValue, bool appendAsString,
+                         bool appendMode, bool remove);
+bool HandleTargetMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue, bool appendAsString,
+                      bool appendMode, bool remove);
+bool HandleTarget(cmTarget* target, cmMakefile& makefile,
+                  const std::string& propertyName,
+                  const std::string& propertyValue, bool appendAsString,
+                  bool appendMode, bool remove);
+bool HandleSourceMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue, bool appendAsString,
+                      bool appendMode, bool remove);
+bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
+                  const std::string& propertyValue, bool appendAsString,
+                  bool appendMode, bool remove);
+bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
+                    const std::string& propertyName,
+                    const std::string& propertyValue, bool appendAsString,
+                    bool appendMode, bool remove);
+bool HandleTest(cmTest* test, const std::string& propertyName,
+                const std::string& propertyValue, bool appendAsString,
+                bool appendMode, bool remove);
+bool HandleCacheMode(cmExecutionStatus& status,
+                     const std::set<std::string>& names,
+                     const std::string& propertyName,
+                     const std::string& propertyValue, bool appendAsString,
+                     bool appendMode, bool remove);
+bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
+                      const std::string& propertyName,
+                      const std::string& propertyValue, bool appendAsString,
+                      bool appendMode, bool remove);
+bool HandleInstallMode(cmExecutionStatus& status,
+                       const std::set<std::string>& names,
+                       const std::string& propertyName,
+                       const std::string& propertyValue, bool appendAsString,
+                       bool appendMode, bool remove);
+bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
+                   const std::string& propertyName,
+                   const std::string& propertyValue, bool appendAsString,
+                   bool appendMode, bool remove);
 }
 
-bool cmSetPropertyCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+bool cmSetPropertyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -51,14 +100,20 @@
   } else if (scopeName == "INSTALL") {
     scope = cmProperty::INSTALL;
   } else {
-    std::ostringstream e;
-    e << "given invalid scope " << scopeName << ".  "
-      << "Valid scopes are GLOBAL, DIRECTORY, "
-         "TARGET, SOURCE, TEST, CACHE, INSTALL.";
-    this->SetError(e.str());
+    status.SetError(cmStrCat("given invalid scope ", scopeName,
+                             ".  "
+                             "Valid scopes are GLOBAL, DIRECTORY, "
+                             "TARGET, SOURCE, TEST, CACHE, INSTALL."));
     return false;
   }
 
+  bool appendAsString = false;
+  bool appendMode = false;
+  bool remove = true;
+  std::set<std::string> names;
+  std::string propertyName;
+  std::string propertyValue;
+
   // Parse the rest of the arguments up to the values.
   enum Doing
   {
@@ -74,54 +129,59 @@
       doing = DoingProperty;
     } else if (arg == "APPEND") {
       doing = DoingNone;
-      this->AppendMode = true;
-      this->Remove = false;
-      this->AppendAsString = false;
+      appendMode = true;
+      remove = false;
+      appendAsString = false;
     } else if (arg == "APPEND_STRING") {
       doing = DoingNone;
-      this->AppendMode = true;
-      this->Remove = false;
-      this->AppendAsString = true;
+      appendMode = true;
+      remove = false;
+      appendAsString = true;
     } else if (doing == DoingNames) {
-      this->Names.insert(arg);
+      names.insert(arg);
     } else if (doing == DoingProperty) {
-      this->PropertyName = arg;
+      propertyName = arg;
       doing = DoingValues;
     } else if (doing == DoingValues) {
-      this->PropertyValue += sep;
+      propertyValue += sep;
       sep = ";";
-      this->PropertyValue += arg;
-      this->Remove = false;
+      propertyValue += arg;
+      remove = false;
     } else {
-      std::ostringstream e;
-      e << "given invalid argument \"" << arg << "\".";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("given invalid argument \"", arg, "\"."));
       return false;
     }
   }
 
   // Make sure a property name was found.
-  if (this->PropertyName.empty()) {
-    this->SetError("not given a PROPERTY <name> argument.");
+  if (propertyName.empty()) {
+    status.SetError("not given a PROPERTY <name> argument.");
     return false;
   }
 
   // Dispatch property setting.
   switch (scope) {
     case cmProperty::GLOBAL:
-      return this->HandleGlobalMode();
+      return HandleGlobalMode(status, names, propertyName, propertyValue,
+                              appendAsString, appendMode, remove);
     case cmProperty::DIRECTORY:
-      return this->HandleDirectoryMode();
+      return HandleDirectoryMode(status, names, propertyName, propertyValue,
+                                 appendAsString, appendMode, remove);
     case cmProperty::TARGET:
-      return this->HandleTargetMode();
+      return HandleTargetMode(status, names, propertyName, propertyValue,
+                              appendAsString, appendMode, remove);
     case cmProperty::SOURCE_FILE:
-      return this->HandleSourceMode();
+      return HandleSourceMode(status, names, propertyName, propertyValue,
+                              appendAsString, appendMode, remove);
     case cmProperty::TEST:
-      return this->HandleTestMode();
+      return HandleTestMode(status, names, propertyName, propertyValue,
+                            appendAsString, appendMode, remove);
     case cmProperty::CACHE:
-      return this->HandleCacheMode();
+      return HandleCacheMode(status, names, propertyName, propertyValue,
+                             appendAsString, appendMode, remove);
     case cmProperty::INSTALL:
-      return this->HandleInstallMode();
+      return HandleInstallMode(status, names, propertyName, propertyValue,
+                               appendAsString, appendMode, remove);
 
     case cmProperty::VARIABLE:
     case cmProperty::CACHED_VARIABLE:
@@ -130,57 +190,66 @@
   return true;
 }
 
-bool cmSetPropertyCommand::HandleGlobalMode()
+namespace {
+bool HandleGlobalMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue,
+                      const bool appendAsString, const bool appendMode,
+                      const bool remove)
 {
-  if (!this->Names.empty()) {
-    this->SetError("given names for GLOBAL scope.");
+  if (!names.empty()) {
+    status.SetError("given names for GLOBAL scope.");
     return false;
   }
 
   // Set or append the property.
-  cmake* cm = this->Makefile->GetCMakeInstance();
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
+  cmake* cm = status.GetMakefile().GetCMakeInstance();
+  const char* value = propertyValue.c_str();
+  if (remove) {
     value = nullptr;
   }
-  if (this->AppendMode) {
-    cm->AppendProperty(name, value ? value : "", this->AppendAsString);
+  if (appendMode) {
+    cm->AppendProperty(propertyName, value ? value : "", appendAsString);
   } else {
-    cm->SetProperty(name, value);
+    cm->SetProperty(propertyName, value);
   }
 
   return true;
 }
 
-bool cmSetPropertyCommand::HandleDirectoryMode()
+bool HandleDirectoryMode(cmExecutionStatus& status,
+                         const std::set<std::string>& names,
+                         const std::string& propertyName,
+                         const std::string& propertyValue,
+                         const bool appendAsString, const bool appendMode,
+                         const bool remove)
 {
-  if (this->Names.size() > 1) {
-    this->SetError("allows at most one name for DIRECTORY scope.");
+  if (names.size() > 1) {
+    status.SetError("allows at most one name for DIRECTORY scope.");
     return false;
   }
 
   // Default to the current directory.
-  cmMakefile* mf = this->Makefile;
+  cmMakefile* mf = &status.GetMakefile();
 
   // Lookup the directory if given.
-  if (!this->Names.empty()) {
+  if (!names.empty()) {
     // Construct the directory name.  Interpret relative paths with
     // respect to the current directory.
-    std::string dir = *this->Names.begin();
+    std::string dir = *names.begin();
     if (!cmSystemTools::FileIsFullPath(dir)) {
-      dir = this->Makefile->GetCurrentSourceDirectory();
-      dir += "/";
-      dir += *this->Names.begin();
+      dir = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
+                     *names.begin());
     }
 
     // The local generators are associated with collapsed paths.
     dir = cmSystemTools::CollapseFullPath(dir);
 
-    mf = this->Makefile->GetGlobalGenerator()->FindMakefile(dir);
+    mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir);
     if (!mf) {
       // Could not find the directory.
-      this->SetError(
+      status.SetError(
         "DIRECTORY scope provided but requested directory was not found. "
         "This could be because the directory argument was invalid or, "
         "it is valid but has not been processed yet.");
@@ -189,109 +258,124 @@
   }
 
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
+  const char* value = propertyValue.c_str();
+  if (remove) {
     value = nullptr;
   }
-  if (this->AppendMode) {
-    mf->AppendProperty(name, value ? value : "", this->AppendAsString);
+  if (appendMode) {
+    mf->AppendProperty(propertyName, value ? value : "", appendAsString);
   } else {
-    mf->SetProperty(name, value);
+    mf->SetProperty(propertyName, value);
   }
 
   return true;
 }
 
-bool cmSetPropertyCommand::HandleTargetMode()
+bool HandleTargetMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue,
+                      const bool appendAsString, const bool appendMode,
+                      const bool remove)
 {
-  for (std::string const& name : this->Names) {
-    if (this->Makefile->IsAlias(name)) {
-      this->SetError("can not be used on an ALIAS target.");
+  for (std::string const& name : names) {
+    if (status.GetMakefile().IsAlias(name)) {
+      status.SetError("can not be used on an ALIAS target.");
       return false;
     }
-    if (cmTarget* target = this->Makefile->FindTargetToUse(name)) {
+    if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
       // Handle the current target.
-      if (!this->HandleTarget(target)) {
+      if (!HandleTarget(target, status.GetMakefile(), propertyName,
+                        propertyValue, appendAsString, appendMode, remove)) {
         return false;
       }
     } else {
-      std::ostringstream e;
-      e << "could not find TARGET " << name
-        << ".  Perhaps it has not yet been created.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("could not find TARGET ", name,
+                               ".  Perhaps it has not yet been created."));
       return false;
     }
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleTarget(cmTarget* target)
+bool HandleTarget(cmTarget* target, cmMakefile& makefile,
+                  const std::string& propertyName,
+                  const std::string& propertyValue, const bool appendAsString,
+                  const bool appendMode, const bool remove)
 {
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
+  const char* value = propertyValue.c_str();
+  if (remove) {
     value = nullptr;
   }
-  if (this->AppendMode) {
-    target->AppendProperty(name, value, this->AppendAsString);
+  if (appendMode) {
+    target->AppendProperty(propertyName, value, appendAsString);
   } else {
-    target->SetProperty(name, value);
+    target->SetProperty(propertyName, value);
   }
 
   // Check the resulting value.
-  target->CheckProperty(name, this->Makefile);
+  target->CheckProperty(propertyName, &makefile);
 
   return true;
 }
 
-bool cmSetPropertyCommand::HandleSourceMode()
+bool HandleSourceMode(cmExecutionStatus& status,
+                      const std::set<std::string>& names,
+                      const std::string& propertyName,
+                      const std::string& propertyValue,
+                      const bool appendAsString, const bool appendMode,
+                      const bool remove)
 {
-  for (std::string const& name : this->Names) {
+  for (std::string const& name : names) {
     // Get the source file.
-    if (cmSourceFile* sf = this->Makefile->GetOrCreateSource(name)) {
-      if (!this->HandleSource(sf)) {
+    if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(name)) {
+      if (!HandleSource(sf, propertyName, propertyValue, appendAsString,
+                        appendMode, remove)) {
         return false;
       }
     } else {
-      std::ostringstream e;
-      e << "given SOURCE name that could not be found or created: " << name;
-      this->SetError(e.str());
+      status.SetError(cmStrCat(
+        "given SOURCE name that could not be found or created: ", name));
       return false;
     }
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleSource(cmSourceFile* sf)
+bool HandleSource(cmSourceFile* sf, const std::string& propertyName,
+                  const std::string& propertyValue, const bool appendAsString,
+                  const bool appendMode, const bool remove)
 {
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
+  const char* value = propertyValue.c_str();
+  if (remove) {
     value = nullptr;
   }
 
-  if (this->AppendMode) {
-    sf->AppendProperty(name, value, this->AppendAsString);
+  if (appendMode) {
+    sf->AppendProperty(propertyName, value, appendAsString);
   } else {
-    sf->SetProperty(name, value);
+    sf->SetProperty(propertyName, value);
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleTestMode()
+bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
+                    const std::string& propertyName,
+                    const std::string& propertyValue,
+                    const bool appendAsString, const bool appendMode,
+                    const bool remove)
 {
   // Look for tests with all names given.
   std::set<std::string>::iterator next;
-  for (std::set<std::string>::iterator ni = this->Names.begin();
-       ni != this->Names.end(); ni = next) {
+  for (auto ni = names.begin(); ni != names.end(); ni = next) {
     next = ni;
     ++next;
-    if (cmTest* test = this->Makefile->GetTest(*ni)) {
-      if (this->HandleTest(test)) {
-        this->Names.erase(ni);
+    if (cmTest* test = status.GetMakefile().GetTest(*ni)) {
+      if (HandleTest(test, propertyName, propertyValue, appendAsString,
+                     appendMode, remove)) {
+        names.erase(ni);
       } else {
         return false;
       }
@@ -299,137 +383,145 @@
   }
 
   // Names that are still left were not found.
-  if (!this->Names.empty()) {
+  if (!names.empty()) {
     std::ostringstream e;
     e << "given TEST names that do not exist:\n";
-    for (std::string const& name : this->Names) {
+    for (std::string const& name : names) {
       e << "  " << name << "\n";
     }
-    this->SetError(e.str());
+    status.SetError(e.str());
     return false;
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleTest(cmTest* test)
+bool HandleTest(cmTest* test, const std::string& propertyName,
+                const std::string& propertyValue, const bool appendAsString,
+                const bool appendMode, const bool remove)
 {
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
+  const char* value = propertyValue.c_str();
+  if (remove) {
     value = nullptr;
   }
-  if (this->AppendMode) {
-    test->AppendProperty(name, value, this->AppendAsString);
+  if (appendMode) {
+    test->AppendProperty(propertyName, value, appendAsString);
   } else {
-    test->SetProperty(name, value);
+    test->SetProperty(propertyName, value);
   }
 
   return true;
 }
 
-bool cmSetPropertyCommand::HandleCacheMode()
+bool HandleCacheMode(cmExecutionStatus& status,
+                     const std::set<std::string>& names,
+                     const std::string& propertyName,
+                     const std::string& propertyValue,
+                     const bool appendAsString, const bool appendMode,
+                     const bool remove)
 {
-  if (this->PropertyName == "ADVANCED") {
-    if (!this->Remove && !cmSystemTools::IsOn(this->PropertyValue) &&
-        !cmSystemTools::IsOff(this->PropertyValue)) {
-      std::ostringstream e;
-      e << "given non-boolean value \"" << this->PropertyValue
-        << R"(" for CACHE property "ADVANCED".  )";
-      this->SetError(e.str());
+  if (propertyName == "ADVANCED") {
+    if (!remove && !cmIsOn(propertyValue) && !cmIsOff(propertyValue)) {
+      status.SetError(cmStrCat("given non-boolean value \"", propertyValue,
+                               R"(" for CACHE property "ADVANCED".  )"));
       return false;
     }
-  } else if (this->PropertyName == "TYPE") {
-    if (!cmState::IsCacheEntryType(this->PropertyValue)) {
-      std::ostringstream e;
-      e << "given invalid CACHE entry TYPE \"" << this->PropertyValue << "\"";
-      this->SetError(e.str());
+  } else if (propertyName == "TYPE") {
+    if (!cmState::IsCacheEntryType(propertyValue)) {
+      status.SetError(
+        cmStrCat("given invalid CACHE entry TYPE \"", propertyValue, "\""));
       return false;
     }
-  } else if (this->PropertyName != "HELPSTRING" &&
-             this->PropertyName != "STRINGS" &&
-             this->PropertyName != "VALUE") {
-    std::ostringstream e;
-    e << "given invalid CACHE property " << this->PropertyName << ".  "
-      << "Settable CACHE properties are: "
-      << "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE.";
-    this->SetError(e.str());
+  } else if (propertyName != "HELPSTRING" && propertyName != "STRINGS" &&
+             propertyName != "VALUE") {
+    status.SetError(
+      cmStrCat("given invalid CACHE property ", propertyName,
+               ".  "
+               "Settable CACHE properties are: "
+               "ADVANCED, HELPSTRING, STRINGS, TYPE, and VALUE."));
     return false;
   }
 
-  for (std::string const& name : this->Names) {
+  for (std::string const& name : names) {
     // Get the source file.
-    cmMakefile* mf = this->GetMakefile();
-    cmake* cm = mf->GetCMakeInstance();
+    cmake* cm = status.GetMakefile().GetCMakeInstance();
     const char* existingValue = cm->GetState()->GetCacheEntryValue(name);
     if (existingValue) {
-      if (!this->HandleCacheEntry(name)) {
+      if (!HandleCacheEntry(name, status.GetMakefile(), propertyName,
+                            propertyValue, appendAsString, appendMode,
+                            remove)) {
         return false;
       }
     } else {
-      std::ostringstream e;
-      e << "could not find CACHE variable " << name
-        << ".  Perhaps it has not yet been created.";
-      this->SetError(e.str());
+      status.SetError(cmStrCat("could not find CACHE variable ", name,
+                               ".  Perhaps it has not yet been created."));
       return false;
     }
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleCacheEntry(std::string const& cacheKey)
+bool HandleCacheEntry(std::string const& cacheKey, const cmMakefile& makefile,
+                      const std::string& propertyName,
+                      const std::string& propertyValue,
+                      const bool appendAsString, const bool appendMode,
+                      const bool remove)
 {
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-  const char* value = this->PropertyValue.c_str();
-  cmState* state = this->Makefile->GetState();
-  if (this->Remove) {
-    state->RemoveCacheEntryProperty(cacheKey, name);
+  const char* value = propertyValue.c_str();
+  cmState* state = makefile.GetState();
+  if (remove) {
+    state->RemoveCacheEntryProperty(cacheKey, propertyName);
   }
-  if (this->AppendMode) {
-    state->AppendCacheEntryProperty(cacheKey, name, value,
-                                    this->AppendAsString);
+  if (appendMode) {
+    state->AppendCacheEntryProperty(cacheKey, propertyName, value,
+                                    appendAsString);
   } else {
-    state->SetCacheEntryProperty(cacheKey, name, value);
+    state->SetCacheEntryProperty(cacheKey, propertyName, value);
   }
 
   return true;
 }
 
-bool cmSetPropertyCommand::HandleInstallMode()
+bool HandleInstallMode(cmExecutionStatus& status,
+                       const std::set<std::string>& names,
+                       const std::string& propertyName,
+                       const std::string& propertyValue,
+                       const bool appendAsString, const bool appendMode,
+                       const bool remove)
 {
-  cmake* cm = this->Makefile->GetCMakeInstance();
+  cmake* cm = status.GetMakefile().GetCMakeInstance();
 
-  for (std::string const& name : this->Names) {
+  for (std::string const& name : names) {
     if (cmInstalledFile* file =
-          cm->GetOrCreateInstalledFile(this->Makefile, name)) {
-      if (!this->HandleInstall(file)) {
+          cm->GetOrCreateInstalledFile(&status.GetMakefile(), name)) {
+      if (!HandleInstall(file, status.GetMakefile(), propertyName,
+                         propertyValue, appendAsString, appendMode, remove)) {
         return false;
       }
     } else {
-      std::ostringstream e;
-      e << "given INSTALL name that could not be found or created: " << name;
-      this->SetError(e.str());
+      status.SetError(cmStrCat(
+        "given INSTALL name that could not be found or created: ", name));
       return false;
     }
   }
   return true;
 }
 
-bool cmSetPropertyCommand::HandleInstall(cmInstalledFile* file)
+bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
+                   const std::string& propertyName,
+                   const std::string& propertyValue, const bool appendAsString,
+                   const bool appendMode, const bool remove)
 {
   // Set or append the property.
-  std::string const& name = this->PropertyName;
-
-  cmMakefile* mf = this->Makefile;
-
-  const char* value = this->PropertyValue.c_str();
-  if (this->Remove) {
-    file->RemoveProperty(name);
-  } else if (this->AppendMode) {
-    file->AppendProperty(mf, name, value, this->AppendAsString);
+  const char* value = propertyValue.c_str();
+  if (remove) {
+    file->RemoveProperty(propertyName);
+  } else if (appendMode) {
+    file->AppendProperty(&makefile, propertyName, value, appendAsString);
   } else {
-    file->SetProperty(mf, name, value);
+    file->SetProperty(&makefile, propertyName, value);
   }
   return true;
 }
+}
diff --git a/Source/cmSetPropertyCommand.h b/Source/cmSetPropertyCommand.h
index f1126bb..ec36f84 100644
--- a/Source/cmSetPropertyCommand.h
+++ b/Source/cmSetPropertyCommand.h
@@ -5,53 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <set>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmInstalledFile;
-class cmSourceFile;
-class cmTarget;
-class cmTest;
 
-class cmSetPropertyCommand : public cmCommand
-{
-public:
-  cmSetPropertyCommand();
-
-  cmCommand* Clone() override { return new cmSetPropertyCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  std::set<std::string> Names;
-  std::string PropertyName;
-  std::string PropertyValue;
-  bool Remove;
-  bool AppendMode;
-  bool AppendAsString;
-
-  // Implementation of each property type.
-  bool HandleGlobalMode();
-  bool HandleDirectoryMode();
-  bool HandleTargetMode();
-  bool HandleTarget(cmTarget* target);
-  bool HandleSourceMode();
-  bool HandleSource(cmSourceFile* sf);
-  bool HandleTestMode();
-  bool HandleTest(cmTest* test);
-  bool HandleCacheMode();
-  bool HandleCacheEntry(std::string const&);
-  bool HandleInstallMode();
-  bool HandleInstall(cmInstalledFile* file);
-};
+bool cmSetPropertyCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSetSourceFilesPropertiesCommand.cxx b/Source/cmSetSourceFilesPropertiesCommand.cxx
index 9388e7c..7ff604b 100644
--- a/Source/cmSetSourceFilesPropertiesCommand.cxx
+++ b/Source/cmSetSourceFilesPropertiesCommand.cxx
@@ -2,18 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSetSourceFilesPropertiesCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 
-class cmExecutionStatus;
+static bool RunCommand(cmMakefile* mf,
+                       std::vector<std::string>::const_iterator filebeg,
+                       std::vector<std::string>::const_iterator fileend,
+                       std::vector<std::string>::const_iterator propbeg,
+                       std::vector<std::string>::const_iterator propend,
+                       std::string& errors);
 
-// cmSetSourceFilesPropertiesCommand
-bool cmSetSourceFilesPropertiesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -29,22 +34,24 @@
     ++j;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // now call the worker function
   std::string errors;
-  bool ret = cmSetSourceFilesPropertiesCommand::RunCommand(
-    this->Makefile, args.begin(), args.begin() + numFiles,
-    args.begin() + numFiles, args.end(), errors);
+  bool ret = RunCommand(&mf, args.begin(), args.begin() + numFiles,
+                        args.begin() + numFiles, args.end(), errors);
   if (!ret) {
-    this->SetError(errors);
+    status.SetError(errors);
   }
   return ret;
 }
 
-bool cmSetSourceFilesPropertiesCommand::RunCommand(
-  cmMakefile* mf, std::vector<std::string>::const_iterator filebeg,
-  std::vector<std::string>::const_iterator fileend,
-  std::vector<std::string>::const_iterator propbeg,
-  std::vector<std::string>::const_iterator propend, std::string& errors)
+static bool RunCommand(cmMakefile* mf,
+                       std::vector<std::string>::const_iterator filebeg,
+                       std::vector<std::string>::const_iterator fileend,
+                       std::vector<std::string>::const_iterator propbeg,
+                       std::vector<std::string>::const_iterator propend,
+                       std::string& errors)
 {
   std::vector<std::string> propertyPairs;
   bool generated = false;
@@ -87,7 +94,7 @@
         propertyPairs.push_back(*j);
         if (*j == "GENERATED") {
           ++j;
-          if (j != propend && cmSystemTools::IsOn(*j)) {
+          if (j != propend && cmIsOn(*j)) {
             generated = true;
           }
         } else {
diff --git a/Source/cmSetSourceFilesPropertiesCommand.h b/Source/cmSetSourceFilesPropertiesCommand.h
index afb19f6..5eef785 100644
--- a/Source/cmSetSourceFilesPropertiesCommand.h
+++ b/Source/cmSetSourceFilesPropertiesCommand.h
@@ -8,29 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmMakefile;
 
-class cmSetSourceFilesPropertiesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmSetSourceFilesPropertiesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  static bool RunCommand(cmMakefile* mf,
-                         std::vector<std::string>::const_iterator filebeg,
-                         std::vector<std::string>::const_iterator fileend,
-                         std::vector<std::string>::const_iterator propbeg,
-                         std::vector<std::string>::const_iterator propend,
-                         std::string& errors);
-};
+bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSetTargetPropertiesCommand.cxx b/Source/cmSetTargetPropertiesCommand.cxx
index 1dc7e69..8d917db 100644
--- a/Source/cmSetTargetPropertiesCommand.cxx
+++ b/Source/cmSetTargetPropertiesCommand.cxx
@@ -5,30 +5,32 @@
 #include <iterator>
 
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 
-class cmExecutionStatus;
+static bool SetOneTarget(const std::string& tname,
+                         std::vector<std::string>& propertyPairs,
+                         cmMakefile* mf);
 
-// cmSetTargetPropertiesCommand
-bool cmSetTargetPropertiesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // first collect up the list of files
   std::vector<std::string> propertyPairs;
   int numFiles = 0;
-  std::vector<std::string>::const_iterator j;
-  for (j = args.begin(); j != args.end(); ++j) {
+  for (auto j = args.begin(); j != args.end(); ++j) {
     if (*j == "PROPERTIES") {
       // now loop through the rest of the arguments, new style
       ++j;
       if (std::distance(j, args.end()) % 2 != 0) {
-        this->SetError("called with incorrect number of arguments.");
+        status.SetError("called with incorrect number of arguments.");
         return false;
       }
       cmAppend(propertyPairs, j, args.end());
@@ -37,33 +39,32 @@
     numFiles++;
   }
   if (propertyPairs.empty()) {
-    this->SetError("called with illegal arguments, maybe missing "
-                   "a PROPERTIES specifier?");
+    status.SetError("called with illegal arguments, maybe missing "
+                    "a PROPERTIES specifier?");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // now loop over all the targets
-  int i;
-  for (i = 0; i < numFiles; ++i) {
-    if (this->Makefile->IsAlias(args[i])) {
-      this->SetError("can not be used on an ALIAS target.");
+  for (int i = 0; i < numFiles; ++i) {
+    if (mf.IsAlias(args[i])) {
+      status.SetError("can not be used on an ALIAS target.");
       return false;
     }
-    bool ret = cmSetTargetPropertiesCommand::SetOneTarget(
-      args[i], propertyPairs, this->Makefile);
+    bool ret = SetOneTarget(args[i], propertyPairs, &mf);
     if (!ret) {
-      std::string message = "Can not find target to add properties to: ";
-      message += args[i];
-      this->SetError(message);
+      status.SetError(
+        cmStrCat("Can not find target to add properties to: ", args[i]));
       return false;
     }
   }
   return true;
 }
 
-bool cmSetTargetPropertiesCommand::SetOneTarget(
-  const std::string& tname, std::vector<std::string>& propertyPairs,
-  cmMakefile* mf)
+static bool SetOneTarget(const std::string& tname,
+                         std::vector<std::string>& propertyPairs,
+                         cmMakefile* mf)
 {
   if (cmTarget* target = mf->FindTargetToUse(tname)) {
     // now loop through all the props and set them
diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h
index c9755da..9d40c74 100644
--- a/Source/cmSetTargetPropertiesCommand.h
+++ b/Source/cmSetTargetPropertiesCommand.h
@@ -8,29 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmMakefile;
 
-class cmSetTargetPropertiesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmSetTargetPropertiesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /**
-   *  Used by this command and cmSetPropertiesCommand
-   */
-  static bool SetOneTarget(const std::string& tname,
-                           std::vector<std::string>& propertyPairs,
-                           cmMakefile* mf);
-};
+bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSetTestsPropertiesCommand.cxx b/Source/cmSetTestsPropertiesCommand.cxx
index cc9587c..de61eda 100644
--- a/Source/cmSetTestsPropertiesCommand.cxx
+++ b/Source/cmSetTestsPropertiesCommand.cxx
@@ -5,20 +5,25 @@
 #include <iterator>
 
 #include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmTest.h"
 
-class cmExecutionStatus;
+static bool SetOneTest(const std::string& tname,
+                       std::vector<std::string>& propertyPairs, cmMakefile* mf,
+                       std::string& errors);
 
-// cmSetTestsPropertiesCommand
-bool cmSetTestsPropertiesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmSetTestsPropertiesCommand(std::vector<std::string> const& args,
+                                 cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // first collect up the list of files
   std::vector<std::string> propertyPairs;
   int numFiles = 0;
@@ -28,7 +33,7 @@
       // now loop through the rest of the arguments, new style
       ++j;
       if (std::distance(j, args.end()) % 2 != 0) {
-        this->SetError("called with incorrect number of arguments.");
+        status.SetError("called with incorrect number of arguments.");
         return false;
       }
       cmAppend(propertyPairs, j, args.end());
@@ -37,8 +42,8 @@
     numFiles++;
   }
   if (propertyPairs.empty()) {
-    this->SetError("called with illegal arguments, maybe "
-                   "missing a PROPERTIES specifier?");
+    status.SetError("called with illegal arguments, maybe "
+                    "missing a PROPERTIES specifier?");
     return false;
   }
 
@@ -46,10 +51,9 @@
   int i;
   for (i = 0; i < numFiles; ++i) {
     std::string errors;
-    bool ret = cmSetTestsPropertiesCommand::SetOneTest(args[i], propertyPairs,
-                                                       this->Makefile, errors);
+    bool ret = SetOneTest(args[i], propertyPairs, &mf, errors);
     if (!ret) {
-      this->SetError(errors);
+      status.SetError(errors);
       return ret;
     }
   }
@@ -57,9 +61,9 @@
   return true;
 }
 
-bool cmSetTestsPropertiesCommand::SetOneTest(
-  const std::string& tname, std::vector<std::string>& propertyPairs,
-  cmMakefile* mf, std::string& errors)
+static bool SetOneTest(const std::string& tname,
+                       std::vector<std::string>& propertyPairs, cmMakefile* mf,
+                       std::string& errors)
 {
   if (cmTest* test = mf->GetTest(tname)) {
     // now loop through all the props and set them
@@ -70,8 +74,7 @@
       }
     }
   } else {
-    errors = "Can not find test to add properties to: ";
-    errors += tname;
+    errors = cmStrCat("Can not find test to add properties to: ", tname);
     return false;
   }
 
diff --git a/Source/cmSetTestsPropertiesCommand.h b/Source/cmSetTestsPropertiesCommand.h
index 84b2645..4b75464 100644
--- a/Source/cmSetTestsPropertiesCommand.h
+++ b/Source/cmSetTestsPropertiesCommand.h
@@ -8,26 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
-class cmMakefile;
 
-class cmSetTestsPropertiesCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmSetTestsPropertiesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  static bool SetOneTest(const std::string& tname,
-                         std::vector<std::string>& propertyPairs,
-                         cmMakefile* mf, std::string& errors);
-};
+bool cmSetTestsPropertiesCommand(std::vector<std::string> const& args,
+                                 cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSiteNameCommand.cxx b/Source/cmSiteNameCommand.cxx
index 9f041bc..d47f121 100644
--- a/Source/cmSiteNameCommand.cxx
+++ b/Source/cmSiteNameCommand.cxx
@@ -4,18 +4,18 @@
 
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmSiteNameCommand
-bool cmSiteNameCommand::InitialPass(std::vector<std::string> const& args,
-                                    cmExecutionStatus&)
+bool cmSiteNameCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() != 1) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::vector<std::string> paths;
@@ -26,12 +26,12 @@
   paths.emplace_back("/sbin");
   paths.emplace_back("/usr/local/bin");
 
-  const char* cacheValue = this->Makefile->GetDefinition(args[0]);
+  const char* cacheValue = status.GetMakefile().GetDefinition(args[0]);
   if (cacheValue) {
     return true;
   }
 
-  const char* temp = this->Makefile->GetDefinition("HOSTNAME");
+  const char* temp = status.GetMakefile().GetDefinition("HOSTNAME");
   std::string hostname_cmd;
   if (temp) {
     hostname_cmd = temp;
@@ -50,7 +50,7 @@
   }
 #else
   // try to find the hostname for this computer
-  if (!cmSystemTools::IsOff(hostname_cmd)) {
+  if (!cmIsOff(hostname_cmd)) {
     std::string host;
     cmSystemTools::RunSingleCommand(hostname_cmd, &host, nullptr, nullptr,
                                     nullptr, cmSystemTools::OUTPUT_NONE);
@@ -71,7 +71,7 @@
     }
   }
 #endif
-  this->Makefile->AddCacheDefinition(
+  status.GetMakefile().AddCacheDefinition(
     args[0], siteName.c_str(),
     "Name of the computer/site where compile is being run",
     cmStateEnums::STRING);
diff --git a/Source/cmSiteNameCommand.h b/Source/cmSiteNameCommand.h
index 2d8dc17..e8fc608 100644
--- a/Source/cmSiteNameCommand.h
+++ b/Source/cmSiteNameCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmSiteNameCommand
+/**
  * \brief site_name command
  *
  * cmSiteNameCommand implements the site_name CMake command
  */
-class cmSiteNameCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmSiteNameCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmSiteNameCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index d05fb68..2a345eb 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -5,12 +5,13 @@
 #include <array>
 #include <utility>
 
-#include "cmCustomCommand.h"
 #include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmProperty.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -20,11 +21,6 @@
 {
 }
 
-cmSourceFile::~cmSourceFile()
-{
-  this->SetCustomCommand(nullptr);
-}
-
 std::string const& cmSourceFile::GetExtension() const
 {
   return this->Extension;
@@ -33,6 +29,11 @@
 const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
 const std::string cmSourceFile::propLOCATION = "LOCATION";
 const std::string cmSourceFile::propGENERATED = "GENERATED";
+const std::string cmSourceFile::propCOMPILE_DEFINITIONS =
+  "COMPILE_DEFINITIONS";
+const std::string cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
+const std::string cmSourceFile::propINCLUDE_DIRECTORIES =
+  "INCLUDE_DIRECTORIES";
 
 void cmSourceFile::SetObjectLibrary(std::string const& objlib)
 {
@@ -44,11 +45,13 @@
   return this->ObjectLibrary;
 }
 
-std::string cmSourceFile::GetLanguage()
+std::string const& cmSourceFile::GetOrDetermineLanguage()
 {
   // If the language was set explicitly by the user then use it.
   if (const char* lang = this->GetProperty(propLANGUAGE)) {
-    return lang;
+    // Assign to member in order to return a reference.
+    this->Language = lang;
+    return this->Language;
   }
 
   // Perform computation needed to get the language if necessary.
@@ -62,7 +65,7 @@
         this->Location.DirectoryIsAmbiguous()) {
       // Finalize the file location to get the extension and set the
       // language.
-      this->GetFullPath();
+      this->ResolveFullPath();
     } else {
       // Use the known extension to get the language if possible.
       std::string ext =
@@ -71,8 +74,8 @@
     }
   }
 
-  // Now try to determine the language.
-  return static_cast<cmSourceFile const*>(this)->GetLanguage();
+  // Use the language determined from the file extension.
+  return this->Language;
 }
 
 std::string cmSourceFile::GetLanguage() const
@@ -82,13 +85,8 @@
     return lang;
   }
 
-  // If the language was determined from the source file extension use it.
-  if (!this->Language.empty()) {
-    return this->Language;
-  }
-
-  // The language is not known.
-  return "";
+  // Use the language determined from the file extension.
+  return this->Language;
 }
 
 cmSourceFileLocation const& cmSourceFile::GetLocation() const
@@ -96,7 +94,7 @@
   return this->Location;
 }
 
-std::string const& cmSourceFile::GetFullPath(std::string* error)
+std::string const& cmSourceFile::ResolveFullPath(std::string* error)
 {
   if (this->FullPath.empty()) {
     if (this->FindFullPath(error)) {
@@ -150,9 +148,7 @@
     for (auto exts : extsLists) {
       for (std::string const& ext : *exts) {
         if (!ext.empty()) {
-          std::string extPath = fullPath;
-          extPath += '.';
-          extPath += ext;
+          std::string extPath = cmStrCat(fullPath, '.', ext);
           if (cmSystemTools::FileExists(extPath)) {
             this->FullPath = extPath;
             return true;
@@ -177,10 +173,8 @@
   }
 
   // Compose error
-  std::string err;
-  err += "Cannot find source file:\n  ";
-  err += lPath;
-  err += "\nTried extensions";
+  std::string err =
+    cmStrCat("Cannot find source file:\n  ", lPath, "\nTried extensions");
   for (auto exts : extsLists) {
     for (std::string const& ext : *exts) {
       err += " .";
@@ -238,18 +232,55 @@
 
 void cmSourceFile::SetProperty(const std::string& prop, const char* value)
 {
-  this->Properties.SetProperty(prop, value);
+  if (prop == propINCLUDE_DIRECTORIES) {
+    this->IncludeDirectories.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->IncludeDirectories.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_OPTIONS) {
+    this->CompileOptions.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->CompileOptions.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_DEFINITIONS) {
+    this->CompileDefinitions.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->CompileDefinitions.emplace_back(value, lfbt);
+    }
+  } else {
+    this->Properties.SetProperty(prop, value);
+  }
 
   // Update IsGenerated flag
   if (prop == propGENERATED) {
-    this->IsGenerated = cmSystemTools::IsOn(value);
+    this->IsGenerated = cmIsOn(value);
   }
 }
 
 void cmSourceFile::AppendProperty(const std::string& prop, const char* value,
                                   bool asString)
 {
-  this->Properties.AppendProperty(prop, value, asString);
+  if (prop == propINCLUDE_DIRECTORIES) {
+    if (value && *value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->IncludeDirectories.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_OPTIONS) {
+    if (value && *value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->CompileOptions.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_DEFINITIONS) {
+    if (value && *value) {
+      cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
+      this->CompileDefinitions.emplace_back(value, lfbt);
+    }
+  } else {
+    this->Properties.AppendProperty(prop, value, asString);
+  }
 
   // Update IsGenerated flag
   if (prop == propGENERATED) {
@@ -275,7 +306,14 @@
   // LOCATION property we must commit now.
   if (prop == propLOCATION) {
     // Commit to a location.
-    this->GetFullPath();
+    this->ResolveFullPath();
+  }
+
+  // Similarly, LANGUAGE can be determined by the file extension
+  // if it is requested by the user.
+  if (prop == propLANGUAGE) {
+    // The c_str pointer is valid until `this->Language` is modified.
+    return this->GetOrDetermineLanguage().c_str();
   }
 
   // Perform the normal property lookup.
@@ -292,6 +330,37 @@
     return this->FullPath.c_str();
   }
 
+  // Check for the properties with backtraces.
+  if (prop == propINCLUDE_DIRECTORIES) {
+    if (this->IncludeDirectories.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmJoin(this->IncludeDirectories, ";");
+    return output.c_str();
+  }
+
+  if (prop == propCOMPILE_OPTIONS) {
+    if (this->CompileOptions.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmJoin(this->CompileOptions, ";");
+    return output.c_str();
+  }
+
+  if (prop == propCOMPILE_DEFINITIONS) {
+    if (this->CompileDefinitions.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmJoin(this->CompileDefinitions, ";");
+    return output.c_str();
+  }
+
   const char* retVal = this->Properties.GetPropertyValue(prop);
   if (!retVal) {
     cmMakefile const* mf = this->Location.GetMakefile();
@@ -316,22 +385,22 @@
 
 bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmSystemTools::IsOn(this->GetProperty(prop));
+  return cmIsOn(this->GetProperty(prop));
 }
 
-cmCustomCommand* cmSourceFile::GetCustomCommand()
+void cmSourceFile::SetProperties(cmPropertyMap properties)
 {
-  return this->CustomCommand;
+  this->Properties = std::move(properties);
+
+  this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
 }
 
-cmCustomCommand const* cmSourceFile::GetCustomCommand() const
+cmCustomCommand* cmSourceFile::GetCustomCommand() const
 {
-  return this->CustomCommand;
+  return this->CustomCommand.get();
 }
 
-void cmSourceFile::SetCustomCommand(cmCustomCommand* cc)
+void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
 {
-  cmCustomCommand* old = this->CustomCommand;
-  this->CustomCommand = cc;
-  delete old;
+  this->CustomCommand = std::move(cc);
 }
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index edad4c7..82a3625 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -5,14 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmCustomCommand.h"
+#include "cmListFileCache.h"
 #include "cmPropertyMap.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocationKind.h"
 
-#include <string>
-#include <vector>
-
-class cmCustomCommand;
 class cmMakefile;
 
 /** \class cmSourceFile
@@ -32,17 +34,11 @@
     cmMakefile* mf, const std::string& name,
     cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous);
 
-  ~cmSourceFile();
-
-  cmSourceFile(const cmSourceFile&) = delete;
-  cmSourceFile& operator=(const cmSourceFile&) = delete;
-
   /**
-   * Get the list of the custom commands for this source file
+   * Get the custom command for this source file
    */
-  cmCustomCommand* GetCustomCommand();
-  cmCustomCommand const* GetCustomCommand() const;
-  void SetCustomCommand(cmCustomCommand* cc);
+  cmCustomCommand* GetCustomCommand() const;
+  void SetCustomCommand(std::unique_ptr<cmCustomCommand> cc);
 
   //! Set/Get a property of this source file
   void SetProperty(const std::string& prop, const char* value);
@@ -62,15 +58,31 @@
   /// @return Equivalent to GetPropertyAsBool("GENERATED")
   bool GetIsGenerated() const { return this->IsGenerated; }
 
+  const std::vector<BT<std::string>>& GetCompileOptions() const
+  {
+    return this->CompileOptions;
+  }
+
+  const std::vector<BT<std::string>>& GetCompileDefinitions() const
+  {
+    return this->CompileDefinitions;
+  }
+
+  const std::vector<BT<std::string>>& GetIncludeDirectories() const
+  {
+    return this->IncludeDirectories;
+  }
+
   /**
-   * The full path to the file.  The non-const version of this method
-   * may attempt to locate the file on disk and finalize its location.
-   * The const version of this method may return an empty string if
-   * the non-const version has not yet been called (yes this is a
-   * horrible interface, but is necessary for backwards
-   * compatibility).
+   * Resolves the full path to the file.  Attempts to locate the file on disk
+   * and finalizes its location.
    */
-  std::string const& GetFullPath(std::string* error = nullptr);
+  std::string const& ResolveFullPath(std::string* error = nullptr);
+
+  /**
+   * The resolved full path to the file.  The returned file name might be empty
+   * if the path has not yet been resolved.
+   */
   std::string const& GetFullPath() const;
 
   /**
@@ -88,7 +100,7 @@
   /**
    * Get the language of the compiler to use for this source file.
    */
-  std::string GetLanguage();
+  std::string const& GetOrDetermineLanguage();
   std::string GetLanguage() const;
 
   /**
@@ -98,8 +110,9 @@
   void AddDepend(const std::string& d) { this->Depends.push_back(d); }
 
   // Get the properties
-  cmPropertyMap& GetProperties() { return this->Properties; }
   const cmPropertyMap& GetProperties() const { return this->Properties; }
+  // Set the properties
+  void SetProperties(cmPropertyMap properties);
 
   /**
    * Check whether the given source file location could refer to this
@@ -113,12 +126,15 @@
 private:
   cmSourceFileLocation Location;
   cmPropertyMap Properties;
-  cmCustomCommand* CustomCommand = nullptr;
+  std::unique_ptr<cmCustomCommand> CustomCommand;
   std::string Extension;
   std::string Language;
   std::string FullPath;
   std::string ObjectLibrary;
   std::vector<std::string> Depends;
+  std::vector<BT<std::string>> CompileOptions;
+  std::vector<BT<std::string>> CompileDefinitions;
+  std::vector<BT<std::string>> IncludeDirectories;
   bool FindFullPathFailed = false;
   bool IsGenerated = false;
 
@@ -129,6 +145,9 @@
   static const std::string propLANGUAGE;
   static const std::string propLOCATION;
   static const std::string propGENERATED;
+  static const std::string propCOMPILE_DEFINITIONS;
+  static const std::string propCOMPILE_OPTIONS;
+  static const std::string propINCLUDE_DIRECTORIES;
 };
 
 // TODO: Factor out into platform information modules.
@@ -139,6 +158,8 @@
   "hpj"                                                                       \
   "|bat)$"
 
+#define CM_PCH_REGEX "cmake_pch\\.(h|hxx)$"
+
 #define CM_RESOURCE_REGEX "\\.(pdf|plist|png|jpeg|jpg|storyboard|xcassets)$"
 
 #endif
diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx
index acacba2..df702b0 100644
--- a/Source/cmSourceFileLocation.cxx
+++ b/Source/cmSourceFileLocation.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSourceFileLocation.h"
 
-#include "cmAlgorithms.h"
+#include <cassert>
+
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <assert.h>
-
 cmSourceFileLocation::cmSourceFileLocation() = default;
 
 cmSourceFileLocation::cmSourceFileLocation(const cmSourceFileLocation& loc)
@@ -110,8 +110,7 @@
       // Check the source tree only because a file in the build tree should
       // be specified by full path at least once.  We do not want this
       // detection to depend on whether the project has already been built.
-      tryPath = this->Makefile->GetCurrentSourceDirectory();
-      tryPath += "/";
+      tryPath = cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/');
     }
     if (!this->Directory.empty()) {
       tryPath += this->Directory;
@@ -146,8 +145,7 @@
   // adding an extension.
   if (!(this->Name.size() > loc.Name.size() &&
         this->Name[loc.Name.size()] == '.' &&
-        cmHasLiteralPrefixImpl(this->Name.c_str(), loc.Name.c_str(),
-                               loc.Name.size()))) {
+        cmHasPrefix(this->Name, loc.Name))) {
     return false;
   }
 
diff --git a/Source/cmSourceGroup.cxx b/Source/cmSourceGroup.cxx
index 7e1e836..8c3ec9f 100644
--- a/Source/cmSourceGroup.cxx
+++ b/Source/cmSourceGroup.cxx
@@ -4,6 +4,8 @@
 
 #include <utility>
 
+#include "cmStringAlgorithms.h"
+
 class cmSourceGroupInternals
 {
 public:
@@ -17,8 +19,7 @@
   this->Internal = new cmSourceGroupInternals;
   this->SetGroupRegex(regex);
   if (parentName) {
-    this->FullName = parentName;
-    this->FullName += "\\";
+    this->FullName = cmStrCat(parentName, '\\');
   }
   this->FullName += this->Name;
 }
diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h
index 7c65494..581dc5d 100644
--- a/Source/cmSourceGroup.h
+++ b/Source/cmSourceGroup.h
@@ -5,11 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/RegularExpression.hxx"
 #include <set>
 #include <string>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 class cmSourceFile;
 class cmSourceGroupInternals;
 
diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx
index 04b4d72..3a13e57 100644
--- a/Source/cmSourceGroupCommand.cxx
+++ b/Source/cmSourceGroupCommand.cxx
@@ -2,16 +2,23 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSourceGroupCommand.h"
 
-#include <algorithm>
+#include <cstddef>
+#include <map>
 #include <set>
-#include <stddef.h>
 #include <utility>
 
+#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmSourceGroup.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 namespace {
+
+using ParsedArguments = std::map<std::string, std::vector<std::string>>;
+using ExpectedOptions = std::vector<std::string>;
+
 const std::string kTreeOptionName = "TREE";
 const std::string kPrefixOptionName = "PREFIX";
 const std::string kFilesOptionName = "FILES";
@@ -20,7 +27,7 @@
 
 std::vector<std::string> tokenizePath(const std::string& path)
 {
-  return cmSystemTools::tokenize(path, "\\/");
+  return cmTokenize(path, "\\/");
 }
 
 std::string getFullFilePath(const std::string& currentPath,
@@ -29,9 +36,7 @@
   std::string fullPath = path;
 
   if (!cmSystemTools::FileIsFullPath(path)) {
-    fullPath = currentPath;
-    fullPath += "/";
-    fullPath += path;
+    fullPath = cmStrCat(currentPath, '/', path);
   }
 
   return cmSystemTools::CollapseFullPath(fullPath);
@@ -54,8 +59,8 @@
                   const std::vector<std::string>& files, std::string& error)
 {
   for (std::string const& file : files) {
-    if (!cmSystemTools::StringStartsWith(file, root.c_str())) {
-      error = "ROOT: " + root + " is not a prefix of file: " + file;
+    if (!cmHasPrefix(file, root)) {
+      error = cmStrCat("ROOT: ", root, " is not a prefix of file: ", file);
       return false;
     }
   }
@@ -93,7 +98,7 @@
 
     std::vector<std::string> tokenizedPath;
     if (!prefix.empty()) {
-      tokenizedPath = tokenizePath(prefix + '/' + sgFilesPath);
+      tokenizedPath = tokenizePath(cmStrCat(prefix, '/', sgFilesPath));
     } else {
       tokenizedPath = tokenizePath(sgFilesPath);
     }
@@ -118,13 +123,8 @@
 
   return true;
 }
-}
 
-class cmExecutionStatus;
-
-// cmSourceGroupCommand
-cmSourceGroupCommand::ExpectedOptions
-cmSourceGroupCommand::getExpectedOptions() const
+ExpectedOptions getExpectedOptions()
 {
   ExpectedOptions options;
 
@@ -136,16 +136,14 @@
   return options;
 }
 
-bool cmSourceGroupCommand::isExpectedOption(
-  const std::string& argument, const ExpectedOptions& expectedOptions)
+bool isExpectedOption(const std::string& argument,
+                      const ExpectedOptions& expectedOptions)
 {
-  return std::find(expectedOptions.begin(), expectedOptions.end(), argument) !=
-    expectedOptions.end();
+  return cmContains(expectedOptions, argument);
 }
 
-void cmSourceGroupCommand::parseArguments(
-  const std::vector<std::string>& args,
-  cmSourceGroupCommand::ParsedArguments& parsedArguments)
+void parseArguments(const std::vector<std::string>& args,
+                    ParsedArguments& parsedArguments)
 {
   const ExpectedOptions expectedOptions = getExpectedOptions();
   size_t i = 0;
@@ -174,21 +172,35 @@
   }
 }
 
-bool cmSourceGroupCommand::InitialPass(std::vector<std::string> const& args,
-                                       cmExecutionStatus&)
+} // namespace
+
+static bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments,
+                                        std::string& errorMsg);
+
+static bool processTree(cmMakefile& mf, ParsedArguments& parsedArguments,
+                        std::string& errorMsg);
+
+static bool checkSingleParameterArgumentPreconditions(
+  const std::string& argument, const ParsedArguments& parsedArguments,
+  std::string& errorMsg);
+
+bool cmSourceGroupCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  cmMakefile& mf = status.GetMakefile();
+
   // If only two arguments are given, the pre-1.8 version of the
   // command is being invoked.
   if (args.size() == 2 && args[1] != "FILES") {
-    cmSourceGroup* sg = this->Makefile->GetOrCreateSourceGroup(args[0]);
+    cmSourceGroup* sg = mf.GetOrCreateSourceGroup(args[0]);
 
     if (!sg) {
-      this->SetError("Could not create or find source group");
+      status.SetError("Could not create or find source group");
       return false;
     }
 
@@ -206,21 +218,21 @@
   }
 
   if (parsedArguments.find(kTreeOptionName) != parsedArguments.end()) {
-    if (!processTree(parsedArguments, errorMsg)) {
-      this->SetError(errorMsg);
+    if (!processTree(mf, parsedArguments, errorMsg)) {
+      status.SetError(errorMsg);
       return false;
     }
   } else {
     if (parsedArguments.find(kSourceGroupOptionName) ==
         parsedArguments.end()) {
-      this->SetError("Missing source group name.");
+      status.SetError("Missing source group name.");
       return false;
     }
 
-    cmSourceGroup* sg = this->Makefile->GetOrCreateSourceGroup(args[0]);
+    cmSourceGroup* sg = mf.GetOrCreateSourceGroup(args[0]);
 
     if (!sg) {
-      this->SetError("Could not create or find source group");
+      status.SetError("Could not create or find source group");
       return false;
     }
 
@@ -236,9 +248,7 @@
     for (auto const& filesArg : filesArguments) {
       std::string src = filesArg;
       if (!cmSystemTools::FileIsFullPath(src)) {
-        src = this->Makefile->GetCurrentSourceDirectory();
-        src += "/";
-        src += filesArg;
+        src = cmStrCat(mf.GetCurrentSourceDirectory(), '/', filesArg);
       }
       src = cmSystemTools::CollapseFullPath(src);
       sg->AddGroupFile(src);
@@ -248,8 +258,8 @@
   return true;
 }
 
-bool cmSourceGroupCommand::checkArgumentsPreconditions(
-  const ParsedArguments& parsedArguments, std::string& errorMsg) const
+static bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments,
+                                        std::string& errorMsg)
 {
   return checkSingleParameterArgumentPreconditions(
            kPrefixOptionName, parsedArguments, errorMsg) &&
@@ -259,8 +269,8 @@
                                               parsedArguments, errorMsg);
 }
 
-bool cmSourceGroupCommand::processTree(ParsedArguments& parsedArguments,
-                                       std::string& errorMsg)
+static bool processTree(cmMakefile& mf, ParsedArguments& parsedArguments,
+                        std::string& errorMsg)
 {
   const std::string root =
     cmSystemTools::CollapseFullPath(parsedArguments[kTreeOptionName].front());
@@ -268,9 +278,8 @@
     ? ""
     : parsedArguments[kPrefixOptionName].front();
 
-  const std::vector<std::string> filesVector =
-    prepareFilesPathsForTree(parsedArguments[kFilesOptionName],
-                             this->Makefile->GetCurrentSourceDirectory());
+  const std::vector<std::string> filesVector = prepareFilesPathsForTree(
+    parsedArguments[kFilesOptionName], mf.GetCurrentSourceDirectory());
 
   if (!rootIsPrefix(root, filesVector, errorMsg)) {
     return false;
@@ -279,16 +288,15 @@
   std::set<std::string> sourceGroupPaths =
     getSourceGroupFilesPaths(root, filesVector);
 
-  return addFilesToItsSourceGroups(root, sourceGroupPaths, prefix,
-                                   *(this->Makefile), errorMsg);
+  return addFilesToItsSourceGroups(root, sourceGroupPaths, prefix, mf,
+                                   errorMsg);
 }
 
-bool cmSourceGroupCommand::checkSingleParameterArgumentPreconditions(
+static bool checkSingleParameterArgumentPreconditions(
   const std::string& argument, const ParsedArguments& parsedArguments,
-  std::string& errorMsg) const
+  std::string& errorMsg)
 {
-  ParsedArguments::const_iterator foundArgument =
-    parsedArguments.find(argument);
+  auto foundArgument = parsedArguments.find(argument);
   if (foundArgument != parsedArguments.end()) {
     const std::vector<std::string>& optionArguments = foundArgument->second;
 
diff --git a/Source/cmSourceGroupCommand.h b/Source/cmSourceGroupCommand.h
index ec5ad32..ad39701 100644
--- a/Source/cmSourceGroupCommand.h
+++ b/Source/cmSourceGroupCommand.h
@@ -5,54 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <map>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmSourceGroupCommand
- * \brief Adds a cmSourceGroup to the cmMakefile.
- *
- * cmSourceGroupCommand is used to define cmSourceGroups which split up
- * source files in to named, organized groups in the generated makefiles.
- */
-class cmSourceGroupCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmSourceGroupCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  typedef std::map<std::string, std::vector<std::string>> ParsedArguments;
-  typedef std::vector<std::string> ExpectedOptions;
-
-  ExpectedOptions getExpectedOptions() const;
-
-  bool isExpectedOption(const std::string& argument,
-                        const ExpectedOptions& expectedOptions);
-
-  void parseArguments(const std::vector<std::string>& args,
-                      cmSourceGroupCommand::ParsedArguments& parsedArguments);
-
-  bool processTree(ParsedArguments& parsedArguments, std::string& errorMsg);
-
-  bool checkArgumentsPreconditions(const ParsedArguments& parsedArguments,
-                                   std::string& errorMsg) const;
-  bool checkSingleParameterArgumentPreconditions(
-    const std::string& argument, const ParsedArguments& parsedArguments,
-    std::string& errorMsg) const;
-};
+bool cmSourceGroupCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index fa7df0b..f9b5ed1 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -2,38 +2,37 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmState.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
-#include <string.h>
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
 #include <utility>
 
-#include "cmAlgorithms.h"
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmCacheManager.h"
 #include "cmCommand.h"
 #include "cmDefinitions.h"
-#include "cmDisallowedCommand.h"
+#include "cmExecutionStatus.h"
 #include "cmGlobVerificationManager.h"
 #include "cmListFileCache.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmStatePrivate.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUnexpectedCommand.h"
 #include "cmake.h"
 
 cmState::cmState()
 {
-  this->CacheManager = new cmCacheManager;
-  this->GlobVerificationManager = new cmGlobVerificationManager;
+  this->CacheManager = cm::make_unique<cmCacheManager>();
+  this->GlobVerificationManager = cm::make_unique<cmGlobVerificationManager>();
 }
 
-cmState::~cmState()
-{
-  delete this->CacheManager;
-  delete this->GlobVerificationManager;
-  cmDeleteAll(this->BuiltinCommands);
-  cmDeleteAll(this->ScriptedCommands);
-}
+cmState::~cmState() = default;
 
 const char* cmState::GetTargetTypeName(cmStateEnums::TargetType targetType)
 {
@@ -267,7 +266,7 @@
 
 cmStateSnapshot cmState::Reset()
 {
-  this->GlobalProperties.clear();
+  this->GlobalProperties.Clear();
   this->PropertyDefinitions.clear();
   this->GlobVerificationManager->Reset();
 
@@ -289,7 +288,7 @@
     it->LinkDirectoriesBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
-    it->Properties.clear();
+    it->Properties.Clear();
     it->Children.clear();
   }
 
@@ -310,8 +309,8 @@
     pos->Parent = this->VarTree.Root();
     pos->Root = this->VarTree.Root();
 
-    pos->Vars->Set("CMAKE_SOURCE_DIR", srcDir.c_str());
-    pos->Vars->Set("CMAKE_BINARY_DIR", binDir.c_str());
+    pos->Vars->Set("CMAKE_SOURCE_DIR", srcDir);
+    pos->Vars->Set("CMAKE_BINARY_DIR", binDir);
   }
 
   this->DefineProperty("RULE_LAUNCH_COMPILE", cmProperty::DIRECTORY, "", "",
@@ -326,7 +325,7 @@
   this->DefineProperty("RULE_LAUNCH_LINK", cmProperty::TARGET, "", "", true);
   this->DefineProperty("RULE_LAUNCH_CUSTOM", cmProperty::TARGET, "", "", true);
 
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 void cmState::DefineProperty(const std::string& name,
@@ -352,8 +351,7 @@
 bool cmState::IsPropertyDefined(const std::string& name,
                                 cmProperty::ScopeType scope) const
 {
-  std::map<cmProperty::ScopeType, cmPropertyDefinitionMap>::const_iterator it =
-    this->PropertyDefinitions.find(scope);
+  auto it = this->PropertyDefinitions.find(scope);
   if (it == this->PropertyDefinitions.end()) {
     return false;
   }
@@ -363,8 +361,7 @@
 bool cmState::IsPropertyChained(const std::string& name,
                                 cmProperty::ScopeType scope) const
 {
-  std::map<cmProperty::ScopeType, cmPropertyDefinitionMap>::const_iterator it =
-    this->PropertyDefinitions.find(scope);
+  auto it = this->PropertyDefinitions.find(scope);
   if (it == this->PropertyDefinitions.end()) {
     return false;
   }
@@ -373,8 +370,8 @@
 
 void cmState::SetLanguageEnabled(std::string const& l)
 {
-  std::vector<std::string>::iterator it = std::lower_bound(
-    this->EnabledLanguages.begin(), this->EnabledLanguages.end(), l);
+  auto it = std::lower_bound(this->EnabledLanguages.begin(),
+                             this->EnabledLanguages.end(), l);
   if (it == this->EnabledLanguages.end() || *it != l) {
     this->EnabledLanguages.insert(it, l);
   }
@@ -421,61 +418,107 @@
   this->IsGeneratorMultiConfig = b;
 }
 
-void cmState::AddBuiltinCommand(std::string const& name, cmCommand* command)
+void cmState::AddBuiltinCommand(std::string const& name,
+                                std::unique_ptr<cmCommand> command)
+{
+  this->AddBuiltinCommand(name, cmLegacyCommandWrapper(std::move(command)));
+}
+
+void cmState::AddBuiltinCommand(std::string const& name, Command command)
 {
   assert(name == cmSystemTools::LowerCase(name));
   assert(this->BuiltinCommands.find(name) == this->BuiltinCommands.end());
-  this->BuiltinCommands.insert(std::make_pair(name, command));
+  this->BuiltinCommands.emplace(name, std::move(command));
 }
 
-void cmState::AddDisallowedCommand(std::string const& name, cmCommand* command,
+static bool InvokeBuiltinCommand(cmState::BuiltinCommand command,
+                                 std::vector<cmListFileArgument> const& args,
+                                 cmExecutionStatus& status)
+{
+  cmMakefile& mf = status.GetMakefile();
+  std::vector<std::string> expandedArguments;
+  if (!mf.ExpandArguments(args, expandedArguments)) {
+    // There was an error expanding arguments.  It was already
+    // reported, so we can skip this command without error.
+    return true;
+  }
+  return command(expandedArguments, status);
+}
+
+void cmState::AddBuiltinCommand(std::string const& name,
+                                BuiltinCommand command)
+{
+  this->AddBuiltinCommand(
+    name,
+    [command](const std::vector<cmListFileArgument>& args,
+              cmExecutionStatus& status) -> bool {
+      return InvokeBuiltinCommand(command, args, status);
+    });
+}
+
+void cmState::AddDisallowedCommand(std::string const& name,
+                                   BuiltinCommand command,
                                    cmPolicies::PolicyID policy,
                                    const char* message)
 {
-  this->AddBuiltinCommand(name,
-                          new cmDisallowedCommand(command, policy, message));
+  this->AddBuiltinCommand(
+    name,
+    [command, policy, message](const std::vector<cmListFileArgument>& args,
+                               cmExecutionStatus& status) -> bool {
+      cmMakefile& mf = status.GetMakefile();
+      switch (mf.GetPolicyStatus(policy)) {
+        case cmPolicies::WARN:
+          mf.IssueMessage(MessageType::AUTHOR_WARNING,
+                          cmPolicies::GetPolicyWarning(policy));
+          break;
+        case cmPolicies::OLD:
+          break;
+        case cmPolicies::REQUIRED_IF_USED:
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::NEW:
+          mf.IssueMessage(MessageType::FATAL_ERROR, message);
+          return true;
+      }
+      return InvokeBuiltinCommand(command, args, status);
+    });
 }
 
 void cmState::AddUnexpectedCommand(std::string const& name, const char* error)
 {
-  this->AddBuiltinCommand(name, new cmUnexpectedCommand(name, error));
+  this->AddBuiltinCommand(
+    name,
+    [name, error](std::vector<cmListFileArgument> const&,
+                  cmExecutionStatus& status) -> bool {
+      const char* versionValue =
+        status.GetMakefile().GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION");
+      if (name == "endif" && (!versionValue || atof(versionValue) <= 1.4)) {
+        return true;
+      }
+      status.SetError(error);
+      return false;
+    });
 }
 
-void cmState::AddScriptedCommand(std::string const& name, cmCommand* command)
+void cmState::AddScriptedCommand(std::string const& name, Command command)
 {
   std::string sName = cmSystemTools::LowerCase(name);
 
   // if the command already exists, give a new name to the old command.
-  if (cmCommand* oldCmd = this->GetCommand(sName)) {
-    std::string const newName = "_" + sName;
-    std::map<std::string, cmCommand*>::iterator pos =
-      this->ScriptedCommands.find(newName);
-    if (pos != this->ScriptedCommands.end()) {
-      delete pos->second;
-      this->ScriptedCommands.erase(pos);
-    }
-    this->ScriptedCommands.insert(std::make_pair(newName, oldCmd->Clone()));
+  if (Command oldCmd = this->GetCommandByExactName(sName)) {
+    this->ScriptedCommands["_" + sName] = oldCmd;
   }
 
-  // if the command already exists, free the old one
-  std::map<std::string, cmCommand*>::iterator pos =
-    this->ScriptedCommands.find(sName);
-  if (pos != this->ScriptedCommands.end()) {
-    delete pos->second;
-    this->ScriptedCommands.erase(pos);
-  }
-  this->ScriptedCommands.insert(std::make_pair(sName, command));
+  this->ScriptedCommands[sName] = std::move(command);
 }
 
-cmCommand* cmState::GetCommand(std::string const& name) const
+cmState::Command cmState::GetCommand(std::string const& name) const
 {
   return GetCommandByExactName(cmSystemTools::LowerCase(name));
 }
 
-cmCommand* cmState::GetCommandByExactName(std::string const& name) const
+cmState::Command cmState::GetCommandByExactName(std::string const& name) const
 {
-  std::map<std::string, cmCommand*>::const_iterator pos;
-  pos = this->ScriptedCommands.find(name);
+  auto pos = this->ScriptedCommands.find(name);
   if (pos != this->ScriptedCommands.end()) {
     return pos->second;
   }
@@ -506,16 +549,11 @@
 void cmState::RemoveBuiltinCommand(std::string const& name)
 {
   assert(name == cmSystemTools::LowerCase(name));
-  std::map<std::string, cmCommand*>::iterator i =
-    this->BuiltinCommands.find(name);
-  assert(i != this->BuiltinCommands.end());
-  delete i->second;
-  this->BuiltinCommands.erase(i);
+  this->BuiltinCommands.erase(name);
 }
 
 void cmState::RemoveUserDefinedCommands()
 {
-  cmDeleteAll(this->ScriptedCommands);
   this->ScriptedCommands.clear();
 }
 
@@ -584,7 +622,7 @@
 
 bool cmState::GetGlobalPropertyAsBool(const std::string& prop)
 {
-  return cmSystemTools::IsOn(this->GetGlobalProperty(prop));
+  return cmIsOn(this->GetGlobalProperty(prop));
 }
 
 void cmState::SetSourceDirectory(std::string const& sourceDirectory)
@@ -750,7 +788,7 @@
   assert(pos->Vars.IsValid());
   pos->Parent = this->VarTree.Root();
   pos->Root = this->VarTree.Root();
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreateBuildsystemDirectorySnapshot(
@@ -803,7 +841,7 @@
   cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars;
   pos->Parent = origin;
   pos->Vars = this->VarTree.Push(origin);
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreateMacroCallSnapshot(
@@ -818,7 +856,7 @@
   assert(originSnapshot.Position->Vars.IsValid());
   pos->BuildSystemDirectory->DirectoryEnd = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreateIncludeFileSnapshot(
@@ -833,7 +871,7 @@
   assert(originSnapshot.Position->Vars.IsValid());
   pos->BuildSystemDirectory->DirectoryEnd = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreateVariableScopeSnapshot(
@@ -851,7 +889,7 @@
   pos->Parent = origin;
   pos->Vars = this->VarTree.Push(origin);
   assert(pos->Vars.IsValid());
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreateInlineListFileSnapshot(
@@ -865,7 +903,7 @@
     originSnapshot.Position->ExecutionListFile, fileName);
   pos->BuildSystemDirectory->DirectoryEnd = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::CreatePolicyScopeSnapshot(
@@ -877,7 +915,7 @@
   pos->Keep = false;
   pos->BuildSystemDirectory->DirectoryEnd = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
-  return cmStateSnapshot(this, pos);
+  return { this, pos };
 }
 
 cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot)
@@ -909,7 +947,7 @@
     this->SnapshotData.Pop(pos);
   }
 
-  return cmStateSnapshot(this, prevPos);
+  return { this, prevPos };
 }
 
 static bool ParseEntryWithoutType(const std::string& entry, std::string& var,
diff --git a/Source/cmState.h b/Source/cmState.h
index 6abe71c..a7ca015 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -5,7 +5,9 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <functional>
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
@@ -26,6 +28,7 @@
 class cmPropertyDefinition;
 class cmStateSnapshot;
 class cmMessenger;
+class cmExecutionStatus;
 
 class cmState
 {
@@ -140,16 +143,24 @@
   bool GetIsGeneratorMultiConfig() const;
   void SetIsGeneratorMultiConfig(bool b);
 
-  // Returns a command from its name, case insensitive, or nullptr
-  cmCommand* GetCommand(std::string const& name) const;
-  // Returns a command from its name, or nullptr
-  cmCommand* GetCommandByExactName(std::string const& name) const;
+  using Command = std::function<bool(std::vector<cmListFileArgument> const&,
+                                     cmExecutionStatus&)>;
+  using BuiltinCommand = bool (*)(std::vector<std::string> const&,
+                                  cmExecutionStatus&);
 
-  void AddBuiltinCommand(std::string const& name, cmCommand* command);
-  void AddDisallowedCommand(std::string const& name, cmCommand* command,
+  // Returns a command from its name, case insensitive, or nullptr
+  Command GetCommand(std::string const& name) const;
+  // Returns a command from its name, or nullptr
+  Command GetCommandByExactName(std::string const& name) const;
+
+  void AddBuiltinCommand(std::string const& name,
+                         std::unique_ptr<cmCommand> command);
+  void AddBuiltinCommand(std::string const& name, Command command);
+  void AddBuiltinCommand(std::string const& name, BuiltinCommand command);
+  void AddDisallowedCommand(std::string const& name, BuiltinCommand command,
                             cmPolicies::PolicyID policy, const char* message);
   void AddUnexpectedCommand(std::string const& name, const char* error);
-  void AddScriptedCommand(std::string const& name, cmCommand* command);
+  void AddScriptedCommand(std::string const& name, Command command);
   void RemoveBuiltinCommand(std::string const& name);
   void RemoveUserDefinedCommands();
   std::vector<std::string> GetCommandNames() const;
@@ -208,11 +219,11 @@
 
   std::map<cmProperty::ScopeType, cmPropertyDefinitionMap> PropertyDefinitions;
   std::vector<std::string> EnabledLanguages;
-  std::map<std::string, cmCommand*> BuiltinCommands;
-  std::map<std::string, cmCommand*> ScriptedCommands;
+  std::map<std::string, Command> BuiltinCommands;
+  std::map<std::string, Command> ScriptedCommands;
   cmPropertyMap GlobalProperties;
-  cmCacheManager* CacheManager;
-  cmGlobVerificationManager* GlobVerificationManager;
+  std::unique_ptr<cmCacheManager> CacheManager;
+  std::unique_ptr<cmGlobVerificationManager> GlobVerificationManager;
 
   cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType>
     BuildsystemDirectory;
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 182d3fe..1262f53 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -4,9 +4,10 @@
 #include "cmStateDirectory.h"
 
 #include <algorithm>
-#include <assert.h>
-#include <iterator>
-#include <utility>
+#include <cassert>
+#include <vector>
+
+#include <cm/iterator>
 
 #include "cmAlgorithms.h"
 #include "cmProperty.h"
@@ -175,11 +176,9 @@
 template <typename T, typename U>
 cmStringRange GetPropertyContent(T const& content, U contentEndPosition)
 {
-  std::vector<std::string>::const_iterator end =
-    content.begin() + contentEndPosition;
+  auto end = content.begin() + contentEndPosition;
 
-  std::vector<std::string>::const_reverse_iterator rbegin =
-    cmMakeReverseIterator(end);
+  auto rbegin = cm::make_reverse_iterator(end);
   rbegin = std::find(rbegin, content.rend(), cmPropertySentinal);
 
   return cmMakeRange(rbegin.base(), end);
@@ -189,17 +188,14 @@
 cmBacktraceRange GetPropertyBacktraces(T const& content, U const& backtraces,
                                        V contentEndPosition)
 {
-  std::vector<std::string>::const_iterator entryEnd =
-    content.begin() + contentEndPosition;
+  auto entryEnd = content.begin() + contentEndPosition;
 
-  std::vector<std::string>::const_reverse_iterator rbegin =
-    cmMakeReverseIterator(entryEnd);
+  auto rbegin = cm::make_reverse_iterator(entryEnd);
   rbegin = std::find(rbegin, content.rend(), cmPropertySentinal);
 
-  std::vector<cmListFileBacktrace>::const_iterator it =
-    backtraces.begin() + std::distance(content.begin(), rbegin.base());
+  auto it = backtraces.begin() + std::distance(content.begin(), rbegin.base());
 
-  std::vector<cmListFileBacktrace>::const_iterator end = backtraces.end();
+  auto end = backtraces.end();
   return cmMakeRange(it, end);
 }
 
@@ -271,22 +267,17 @@
 void cmStateDirectory::PrependIncludeDirectoriesEntry(
   const std::string& vec, const cmListFileBacktrace& lfbt)
 {
-  std::vector<std::string>::iterator entryEnd =
-    this->DirectoryState->IncludeDirectories.begin() +
+  auto entryEnd = this->DirectoryState->IncludeDirectories.begin() +
     this->Snapshot_.Position->IncludeDirectoryPosition;
 
-  std::vector<std::string>::reverse_iterator rend =
-    this->DirectoryState->IncludeDirectories.rend();
-  std::vector<std::string>::reverse_iterator rbegin =
-    cmMakeReverseIterator(entryEnd);
+  auto rend = this->DirectoryState->IncludeDirectories.rend();
+  auto rbegin = cm::make_reverse_iterator(entryEnd);
   rbegin = std::find(rbegin, rend, cmPropertySentinal);
 
-  std::vector<std::string>::iterator entryIt = rbegin.base();
-  std::vector<std::string>::iterator entryBegin =
-    this->DirectoryState->IncludeDirectories.begin();
+  auto entryIt = rbegin.base();
+  auto entryBegin = this->DirectoryState->IncludeDirectories.begin();
 
-  std::vector<cmListFileBacktrace>::iterator btIt =
-    this->DirectoryState->IncludeDirectoryBacktraces.begin() +
+  auto btIt = this->DirectoryState->IncludeDirectoryBacktraces.begin() +
     std::distance(entryBegin, entryIt);
 
   this->DirectoryState->IncludeDirectories.insert(entryIt, vec);
@@ -446,22 +437,17 @@
 void cmStateDirectory::PrependLinkDirectoriesEntry(
   const std::string& vec, const cmListFileBacktrace& lfbt)
 {
-  std::vector<std::string>::iterator entryEnd =
-    this->DirectoryState->LinkDirectories.begin() +
+  auto entryEnd = this->DirectoryState->LinkDirectories.begin() +
     this->Snapshot_.Position->LinkDirectoriesPosition;
 
-  std::vector<std::string>::reverse_iterator rend =
-    this->DirectoryState->LinkDirectories.rend();
-  std::vector<std::string>::reverse_iterator rbegin =
-    cmMakeReverseIterator(entryEnd);
+  auto rend = this->DirectoryState->LinkDirectories.rend();
+  auto rbegin = cm::make_reverse_iterator(entryEnd);
   rbegin = std::find(rbegin, rend, cmPropertySentinal);
 
-  std::vector<std::string>::iterator entryIt = rbegin.base();
-  std::vector<std::string>::iterator entryBegin =
-    this->DirectoryState->LinkDirectories.begin();
+  auto entryIt = rbegin.base();
+  auto entryBegin = this->DirectoryState->LinkDirectories.begin();
 
-  std::vector<cmListFileBacktrace>::iterator btIt =
-    this->DirectoryState->LinkDirectoriesBacktraces.begin() +
+  auto btIt = this->DirectoryState->LinkDirectoriesBacktraces.begin() +
     std::distance(entryBegin, entryIt);
 
   this->DirectoryState->LinkDirectories.insert(entryIt, vec);
@@ -662,17 +648,12 @@
 
 bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmSystemTools::IsOn(this->GetProperty(prop));
+  return cmIsOn(this->GetProperty(prop));
 }
 
 std::vector<std::string> cmStateDirectory::GetPropertyKeys() const
 {
-  std::vector<std::string> keys;
-  keys.reserve(this->DirectoryState->Properties.size());
-  for (auto const& it : this->DirectoryState->Properties) {
-    keys.push_back(it.first);
-  }
-  return keys;
+  return this->DirectoryState->Properties.GetKeys();
 }
 
 void cmStateDirectory::AddNormalTargetName(std::string const& name)
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/cmStatePrivate.h b/Source/cmStatePrivate.h
index ec0ed6c..4efaf97 100644
--- a/Source/cmStatePrivate.h
+++ b/Source/cmStatePrivate.h
@@ -48,7 +48,7 @@
 
 struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap
 {
-  typedef cmPolicies::PolicyMap derived;
+  using derived = cmPolicies::PolicyMap;
   PolicyStackEntry(bool w = false)
     : Weak(w)
   {
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index 63bec71..645907c 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -4,11 +4,11 @@
 #include "cmStateSnapshot.h"
 
 #include <algorithm>
-#include <assert.h>
-#include <iterator>
+#include <cassert>
 #include <string>
 
-#include "cmAlgorithms.h"
+#include <cm/iterator>
+
 #include "cmDefinitions.h"
 #include "cmListFileCache.h"
 #include "cmPropertyMap.h"
@@ -66,8 +66,7 @@
 
 cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const
 {
-  return cmStateSnapshot(this->State,
-                         this->Position->BuildSystemDirectory->DirectoryEnd);
+  return { this->State, this->Position->BuildSystemDirectory->DirectoryEnd };
 }
 
 cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const
@@ -126,7 +125,7 @@
          pos != this->State->SnapshotData.Root()) {
     ++pos;
   }
-  return cmStateSnapshot(this->State, pos);
+  return { this->State, pos };
 }
 
 void cmStateSnapshot::PushPolicy(cmPolicies::PolicyMap const& entry, bool weak)
@@ -222,14 +221,14 @@
 }
 
 void cmStateSnapshot::SetDefinition(std::string const& name,
-                                    std::string const& value)
+                                    cm::string_view value)
 {
-  this->Position->Vars->Set(name, value.c_str());
+  this->Position->Vars->Set(name, value);
 }
 
 void cmStateSnapshot::RemoveDefinition(std::string const& name)
 {
-  this->Position->Vars->Set(name, nullptr);
+  this->Position->Vars->Unset(name);
 }
 
 std::vector<std::string> cmStateSnapshot::UnusedKeys() const
@@ -264,7 +263,11 @@
   cmDefinitions::Raise(var, this->Position->Vars, this->Position->Root);
 
   // Now update the definition in the parent scope.
-  this->Position->Parent->Set(var, varDef);
+  if (varDef) {
+    this->Position->Parent->Set(var, varDef);
+  } else {
+    this->Position->Parent->Unset(var);
+  }
   return true;
 }
 
@@ -273,22 +276,18 @@
                                  U& parentBacktraces, U& thisBacktraces,
                                  V& contentEndPosition)
 {
-  std::vector<std::string>::const_iterator parentBegin = parentContent.begin();
-  std::vector<std::string>::const_iterator parentEnd = parentContent.end();
+  auto parentBegin = parentContent.begin();
+  auto parentEnd = parentContent.end();
 
-  std::vector<std::string>::const_reverse_iterator parentRbegin =
-    cmMakeReverseIterator(parentEnd);
-  std::vector<std::string>::const_reverse_iterator parentRend =
-    parentContent.rend();
+  auto parentRbegin = cm::make_reverse_iterator(parentEnd);
+  auto parentRend = parentContent.rend();
   parentRbegin = std::find(parentRbegin, parentRend, cmPropertySentinal);
-  std::vector<std::string>::const_iterator parentIt = parentRbegin.base();
+  auto parentIt = parentRbegin.base();
 
   thisContent = std::vector<std::string>(parentIt, parentEnd);
 
-  std::vector<cmListFileBacktrace>::const_iterator btIt =
-    parentBacktraces.begin() + std::distance(parentBegin, parentIt);
-  std::vector<cmListFileBacktrace>::const_iterator btEnd =
-    parentBacktraces.end();
+  auto btIt = parentBacktraces.begin() + std::distance(parentBegin, parentIt);
+  auto btEnd = parentBacktraces.end();
 
   thisBacktraces = std::vector<cmListFileBacktrace>(btIt, btEnd);
 
@@ -324,7 +323,7 @@
 #if defined(__CYGWIN__)
   std::string legacy;
   if (cmSystemTools::GetEnv("CMAKE_LEGACY_CYGWIN_WIN32", legacy) &&
-      cmSystemTools::IsOn(legacy.c_str())) {
+      cmIsOn(legacy.c_str())) {
     this->SetDefinition("WIN32", "1");
     this->SetDefinition("CMAKE_HOST_WIN32", "1");
   }
@@ -422,7 +421,7 @@
 
 cmStateDirectory cmStateSnapshot::GetDirectory() const
 {
-  return cmStateDirectory(this->Position->BuildSystemDirectory, *this);
+  return { this->Position->BuildSystemDirectory, *this };
 }
 
 void cmStateSnapshot::SetProjectName(const std::string& name)
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h
index c315f48..021fd53 100644
--- a/Source/cmStateSnapshot.h
+++ b/Source/cmStateSnapshot.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmLinkedTree.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
@@ -24,7 +26,7 @@
 
   std::string const* GetDefinition(std::string const& name) const;
   bool IsInitialized(std::string const& name) const;
-  void SetDefinition(std::string const& name, std::string const& value);
+  void SetDefinition(std::string const& name, cm::string_view value);
   void RemoveDefinition(std::string const& name);
   std::vector<std::string> UnusedKeys() const;
   std::vector<std::string> ClosureKeys() const;
diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h
index 7d6158e..d089ea7 100644
--- a/Source/cmStateTypes.h
+++ b/Source/cmStateTypes.h
@@ -10,7 +10,7 @@
 
 namespace cmStateDetail {
 struct SnapshotDataType;
-typedef cmLinkedTree<cmStateDetail::SnapshotDataType>::iterator PositionType;
+using PositionType = cmLinkedTree<cmStateDetail::SnapshotDataType>::iterator;
 }
 
 namespace cmStateEnums {
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index 49bad78..073f4c9 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -5,10 +5,8 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_static_string_view.hxx"
-#include "cm_string_view.hxx"
-
 #include <algorithm>
+#include <cstddef>
 #include <functional>
 #include <initializer_list>
 #include <memory>
@@ -17,6 +15,10 @@
 #include <type_traits>
 #include <utility>
 
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 namespace cm {
 
 class String;
@@ -713,7 +715,7 @@
 struct StringAdd
 {
   static const bool value = AsStringView<T>::value;
-  typedef string_view temp_type;
+  using temp_type = string_view;
   template <typename S>
   static temp_type temp(S&& s)
   {
@@ -724,7 +726,7 @@
 template <typename L, typename R>
 struct StringAdd<StringOpPlus<L, R>> : std::true_type
 {
-  typedef StringOpPlus<L, R> const& temp_type;
+  using temp_type = StringOpPlus<L, R> const&;
   static temp_type temp(temp_type s) { return s; }
 };
 
@@ -801,8 +803,8 @@
 template <>
 struct hash<cm::String>
 {
-  typedef cm::String argument_type;
-  typedef size_t result_type;
+  using argument_type = cm::String;
+  using result_type = size_t;
 
   result_type operator()(argument_type const& s) const noexcept
   {
diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx
new file mode 100644
index 0000000..bb6dcd7
--- /dev/null
+++ b/Source/cmStringAlgorithms.cxx
@@ -0,0 +1,325 @@
+/* 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 <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+
+std::string cmTrimWhitespace(cm::string_view str)
+{
+  auto start = str.begin();
+  while (start != str.end() && cmIsSpace(*start)) {
+    ++start;
+  }
+  if (start == str.end()) {
+    return std::string();
+  }
+  auto stop = str.end() - 1;
+  while (cmIsSpace(*stop)) {
+    --stop;
+  }
+  return std::string(start, stop + 1);
+}
+
+std::string cmRemoveQuotes(cm::string_view str)
+{
+  // We process only strings that have two quotes at least.
+  // Also front() and back() are only defined behavior on non empty strings.
+  if (str.size() >= 2 &&    //
+      str.front() == '"' && //
+      str.back() == '"') {
+    // Remove a quote from the front and back
+    str.remove_prefix(1);
+    str.remove_suffix(1);
+  }
+  return std::string(str);
+}
+
+std::string cmEscapeQuotes(cm::string_view str)
+{
+  std::string result;
+  result.reserve(str.size());
+  for (const char ch : str) {
+    if (ch == '"') {
+      result += '\\';
+    }
+    result += ch;
+  }
+  return result;
+}
+
+std::vector<std::string> cmTokenize(cm::string_view str, cm::string_view sep)
+{
+  std::vector<std::string> tokens;
+  cm::string_view::size_type tokend = 0;
+
+  do {
+    cm::string_view::size_type tokstart = str.find_first_not_of(sep, tokend);
+    if (tokstart == cm::string_view::npos) {
+      break; // no more tokens
+    }
+    tokend = str.find_first_of(sep, tokstart);
+    if (tokend == cm::string_view::npos) {
+      tokens.emplace_back(str.substr(tokstart));
+    } else {
+      tokens.emplace_back(str.substr(tokstart, tokend - tokstart));
+    }
+  } while (tokend != cm::string_view::npos);
+
+  if (tokens.empty()) {
+    tokens.emplace_back();
+  }
+  return tokens;
+}
+
+void cmExpandList(cm::string_view arg, std::vector<std::string>& argsOut,
+                  bool emptyArgs)
+{
+  // If argument is empty, it is an empty list.
+  if (!emptyArgs && arg.empty()) {
+    return;
+  }
+
+  // if there are no ; in the name then just copy the current string
+  if (arg.find(';') == cm::string_view::npos) {
+    argsOut.emplace_back(arg);
+    return;
+  }
+
+  std::string newArg;
+  // Break the string at non-escaped semicolons not nested in [].
+  int squareNesting = 0;
+  cm::string_view::iterator last = arg.begin();
+  cm::string_view::iterator const cend = arg.end();
+  for (cm::string_view::iterator c = last; c != cend; ++c) {
+    switch (*c) {
+      case '\\': {
+        // We only want to allow escaping of semicolons.  Other
+        // escapes should not be processed here.
+        cm::string_view::iterator cnext = c + 1;
+        if ((cnext != cend) && *cnext == ';') {
+          newArg.append(last, c);
+          // Skip over the escape character
+          last = cnext;
+          c = cnext;
+        }
+      } break;
+      case '[': {
+        ++squareNesting;
+      } break;
+      case ']': {
+        --squareNesting;
+      } break;
+      case ';': {
+        // Break the string here if we are not nested inside square
+        // brackets.
+        if (squareNesting == 0) {
+          newArg.append(last, c);
+          // Skip over the semicolon
+          last = c + 1;
+          if (!newArg.empty() || emptyArgs) {
+            // Add the last argument if the string is not empty.
+            argsOut.push_back(newArg);
+            newArg.clear();
+          }
+        }
+      } break;
+      default: {
+        // Just append this character.
+      } break;
+    }
+  }
+  newArg.append(last, cend);
+  if (!newArg.empty() || emptyArgs) {
+    // Add the last argument if the string is not empty.
+    argsOut.push_back(std::move(newArg));
+  }
+}
+
+std::vector<std::string> cmExpandedList(cm::string_view arg, bool emptyArgs)
+{
+  std::vector<std::string> argsOut;
+  cmExpandList(arg, argsOut, emptyArgs);
+  return argsOut;
+}
+
+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;
+}
+
+bool cmIsInternallyOn(cm::string_view val)
+{
+  return (val.size() == 4) &&           //
+    (val[0] == 'I' || val[0] == 'i') && //
+    (val[1] == '_') &&                  //
+    (val[2] == 'O' || val[2] == 'o') && //
+    (val[3] == 'N' || val[3] == 'n');
+}
+
+bool cmIsNOTFOUND(cm::string_view val)
+{
+  return (val == "NOTFOUND") || cmHasLiteralSuffix(val, "-NOTFOUND");
+}
+
+bool cmIsOn(cm::string_view val)
+{
+  switch (val.size()) {
+    case 1:
+      return val[0] == '1' || val[0] == 'Y' || val[0] == 'y';
+    case 2:
+      return                                //
+        (val[0] == 'O' || val[0] == 'o') && //
+        (val[1] == 'N' || val[1] == 'n');
+    case 3:
+      return                                //
+        (val[0] == 'Y' || val[0] == 'y') && //
+        (val[1] == 'E' || val[1] == 'e') && //
+        (val[2] == 'S' || val[2] == 's');
+    case 4:
+      return                                //
+        (val[0] == 'T' || val[0] == 't') && //
+        (val[1] == 'R' || val[1] == 'r') && //
+        (val[2] == 'U' || val[2] == 'u') && //
+        (val[3] == 'E' || val[3] == 'e');
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool cmIsOff(cm::string_view val)
+{
+  switch (val.size()) {
+    case 0:
+      return true;
+    case 1:
+      return val[0] == '0' || val[0] == 'N' || val[0] == 'n';
+    case 2:
+      return                                //
+        (val[0] == 'N' || val[0] == 'n') && //
+        (val[1] == 'O' || val[1] == 'o');
+    case 3:
+      return                                //
+        (val[0] == 'O' || val[0] == 'o') && //
+        (val[1] == 'F' || val[1] == 'f') && //
+        (val[2] == 'F' || val[2] == 'f');
+    case 5:
+      return                                //
+        (val[0] == 'F' || val[0] == 'f') && //
+        (val[1] == 'A' || val[1] == 'a') && //
+        (val[2] == 'L' || val[2] == 'l') && //
+        (val[3] == 'S' || val[3] == 's') && //
+        (val[4] == 'E' || val[4] == 'e');
+    case 6:
+      return                                //
+        (val[0] == 'I' || val[0] == 'i') && //
+        (val[1] == 'G' || val[1] == 'g') && //
+        (val[2] == 'N' || val[2] == 'n') && //
+        (val[3] == 'O' || val[3] == 'o') && //
+        (val[4] == 'R' || val[4] == 'r') && //
+        (val[5] == 'E' || val[5] == 'e');
+    default:
+      break;
+  }
+
+  return cmIsNOTFOUND(val);
+}
+
+bool cmStrToLong(const char* str, long* value)
+{
+  errno = 0;
+  char* endp;
+  *value = strtol(str, &endp, 10);
+  return (*endp == '\0') && (endp != str) && (errno == 0);
+}
+
+bool cmStrToLong(std::string const& str, long* value)
+{
+  return cmStrToLong(str.c_str(), value);
+}
+
+bool cmStrToULong(const char* str, unsigned long* value)
+{
+  errno = 0;
+  char* endp;
+  while (cmIsSpace(*str)) {
+    ++str;
+  }
+  if (*str == '-') {
+    return false;
+  }
+  *value = strtoul(str, &endp, 10);
+  return (*endp == '\0') && (endp != str) && (errno == 0);
+}
+
+bool cmStrToULong(std::string const& str, unsigned long* value)
+{
+  return cmStrToULong(str.c_str(), value);
+}
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
new file mode 100644
index 0000000..0e405de
--- /dev/null
+++ b/Source/cmStringAlgorithms.h
@@ -0,0 +1,294 @@
+/* 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 <cctype>
+#include <cstring>
+#include <initializer_list>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmRange.h"
+
+/** String range type.  */
+using cmStringRange = cmRange<std::vector<std::string>::const_iterator>;
+
+/** Callable string comparison struct.  */
+struct cmStrCmp
+{
+  cmStrCmp(std::string str)
+    : Test_(std::move(str))
+  {
+  }
+
+  bool operator()(cm::string_view sv) const { return Test_ == sv; }
+
+private:
+  std::string const Test_;
+};
+
+/** Returns true if the character @a ch is a whitespace character.  **/
+inline bool cmIsSpace(char ch)
+{
+  return ((ch & 0x80) == 0) && std::isspace(ch);
+}
+
+/** Returns a string that has whitespace removed from the start and the end. */
+std::string cmTrimWhitespace(cm::string_view str);
+
+/** Returns a string that has quotes removed from the start and the end. */
+std::string cmRemoveQuotes(cm::string_view str);
+
+/** Escape quotes in a string.  */
+std::string cmEscapeQuotes(cm::string_view str);
+
+/** Joins elements of a range with separator into a single string.  */
+template <typename Range>
+std::string cmJoin(Range const& rng, cm::string_view separator)
+{
+  if (rng.empty()) {
+    return std::string();
+  }
+
+  std::ostringstream os;
+  auto it = rng.begin();
+  auto const end = rng.end();
+  os << *it;
+  while (++it != end) {
+    os << separator << *it;
+  }
+  return os.str();
+}
+
+/** Extract tokens that are separated by any of the characters in @a sep.  */
+std::vector<std::string> cmTokenize(cm::string_view str, cm::string_view sep);
+
+/**
+ * Expand the ; separated string @a arg into multiple arguments.
+ * All found arguments are appended to @a argsOut.
+ */
+void cmExpandList(cm::string_view arg, std::vector<std::string>& argsOut,
+                  bool emptyArgs = false);
+
+/**
+ * Expand out any arguments in the string range [@a first, @a last) that have
+ * ; separated strings into multiple arguments.  All found arguments are
+ * appended to @a argsOut.
+ */
+template <class InputIt>
+void cmExpandLists(InputIt first, InputIt last,
+                   std::vector<std::string>& argsOut)
+{
+  for (; first != last; ++first) {
+    ExpandList(*first, argsOut);
+  }
+}
+
+/**
+ * Same as cmExpandList but a new vector is created containing
+ * the expanded arguments from the string @a arg.
+ */
+std::vector<std::string> cmExpandedList(cm::string_view arg,
+                                        bool emptyArgs = false);
+
+/**
+ * Same as cmExpandList but a new vector is created containing the expanded
+ * versions of all arguments in the string range [@a first, @a last).
+ */
+template <class InputIt>
+std::vector<std::string> cmExpandedLists(InputIt first, InputIt last)
+{
+  std::vector<std::string> argsOut;
+  for (; first != last; ++first) {
+    cmExpandList(*first, argsOut);
+  }
+  return argsOut;
+}
+
+/** Concatenate string pieces into a single string.  */
+std::string cmCatViews(std::initializer_list<cm::string_view> views);
+
+/** Utility class for cmStrCat.  */
+class cmAlphaNum
+{
+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(cm::string_view prefix, Range const& rng,
+                   cm::string_view suffix, cm::string_view sep)
+{
+  if (rng.empty()) {
+    return std::string();
+  }
+  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& rng, char suffix,
+                   cm::string_view sep)
+{
+  return cmWrap(cm::string_view(&prefix, 1), rng, cm::string_view(&suffix, 1),
+                sep);
+}
+
+/**
+ * Does a string indicates that CMake/CPack/CTest internally
+ * forced this value. This is not the same as On, but this
+ * may be considered as "internally switched on".
+ */
+bool cmIsInternallyOn(cm::string_view val);
+inline bool cmIsInternallyOn(const char* val)
+{
+  if (!val) {
+    return false;
+  }
+  return cmIsInternallyOn(cm::string_view(val));
+}
+
+/** Return true if value is NOTFOUND or ends in -NOTFOUND.  */
+bool cmIsNOTFOUND(cm::string_view val);
+
+/**
+ * Does a string indicate a true or ON value? This is not the same as ifdef.
+ */
+bool cmIsOn(cm::string_view val);
+inline bool cmIsOn(const char* val)
+{
+  if (!val) {
+    return false;
+  }
+  return cmIsOn(cm::string_view(val));
+}
+
+/**
+ * Does a string indicate a false or off value ? Note that this is
+ * not the same as !IsOn(...) because there are a number of
+ * ambiguous values such as "/usr/local/bin" a path will result in
+ * IsON and IsOff both returning false. Note that the special path
+ * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+ */
+bool cmIsOff(cm::string_view val);
+inline bool cmIsOff(const char* val)
+{
+  if (!val) {
+    return true;
+  }
+  return cmIsOff(cm::string_view(val));
+}
+
+/** 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());
+  }
+}
+
+/** Converts a string to long. Expects that the whole string is an integer.  */
+bool cmStrToLong(const char* str, long* value);
+bool cmStrToLong(std::string const& str, long* value);
+
+/** Converts a string to unsigned long. Expects that the whole string is an
+ * integer */
+bool cmStrToULong(const char* str, unsigned long* value);
+bool cmStrToULong(std::string const& str, unsigned long* value);
+
+#endif
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 998f904..9212195 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -4,143 +4,71 @@
 
 #include "cmStringCommand.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <ctype.h>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
 
-#include "cmAlgorithms.h"
+#include <cm/iterator>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_static_string_view.hxx"
+
 #include "cmCryptoHash.h"
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
+#include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
+#include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmUuid.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmStringCommand::InitialPass(std::vector<std::string> const& args,
-                                  cmExecutionStatus&)
+bool RegexMatch(std::vector<std::string> const& args,
+                cmExecutionStatus& status);
+bool RegexMatchAll(std::vector<std::string> const& args,
+                   cmExecutionStatus& status);
+bool RegexReplace(std::vector<std::string> const& args,
+                  cmExecutionStatus& status);
+
+bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
+              size_t varIdx, cmMakefile& makefile);
+
+bool HandleHashCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-  if (args.empty()) {
-    this->SetError("must be called with at least one argument.");
-    return false;
-  }
-
-  const std::string& subCommand = args[0];
-  if (subCommand == "REGEX") {
-    return this->HandleRegexCommand(args);
-  }
-  if (subCommand == "REPLACE") {
-    return this->HandleReplaceCommand(args);
-  }
-  if (subCommand == "MD5" || subCommand == "SHA1" || subCommand == "SHA224" ||
-      subCommand == "SHA256" || subCommand == "SHA384" ||
-      subCommand == "SHA512" || subCommand == "SHA3_224" ||
-      subCommand == "SHA3_256" || subCommand == "SHA3_384" ||
-      subCommand == "SHA3_512") {
-    return this->HandleHashCommand(args);
-  }
-  if (subCommand == "TOLOWER") {
-    return this->HandleToUpperLowerCommand(args, false);
-  }
-  if (subCommand == "TOUPPER") {
-    return this->HandleToUpperLowerCommand(args, true);
-  }
-  if (subCommand == "COMPARE") {
-    return this->HandleCompareCommand(args);
-  }
-  if (subCommand == "ASCII") {
-    return this->HandleAsciiCommand(args);
-  }
-  if (subCommand == "CONFIGURE") {
-    return this->HandleConfigureCommand(args);
-  }
-  if (subCommand == "LENGTH") {
-    return this->HandleLengthCommand(args);
-  }
-  if (subCommand == "APPEND") {
-    return this->HandleAppendCommand(args);
-  }
-  if (subCommand == "PREPEND") {
-    return this->HandlePrependCommand(args);
-  }
-  if (subCommand == "CONCAT") {
-    return this->HandleConcatCommand(args);
-  }
-  if (subCommand == "JOIN") {
-    return this->HandleJoinCommand(args);
-  }
-  if (subCommand == "SUBSTRING") {
-    return this->HandleSubstringCommand(args);
-  }
-  if (subCommand == "STRIP") {
-    return this->HandleStripCommand(args);
-  }
-  if (subCommand == "REPEAT") {
-    return this->HandleRepeatCommand(args);
-  }
-  if (subCommand == "RANDOM") {
-    return this->HandleRandomCommand(args);
-  }
-  if (subCommand == "FIND") {
-    return this->HandleFindCommand(args);
-  }
-  if (subCommand == "TIMESTAMP") {
-    return this->HandleTimestampCommand(args);
-  }
-  if (subCommand == "MAKE_C_IDENTIFIER") {
-    return this->HandleMakeCIdentifierCommand(args);
-  }
-  if (subCommand == "GENEX_STRIP") {
-    return this->HandleGenexStripCommand(args);
-  }
-  if (subCommand == "UUID") {
-    return this->HandleUuidCommand(args);
-  }
-
-  std::string e = "does not recognize sub-command " + subCommand;
-  this->SetError(e);
-  return false;
-}
-
-bool cmStringCommand::HandleHashCommand(std::vector<std::string> const& args)
-{
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   if (args.size() != 3) {
-    std::ostringstream e;
-    e << args[0] << " requires an output variable and an input string";
-    this->SetError(e.str());
+    status.SetError(
+      cmStrCat(args[0], " requires an output variable and an input string"));
     return false;
   }
 
-  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0].c_str()));
+  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
   if (hash) {
     std::string out = hash->HashString(args[2]);
-    this->Makefile->AddDefinition(args[1], out.c_str());
+    status.GetMakefile().AddDefinition(args[1], out);
     return true;
   }
   return false;
 #else
-  std::ostringstream e;
-  e << args[0] << " not available during bootstrap";
-  this->SetError(e.str().c_str());
+  status.SetError(cmStrCat(args[0], " not available during bootstrap"));
   return false;
 #endif
 }
 
-bool cmStringCommand::HandleToUpperLowerCommand(
-  std::vector<std::string> const& args, bool toUpper)
+bool HandleToUpperLowerCommand(std::vector<std::string> const& args,
+                               bool toUpper, cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("no output variable specified");
+    status.SetError("no output variable specified");
     return false;
   }
 
@@ -154,14 +82,27 @@
   }
 
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(outvar, output.c_str());
+  status.GetMakefile().AddDefinition(outvar, output);
   return true;
 }
 
-bool cmStringCommand::HandleAsciiCommand(std::vector<std::string> const& args)
+bool HandleToUpperCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
+{
+  return HandleToUpperLowerCommand(args, true, status);
+}
+
+bool HandleToLowerCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
+{
+  return HandleToUpperLowerCommand(args, false, status);
+}
+
+bool HandleAsciiCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("No output variable specified");
+    status.SetError("No output variable specified");
     return false;
   }
   std::string::size_type cc;
@@ -172,27 +113,26 @@
     if (ch > 0 && ch < 256) {
       output += static_cast<char>(ch);
     } else {
-      std::string error = "Character with code ";
-      error += args[cc];
-      error += " does not exist.";
-      this->SetError(error);
+      std::string error =
+        cmStrCat("Character with code ", args[cc], " does not exist.");
+      status.SetError(error);
       return false;
     }
   }
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(outvar, output.c_str());
+  status.GetMakefile().AddDefinition(outvar, output);
   return true;
 }
 
-bool cmStringCommand::HandleConfigureCommand(
-  std::vector<std::string> const& args)
+bool HandleConfigureCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("No input string specified.");
+    status.SetError("No input string specified.");
     return false;
   }
   if (args.size() < 3) {
-    this->SetError("No output variable specified.");
+    status.SetError("No output variable specified.");
     return false;
   }
 
@@ -205,75 +145,75 @@
     } else if (args[i] == "ESCAPE_QUOTES") {
       escapeQuotes = true;
     } else {
-      std::ostringstream err;
-      err << "Unrecognized argument \"" << args[i] << "\"";
-      this->SetError(err.str());
+      status.SetError(cmStrCat("Unrecognized argument \"", args[i], "\""));
       return false;
     }
   }
 
   // Configure the string.
   std::string output;
-  this->Makefile->ConfigureString(args[1], output, atOnly, escapeQuotes);
+  status.GetMakefile().ConfigureString(args[1], output, atOnly, escapeQuotes);
 
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(args[2], output.c_str());
+  status.GetMakefile().AddDefinition(args[2], output);
 
   return true;
 }
 
-bool cmStringCommand::HandleRegexCommand(std::vector<std::string> const& args)
+bool HandleRegexCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command REGEX requires a mode to be specified.");
+    status.SetError("sub-command REGEX requires a mode to be specified.");
     return false;
   }
   std::string const& mode = args[1];
   if (mode == "MATCH") {
     if (args.size() < 5) {
-      this->SetError("sub-command REGEX, mode MATCH needs "
-                     "at least 5 arguments total to command.");
+      status.SetError("sub-command REGEX, mode MATCH needs "
+                      "at least 5 arguments total to command.");
       return false;
     }
-    return this->RegexMatch(args);
+    return RegexMatch(args, status);
   }
   if (mode == "MATCHALL") {
     if (args.size() < 5) {
-      this->SetError("sub-command REGEX, mode MATCHALL needs "
-                     "at least 5 arguments total to command.");
+      status.SetError("sub-command REGEX, mode MATCHALL needs "
+                      "at least 5 arguments total to command.");
       return false;
     }
-    return this->RegexMatchAll(args);
+    return RegexMatchAll(args, status);
   }
   if (mode == "REPLACE") {
     if (args.size() < 6) {
-      this->SetError("sub-command REGEX, mode REPLACE needs "
-                     "at least 6 arguments total to command.");
+      status.SetError("sub-command REGEX, mode REPLACE needs "
+                      "at least 6 arguments total to command.");
       return false;
     }
-    return this->RegexReplace(args);
+    return RegexReplace(args, status);
   }
 
   std::string e = "sub-command REGEX does not recognize mode " + mode;
-  this->SetError(e);
+  status.SetError(e);
   return false;
 }
 
-bool cmStringCommand::RegexMatch(std::vector<std::string> const& args)
+bool RegexMatch(std::vector<std::string> const& args,
+                cmExecutionStatus& status)
 {
   //"STRING(REGEX MATCH <regular_expression> <output variable>
   // <input> [<input>...])\n";
   std::string const& regex = args[2];
   std::string const& outvar = args[3];
 
-  this->Makefile->ClearMatches();
+  status.GetMakefile().ClearMatches();
   // Compile the regular expression.
   cmsys::RegularExpression re;
   if (!re.compile(regex.c_str())) {
     std::string e =
       "sub-command REGEX, mode MATCH failed to compile regex \"" + regex +
       "\".";
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
@@ -283,38 +223,39 @@
   // Scan through the input for all matches.
   std::string output;
   if (re.find(input)) {
-    this->Makefile->StoreMatches(re);
+    status.GetMakefile().StoreMatches(re);
     std::string::size_type l = re.start();
     std::string::size_type r = re.end();
     if (r - l == 0) {
       std::string e = "sub-command REGEX, mode MATCH regex \"" + regex +
         "\" matched an empty string.";
-      this->SetError(e);
+      status.SetError(e);
       return false;
     }
     output = input.substr(l, r - l);
   }
 
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(outvar, output.c_str());
+  status.GetMakefile().AddDefinition(outvar, output);
   return true;
 }
 
-bool cmStringCommand::RegexMatchAll(std::vector<std::string> const& args)
+bool RegexMatchAll(std::vector<std::string> const& args,
+                   cmExecutionStatus& status)
 {
   //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
   // [<input>...])\n";
   std::string const& regex = args[2];
   std::string const& outvar = args[3];
 
-  this->Makefile->ClearMatches();
+  status.GetMakefile().ClearMatches();
   // Compile the regular expression.
   cmsys::RegularExpression re;
   if (!re.compile(regex.c_str())) {
     std::string e =
       "sub-command REGEX, mode MATCHALL failed to compile regex \"" + regex +
       "\".";
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
@@ -325,14 +266,14 @@
   std::string output;
   const char* p = input.c_str();
   while (re.find(p)) {
-    this->Makefile->ClearMatches();
-    this->Makefile->StoreMatches(re);
+    status.GetMakefile().ClearMatches();
+    status.GetMakefile().StoreMatches(re);
     std::string::size_type l = re.start();
     std::string::size_type r = re.end();
     if (r - l == 0) {
       std::string e = "sub-command REGEX, mode MATCHALL regex \"" + regex +
         "\" matched an empty string.";
-      this->SetError(e);
+      status.SetError(e);
       return false;
     }
     if (!output.empty()) {
@@ -343,32 +284,33 @@
   }
 
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(outvar, output.c_str());
+  status.GetMakefile().AddDefinition(outvar, output);
   return true;
 }
 
-bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
+bool RegexReplace(std::vector<std::string> const& args,
+                  cmExecutionStatus& status)
 {
   //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
   // <output variable> <input> [<input>...])\n"
   std::string const& regex = args[2];
   std::string const& replace = args[3];
   std::string const& outvar = args[4];
-  cmStringReplaceHelper replaceHelper(regex, replace, this->Makefile);
+  cmStringReplaceHelper replaceHelper(regex, replace, &status.GetMakefile());
 
   if (!replaceHelper.IsReplaceExpressionValid()) {
-    this->SetError(
+    status.SetError(
       "sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
     return false;
   }
 
-  this->Makefile->ClearMatches();
+  status.GetMakefile().ClearMatches();
 
   if (!replaceHelper.IsRegularExpressionValid()) {
     std::string e =
       "sub-command REGEX, mode REPLACE failed to compile regex \"" + regex +
       "\".";
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
@@ -378,21 +320,22 @@
   std::string output;
 
   if (!replaceHelper.Replace(input, output)) {
-    this->SetError(
+    status.SetError(
       "sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
     return false;
   }
 
   // Store the output in the provided variable.
-  this->Makefile->AddDefinition(outvar, output.c_str());
+  status.GetMakefile().AddDefinition(outvar, output);
   return true;
 }
 
-bool cmStringCommand::HandleFindCommand(std::vector<std::string> const& args)
+bool HandleFindCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   // check if all required parameters were passed
   if (args.size() < 4 || args.size() > 5) {
-    this->SetError("sub-command FIND requires 3 or 4 parameters.");
+    status.SetError("sub-command FIND requires 3 or 4 parameters.");
     return false;
   }
 
@@ -404,7 +347,7 @@
 
   // if we have 5 arguments the last one must be REVERSE
   if (args.size() == 5 && args[4] != "REVERSE") {
-    this->SetError("sub-command FIND: unknown last parameter");
+    status.SetError("sub-command FIND: unknown last parameter");
     return false;
   }
 
@@ -415,9 +358,9 @@
 
   // ensure that the user cannot accidentally specify REVERSE as a variable
   if (outvar == "REVERSE") {
-    this->SetError("sub-command FIND does not allow one to select REVERSE as "
-                   "the output variable.  "
-                   "Maybe you missed the actual output variable?");
+    status.SetError("sub-command FIND does not allow one to select REVERSE as "
+                    "the output variable.  "
+                    "Maybe you missed the actual output variable?");
     return false;
   }
 
@@ -429,22 +372,20 @@
     pos = sstring.rfind(schar);
   }
   if (std::string::npos != pos) {
-    std::ostringstream s;
-    s << pos;
-    this->Makefile->AddDefinition(outvar, s.str().c_str());
+    status.GetMakefile().AddDefinition(outvar, std::to_string(pos));
     return true;
   }
 
   // the character was not found, but this is not really an error
-  this->Makefile->AddDefinition(outvar, "-1");
+  status.GetMakefile().AddDefinition(outvar, "-1");
   return true;
 }
 
-bool cmStringCommand::HandleCompareCommand(
-  std::vector<std::string> const& args)
+bool HandleCompareCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command COMPARE requires a mode to be specified.");
+    status.SetError("sub-command COMPARE requires a mode to be specified.");
     return false;
   }
   std::string const& mode = args[1];
@@ -452,10 +393,10 @@
       (mode == "LESS_EQUAL") || (mode == "GREATER") ||
       (mode == "GREATER_EQUAL")) {
     if (args.size() < 5) {
-      std::string e = "sub-command COMPARE, mode ";
-      e += mode;
-      e += " needs at least 5 arguments total to command.";
-      this->SetError(e);
+      std::string e =
+        cmStrCat("sub-command COMPARE, mode ", mode,
+                 " needs at least 5 arguments total to command.");
+      status.SetError(e);
       return false;
     }
 
@@ -478,22 +419,22 @@
       result = !(left == right);
     }
     if (result) {
-      this->Makefile->AddDefinition(outvar, "1");
+      status.GetMakefile().AddDefinition(outvar, "1");
     } else {
-      this->Makefile->AddDefinition(outvar, "0");
+      status.GetMakefile().AddDefinition(outvar, "0");
     }
     return true;
   }
   std::string e = "sub-command COMPARE does not recognize mode " + mode;
-  this->SetError(e);
+  status.SetError(e);
   return false;
 }
 
-bool cmStringCommand::HandleReplaceCommand(
-  std::vector<std::string> const& args)
+bool HandleReplaceCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 5) {
-    this->SetError("sub-command REPLACE requires at least four arguments.");
+    status.SetError("sub-command REPLACE requires at least four arguments.");
     return false;
   }
 
@@ -506,15 +447,15 @@
   cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
                                     replaceExpression.c_str());
 
-  this->Makefile->AddDefinition(variableName, input.c_str());
+  status.GetMakefile().AddDefinition(variableName, input);
   return true;
 }
 
-bool cmStringCommand::HandleSubstringCommand(
-  std::vector<std::string> const& args)
+bool HandleSubstringCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() != 5) {
-    this->SetError("sub-command SUBSTRING requires four arguments.");
+    status.SetError("sub-command SUBSTRING requires four arguments.");
     return false;
   }
 
@@ -526,28 +467,25 @@
   size_t stringLength = stringValue.size();
   int intStringLength = static_cast<int>(stringLength);
   if (begin < 0 || begin > intStringLength) {
-    std::ostringstream ostr;
-    ostr << "begin index: " << begin << " is out of range 0 - "
-         << stringLength;
-    this->SetError(ostr.str());
+    status.SetError(
+      cmStrCat("begin index: ", begin, " is out of range 0 - ", stringLength));
     return false;
   }
   if (end < -1) {
-    std::ostringstream ostr;
-    ostr << "end index: " << end << " should be -1 or greater";
-    this->SetError(ostr.str());
+    status.SetError(cmStrCat("end index: ", end, " should be -1 or greater"));
     return false;
   }
 
-  this->Makefile->AddDefinition(variableName,
-                                stringValue.substr(begin, end).c_str());
+  status.GetMakefile().AddDefinition(variableName,
+                                     stringValue.substr(begin, end));
   return true;
 }
 
-bool cmStringCommand::HandleLengthCommand(std::vector<std::string> const& args)
+bool HandleLengthCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("sub-command LENGTH requires two arguments.");
+    status.SetError("sub-command LENGTH requires two arguments.");
     return false;
   }
 
@@ -558,14 +496,15 @@
   char buffer[1024];
   sprintf(buffer, "%d", static_cast<int>(length));
 
-  this->Makefile->AddDefinition(variableName, buffer);
+  status.GetMakefile().AddDefinition(variableName, buffer);
   return true;
 }
 
-bool cmStringCommand::HandleAppendCommand(std::vector<std::string> const& args)
+bool HandleAppendCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command APPEND requires at least one argument.");
+    status.SetError("sub-command APPEND requires at least one argument.");
     return false;
   }
 
@@ -577,20 +516,20 @@
   const std::string& variable = args[1];
 
   std::string value;
-  const char* oldValue = this->Makefile->GetDefinition(variable);
+  const char* oldValue = status.GetMakefile().GetDefinition(variable);
   if (oldValue) {
     value = oldValue;
   }
   value += cmJoin(cmMakeRange(args).advance(2), std::string());
-  this->Makefile->AddDefinition(variable, value.c_str());
+  status.GetMakefile().AddDefinition(variable, value);
   return true;
 }
 
-bool cmStringCommand::HandlePrependCommand(
-  std::vector<std::string> const& args)
+bool HandlePrependCommand(std::vector<std::string> const& args,
+                          cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command PREPEND requires at least one argument.");
+    status.SetError("sub-command PREPEND requires at least one argument.");
     return false;
   }
 
@@ -602,67 +541,69 @@
   const std::string& variable = args[1];
 
   std::string value = cmJoin(cmMakeRange(args).advance(2), std::string());
-  const char* oldValue = this->Makefile->GetDefinition(variable);
+  const char* oldValue = status.GetMakefile().GetDefinition(variable);
   if (oldValue) {
     value += oldValue;
   }
-  this->Makefile->AddDefinition(variable, value.c_str());
+  status.GetMakefile().AddDefinition(variable, value);
   return true;
 }
 
-bool cmStringCommand::HandleConcatCommand(std::vector<std::string> const& args)
+bool HandleConcatCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command CONCAT requires at least one argument.");
+    status.SetError("sub-command CONCAT requires at least one argument.");
     return false;
   }
 
-  return this->joinImpl(args, std::string(), 1);
+  return joinImpl(args, std::string(), 1, status.GetMakefile());
 }
 
-bool cmStringCommand::HandleJoinCommand(std::vector<std::string> const& args)
+bool HandleJoinCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("sub-command JOIN requires at least two arguments.");
+    status.SetError("sub-command JOIN requires at least two arguments.");
     return false;
   }
 
-  return this->joinImpl(args, args[1], 2);
+  return joinImpl(args, args[1], 2, status.GetMakefile());
 }
 
-bool cmStringCommand::joinImpl(std::vector<std::string> const& args,
-                               std::string const& glue, const size_t varIdx)
+bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
+              const size_t varIdx, cmMakefile& makefile)
 {
   std::string const& variableName = args[varIdx];
   // NOTE Items to concat/join placed right after the variable for
   // both `CONCAT` and `JOIN` sub-commands.
   std::string value = cmJoin(cmMakeRange(args).advance(varIdx + 1), glue);
 
-  this->Makefile->AddDefinition(variableName, value.c_str());
+  makefile.AddDefinition(variableName, value);
   return true;
 }
 
-bool cmStringCommand::HandleMakeCIdentifierCommand(
-  std::vector<std::string> const& args)
+bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("sub-command MAKE_C_IDENTIFIER requires two arguments.");
+    status.SetError("sub-command MAKE_C_IDENTIFIER requires two arguments.");
     return false;
   }
 
   const std::string& input = args[1];
   const std::string& variableName = args[2];
 
-  this->Makefile->AddDefinition(variableName,
-                                cmSystemTools::MakeCidentifier(input).c_str());
+  status.GetMakefile().AddDefinition(variableName,
+                                     cmSystemTools::MakeCidentifier(input));
   return true;
 }
 
-bool cmStringCommand::HandleGenexStripCommand(
-  std::vector<std::string> const& args)
+bool HandleGenexStripCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("sub-command GENEX_STRIP requires two arguments.");
+    status.SetError("sub-command GENEX_STRIP requires two arguments.");
     return false;
   }
 
@@ -673,14 +614,15 @@
 
   const std::string& variableName = args[2];
 
-  this->Makefile->AddDefinition(variableName, result.c_str());
+  status.GetMakefile().AddDefinition(variableName, result);
   return true;
 }
 
-bool cmStringCommand::HandleStripCommand(std::vector<std::string> const& args)
+bool HandleStripCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.size() != 3) {
-    this->SetError("sub-command STRIP requires two arguments.");
+    status.SetError("sub-command STRIP requires two arguments.");
     return false;
   }
 
@@ -712,13 +654,16 @@
     outLength = endPos - startPos + 1;
   }
 
-  this->Makefile->AddDefinition(
-    variableName, stringValue.substr(startPos, outLength).c_str());
+  status.GetMakefile().AddDefinition(variableName,
+                                     stringValue.substr(startPos, outLength));
   return true;
 }
 
-bool cmStringCommand::HandleRepeatCommand(std::vector<std::string> const& args)
+bool HandleRepeatCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
+  cmMakefile& makefile = status.GetMakefile();
+
   // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)`
   enum ArgPos : std::size_t
   {
@@ -730,16 +675,15 @@
   };
 
   if (args.size() != ArgPos::TOTAL_ARGS) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "sub-command REPEAT requires three arguments.");
+    makefile.IssueMessage(MessageType::FATAL_ERROR,
+                          "sub-command REPEAT requires three arguments.");
     return true;
   }
 
   unsigned long times;
-  if (!cmSystemTools::StringToULong(args[ArgPos::TIMES].c_str(), &times)) {
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                 "repeat count is not a positive number.");
+  if (!cmStrToULong(args[ArgPos::TIMES], &times)) {
+    makefile.IssueMessage(MessageType::FATAL_ERROR,
+                          "repeat count is not a positive number.");
     return true;
   }
 
@@ -766,14 +710,15 @@
       break;
   }
 
-  this->Makefile->AddDefinition(variableName, result.c_str());
+  makefile.AddDefinition(variableName, result);
   return true;
 }
 
-bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args)
+bool HandleRandomCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
   if (args.size() < 2 || args.size() == 3 || args.size() == 5) {
-    this->SetError("sub-command RANDOM requires at least one argument.");
+    status.SetError("sub-command RANDOM requires at least one argument.");
     return false;
   }
 
@@ -810,11 +755,11 @@
 
   double sizeofAlphabet = static_cast<double>(alphabet.size());
   if (sizeofAlphabet < 1) {
-    this->SetError("sub-command RANDOM invoked with bad alphabet.");
+    status.SetError("sub-command RANDOM invoked with bad alphabet.");
     return false;
   }
   if (length < 1) {
-    this->SetError("sub-command RANDOM invoked with bad length.");
+    status.SetError("sub-command RANDOM invoked with bad length.");
     return false;
   }
   const std::string& variableName = args.back();
@@ -833,19 +778,19 @@
   }
   result.push_back(0);
 
-  this->Makefile->AddDefinition(variableName, result.data());
+  status.GetMakefile().AddDefinition(variableName, result.data());
   return true;
 }
 
-bool cmStringCommand::HandleTimestampCommand(
-  std::vector<std::string> const& args)
+bool HandleTimestampCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("sub-command TIMESTAMP requires at least one argument.");
+    status.SetError("sub-command TIMESTAMP requires at least one argument.");
     return false;
   }
   if (args.size() > 4) {
-    this->SetError("sub-command TIMESTAMP takes at most three arguments.");
+    status.SetError("sub-command TIMESTAMP takes at most three arguments.");
     return false;
   }
 
@@ -865,25 +810,26 @@
     } else {
       std::string e = " TIMESTAMP sub-command does not recognize option " +
         args[argsIndex] + ".";
-      this->SetError(e);
+      status.SetError(e);
       return false;
     }
   }
 
   cmTimestamp timestamp;
   std::string result = timestamp.CurrentTime(formatString, utcFlag);
-  this->Makefile->AddDefinition(outputVariable, result.c_str());
+  status.GetMakefile().AddDefinition(outputVariable, result);
 
   return true;
 }
 
-bool cmStringCommand::HandleUuidCommand(std::vector<std::string> const& args)
+bool HandleUuidCommand(std::vector<std::string> const& args,
+                       cmExecutionStatus& status)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   unsigned int argsIndex = 1;
 
   if (args.size() < 2) {
-    this->SetError("UUID sub-command requires an output variable.");
+    status.SetError("UUID sub-command requires an output variable.");
     return false;
   }
 
@@ -898,21 +844,21 @@
     if (args[argsIndex] == "NAMESPACE") {
       ++argsIndex;
       if (argsIndex >= args.size()) {
-        this->SetError("UUID sub-command, NAMESPACE requires a value.");
+        status.SetError("UUID sub-command, NAMESPACE requires a value.");
         return false;
       }
       uuidNamespaceString = args[argsIndex++];
     } else if (args[argsIndex] == "NAME") {
       ++argsIndex;
       if (argsIndex >= args.size()) {
-        this->SetError("UUID sub-command, NAME requires a value.");
+        status.SetError("UUID sub-command, NAME requires a value.");
         return false;
       }
       uuidName = args[argsIndex++];
     } else if (args[argsIndex] == "TYPE") {
       ++argsIndex;
       if (argsIndex >= args.size()) {
-        this->SetError("UUID sub-command, TYPE requires a value.");
+        status.SetError("UUID sub-command, TYPE requires a value.");
         return false;
       }
       uuidType = args[argsIndex++];
@@ -922,7 +868,7 @@
     } else {
       std::string e =
         "UUID sub-command does not recognize option " + args[argsIndex] + ".";
-      this->SetError(e);
+      status.SetError(e);
       return false;
     }
   }
@@ -932,7 +878,7 @@
 
   std::vector<unsigned char> uuidNamespace;
   if (!uuidGenerator.StringToBinary(uuidNamespaceString, uuidNamespace)) {
-    this->SetError("UUID sub-command, malformed NAMESPACE UUID.");
+    status.SetError("UUID sub-command, malformed NAMESPACE UUID.");
     return false;
   }
 
@@ -942,12 +888,12 @@
     uuid = uuidGenerator.FromSha1(uuidNamespace, uuidName);
   } else {
     std::string e = "UUID sub-command, unknown TYPE '" + uuidType + "'.";
-    this->SetError(e);
+    status.SetError(e);
     return false;
   }
 
   if (uuid.empty()) {
-    this->SetError("UUID sub-command, generation failed.");
+    status.SetError("UUID sub-command, generation failed.");
     return false;
   }
 
@@ -955,12 +901,57 @@
     uuid = cmSystemTools::UpperCase(uuid);
   }
 
-  this->Makefile->AddDefinition(outputVariable, uuid.c_str());
+  status.GetMakefile().AddDefinition(outputVariable, uuid);
   return true;
 #else
-  std::ostringstream e;
-  e << args[0] << " not available during bootstrap";
-  this->SetError(e.str().c_str());
+  status.SetError(cmStrCat(args[0], " not available during bootstrap"));
   return false;
 #endif
 }
+
+} // namespace
+
+bool cmStringCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
+{
+  if (args.empty()) {
+    status.SetError("must be called with at least one argument.");
+    return false;
+  }
+
+  static cmSubcommandTable const subcommand{
+    { "REGEX"_s, HandleRegexCommand },
+    { "REPLACE"_s, HandleReplaceCommand },
+    { "MD5"_s, HandleHashCommand },
+    { "SHA1"_s, HandleHashCommand },
+    { "SHA224"_s, HandleHashCommand },
+    { "SHA256"_s, HandleHashCommand },
+    { "SHA384"_s, HandleHashCommand },
+    { "SHA512"_s, HandleHashCommand },
+    { "SHA3_224"_s, HandleHashCommand },
+    { "SHA3_256"_s, HandleHashCommand },
+    { "SHA3_384"_s, HandleHashCommand },
+    { "SHA3_512"_s, HandleHashCommand },
+    { "TOLOWER"_s, HandleToLowerCommand },
+    { "TOUPPER"_s, HandleToUpperCommand },
+    { "COMPARE"_s, HandleCompareCommand },
+    { "ASCII"_s, HandleAsciiCommand },
+    { "CONFIGURE"_s, HandleConfigureCommand },
+    { "LENGTH"_s, HandleLengthCommand },
+    { "APPEND"_s, HandleAppendCommand },
+    { "PREPEND"_s, HandlePrependCommand },
+    { "CONCAT"_s, HandleConcatCommand },
+    { "JOIN"_s, HandleJoinCommand },
+    { "SUBSTRING"_s, HandleSubstringCommand },
+    { "STRIP"_s, HandleStripCommand },
+    { "REPEAT"_s, HandleRepeatCommand },
+    { "RANDOM"_s, HandleRandomCommand },
+    { "FIND"_s, HandleFindCommand },
+    { "TIMESTAMP"_s, HandleTimestampCommand },
+    { "MAKE_C_IDENTIFIER"_s, HandleMakeCIdentifierCommand },
+    { "GENEX_STRIP"_s, HandleGenexStripCommand },
+    { "UUID"_s, HandleUuidCommand },
+  };
+
+  return subcommand(args[0], args, status);
+}
diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h
index acde605..bd71ba2 100644
--- a/Source/cmStringCommand.h
+++ b/Source/cmStringCommand.h
@@ -5,62 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <cstddef>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmStringCommand
+/**
  * \brief Common string operations
  *
  */
-class cmStringCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmStringCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  bool HandleConfigureCommand(std::vector<std::string> const& args);
-  bool HandleAsciiCommand(std::vector<std::string> const& args);
-  bool HandleRegexCommand(std::vector<std::string> const& args);
-  bool RegexMatch(std::vector<std::string> const& args);
-  bool RegexMatchAll(std::vector<std::string> const& args);
-  bool RegexReplace(std::vector<std::string> const& args);
-  bool HandleHashCommand(std::vector<std::string> const& args);
-  bool HandleToUpperLowerCommand(std::vector<std::string> const& args,
-                                 bool toUpper);
-  bool HandleCompareCommand(std::vector<std::string> const& args);
-  bool HandleReplaceCommand(std::vector<std::string> const& args);
-  bool HandleLengthCommand(std::vector<std::string> const& args);
-  bool HandleSubstringCommand(std::vector<std::string> const& args);
-  bool HandleAppendCommand(std::vector<std::string> const& args);
-  bool HandlePrependCommand(std::vector<std::string> const& args);
-  bool HandleConcatCommand(std::vector<std::string> const& args);
-  bool HandleJoinCommand(std::vector<std::string> const& args);
-  bool HandleStripCommand(std::vector<std::string> const& args);
-  bool HandleRepeatCommand(std::vector<std::string> const& args);
-  bool HandleRandomCommand(std::vector<std::string> const& args);
-  bool HandleFindCommand(std::vector<std::string> const& args);
-  bool HandleTimestampCommand(std::vector<std::string> const& args);
-  bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args);
-  bool HandleGenexStripCommand(std::vector<std::string> const& args);
-  bool HandleUuidCommand(std::vector<std::string> const& args);
-
-  bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
-                size_t varIdx);
-};
+bool cmStringCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx
index 4a62987..998c135 100644
--- a/Source/cmStringReplaceHelper.cxx
+++ b/Source/cmStringReplaceHelper.cxx
@@ -3,10 +3,11 @@
 
 #include "cmStringReplaceHelper.h"
 
-#include "cmMakefile.h"
 #include <sstream>
 #include <utility>
 
+#include "cmMakefile.h"
+
 cmStringReplaceHelper::cmStringReplaceHelper(const std::string& regex,
                                              std::string replace_expr,
                                              cmMakefile* makefile)
diff --git a/Source/cmStringReplaceHelper.h b/Source/cmStringReplaceHelper.h
index b3e4704..74d481d 100644
--- a/Source/cmStringReplaceHelper.h
+++ b/Source/cmStringReplaceHelper.h
@@ -3,12 +3,12 @@
 #ifndef cmStringReplaceHelper_h
 #define cmStringReplaceHelper_h
 
-#include "cmsys/RegularExpression.hxx"
-
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmsys/RegularExpression.hxx"
+
 class cmMakefile;
 
 class cmStringReplaceHelper
diff --git a/Source/cmSubcommandTable.cxx b/Source/cmSubcommandTable.cxx
new file mode 100644
index 0000000..f6194f8
--- /dev/null
+++ b/Source/cmSubcommandTable.cxx
@@ -0,0 +1,31 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmSubcommandTable.h"
+
+#include <algorithm>
+
+#include "cmExecutionStatus.h"
+#include "cmStringAlgorithms.h"
+
+cmSubcommandTable::cmSubcommandTable(std::initializer_list<InitElem> init)
+  : Impl(init.begin(), init.end())
+{
+  std::sort(this->Impl.begin(), this->Impl.end(),
+            [](Elem const& left, Elem const& right) {
+              return left.first < right.first;
+            });
+}
+
+bool cmSubcommandTable::operator()(cm::string_view key,
+                                   std::vector<std::string> const& args,
+                                   cmExecutionStatus& status) const
+{
+  auto const it = std::lower_bound(
+    this->Impl.begin(), this->Impl.end(), key,
+    [](Elem const& elem, cm::string_view k) { return elem.first < k; });
+  if (it != this->Impl.end() && it->first == key) {
+    return it->second(args, status);
+  }
+  status.SetError(cmStrCat("does not recognize sub-command ", key));
+  return false;
+}
diff --git a/Source/cmSubcommandTable.h b/Source/cmSubcommandTable.h
new file mode 100644
index 0000000..65eb8c7
--- /dev/null
+++ b/Source/cmSubcommandTable.h
@@ -0,0 +1,37 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmSubcommandTable_h
+#define cmSubcommandTable_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
+class cmExecutionStatus;
+
+class cmSubcommandTable
+{
+public:
+  using Command = bool (*)(std::vector<std::string> const&,
+                           cmExecutionStatus&);
+
+  using Elem = std::pair<cm::string_view, Command>;
+  using InitElem = std::pair<cm::static_string_view, Command>;
+
+  cmSubcommandTable(std::initializer_list<InitElem> init);
+
+  bool operator()(cm::string_view key, std::vector<std::string> const& args,
+                  cmExecutionStatus& status) const;
+
+private:
+  std::vector<Elem> Impl;
+};
+
+#endif
diff --git a/Source/cmSubdirCommand.cxx b/Source/cmSubdirCommand.cxx
index 9d36228..2477d7a 100644
--- a/Source/cmSubdirCommand.cxx
+++ b/Source/cmSubdirCommand.cxx
@@ -2,21 +2,21 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSubdirCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
-// cmSubdirCommand
-bool cmSubdirCommand::InitialPass(std::vector<std::string> const& args,
-                                  cmExecutionStatus&)
+bool cmSubdirCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   bool res = true;
   bool excludeFromAll = false;
+  cmMakefile& mf = status.GetMakefile();
 
   for (std::string const& i : args) {
     if (i == "EXCLUDE_FROM_ALL") {
@@ -29,24 +29,21 @@
     }
 
     // if they specified a relative path then compute the full
-    std::string srcPath =
-      this->Makefile->GetCurrentSourceDirectory() + "/" + i;
+    std::string srcPath = mf.GetCurrentSourceDirectory() + "/" + i;
     if (cmSystemTools::FileIsDirectory(srcPath)) {
-      std::string binPath =
-        this->Makefile->GetCurrentBinaryDirectory() + "/" + i;
-      this->Makefile->AddSubDirectory(srcPath, binPath, excludeFromAll, false);
+      std::string binPath = mf.GetCurrentBinaryDirectory() + "/" + i;
+      mf.AddSubDirectory(srcPath, binPath, excludeFromAll, false);
     }
     // otherwise it is a full path
     else if (cmSystemTools::FileIsDirectory(i)) {
       // we must compute the binPath from the srcPath, we just take the last
       // element from the source path and use that
-      std::string binPath = this->Makefile->GetCurrentBinaryDirectory() + "/" +
+      std::string binPath = mf.GetCurrentBinaryDirectory() + "/" +
         cmSystemTools::GetFilenameName(i);
-      this->Makefile->AddSubDirectory(i, binPath, excludeFromAll, false);
+      mf.AddSubDirectory(i, binPath, excludeFromAll, false);
     } else {
-      std::string error = "Incorrect SUBDIRS command. Directory: ";
-      error += i + " does not exist.";
-      this->SetError(error);
+      status.SetError(cmStrCat("Incorrect SUBDIRS command. Directory: ", i,
+                               " does not exist."));
       res = false;
     }
   }
diff --git a/Source/cmSubdirCommand.h b/Source/cmSubdirCommand.h
index adab757..3254e84 100644
--- a/Source/cmSubdirCommand.h
+++ b/Source/cmSubdirCommand.h
@@ -8,31 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmSubdirCommand
- * \brief Specify a list of subdirectories to build.
- *
- * cmSubdirCommand specifies a list of subdirectories to process
- * by CMake. For each subdirectory listed, CMake will descend
- * into that subdirectory and process any CMakeLists.txt found.
- */
-class cmSubdirCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmSubdirCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmSubdirCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSubdirDependsCommand.cxx b/Source/cmSubdirDependsCommand.cxx
index 0bb2c0a..496c60d 100644
--- a/Source/cmSubdirDependsCommand.cxx
+++ b/Source/cmSubdirDependsCommand.cxx
@@ -4,8 +4,8 @@
 
 class cmExecutionStatus;
 
-bool cmSubdirDependsCommand::InitialPass(std::vector<std::string> const&,
-                                         cmExecutionStatus&)
+bool cmSubdirDependsCommand(std::vector<std::string> const&,
+                            cmExecutionStatus&)
 {
   return true;
 }
diff --git a/Source/cmSubdirDependsCommand.h b/Source/cmSubdirDependsCommand.h
index 2db28c6..bf99bd1 100644
--- a/Source/cmSubdirDependsCommand.h
+++ b/Source/cmSubdirDependsCommand.h
@@ -8,16 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmSubdirDependsCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmSubdirDependsCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmSubdirDependsCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 1501481..a50e829 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -2,16 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmSystemTools.h"
 
+#include "cm_uv.h"
+
 #include "cmAlgorithms.h"
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
 #include "cmRange.h"
-#include "cm_uv.h"
+#include "cmStringAlgorithms.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
+#  include "cm_libarchive.h"
+
 #  include "cmArchiveWrite.h"
 #  include "cmLocale.h"
-#  include "cm_libarchive.h"
 #  ifndef __LA_INT64_T
 #    define __LA_INT64_T la_int64_t
 #  endif
@@ -20,7 +23,7 @@
 #  endif
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmCryptoHash.h"
 #endif
 
@@ -32,33 +35,36 @@
 #  include "cmMachO.h"
 #endif
 
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <fcntl.h>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/Encoding.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/System.h"
 #include "cmsys/Terminal.h"
-#include <algorithm>
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <iostream>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <utility>
-#include <vector>
 
 #if defined(_WIN32)
 #  include <windows.h>
 // include wincrypt.h after windows.h
 #  include <wincrypt.h>
 #else
-#  include <sys/time.h>
 #  include <unistd.h>
+
+#  include <sys/time.h>
 #endif
 
 #if defined(_WIN32) &&                                                        \
@@ -83,11 +89,6 @@
 
 } // namespace
 
-static bool cm_isspace(char c)
-{
-  return ((c & 0x80) == 0) && isspace(c);
-}
-
 #if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
 // For GetEnvironmentVariables
 #  if defined(_WIN32)
@@ -97,7 +98,7 @@
 #  endif
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 static std::string cm_archive_entry_pathname(struct archive_entry* entry)
 {
 #  if cmsys_STL_HAS_WSTRING
@@ -151,12 +152,10 @@
     std::string key = regEntry.match(1);
     std::string val;
     if (ReadRegistryValue(key.c_str(), val, view)) {
-      std::string reg = "[";
-      reg += key + "]";
+      std::string reg = cmStrCat('[', key, ']');
       cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
     } else {
-      std::string reg = "[";
-      reg += key + "]";
+      std::string reg = cmStrCat('[', key, ']');
       cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
     }
   }
@@ -169,49 +168,20 @@
   while (regEntry.find(source)) {
     // the arguments are the second match
     std::string key = regEntry.match(1);
-    std::string reg = "[";
-    reg += key + "]";
+    std::string reg = cmStrCat('[', key, ']');
     cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
   }
 }
 #endif
 
-std::string cmSystemTools::EscapeQuotes(const std::string& str)
+std::string cmSystemTools::HelpFileName(cm::string_view str)
 {
-  std::string result;
-  result.reserve(str.size());
-  for (const char* ch = str.c_str(); *ch != '\0'; ++ch) {
-    if (*ch == '"') {
-      result += '\\';
-    }
-    result += *ch;
-  }
-  return result;
-}
-
-std::string cmSystemTools::HelpFileName(std::string name)
-{
+  std::string name(str);
   cmSystemTools::ReplaceString(name, "<", "");
   cmSystemTools::ReplaceString(name, ">", "");
   return name;
 }
 
-std::string cmSystemTools::TrimWhitespace(const std::string& s)
-{
-  std::string::const_iterator start = s.begin();
-  while (start != s.end() && cm_isspace(*start)) {
-    ++start;
-  }
-  if (start == s.end()) {
-    return "";
-  }
-  std::string::const_iterator stop = s.end() - 1;
-  while (cm_isspace(*stop)) {
-    --stop;
-  }
-  return std::string(start, stop + 1);
-}
-
 void cmSystemTools::Error(const std::string& m)
 {
   std::string message = "CMake Error: " + m;
@@ -276,121 +246,11 @@
 
 void cmSystemTools::ReportLastSystemError(const char* msg)
 {
-  std::string m = msg;
-  m += ": System Error: ";
-  m += Superclass::GetLastSystemError();
+  std::string m =
+    cmStrCat(msg, ": System Error: ", Superclass::GetLastSystemError());
   cmSystemTools::Error(m);
 }
 
-bool cmSystemTools::IsInternallyOn(const char* val)
-{
-  if (!val) {
-    return false;
-  }
-  std::string v = val;
-  if (v.size() > 4) {
-    return false;
-  }
-
-  for (char& c : v) {
-    c = static_cast<char>(toupper(c));
-  }
-  return v == "I_ON";
-}
-
-bool cmSystemTools::IsOn(const char* val)
-{
-  if (!val) {
-    return false;
-  }
-  /* clang-format off */
-  // "1"
-  if (val[0] == '1' && val[1] == '\0') {
-    return true;
-  }
-  // "ON"
-  if ((val[0] == 'O' || val[0] == 'o') &&
-      (val[1] == 'N' || val[1] == 'n') && val[2] == '\0') {
-    return true;
-  }
-  // "Y", "YES"
-  if ((val[0] == 'Y' || val[0] == 'y') && (val[1] == '\0' || (
-      (val[1] == 'E' || val[1] == 'e') &&
-      (val[2] == 'S' || val[2] == 's') && val[3] == '\0'))) {
-    return true;
-  }
-  // "TRUE"
-  if ((val[0] == 'T' || val[0] == 't') &&
-      (val[1] == 'R' || val[1] == 'r') &&
-      (val[2] == 'U' || val[2] == 'u') &&
-      (val[3] == 'E' || val[3] == 'e') && val[4] == '\0') {
-    return true;
-  }
-  /* clang-format on */
-  return false;
-}
-
-bool cmSystemTools::IsOn(const std::string& val)
-{
-  return cmSystemTools::IsOn(val.c_str());
-}
-
-bool cmSystemTools::IsNOTFOUND(const char* val)
-{
-  if (strcmp(val, "NOTFOUND") == 0) {
-    return true;
-  }
-  return cmHasLiteralSuffix(val, "-NOTFOUND");
-}
-
-bool cmSystemTools::IsOff(const char* val)
-{
-  // ""
-  if (!val || val[0] == '\0') {
-    return true;
-  }
-  /* clang-format off */
-  // "0"
-  if (val[0] == '0' && val[1] == '\0') {
-    return true;
-  }
-  // "OFF"
-  if ((val[0] == 'O' || val[0] == 'o') &&
-      (val[1] == 'F' || val[1] == 'f') &&
-      (val[2] == 'F' || val[2] == 'f') && val[3] == '\0') {
-    return true;
-  }
-  // "N", "NO"
-  if ((val[0] == 'N' || val[0] == 'n') && (val[1] == '\0' || (
-      (val[1] == 'O' || val[1] == 'o') && val[2] == '\0'))) {
-    return true;
-  }
-  // "FALSE"
-  if ((val[0] == 'F' || val[0] == 'f') &&
-      (val[1] == 'A' || val[1] == 'a') &&
-      (val[2] == 'L' || val[2] == 'l') &&
-      (val[3] == 'S' || val[3] == 's') &&
-      (val[4] == 'E' || val[4] == 'e') && val[5] == '\0') {
-    return true;
-  }
-  // "IGNORE"
-  if ((val[0] == 'I' || val[0] == 'i') &&
-      (val[1] == 'G' || val[1] == 'g') &&
-      (val[2] == 'N' || val[2] == 'n') &&
-      (val[3] == 'O' || val[3] == 'o') &&
-      (val[4] == 'R' || val[4] == 'r') &&
-      (val[5] == 'E' || val[5] == 'e') && val[6] == '\0') {
-    return true;
-  }
-  /* clang-format on */
-  return cmSystemTools::IsNOTFOUND(val);
-}
-
-bool cmSystemTools::IsOff(const std::string& val)
-{
-  return cmSystemTools::IsOff(val.c_str());
-}
-
 void cmSystemTools::ParseWindowsCommandLine(const char* command,
                                             std::vector<std::string>& args)
 {
@@ -424,7 +284,7 @@
     } else {
       arg.append(backslashes, '\\');
       backslashes = 0;
-      if (cm_isspace(*c)) {
+      if (cmIsSpace(*c)) {
         if (in_quotes) {
           arg.append(1, *c);
         } else if (in_argument) {
@@ -487,10 +347,9 @@
     if (cmHasLiteralPrefix(arg, "@")) {
       cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
       if (!responseFile) {
-        std::string error = "failed to open for reading (";
-        error += cmSystemTools::GetLastSystemError();
-        error += "):\n  ";
-        error += arg.substr(1);
+        std::string error = cmStrCat("failed to open for reading (",
+                                     cmSystemTools::GetLastSystemError(),
+                                     "):\n  ", cm::string_view(arg).substr(1));
         cmSystemTools::Error(error);
       } else {
         std::string line;
@@ -843,9 +702,7 @@
   std::string hname;
 
   for (std::string const& headerExt : headerExts) {
-    hname = name;
-    hname += ".";
-    hname += headerExt;
+    hname = cmStrCat(name, '.', headerExt);
     if (cmSystemTools::FileExists(hname)) {
       return true;
     }
@@ -863,7 +720,7 @@
   cmSystemTools::ConvertToUnixSlashes(dir);
   std::string prevDir;
   while (dir != prevDir) {
-    std::string path = dir + "/" + file;
+    std::string path = cmStrCat(dir, "/", file);
     if (cmSystemTools::FileExists(path)) {
       return path;
     }
@@ -1003,10 +860,22 @@
 #endif
 }
 
+void cmSystemTools::MoveFileIfDifferent(const std::string& source,
+                                        const std::string& destination)
+{
+  if (FilesDiffer(source, destination)) {
+    if (RenameFile(source, destination)) {
+      return;
+    }
+    CopyFileAlways(source, destination);
+  }
+  RemoveFile(source);
+}
+
 std::string cmSystemTools::ComputeFileHash(const std::string& source,
                                            cmCryptoHash::Algo algo)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   cmCryptoHash hash(algo);
   return hash.HashFile(source);
 #else
@@ -1019,7 +888,7 @@
 
 std::string cmSystemTools::ComputeStringMD5(const std::string& input)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   cmCryptoHash md5(cmCryptoHash::AlgoMD5);
   return md5.HashString(input);
 #else
@@ -1035,7 +904,7 @@
 {
   std::string thumbprint;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32)
+#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32)
   BYTE* certData = NULL;
   CRYPT_INTEGER_BLOB cryptBlob;
   HCERTSTORE certStore = NULL;
@@ -1139,9 +1008,7 @@
     for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
       if ((std::string(d.GetFile(i)) != ".") &&
           (std::string(d.GetFile(i)) != "..")) {
-        std::string fname = startPath;
-        fname += "/";
-        fname += d.GetFile(i);
+        std::string fname = cmStrCat(startPath, '/', d.GetFile(i));
         if (cmSystemTools::FileIsDirectory(fname)) {
           fname += finishPath;
           cmSystemTools::GlobDirs(fname, files);
@@ -1151,75 +1018,6 @@
   }
 }
 
-void cmSystemTools::ExpandListArgument(const std::string& arg,
-                                       std::vector<std::string>& argsOut,
-                                       bool emptyArgs)
-{
-  // If argument is empty, it is an empty list.
-  if (!emptyArgs && arg.empty()) {
-    return;
-  }
-  // if there are no ; in the name then just copy the current string
-  if (arg.find(';') == std::string::npos) {
-    argsOut.push_back(arg);
-    return;
-  }
-  std::string newArg;
-  const char* last = arg.c_str();
-  // Break the string at non-escaped semicolons not nested in [].
-  int squareNesting = 0;
-  for (const char* c = last; *c; ++c) {
-    switch (*c) {
-      case '\\': {
-        // We only want to allow escaping of semicolons.  Other
-        // escapes should not be processed here.
-        const char* next = c + 1;
-        if (*next == ';') {
-          newArg.append(last, c - last);
-          // Skip over the escape character
-          last = c = next;
-        }
-      } break;
-      case '[': {
-        ++squareNesting;
-      } break;
-      case ']': {
-        --squareNesting;
-      } break;
-      case ';': {
-        // Break the string here if we are not nested inside square
-        // brackets.
-        if (squareNesting == 0) {
-          newArg.append(last, c - last);
-          // Skip over the semicolon
-          last = c + 1;
-          if (!newArg.empty() || emptyArgs) {
-            // Add the last argument if the string is not empty.
-            argsOut.push_back(newArg);
-            newArg.clear();
-          }
-        }
-      } break;
-      default: {
-        // Just append this character.
-      } break;
-    }
-  }
-  newArg.append(last);
-  if (!newArg.empty() || emptyArgs) {
-    // Add the last argument if the string is not empty.
-    argsOut.push_back(newArg);
-  }
-}
-
-std::vector<std::string> cmSystemTools::ExpandedListArgument(
-  const std::string& arg, bool emptyArgs)
-{
-  std::vector<std::string> argsOut;
-  ExpandListArgument(arg, argsOut, emptyArgs);
-  return argsOut;
-}
-
 bool cmSystemTools::SimpleGlob(const std::string& glob,
                                std::vector<std::string>& files,
                                int type /* = 0 */)
@@ -1264,65 +1062,6 @@
   return res;
 }
 
-cmSystemTools::FileFormat cmSystemTools::GetFileFormat(std::string const& ext)
-{
-  if (ext.empty()) {
-    return cmSystemTools::NO_FILE_FORMAT;
-  }
-  if (ext == "c" || ext == ".c" || ext == "m" || ext == ".m") {
-    return cmSystemTools::C_FILE_FORMAT;
-  }
-  if (ext == "C" || ext == ".C" || ext == "M" || ext == ".M" || ext == "c++" ||
-      ext == ".c++" || ext == "cc" || ext == ".cc" || ext == "cpp" ||
-      ext == ".cpp" || ext == "cxx" || ext == ".cxx" || ext == "mm" ||
-      ext == ".mm") {
-    return cmSystemTools::CXX_FILE_FORMAT;
-  }
-  if (ext == "f" || ext == ".f" || ext == "F" || ext == ".F" || ext == "f77" ||
-      ext == ".f77" || ext == "f90" || ext == ".f90" || ext == "for" ||
-      ext == ".for" || ext == "f95" || ext == ".f95") {
-    return cmSystemTools::FORTRAN_FILE_FORMAT;
-  }
-  if (ext == "java" || ext == ".java") {
-    return cmSystemTools::JAVA_FILE_FORMAT;
-  }
-  if (ext == "cu" || ext == ".cu") {
-    return cmSystemTools::CUDA_FILE_FORMAT;
-  }
-  if (ext == "H" || ext == ".H" || ext == "h" || ext == ".h" || ext == "h++" ||
-      ext == ".h++" || ext == "hm" || ext == ".hm" || ext == "hpp" ||
-      ext == ".hpp" || ext == "hxx" || ext == ".hxx" || ext == "in" ||
-      ext == ".in" || ext == "txx" || ext == ".txx") {
-    return cmSystemTools::HEADER_FILE_FORMAT;
-  }
-  if (ext == "rc" || ext == ".rc") {
-    return cmSystemTools::RESOURCE_FILE_FORMAT;
-  }
-  if (ext == "def" || ext == ".def") {
-    return cmSystemTools::DEFINITION_FILE_FORMAT;
-  }
-  if (ext == "lib" || ext == ".lib" || ext == "a" || ext == ".a") {
-    return cmSystemTools::STATIC_LIBRARY_FILE_FORMAT;
-  }
-  if (ext == "o" || ext == ".o" || ext == "obj" || ext == ".obj") {
-    return cmSystemTools::OBJECT_FILE_FORMAT;
-  }
-#ifdef __APPLE__
-  if (ext == "dylib" || ext == ".dylib") {
-    return cmSystemTools::SHARED_LIBRARY_FILE_FORMAT;
-  }
-  if (ext == "so" || ext == ".so" || ext == "bundle" || ext == ".bundle") {
-    return cmSystemTools::MODULE_FILE_FORMAT;
-  }
-#else  // __APPLE__
-  if (ext == "so" || ext == ".so" || ext == "sl" || ext == ".sl" ||
-      ext == "dll" || ext == ".dll") {
-    return cmSystemTools::SHARED_LIBRARY_FILE_FORMAT;
-  }
-#endif // __APPLE__
-  return cmSystemTools::UNKNOWN_FILE_FORMAT;
-}
-
 std::string cmSystemTools::ConvertToOutputPath(std::string const& path)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
@@ -1448,12 +1187,11 @@
   return relative;
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 bool cmSystemTools::UnsetEnv(const char* value)
 {
 #  if !defined(HAVE_UNSETENV)
-  std::string var = value;
-  var += "=";
+  std::string var = cmStrCat(value, '=');
   return cmSystemTools::PutEnv(var.c_str());
 #  else
   unsetenv(value);
@@ -1513,7 +1251,7 @@
   // output and allow it to be captured on the fly.
   cmSystemTools::PutEnv("vsconsoleoutput=1");
 
-#  ifdef CMAKE_BUILD_WITH_CMAKE
+#  ifndef CMAKE_BOOTSTRAP
   // VS sets an environment variable to tell MS tools like "cl" to report
   // output through a backdoor pipe instead of stdout/stderr.  Unset the
   // environment variable to close this backdoor for any path of process
@@ -1535,14 +1273,12 @@
                               std::string const& mtime,
                               std::string const& format)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
   cmsys::ofstream fout(outFileName.c_str(), std::ios::out | std::ios::binary);
   if (!fout) {
-    std::string e = "Cannot open output file \"";
-    e += outFileName;
-    e += "\": ";
-    e += cmSystemTools::GetLastSystemError();
+    std::string e = cmStrCat("Cannot open output file \"", outFileName,
+                             "\": ", cmSystemTools::GetLastSystemError());
     cmSystemTools::Error(e);
     return false;
   }
@@ -1589,7 +1325,7 @@
 #endif
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 namespace {
 #  define BSDTAR_FILESIZE_PRINTF "%lu"
 #  define BSDTAR_FILESIZE_TYPE unsigned long
@@ -1885,7 +1621,7 @@
                                const std::vector<std::string>& files,
                                bool verbose)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   return extract_tar(outFileName, files, verbose, true);
 #else
   (void)outFileName;
@@ -1899,7 +1635,7 @@
                             const std::vector<std::string>& files,
                             bool verbose)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   return extract_tar(outFileName, files, verbose, false);
 #else
   (void)outFileName;
@@ -1914,8 +1650,8 @@
                                std::vector<char>& err)
 {
   line.clear();
-  std::vector<char>::iterator outiter = out.begin();
-  std::vector<char>::iterator erriter = err.begin();
+  auto outiter = out.begin();
+  auto erriter = err.begin();
   cmProcessOutput processOutput;
   std::string strdata;
   while (true) {
@@ -2225,40 +1961,34 @@
   }
 #endif
   exe_dir = cmSystemTools::GetActualCaseForPath(exe_dir);
-  cmSystemToolsCMakeCommand = exe_dir;
-  cmSystemToolsCMakeCommand += "/cmake";
-  cmSystemToolsCMakeCommand += cmSystemTools::GetExecutableExtension();
-#ifndef CMAKE_BUILD_WITH_CMAKE
+  cmSystemToolsCMakeCommand =
+    cmStrCat(exe_dir, "/cmake", cmSystemTools::GetExecutableExtension());
+#ifdef CMAKE_BOOTSTRAP
   // The bootstrap cmake does not provide the other tools,
   // so use the directory where they are about to be built.
   exe_dir = CMAKE_BOOTSTRAP_BINARY_DIR "/bin";
 #endif
-  cmSystemToolsCTestCommand = exe_dir;
-  cmSystemToolsCTestCommand += "/ctest";
-  cmSystemToolsCTestCommand += cmSystemTools::GetExecutableExtension();
-  cmSystemToolsCPackCommand = exe_dir;
-  cmSystemToolsCPackCommand += "/cpack";
-  cmSystemToolsCPackCommand += cmSystemTools::GetExecutableExtension();
-  cmSystemToolsCMakeGUICommand = exe_dir;
-  cmSystemToolsCMakeGUICommand += "/cmake-gui";
-  cmSystemToolsCMakeGUICommand += cmSystemTools::GetExecutableExtension();
+  cmSystemToolsCTestCommand =
+    cmStrCat(exe_dir, "/ctest", cmSystemTools::GetExecutableExtension());
+  cmSystemToolsCPackCommand =
+    cmStrCat(exe_dir, "/cpack", cmSystemTools::GetExecutableExtension());
+  cmSystemToolsCMakeGUICommand =
+    cmStrCat(exe_dir, "/cmake-gui", cmSystemTools::GetExecutableExtension());
   if (!cmSystemTools::FileExists(cmSystemToolsCMakeGUICommand)) {
     cmSystemToolsCMakeGUICommand.clear();
   }
-  cmSystemToolsCMakeCursesCommand = exe_dir;
-  cmSystemToolsCMakeCursesCommand += "/ccmake";
-  cmSystemToolsCMakeCursesCommand += cmSystemTools::GetExecutableExtension();
+  cmSystemToolsCMakeCursesCommand =
+    cmStrCat(exe_dir, "/ccmake", cmSystemTools::GetExecutableExtension());
   if (!cmSystemTools::FileExists(cmSystemToolsCMakeCursesCommand)) {
     cmSystemToolsCMakeCursesCommand.clear();
   }
-  cmSystemToolsCMClDepsCommand = exe_dir;
-  cmSystemToolsCMClDepsCommand += "/cmcldeps";
-  cmSystemToolsCMClDepsCommand += cmSystemTools::GetExecutableExtension();
+  cmSystemToolsCMClDepsCommand =
+    cmStrCat(exe_dir, "/cmcldeps", cmSystemTools::GetExecutableExtension());
   if (!cmSystemTools::FileExists(cmSystemToolsCMClDepsCommand)) {
     cmSystemToolsCMClDepsCommand.clear();
   }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   // Install tree has
   // - "<prefix><CMAKE_BIN_DIR>/cmake"
   // - "<prefix><CMAKE_DATA_DIR>"
@@ -2456,7 +2186,8 @@
 #if defined(CMAKE_USE_ELF_PARSER)
 bool cmSystemTools::ChangeRPath(std::string const& file,
                                 std::string const& oldRPath,
-                                std::string const& newRPath, std::string* emsg,
+                                std::string const& newRPath,
+                                bool removeEnvironmentRPath, std::string* emsg,
                                 bool* changed)
 {
   if (changed) {
@@ -2490,8 +2221,9 @@
         return true;
       }
       if (emsg) {
-        *emsg = "No valid ELF RPATH or RUNPATH entry exists in the file; ";
-        *emsg += elf.GetErrorMessage();
+        *emsg =
+          cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
+                   elf.GetErrorMessage());
       }
       return false;
     }
@@ -2543,7 +2275,9 @@
 
       // Construct the new value which preserves the part of the path
       // not being changed.
-      rp[rp_count].Value = se[i]->Value.substr(0, prefix_len);
+      if (!removeEnvironmentRPath) {
+        rp[rp_count].Value = se[i]->Value.substr(0, prefix_len);
+      }
       rp[rp_count].Value += newRPath;
       rp[rp_count].Value += se[i]->Value.substr(pos + oldRPath.length());
 
@@ -2555,9 +2289,8 @@
       // least one null terminator.
       if (rp[rp_count].Size < rp[rp_count].Value.length() + 1) {
         if (emsg) {
-          *emsg = "The replacement path is too long for the ";
-          *emsg += se_name[i];
-          *emsg += " entry.";
+          *emsg = cmStrCat("The replacement path is too long for the ",
+                           se_name[i], " entry.");
         }
         return false;
       }
@@ -2593,9 +2326,7 @@
       // Seek to the RPATH position.
       if (!f.seekp(rp[i].Position)) {
         if (emsg) {
-          *emsg = "Error seeking to ";
-          *emsg += rp[i].Name;
-          *emsg += " position.";
+          *emsg = cmStrCat("Error seeking to ", rp[i].Name, " position.");
         }
         return false;
       }
@@ -2610,9 +2341,8 @@
       // Make sure it wrote correctly.
       if (!f) {
         if (emsg) {
-          *emsg = "Error writing the new ";
-          *emsg += rp[i].Name;
-          *emsg += " string to the file.";
+          *emsg = cmStrCat("Error writing the new ", rp[i].Name,
+                           " string to the file.");
         }
         return false;
       }
@@ -2629,6 +2359,7 @@
 bool cmSystemTools::ChangeRPath(std::string const& /*file*/,
                                 std::string const& /*oldRPath*/,
                                 std::string const& /*newRPath*/,
+                                bool /*removeEnvironmentRPath*/,
                                 std::string* /*emsg*/, bool* /*changed*/)
 {
   return false;
@@ -2640,7 +2371,8 @@
 {
   const char* endl = lhss;
   const char* endr = rhss;
-  unsigned long lhs, rhs;
+  unsigned long lhs;
+  unsigned long rhs;
 
   while (((*endl >= '0') && (*endl <= '9')) ||
          ((*endr >= '0') && (*endr <= '9'))) {
@@ -2825,8 +2557,7 @@
 
     // Adjust the entry list as necessary to remove the run path
     unsigned long entriesErased = 0;
-    for (cmELF::DynamicEntryList::iterator it = dentries.begin();
-         it != dentries.end();) {
+    for (auto it = dentries.begin(); it != dentries.end();) {
       if (it->first == cmELF::TagRPath || it->first == cmELF::TagRunPath) {
         it = dentries.erase(it);
         entriesErased++;
@@ -2951,61 +2682,20 @@
 
 bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir)
 {
+#ifdef _WIN32
   // Windows sometimes locks files temporarily so try a few times.
-  for (int i = 0; i < 10; ++i) {
+  WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
+
+  for (unsigned int i = 0; i < retry.Count; ++i) {
     if (cmSystemTools::RemoveADirectory(dir)) {
       return true;
     }
-    cmSystemTools::Delay(100);
+    cmSystemTools::Delay(retry.Delay);
   }
   return false;
-}
-
-std::vector<std::string> cmSystemTools::tokenize(const std::string& str,
-                                                 const std::string& sep)
-{
-  std::vector<std::string> tokens;
-  std::string::size_type tokend = 0;
-
-  do {
-    std::string::size_type tokstart = str.find_first_not_of(sep, tokend);
-    if (tokstart == std::string::npos) {
-      break; // no more tokens
-    }
-    tokend = str.find_first_of(sep, tokstart);
-    if (tokend == std::string::npos) {
-      tokens.push_back(str.substr(tokstart));
-    } else {
-      tokens.push_back(str.substr(tokstart, tokend - tokstart));
-    }
-  } while (tokend != std::string::npos);
-
-  if (tokens.empty()) {
-    tokens.emplace_back();
-  }
-  return tokens;
-}
-
-bool cmSystemTools::StringToLong(const char* str, long* value)
-{
-  errno = 0;
-  char* endp;
-  *value = strtol(str, &endp, 10);
-  return (*endp == '\0') && (endp != str) && (errno == 0);
-}
-
-bool cmSystemTools::StringToULong(const char* str, unsigned long* value)
-{
-  errno = 0;
-  char* endp;
-  while (isspace(*str)) {
-    ++str;
-  }
-  if (*str == '-') {
-    return false;
-  }
-  *value = strtoul(str, &endp, 10);
-  return (*endp == '\0') && (endp != str) && (errno == 0);
+#else
+  return cmSystemTools::RemoveADirectory(dir);
+#endif
 }
 
 std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 016c266..ee149a0 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -5,15 +5,19 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmsys/Process.h"
+#include "cmsys/SystemTools.hxx" // IWYU pragma: export
+
 #include "cmCryptoHash.h"
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
-#include "cmsys/Process.h"
-#include "cmsys/SystemTools.hxx" // IWYU pragma: export
-#include <functional>
-#include <stddef.h>
-#include <string>
-#include <vector>
 
 /** \class cmSystemTools
  * \brief A collection of useful functions for CMake.
@@ -24,51 +28,8 @@
 class cmSystemTools : public cmsys::SystemTools
 {
 public:
-  typedef cmsys::SystemTools Superclass;
-  typedef cmProcessOutput::Encoding Encoding;
-
-  /**
-   * Expand the ; separated string @a arg into multiple arguments.
-   * All found arguments are appended to @a argsOut.
-   */
-  static void ExpandListArgument(const std::string& arg,
-                                 std::vector<std::string>& argsOut,
-                                 bool emptyArgs = false);
-
-  /**
-   * Expand out any arguments in the string range [@a first, @a last) that have
-   * ; separated strings into multiple arguments.  All found arguments are
-   * appended to @a argsOut.
-   */
-  template <class InputIt>
-  static void ExpandLists(InputIt first, InputIt last,
-                          std::vector<std::string>& argsOut)
-  {
-    for (; first != last; ++first) {
-      cmSystemTools::ExpandListArgument(*first, argsOut);
-    }
-  }
-
-  /**
-   * Same as ExpandListArgument but a new vector is created containing
-   * the expanded arguments from the string @a arg.
-   */
-  static std::vector<std::string> ExpandedListArgument(const std::string& arg,
-                                                       bool emptyArgs = false);
-
-  /**
-   * Same as ExpandList but a new vector is created containing the expanded
-   * versions of all arguments in the string range [@a first, @a last).
-   */
-  template <class InputIt>
-  static std::vector<std::string> ExpandedLists(InputIt first, InputIt last)
-  {
-    std::vector<std::string> argsOut;
-    for (; first != last; ++first) {
-      cmSystemTools::ExpandListArgument(*first, argsOut);
-    }
-    return argsOut;
-  }
+  using Superclass = cmsys::SystemTools;
+  using Encoding = cmProcessOutput::Encoding;
 
   /**
    * Look for and replace registry values in a string
@@ -76,16 +37,8 @@
   static void ExpandRegistryValues(std::string& source,
                                    KeyWOW64 view = KeyWOW64_Default);
 
-  //! Escape quotes in a string.
-  static std::string EscapeQuotes(const std::string& str);
-
   /** Map help document name to file name.  */
-  static std::string HelpFileName(std::string);
-
-  /**
-   * Returns a string that has whitespace removed from the start and the end.
-   */
-  static std::string TrimWhitespace(const std::string& s);
+  static std::string HelpFileName(cm::string_view);
 
   using MessageCallback = std::function<void(const std::string&, const char*)>;
   /**
@@ -144,31 +97,6 @@
     cmSystemTools::s_ErrorOccured = false;
   }
 
-  /**
-   * Does a string indicates that CMake/CPack/CTest internally
-   * forced this value. This is not the same as On, but this
-   * may be considered as "internally switched on".
-   */
-  static bool IsInternallyOn(const char* val);
-  /**
-   * does a string indicate a true or on value ? This is not the same
-   * as ifdef.
-   */
-  static bool IsOn(const char* val);
-  static bool IsOn(const std::string& val);
-
-  /**
-   * does a string indicate a false or off value ? Note that this is
-   * not the same as !IsOn(...) because there are a number of
-   * ambiguous values such as "/usr/local/bin" a path will result in
-   * IsON and IsOff both returning false. Note that the special path
-   * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
-   */
-  static bool IsOff(const char* val);
-  static bool IsOff(const std::string& val);
-
-  //! Return true if value is NOTFOUND or ends in -NOTFOUND.
-  static bool IsNOTFOUND(const char* value);
   //! Return true if the path is a framework
   static bool IsPathToFramework(const std::string& value);
 
@@ -206,6 +134,10 @@
   static bool RenameFile(const std::string& oldname,
                          const std::string& newname);
 
+  //! Rename a file if contents are different, delete the source otherwise
+  static void MoveFileIfDifferent(const std::string& source,
+                                  const std::string& destination);
+
   //! Compute the hash of a file
   static std::string ComputeFileHash(const std::string& source,
                                      cmCryptoHash::Algo algo);
@@ -299,27 +231,6 @@
   static void EnableRunCommandOutput() { s_DisableRunCommandOutput = false; }
   static bool GetRunCommandOutput() { return s_DisableRunCommandOutput; }
 
-  /**
-   * Some constants for different file formats.
-   */
-  enum FileFormat
-  {
-    NO_FILE_FORMAT = 0,
-    C_FILE_FORMAT,
-    CXX_FILE_FORMAT,
-    FORTRAN_FILE_FORMAT,
-    JAVA_FILE_FORMAT,
-    CUDA_FILE_FORMAT,
-    HEADER_FILE_FORMAT,
-    RESOURCE_FILE_FORMAT,
-    DEFINITION_FILE_FORMAT,
-    STATIC_LIBRARY_FILE_FORMAT,
-    SHARED_LIBRARY_FILE_FORMAT,
-    MODULE_FILE_FORMAT,
-    OBJECT_FILE_FORMAT,
-    UNKNOWN_FILE_FORMAT
-  };
-
   enum CompareOp
   {
     OP_EQUAL = 1,
@@ -350,11 +261,6 @@
    */
   static int strverscmp(std::string const& lhs, std::string const& rhs);
 
-  /**
-   * Determine the file type based on the extension
-   */
-  static FileFormat GetFileFormat(std::string const& ext);
-
   /** Windows if this is true, the CreateProcess in RunCommand will
    *  not show new console windows when running programs.
    */
@@ -401,7 +307,7 @@
   static std::string ForceToRelativePath(std::string const& local_path,
                                          std::string const& remote_path);
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   /** Remove an environment variable */
   static bool UnsetEnv(const char* value);
 
@@ -499,6 +405,7 @@
   /** Try to set the RPATH in an ELF binary.  */
   static bool ChangeRPath(std::string const& file, std::string const& oldRPath,
                           std::string const& newRPath,
+                          bool removeEnvironmentRPath,
                           std::string* emsg = nullptr,
                           bool* changed = nullptr);
 
@@ -513,14 +420,6 @@
   /** Remove a directory; repeat a few times in case of locked files.  */
   static bool RepeatedRemoveDirectory(const std::string& dir);
 
-  /** Tokenize a string */
-  static std::vector<std::string> tokenize(const std::string& str,
-                                           const std::string& sep);
-
-  /** Convert string to long. Expected that the whole string is an integer */
-  static bool StringToLong(const char* str, long* value);
-  static bool StringToULong(const char* str, unsigned long* value);
-
   /** Encode a string as a URL.  */
   static std::string EncodeURL(std::string const& in,
                                bool escapeSlashes = true);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index a67122c..99c16f2 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -2,16 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTarget.h"
 
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
-#include <assert.h>
+#include <cassert>
+#include <cstring>
 #include <initializer_list>
 #include <iterator>
 #include <set>
 #include <sstream>
-#include <string.h>
 #include <unordered_set>
 
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmGeneratorExpression.h"
@@ -86,8 +89,7 @@
   std::ostringstream ss;
   const char* sep = "";
   for (std::string const& entry : entries) {
-    std::vector<std::string> files;
-    cmSystemTools::ExpandListArgument(entry, files);
+    std::vector<std::string> files = cmExpandedList(entry);
     for (std::string const& file : files) {
       if (cmHasLiteralPrefix(file, "$<TARGET_OBJECTS:") &&
           file.back() == '>') {
@@ -168,7 +170,8 @@
   cmPropertyMap Properties;
   bool IsGeneratorProvided;
   bool HaveInstallRule;
-  bool DLLPlatform;
+  bool IsDLLPlatform;
+  bool IsAIX;
   bool IsAndroid;
   bool IsImportedTarget;
   bool ImportedGloballyVisible;
@@ -188,6 +191,8 @@
   std::vector<cmListFileBacktrace> CompileFeaturesBacktraces;
   std::vector<std::string> CompileDefinitionsEntries;
   std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
+  std::vector<std::string> PrecompileHeadersEntries;
+  std::vector<cmListFileBacktrace> PrecompileHeadersBacktraces;
   std::vector<std::string> SourceEntries;
   std::vector<cmListFileBacktrace> SourceBacktraces;
   std::vector<std::string> LinkOptionsEntries;
@@ -217,7 +222,8 @@
   impl->Name = name;
   impl->IsGeneratorProvided = false;
   impl->HaveInstallRule = false;
-  impl->DLLPlatform = false;
+  impl->IsDLLPlatform = false;
+  impl->IsAIX = false;
   impl->IsAndroid = false;
   impl->IsImportedTarget =
     (vis == VisibilityImported || vis == VisibilityImportedGlobally);
@@ -225,21 +231,32 @@
   impl->BuildInterfaceIncludesAppended = false;
 
   // Check whether this is a DLL platform.
-  impl->DLLPlatform =
+  impl->IsDLLPlatform =
     !impl->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty();
 
+  // Check whether we are targeting AIX.
+  impl->IsAIX =
+    (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "AIX");
+
   // Check whether we are targeting an Android platform.
   impl->IsAndroid =
     (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android");
 
-  std::string gKey;
-  gKey.reserve(128);
-  gKey += "CMAKE_";
-  auto InitProperty = [this, mf, &gKey](const std::string& property,
-                                        const char* default_value) {
+  std::string defKey;
+  defKey.reserve(128);
+  defKey += "CMAKE_";
+  auto initProp = [this, mf, &defKey](const std::string& property) {
     // Replace everything after "CMAKE_"
-    gKey.replace(gKey.begin() + 6, gKey.end(), property);
-    if (const char* value = mf->GetDefinition(gKey)) {
+    defKey.replace(defKey.begin() + 6, defKey.end(), property);
+    if (const char* value = mf->GetDefinition(defKey)) {
+      this->SetProperty(property, value);
+    }
+  };
+  auto initPropValue = [this, mf, &defKey](const std::string& property,
+                                           const char* default_value) {
+    // Replace everything after "CMAKE_"
+    defKey.replace(defKey.begin() + 6, defKey.end(), property);
+    if (const char* value = mf->GetDefinition(defKey)) {
       this->SetProperty(property, value);
     } else if (default_value) {
       this->SetProperty(property, default_value);
@@ -249,111 +266,130 @@
   // Setup default property values.
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("ANDROID_API", nullptr);
-    InitProperty("ANDROID_API_MIN", nullptr);
-    InitProperty("ANDROID_ARCH", nullptr);
-    InitProperty("ANDROID_STL_TYPE", nullptr);
-    InitProperty("ANDROID_SKIP_ANT_STEP", nullptr);
-    InitProperty("ANDROID_PROCESS_MAX", nullptr);
-    InitProperty("ANDROID_PROGUARD", nullptr);
-    InitProperty("ANDROID_PROGUARD_CONFIG_PATH", nullptr);
-    InitProperty("ANDROID_SECURE_PROPS_PATH", nullptr);
-    InitProperty("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr);
-    InitProperty("ANDROID_JAVA_SOURCE_DIR", nullptr);
-    InitProperty("ANDROID_JAR_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_JAR_DEPENDENCIES", nullptr);
-    InitProperty("ANDROID_ASSETS_DIRECTORIES", nullptr);
-    InitProperty("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr);
-    InitProperty("BUILD_RPATH", nullptr);
-    InitProperty("BUILD_RPATH_USE_ORIGIN", nullptr);
-    InitProperty("INSTALL_NAME_DIR", nullptr);
-    InitProperty("INSTALL_RPATH", "");
-    InitProperty("INSTALL_RPATH_USE_LINK_PATH", "OFF");
-    InitProperty("INTERPROCEDURAL_OPTIMIZATION", nullptr);
-    InitProperty("SKIP_BUILD_RPATH", "OFF");
-    InitProperty("BUILD_WITH_INSTALL_RPATH", "OFF");
-    InitProperty("ARCHIVE_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("LIBRARY_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("RUNTIME_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("PDB_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr);
-    InitProperty("FRAMEWORK", nullptr);
-    InitProperty("Fortran_FORMAT", nullptr);
-    InitProperty("Fortran_MODULE_DIRECTORY", nullptr);
-    InitProperty("Fortran_COMPILER_LAUNCHER", nullptr);
-    InitProperty("GNUtoMS", nullptr);
-    InitProperty("OSX_ARCHITECTURES", nullptr);
-    InitProperty("IOS_INSTALL_COMBINED", nullptr);
-    InitProperty("AUTOMOC", nullptr);
-    InitProperty("AUTOUIC", nullptr);
-    InitProperty("AUTORCC", nullptr);
-    InitProperty("AUTOGEN_ORIGIN_DEPENDS", nullptr);
-    InitProperty("AUTOGEN_PARALLEL", nullptr);
-    InitProperty("AUTOMOC_COMPILER_PREDEFINES", nullptr);
-    InitProperty("AUTOMOC_DEPEND_FILTERS", nullptr);
-    InitProperty("AUTOMOC_MACRO_NAMES", nullptr);
-    InitProperty("AUTOMOC_MOC_OPTIONS", nullptr);
-    InitProperty("AUTOUIC_OPTIONS", nullptr);
-    InitProperty("AUTOUIC_SEARCH_PATHS", nullptr);
-    InitProperty("AUTORCC_OPTIONS", nullptr);
-    InitProperty("LINK_DEPENDS_NO_SHARED", nullptr);
-    InitProperty("LINK_INTERFACE_LIBRARIES", nullptr);
-    InitProperty("MSVC_RUNTIME_LIBRARY", nullptr);
-    InitProperty("WIN32_EXECUTABLE", nullptr);
-    InitProperty("MACOSX_BUNDLE", nullptr);
-    InitProperty("MACOSX_RPATH", nullptr);
-    InitProperty("NO_SYSTEM_FROM_IMPORTED", nullptr);
-    InitProperty("BUILD_WITH_INSTALL_NAME_DIR", nullptr);
-    InitProperty("C_CLANG_TIDY", nullptr);
-    InitProperty("C_COMPILER_LAUNCHER", nullptr);
-    InitProperty("C_CPPLINT", nullptr);
-    InitProperty("C_CPPCHECK", nullptr);
-    InitProperty("C_INCLUDE_WHAT_YOU_USE", nullptr);
-    InitProperty("LINK_WHAT_YOU_USE", nullptr);
-    InitProperty("C_STANDARD", nullptr);
-    InitProperty("C_STANDARD_REQUIRED", nullptr);
-    InitProperty("C_EXTENSIONS", nullptr);
-    InitProperty("CXX_CLANG_TIDY", nullptr);
-    InitProperty("CXX_COMPILER_LAUNCHER", nullptr);
-    InitProperty("CXX_CPPLINT", nullptr);
-    InitProperty("CXX_CPPCHECK", nullptr);
-    InitProperty("CXX_INCLUDE_WHAT_YOU_USE", nullptr);
-    InitProperty("CXX_STANDARD", nullptr);
-    InitProperty("CXX_STANDARD_REQUIRED", nullptr);
-    InitProperty("CXX_EXTENSIONS", nullptr);
-    InitProperty("CUDA_STANDARD", nullptr);
-    InitProperty("CUDA_STANDARD_REQUIRED", nullptr);
-    InitProperty("CUDA_EXTENSIONS", nullptr);
-    InitProperty("CUDA_COMPILER_LAUNCHER", nullptr);
-    InitProperty("CUDA_SEPARABLE_COMPILATION", nullptr);
-    InitProperty("LINK_SEARCH_START_STATIC", nullptr);
-    InitProperty("LINK_SEARCH_END_STATIC", nullptr);
-    InitProperty("FOLDER", nullptr);
-    InitProperty("Swift_MODULE_DIRECTORY", nullptr);
-    InitProperty("VS_JUST_MY_CODE_DEBUGGING", nullptr);
+    initProp("ANDROID_API");
+    initProp("ANDROID_API_MIN");
+    initProp("ANDROID_ARCH");
+    initProp("ANDROID_STL_TYPE");
+    initProp("ANDROID_SKIP_ANT_STEP");
+    initProp("ANDROID_PROCESS_MAX");
+    initProp("ANDROID_PROGUARD");
+    initProp("ANDROID_PROGUARD_CONFIG_PATH");
+    initProp("ANDROID_SECURE_PROPS_PATH");
+    initProp("ANDROID_NATIVE_LIB_DIRECTORIES");
+    initProp("ANDROID_NATIVE_LIB_DEPENDENCIES");
+    initProp("ANDROID_JAVA_SOURCE_DIR");
+    initProp("ANDROID_JAR_DIRECTORIES");
+    initProp("ANDROID_JAR_DEPENDENCIES");
+    initProp("ANDROID_ASSETS_DIRECTORIES");
+    initProp("ANDROID_ANT_ADDITIONAL_OPTIONS");
+    initProp("BUILD_RPATH");
+    initProp("BUILD_RPATH_USE_ORIGIN");
+    initProp("INSTALL_NAME_DIR");
+    initProp("INSTALL_REMOVE_ENVIRONMENT_RPATH");
+    initPropValue("INSTALL_RPATH", "");
+    initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF");
+    initProp("INTERPROCEDURAL_OPTIMIZATION");
+    initPropValue("SKIP_BUILD_RPATH", "OFF");
+    initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF");
+    initProp("ARCHIVE_OUTPUT_DIRECTORY");
+    initProp("LIBRARY_OUTPUT_DIRECTORY");
+    initProp("RUNTIME_OUTPUT_DIRECTORY");
+    initProp("PDB_OUTPUT_DIRECTORY");
+    initProp("COMPILE_PDB_OUTPUT_DIRECTORY");
+    initProp("FRAMEWORK");
+    initProp("Fortran_FORMAT");
+    initProp("Fortran_MODULE_DIRECTORY");
+    initProp("Fortran_COMPILER_LAUNCHER");
+    initProp("GNUtoMS");
+    initProp("OSX_ARCHITECTURES");
+    initProp("IOS_INSTALL_COMBINED");
+    initProp("AUTOMOC");
+    initProp("AUTOUIC");
+    initProp("AUTORCC");
+    initProp("AUTOGEN_ORIGIN_DEPENDS");
+    initProp("AUTOGEN_PARALLEL");
+    initProp("AUTOMOC_COMPILER_PREDEFINES");
+    initProp("AUTOMOC_DEPEND_FILTERS");
+    initProp("AUTOMOC_MACRO_NAMES");
+    initProp("AUTOMOC_MOC_OPTIONS");
+    initProp("AUTOUIC_OPTIONS");
+    initProp("AUTOMOC_PATH_PREFIX");
+    initProp("AUTOUIC_SEARCH_PATHS");
+    initProp("AUTORCC_OPTIONS");
+    initProp("LINK_DEPENDS_NO_SHARED");
+    initProp("LINK_INTERFACE_LIBRARIES");
+    initProp("MSVC_RUNTIME_LIBRARY");
+    initProp("WIN32_EXECUTABLE");
+    initProp("MACOSX_BUNDLE");
+    initProp("MACOSX_RPATH");
+    initProp("NO_SYSTEM_FROM_IMPORTED");
+    initProp("BUILD_WITH_INSTALL_NAME_DIR");
+    initProp("C_CLANG_TIDY");
+    initProp("C_COMPILER_LAUNCHER");
+    initProp("C_CPPLINT");
+    initProp("C_CPPCHECK");
+    initProp("C_INCLUDE_WHAT_YOU_USE");
+    initProp("LINK_WHAT_YOU_USE");
+    initProp("C_STANDARD");
+    initProp("C_STANDARD_REQUIRED");
+    initProp("C_EXTENSIONS");
+    initProp("OBJC_STANDARD");
+    initProp("OBJC_STANDARD_REQUIRED");
+    initProp("OBJC_EXTENSIONS");
+    initProp("CXX_CLANG_TIDY");
+    initProp("CXX_COMPILER_LAUNCHER");
+    initProp("CXX_CPPLINT");
+    initProp("CXX_CPPCHECK");
+    initProp("CXX_INCLUDE_WHAT_YOU_USE");
+    initProp("CXX_STANDARD");
+    initProp("CXX_STANDARD_REQUIRED");
+    initProp("CXX_EXTENSIONS");
+    initProp("OBJCXX_STANDARD");
+    initProp("OBJCXX_STANDARD_REQUIRED");
+    initProp("OBJCXX_EXTENSIONS");
+    initProp("CUDA_STANDARD");
+    initProp("CUDA_STANDARD_REQUIRED");
+    initProp("CUDA_EXTENSIONS");
+    initProp("CUDA_COMPILER_LAUNCHER");
+    initProp("CUDA_SEPARABLE_COMPILATION");
+    initProp("CUDA_RESOLVE_DEVICE_SYMBOLS");
+    initProp("LINK_SEARCH_START_STATIC");
+    initProp("LINK_SEARCH_END_STATIC");
+    initProp("FOLDER");
+    initProp("Swift_LANGUAGE_VERSION");
+    initProp("Swift_MODULE_DIRECTORY");
+    initProp("VS_JUST_MY_CODE_DEBUGGING");
+    initProp("DISABLE_PRECOMPILE_HEADERS");
+    initProp("UNITY_BUILD");
+    initPropValue("UNITY_BUILD_BATCH_SIZE", "8");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
-      InitProperty("XCODE_GENERATE_SCHEME", nullptr);
-      InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr);
-      InitProperty("XCODE_SCHEME_THREAD_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", nullptr);
-      InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", nullptr);
-      InitProperty("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr);
-      InitProperty("XCODE_SCHEME_GUARD_MALLOC", nullptr);
-      InitProperty("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr);
-      InitProperty("XCODE_SCHEME_MALLOC_STACK", nullptr);
-      InitProperty("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", nullptr);
-      InitProperty("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr);
+      initProp("XCODE_SCHEME_ADDRESS_SANITIZER");
+      initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
+      initProp("XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING");
+      initProp("XCODE_SCHEME_THREAD_SANITIZER");
+      initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP");
+      initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
+      initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
+      initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER");
+      initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
+      initProp("XCODE_SCHEME_MALLOC_SCRIBBLE");
+      initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES");
+      initProp("XCODE_SCHEME_GUARD_MALLOC");
+      initProp("XCODE_SCHEME_ZOMBIE_OBJECTS");
+      initProp("XCODE_SCHEME_MALLOC_STACK");
+      initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
+      initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
     }
 #endif
   }
 
+  if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+    if (this->GetGlobalGenerator()->IsXcode()) {
+      initProp("XCODE_GENERATE_SCHEME");
+    }
+  }
+
   // Setup per-configuration property default values.
   if (this->GetType() != cmStateEnums::UTILITY) {
     static const auto configProps = {
@@ -375,9 +411,8 @@
             strcmp(prop, "MAP_IMPORTED_CONFIG_") != 0) {
           continue;
         }
-        std::string property = prop;
-        property += configUpper;
-        InitProperty(property, nullptr);
+        std::string property = cmStrCat(prop, configUpper);
+        initProp(property);
       }
 
       // Initialize per-configuration name postfix property from the
@@ -387,9 +422,9 @@
       // property directly.
       if (impl->TargetType != cmStateEnums::EXECUTABLE &&
           impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) {
-        std::string property = cmSystemTools::UpperCase(configName);
-        property += "_POSTFIX";
-        InitProperty(property, nullptr);
+        std::string property =
+          cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX");
+        initProp(property);
       }
     }
   }
@@ -428,16 +463,18 @@
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("C_VISIBILITY_PRESET", nullptr);
-    InitProperty("CXX_VISIBILITY_PRESET", nullptr);
-    InitProperty("CUDA_VISIBILITY_PRESET", nullptr);
-    InitProperty("VISIBILITY_INLINES_HIDDEN", nullptr);
+    initProp("C_VISIBILITY_PRESET");
+    initProp("CXX_VISIBILITY_PRESET");
+    initProp("OBJC_VISIBILITY_PRESET");
+    initProp("OBJCXX_VISIBILITY_PRESET");
+    initProp("CUDA_VISIBILITY_PRESET");
+    initProp("VISIBILITY_INLINES_HIDDEN");
   }
 
   if (impl->TargetType == cmStateEnums::EXECUTABLE) {
-    InitProperty("ANDROID_GUI", nullptr);
-    InitProperty("CROSSCOMPILING_EMULATOR", nullptr);
-    InitProperty("ENABLE_EXPORTS", nullptr);
+    initProp("ANDROID_GUI");
+    initProp("CROSSCOMPILING_EMULATOR");
+    initProp("ENABLE_EXPORTS");
   }
   if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
       impl->TargetType == cmStateEnums::MODULE_LIBRARY) {
@@ -445,12 +482,12 @@
   }
   if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
       impl->TargetType == cmStateEnums::EXECUTABLE) {
-    InitProperty("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr);
+    initProp("WINDOWS_EXPORT_ALL_SYMBOLS");
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("POSITION_INDEPENDENT_CODE", nullptr);
+    initProp("POSITION_INDEPENDENT_CODE");
   }
 
   // Record current policies for later use.
@@ -466,12 +503,12 @@
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
       this->GetType() != cmStateEnums::UTILITY) {
-    InitProperty("JOB_POOL_COMPILE", nullptr);
-    InitProperty("JOB_POOL_LINK", nullptr);
+    initProp("JOB_POOL_COMPILE");
+    initProp("JOB_POOL_LINK");
   }
 
   if (impl->TargetType <= cmStateEnums::UTILITY) {
-    InitProperty("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr);
+    initProp("DOTNET_TARGET_FRAMEWORK_VERSION");
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
@@ -483,8 +520,7 @@
     if (globals) {
       const std::string genName = mf->GetGlobalGenerator()->GetName();
       if (cmHasLiteralPrefix(genName, "Visual Studio")) {
-        std::vector<std::string> props;
-        cmSystemTools::ExpandListArgument(globals, props);
+        std::vector<std::string> props = cmExpandedList(globals);
         const std::string vsGlobal = "VS_GLOBAL_";
         for (const std::string& i : props) {
           // split NAME=VALUE
@@ -492,7 +528,7 @@
           if (assignment != std::string::npos) {
             const std::string propName = vsGlobal + i.substr(0, assignment);
             const std::string propValue = i.substr(assignment + 1);
-            InitProperty(propName, propValue.c_str());
+            initPropValue(propName, propValue.c_str());
           }
         }
       }
@@ -583,6 +619,11 @@
   impl->PreBuildCommands.push_back(cmd);
 }
 
+void cmTarget::AddPreBuildCommand(cmCustomCommand&& cmd)
+{
+  impl->PreBuildCommands.push_back(std::move(cmd));
+}
+
 std::vector<cmCustomCommand> const& cmTarget::GetPreLinkCommands() const
 {
   return impl->PreLinkCommands;
@@ -593,6 +634,11 @@
   impl->PreLinkCommands.push_back(cmd);
 }
 
+void cmTarget::AddPreLinkCommand(cmCustomCommand&& cmd)
+{
+  impl->PreLinkCommands.push_back(std::move(cmd));
+}
+
 std::vector<cmCustomCommand> const& cmTarget::GetPostBuildCommands() const
 {
   return impl->PostBuildCommands;
@@ -603,6 +649,11 @@
   impl->PostBuildCommands.push_back(cmd);
 }
 
+void cmTarget::AddPostBuildCommand(cmCustomCommand&& cmd)
+{
+  impl->PostBuildCommands.push_back(std::move(cmd));
+}
+
 void cmTarget::AddTracedSources(std::vector<std::string> const& srcs)
 {
   if (!srcs.empty()) {
@@ -674,13 +725,9 @@
   return src;
 }
 
-cmSourceFile* cmTarget::AddSourceCMP0049(const std::string& s)
+std::string cmTarget::GetSourceCMP0049(const std::string& s)
 {
-  std::string src = impl->ProcessSourceItemCMP0049(s);
-  if (!s.empty() && src.empty()) {
-    return nullptr;
-  }
-  return this->AddSource(src);
+  return impl->ProcessSourceItemCMP0049(s);
 }
 
 struct CreateLocation
@@ -726,8 +773,7 @@
 
   bool operator()(std::string const& entry)
   {
-    std::vector<std::string> files;
-    cmSystemTools::ExpandListArgument(entry, files);
+    std::vector<std::string> files = cmExpandedList(entry);
     std::vector<cmSourceFileLocation> locations;
     locations.reserve(files.size());
     std::transform(files.begin(), files.end(), std::back_inserter(locations),
@@ -761,8 +807,7 @@
 
 void cmTarget::ClearDependencyInformation(cmMakefile& mf)
 {
-  std::string depname = this->GetName();
-  depname += "_LIB_DEPENDS";
+  std::string depname = cmStrCat(this->GetName(), "_LIB_DEPENDS");
   mf.RemoveCacheDefinition(depname);
 }
 
@@ -930,8 +975,7 @@
       impl->TargetType <= cmStateEnums::MODULE_LIBRARY &&
       (this->GetPolicyStatusCMP0073() == cmPolicies::OLD ||
        this->GetPolicyStatusCMP0073() == cmPolicies::WARN)) {
-    std::string targetEntry = impl->Name;
-    targetEntry += "_LIB_DEPENDS";
+    std::string targetEntry = cmStrCat(impl->Name, "_LIB_DEPENDS");
     std::string dependencies;
     const char* old_val = mf.GetDefinition(targetEntry);
     if (old_val) {
@@ -1006,6 +1050,16 @@
   return cmMakeRange(impl->CompileDefinitionsBacktraces);
 }
 
+cmStringRange cmTarget::GetPrecompileHeadersEntries() const
+{
+  return cmMakeRange(impl->PrecompileHeadersEntries);
+}
+
+cmBacktraceRange cmTarget::GetPrecompileHeadersBacktraces() const
+{
+  return cmMakeRange(impl->PrecompileHeadersBacktraces);
+}
+
 cmStringRange cmTarget::GetSourceEntries() const
 {
   return cmMakeRange(impl->SourceEntries);
@@ -1057,6 +1111,8 @@
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(COMPILE_FEATURES);
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS_REUSE_FROM);
   MAKE_STATIC_PROP(CUDA_PTX_COMPILATION);
   MAKE_STATIC_PROP(EXPORT_NAME);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
@@ -1070,21 +1126,19 @@
   MAKE_STATIC_PROP(TYPE);
 #undef MAKE_STATIC_PROP
   if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
-    std::ostringstream e;
-    e << "MANUALLY_ADDED_DEPENDENCIES property is read-only\n";
-    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    impl->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      "MANUALLY_ADDED_DEPENDENCIES property is read-only\n");
     return;
   }
   if (prop == propNAME) {
-    std::ostringstream e;
-    e << "NAME property is read-only\n";
-    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 "NAME property is read-only\n");
     return;
   }
   if (prop == propTYPE) {
-    std::ostringstream e;
-    e << "TYPE property is read-only\n";
-    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 "TYPE property is read-only\n");
     return;
   }
   if (prop == propEXPORT_NAME && this->IsImported()) {
@@ -1157,6 +1211,14 @@
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == propPRECOMPILE_HEADERS) {
+    impl->PrecompileHeadersEntries.clear();
+    impl->PrecompileHeadersBacktraces.clear();
+    if (value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == propLINK_LIBRARIES) {
     impl->LinkImplementationPropertyEntries.clear();
     impl->LinkImplementationPropertyBacktraces.clear();
@@ -1174,7 +1236,7 @@
       impl->SourceBacktraces.push_back(lfbt);
     }
   } else if (prop == propIMPORTED_GLOBAL) {
-    if (!cmSystemTools::IsOn(value)) {
+    if (!cmIsOn(value)) {
       std::ostringstream e;
       e << "IMPORTED_GLOBAL property can't be set to FALSE on targets (\""
         << impl->Name << "\")\n";
@@ -1197,6 +1259,41 @@
       << impl->Name << "\")\n";
     impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
     return;
+  } else if (prop == propPRECOMPILE_HEADERS_REUSE_FROM) {
+    if (this->GetProperty("PRECOMPILE_HEADERS")) {
+      std::ostringstream e;
+      e << "PRECOMPILE_HEADERS property is already set on target (\""
+        << impl->Name << "\")\n";
+      impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return;
+    }
+    auto reusedTarget =
+      impl->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(
+        value);
+    if (!reusedTarget) {
+      const std::string e(
+        "PRECOMPILE_HEADERS_REUSE_FROM set with non existing target");
+      impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+      return;
+    }
+
+    std::string reusedFrom = reusedTarget->GetSafeProperty(prop);
+    if (reusedFrom.empty()) {
+      reusedFrom = value;
+    }
+
+    impl->Properties.SetProperty(prop, reusedFrom.c_str());
+
+    reusedTarget->SetProperty("COMPILE_PDB_NAME", reusedFrom.c_str());
+    reusedTarget->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
+                              cmStrCat(reusedFrom, ".dir/").c_str());
+
+    for (auto p : { "COMPILE_PDB_NAME", "PRECOMPILE_HEADERS",
+                    "INTERFACE_PRECOMPILE_HEADERS" }) {
+      this->SetProperty(p, reusedTarget->GetProperty(p));
+    }
+
+    this->AddUtility(reusedFrom, impl->Makefile);
   } else {
     impl->Properties.SetProperty(prop, value);
   }
@@ -1211,9 +1308,8 @@
     return;
   }
   if (prop == "NAME") {
-    std::ostringstream e;
-    e << "NAME property is read-only\n";
-    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 "NAME property is read-only\n");
     return;
   }
   if (prop == "EXPORT_NAME" && this->IsImported()) {
@@ -1274,6 +1370,20 @@
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == "PRECOMPILE_HEADERS") {
+    if (this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
+      std::ostringstream e;
+      e << "PRECOMPILE_HEADERS_REUSE_FROM property is already set on target "
+           "(\""
+        << impl->Name << "\")\n";
+      impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return;
+    }
+    if (value && *value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
@@ -1322,13 +1432,11 @@
 void cmTarget::InsertInclude(std::string const& entry,
                              cmListFileBacktrace const& bt, bool before)
 {
-  std::vector<std::string>::iterator position = before
-    ? impl->IncludeDirectoriesEntries.begin()
-    : impl->IncludeDirectoriesEntries.end();
+  auto position = before ? impl->IncludeDirectoriesEntries.begin()
+                         : impl->IncludeDirectoriesEntries.end();
 
-  std::vector<cmListFileBacktrace>::iterator btPosition = before
-    ? impl->IncludeDirectoriesBacktraces.begin()
-    : impl->IncludeDirectoriesBacktraces.end();
+  auto btPosition = before ? impl->IncludeDirectoriesBacktraces.begin()
+                           : impl->IncludeDirectoriesBacktraces.end();
 
   impl->IncludeDirectoriesEntries.insert(position, entry);
   impl->IncludeDirectoriesBacktraces.insert(btPosition, bt);
@@ -1337,13 +1445,11 @@
 void cmTarget::InsertCompileOption(std::string const& entry,
                                    cmListFileBacktrace const& bt, bool before)
 {
-  std::vector<std::string>::iterator position = before
-    ? impl->CompileOptionsEntries.begin()
-    : impl->CompileOptionsEntries.end();
+  auto position = before ? impl->CompileOptionsEntries.begin()
+                         : impl->CompileOptionsEntries.end();
 
-  std::vector<cmListFileBacktrace>::iterator btPosition = before
-    ? impl->CompileOptionsBacktraces.begin()
-    : impl->CompileOptionsBacktraces.end();
+  auto btPosition = before ? impl->CompileOptionsBacktraces.begin()
+                           : impl->CompileOptionsBacktraces.end();
 
   impl->CompileOptionsEntries.insert(position, entry);
   impl->CompileOptionsBacktraces.insert(btPosition, bt);
@@ -1359,12 +1465,11 @@
 void cmTarget::InsertLinkOption(std::string const& entry,
                                 cmListFileBacktrace const& bt, bool before)
 {
-  std::vector<std::string>::iterator position =
+  auto position =
     before ? impl->LinkOptionsEntries.begin() : impl->LinkOptionsEntries.end();
 
-  std::vector<cmListFileBacktrace>::iterator btPosition = before
-    ? impl->LinkOptionsBacktraces.begin()
-    : impl->LinkOptionsBacktraces.end();
+  auto btPosition = before ? impl->LinkOptionsBacktraces.begin()
+                           : impl->LinkOptionsBacktraces.end();
 
   impl->LinkOptionsEntries.insert(position, entry);
   impl->LinkOptionsBacktraces.insert(btPosition, bt);
@@ -1373,18 +1478,23 @@
 void cmTarget::InsertLinkDirectory(std::string const& entry,
                                    cmListFileBacktrace const& bt, bool before)
 {
-  std::vector<std::string>::iterator position = before
-    ? impl->LinkDirectoriesEntries.begin()
-    : impl->LinkDirectoriesEntries.end();
+  auto position = before ? impl->LinkDirectoriesEntries.begin()
+                         : impl->LinkDirectoriesEntries.end();
 
-  std::vector<cmListFileBacktrace>::iterator btPosition = before
-    ? impl->LinkDirectoriesBacktraces.begin()
-    : impl->LinkDirectoriesBacktraces.end();
+  auto btPosition = before ? impl->LinkDirectoriesBacktraces.begin()
+                           : impl->LinkDirectoriesBacktraces.end();
 
   impl->LinkDirectoriesEntries.insert(position, entry);
   impl->LinkDirectoriesBacktraces.insert(btPosition, bt);
 }
 
+void cmTarget::InsertPrecompileHeader(std::string const& entry,
+                                      cmListFileBacktrace const& bt)
+{
+  impl->PrecompileHeadersEntries.push_back(entry);
+  impl->PrecompileHeadersBacktraces.push_back(bt);
+}
+
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
                                                   const char* value,
                                                   cmMakefile* context,
@@ -1450,8 +1560,7 @@
                                          cmMakefile* context)
 {
   std::vector<cmTarget*> targets = context->GetOwnedImportedTargets();
-  std::vector<cmTarget*>::const_iterator it =
-    std::find(targets.begin(), targets.end(), target);
+  auto it = std::find(targets.begin(), targets.end(), target);
   if (it == targets.end()) {
     std::ostringstream e;
     e << "Attempt to promote imported target \"" << target->GetName()
@@ -1496,7 +1605,6 @@
 
 const char* cmTarget::GetProperty(const std::string& prop) const
 {
-  static std::unordered_set<std::string> specialProps;
 #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
   MAKE_STATIC_PROP(LINK_LIBRARIES);
   MAKE_STATIC_PROP(TYPE);
@@ -1506,6 +1614,7 @@
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(LINK_OPTIONS);
   MAKE_STATIC_PROP(LINK_DIRECTORIES);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
   MAKE_STATIC_PROP(IMPORTED);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1514,23 +1623,24 @@
   MAKE_STATIC_PROP(SOURCE_DIR);
   MAKE_STATIC_PROP(SOURCES);
 #undef MAKE_STATIC_PROP
-  if (specialProps.empty()) {
-    specialProps.insert(propLINK_LIBRARIES);
-    specialProps.insert(propTYPE);
-    specialProps.insert(propINCLUDE_DIRECTORIES);
-    specialProps.insert(propCOMPILE_FEATURES);
-    specialProps.insert(propCOMPILE_OPTIONS);
-    specialProps.insert(propCOMPILE_DEFINITIONS);
-    specialProps.insert(propLINK_OPTIONS);
-    specialProps.insert(propLINK_DIRECTORIES);
-    specialProps.insert(propIMPORTED);
-    specialProps.insert(propIMPORTED_GLOBAL);
-    specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES);
-    specialProps.insert(propNAME);
-    specialProps.insert(propBINARY_DIR);
-    specialProps.insert(propSOURCE_DIR);
-    specialProps.insert(propSOURCES);
-  }
+  static std::unordered_set<std::string> const specialProps{
+    propLINK_LIBRARIES,
+    propTYPE,
+    propINCLUDE_DIRECTORIES,
+    propCOMPILE_FEATURES,
+    propCOMPILE_OPTIONS,
+    propCOMPILE_DEFINITIONS,
+    propPRECOMPILE_HEADERS,
+    propLINK_OPTIONS,
+    propLINK_DIRECTORIES,
+    propIMPORTED,
+    propIMPORTED_GLOBAL,
+    propMANUALLY_ADDED_DEPENDENCIES,
+    propNAME,
+    propBINARY_DIR,
+    propSOURCE_DIR,
+    propSOURCES
+  };
   if (specialProps.count(prop)) {
     if (prop == propLINK_LIBRARIES) {
       if (impl->LinkImplementationPropertyEntries.empty()) {
@@ -1609,6 +1719,15 @@
       output = cmJoin(impl->Utilities, ";");
       return output.c_str();
     }
+    if (prop == propPRECOMPILE_HEADERS) {
+      if (impl->PrecompileHeadersEntries.empty()) {
+        return nullptr;
+      }
+
+      static std::string output;
+      output = cmJoin(impl->PrecompileHeadersEntries, ";");
+      return output.c_str();
+    }
     if (prop == propIMPORTED) {
       return this->IsImported() ? "TRUE" : "FALSE";
     }
@@ -1655,7 +1774,7 @@
 
 bool cmTarget::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmSystemTools::IsOn(this->GetProperty(prop));
+  return cmIsOn(this->GetProperty(prop));
 }
 
 cmPropertyMap const& cmTarget::GetProperties() const
@@ -1663,6 +1782,16 @@
   return impl->Properties;
 }
 
+bool cmTarget::IsDLLPlatform() const
+{
+  return impl->IsDLLPlatform;
+}
+
+bool cmTarget::IsAIX() const
+{
+  return impl->IsAIX;
+}
+
 bool cmTarget::IsImported() const
 {
   return impl->IsImportedTarget;
@@ -1704,7 +1833,8 @@
                     ? "CMAKE_SHARED_LIBRARY_SUFFIX"
                     : "CMAKE_EXECUTABLE_SUFFIX");
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_SUFFIX";
+          return (impl->IsAIX ? "CMAKE_AIX_IMPORT_FILE_SUFFIX"
+                              : "CMAKE_IMPORT_LIBRARY_SUFFIX");
       }
       break;
     default:
@@ -1744,7 +1874,8 @@
                     ? "CMAKE_SHARED_LIBRARY_PREFIX"
                     : "");
         case cmStateEnums::ImportLibraryArtifact:
-          return "CMAKE_IMPORT_LIBRARY_PREFIX";
+          return (impl->IsAIX ? "CMAKE_AIX_IMPORT_FILE_PREFIX"
+                              : "CMAKE_IMPORT_LIBRARY_PREFIX");
       }
       break;
     default:
@@ -1778,8 +1909,7 @@
         if (loc) {
           result = loc;
         } else {
-          std::string impProp = "IMPORTED_LOCATION";
-          impProp += suffix;
+          std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
           if (const char* config_location = this->GetProperty(impProp)) {
             result = config_location;
           } else if (const char* location =
@@ -1794,8 +1924,7 @@
           result = imp;
         } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
                    this->IsExecutableWithExports()) {
-          std::string impProp = "IMPORTED_IMPLIB";
-          impProp += suffix;
+          std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
           if (const char* config_implib = this->GetProperty(impProp)) {
             result = config_implib;
           } else if (const char* implib =
@@ -1808,8 +1937,7 @@
   }
 
   if (result.empty()) {
-    result = this->GetName();
-    result += "-NOTFOUND";
+    result = cmStrCat(this->GetName(), "-NOTFOUND");
   }
   return result;
 }
@@ -1863,27 +1991,26 @@
   }
 
   // Track the configuration-specific property suffix.
-  suffix = "_";
-  suffix += config_upper;
+  suffix = cmStrCat('_', config_upper);
 
   std::vector<std::string> mappedConfigs;
   {
-    std::string mapProp = "MAP_IMPORTED_CONFIG_";
-    mapProp += config_upper;
+    std::string mapProp = cmStrCat("MAP_IMPORTED_CONFIG_", config_upper);
     if (const char* mapValue = this->GetProperty(mapProp)) {
-      cmSystemTools::ExpandListArgument(mapValue, mappedConfigs, true);
+      cmExpandList(mapValue, mappedConfigs, true);
     }
   }
 
   // If we needed to find one of the mapped configurations but did not
   // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
   // library or an executable with exports.
-  bool allowImp = (impl->DLLPlatform &&
+  bool allowImp = (this->IsDLLPlatform() &&
                    (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
-                    this->IsExecutableWithExports()));
+                    this->IsExecutableWithExports())) ||
+    (this->IsAIX() && this->IsExecutableWithExports());
 
   // If a mapping was found, check its configurations.
-  for (std::vector<std::string>::const_iterator mci = mappedConfigs.begin();
+  for (auto mci = mappedConfigs.begin();
        !*loc && !*imp && mci != mappedConfigs.end(); ++mci) {
     // Look for this configuration.
     if (mci->empty()) {
@@ -1899,19 +2026,16 @@
       }
     } else {
       std::string mcUpper = cmSystemTools::UpperCase(*mci);
-      std::string locProp = locPropBase + "_";
-      locProp += mcUpper;
+      std::string locProp = cmStrCat(locPropBase, '_', mcUpper);
       *loc = this->GetProperty(locProp);
       if (allowImp) {
-        std::string impProp = "IMPORTED_IMPLIB_";
-        impProp += mcUpper;
+        std::string impProp = cmStrCat("IMPORTED_IMPLIB_", mcUpper);
         *imp = this->GetProperty(impProp);
       }
 
       // If it was found, use it for all properties below.
       if (*loc || *imp) {
-        suffix = "_";
-        suffix += mcUpper;
+        suffix = cmStrCat('_', mcUpper);
       }
     }
   }
@@ -1928,12 +2052,10 @@
   // If we have not yet found it then there are no mapped
   // configurations.  Look for an exact-match.
   if (!*loc && !*imp) {
-    std::string locProp = locPropBase;
-    locProp += suffix;
+    std::string locProp = cmStrCat(locPropBase, suffix);
     *loc = this->GetProperty(locProp);
     if (allowImp) {
-      std::string impProp = "IMPORTED_IMPLIB";
-      impProp += suffix;
+      std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
       *imp = this->GetProperty(impProp);
     }
   }
@@ -1957,19 +2079,15 @@
   if (!*loc && !*imp) {
     std::vector<std::string> availableConfigs;
     if (const char* iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) {
-      cmSystemTools::ExpandListArgument(iconfigs, availableConfigs);
+      cmExpandList(iconfigs, availableConfigs);
     }
-    for (std::vector<std::string>::const_iterator aci =
-           availableConfigs.begin();
+    for (auto aci = availableConfigs.begin();
          !*loc && !*imp && aci != availableConfigs.end(); ++aci) {
-      suffix = "_";
-      suffix += cmSystemTools::UpperCase(*aci);
-      std::string locProp = locPropBase;
-      locProp += suffix;
+      suffix = cmStrCat('_', cmSystemTools::UpperCase(*aci));
+      std::string locProp = cmStrCat(locPropBase, suffix);
       *loc = this->GetProperty(locProp);
       if (allowImp) {
-        std::string impProp = "IMPORTED_IMPLIB";
-        impProp += suffix;
+        std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
         *imp = this->GetProperty(impProp);
       }
     }
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 2bd9e6d..65a1ce3 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -6,7 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
@@ -16,6 +16,7 @@
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmTargetLinkLibraryType.h"
 
 class cmCustomCommand;
@@ -42,13 +43,6 @@
     VisibilityImportedGlobally
   };
 
-  enum CustomCommandType
-  {
-    PRE_BUILD,
-    PRE_LINK,
-    POST_BUILD
-  };
-
   cmTarget(std::string const& name, cmStateEnums::TargetType type,
            Visibility vis, cmMakefile* mf);
 
@@ -90,24 +84,27 @@
   //! Get the list of the PRE_BUILD custom commands for this target
   std::vector<cmCustomCommand> const& GetPreBuildCommands() const;
   void AddPreBuildCommand(cmCustomCommand const& cmd);
+  void AddPreBuildCommand(cmCustomCommand&& cmd);
 
   //! Get the list of the PRE_LINK custom commands for this target
   std::vector<cmCustomCommand> const& GetPreLinkCommands() const;
   void AddPreLinkCommand(cmCustomCommand const& cmd);
+  void AddPreLinkCommand(cmCustomCommand&& cmd);
 
   //! Get the list of the POST_BUILD custom commands for this target
   std::vector<cmCustomCommand> const& GetPostBuildCommands() const;
   void AddPostBuildCommand(cmCustomCommand const& cmd);
+  void AddPostBuildCommand(cmCustomCommand&& cmd);
 
   //! Add sources to the target.
   void AddSources(std::vector<std::string> const& srcs);
   void AddTracedSources(std::vector<std::string> const& srcs);
-  cmSourceFile* AddSourceCMP0049(const std::string& src);
+  std::string GetSourceCMP0049(const std::string& src);
   cmSourceFile* AddSource(const std::string& src, bool before = false);
 
   //! how we identify a library, by name and type
-  typedef std::pair<std::string, cmTargetLinkLibraryType> LibraryID;
-  typedef std::vector<LibraryID> LinkLibraryVectorType;
+  using LibraryID = std::pair<std::string, cmTargetLinkLibraryType>;
+  using LinkLibraryVectorType = std::vector<LibraryID>;
   LinkLibraryVectorType const& GetOriginalLinkLibraries() const;
 
   //! Clear the dependency information recorded for this target, if any.
@@ -181,6 +178,12 @@
   //! Get all properties
   cmPropertyMap const& GetProperties() const;
 
+  //! Return whether or not the target is for a DLL platform.
+  bool IsDLLPlatform() const;
+
+  //! Return whether or not we are targeting AIX.
+  bool IsAIX() const;
+
   bool IsImported() const;
   bool IsImportedGloballyVisible() const;
 
@@ -209,6 +212,8 @@
                         cmListFileBacktrace const& bt, bool before = false);
   void InsertLinkDirectory(std::string const& entry,
                            cmListFileBacktrace const& bt, bool before = false);
+  void InsertPrecompileHeader(std::string const& entry,
+                              cmListFileBacktrace const& bt);
 
   void AppendBuildInterfaceIncludes();
 
@@ -230,6 +235,9 @@
   cmStringRange GetCompileDefinitionsEntries() const;
   cmBacktraceRange GetCompileDefinitionsBacktraces() const;
 
+  cmStringRange GetPrecompileHeadersEntries() const;
+  cmBacktraceRange GetPrecompileHeadersBacktraces() const;
+
   cmStringRange GetSourceEntries() const;
   cmBacktraceRange GetSourceBacktraces() const;
 
diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx
index c4dc838..edee167 100644
--- a/Source/cmTargetCompileDefinitionsCommand.cxx
+++ b/Source/cmTargetCompileDefinitionsCommand.cxx
@@ -2,50 +2,57 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileDefinitionsCommand.h"
 
-#include <sstream>
-
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetCompileDefinitionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+class TargetCompileDefinitionsImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "COMPILE_DEFINITIONS");
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetCompileDefinitionsCommand::HandleMissingTarget(
-  const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify compile definitions for target \"" << name
-    << "\" "
-       "which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
-
-std::string cmTargetCompileDefinitionsCommand::Join(
-  const std::vector<std::string>& content)
-{
-  std::string defs;
-  std::string sep;
-  for (std::string const& it : content) {
-    if (cmHasLiteralPrefix(it, "-D")) {
-      defs += sep + it.substr(2);
-    } else {
-      defs += sep + it;
-    }
-    sep = ";";
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify compile definitions for target \"", name,
+               "\" which is not built by this project."));
   }
-  return defs;
-}
 
-bool cmTargetCompileDefinitionsCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool /*prepend*/, bool /*system*/) override
+  {
+    tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content).c_str());
+    return true; // Successfully handled.
+  }
+
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    std::string defs;
+    std::string sep;
+    for (std::string const& it : content) {
+      if (cmHasLiteralPrefix(it, "-D")) {
+        defs += sep + it.substr(2);
+      } else {
+        defs += sep + it;
+      }
+      sep = ";";
+    }
+    return defs;
+  }
+};
+
+} // namespace
+
+bool cmTargetCompileDefinitionsCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status)
 {
-  tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content).c_str());
-  return true; // Successfully handled.
+  return TargetCompileDefinitionsImpl(status).HandleArguments(
+    args, "COMPILE_DEFINITIONS");
 }
diff --git a/Source/cmTargetCompileDefinitionsCommand.h b/Source/cmTargetCompileDefinitionsCommand.h
index d41483a..05ff092 100644
--- a/Source/cmTargetCompileDefinitionsCommand.h
+++ b/Source/cmTargetCompileDefinitionsCommand.h
@@ -8,34 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetCompileDefinitionsCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetCompileDefinitionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-  std::string Join(const std::vector<std::string>& content) override;
-};
+bool cmTargetCompileDefinitionsCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx
index c9e394b..06be4f0 100644
--- a/Source/cmTargetCompileFeaturesCommand.cxx
+++ b/Source/cmTargetCompileFeaturesCommand.cxx
@@ -2,46 +2,54 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileFeaturesCommand.h"
 
-#include <sstream>
-
-#include "cmAlgorithms.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
 class cmTarget;
 
-bool cmTargetCompileFeaturesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
-{
-  return this->HandleArguments(args, "COMPILE_FEATURES", NO_FLAGS);
-}
+namespace {
 
-void cmTargetCompileFeaturesCommand::HandleMissingTarget(
-  const std::string& name)
+class TargetCompileFeaturesImpl : public cmTargetPropCommandBase
 {
-  std::ostringstream e;
-  e << "Cannot specify compile features for target \"" << name
-    << "\" "
-       "which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-std::string cmTargetCompileFeaturesCommand::Join(
-  const std::vector<std::string>& content)
-{
-  return cmJoin(content, ";");
-}
-
-bool cmTargetCompileFeaturesCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
-{
-  for (std::string const& it : content) {
-    std::string error;
-    if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) {
-      this->SetError(error);
-      return false; // Not (successfully) handled.
-    }
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify compile features for target \"", name,
+               "\" which is not built by this project."));
   }
-  return true; // Successfully handled.
+
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool /*prepend*/, bool /*system*/) override
+  {
+    for (std::string const& it : content) {
+      std::string error;
+      if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) {
+        this->SetError(error);
+        return false; // Not (successfully) handled.
+      }
+    }
+    return true; // Successfully handled.
+  }
+
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    return cmJoin(content, ";");
+  }
+};
+
+} // namespace
+
+bool cmTargetCompileFeaturesCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status)
+{
+  return TargetCompileFeaturesImpl(status).HandleArguments(args,
+                                                           "COMPILE_FEATURES");
 }
diff --git a/Source/cmTargetCompileFeaturesCommand.h b/Source/cmTargetCompileFeaturesCommand.h
index 45240a5..db0c04b 100644
--- a/Source/cmTargetCompileFeaturesCommand.h
+++ b/Source/cmTargetCompileFeaturesCommand.h
@@ -8,26 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetCompileFeaturesCommand : public cmTargetPropCommandBase
-{
-  cmCommand* Clone() override { return new cmTargetCompileFeaturesCommand; }
-
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-  std::string Join(const std::vector<std::string>& content) override;
-};
+bool cmTargetCompileFeaturesCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx
index 8b4763a..e39b726 100644
--- a/Source/cmTargetCompileOptionsCommand.cxx
+++ b/Source/cmTargetCompileOptionsCommand.cxx
@@ -2,41 +2,49 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileOptionsCommand.h"
 
-#include <sstream>
-
-#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetCompileOptionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+class TargetCompileOptionsImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "COMPILE_OPTIONS", PROCESS_BEFORE);
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetCompileOptionsCommand::HandleMissingTarget(
-  const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify compile options for target \"" << name
-    << "\" which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify compile options for target \"", name,
+               "\" which is not built by this project."));
+  }
 
-std::string cmTargetCompileOptionsCommand::Join(
-  const std::vector<std::string>& content)
-{
-  return cmJoin(content, ";");
-}
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool /*prepend*/, bool /*system*/) override
+  {
+    cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+    tgt->InsertCompileOption(this->Join(content), lfbt);
+    return true; // Successfully handled.
+  }
 
-bool cmTargetCompileOptionsCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    return cmJoin(content, ";");
+  }
+};
+
+} // namespace
+
+bool cmTargetCompileOptionsCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status)
 {
-  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-  tgt->InsertCompileOption(this->Join(content), lfbt);
-  return true; // Successfully handled.
+  return TargetCompileOptionsImpl(status).HandleArguments(
+    args, "COMPILE_OPTIONS", TargetCompileOptionsImpl::PROCESS_BEFORE);
 }
diff --git a/Source/cmTargetCompileOptionsCommand.h b/Source/cmTargetCompileOptionsCommand.h
index 6fb151a..3ab1a89 100644
--- a/Source/cmTargetCompileOptionsCommand.h
+++ b/Source/cmTargetCompileOptionsCommand.h
@@ -8,34 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetCompileOptionsCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetCompileOptionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-  std::string Join(const std::vector<std::string>& content) override;
-};
+bool cmTargetCompileOptionsCommand(std::vector<std::string> const& args,
+                                   cmExecutionStatus& status);
 
 #endif
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/cmTargetIncludeDirectoriesCommand.cxx b/Source/cmTargetIncludeDirectoriesCommand.cxx
index d6918c0..95b69f3 100644
--- a/Source/cmTargetIncludeDirectoriesCommand.cxx
+++ b/Source/cmTargetIncludeDirectoriesCommand.cxx
@@ -3,34 +3,44 @@
 #include "cmTargetIncludeDirectoriesCommand.h"
 
 #include <set>
-#include <sstream>
 
 #include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetIncludeDirectoriesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+class TargetIncludeDirectoriesImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "INCLUDE_DIRECTORIES",
-                               ArgumentFlags(PROCESS_BEFORE | PROCESS_SYSTEM));
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetIncludeDirectoriesCommand::HandleMissingTarget(
-  const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify include directories for target \"" << name
-    << "\" which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify include directories for target \"", name,
+               "\" which is not built by this project."));
+  }
 
-std::string cmTargetIncludeDirectoriesCommand::Join(
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool system) override;
+
+  void HandleInterfaceContent(cmTarget* tgt,
+                              const std::vector<std::string>& content,
+                              bool prepend, bool system) override;
+
+  std::string Join(const std::vector<std::string>& content) override;
+};
+
+std::string TargetIncludeDirectoriesImpl::Join(
   const std::vector<std::string>& content)
 {
   std::string dirs;
@@ -39,16 +49,16 @@
   for (std::string const& it : content) {
     if (cmSystemTools::FileIsFullPath(it) ||
         cmGeneratorExpression::Find(it) == 0) {
-      dirs += sep + it;
+      dirs += cmStrCat(sep, it);
     } else {
-      dirs += sep + prefix + it;
+      dirs += cmStrCat(sep, prefix, it);
     }
     sep = ";";
   }
   return dirs;
 }
 
-bool cmTargetIncludeDirectoriesCommand::HandleDirectContent(
+bool TargetIncludeDirectoriesImpl::HandleDirectContent(
   cmTarget* tgt, const std::vector<std::string>& content, bool prepend,
   bool system)
 {
@@ -70,16 +80,27 @@
   return true; // Successfully handled.
 }
 
-void cmTargetIncludeDirectoriesCommand::HandleInterfaceContent(
+void TargetIncludeDirectoriesImpl::HandleInterfaceContent(
   cmTarget* tgt, const std::vector<std::string>& content, bool prepend,
   bool system)
 {
   cmTargetPropCommandBase::HandleInterfaceContent(tgt, content, prepend,
                                                   system);
-
   if (system) {
     std::string joined = this->Join(content);
     tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES",
                         joined.c_str());
   }
 }
+
+} // namespace
+
+bool cmTargetIncludeDirectoriesCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status)
+{
+  return TargetIncludeDirectoriesImpl(status).HandleArguments(
+    args, "INCLUDE_DIRECTORIES",
+    TargetIncludeDirectoriesImpl::ArgumentFlags(
+      TargetIncludeDirectoriesImpl::PROCESS_BEFORE |
+      TargetIncludeDirectoriesImpl::PROCESS_SYSTEM));
+}
diff --git a/Source/cmTargetIncludeDirectoriesCommand.h b/Source/cmTargetIncludeDirectoriesCommand.h
index 57bf8fc..9958f41 100644
--- a/Source/cmTargetIncludeDirectoriesCommand.h
+++ b/Source/cmTargetIncludeDirectoriesCommand.h
@@ -8,38 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetIncludeDirectoriesCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetIncludeDirectoriesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-  void HandleInterfaceContent(cmTarget* tgt,
-                              const std::vector<std::string>& content,
-                              bool prepend, bool system) override;
-
-  std::string Join(const std::vector<std::string>& content) override;
-};
+bool cmTargetIncludeDirectoriesCommand(std::vector<std::string> const& args,
+                                       cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx
index 269f751..0c68d60 100644
--- a/Source/cmTargetLinkDirectoriesCommand.cxx
+++ b/Source/cmTargetLinkDirectoriesCommand.cxx
@@ -2,34 +2,44 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetLinkDirectoriesCommand.h"
 
-#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"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetLinkDirectoriesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+class TargetLinkDirectoriesImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "LINK_DIRECTORIES", PROCESS_BEFORE);
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetLinkDirectoriesCommand::HandleMissingTarget(
-  const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify link directories for target \"" << name
-    << "\" which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify link directories for target \"", name,
+               "\" which is not built by this project."));
+  }
 
-std::string cmTargetLinkDirectoriesCommand::Join(
+  std::string Join(const std::vector<std::string>& content) override;
+
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool /*system*/) override
+  {
+    cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+    tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend);
+    return true; // Successfully handled.
+  }
+};
+
+std::string TargetLinkDirectoriesImpl::Join(
   const std::vector<std::string>& content)
 {
   std::vector<std::string> directories;
@@ -50,12 +60,11 @@
   return cmJoin(directories, ";");
 }
 
-bool cmTargetLinkDirectoriesCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool)
+} // namespace
+
+bool cmTargetLinkDirectoriesCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status)
 {
-  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-
-  tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend);
-
-  return true; // Successfully handled.
+  return TargetLinkDirectoriesImpl(status).HandleArguments(
+    args, "LINK_DIRECTORIES", TargetLinkDirectoriesImpl::PROCESS_BEFORE);
 }
diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h
index 52c75a0..3724d6c 100644
--- a/Source/cmTargetLinkDirectoriesCommand.h
+++ b/Source/cmTargetLinkDirectoriesCommand.h
@@ -8,34 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetLinkDirectoriesCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetLinkDirectoriesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  std::string Join(const std::vector<std::string>& content) override;
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-};
+bool cmTargetLinkDirectoriesCommand(std::vector<std::string> const& args,
+                                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index 3883b52..0d2383a 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -2,9 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetLinkLibrariesCommand.h"
 
+#include <cstring>
 #include <sstream>
-#include <string.h>
 
+#include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -12,46 +13,67 @@
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmTargetLinkLibraryType.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
+namespace {
 
-const char* cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[3] = {
-  "general", "debug", "optimized"
+enum ProcessingState
+{
+  ProcessingLinkLibraries,
+  ProcessingPlainLinkInterface,
+  ProcessingKeywordLinkInterface,
+  ProcessingPlainPublicInterface,
+  ProcessingKeywordPublicInterface,
+  ProcessingPlainPrivateInterface,
+  ProcessingKeywordPrivateInterface
 };
 
-// cmTargetLinkLibrariesCommand
-bool cmTargetLinkLibrariesCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" };
+
+} // namespace
+
+static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
+                                            int right);
+
+static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
+                          ProcessingState currentProcessingState,
+                          const std::string& lib, cmTargetLinkLibraryType llt);
+
+bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status)
 {
   // Must have at least one argument.
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
+
+  cmMakefile& mf = status.GetMakefile();
+
   // Alias targets cannot be on the LHS of this command.
-  if (this->Makefile->IsAlias(args[0])) {
-    this->SetError("can not be used on an ALIAS target.");
+  if (mf.IsAlias(args[0])) {
+    status.SetError("can not be used on an ALIAS target.");
     return false;
   }
 
   // Lookup the target for which libraries are specified.
-  this->Target =
-    this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(
-      args[0]);
-  if (!this->Target) {
+  cmTarget* target =
+    mf.GetCMakeInstance()->GetGlobalGenerator()->FindTarget(args[0]);
+  if (!target) {
     const std::vector<cmTarget*>& importedTargets =
-      this->Makefile->GetOwnedImportedTargets();
+      mf.GetOwnedImportedTargets();
     for (cmTarget* importedTarget : importedTargets) {
       if (importedTarget->GetName() == args[0]) {
-        this->Target = importedTarget;
+        target = importedTarget;
         break;
       }
     }
   }
-  if (!this->Target) {
+  if (!target) {
     MessageType t = MessageType::FATAL_ERROR; // fail by default
     std::ostringstream e;
     e << "Cannot specify link libraries for target \"" << args[0] << "\" "
@@ -59,7 +81,7 @@
     // The bad target is the only argument. Check how policy CMP0016 is set,
     // and accept, warn or fail respectively:
     if (args.size() < 2) {
-      switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0016)) {
+      switch (mf.GetPolicyStatus(cmPolicies::CMP0016)) {
         case cmPolicies::WARN:
           t = MessageType::AUTHOR_WARNING;
           // Print the warning.
@@ -83,10 +105,10 @@
     // Now actually print the message.
     switch (t) {
       case MessageType::AUTHOR_WARNING:
-        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+        mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
         break;
       case MessageType::FATAL_ERROR:
-        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        mf.IssueMessage(MessageType::FATAL_ERROR, e.str());
         cmSystemTools::SetFatalErrorOccured();
         break;
       default:
@@ -96,11 +118,11 @@
   }
 
   // Having a UTILITY library on the LHS is a bug.
-  if (this->Target->GetType() == cmStateEnums::UTILITY) {
+  if (target->GetType() == cmStateEnums::UTILITY) {
     std::ostringstream e;
     const char* modal = nullptr;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0039)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0039)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0039) << "\n";
         modal = "should";
@@ -113,9 +135,9 @@
         messageType = MessageType::FATAL_ERROR;
     }
     if (modal) {
-      e << "Utility target \"" << this->Target->GetName() << "\" " << modal
+      e << "Utility target \"" << target->GetName() << "\" " << modal
         << " not be used as the target of a target_link_libraries call.";
-      this->Makefile->IssueMessage(messageType, e.str());
+      mf.IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
@@ -133,15 +155,15 @@
 
   // Start with primary linking and switch to link interface
   // specification if the keyword is encountered as the first argument.
-  this->CurrentProcessingState = ProcessingLinkLibraries;
+  ProcessingState currentProcessingState = ProcessingLinkLibraries;
 
   // Add libraries, note that there is an optional prefix
   // of debug and optimized that can be used.
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (args[i] == "LINK_INTERFACE_LIBRARIES") {
-      this->CurrentProcessingState = ProcessingPlainLinkInterface;
+      currentProcessingState = ProcessingPlainLinkInterface;
       if (i != 1) {
-        this->Makefile->IssueMessage(
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The LINK_INTERFACE_LIBRARIES option must appear as the second "
           "argument, just after the target name.");
@@ -149,84 +171,83 @@
       }
     } else if (args[i] == "INTERFACE") {
       if (i != 1 &&
-          this->CurrentProcessingState != ProcessingKeywordPrivateInterface &&
-          this->CurrentProcessingState != ProcessingKeywordPublicInterface &&
-          this->CurrentProcessingState != ProcessingKeywordLinkInterface) {
-        this->Makefile->IssueMessage(
+          currentProcessingState != ProcessingKeywordPrivateInterface &&
+          currentProcessingState != ProcessingKeywordPublicInterface &&
+          currentProcessingState != ProcessingKeywordLinkInterface) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The INTERFACE, PUBLIC or PRIVATE option must appear as the second "
           "argument, just after the target name.");
         return true;
       }
-      this->CurrentProcessingState = ProcessingKeywordLinkInterface;
+      currentProcessingState = ProcessingKeywordLinkInterface;
     } else if (args[i] == "LINK_PUBLIC") {
       if (i != 1 &&
-          this->CurrentProcessingState != ProcessingPlainPrivateInterface &&
-          this->CurrentProcessingState != ProcessingPlainPublicInterface) {
-        this->Makefile->IssueMessage(
+          currentProcessingState != ProcessingPlainPrivateInterface &&
+          currentProcessingState != ProcessingPlainPublicInterface) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The LINK_PUBLIC or LINK_PRIVATE option must appear as the second "
           "argument, just after the target name.");
         return true;
       }
-      this->CurrentProcessingState = ProcessingPlainPublicInterface;
+      currentProcessingState = ProcessingPlainPublicInterface;
     } else if (args[i] == "PUBLIC") {
       if (i != 1 &&
-          this->CurrentProcessingState != ProcessingKeywordPrivateInterface &&
-          this->CurrentProcessingState != ProcessingKeywordPublicInterface &&
-          this->CurrentProcessingState != ProcessingKeywordLinkInterface) {
-        this->Makefile->IssueMessage(
+          currentProcessingState != ProcessingKeywordPrivateInterface &&
+          currentProcessingState != ProcessingKeywordPublicInterface &&
+          currentProcessingState != ProcessingKeywordLinkInterface) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The INTERFACE, PUBLIC or PRIVATE option must appear as the second "
           "argument, just after the target name.");
         return true;
       }
-      this->CurrentProcessingState = ProcessingKeywordPublicInterface;
+      currentProcessingState = ProcessingKeywordPublicInterface;
     } else if (args[i] == "LINK_PRIVATE") {
-      if (i != 1 &&
-          this->CurrentProcessingState != ProcessingPlainPublicInterface &&
-          this->CurrentProcessingState != ProcessingPlainPrivateInterface) {
-        this->Makefile->IssueMessage(
+      if (i != 1 && currentProcessingState != ProcessingPlainPublicInterface &&
+          currentProcessingState != ProcessingPlainPrivateInterface) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The LINK_PUBLIC or LINK_PRIVATE option must appear as the second "
           "argument, just after the target name.");
         return true;
       }
-      this->CurrentProcessingState = ProcessingPlainPrivateInterface;
+      currentProcessingState = ProcessingPlainPrivateInterface;
     } else if (args[i] == "PRIVATE") {
       if (i != 1 &&
-          this->CurrentProcessingState != ProcessingKeywordPrivateInterface &&
-          this->CurrentProcessingState != ProcessingKeywordPublicInterface &&
-          this->CurrentProcessingState != ProcessingKeywordLinkInterface) {
-        this->Makefile->IssueMessage(
+          currentProcessingState != ProcessingKeywordPrivateInterface &&
+          currentProcessingState != ProcessingKeywordPublicInterface &&
+          currentProcessingState != ProcessingKeywordLinkInterface) {
+        mf.IssueMessage(
           MessageType::FATAL_ERROR,
           "The INTERFACE, PUBLIC or PRIVATE option must appear as the second "
           "argument, just after the target name.");
         return true;
       }
-      this->CurrentProcessingState = ProcessingKeywordPrivateInterface;
+      currentProcessingState = ProcessingKeywordPrivateInterface;
     } else if (args[i] == "debug") {
       if (haveLLT) {
-        this->LinkLibraryTypeSpecifierWarning(llt, DEBUG_LibraryType);
+        LinkLibraryTypeSpecifierWarning(mf, llt, DEBUG_LibraryType);
       }
       llt = DEBUG_LibraryType;
       haveLLT = true;
     } else if (args[i] == "optimized") {
       if (haveLLT) {
-        this->LinkLibraryTypeSpecifierWarning(llt, OPTIMIZED_LibraryType);
+        LinkLibraryTypeSpecifierWarning(mf, llt, OPTIMIZED_LibraryType);
       }
       llt = OPTIMIZED_LibraryType;
       haveLLT = true;
     } else if (args[i] == "general") {
       if (haveLLT) {
-        this->LinkLibraryTypeSpecifierWarning(llt, GENERAL_LibraryType);
+        LinkLibraryTypeSpecifierWarning(mf, llt, GENERAL_LibraryType);
       }
       llt = GENERAL_LibraryType;
       haveLLT = true;
     } else if (haveLLT) {
       // The link type was specified by the previous argument.
       haveLLT = false;
-      if (!this->HandleLibrary(args[i], llt)) {
+      if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) {
         return false;
       }
     } else {
@@ -237,9 +258,8 @@
       // only there for backwards compatibility when mixing projects built
       // with old versions of CMake and new)
       llt = GENERAL_LibraryType;
-      std::string linkType = args[0];
-      linkType += "_LINK_TYPE";
-      const char* linkTypeString = this->Makefile->GetDefinition(linkType);
+      std::string linkType = cmStrCat(args[0], "_LINK_TYPE");
+      const char* linkTypeString = mf.GetDefinition(linkType);
       if (linkTypeString) {
         if (strcmp(linkTypeString, "debug") == 0) {
           llt = DEBUG_LibraryType;
@@ -248,7 +268,7 @@
           llt = OPTIMIZED_LibraryType;
         }
       }
-      if (!this->HandleLibrary(args[i], llt)) {
+      if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) {
         return false;
       }
     }
@@ -256,15 +276,14 @@
 
   // Make sure the last argument was not a library type specifier.
   if (haveLLT) {
-    std::ostringstream e;
-    e << "The \"" << cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[llt]
-      << "\" argument must be followed by a library.";
-    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf.IssueMessage(MessageType::FATAL_ERROR,
+                    cmStrCat("The \"", LinkLibraryTypeNames[llt],
+                             "\" argument must be followed by a library."));
     cmSystemTools::SetFatalErrorOccured();
   }
 
   const cmPolicies::PolicyStatus policy22Status =
-    this->Target->GetPolicyStatusCMP0022();
+    target->GetPolicyStatusCMP0022();
 
   // If any of the LINK_ options were given, make sure the
   // LINK_INTERFACE_LIBRARIES target property exists.
@@ -273,41 +292,40 @@
   // result in an empty link interface.
   if ((policy22Status == cmPolicies::OLD ||
        policy22Status == cmPolicies::WARN) &&
-      this->CurrentProcessingState != ProcessingLinkLibraries &&
-      !this->Target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
-    this->Target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
+      currentProcessingState != ProcessingLinkLibraries &&
+      !target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
+    target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
   }
 
   return true;
 }
 
-void cmTargetLinkLibrariesCommand::LinkLibraryTypeSpecifierWarning(int left,
-                                                                   int right)
+static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
+                                            int right)
 {
-  std::ostringstream w;
-  w << "Link library type specifier \""
-    << cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[left]
-    << "\" is followed by specifier \""
-    << cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[right]
-    << "\" instead of a library name.  "
-    << "The first specifier will be ignored.";
-  this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+  mf.IssueMessage(
+    MessageType::AUTHOR_WARNING,
+    cmStrCat(
+      "Link library type specifier \"", LinkLibraryTypeNames[left],
+      "\" is followed by specifier \"", LinkLibraryTypeNames[right],
+      "\" instead of a library name.  The first specifier will be ignored."));
 }
 
-bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
-                                                 cmTargetLinkLibraryType llt)
+static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
+                          ProcessingState currentProcessingState,
+                          const std::string& lib, cmTargetLinkLibraryType llt)
 {
-  if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
-      this->CurrentProcessingState != ProcessingKeywordLinkInterface) {
-    this->Makefile->IssueMessage(
+  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
+      currentProcessingState != ProcessingKeywordLinkInterface) {
+    mf.IssueMessage(
       MessageType::FATAL_ERROR,
       "INTERFACE library can only be used with the INTERFACE keyword of "
       "target_link_libraries");
     return false;
   }
-  if (this->Target->IsImported() &&
-      this->CurrentProcessingState != ProcessingKeywordLinkInterface) {
-    this->Makefile->IssueMessage(
+  if (target->IsImported() &&
+      currentProcessingState != ProcessingKeywordLinkInterface) {
+    mf.IssueMessage(
       MessageType::FATAL_ERROR,
       "IMPORTED library can only be used with the INTERFACE keyword of "
       "target_link_libraries");
@@ -315,19 +333,18 @@
   }
 
   cmTarget::TLLSignature sig =
-    (this->CurrentProcessingState == ProcessingPlainPrivateInterface ||
-     this->CurrentProcessingState == ProcessingPlainPublicInterface ||
-     this->CurrentProcessingState == ProcessingKeywordPrivateInterface ||
-     this->CurrentProcessingState == ProcessingKeywordPublicInterface ||
-     this->CurrentProcessingState == ProcessingKeywordLinkInterface)
+    (currentProcessingState == ProcessingPlainPrivateInterface ||
+     currentProcessingState == ProcessingPlainPublicInterface ||
+     currentProcessingState == ProcessingKeywordPrivateInterface ||
+     currentProcessingState == ProcessingKeywordPublicInterface ||
+     currentProcessingState == ProcessingKeywordLinkInterface)
     ? cmTarget::KeywordTLLSignature
     : cmTarget::PlainTLLSignature;
-  if (!this->Target->PushTLLCommandTrace(
-        sig, this->Makefile->GetExecutionContext())) {
+  if (!target->PushTLLCommandTrace(sig, mf.GetExecutionContext())) {
     std::ostringstream e;
     const char* modal = nullptr;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0023)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0023)) {
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n";
         modal = "should";
@@ -348,14 +365,14 @@
       e << "The " << existingSig
         << " signature for target_link_libraries has "
            "already been used with the target \""
-        << this->Target->GetName()
+        << target->GetName()
         << "\".  All uses of target_link_libraries with a target " << modal
         << " be either all-keyword or all-plain.\n";
-      this->Target->GetTllSignatureTraces(e,
-                                          sig == cmTarget::KeywordTLLSignature
-                                            ? cmTarget::PlainTLLSignature
-                                            : cmTarget::KeywordTLLSignature);
-      this->Makefile->IssueMessage(messageType, e.str());
+      target->GetTllSignatureTraces(e,
+                                    sig == cmTarget::KeywordTLLSignature
+                                      ? cmTarget::PlainTLLSignature
+                                      : cmTarget::KeywordTLLSignature);
+      mf.IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
       }
@@ -365,9 +382,9 @@
   bool warnRemoteInterface = false;
   bool rejectRemoteLinking = false;
   bool encodeRemoteReference = false;
-  if (this->Makefile != this->Target->GetMakefile()) {
+  if (&mf != target->GetMakefile()) {
     // The LHS target was created in another directory.
-    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0079)) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0079)) {
       case cmPolicies::WARN:
         warnRemoteInterface = true;
         CM_FALLTHROUGH;
@@ -388,7 +405,7 @@
     // same directory as the target was created.  Add a suffix to
     // the name to tell ResolveLinkItem to look up the name in the
     // caller's directory.
-    cmDirectoryId const dirId = this->Makefile->GetDirectoryId();
+    cmDirectoryId const dirId = mf.GetDirectoryId();
     libRef = lib + CMAKE_DIRECTORY_ID_SEP + dirId.String;
   } else {
     // This is an absolute path or a library name added by a caller
@@ -400,20 +417,21 @@
   // Handle normal case where the command was called with another keyword than
   // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
   // property of the target on the LHS shall be populated.)
-  if (this->CurrentProcessingState != ProcessingKeywordLinkInterface &&
-      this->CurrentProcessingState != ProcessingPlainLinkInterface) {
+  if (currentProcessingState != ProcessingKeywordLinkInterface &&
+      currentProcessingState != ProcessingPlainLinkInterface) {
 
     if (rejectRemoteLinking) {
-      std::ostringstream e;
-      e << "Attempt to add link library \"" << lib << "\" to target \""
-        << this->Target->GetName()
-        << "\" which is not built in this directory.\n"
-        << "This is allowed only when policy CMP0079 is set to NEW.";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf.IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Attempt to add link library \"", lib, "\" to target \"",
+                 target->GetName(),
+                 "\" which is not built in this "
+                 "directory.\nThis is allowed only when policy CMP0079 "
+                 "is set to NEW."));
       return false;
     }
 
-    cmTarget* tgt = this->Makefile->GetGlobalGenerator()->FindTarget(lib);
+    cmTarget* tgt = mf.GetGlobalGenerator()->FindTarget(lib);
 
     if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) &&
@@ -421,47 +439,48 @@
         (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
         !tgt->IsExecutableWithExports()) {
-      std::ostringstream e;
-      e << "Target \"" << lib << "\" of type "
-        << cmState::GetTargetTypeName(tgt->GetType())
-        << " may not be linked into another target.  One may link only to "
-           "INTERFACE, OBJECT, STATIC or SHARED libraries, or to executables "
-           "with the ENABLE_EXPORTS property set.";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf.IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(
+          "Target \"", lib, "\" of type ",
+          cmState::GetTargetTypeName(tgt->GetType()),
+          " may not be linked into another target. One may link only to "
+          "INTERFACE, OBJECT, STATIC or SHARED libraries, or to ",
+          "executables with the ENABLE_EXPORTS property set."));
     }
 
-    this->Target->AddLinkLibrary(*this->Makefile, lib, libRef, llt);
+    target->AddLinkLibrary(mf, lib, libRef, llt);
   }
 
   if (warnRemoteInterface) {
-    std::ostringstream w;
-    /* clang-format off */
-    w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0079) << "\n"
-      "Target\n  " << this->Target->GetName() << "\nis not created in this "
-      "directory.  For compatibility with older versions of CMake, link "
-      "library\n  " << lib << "\nwill be looked up in the directory in "
-      "which the target was created rather than in this calling "
-      "directory.";
-    /* clang-format on */
-    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+    mf.IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      cmStrCat(
+        cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n  ",
+        target->GetName(),
+        "\nis not created in this "
+        "directory.  For compatibility with older versions of CMake, link "
+        "library\n  ",
+        lib,
+        "\nwill be looked up in the directory in which "
+        "the target was created rather than in this calling directory."));
   }
 
   // Handle (additional) case where the command was called with PRIVATE /
   // LINK_PRIVATE and stop its processing. (The "INTERFACE_LINK_LIBRARIES"
   // property of the target on the LHS shall only be populated if it is a
   // STATIC library.)
-  if (this->CurrentProcessingState == ProcessingKeywordPrivateInterface ||
-      this->CurrentProcessingState == ProcessingPlainPrivateInterface) {
-    if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
-        this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+  if (currentProcessingState == ProcessingKeywordPrivateInterface ||
+      currentProcessingState == ProcessingPlainPrivateInterface) {
+    if (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
+        target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
       std::string configLib =
-        this->Target->GetDebugGeneratorExpressions(libRef, llt);
+        target->GetDebugGeneratorExpressions(libRef, llt);
       if (cmGeneratorExpression::IsValidTargetName(lib) ||
           cmGeneratorExpression::Find(lib) != std::string::npos) {
         configLib = "$<LINK_ONLY:" + configLib + ">";
       }
-      this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
-                                   configLib.c_str());
+      target->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib.c_str());
     }
     return true;
   }
@@ -469,23 +488,23 @@
   // Handle general case where the command was called with another keyword than
   // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES"
   // property of the target on the LHS shall be populated.)
-  this->Target->AppendProperty(
+  target->AppendProperty(
     "INTERFACE_LINK_LIBRARIES",
-    this->Target->GetDebugGeneratorExpressions(libRef, llt).c_str());
+    target->GetDebugGeneratorExpressions(libRef, llt).c_str());
 
   // Stop processing if called without any keyword.
-  if (this->CurrentProcessingState == ProcessingLinkLibraries) {
+  if (currentProcessingState == ProcessingLinkLibraries) {
     return true;
   }
   // Stop processing if policy CMP0022 is set to NEW.
   const cmPolicies::PolicyStatus policy22Status =
-    this->Target->GetPolicyStatusCMP0022();
+    target->GetPolicyStatusCMP0022();
   if (policy22Status != cmPolicies::OLD &&
       policy22Status != cmPolicies::WARN) {
     return true;
   }
   // Stop processing if called with an INTERFACE library on the LHS.
-  if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
     return true;
   }
 
@@ -495,29 +514,27 @@
   {
     // Get the list of configurations considered to be DEBUG.
     std::vector<std::string> debugConfigs =
-      this->Makefile->GetCMakeInstance()->GetDebugConfigs();
+      mf.GetCMakeInstance()->GetDebugConfigs();
     std::string prop;
 
     // Include this library in the link interface for the target.
     if (llt == DEBUG_LibraryType || llt == GENERAL_LibraryType) {
       // Put in the DEBUG configuration interfaces.
       for (std::string const& dc : debugConfigs) {
-        prop = "LINK_INTERFACE_LIBRARIES_";
-        prop += dc;
-        this->Target->AppendProperty(prop, libRef.c_str());
+        prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
+        target->AppendProperty(prop, libRef.c_str());
       }
     }
     if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
       // Put in the non-DEBUG configuration interfaces.
-      this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str());
+      target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str());
 
       // Make sure the DEBUG configuration interfaces exist so that the
       // general one will not be used as a fall-back.
       for (std::string const& dc : debugConfigs) {
-        prop = "LINK_INTERFACE_LIBRARIES_";
-        prop += dc;
-        if (!this->Target->GetProperty(prop)) {
-          this->Target->SetProperty(prop, "");
+        prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
+        if (!target->GetProperty(prop)) {
+          target->SetProperty(prop, "");
         }
       }
     }
diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h
index 54f8cf4..4b2deab 100644
--- a/Source/cmTargetLinkLibrariesCommand.h
+++ b/Source/cmTargetLinkLibrariesCommand.h
@@ -8,56 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmTargetLinkLibraryType.h"
-
 class cmExecutionStatus;
-class cmTarget;
 
-/** \class cmTargetLinkLibrariesCommand
- * \brief Specify a list of libraries to link into executables.
- *
- * cmTargetLinkLibrariesCommand is used to specify a list of libraries to link
- * into executable(s) or shared objects. The names of the libraries
- * should be those defined by the LIBRARY(library) command(s).
- *
- * Additionally, it allows to propagate usage-requirements (including link
- * libraries) from one target into another.
- */
-class cmTargetLinkLibrariesCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetLinkLibrariesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void LinkLibraryTypeSpecifierWarning(int left, int right);
-  static const char* LinkLibraryTypeNames[3];
-
-  cmTarget* Target = nullptr;
-  enum ProcessingState
-  {
-    ProcessingLinkLibraries,
-    ProcessingPlainLinkInterface,
-    ProcessingKeywordLinkInterface,
-    ProcessingPlainPublicInterface,
-    ProcessingKeywordPublicInterface,
-    ProcessingPlainPrivateInterface,
-    ProcessingKeywordPrivateInterface
-  };
-
-  ProcessingState CurrentProcessingState = ProcessingLinkLibraries;
-
-  bool HandleLibrary(const std::string& lib, cmTargetLinkLibraryType llt);
-};
+bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
+                                  cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetLinkOptionsCommand.cxx b/Source/cmTargetLinkOptionsCommand.cxx
index 5366486..df9416f 100644
--- a/Source/cmTargetLinkOptionsCommand.cxx
+++ b/Source/cmTargetLinkOptionsCommand.cxx
@@ -2,40 +2,49 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetLinkOptionsCommand.h"
 
-#include <sstream>
-
-#include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetLinkOptionsCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+class TargetLinkOptionsImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "LINK_OPTIONS", PROCESS_BEFORE);
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetLinkOptionsCommand::HandleMissingTarget(const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify link options for target \"" << name
-    << "\" which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify link options for target \"", name,
+               "\" which is not built by this project."));
+  }
 
-std::string cmTargetLinkOptionsCommand::Join(
-  const std::vector<std::string>& content)
-{
-  return cmJoin(content, ";");
-}
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool /*system*/) override
+  {
+    cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+    tgt->InsertLinkOption(this->Join(content), lfbt, prepend);
+    return true; // Successfully handled.
+  }
 
-bool cmTargetLinkOptionsCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool)
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    return cmJoin(content, ";");
+  }
+};
+
+} // namespace
+
+bool cmTargetLinkOptionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status)
 {
-  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-  tgt->InsertLinkOption(this->Join(content), lfbt, prepend);
-  return true; // Successfully handled.
+  return TargetLinkOptionsImpl(status).HandleArguments(
+    args, "LINK_OPTIONS", TargetLinkOptionsImpl::PROCESS_BEFORE);
 }
diff --git a/Source/cmTargetLinkOptionsCommand.h b/Source/cmTargetLinkOptionsCommand.h
index a1fc9fc..13fb40c 100644
--- a/Source/cmTargetLinkOptionsCommand.h
+++ b/Source/cmTargetLinkOptionsCommand.h
@@ -8,34 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetLinkOptionsCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetLinkOptionsCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-  std::string Join(const std::vector<std::string>& content) override;
-};
+bool cmTargetLinkOptionsCommand(std::vector<std::string> const& args,
+                                cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx
new file mode 100644
index 0000000..c6e2e5c
--- /dev/null
+++ b/Source/cmTargetPrecompileHeadersCommand.cxx
@@ -0,0 +1,87 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTargetPrecompileHeadersCommand.h"
+
+#include <utility>
+
+#include "cmGeneratorExpression.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmTargetPropCommandBase.h"
+
+namespace {
+
+std::vector<std::string> ConvertToAbsoluteContent(
+  const std::vector<std::string>& content, std::string const& baseDir)
+{
+  std::vector<std::string> absoluteContent;
+  absoluteContent.reserve(content.size());
+  for (std::string const& src : content) {
+    std::string absoluteSrc;
+    // Use '<foo.h>' and '"foo.h"' includes and absolute paths as-is.
+    // Interpret relative paths with respect to the source directory.
+    // If the path starts in a generator expression, assume it is absolute.
+    if (cmHasLiteralPrefix(src, "<") || cmHasLiteralPrefix(src, "\"") ||
+        cmSystemTools::FileIsFullPath(src) ||
+        cmGeneratorExpression::Find(src) == 0) {
+      absoluteSrc = src;
+    } else {
+      absoluteSrc = cmStrCat(baseDir, '/', src);
+    }
+    absoluteContent.emplace_back(std::move(absoluteSrc));
+  }
+  return absoluteContent;
+}
+
+class TargetPrecompileHeadersImpl : public cmTargetPropCommandBase
+{
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
+
+private:
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool /*prepend*/, bool /*system*/) override
+  {
+    std::string const& base = this->Makefile->GetCurrentSourceDirectory();
+    tgt->AppendProperty(
+      "PRECOMPILE_HEADERS",
+      this->Join(ConvertToAbsoluteContent(content, base)).c_str());
+    return true;
+  }
+
+  void HandleInterfaceContent(cmTarget* tgt,
+                              const std::vector<std::string>& content,
+                              bool prepend, bool system) override
+  {
+    std::string const& base = this->Makefile->GetCurrentSourceDirectory();
+    cmTargetPropCommandBase::HandleInterfaceContent(
+      tgt, ConvertToAbsoluteContent(content, base), prepend, system);
+  }
+
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify precompile headers for target \"", name,
+               "\" which is not built by this project."));
+  }
+
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    return cmJoin(content, ";");
+  }
+};
+
+} // namespace
+
+bool cmTargetPrecompileHeadersCommand(std::vector<std::string> const& args,
+                                      cmExecutionStatus& status)
+{
+  return TargetPrecompileHeadersImpl(status).HandleArguments(
+    args, "PRECOMPILE_HEADERS",
+    TargetPrecompileHeadersImpl::PROCESS_REUSE_FROM);
+}
diff --git a/Source/cmTargetPrecompileHeadersCommand.h b/Source/cmTargetPrecompileHeadersCommand.h
new file mode 100644
index 0000000..8b0ac97
--- /dev/null
+++ b/Source/cmTargetPrecompileHeadersCommand.h
@@ -0,0 +1,16 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetPrecompileHeadersCommand_h
+#define cmTargetPrecompileHeadersCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+class cmExecutionStatus;
+
+bool cmTargetPrecompileHeadersCommand(std::vector<std::string> const& args,
+                                      cmExecutionStatus& status);
+
+#endif
diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx
index 1b8ee81..bbc1e16 100644
--- a/Source/cmTargetPropCommandBase.cxx
+++ b/Source/cmTargetPropCommandBase.cxx
@@ -2,12 +2,24 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetPropCommandBase.h"
 
+#include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateTypes.h"
 #include "cmTarget.h"
 #include "cmake.h"
 
+cmTargetPropCommandBase::cmTargetPropCommandBase(cmExecutionStatus& status)
+  : Makefile(&status.GetMakefile())
+  , Status(status)
+{
+}
+
+void cmTargetPropCommandBase::SetError(std::string const& e)
+{
+  this->Status.SetError(e);
+}
+
 bool cmTargetPropCommandBase::HandleArguments(
   std::vector<std::string> const& args, const std::string& prop,
   ArgumentFlags flags)
@@ -32,12 +44,13 @@
     this->HandleMissingTarget(args[0]);
     return false;
   }
-  if ((this->Target->GetType() != cmStateEnums::SHARED_LIBRARY) &&
+  if ((this->Target->GetType() != cmStateEnums::EXECUTABLE) &&
       (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) &&
-      (this->Target->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
+      (this->Target->GetType() != cmStateEnums::SHARED_LIBRARY) &&
       (this->Target->GetType() != cmStateEnums::MODULE_LIBRARY) &&
+      (this->Target->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
       (this->Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
-      (this->Target->GetType() != cmStateEnums::EXECUTABLE)) {
+      (this->Target->GetType() != cmStateEnums::UNKNOWN_LIBRARY)) {
     this->SetError("called with non-compilable target type");
     return false;
   }
@@ -64,6 +77,19 @@
     ++argIndex;
   }
 
+  if ((flags & PROCESS_REUSE_FROM) && args[argIndex] == "REUSE_FROM") {
+    if (args.size() != 3) {
+      this->SetError("called with incorrect number of arguments");
+      return false;
+    }
+    ++argIndex;
+
+    this->Target->SetProperty("PRECOMPILE_HEADERS_REUSE_FROM",
+                              args[argIndex].c_str());
+
+    ++argIndex;
+  }
+
   this->Property = prop;
 
   while (argIndex < args.size()) {
diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h
index 943285d..601ad01 100644
--- a/Source/cmTargetPropCommandBase.h
+++ b/Source/cmTargetPropCommandBase.h
@@ -8,18 +8,24 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
+class cmExecutionStatus;
+class cmMakefile;
 class cmTarget;
 
-class cmTargetPropCommandBase : public cmCommand
+class cmTargetPropCommandBase
 {
 public:
+  cmTargetPropCommandBase(cmExecutionStatus& status);
+  virtual ~cmTargetPropCommandBase() = default;
+
+  void SetError(std::string const& e);
+
   enum ArgumentFlags
   {
-    NO_FLAGS = 0,
-    PROCESS_BEFORE = 1,
-    PROCESS_SYSTEM = 2
+    NO_FLAGS = 0x0,
+    PROCESS_BEFORE = 0x1,
+    PROCESS_SYSTEM = 0x2,
+    PROCESS_REUSE_FROM = 0x3
   };
 
   bool HandleArguments(std::vector<std::string> const& args,
@@ -29,6 +35,7 @@
 protected:
   std::string Property;
   cmTarget* Target = nullptr;
+  cmMakefile* Makefile;
 
   virtual void HandleInterfaceContent(cmTarget* tgt,
                                       const std::vector<std::string>& content,
@@ -48,6 +55,8 @@
   bool PopulateTargetProperies(const std::string& scope,
                                const std::vector<std::string>& content,
                                bool prepend, bool system);
+
+  cmExecutionStatus& Status;
 };
 
 #endif
diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx
index 3f763af..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,
@@ -56,22 +57,21 @@
   if (std::islower(prop[0])) {
     return true;
   }
-  static std::unordered_set<std::string> builtIns;
-  if (builtIns.empty()) {
-    builtIns.insert("COMPATIBLE_INTERFACE_BOOL");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX");
-    builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN");
-    builtIns.insert("COMPATIBLE_INTERFACE_STRING");
-    builtIns.insert("EXPORT_NAME");
-    builtIns.insert("EXPORT_PROPERTIES");
-    builtIns.insert("IMPORTED");
-    builtIns.insert("IMPORTED_GLOBAL");
-    builtIns.insert("MANUALLY_ADDED_DEPENDENCIES");
-    builtIns.insert("NAME");
-    builtIns.insert("PRIVATE_HEADER");
-    builtIns.insert("PUBLIC_HEADER");
-    builtIns.insert("TYPE");
-  }
+  static std::unordered_set<std::string> const builtIns{
+    "COMPATIBLE_INTERFACE_BOOL",
+    "COMPATIBLE_INTERFACE_NUMBER_MAX",
+    "COMPATIBLE_INTERFACE_NUMBER_MIN",
+    "COMPATIBLE_INTERFACE_STRING",
+    "EXPORT_NAME",
+    "EXPORT_PROPERTIES",
+    "IMPORTED",
+    "IMPORTED_GLOBAL",
+    "MANUALLY_ADDED_DEPENDENCIES",
+    "NAME",
+    "PRIVATE_HEADER",
+    "PUBLIC_HEADER",
+    "TYPE"
+  };
 
   if (builtIns.count(prop)) {
     return true;
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..c2e0b28 100644
--- a/Source/cmTargetSourcesCommand.cxx
+++ b/Source/cmTargetSourcesCommand.cxx
@@ -4,55 +4,61 @@
 
 #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"
+#include "cmTargetPropCommandBase.h"
 
-class cmExecutionStatus;
+namespace {
 
-bool cmTargetSourcesCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+class TargetSourcesImpl : public cmTargetPropCommandBase
 {
-  return this->HandleArguments(args, "SOURCES");
-}
+public:
+  using cmTargetPropCommandBase::cmTargetPropCommandBase;
 
-void cmTargetSourcesCommand::HandleInterfaceContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool prepend,
-  bool system)
-{
-  cmTargetPropCommandBase::HandleInterfaceContent(
-    tgt, ConvertToAbsoluteContent(tgt, content, true), prepend, system);
-}
+protected:
+  void HandleInterfaceContent(cmTarget* tgt,
+                              const std::vector<std::string>& content,
+                              bool prepend, bool system) override
+  {
+    cmTargetPropCommandBase::HandleInterfaceContent(
+      tgt, ConvertToAbsoluteContent(tgt, content, true), prepend, system);
+  }
 
-void cmTargetSourcesCommand::HandleMissingTarget(const std::string& name)
-{
-  std::ostringstream e;
-  e << "Cannot specify sources for target \"" << name
-    << "\" "
-       "which is not built by this project.";
-  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
+private:
+  void HandleMissingTarget(const std::string& name) override
+  {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Cannot specify sources for target \"", name,
+               "\" which is not built by this project."));
+  }
 
-std::string cmTargetSourcesCommand::Join(
-  const std::vector<std::string>& content)
-{
-  return cmJoin(content, ";");
-}
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool /*prepend*/, bool /*system*/) override
+  {
+    tgt->AppendProperty(
+      "SOURCES",
+      this->Join(ConvertToAbsoluteContent(tgt, content, false)).c_str());
+    return true; // Successfully handled.
+  }
 
-bool cmTargetSourcesCommand::HandleDirectContent(
-  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
-{
-  tgt->AppendProperty(
-    "SOURCES",
-    this->Join(ConvertToAbsoluteContent(tgt, content, false)).c_str());
-  return true; // Successfully handled.
-}
+  std::string Join(const std::vector<std::string>& content) override
+  {
+    return cmJoin(content, ";");
+  }
 
-std::vector<std::string> cmTargetSourcesCommand::ConvertToAbsoluteContent(
+  std::vector<std::string> ConvertToAbsoluteContent(
+    cmTarget* tgt, const std::vector<std::string>& content,
+    bool isInterfaceContent);
+};
+
+std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent(
   cmTarget* tgt, const std::vector<std::string>& content,
   bool isInterfaceContent)
 {
@@ -75,9 +81,8 @@
       absoluteSrc = src;
     } else {
       changedPath = true;
-      absoluteSrc = this->Makefile->GetCurrentSourceDirectory();
-      absoluteSrc += "/";
-      absoluteSrc += src;
+      absoluteSrc =
+        cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', src);
     }
     absoluteContent.push_back(absoluteSrc);
   }
@@ -122,3 +127,11 @@
 
   return useAbsoluteContent ? absoluteContent : content;
 }
+
+} // namespace
+
+bool cmTargetSourcesCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
+{
+  return TargetSourcesImpl(status).HandleArguments(args, "SOURCES");
+}
diff --git a/Source/cmTargetSourcesCommand.h b/Source/cmTargetSourcesCommand.h
index b01e3ca..5eecf34 100644
--- a/Source/cmTargetSourcesCommand.h
+++ b/Source/cmTargetSourcesCommand.h
@@ -8,44 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmTargetPropCommandBase.h"
-
-class cmCommand;
 class cmExecutionStatus;
-class cmTarget;
 
-class cmTargetSourcesCommand : public cmTargetPropCommandBase
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmTargetSourcesCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  void HandleInterfaceContent(cmTarget* tgt,
-                              const std::vector<std::string>& content,
-                              bool prepend, bool system) override;
-
-private:
-  void HandleMissingTarget(const std::string& name) override;
-
-  bool HandleDirectContent(cmTarget* tgt,
-                           const std::vector<std::string>& content,
-                           bool prepend, bool system) override;
-
-  std::string Join(const std::vector<std::string>& content) override;
-
-  std::vector<std::string> ConvertToAbsoluteContent(
-    cmTarget* tgt, const std::vector<std::string>& content,
-    bool isInterfaceContent);
-};
+bool cmTargetSourcesCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index 7d45cf5..d5c61c1 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -5,10 +5,11 @@
 #include "cmMakefile.h"
 #include "cmProperty.h"
 #include "cmState.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 
 cmTest::cmTest(cmMakefile* mf)
-  : Backtrace(mf->GetBacktrace())
+  : CommandExpandLists(false)
+  , Backtrace(mf->GetBacktrace())
 {
   this->Makefile = mf;
   this->OldStyle = true;
@@ -46,7 +47,7 @@
 
 bool cmTest::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmSystemTools::IsOn(this->GetProperty(prop));
+  return cmIsOn(this->GetProperty(prop));
 }
 
 void cmTest::SetProperty(const std::string& prop, const char* value)
@@ -59,3 +60,13 @@
 {
   this->Properties.AppendProperty(prop, value, asString);
 }
+
+bool cmTest::GetCommandExpandLists() const
+{
+  return this->CommandExpandLists;
+}
+
+void cmTest::SetCommandExpandLists(bool b)
+{
+  this->CommandExpandLists = b;
+}
diff --git a/Source/cmTest.h b/Source/cmTest.h
index 88dc730..dd246c4 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -5,12 +5,12 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmListFileCache.h"
-#include "cmPropertyMap.h"
-
 #include <string>
 #include <vector>
 
+#include "cmListFileCache.h"
+#include "cmPropertyMap.h"
+
 class cmMakefile;
 
 /** \class cmTest
@@ -51,10 +51,15 @@
   bool GetOldStyle() const { return this->OldStyle; }
   void SetOldStyle(bool b) { this->OldStyle = b; }
 
+  /** Set/Get whether lists in command lines should be expanded. */
+  bool GetCommandExpandLists() const;
+  void SetCommandExpandLists(bool b);
+
 private:
   cmPropertyMap Properties;
   std::string Name;
   std::vector<std::string> Command;
+  bool CommandExpandLists;
 
   bool OldStyle;
 
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index 571cd09..333d4d5 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -2,18 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTestGenerator.h"
 
+#include <memory>
 #include <ostream>
 #include <utility>
+#include <vector>
 
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTest.h"
 
@@ -76,12 +78,22 @@
   // Start the test command.
   os << indent << "add_test(" << this->Test->GetName() << " ";
 
-  // Get the test command line to be executed.
-  std::vector<std::string> const& command = this->Test->GetCommand();
+  // Evaluate command line arguments
+  std::vector<std::string> argv =
+    EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config);
+
+  // Expand arguments if COMMAND_EXPAND_LISTS is set
+  if (this->Test->GetCommandExpandLists()) {
+    argv = cmExpandedLists(argv.begin(), argv.end());
+    // Expanding lists on an empty command may have left it empty
+    if (argv.empty()) {
+      argv.emplace_back();
+    }
+  }
 
   // Check whether the command executable is a target whose name is to
   // be translated.
-  std::string exe = command[0];
+  std::string exe = argv[0];
   cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe);
   if (target && target->GetType() == cmStateEnums::EXECUTABLE) {
     // Use the target file on disk.
@@ -90,8 +102,7 @@
     // Prepend with the emulator when cross compiling if required.
     const char* emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
     if (emulator != nullptr && *emulator) {
-      std::vector<std::string> emulatorWithArgs;
-      cmSystemTools::ExpandListArgument(emulator, emulatorWithArgs);
+      std::vector<std::string> emulatorWithArgs = cmExpandedList(emulator);
       std::string emulatorExe(emulatorWithArgs[0]);
       cmSystemTools::ConvertToUnixSlashes(emulatorExe);
       os << cmOutputConverter::EscapeForCMake(emulatorExe) << " ";
@@ -101,29 +112,26 @@
     }
   } else {
     // Use the command name given.
-    exe = ge.Parse(exe)->Evaluate(this->LG, config);
     cmSystemTools::ConvertToUnixSlashes(exe);
   }
 
   // Generate the command line with full escapes.
   os << cmOutputConverter::EscapeForCMake(exe);
-  for (std::string const& arg : cmMakeRange(command).advance(1)) {
-    os << " "
-       << cmOutputConverter::EscapeForCMake(
-            ge.Parse(arg)->Evaluate(this->LG, config));
+
+  for (auto const& arg : cmMakeRange(argv).advance(1)) {
+    os << " " << cmOutputConverter::EscapeForCMake(arg);
   }
 
   // Finish the test command.
   os << ")\n";
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   os << indent << "set_tests_properties(" << this->Test->GetName()
      << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     os << " " << i.first << " "
        << cmOutputConverter::EscapeForCMake(
-            ge.Parse(i.second.GetValue())->Evaluate(this->LG, config));
+            ge.Parse(i.second)->Evaluate(this->LG, config));
   }
   this->GenerateInternalProperties(os);
   os << ")" << std::endl;
@@ -173,12 +181,11 @@
   fout << ")" << std::endl;
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   fout << indent << "set_tests_properties(" << this->Test->GetName()
        << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     fout << " " << i.first << " "
-         << cmOutputConverter::EscapeForCMake(i.second.GetValue());
+         << cmOutputConverter::EscapeForCMake(i.second);
   }
   this->GenerateInternalProperties(fout);
   fout << ")" << std::endl;
@@ -208,3 +215,16 @@
 
   os << "\"";
 }
+
+std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments(
+  const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+  const std::string& config) const
+{
+  // Evaluate executable name and arguments
+  auto evaluatedRange =
+    cmMakeRange(argv).transform([&](const std::string& arg) {
+      return ge.Parse(arg)->Evaluate(this->LG, config);
+    });
+
+  return { evaluatedRange.begin(), evaluatedRange.end() };
+}
diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h
index 8b9cf78..e388c16 100644
--- a/Source/cmTestGenerator.h
+++ b/Source/cmTestGenerator.h
@@ -5,12 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmScriptGenerator.h"
-
 #include <iosfwd>
 #include <string>
 #include <vector>
 
+#include "cmScriptGenerator.h"
+
+class cmGeneratorExpression;
 class cmLocalGenerator;
 class cmTest;
 
@@ -38,6 +39,9 @@
 
 private:
   void GenerateInternalProperties(std::ostream& os);
+  std::vector<std::string> EvaluateCommandLineArguments(
+    const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+    const std::string& config) const;
 
 protected:
   void GenerateScriptConfigs(std::ostream& os, Indent indent) override;
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx
index da5d21e..390fd16 100644
--- a/Source/cmTimestamp.cxx
+++ b/Source/cmTimestamp.cxx
@@ -2,10 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTimestamp.h"
 
+#include <cstdlib>
 #include <cstring>
 #include <sstream>
-#include <stdlib.h>
 
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 std::string cmTimestamp::CurrentTime(const std::string& formatString,
@@ -109,7 +110,7 @@
 
   time_t result = mktime(&tm);
 
-#  ifdef CMAKE_BUILD_WITH_CMAKE
+#  ifndef CMAKE_BOOTSTRAP
   if (tz_was_set) {
     cmSystemTools::PutEnv(tz_old);
   } else {
@@ -131,8 +132,7 @@
                                                struct tm& timeStruct,
                                                const time_t timeT) const
 {
-  std::string formatString = "%";
-  formatString += flag;
+  std::string formatString = cmStrCat('%', flag);
 
   switch (flag) {
     case 'a':
@@ -168,9 +168,7 @@
         return std::string();
       }
 
-      std::ostringstream ss;
-      ss << static_cast<long int>(difftime(timeT, unixEpoch));
-      return ss.str();
+      return std::to_string(static_cast<long int>(difftime(timeT, unixEpoch)));
     }
     default: {
       return formatString;
diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h
index d5fbdfd..40338f8 100644
--- a/Source/cmTimestamp.h
+++ b/Source/cmTimestamp.h
@@ -5,8 +5,8 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <ctime>
 #include <string>
-#include <time.h>
 
 /** \class cmTimestamp
  * \brief Utility class to generate string representation of a timestamp
diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h
index 8237878..e525e85 100644
--- a/Source/cmTryCompileCommand.h
+++ b/Source/cmTryCompileCommand.h
@@ -8,9 +8,11 @@
 #include <string>
 #include <vector>
 
+#include <cm/memory>
+
+#include "cmCommand.h"
 #include "cmCoreTryCompile.h"
 
-class cmCommand;
 class cmExecutionStatus;
 
 /** \class cmTryCompileCommand
@@ -24,7 +26,10 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override { return new cmTryCompileCommand; }
+  std::unique_ptr<cmCommand> Clone() override
+  {
+    return cm::make_unique<cmTryCompileCommand>();
+  }
 
   /**
    * This is called when the command is first encountered in
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx
index a92c2a0..0e8e986 100644
--- a/Source/cmTryRunCommand.cxx
+++ b/Source/cmTryRunCommand.cxx
@@ -2,8 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTryRunCommand.h"
 
+#include <cstdio>
+
 #include "cmsys/FStream.hxx"
-#include <stdio.h>
 
 #include "cmDuration.h"
 #include "cmMakefile.h"
@@ -11,6 +12,7 @@
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -137,7 +139,7 @@
       // now put the output into the variables
       if (!this->RunOutputVariable.empty()) {
         this->Makefile->AddDefinition(this->RunOutputVariable,
-                                      runOutputContents.c_str());
+                                      runOutputContents);
       }
 
       if (!this->OutputVariable.empty()) {
@@ -148,8 +150,7 @@
         if (compileOutput) {
           runOutputContents = compileOutput + runOutputContents;
         }
-        this->Makefile->AddDefinition(this->OutputVariable,
-                                      runOutputContents.c_str());
+        this->Makefile->AddDefinition(this->OutputVariable, runOutputContents);
       }
     }
   }
@@ -170,8 +171,7 @@
   const std::string& emulator =
     this->Makefile->GetSafeDefinition("CMAKE_CROSSCOMPILING_EMULATOR");
   if (!emulator.empty()) {
-    std::vector<std::string> emulatorWithArgs;
-    cmSystemTools::ExpandListArgument(emulator, emulatorWithArgs);
+    std::vector<std::string> emulatorWithArgs = cmExpandedList(emulator);
     finalCommand +=
       cmSystemTools::ConvertToRunCommandPath(emulatorWithArgs[0]);
     finalCommand += " ";
@@ -214,20 +214,17 @@
   // copy the executable out of the CMakeFiles/ directory, so it is not
   // removed at the end of TRY_RUN and the user can run it manually
   // on the target platform.
-  std::string copyDest = this->Makefile->GetHomeOutputDirectory();
-  copyDest += "/CMakeFiles";
-  copyDest += "/";
-  copyDest += cmSystemTools::GetFilenameWithoutExtension(this->OutputFile);
-  copyDest += "-";
-  copyDest += this->RunResultVariable;
-  copyDest += cmSystemTools::GetFilenameExtension(this->OutputFile);
+  std::string copyDest =
+    cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/CMakeFiles/",
+             cmSystemTools::GetFilenameWithoutExtension(this->OutputFile), '-',
+             this->RunResultVariable,
+             cmSystemTools::GetFilenameExtension(this->OutputFile));
   cmSystemTools::CopyFileAlways(this->OutputFile, copyDest);
 
-  std::string resultFileName = this->Makefile->GetHomeOutputDirectory();
-  resultFileName += "/TryRunResults.cmake";
+  std::string resultFileName =
+    cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/TryRunResults.cmake");
 
-  std::string detailsString = "For details see ";
-  detailsString += resultFileName;
+  std::string detailsString = cmStrCat("For details see ", resultFileName);
 
   std::string internalRunOutputName =
     this->RunResultVariable + "__TRYRUN_OUTPUT";
@@ -236,10 +233,10 @@
   if (!this->Makefile->GetDefinition(this->RunResultVariable)) {
     // if the variables doesn't exist, create it with a helpful error text
     // and mark it as advanced
-    std::string comment;
-    comment += "Run result of TRY_RUN(), indicates whether the executable "
-               "would have been able to run on its target platform.\n";
-    comment += detailsString;
+    std::string comment =
+      cmStrCat("Run result of TRY_RUN(), indicates whether the executable "
+               "would have been able to run on its target platform.\n",
+               detailsString);
     this->Makefile->AddCacheDefinition(this->RunResultVariable,
                                        "PLEASE_FILL_OUT-FAILED_TO_RUN",
                                        comment.c_str(), cmStateEnums::STRING);
@@ -259,11 +256,10 @@
     if (!this->Makefile->GetDefinition(internalRunOutputName)) {
       // if the variables doesn't exist, create it with a helpful error text
       // and mark it as advanced
-      std::string comment;
-      comment +=
+      std::string comment = cmStrCat(
         "Output of TRY_RUN(), contains the text, which the executable "
-        "would have printed on stdout and stderr on its target platform.\n";
-      comment += detailsString;
+        "would have printed on stdout and stderr on its target platform.\n",
+        detailsString);
 
       this->Makefile->AddCacheDefinition(
         internalRunOutputName, "PLEASE_FILL_OUT-NOTFOUND", comment.c_str(),
@@ -295,15 +291,15 @@
         /* clang-format on */
       }
 
-      std::string comment = "\n";
-      comment += this->RunResultVariable;
-      comment += "\n   indicates whether the executable would have been able "
+      std::string comment =
+        cmStrCat('\n', this->RunResultVariable,
+                 "\n   indicates whether the executable would have been able "
                  "to run on its\n"
-                 "   target platform. If so, set ";
-      comment += this->RunResultVariable;
-      comment += " to\n"
+                 "   target platform. If so, set ",
+                 this->RunResultVariable,
+                 " to\n"
                  "   the exit code (in many cases 0 for success), otherwise "
-                 "enter \"FAILED_TO_RUN\".\n";
+                 "enter \"FAILED_TO_RUN\".\n");
       if (out) {
         comment += internalRunOutputName;
         comment +=
@@ -344,10 +340,11 @@
     }
     firstTryRun = false;
 
-    std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, "
-                               "please set the following cache variables "
-                               "appropriately:\n";
-    errorMessage += "   " + this->RunResultVariable + " (advanced)\n";
+    std::string errorMessage =
+      cmStrCat("TRY_RUN() invoked in cross-compiling mode, "
+               "please set the following cache variables "
+               "appropriately:\n   ",
+               this->RunResultVariable, " (advanced)\n");
     if (out) {
       errorMessage += "   " + internalRunOutputName + " (advanced)\n";
     }
diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h
index c54622c..c53a694 100644
--- a/Source/cmTryRunCommand.h
+++ b/Source/cmTryRunCommand.h
@@ -8,9 +8,11 @@
 #include <string>
 #include <vector>
 
+#include <cm/memory>
+
+#include "cmCommand.h"
 #include "cmCoreTryCompile.h"
 
-class cmCommand;
 class cmExecutionStatus;
 
 /** \class cmTryRunCommand
@@ -24,7 +26,10 @@
   /**
    * This is a virtual constructor for the command.
    */
-  cmCommand* Clone() override { return new cmTryRunCommand; }
+  std::unique_ptr<cmCommand> Clone() override
+  {
+    return cm::make_unique<cmTryRunCommand>();
+  }
 
   /**
    * This is called when the command is first encountered in
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index db67463..23dabb7 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -3,9 +3,9 @@
 #define cmUVHandlePtr_cxx
 #include "cmUVHandlePtr.h"
 
-#include <assert.h>
+#include <cassert>
+#include <cstdlib>
 #include <mutex>
-#include <stdlib.h>
 
 #include "cm_uv.h"
 
@@ -122,7 +122,7 @@
   return this->handle.get();
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 template <>
 struct uv_handle_deleter<uv_async_t>
 {
@@ -230,7 +230,7 @@
   return uv_timer_start(*this, cb, timeout, repeat);
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 uv_tty_ptr::operator uv_stream_t*() const
 {
   return reinterpret_cast<uv_stream_t*>(handle.get());
@@ -259,7 +259,7 @@
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(timer)
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
index c09e4bf..3083b60 100644
--- a/Source/cmUVHandlePtr.h
+++ b/Source/cmUVHandlePtr.h
@@ -240,8 +240,8 @@
   int init(uv_loop_t& loop, int fd, int readable, void* data = nullptr);
 };
 
-typedef uv_handle_ptr_<uv_stream_t> uv_stream_ptr;
-typedef uv_handle_ptr_<uv_handle_t> uv_handle_ptr;
+using uv_stream_ptr = uv_handle_ptr_<uv_stream_t>;
+using uv_handle_ptr = uv_handle_ptr_<uv_handle_t>;
 
 #ifndef cmUVHandlePtr_cxx
 
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
index 90ece0b..543c330 100644
--- a/Source/cmUVProcessChain.cxx
+++ b/Source/cmUVProcessChain.cxx
@@ -2,17 +2,18 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmUVProcessChain.h"
 
-#include "cmAlgorithms.h"
+#include <cassert>
+#include <istream> // IWYU pragma: keep
+#include <iterator>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cm_uv.h"
+
 #include "cmGetPipes.h"
 #include "cmUVHandlePtr.h"
 #include "cmUVStreambuf.h"
-#include "cm_uv.h"
-
-#include <assert.h>
-
-#include <iterator>
-#include <memory>
-#include <utility>
 
 struct cmUVProcessChain::InternalData
 {
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
index 2b33520..05a7cc8 100644
--- a/Source/cmUVProcessChain.h
+++ b/Source/cmUVProcessChain.h
@@ -3,15 +3,15 @@
 #ifndef cmUVProcessChain_h
 #define cmUVProcessChain_h
 
-#include "cm_uv.h"
-
 #include <array>
+#include <cstddef>
+#include <cstdint>
 #include <iosfwd>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <vector>
 
-#include <stdint.h>
+#include "cm_uv.h"
 
 class cmUVProcessChain;
 
diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h
index 873352b..1c8a771 100644
--- a/Source/cmUVStreambuf.h
+++ b/Source/cmUVStreambuf.h
@@ -3,15 +3,15 @@
 #ifndef cmUVStreambuf_h
 #define cmUVStreambuf_h
 
-#include "cmUVHandlePtr.h"
-
-#include "cm_uv.h"
-
 #include <algorithm>
 #include <cstring>
 #include <streambuf>
 #include <vector>
 
+#include "cm_uv.h"
+
+#include "cmUVHandlePtr.h"
+
 /*
  * This file is based on example code from:
  *
@@ -61,7 +61,7 @@
   cmBasicUVStreambuf* close();
 
 protected:
-  typename cmBasicUVStreambuf::int_type underflow() override;
+  typename cmBasicUVStreambuf<CharT, Traits>::int_type underflow() override;
   std::streamsize showmanyc() override;
 
   // FIXME: Add write support
diff --git a/Source/cmUnexpectedCommand.cxx b/Source/cmUnexpectedCommand.cxx
deleted file mode 100644
index a8de9e6..0000000
--- a/Source/cmUnexpectedCommand.cxx
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmUnexpectedCommand.h"
-
-#include <stdlib.h>
-
-#include "cmMakefile.h"
-
-class cmExecutionStatus;
-
-bool cmUnexpectedCommand::InitialPass(std::vector<std::string> const&,
-                                      cmExecutionStatus&)
-{
-  const char* versionValue =
-    this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION");
-  if (this->Name == "endif" && (!versionValue || atof(versionValue) <= 1.4)) {
-    return true;
-  }
-
-  this->SetError(this->Error);
-  return false;
-}
diff --git a/Source/cmUnexpectedCommand.h b/Source/cmUnexpectedCommand.h
deleted file mode 100644
index 33d6bdc..0000000
--- a/Source/cmUnexpectedCommand.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmUnexpectedCommand_h
-#define cmUnexpectedCommand_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "cmCommand.h"
-
-class cmExecutionStatus;
-
-class cmUnexpectedCommand : public cmCommand
-{
-public:
-  cmUnexpectedCommand(std::string name, const char* error)
-    : Name(std::move(name))
-    , Error(error)
-  {
-  }
-
-  cmCommand* Clone() override
-  {
-    return new cmUnexpectedCommand(this->Name, this->Error);
-  }
-
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  std::string Name;
-  const char* Error;
-};
-
-#endif
diff --git a/Source/cmUnsetCommand.cxx b/Source/cmUnsetCommand.cxx
index cfaa1fd2..3ba95e9 100644
--- a/Source/cmUnsetCommand.cxx
+++ b/Source/cmUnsetCommand.cxx
@@ -2,18 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmUnsetCommand.h"
 
-#include "cmAlgorithms.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmUnsetCommand
-bool cmUnsetCommand::InitialPass(std::vector<std::string> const& args,
-                                 cmExecutionStatus&)
+bool cmUnsetCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status)
 {
   if (args.empty() || args.size() > 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
@@ -24,27 +23,27 @@
     // what is the variable name
     auto const& envVarName = variable.substr(4, variable.size() - 5);
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
     cmSystemTools::UnsetEnv(envVarName.c_str());
 #endif
     return true;
   }
   // unset(VAR)
   if (args.size() == 1) {
-    this->Makefile->RemoveDefinition(variable);
+    status.GetMakefile().RemoveDefinition(variable);
     return true;
   }
   // unset(VAR CACHE)
   if ((args.size() == 2) && (args[1] == "CACHE")) {
-    this->Makefile->RemoveCacheDefinition(variable);
+    status.GetMakefile().RemoveCacheDefinition(variable);
     return true;
   }
   // unset(VAR PARENT_SCOPE)
   if ((args.size() == 2) && (args[1] == "PARENT_SCOPE")) {
-    this->Makefile->RaiseScope(variable, nullptr);
+    status.GetMakefile().RaiseScope(variable, nullptr);
     return true;
   }
   // ERROR: second argument isn't CACHE or PARENT_SCOPE
-  this->SetError("called with an invalid second argument");
+  status.SetError("called with an invalid second argument");
   return false;
 }
diff --git a/Source/cmUnsetCommand.h b/Source/cmUnsetCommand.h
index 4e1208a..be4c166 100644
--- a/Source/cmUnsetCommand.h
+++ b/Source/cmUnsetCommand.h
@@ -8,29 +8,14 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmUnsetCommand
+/**
  * \brief Unset a CMAKE variable
  *
  * cmUnsetCommand unsets or removes a variable.
  */
-class cmUnsetCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmUnsetCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmUnsetCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmUseMangledMesaCommand.cxx b/Source/cmUseMangledMesaCommand.cxx
index 4358194..1fd386b 100644
--- a/Source/cmUseMangledMesaCommand.cxx
+++ b/Source/cmUseMangledMesaCommand.cxx
@@ -5,29 +5,30 @@
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmExecutionStatus.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
+namespace {
+void CopyAndFullPathMesaHeader(const std::string& source,
+                               const std::string& outdir);
+}
 
-bool cmUseMangledMesaCommand::InitialPass(std::vector<std::string> const& args,
-                                          cmExecutionStatus&)
+bool cmUseMangledMesaCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status)
 {
   // expected two arguments:
   // argument one: the full path to gl_mangle.h
   // argument two : directory for output of edited headers
   if (args.size() != 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   const std::string& inputDir = args[0];
-  std::string glh = inputDir;
-  glh += "/";
-  glh += "gl.h";
+  std::string glh = cmStrCat(inputDir, "/gl.h");
   if (!cmSystemTools::FileExists(glh)) {
-    std::string e = "Bad path to Mesa, could not find: ";
-    e += glh;
-    e += " ";
-    this->SetError(e);
+    std::string e = cmStrCat("Bad path to Mesa, could not find: ", glh, ' ');
+    status.SetError(e);
     return false;
   }
   const std::string& destDir = args[1];
@@ -39,25 +40,22 @@
   }
   cmSystemTools::MakeDirectory(destDir);
   for (std::string const& f : files) {
-    std::string path = inputDir;
-    path += "/";
-    path += f;
-    this->CopyAndFullPathMesaHeader(path, destDir);
+    std::string path = cmStrCat(inputDir, '/', f);
+    CopyAndFullPathMesaHeader(path, destDir);
   }
 
   return true;
 }
 
-void cmUseMangledMesaCommand::CopyAndFullPathMesaHeader(
-  const std::string& source, const std::string& outdir)
+namespace {
+void CopyAndFullPathMesaHeader(const std::string& source,
+                               const std::string& outdir)
 {
-  std::string dir, file;
+  std::string dir;
+  std::string file;
   cmSystemTools::SplitProgramPath(source, dir, file);
-  std::string outFile = outdir;
-  outFile += "/";
-  outFile += file;
-  std::string tempOutputFile = outFile;
-  tempOutputFile += ".tmp";
+  std::string outFile = cmStrCat(outdir, '/', file);
+  std::string tempOutputFile = cmStrCat(outFile, ".tmp");
   cmsys::ofstream fout(tempOutputFile.c_str());
   if (!fout) {
     cmSystemTools::Error("Could not open file for write in copy operation: " +
@@ -100,6 +98,6 @@
   // close the files before attempting to copy
   fin.close();
   fout.close();
-  cmSystemTools::CopyFileIfDifferent(tempOutputFile, outFile);
-  cmSystemTools::RemoveFile(tempOutputFile);
+  cmSystemTools::MoveFileIfDifferent(tempOutputFile, outFile);
+}
 }
diff --git a/Source/cmUseMangledMesaCommand.h b/Source/cmUseMangledMesaCommand.h
index e2f1d9b..215e4a3 100644
--- a/Source/cmUseMangledMesaCommand.h
+++ b/Source/cmUseMangledMesaCommand.h
@@ -8,20 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmUseMangledMesaCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmUseMangledMesaCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-protected:
-  void CopyAndFullPathMesaHeader(const std::string& source,
-                                 const std::string& outdir);
-};
+bool cmUseMangledMesaCommand(std::vector<std::string> const& args,
+                             cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmUtilitySourceCommand.cxx b/Source/cmUtilitySourceCommand.cxx
index b59a587..a43165c 100644
--- a/Source/cmUtilitySourceCommand.cxx
+++ b/Source/cmUtilitySourceCommand.cxx
@@ -2,48 +2,48 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmUtilitySourceCommand.h"
 
-#include <string.h>
+#include <cstring>
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmUtilitySourceCommand
-bool cmUtilitySourceCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+bool cmUtilitySourceCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
-  std::vector<std::string>::const_iterator arg = args.begin();
+  auto arg = args.begin();
 
   // The first argument is the cache entry name.
   std::string const& cacheEntry = *arg++;
-  const char* cacheValue = this->Makefile->GetDefinition(cacheEntry);
+  const char* cacheValue = status.GetMakefile().GetDefinition(cacheEntry);
   // If it exists already and appears up to date then we are done.  If
   // the string contains "(IntDir)" but that is not the
   // CMAKE_CFG_INTDIR setting then the value is out of date.
   std::string const& intDir =
-    this->Makefile->GetRequiredDefinition("CMAKE_CFG_INTDIR");
+    status.GetMakefile().GetRequiredDefinition("CMAKE_CFG_INTDIR");
 
   bool haveCacheValue = false;
-  if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
+  if (status.GetMakefile().IsOn("CMAKE_CROSSCOMPILING")) {
     haveCacheValue = (cacheValue != nullptr);
     if (!haveCacheValue) {
-      std::string msg = "UTILITY_SOURCE is used in cross compiling mode for ";
-      msg += cacheEntry;
-      msg += ". If your intention is to run this executable, you need to "
-             "preload the cache with the full path to a version of that "
-             "program, which runs on this build machine.";
+      std::string msg = cmStrCat(
+        "UTILITY_SOURCE is used in cross compiling mode for ", cacheEntry,
+        ". If your intention is to run this executable, you need to "
+        "preload the cache with the full path to a version of that "
+        "program, which runs on this build machine.");
       cmSystemTools::Message(msg, "Warning");
     }
   } else {
-    cmState* state = this->Makefile->GetState();
+    cmState* state = status.GetMakefile().GetState();
     haveCacheValue = (cacheValue &&
                       (strstr(cacheValue, "(IntDir)") == nullptr ||
                        (intDir == "$(IntDir)")) &&
@@ -62,7 +62,7 @@
   // The third argument specifies the relative directory of the source
   // of the utility.
   std::string const& relativeSource = *arg++;
-  std::string utilitySource = this->Makefile->GetCurrentSourceDirectory();
+  std::string utilitySource = status.GetMakefile().GetCurrentSourceDirectory();
   utilitySource = utilitySource + "/" + relativeSource;
 
   // If the directory doesn't exist, the source has not been included.
@@ -80,11 +80,12 @@
 
   // The source exists.
   const std::string& cmakeCFGout =
-    this->Makefile->GetRequiredDefinition("CMAKE_CFG_INTDIR");
-  std::string utilityDirectory = this->Makefile->GetCurrentBinaryDirectory();
+    status.GetMakefile().GetRequiredDefinition("CMAKE_CFG_INTDIR");
+  std::string utilityDirectory =
+    status.GetMakefile().GetCurrentBinaryDirectory();
   std::string exePath;
-  if (this->Makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH")) {
-    exePath = this->Makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH");
+  if (status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH")) {
+    exePath = status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH");
   }
   if (!exePath.empty()) {
     utilityDirectory = exePath;
@@ -94,21 +95,22 @@
 
   // Construct the cache entry for the executable's location.
   std::string utilityExecutable = utilityDirectory + "/" + cmakeCFGout + "/" +
-    utilityName + this->Makefile->GetDefinition("CMAKE_EXECUTABLE_SUFFIX");
+    utilityName +
+    status.GetMakefile().GetDefinition("CMAKE_EXECUTABLE_SUFFIX");
 
   // make sure we remove any /./ in the name
   cmSystemTools::ReplaceString(utilityExecutable, "/./", "/");
 
   // Enter the value into the cache.
-  this->Makefile->AddCacheDefinition(cacheEntry, utilityExecutable.c_str(),
-                                     "Path to an internal program.",
-                                     cmStateEnums::FILEPATH);
+  status.GetMakefile().AddCacheDefinition(
+    cacheEntry, utilityExecutable.c_str(), "Path to an internal program.",
+    cmStateEnums::FILEPATH);
   // add a value into the cache that maps from the
   // full path to the name of the project
   cmSystemTools::ConvertToUnixSlashes(utilityExecutable);
-  this->Makefile->AddCacheDefinition(utilityExecutable, utilityName.c_str(),
-                                     "Executable to project name.",
-                                     cmStateEnums::INTERNAL);
+  status.GetMakefile().AddCacheDefinition(
+    utilityExecutable, utilityName.c_str(), "Executable to project name.",
+    cmStateEnums::INTERNAL);
 
   return true;
 }
diff --git a/Source/cmUtilitySourceCommand.h b/Source/cmUtilitySourceCommand.h
index 165ecef..934d539 100644
--- a/Source/cmUtilitySourceCommand.h
+++ b/Source/cmUtilitySourceCommand.h
@@ -8,16 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmUtilitySourceCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmUtilitySourceCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmUtilitySourceCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmUuid.cxx b/Source/cmUuid.cxx
index 51ecbd1..cd52b3f 100644
--- a/Source/cmUuid.cxx
+++ b/Source/cmUuid.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmUuid.h"
 
-#include "cmCryptoHash.h"
-
 #include <array>
-#include <string.h>
+#include <cstring>
+
+#include "cmCryptoHash.h"
 
 static const std::array<int, 5> kUuidGroups = { { 4, 2, 2, 2, 6 } };
 
@@ -53,7 +53,7 @@
 std::string cmUuid::FromDigest(const unsigned char* digest,
                                unsigned char version) const
 {
-  typedef unsigned char byte_t;
+  using byte_t = unsigned char;
 
   byte_t uuid[16] = { 0 };
   memcpy(uuid, digest, 16);
@@ -114,14 +114,12 @@
 
 std::string cmUuid::ByteToHex(unsigned char byte) const
 {
-  std::string result;
+  std::string result("  ");
   for (int i = 0; i < 2; ++i) {
     unsigned char rest = byte % 16;
     byte /= 16;
-
     char c = (rest < 0xA) ? char('0' + rest) : char('a' + (rest - 0xA));
-
-    result = c + result;
+    result.at(1 - i) = c;
   }
 
   return result;
diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx
index c78361e..dd9f058 100644
--- a/Source/cmVSSetupHelper.cxx
+++ b/Source/cmVSSetupHelper.cxx
@@ -2,10 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVSSetupHelper.h"
 
-#include "cmSystemTools.h"
 #include "cmsys/Encoding.hxx"
 #include "cmsys/FStream.hxx"
 
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
 #ifndef VSSetupConstants
 #  define VSSetupConstants
 /* clang-format off */
@@ -195,7 +197,7 @@
     if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
       return false;
     }
-    vcToolsVersion = cmSystemTools::TrimWhitespace(vcToolsVersion);
+    vcToolsVersion = cmTrimWhitespace(vcToolsVersion);
     std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
     if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
       return false;
@@ -364,8 +366,8 @@
         // We are not looking for a specific instance.
         // If we've been given a hint then use it.
         if (!envVSCommonToolsDir.empty()) {
-          std::string currentVSLocation = instanceInfo.GetInstallLocation();
-          currentVSLocation += "/Common7/Tools";
+          std::string currentVSLocation =
+            cmStrCat(instanceInfo.GetInstallLocation(), "/Common7/Tools");
           if (cmSystemTools::ComparePath(currentVSLocation,
                                          envVSCommonToolsDir)) {
             chosenInstanceInfo = instanceInfo;
diff --git a/Source/cmVSSetupHelper.h b/Source/cmVSSetupHelper.h
index 1bda54a..0980cef 100644
--- a/Source/cmVSSetupHelper.h
+++ b/Source/cmVSSetupHelper.h
@@ -8,13 +8,13 @@
 #endif
 
 // Published by Visual Studio Setup team
-#include "cmvssetup/Setup.Configuration.h"
-
 #include <string>
 #include <vector>
 
 #include <windows.h>
 
+#include "cmvssetup/Setup.Configuration.h"
+
 template <class T>
 class SmartCOMPtr
 {
@@ -74,26 +74,8 @@
 {
 public:
   SmartBSTR() { str = NULL; }
-  SmartBSTR(const SmartBSTR& src)
-  {
-    if (src.str != NULL) {
-      str = ::SysAllocStringByteLen((char*)str, ::SysStringByteLen(str));
-    } else {
-      str = ::SysAllocStringByteLen(NULL, 0);
-    }
-  }
-  SmartBSTR& operator=(const SmartBSTR& src)
-  {
-    if (str != src.str) {
-      ::SysFreeString(str);
-      if (src.str != NULL) {
-        str = ::SysAllocStringByteLen((char*)str, ::SysStringByteLen(str));
-      } else {
-        str = ::SysAllocStringByteLen(NULL, 0);
-      }
-    }
-    return *this;
-  }
+  SmartBSTR(const SmartBSTR& src) = delete;
+  SmartBSTR& operator=(const SmartBSTR& src) = delete;
   operator BSTR() const { return str; }
   BSTR* operator&() throw() { return &str; }
   ~SmartBSTR() throw() { ::SysFreeString(str); }
diff --git a/Source/cmVariableRequiresCommand.cxx b/Source/cmVariableRequiresCommand.cxx
index c02157a..6b93c63 100644
--- a/Source/cmVariableRequiresCommand.cxx
+++ b/Source/cmVariableRequiresCommand.cxx
@@ -2,32 +2,32 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVariableRequiresCommand.h"
 
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmState.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-class cmExecutionStatus;
-
 // cmLibraryCommand
-bool cmVariableRequiresCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus&)
+bool cmVariableRequiresCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status)
 {
   if (args.size() < 3) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   std::string const& testVariable = args[0];
-  if (!this->Makefile->IsOn(testVariable)) {
+  if (!status.GetMakefile().IsOn(testVariable)) {
     return true;
   }
   std::string const& resultVariable = args[1];
   bool requirementsMet = true;
   std::string notSet;
   bool hasAdvanced = false;
-  cmState* state = this->Makefile->GetState();
+  cmState* state = status.GetMakefile().GetState();
   for (unsigned int i = 2; i < args.size(); ++i) {
-    if (!this->Makefile->IsOn(args[i])) {
+    if (!status.GetMakefile().IsOn(args[i])) {
       requirementsMet = false;
       notSet += args[i];
       notSet += "\n";
@@ -37,21 +37,20 @@
       }
     }
   }
-  const char* reqVar = this->Makefile->GetDefinition(resultVariable);
+  const char* reqVar = status.GetMakefile().GetDefinition(resultVariable);
   // if reqVar is unset, then set it to requirementsMet
   // if reqVar is set to true, but requirementsMet is false , then
   // set reqVar to false.
-  if (!reqVar || (!requirementsMet && this->Makefile->IsOn(reqVar))) {
-    this->Makefile->AddDefinition(resultVariable, requirementsMet);
+  if (!reqVar || (!requirementsMet && status.GetMakefile().IsOn(reqVar))) {
+    status.GetMakefile().AddDefinitionBool(resultVariable, requirementsMet);
   }
 
   if (!requirementsMet) {
-    std::string message = "Variable assertion failed:\n";
-    message +=
-      testVariable + " Requires that the following unset variables are set:\n";
-    message += notSet;
-    message += "\nPlease set them, or set ";
-    message += testVariable + " to false, and re-configure.\n";
+    std::string message =
+      cmStrCat("Variable assertion failed:\n", testVariable,
+               " Requires that the following unset variables are set:\n",
+               notSet, "\nPlease set them, or set ", testVariable,
+               " to false, and re-configure.\n");
     if (hasAdvanced) {
       message +=
         "One or more of the required variables is advanced."
diff --git a/Source/cmVariableRequiresCommand.h b/Source/cmVariableRequiresCommand.h
index 94970c5..fb0520e 100644
--- a/Source/cmVariableRequiresCommand.h
+++ b/Source/cmVariableRequiresCommand.h
@@ -8,16 +8,9 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-class cmVariableRequiresCommand : public cmCommand
-{
-public:
-  cmCommand* Clone() override { return new cmVariableRequiresCommand; }
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmVariableRequiresCommand(std::vector<std::string> const& args,
+                               cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmVariableWatch.cxx b/Source/cmVariableWatch.cxx
index 3df1420..4995da9 100644
--- a/Source/cmVariableWatch.cxx
+++ b/Source/cmVariableWatch.cxx
@@ -66,8 +66,7 @@
                                        int access_type, const char* newValue,
                                        const cmMakefile* mf) const
 {
-  cmVariableWatch::StringToVectorOfPairs::const_iterator mit =
-    this->WatchMap.find(variable);
+  auto mit = this->WatchMap.find(variable);
   if (mit != this->WatchMap.end()) {
     // The strategy here is to copy the list of callbacks, and ignore
     // new callbacks that existing ones may add.
diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h
index 1230101..e4b3b7c 100644
--- a/Source/cmVariableWatch.h
+++ b/Source/cmVariableWatch.h
@@ -6,7 +6,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -20,10 +20,9 @@
 class cmVariableWatch
 {
 public:
-  typedef void (*WatchMethod)(const std::string& variable, int access_type,
-                              void* client_data, const char* newValue,
-                              const cmMakefile* mf);
-  typedef void (*DeleteData)(void* client_data);
+  using WatchMethod = void (*)(const std::string&, int, void*, const char*,
+                               const cmMakefile*);
+  using DeleteData = void (*)(void*);
 
   cmVariableWatch();
   ~cmVariableWatch();
@@ -77,8 +76,8 @@
     Pair& operator=(const Pair&) = delete;
   };
 
-  typedef std::vector<std::shared_ptr<Pair>> VectorOfPairs;
-  typedef std::map<std::string, VectorOfPairs> StringToVectorOfPairs;
+  using VectorOfPairs = std::vector<std::shared_ptr<Pair>>;
+  using StringToVectorOfPairs = std::map<std::string, VectorOfPairs>;
 
   StringToVectorOfPairs WatchMap;
 };
diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx
index 5fe55bd..f2c8f3c 100644
--- a/Source/cmVariableWatchCommand.cxx
+++ b/Source/cmVariableWatchCommand.cxx
@@ -2,12 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVariableWatchCommand.h"
 
-#include <sstream>
+#include <memory>
+#include <utility>
 
 #include "cmExecutionStatus.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVariableWatch.h"
 #include "cmake.h"
@@ -54,24 +56,22 @@
     newLFF.Arguments.emplace_back(stack, cmListFileArgument::Quoted, 9999);
     newLFF.Name = data->Command;
     newLFF.Line = 9999;
-    cmExecutionStatus status;
+    cmExecutionStatus status(*makefile);
     if (!makefile->ExecuteCommand(newLFF, status)) {
-      std::ostringstream error;
-      error << "Error in cmake code at\nUnknown:0:\n"
-            << "A command failed during the invocation of callback \""
-            << data->Command << "\".";
-      cmSystemTools::Error(error.str());
+      cmSystemTools::Error(
+        cmStrCat("Error in cmake code at\nUnknown:0:\nA command failed "
+                 "during the invocation of callback \"",
+                 data->Command, "\"."));
       data->InCallback = false;
       return;
     }
     processed = true;
   }
   if (!processed) {
-    std::ostringstream msg;
-    msg << "Variable \"" << variable << "\" was accessed using "
-        << accessString << " with value \"" << (newValue ? newValue : "")
-        << "\".";
-    makefile->IssueMessage(MessageType::LOG, msg.str());
+    makefile->IssueMessage(
+      MessageType::LOG,
+      cmStrCat("Variable \"", variable, "\" was accessed using ", accessString,
+               " with value \"", (newValue ? newValue : ""), "\"."));
   }
 
   data->InCallback = false;
@@ -84,21 +84,46 @@
   delete data;
 }
 
-cmVariableWatchCommand::cmVariableWatchCommand() = default;
-
-cmVariableWatchCommand::~cmVariableWatchCommand()
+/** This command does not really have a final pass but it needs to
+    stay alive since it owns variable watch callback information. */
+class FinalAction
 {
-  for (std::string const& wv : this->WatchedVariables) {
-    this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
-      wv, cmVariableWatchCommandVariableAccessed);
+public:
+  /* NOLINTNEXTLINE(performance-unnecessary-value-param) */
+  FinalAction(cmMakefile* makefile, std::string variable)
+    : Action(std::make_shared<Impl>(makefile, std::move(variable)))
+  {
   }
-}
 
-bool cmVariableWatchCommand::InitialPass(std::vector<std::string> const& args,
-                                         cmExecutionStatus&)
+  void operator()(cmMakefile&) const {}
+
+private:
+  struct Impl
+  {
+    Impl(cmMakefile* makefile, std::string variable)
+      : Makefile(makefile)
+      , Variable(std::move(variable))
+    {
+    }
+
+    ~Impl()
+    {
+      this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
+        this->Variable, cmVariableWatchCommandVariableAccessed);
+    }
+
+    cmMakefile* Makefile;
+    std::string Variable;
+  };
+
+  std::shared_ptr<Impl const> Action;
+};
+
+bool cmVariableWatchCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("must be called with at least one argument.");
+    status.SetError("must be called with at least one argument.");
     return false;
   }
   std::string const& variable = args[0];
@@ -107,9 +132,7 @@
     command = args[1];
   }
   if (variable == "CMAKE_CURRENT_LIST_FILE") {
-    std::ostringstream ostr;
-    ostr << "cannot be set on the variable: " << variable;
-    this->SetError(ostr.str());
+    status.SetError(cmStrCat("cannot be set on the variable: ", variable));
     return false;
   }
 
@@ -118,13 +141,14 @@
   data->InCallback = false;
   data->Command = command;
 
-  this->WatchedVariables.insert(variable);
-  if (!this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch(
+  if (!status.GetMakefile().GetCMakeInstance()->GetVariableWatch()->AddWatch(
         variable, cmVariableWatchCommandVariableAccessed, data,
         deleteVariableWatchCallbackData)) {
     deleteVariableWatchCallbackData(data);
     return false;
   }
 
+  status.GetMakefile().AddFinalAction(
+    FinalAction(&status.GetMakefile(), variable));
   return true;
 }
diff --git a/Source/cmVariableWatchCommand.h b/Source/cmVariableWatchCommand.h
index 6a8115d..3f9f244 100644
--- a/Source/cmVariableWatchCommand.h
+++ b/Source/cmVariableWatchCommand.h
@@ -5,45 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <set>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmVariableWatchCommand
+/**
  * \brief Watch when the variable changes and invoke command
  *
  */
-class cmVariableWatchCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmVariableWatchCommand; }
-
-  //! Default constructor
-  cmVariableWatchCommand();
-
-  //! Destructor.
-  ~cmVariableWatchCommand() override;
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-  /** This command does not really have a final pass but it needs to
-      stay alive since it owns variable watch callback information. */
-  bool HasFinalPass() const override { return true; }
-
-protected:
-  std::set<std::string> WatchedVariables;
-};
+bool cmVariableWatchCommand(std::vector<std::string> const& args,
+                            cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 7a90176..3843bf2 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -2,6 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVisualStudio10TargetGenerator.h"
 
+#include <iterator>
+#include <set>
+
+#include <cm/memory>
+
+#include "windows.h"
+
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
@@ -16,11 +23,6 @@
 #include "cmSourceFile.h"
 #include "cmSystemTools.h"
 #include "cmVisualStudioGeneratorOptions.h"
-#include "windows.h"
-
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <set>
 
 static void ConvertToWindowsSlash(std::string& s);
 
@@ -120,7 +122,7 @@
 class cmVS10GeneratorOptions : public cmVisualStudioGeneratorOptions
 {
 public:
-  typedef cmVisualStudio10TargetGenerator::Elem Elem;
+  using Elem = cmVisualStudio10TargetGenerator::Elem;
   cmVS10GeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool,
                          cmVS7FlagTable const* table,
                          cmVisualStudio10TargetGenerator* g = nullptr)
@@ -363,10 +365,9 @@
       return;
     }
   }
-  std::string path = this->LocalGenerator->GetCurrentBinaryDirectory();
-  path += "/";
-  path += this->Name;
-  path += ProjectFileExtension;
+  std::string path =
+    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             this->Name, ProjectFileExtension);
   cmGeneratedFileStream BuildFileStream(path);
   const std::string PathToProjectFile = path;
   BuildFileStream.SetCopyIfDifferent(true);
@@ -505,6 +506,11 @@
         if (targetFrameworkVersion) {
           e1.Element("TargetFrameworkVersion", targetFrameworkVersion);
         }
+        if (this->ProjectType == vcxproj &&
+            this->GlobalGenerator->TargetsWindowsCE()) {
+          e1.Element("EnableRedirectPlatform", "true");
+          e1.Element("RedirectPlatformValue", this->Platform);
+        }
         if (this->ProjectType == csproj &&
             this->GlobalGenerator->TargetsWindowsCE()) {
           const char* targetFrameworkId = this->GeneratorTarget->GetProperty(
@@ -520,6 +526,13 @@
           }
           e1.Element("TargetFrameworkTargetsVersion", targetFrameworkVer);
         }
+        if (!this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString()
+               .empty()) {
+          e1.Element(
+            "CudaToolkitCustomDir",
+            this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString() +
+              "nvcc");
+        }
       }
 
       // Disable the project upgrade prompt that is displayed the first time a
@@ -606,10 +619,17 @@
       e1.SetHasElements();
 
       if (this->GlobalGenerator->IsCudaEnabled()) {
+        auto customDir =
+          this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
+        std::string cudaPath = customDir.empty()
+          ? "$(VCTargetsPath)\\BuildCustomizations\\"
+          : customDir +
+            "CUDAVisualStudioIntegration\\extras\\"
+            "visual_studio_integration\\MSBuildExtensions\\";
         Elem(e1, "Import")
           .Attribute("Project",
-                     "$(VCTargetsPath)\\BuildCustomizations\\CUDA " +
-                       this->GlobalGenerator->GetPlatformToolsetCudaString() +
+                     std::move(cudaPath) + "CUDA " +
+                       this->GlobalGenerator->GetPlatformToolsetCuda() +
                        ".props");
       }
       if (this->GlobalGenerator->IsMasmEnabled()) {
@@ -622,9 +642,8 @@
         std::string propsTemplate =
           GetCMakeFilePath("Templates/MSBuild/nasm.props.in");
 
-        std::string propsLocal;
-        propsLocal += this->DefaultArtifactDir;
-        propsLocal += "\\nasm.props";
+        std::string propsLocal =
+          cmStrCat(this->DefaultArtifactDir, "\\nasm.props");
         ConvertToWindowsSlash(propsLocal);
         this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true,
                                       true);
@@ -692,10 +711,17 @@
       e1.SetHasElements();
       this->WriteTargetsFileReferences(e1);
       if (this->GlobalGenerator->IsCudaEnabled()) {
+        auto customDir =
+          this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
+        std::string cudaPath = customDir.empty()
+          ? "$(VCTargetsPath)\\BuildCustomizations\\"
+          : customDir +
+            "CUDAVisualStudioIntegration\\extras\\"
+            "visual_studio_integration\\MSBuildExtensions\\";
         Elem(e1, "Import")
           .Attribute("Project",
-                     "$(VCTargetsPath)\\BuildCustomizations\\CUDA " +
-                       this->GlobalGenerator->GetPlatformToolsetCudaString() +
+                     std::move(cudaPath) + "CUDA " +
+                       this->GlobalGenerator->GetPlatformToolsetCuda() +
                        ".targets");
       }
       if (this->GlobalGenerator->IsMasmEnabled()) {
@@ -744,7 +770,7 @@
   std::vector<std::string> packageReferences;
   if (const char* vsPackageReferences =
         this->GeneratorTarget->GetProperty("VS_PACKAGE_REFERENCES")) {
-    cmSystemTools::ExpandListArgument(vsPackageReferences, packageReferences);
+    cmExpandList(vsPackageReferences, packageReferences);
   }
   if (!packageReferences.empty()) {
     Elem e1(e0, "ItemGroup");
@@ -771,14 +797,14 @@
   std::vector<std::string> references;
   if (const char* vsDotNetReferences =
         this->GeneratorTarget->GetProperty("VS_DOTNET_REFERENCES")) {
-    cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
+    cmExpandList(vsDotNetReferences, references);
   }
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (auto const& i : props) {
+  for (auto const& i : props.GetList()) {
     if (i.first.find("VS_DOTNET_REFERENCE_") == 0) {
       std::string name = i.first.substr(20);
       if (!name.empty()) {
-        std::string path = i.second.GetValue();
+        std::string path = i.second;
         if (!cmsys::SystemTools::FileIsFullPath(path)) {
           path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
         }
@@ -832,7 +858,7 @@
     const char* privateReference = "True";
     if (const char* value = this->GeneratorTarget->GetProperty(
           "VS_DOTNET_REFERENCES_COPY_LOCAL")) {
-      if (cmSystemTools::IsOff(value)) {
+      if (cmIsOff(value)) {
         privateReference = "False";
       }
     }
@@ -847,8 +873,8 @@
   const char* imports =
     this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT");
   if (imports) {
-    std::vector<std::string> argsSplit;
-    cmSystemTools::ExpandListArgument(std::string(imports), argsSplit, false);
+    std::vector<std::string> argsSplit =
+      cmExpandedList(std::string(imports), false);
     for (auto& path : argsSplit) {
       if (!cmsys::SystemTools::FileIsFullPath(path)) {
         path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
@@ -867,13 +893,13 @@
   static const std::string refpropPrefix = "VS_DOTNET_REFERENCEPROP_";
   static const std::string refpropInfix = "_TAG_";
   const std::string refPropFullPrefix = refpropPrefix + ref + refpropInfix;
-  typedef std::map<std::string, std::string> CustomTags;
+  using CustomTags = std::map<std::string, std::string>;
   CustomTags tags;
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (const auto& i : props) {
+  for (const auto& i : props.GetList()) {
     if (i.first.find(refPropFullPrefix) == 0) {
       std::string refTag = i.first.substr(refPropFullPrefix.length());
-      std::string refVal = i.second.GetValue();
+      std::string refVal = i.second;
       if (!refTag.empty() && !refVal.empty()) {
         tags[refTag] = refVal;
       }
@@ -967,12 +993,12 @@
           }
         }
         const cmPropertyMap& props = oi->GetProperties();
-        for (const auto& p : props) {
+        for (const std::string& p : props.GetKeys()) {
           static const std::string propNamePrefix = "VS_CSHARP_";
-          if (p.first.find(propNamePrefix) == 0) {
-            std::string tagName = p.first.substr(propNamePrefix.length());
+          if (p.find(propNamePrefix) == 0) {
+            std::string tagName = p.substr(propNamePrefix.length());
             if (!tagName.empty()) {
-              std::string value = props.GetPropertyValue(p.first);
+              std::string value = props.GetPropertyValue(p);
               if (!value.empty()) {
                 e2.Element(tagName.c_str(), value);
               }
@@ -1068,7 +1094,7 @@
   std::vector<std::string> references;
   if (const char* vsWinRTReferences =
         this->GeneratorTarget->GetProperty("VS_WINRT_REFERENCES")) {
-    cmSystemTools::ExpandListArgument(vsWinRTReferences, references);
+    cmExpandList(vsWinRTReferences, references);
   }
 
   if (this->GlobalGenerator->TargetsWindowsPhone() &&
@@ -1111,7 +1137,8 @@
       std::string configType;
       if (const char* vsConfigurationType =
             this->GeneratorTarget->GetProperty("VS_CONFIGURATION_TYPE")) {
-        configType = vsConfigurationType;
+        configType = cmGeneratorExpression::Evaluate(vsConfigurationType,
+                                                     this->LocalGenerator, c);
       } else {
         switch (this->GeneratorTarget->GetType()) {
           case cmStateEnums::SHARED_LIBRARY:
@@ -1266,8 +1293,8 @@
     e1.Element("PlatformToolset", toolset);
   }
 
-  std::string postfixName = cmSystemTools::UpperCase(config);
-  postfixName += "_POSTFIX";
+  std::string postfixName =
+    cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
   std::string assemblyName = this->GeneratorTarget->GetOutputName(
     config, cmStateEnums::RuntimeBinaryArtifact);
   if (const char* postfix = this->GeneratorTarget->GetProperty(postfixName)) {
@@ -1372,9 +1399,8 @@
         // preventing dependent rebuilds.
         this->ForceOld(sourcePath);
       } else {
-        std::string error = "Could not create file: [";
-        error += sourcePath;
-        error += "]  ";
+        std::string error =
+          cmStrCat("Could not create file: [", sourcePath, "]  ");
         cmSystemTools::Error(error + cmSystemTools::GetLastSystemError());
       }
     }
@@ -1557,11 +1583,9 @@
   this->AddMissingSourceGroups(groupsUsed, sourceGroups);
 
   // Write out group file
-  std::string path = this->LocalGenerator->GetCurrentBinaryDirectory();
-  path += "/";
-  path += this->Name;
-  path += computeProjectFileExtension(this->GeneratorTarget);
-  path += ".filters";
+  std::string path = cmStrCat(
+    this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name,
+    computeProjectFileExtension(this->GeneratorTarget), ".filters");
   cmGeneratedFileStream fout(path);
   fout.SetCopyIfDifferent(true);
   char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
@@ -1956,11 +1980,11 @@
         const std::string& enableDebug =
           cge->Evaluate(this->LocalGenerator, this->Configurations[i]);
         if (!enableDebug.empty()) {
-          e2.WritePlatformConfigTag(
-            "EnableDebuggingInformation",
-            "'$(Configuration)|$(Platform)'=='" + this->Configurations[i] +
-              "|" + this->Platform + "'",
-            cmSystemTools::IsOn(enableDebug) ? "true" : "false");
+          e2.WritePlatformConfigTag("EnableDebuggingInformation",
+                                    "'$(Configuration)|$(Platform)'=='" +
+                                      this->Configurations[i] + "|" +
+                                      this->Platform + "'",
+                                    cmIsOn(enableDebug) ? "true" : "false");
         }
       }
     }
@@ -1977,7 +2001,7 @@
             "DisableOptimizations",
             "'$(Configuration)|$(Platform)'=='" + this->Configurations[i] +
               "|" + this->Platform + "'",
-            (cmSystemTools::IsOn(disableOptimizations) ? "true" : "false"));
+            (cmIsOn(disableOptimizations) ? "true" : "false"));
         }
       }
     }
@@ -2057,6 +2081,17 @@
   if (this->GeneratorTarget->GetType() > cmStateEnums::UTILITY) {
     return;
   }
+
+  const bool haveUnityBuild =
+    this->GeneratorTarget->GetPropertyAsBool("UNITY_BUILD");
+
+  if (haveUnityBuild &&
+      this->GlobalGenerator->GetVersion() >=
+        cmGlobalVisualStudioGenerator::VS15) {
+    Elem e1(e0, "PropertyGroup");
+    e1.Element("EnableUnitySupport", "true");
+  }
+
   Elem e1(e0, "ItemGroup");
   e1.SetHasElements();
 
@@ -2155,9 +2190,51 @@
 
       Elem e2(e1, tool);
       this->WriteSource(e2, si.Source);
+
+      bool useNativeUnityBuild = false;
+      if (haveUnityBuild &&
+          this->GlobalGenerator->GetVersion() >=
+            cmGlobalVisualStudioGenerator::VS15) {
+        // Magic value taken from cmGlobalVisualStudioVersionedGenerator.cxx
+        static const std::string vs15 = "141";
+        std::string toolset =
+          this->GlobalGenerator->GetPlatformToolsetString();
+        cmSystemTools::ReplaceString(toolset, "v", "");
+
+        if (toolset.empty() ||
+            cmSystemTools::VersionCompareGreaterEq(toolset, vs15)) {
+          useNativeUnityBuild = true;
+        }
+      }
+
+      if (haveUnityBuild && strcmp(tool, "ClCompile") == 0 &&
+          si.Source->GetProperty("UNITY_SOURCE_FILE")) {
+        if (useNativeUnityBuild) {
+          e2.Attribute(
+            "IncludeInUnityFile",
+            si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")
+              ? "false"
+              : "true");
+          e2.Attribute("CustomUnityFile", "true");
+
+          std::string unityDir = cmSystemTools::GetFilenamePath(
+            si.Source->GetProperty("UNITY_SOURCE_FILE"));
+          e2.Attribute("UnityFilesDirectory", unityDir);
+        } else {
+          // Visual Studio versions prior to 2017 do not know about unity
+          // builds, thus we exclude the files alredy part of unity sources.
+          if (!si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) {
+            exclude_configs = si.Configs;
+          }
+        }
+      }
+
       if (si.Kind == cmGeneratorTarget::SourceKindObjectSource) {
         this->OutputSourceSpecificFlags(e2, si.Source);
       }
+      if (si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+        e2.Element("PrecompiledHeader", "NotUsing");
+      }
       if (!exclude_configs.empty()) {
         this->WriteExcludeFromBuild(e2, exclude_configs);
       }
@@ -2242,8 +2319,7 @@
   for (std::string const& config : this->Configurations) {
     std::string configUpper = cmSystemTools::UpperCase(config);
     std::string configDefines = defines;
-    std::string defPropName = "COMPILE_DEFINITIONS_";
-    defPropName += configUpper;
+    std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
     if (const char* ccdefs = sf.GetProperty(defPropName)) {
       if (!configDefines.empty()) {
         configDefines += ";";
@@ -2252,10 +2328,28 @@
         cmGeneratorExpression::Find(ccdefs) != std::string::npos;
       configDefines += ccdefs;
     }
+
+    // Add precompile headers compile options.
+    std::string customAndPchOptions = options;
+    const std::string pchSource =
+      this->GeneratorTarget->GetPchSource(config, lang);
+    if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+      std::string pchOptions;
+      if (sf.GetFullPath() == pchSource) {
+        pchOptions =
+          this->GeneratorTarget->GetPchCreateCompileOptions(config, lang);
+      } else {
+        pchOptions =
+          this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
+      }
+      customAndPchOptions += pchOptions;
+    }
+
     // if we have flags or defines for this config then
     // use them
     if (!flags.empty() || !options.empty() || !configDefines.empty() ||
-        !includes.empty() || compileAs || noWinRT) {
+        !includes.empty() || compileAs || noWinRT ||
+        !customAndPchOptions.empty()) {
       cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
       cmIDEFlagTable const* flagtable = nullptr;
       const std::string& srclang = source->GetLanguage();
@@ -2288,14 +2382,15 @@
       } else {
         clOptions.Parse(flags);
       }
-      if (!options.empty()) {
+      if (!customAndPchOptions.empty()) {
         std::string expandedOptions;
         if (configDependentOptions) {
           this->LocalGenerator->AppendCompileOptions(
             expandedOptions,
-            genexInterpreter.Evaluate(options, "COMPILE_OPTIONS"));
+            genexInterpreter.Evaluate(customAndPchOptions, "COMPILE_OPTIONS"));
         } else {
-          this->LocalGenerator->AppendCompileOptions(expandedOptions, options);
+          this->LocalGenerator->AppendCompileOptions(expandedOptions,
+                                                     customAndPchOptions);
         }
         clOptions.Parse(expandedOptions);
       }
@@ -2334,7 +2429,7 @@
   }
   if (this->ProjectType == csproj) {
     std::string f = source->GetFullPath();
-    typedef std::map<std::string, std::string> CsPropMap;
+    using CsPropMap = std::map<std::string, std::string>;
     CsPropMap sourceFileTags;
     // set <Link> tag if necessary
     std::string link;
@@ -2381,49 +2476,32 @@
     if (ttype <= cmStateEnums::UTILITY) {
       if (const char* workingDir = this->GeneratorTarget->GetProperty(
             "VS_DEBUGGER_WORKING_DIRECTORY")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(workingDir);
-        std::string genWorkingDir =
-          cge->Evaluate(this->LocalGenerator, config);
-
+        std::string genWorkingDir = cmGeneratorExpression::Evaluate(
+          workingDir, this->LocalGenerator, config);
         e1.WritePlatformConfigTag("LocalDebuggerWorkingDirectory", cond,
                                   genWorkingDir);
       }
 
       if (const char* environment =
             this->GeneratorTarget->GetProperty("VS_DEBUGGER_ENVIRONMENT")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(environment);
-        std::string genEnvironment =
-          cge->Evaluate(this->LocalGenerator, config);
-
+        std::string genEnvironment = cmGeneratorExpression::Evaluate(
+          environment, this->LocalGenerator, config);
         e1.WritePlatformConfigTag("LocalDebuggerEnvironment", cond,
                                   genEnvironment);
       }
 
       if (const char* debuggerCommand =
             this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) {
-
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(debuggerCommand);
-        std::string genDebuggerCommand =
-          cge->Evaluate(this->LocalGenerator, config);
-
+        std::string genDebuggerCommand = cmGeneratorExpression::Evaluate(
+          debuggerCommand, this->LocalGenerator, config);
         e1.WritePlatformConfigTag("LocalDebuggerCommand", cond,
                                   genDebuggerCommand);
       }
 
       if (const char* commandArguments = this->GeneratorTarget->GetProperty(
             "VS_DEBUGGER_COMMAND_ARGUMENTS")) {
-        cmGeneratorExpression ge;
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(commandArguments);
-        std::string genCommandArguments =
-          cge->Evaluate(this->LocalGenerator, config);
-
+        std::string genCommandArguments = cmGeneratorExpression::Evaluate(
+          commandArguments, this->LocalGenerator, config);
         e1.WritePlatformConfigTag("LocalDebuggerCommandArguments", cond,
                                   genCommandArguments);
       }
@@ -2433,17 +2511,14 @@
       e1.WritePlatformConfigTag(
         "IntDir", cond, "$(Platform)\\$(Configuration)\\$(ProjectName)\\");
     } else {
-      std::string intermediateDir =
-        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
-      intermediateDir += "/";
-      intermediateDir += config;
-      intermediateDir += "/";
+      std::string intermediateDir = cmStrCat(
+        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
+        config, '/');
       std::string outDir;
       std::string targetNameFull;
       if (ttype == cmStateEnums::OBJECT_LIBRARY) {
         outDir = intermediateDir;
-        targetNameFull = this->GeneratorTarget->GetName();
-        targetNameFull += ".lib";
+        targetNameFull = cmStrCat(this->GeneratorTarget->GetName(), ".lib");
       } else {
         outDir = this->GeneratorTarget->GetDirectory(config) + "/";
         targetNameFull = this->GeneratorTarget->GetFullName(config);
@@ -2611,8 +2686,7 @@
   std::string langForClCompile;
   if (this->ProjectType == csproj) {
     langForClCompile = "CSharp";
-  } else if (std::find(cm::cbegin(clLangs), cm::cend(clLangs), linkLanguage) !=
-             cm::cend(clLangs)) {
+  } else if (cmContains(clLangs, linkLanguage)) {
     langForClCompile = linkLanguage;
   } else {
     std::set<std::string> languages;
@@ -2644,6 +2718,13 @@
     this->IPOEnabledConfigurations.insert(configName);
   }
 
+  // Precompile Headers
+  std::string pchHeader =
+    this->GeneratorTarget->GetPchHeader(configName, linkLanguage);
+  if (this->MSTools && vcxproj == this->ProjectType && pchHeader.empty()) {
+    clOptions.AddFlag("PrecompiledHeader", "NotUsing");
+  }
+
   // Get preprocessor definitions for this directory.
   std::string defineFlags = this->Makefile->GetDefineFlags();
   if (this->MSTools) {
@@ -2659,7 +2740,6 @@
         // replace this setting with "true" below.
         clOptions.AddFlag("UseFullPaths", "false");
       }
-      clOptions.AddFlag("PrecompiledHeader", "NotUsing");
       clOptions.AddFlag("AssemblerListingLocation", "$(IntDir)");
     }
   }
@@ -2724,9 +2804,7 @@
   }
 
   // Add a definition for the configuration name.
-  std::string configDefine = "CMAKE_INTDIR=\"";
-  configDefine += configName;
-  configDefine += "\"";
+  std::string configDefine = cmStrCat("CMAKE_INTDIR=\"", configName, '"');
   clOptions.AddDefine(configDefine);
   if (const std::string* exportMacro =
         this->GeneratorTarget->GetExportMacro()) {
@@ -2751,7 +2829,7 @@
       }
     }
     if (const char* winRT = clOptions.GetFlag("CompileAsWinRT")) {
-      if (cmSystemTools::IsOn(winRT)) {
+      if (cmIsOn(winRT)) {
         this->TargetCompileAsWinRT = true;
       }
     }
@@ -3021,9 +3099,7 @@
   cudaOptions.AddDefines(targetDefines);
 
   // Add a definition for the configuration name.
-  std::string configDefine = "CMAKE_INTDIR=\"";
-  configDefine += configName;
-  configDefine += "\"";
+  std::string configDefine = cmStrCat("CMAKE_INTDIR=\"", configName, '"');
   cudaOptions.AddDefine(configDefine);
   if (const std::string* exportMacro =
         this->GeneratorTarget->GetExportMacro()) {
@@ -3088,6 +3164,82 @@
                                      "-Wno-deprecated-gpu-targets");
   }
 
+  // For static libraries that have device linking enabled compute
+  // the  libraries
+  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY &&
+      doDeviceLinking) {
+    cmComputeLinkInformation* pcli =
+      this->GeneratorTarget->GetLinkInformation(configName);
+    if (!pcli) {
+      cmSystemTools::Error(
+        "CMake can not compute cmComputeLinkInformation for target: " +
+        this->Name);
+      return false;
+    }
+
+    // Would like to use:
+    // cmLinkLineDeviceComputer computer(this->LocalGenerator,
+    //                                   this->LocalGenerator->GetStateSnapshot().GetDirectory());
+    // std::string computed_libs = computer.ComputeLinkLibraries(cli,
+    // std::string{}); but it outputs in "<libA> <libB>" format instead of
+    // "<libA>;<libB>"
+    // Note:
+    // Any modification of this algorithm should be reflected also in
+    // cmLinkLineDeviceComputer
+    cmComputeLinkInformation& cli = *pcli;
+    std::vector<std::string> libVec;
+    const std::string currentBinDir =
+      this->LocalGenerator->GetCurrentBinaryDirectory();
+    const auto& libs = cli.GetItems();
+    for (cmComputeLinkInformation::Item const& l : libs) {
+
+      if (l.Target) {
+        auto managedType = l.Target->GetManagedType(configName);
+        // Do not allow C# targets to be added to the LIB listing. LIB files
+        // are used for linking C++ dependencies. C# libraries do not have lib
+        // files. Instead, they compile down to C# reference libraries (DLL
+        // files). The
+        // `<ProjectReference>` elements added to the vcxproj are enough for
+        // the IDE to deduce the DLL file required by other C# projects that
+        // need its reference library.
+        if (managedType == cmGeneratorTarget::ManagedType::Managed) {
+          continue;
+        }
+        const auto type = l.Target->GetType();
+
+        bool skip = false;
+        switch (type) {
+          case cmStateEnums::SHARED_LIBRARY:
+          case cmStateEnums::MODULE_LIBRARY:
+          case cmStateEnums::INTERFACE_LIBRARY:
+            skip = true;
+            break;
+          case cmStateEnums::STATIC_LIBRARY:
+            skip = l.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
+            break;
+          default:
+            break;
+        }
+        if (skip) {
+          continue;
+        }
+      }
+
+      if (l.IsPath) {
+        std::string path = this->LocalGenerator->MaybeConvertToRelativePath(
+          currentBinDir, l.Value);
+        ConvertToWindowsSlash(path);
+        if (!cmVS10IsTargetsFile(l.Value)) {
+          libVec.push_back(path);
+        }
+      } else {
+        libVec.push_back(l.Value);
+      }
+    }
+
+    cudaLinkOptions.AddFlag("AdditionalDependencies", libVec);
+  }
+
   this->CudaLinkOptions[configName] = std::move(pOptions);
   return true;
 }
@@ -3264,15 +3416,32 @@
 
   std::vector<cmSourceFile const*> manifest_srcs;
   this->GeneratorTarget->GetManifests(manifest_srcs, config);
-  if (!manifest_srcs.empty()) {
-    std::ostringstream oss;
-    for (cmSourceFile const* mi : manifest_srcs) {
-      std::string m = this->ConvertPath(mi->GetFullPath(), false);
-      ConvertToWindowsSlash(m);
-      oss << m << ";";
-    }
+
+  const char* dpiAware = this->GeneratorTarget->GetProperty("VS_DPI_AWARE");
+
+  if (!manifest_srcs.empty() || dpiAware) {
     Elem e2(e1, "Manifest");
-    e2.Element("AdditionalManifestFiles", oss.str());
+    if (!manifest_srcs.empty()) {
+      std::ostringstream oss;
+      for (cmSourceFile const* mi : manifest_srcs) {
+        std::string m = this->ConvertPath(mi->GetFullPath(), false);
+        ConvertToWindowsSlash(m);
+        oss << m << ";";
+      }
+      e2.Element("AdditionalManifestFiles", oss.str());
+    }
+    if (dpiAware) {
+      if (!strcmp(dpiAware, "PerMonitor")) {
+        e2.Element("EnableDpiAwareness", "PerMonitorHighDPIAware");
+      } else if (cmIsOn(dpiAware)) {
+        e2.Element("EnableDpiAwareness", "true");
+      } else if (cmIsOff(dpiAware)) {
+        e2.Element("EnableDpiAwareness", "false");
+      } else {
+        cmSystemTools::Error("Bad parameter for VS_DPI_AWARE: " +
+                             std::string(dpiAware));
+      }
+    }
   }
 }
 
@@ -3322,22 +3491,16 @@
 
   if (const char* nativeLibDirectoriesExpression =
         this->GeneratorTarget->GetProperty("ANDROID_NATIVE_LIB_DIRECTORIES")) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(nativeLibDirectoriesExpression);
-    std::string nativeLibDirs =
-      cge->Evaluate(this->LocalGenerator, configName);
+    std::string nativeLibDirs = cmGeneratorExpression::Evaluate(
+      nativeLibDirectoriesExpression, this->LocalGenerator, configName);
     e2.Element("NativeLibDirectories", nativeLibDirs);
   }
 
   if (const char* nativeLibDependenciesExpression =
         this->GeneratorTarget->GetProperty(
           "ANDROID_NATIVE_LIB_DEPENDENCIES")) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(nativeLibDependenciesExpression);
-    std::string nativeLibDeps =
-      cge->Evaluate(this->LocalGenerator, configName);
+    std::string nativeLibDeps = cmGeneratorExpression::Evaluate(
+      nativeLibDependenciesExpression, this->LocalGenerator, configName);
     e2.Element("NativeLibDependencies", nativeLibDeps);
   }
 
@@ -3348,11 +3511,8 @@
 
   if (const char* jarDirectoriesExpression =
         this->GeneratorTarget->GetProperty("ANDROID_JAR_DIRECTORIES")) {
-    cmGeneratorExpression ge;
-    std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(jarDirectoriesExpression);
-    std::string jarDirectories =
-      cge->Evaluate(this->LocalGenerator, configName);
+    std::string jarDirectories = cmGeneratorExpression::Evaluate(
+      jarDirectoriesExpression, this->LocalGenerator, configName);
     e2.Element("JarDirectories", jarDirectories);
   }
 
@@ -3421,9 +3581,7 @@
     linkType = "EXE";
   }
   std::string flags;
-  std::string linkFlagVarBase = "CMAKE_";
-  linkFlagVarBase += linkType;
-  linkFlagVarBase += "_LINKER_FLAGS";
+  std::string linkFlagVarBase = cmStrCat("CMAKE_", linkType, "_LINKER_FLAGS");
   flags += " ";
   flags += this->Makefile->GetRequiredDefinition(linkFlagVarBase);
   std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG;
@@ -3435,8 +3593,7 @@
     flags += " ";
     flags += targetLinkFlags;
   }
-  std::string flagsProp = "LINK_FLAGS_";
-  flagsProp += CONFIG;
+  std::string flagsProp = cmStrCat("LINK_FLAGS_", CONFIG);
   if (const char* flagsConfig =
         this->GeneratorTarget->GetProperty(flagsProp)) {
     flags += " ";
@@ -3461,8 +3618,7 @@
   std::vector<std::string> libVec;
   std::vector<std::string> vsTargetVec;
   this->AddLibraries(cli, libVec, vsTargetVec, config);
-  if (std::find(linkClosure->Languages.begin(), linkClosure->Languages.end(),
-                "CUDA") != linkClosure->Languages.end() &&
+  if (cmContains(linkClosure->Languages, "CUDA") &&
       this->CudaOptions[config] != nullptr) {
     switch (this->CudaOptions[config]->GetCudaRuntime()) {
       case cmVisualStudioGeneratorOptions::CudaRuntimeStatic:
@@ -3477,9 +3633,8 @@
         break;
     }
   }
-  std::string standardLibsVar = "CMAKE_";
-  standardLibsVar += linkLanguage;
-  standardLibsVar += "_STANDARD_LIBRARIES";
+  std::string standardLibsVar =
+    cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES");
   std::string const& libs = this->Makefile->GetSafeDefinition(standardLibsVar);
   cmSystemTools::ParseWindowsCommandLine(libs.c_str(), libVec);
   linkOptions.AddFlag("AdditionalDependencies", libVec);
@@ -3543,13 +3698,12 @@
 
     linkOptions.AddFlag("GenerateDebugInformation", "false");
 
-    std::string pdb = this->GeneratorTarget->GetPDBDirectory(config);
-    pdb += "/";
-    pdb += targetNames.PDB;
-    std::string imLib = this->GeneratorTarget->GetDirectory(
-      config, cmStateEnums::ImportLibraryArtifact);
-    imLib += "/";
-    imLib += targetNames.ImportLibrary;
+    std::string pdb = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config),
+                               '/', targetNames.PDB);
+    std::string imLib =
+      cmStrCat(this->GeneratorTarget->GetDirectory(
+                 config, cmStateEnums::ImportLibraryArtifact),
+               '/', targetNames.ImportLibrary);
 
     linkOptions.AddFlag("ImportLibrary", imLib);
     linkOptions.AddFlag("ProgramDataBaseFile", pdb);
@@ -3639,7 +3793,7 @@
   }
 
   cmComputeLinkInformation& cli = *pcli;
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   const ItemVector& libs = cli.GetItems();
   std::string currentBinDir =
     this->LocalGenerator->GetCurrentBinaryDirectory();
@@ -3684,7 +3838,7 @@
   const cmComputeLinkInformation& cli, std::vector<std::string>& libVec,
   std::vector<std::string>& vsTargetVec, const std::string& config)
 {
-  typedef cmComputeLinkInformation::ItemVector ItemVector;
+  using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& libs = cli.GetItems();
   std::string currentBinDir =
     this->LocalGenerator->GetCurrentBinaryDirectory();
@@ -3749,8 +3903,7 @@
 {
   for (TargetsFileAndConfigs& i : this->TargetsFileAndConfigsVec) {
     if (cmSystemTools::ComparePath(targetsFile, i.File)) {
-      if (std::find(i.Configs.begin(), i.Configs.end(), config) ==
-          i.Configs.end()) {
+      if (!cmContains(i.Configs, config)) {
         i.Configs.push_back(config);
       }
       return;
@@ -3911,8 +4064,8 @@
 {
   cmGlobalGenerator::TargetDependSet const& unordered =
     this->GlobalGenerator->GetTargetDirectDepends(this->GeneratorTarget);
-  typedef cmGlobalVisualStudioGenerator::OrderedTargetDependSet
-    OrderedTargetDependSet;
+  using OrderedTargetDependSet =
+    cmGlobalVisualStudioGenerator::OrderedTargetDependSet;
   OrderedTargetDependSet depends(unordered, CMAKE_CHECK_BUILD_SYSTEM_TARGET);
   Elem e1(e0, "ItemGroup");
   e1.SetHasElements();
@@ -3932,10 +4085,8 @@
     if (p) {
       path = p;
     } else {
-      path = lg->GetCurrentBinaryDirectory();
-      path += "/";
-      path += dt->GetName();
-      path += computeProjectFileExtension(dt);
+      path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(),
+                      computeProjectFileExtension(dt));
     }
     ConvertToWindowsSlash(path);
     Elem e2(e1, "ProjectReference");
@@ -3944,32 +4095,8 @@
     e2.Element("Name", name);
     this->WriteDotNetReferenceCustomTags(e2, name);
 
-    // If the dependency target is not managed (compiled with /clr or
-    // C# target) and not a WinRT component we cannot reference it and
-    // have to set 'ReferenceOutputAssembly' to false.
-    auto referenceNotManaged =
-      dt->GetManagedType("") < cmGeneratorTarget::ManagedType::Mixed;
-    // Workaround to check for manually set /clr flags.
-    if (referenceNotManaged) {
-      if (const auto* flags = dt->GetProperty("COMPILE_OPTIONS")) {
-        std::string flagsStr = flags;
-        if (flagsStr.find("clr") != std::string::npos) {
-          // There is a warning already issued when building the flags.
-          referenceNotManaged = false;
-        }
-      }
-    }
-    // Workaround for static library C# targets
-    if (referenceNotManaged && dt->GetType() == cmStateEnums::STATIC_LIBRARY) {
-      referenceNotManaged = !dt->IsCSharpOnly();
-    }
-
-    // Referencing WinRT components is okay.
-    if (referenceNotManaged) {
-      referenceNotManaged = !dt->GetPropertyAsBool("VS_WINRT_COMPONENT");
-    }
-
-    if (referenceNotManaged) {
+    // Don't reference targets that don't produce any output.
+    if (dt->GetManagedType("") == cmGeneratorTarget::ManagedType::Undefined) {
       e2.Element("ReferenceOutputAssembly", "false");
       e2.Element("CopyToOutputDirectory", "Never");
     }
@@ -4018,7 +4145,7 @@
   std::unique_ptr<Elem> spe1;
   if (const char* vsSDKReferences =
         this->GeneratorTarget->GetProperty("VS_SDK_REFERENCES")) {
-    cmSystemTools::ExpandListArgument(vsSDKReferences, sdkReferences);
+    cmExpandList(vsSDKReferences, sdkReferences);
     spe1 = cm::make_unique<Elem>(e0, "ItemGroup");
     for (std::string const& ri : sdkReferences) {
       Elem(*spe1, "SDKReference").Attribute("Include", ri);
@@ -4693,12 +4820,12 @@
 {
   if (this->ProjectType == csproj) {
     const cmPropertyMap& props = sf->GetProperties();
-    for (auto const& p : props) {
+    for (const std::string& p : props.GetKeys()) {
       static const std::string propNamePrefix = "VS_CSHARP_";
-      if (p.first.find(propNamePrefix) == 0) {
-        std::string tagName = p.first.substr(propNamePrefix.length());
+      if (p.find(propNamePrefix) == 0) {
+        std::string tagName = p.substr(propNamePrefix.length());
         if (!tagName.empty()) {
-          const std::string val = props.GetPropertyValue(p.first);
+          const std::string val = props.GetPropertyValue(p);
           if (!val.empty()) {
             tags[tagName] = val;
           } else {
@@ -4742,8 +4869,8 @@
   const char* relativeFilePath) const
 {
   // Always search in the standard modules location.
-  std::string path = cmSystemTools::GetCMakeRoot() + "/";
-  path += relativeFilePath;
+  std::string path =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), '/', relativeFilePath);
   ConvertToWindowsSlash(path);
 
   return path;
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index d453d1a..a18a33d 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -186,8 +186,8 @@
 
 private:
   friend class cmVS10GeneratorOptions;
-  typedef cmVS10GeneratorOptions Options;
-  typedef std::map<std::string, std::unique_ptr<Options>> OptionsMap;
+  using Options = cmVS10GeneratorOptions;
+  using OptionsMap = std::map<std::string, std::unique_ptr<Options>>;
   OptionsMap ClOptions;
   OptionsMap RcOptions;
   OptionsMap CudaOptions;
@@ -224,16 +224,16 @@
   std::string DefaultArtifactDir;
   bool AddedDefaultCertificate = false;
   // managed C++/C# relevant members
-  typedef std::pair<std::string, std::string> DotNetHintReference;
-  typedef std::vector<DotNetHintReference> DotNetHintReferenceList;
-  typedef std::map<std::string, DotNetHintReferenceList>
-    DotNetHintReferenceMap;
+  using DotNetHintReference = std::pair<std::string, std::string>;
+  using DotNetHintReferenceList = std::vector<DotNetHintReference>;
+  using DotNetHintReferenceMap =
+    std::map<std::string, DotNetHintReferenceList>;
   DotNetHintReferenceMap DotNetHintReferences;
-  typedef std::set<std::string> UsingDirectories;
-  typedef std::map<std::string, UsingDirectories> UsingDirectoriesMap;
+  using UsingDirectories = std::set<std::string>;
+  using UsingDirectoriesMap = std::map<std::string, UsingDirectories>;
   UsingDirectoriesMap AdditionalUsingDirectories;
 
-  typedef std::map<std::string, ToolSources> ToolSourceMap;
+  using ToolSourceMap = std::map<std::string, ToolSources>;
   ToolSourceMap Tools;
   std::string GetCMakeFilePath(const char* name) const;
 };
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index e1b0c70..1139aa9 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -1,5 +1,7 @@
 #include "cmVisualStudioGeneratorOptions.h"
 
+#include <cm/iterator>
+
 #include "cmAlgorithms.h"
 #include "cmLocalVisualStudioGenerator.h"
 #include "cmOutputConverter.h"
@@ -193,7 +195,7 @@
     std::string arch_name = arch[0];
     std::vector<std::string> codes;
     if (!code.empty()) {
-      codes = cmSystemTools::tokenize(code[0], ",");
+      codes = cmTokenize(code[0], ",");
     }
     if (codes.empty()) {
       codes.push_back(arch_name);
@@ -220,7 +222,7 @@
     cmSystemTools::ReplaceString(entry, "]", "");
     cmSystemTools::ReplaceString(entry, "\"", "");
 
-    std::vector<std::string> codes = cmSystemTools::tokenize(entry, ",");
+    std::vector<std::string> codes = cmTokenize(entry, ",");
     if (codes.size() >= 2) {
       auto gencode_arch = cm::cbegin(codes);
       for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) {
@@ -269,8 +271,8 @@
     }
 
     if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
-      keyValue[1] =
-        keyValue[1].substr(1, std::max(0, cm::isize(keyValue[1]) - 2));
+      keyValue[1] = keyValue[1].substr(
+        1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
     }
 
     if (keyValue[0] == "level") {
@@ -325,9 +327,9 @@
     //  "rtSingleThreadedDLL", "10", /libs:dll
     //  "rtSingleThreadedDebug", "5", /dbglibs /libs:static
     //  "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
-    std::string rl = "rtMultiThreaded";
-    rl += this->FortranRuntimeDebug ? "Debug" : "";
-    rl += this->FortranRuntimeDLL ? "DLL" : "";
+    std::string rl =
+      cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
+               this->FortranRuntimeDLL ? "DLL" : "");
     this->FlagMap["RuntimeLibrary"] = rl;
   }
 
diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h
index a30a67f..560593e 100644
--- a/Source/cmVisualStudioGeneratorOptions.h
+++ b/Source/cmVisualStudioGeneratorOptions.h
@@ -14,7 +14,7 @@
 
 class cmLocalVisualStudioGenerator;
 
-typedef cmIDEFlagTable cmVS7FlagTable;
+using cmVS7FlagTable = cmIDEFlagTable;
 
 class cmVisualStudioGeneratorOptions : public cmIDEOptions
 {
diff --git a/Source/cmVisualStudioSlnData.h b/Source/cmVisualStudioSlnData.h
index 9c1dffc..5ce7d74 100644
--- a/Source/cmVisualStudioSlnData.h
+++ b/Source/cmVisualStudioSlnData.h
@@ -45,9 +45,9 @@
                                 const std::string& projectRelativePath);
 
 private:
-  typedef std::map<std::string, cmSlnProjectEntry> ProjectStorage;
+  using ProjectStorage = std::map<std::string, cmSlnProjectEntry>;
   ProjectStorage ProjectsByGUID;
-  typedef std::map<std::string, ProjectStorage::iterator> ProjectStringIndex;
+  using ProjectStringIndex = std::map<std::string, ProjectStorage::iterator>;
   ProjectStringIndex ProjectNameIndex;
 };
 
diff --git a/Source/cmVisualStudioSlnParser.cxx b/Source/cmVisualStudioSlnParser.cxx
index 9353276..4533e9b 100644
--- a/Source/cmVisualStudioSlnParser.cxx
+++ b/Source/cmVisualStudioSlnParser.cxx
@@ -2,13 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmVisualStudioSlnParser.h"
 
-#include "cmSystemTools.h"
-#include "cmVisualStudioSlnData.h"
-#include "cmsys/FStream.hxx"
-
 #include <cassert>
 #include <stack>
 
+#include "cmsys/FStream.hxx"
+
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmVisualStudioSlnData.h"
+
 namespace {
 enum LineFormat
 {
@@ -50,7 +52,7 @@
   void CopyVerbatim(const std::string& line) { this->Tag = line; }
 
 private:
-  typedef std::pair<std::string, bool> StringData;
+  using StringData = std::pair<std::string, bool>;
   std::string Tag;
   StringData Arg;
   std::vector<StringData> Values;
@@ -192,8 +194,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;
       }
@@ -462,7 +464,7 @@
   if (!this->ParseBOM(input, line, state))
     return false;
   do {
-    line = cmSystemTools::TrimWhitespace(line);
+    line = cmTrimWhitespace(line);
     if (line.empty())
       continue;
     ParsedLine parsedLine;
@@ -578,9 +580,9 @@
     return true;
   }
   const std::string& key = line.substr(0, idxEqualSign);
-  parsedLine.SetTag(cmSystemTools::TrimWhitespace(key));
+  parsedLine.SetTag(cmTrimWhitespace(key));
   const std::string& value = line.substr(idxEqualSign + 1);
-  parsedLine.AddValue(cmSystemTools::TrimWhitespace(value));
+  parsedLine.AddValue(cmTrimWhitespace(value));
   return true;
 }
 
@@ -589,18 +591,17 @@
 {
   size_t idxLeftParen = fullTag.find('(');
   if (idxLeftParen == fullTag.npos) {
-    parsedLine.SetTag(cmSystemTools::TrimWhitespace(fullTag));
+    parsedLine.SetTag(cmTrimWhitespace(fullTag));
     return true;
   }
-  parsedLine.SetTag(
-    cmSystemTools::TrimWhitespace(fullTag.substr(0, idxLeftParen)));
+  parsedLine.SetTag(cmTrimWhitespace(fullTag.substr(0, idxLeftParen)));
   size_t idxRightParen = fullTag.rfind(')');
   if (idxRightParen == fullTag.npos) {
     this->LastResult.SetError(ResultErrorInputStructure,
                               state.GetCurrentLine());
     return false;
   }
-  const std::string& arg = cmSystemTools::TrimWhitespace(
+  const std::string& arg = cmTrimWhitespace(
     fullTag.substr(idxLeftParen + 1, idxRightParen - idxLeftParen - 1));
   if (arg.front() == '"') {
     if (arg.back() != '"') {
@@ -617,7 +618,7 @@
 bool cmVisualStudioSlnParser::ParseValue(const std::string& value,
                                          ParsedLine& parsedLine)
 {
-  const std::string& trimmed = cmSystemTools::TrimWhitespace(value);
+  const std::string& trimmed = cmTrimWhitespace(value);
   if (trimmed.empty())
     parsedLine.AddValue(trimmed);
   else if (trimmed.front() == '"' && trimmed.back() == '"')
diff --git a/Source/cmVisualStudioSlnParser.h b/Source/cmVisualStudioSlnParser.h
index d6345a8..6c05633 100644
--- a/Source/cmVisualStudioSlnParser.h
+++ b/Source/cmVisualStudioSlnParser.h
@@ -7,9 +7,10 @@
 
 #include <bitset>
 #include <iosfwd>
-#include <stddef.h>
 #include <string>
 
+#include <stddef.h>
+
 class cmSlnData;
 
 class cmVisualStudioSlnParser
@@ -42,7 +43,7 @@
     DataGroupCount
   };
 
-  typedef std::bitset<DataGroupCount> DataGroupSet;
+  using DataGroupSet = std::bitset<DataGroupCount>;
 
   static const DataGroupSet DataGroupProjects;
   static const DataGroupSet DataGroupProjectDependencies;
diff --git a/Source/cmVisualStudioWCEPlatformParser.h b/Source/cmVisualStudioWCEPlatformParser.h
index c19691a..60a6611 100644
--- a/Source/cmVisualStudioWCEPlatformParser.h
+++ b/Source/cmVisualStudioWCEPlatformParser.h
@@ -6,10 +6,11 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <map>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
 #include "cmXMLParser.h"
 
 // This class is used to parse XML with configuration
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index a01fa6f..26e7c75 100644
--- a/Source/cmWhileCommand.cxx
+++ b/Source/cmWhileCommand.cxx
@@ -2,18 +2,46 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWhileCommand.h"
 
+#include <string>
+#include <utility>
+
+#include <cm/memory>
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmConditionEvaluator.h"
 #include "cmExecutionStatus.h"
 #include "cmExpandedCommandArgument.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmSystemTools.h"
 
-#include <memory> // IWYU pragma: keep
+class cmWhileFunctionBlocker : public cmFunctionBlocker
+{
+public:
+  cmWhileFunctionBlocker(cmMakefile* mf);
+  ~cmWhileFunctionBlocker() override;
+
+  cm::string_view StartCommandName() const override { return "while"_s; }
+  cm::string_view EndCommandName() const override { return "endwhile"_s; }
+
+  bool ArgumentsMatch(cmListFileFunction const& lff,
+                      cmMakefile& mf) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& inStatus) override;
+
+  std::vector<cmListFileArgument> Args;
+
+private:
+  cmMakefile* Makefile;
+};
 
 cmWhileFunctionBlocker::cmWhileFunctionBlocker(cmMakefile* mf)
   : Makefile(mf)
-  , Depth(0)
 {
   this->Makefile->PushLoopBlock();
 }
@@ -23,122 +51,93 @@
   this->Makefile->PopLoopBlock();
 }
 
-bool cmWhileFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
-                                               cmMakefile& mf,
-                                               cmExecutionStatus& inStatus)
+bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                            cmMakefile&) const
 {
-  // at end of for each execute recorded commands
-  if (lff.Name.Lower == "while") {
-    // record the number of while commands past this one
-    this->Depth++;
-  } else if (lff.Name.Lower == "endwhile") {
-    // if this is the endwhile for this while loop then execute
-    if (!this->Depth) {
-      // Remove the function blocker for this scope or bail.
-      std::unique_ptr<cmFunctionBlocker> fb(
-        mf.RemoveFunctionBlocker(this, lff));
-      if (!fb) {
-        return false;
+  return lff.Arguments.empty() || lff.Arguments == this->Args;
+}
+
+bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
+                                    cmExecutionStatus& inStatus)
+{
+  cmMakefile& mf = inStatus.GetMakefile();
+  std::string errorString;
+
+  std::vector<cmExpandedCommandArgument> expandedArguments;
+  mf.ExpandArguments(this->Args, expandedArguments);
+  MessageType messageType;
+
+  cmListFileContext execContext = this->GetStartingContext();
+
+  cmCommandContext commandContext;
+  commandContext.Line = execContext.Line;
+  commandContext.Name = execContext.Name;
+
+  cmConditionEvaluator conditionEvaluator(mf, this->GetStartingContext(),
+                                          mf.GetBacktrace(commandContext));
+
+  bool isTrue =
+    conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
+
+  while (isTrue) {
+    if (!errorString.empty()) {
+      std::string err = "had incorrect arguments: ";
+      for (cmListFileArgument const& arg : this->Args) {
+        err += (arg.Delim ? "\"" : "");
+        err += arg.Value;
+        err += (arg.Delim ? "\"" : "");
+        err += " ";
       }
-
-      std::string errorString;
-
-      std::vector<cmExpandedCommandArgument> expandedArguments;
-      mf.ExpandArguments(this->Args, expandedArguments);
-      MessageType messageType;
-
-      cmListFileContext execContext = this->GetStartingContext();
-
-      cmCommandContext commandContext;
-      commandContext.Line = execContext.Line;
-      commandContext.Name = execContext.Name;
-
-      cmConditionEvaluator conditionEvaluator(mf, this->GetStartingContext(),
-                                              mf.GetBacktrace(commandContext));
-
-      bool isTrue =
-        conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
-
-      while (isTrue) {
-        if (!errorString.empty()) {
-          std::string err = "had incorrect arguments: ";
-          for (cmListFileArgument const& arg : this->Args) {
-            err += (arg.Delim ? "\"" : "");
-            err += arg.Value;
-            err += (arg.Delim ? "\"" : "");
-            err += " ";
-          }
-          err += "(";
-          err += errorString;
-          err += ").";
-          mf.IssueMessage(messageType, err);
-          if (messageType == MessageType::FATAL_ERROR) {
-            cmSystemTools::SetFatalErrorOccured();
-            return true;
-          }
-        }
-
-        // Invoke all the functions that were collected in the block.
-        for (cmListFileFunction const& fn : this->Functions) {
-          cmExecutionStatus status;
-          mf.ExecuteCommand(fn, status);
-          if (status.GetReturnInvoked()) {
-            inStatus.SetReturnInvoked();
-            return true;
-          }
-          if (status.GetBreakInvoked()) {
-            return true;
-          }
-          if (status.GetContinueInvoked()) {
-            break;
-          }
-          if (cmSystemTools::GetFatalErrorOccured()) {
-            return true;
-          }
-        }
-        expandedArguments.clear();
-        mf.ExpandArguments(this->Args, expandedArguments);
-        isTrue = conditionEvaluator.IsTrue(expandedArguments, errorString,
-                                           messageType);
+      err += "(";
+      err += errorString;
+      err += ").";
+      mf.IssueMessage(messageType, err);
+      if (messageType == MessageType::FATAL_ERROR) {
+        cmSystemTools::SetFatalErrorOccured();
+        return true;
       }
-      return true;
     }
-    // decrement for each nested while that ends
-    this->Depth--;
+
+    // Invoke all the functions that were collected in the block.
+    for (cmListFileFunction const& fn : functions) {
+      cmExecutionStatus status(mf);
+      mf.ExecuteCommand(fn, status);
+      if (status.GetReturnInvoked()) {
+        inStatus.SetReturnInvoked();
+        return true;
+      }
+      if (status.GetBreakInvoked()) {
+        return true;
+      }
+      if (status.GetContinueInvoked()) {
+        break;
+      }
+      if (cmSystemTools::GetFatalErrorOccured()) {
+        return true;
+      }
+    }
+    expandedArguments.clear();
+    mf.ExpandArguments(this->Args, expandedArguments);
+    isTrue =
+      conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
   }
-
-  // record the command
-  this->Functions.push_back(lff);
-
-  // always return true
   return true;
 }
 
-bool cmWhileFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
-                                          cmMakefile&)
-{
-  if (lff.Name.Lower == "endwhile") {
-    // if the endwhile has arguments, then make sure
-    // they match the arguments of the matching while
-    if (lff.Arguments.empty() || lff.Arguments == this->Args) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool cmWhileCommand::InvokeInitialPass(
-  const std::vector<cmListFileArgument>& args, cmExecutionStatus&)
+bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
+                    cmExecutionStatus& status)
 {
   if (args.empty()) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
 
   // create a function blocker
-  cmWhileFunctionBlocker* f = new cmWhileFunctionBlocker(this->Makefile);
-  f->Args = args;
-  this->Makefile->AddFunctionBlocker(f);
-
+  {
+    cmMakefile& makefile = status.GetMakefile();
+    auto fb = cm::make_unique<cmWhileFunctionBlocker>(&makefile);
+    fb->Args = args;
+    makefile.AddFunctionBlocker(std::move(fb));
+  }
   return true;
 }
diff --git a/Source/cmWhileCommand.h b/Source/cmWhileCommand.h
index 6f6d405..beca652 100644
--- a/Source/cmWhileCommand.h
+++ b/Source/cmWhileCommand.h
@@ -5,58 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
 #include <vector>
 
-#include "cmCommand.h"
-#include "cmFunctionBlocker.h"
-#include "cmListFileCache.h"
-
 class cmExecutionStatus;
-class cmMakefile;
-
-class cmWhileFunctionBlocker : public cmFunctionBlocker
-{
-public:
-  cmWhileFunctionBlocker(cmMakefile* mf);
-  ~cmWhileFunctionBlocker() override;
-  bool IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile& mf,
-                         cmExecutionStatus&) override;
-  bool ShouldRemove(const cmListFileFunction& lff, cmMakefile& mf) override;
-
-  std::vector<cmListFileArgument> Args;
-  std::vector<cmListFileFunction> Functions;
-
-private:
-  cmMakefile* Makefile;
-  int Depth;
-};
+struct cmListFileArgument;
 
 /// \brief Starts a while loop
-class cmWhileCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmWhileCommand; }
-
-  /**
-   * This overrides the default InvokeInitialPass implementation.
-   * It records the arguments before expansion.
-   */
-  bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
-                         cmExecutionStatus&) override;
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const&,
-                   cmExecutionStatus&) override
-  {
-    return false;
-  }
-};
+bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
+                    cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx
index cbf070e..aa0d6b3 100644
--- a/Source/cmWorkerPool.cxx
+++ b/Source/cmWorkerPool.cxx
@@ -2,30 +2,34 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWorkerPool.h"
 
-#include "cmRange.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
-#include "cm_uv.h"
-
 #include <algorithm>
 #include <array>
 #include <condition_variable>
+#include <cstddef>
 #include <deque>
 #include <functional>
 #include <mutex>
-#include <stddef.h>
 #include <thread>
 
+#include <cm/memory>
+
+#include "cm_uv.h"
+
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
+
 /**
  * @brief libuv pipe buffer class
  */
 class cmUVPipeBuffer
 {
 public:
-  typedef cmRange<char const*> DataRange;
-  typedef std::function<void(DataRange)> DataFunction;
+  using DataRange = cmRange<const char*>;
+  using DataFunction = std::function<void(DataRange)>;
   /// On error the ssize_t argument is a non zero libuv error code
-  typedef std::function<void(ssize_t)> EndFunction;
+  using EndFunction = std::function<void(ssize_t)>;
 
 public:
   /**
@@ -304,13 +308,11 @@
     proc.Result()->TermSignal = termSignal;
     if (!proc.Result()->error()) {
       if (termSignal != 0) {
-        proc.Result()->ErrorMessage = "Process was terminated by signal ";
-        proc.Result()->ErrorMessage +=
-          std::to_string(proc.Result()->TermSignal);
+        proc.Result()->ErrorMessage = cmStrCat(
+          "Process was terminated by signal ", proc.Result()->TermSignal);
       } else if (exitStatus != 0) {
-        proc.Result()->ErrorMessage = "Process failed with return value ";
-        proc.Result()->ErrorMessage +=
-          std::to_string(proc.Result()->ExitStatus);
+        proc.Result()->ErrorMessage = cmStrCat(
+          "Process failed with return value ", proc.Result()->ExitStatus);
       }
     }
 
@@ -330,9 +332,8 @@
 {
   // Process pipe error
   if ((error != 0) && !Result()->error()) {
-    Result()->ErrorMessage =
-      "Reading from stdout pipe failed with libuv error code ";
-    Result()->ErrorMessage += std::to_string(error);
+    Result()->ErrorMessage = cmStrCat(
+      "Reading from stdout pipe failed with libuv error code ", error);
   }
   // Try finish
   UVTryFinish();
@@ -349,9 +350,8 @@
 {
   // Process pipe error
   if ((error != 0) && !Result()->error()) {
-    Result()->ErrorMessage =
-      "Reading from stderr pipe failed with libuv error code ";
-    Result()->ErrorMessage += std::to_string(error);
+    Result()->ErrorMessage = cmStrCat(
+      "Reading from stderr pipe failed with libuv error code ", error);
   }
   // Try finish
   UVTryFinish();
diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h
index f08bb4f..9179922 100644
--- a/Source/cmWorkerPool.h
+++ b/Source/cmWorkerPool.h
@@ -5,14 +5,13 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmAlgorithms.h" // IWYU pragma: keep
-
-#include <memory> // IWYU pragma: keep
-#include <stdint.h>
+#include <cstdint>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <cm/memory>
+
 // -- Types
 class cmWorkerPoolInternal;
 
@@ -127,7 +126,7 @@
   /**
    * Job handle type
    */
-  typedef std::unique_ptr<JobT> JobHandleT;
+  using JobHandleT = std::unique_ptr<JobT>;
 
   /**
    * Fence job base class
diff --git a/Source/cmWorkingDirectory.cxx b/Source/cmWorkingDirectory.cxx
index 816f104..5700b1c 100644
--- a/Source/cmWorkingDirectory.cxx
+++ b/Source/cmWorkingDirectory.cxx
@@ -2,10 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWorkingDirectory.h"
 
-#include "cmSystemTools.h"
-
 #include <cerrno>
 
+#include "cmSystemTools.h"
+
 cmWorkingDirectory::cmWorkingDirectory(std::string const& newdir)
 {
   this->OldDir = cmSystemTools::GetCurrentWorkingDirectory();
diff --git a/Source/cmWriteFileCommand.cxx b/Source/cmWriteFileCommand.cxx
index 49dbf1a..34e21b2 100644
--- a/Source/cmWriteFileCommand.cxx
+++ b/Source/cmWriteFileCommand.cxx
@@ -4,22 +4,23 @@
 
 #include "cmsys/FStream.hxx"
 
-#include "cmMakefile.h"
-#include "cmSystemTools.h"
 #include "cm_sys_stat.h"
 
-class cmExecutionStatus;
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 // cmLibraryCommand
-bool cmWriteFileCommand::InitialPass(std::vector<std::string> const& args,
-                                     cmExecutionStatus&)
+bool cmWriteFileCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status)
 {
   if (args.size() < 2) {
-    this->SetError("called with incorrect number of arguments");
+    status.SetError("called with incorrect number of arguments");
     return false;
   }
   std::string message;
-  std::vector<std::string>::const_iterator i = args.begin();
+  auto i = args.begin();
 
   std::string const& fileName = *i;
   bool overwrite = true;
@@ -33,10 +34,10 @@
     }
   }
 
-  if (!this->Makefile->CanIWriteThisFile(fileName)) {
+  if (!status.GetMakefile().CanIWriteThisFile(fileName)) {
     std::string e =
       "attempted to write a file: " + fileName + " into a source directory.";
-    this->SetError(e);
+    status.SetError(e);
     cmSystemTools::SetFatalErrorOccured();
     return false;
   }
@@ -65,10 +66,10 @@
   cmsys::ofstream file(fileName.c_str(),
                        overwrite ? std::ios::out : std::ios::app);
   if (!file) {
-    std::string error = "Internal CMake error when trying to open file: ";
-    error += fileName;
-    error += " for writing.";
-    this->SetError(error);
+    std::string error =
+      cmStrCat("Internal CMake error when trying to open file: ", fileName,
+               " for writing.");
+    status.SetError(error);
     return false;
   }
   file << message << std::endl;
diff --git a/Source/cmWriteFileCommand.h b/Source/cmWriteFileCommand.h
index 9028f84..3e0e043 100644
--- a/Source/cmWriteFileCommand.h
+++ b/Source/cmWriteFileCommand.h
@@ -8,28 +8,13 @@
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
-
 class cmExecutionStatus;
 
-/** \class cmWriteFileCommand
+/**
  * \brief Writes a message to a file
  *
  */
-class cmWriteFileCommand : public cmCommand
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  cmCommand* Clone() override { return new cmWriteFileCommand; }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmWriteFileCommand(std::vector<std::string> const& args,
+                        cmExecutionStatus& status);
 
 #endif
diff --git a/Source/cmXCodeObject.cxx b/Source/cmXCodeObject.cxx
index 1747c9c..b301ab1 100644
--- a/Source/cmXCodeObject.cxx
+++ b/Source/cmXCodeObject.cxx
@@ -2,9 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmXCodeObject.h"
 
-#include <CoreFoundation/CoreFoundation.h>
 #include <ostream>
 
+#include <CoreFoundation/CoreFoundation.h>
+
 #include "cmSystemTools.h"
 
 const char* cmXCodeObject::PBXTypeNames[] = {
diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h
index 51e5d36..d9be3d2 100644
--- a/Source/cmXCodeObject.h
+++ b/Source/cmXCodeObject.h
@@ -12,6 +12,8 @@
 #include <utility>
 #include <vector>
 
+#include "cmAlgorithms.h"
+
 class cmGeneratorTarget;
 
 class cmXCodeObject
@@ -80,15 +82,10 @@
   void SetObject(cmXCodeObject* value) { this->Object = value; }
   cmXCodeObject* GetObject() { return this->Object; }
   void AddObject(cmXCodeObject* value) { this->List.push_back(value); }
-  bool HasObject(cmXCodeObject* o) const
-  {
-    return !(std::find(this->List.begin(), this->List.end(), o) ==
-             this->List.end());
-  }
+  bool HasObject(cmXCodeObject* o) const { return cmContains(this->List, o); }
   void AddUniqueObject(cmXCodeObject* value)
   {
-    if (std::find(this->List.begin(), this->List.end(), value) ==
-        this->List.end()) {
+    if (!cmContains(this->List, value)) {
       this->List.push_back(value);
     }
   }
@@ -109,8 +106,7 @@
   bool HasComment() const { return (!this->Comment.empty()); }
   cmXCodeObject* GetObject(const char* name) const
   {
-    std::map<std::string, cmXCodeObject*>::const_iterator i =
-      this->ObjectAttributes.find(name);
+    auto const i = this->ObjectAttributes.find(name);
     if (i != this->ObjectAttributes.end()) {
       return i->second;
     }
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx
index c33bb7e..afc95f5 100644
--- a/Source/cmXCodeScheme.cxx
+++ b/Source/cmXCodeScheme.cxx
@@ -27,14 +27,11 @@
 {
   // Create shared scheme sub-directory tree
   //
-  std::string xcodeSchemeDir = xcProjDir;
-  xcodeSchemeDir += "/xcshareddata/xcschemes";
+  std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes");
   cmSystemTools::MakeDirectory(xcodeSchemeDir.c_str());
 
-  std::string xcodeSchemeFile = xcodeSchemeDir;
-  xcodeSchemeFile += "/";
-  xcodeSchemeFile += this->TargetName;
-  xcodeSchemeFile += ".xcscheme";
+  std::string xcodeSchemeFile =
+    cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme");
 
   cmGeneratedFileStream fout(xcodeSchemeFile);
   fout.SetCopyIfDifferent(true);
@@ -140,7 +137,9 @@
   xout.Attribute("launchStyle", "0");
   xout.Attribute("useCustomWorkingDirectory", "NO");
   xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
-  xout.Attribute("debugDocumentVersioning", "YES");
+  WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
+                                    "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
+                                    true);
   xout.Attribute("debugServiceExtension", "internal");
   xout.Attribute("allowLocationSimulation", "YES");
 
@@ -217,8 +216,7 @@
 
   if (const char* argList =
         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
-    std::vector<std::string> arguments;
-    cmSystemTools::ExpandListArgument(argList, arguments);
+    std::vector<std::string> arguments = cmExpandedList(argList);
     if (!arguments.empty()) {
       xout.StartElement("CommandLineArguments");
 
@@ -238,8 +236,7 @@
 
   if (const char* envList =
         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
-    std::vector<std::string> envs;
-    cmSystemTools::ExpandListArgument(envList, envs);
+    std::vector<std::string> envs = cmExpandedList(envList);
     if (!envs.empty()) {
       xout.StartElement("EnvironmentVariables");
 
@@ -316,6 +313,21 @@
   return false;
 }
 
+bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
+  cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
+  bool defaultValue)
+{
+  auto property = Target->GetTarget()->GetProperty(varName);
+  bool isOn = (property == nullptr && defaultValue) || cmIsOn(property);
+
+  if (isOn) {
+    xout.Attribute(attrName.c_str(), "YES");
+  } else {
+    xout.Attribute(attrName.c_str(), "NO");
+  }
+  return isOn;
+}
+
 bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
   cmXMLWriter& xout, const std::string& key, const std::string& value,
   const std::string& varName)
@@ -344,7 +356,9 @@
   xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
   xout.Attribute("savedToolIdentifier", "");
   xout.Attribute("useCustomWorkingDirectory", "NO");
-  xout.Attribute("debugDocumentVersioning", "YES");
+  WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
+                                    "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
+                                    true);
   xout.EndElement();
 }
 
@@ -393,9 +407,7 @@
   // Try to find the desired configuration by name,
   // and if it's not found return first from the list
   //
-  if (std::find(this->ConfigList.begin(), this->ConfigList.end(), name) ==
-        this->ConfigList.end() &&
-      !this->ConfigList.empty()) {
+  if (!cmContains(this->ConfigList, name) && !this->ConfigList.empty()) {
     return this->ConfigList[0];
   }
 
diff --git a/Source/cmXCodeScheme.h b/Source/cmXCodeScheme.h
index 8c47123..dff5e35 100644
--- a/Source/cmXCodeScheme.h
+++ b/Source/cmXCodeScheme.h
@@ -18,7 +18,7 @@
 class cmXCodeScheme
 {
 public:
-  typedef std::vector<const cmXCodeObject*> TestObjects;
+  using TestObjects = std::vector<const cmXCodeObject*>;
 
   cmXCodeScheme(cmXCodeObject* xcObj, TestObjects tests,
                 const std::vector<std::string>& configList,
@@ -46,6 +46,11 @@
                                   const std::string& attrName,
                                   const std::string& varName);
 
+  bool WriteLaunchActionBooleanAttribute(cmXMLWriter& xout,
+                                         const std::string& attrName,
+                                         const std::string& varName,
+                                         bool defaultValue);
+
   bool WriteLaunchActionAdditionalOption(cmXMLWriter& xout,
                                          const std::string& attrName,
                                          const std::string& value,
diff --git a/Source/cmXMLParser.cxx b/Source/cmXMLParser.cxx
index 4c6c35a..ad5c4ba 100644
--- a/Source/cmXMLParser.cxx
+++ b/Source/cmXMLParser.cxx
@@ -2,12 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmXMLParser.h"
 
-#include "cm_expat.h"
-#include "cmsys/FStream.hxx"
-#include <ctype.h>
+#include <cctype>
+#include <cstring>
 #include <iostream>
 #include <sstream>
-#include <string.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cm_expat.h"
 
 cmXMLParser::cmXMLParser()
 {
diff --git a/Source/cmXMLParser.h b/Source/cmXMLParser.h
index 98ba049..1bc8d64 100644
--- a/Source/cmXMLParser.h
+++ b/Source/cmXMLParser.h
@@ -42,7 +42,7 @@
   virtual int ParseChunk(const char* inputString,
                          std::string::size_type length);
   virtual int CleanupParser();
-  typedef void (*ReportFunction)(int, const char*, void*);
+  using ReportFunction = void (*)(int, const char*, void*);
   void SetErrorCallback(ReportFunction f, void* d)
   {
     this->ReportCallback = f;
diff --git a/Source/cmXMLSafe.cxx b/Source/cmXMLSafe.cxx
index d9bdc02..8cd5f6e 100644
--- a/Source/cmXMLSafe.cxx
+++ b/Source/cmXMLSafe.cxx
@@ -2,11 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmXMLSafe.h"
 
-#include "cm_utf8.h"
-
+#include <cstdio>
+#include <cstring>
 #include <sstream>
-#include <stdio.h>
-#include <string.h>
+
+#include "cm_utf8.h"
 
 cmXMLSafe::cmXMLSafe(const char* s)
   : Data(s)
diff --git a/Source/cmXMLWriter.cxx b/Source/cmXMLWriter.cxx
index f1ce608..0811bd0 100644
--- a/Source/cmXMLWriter.cxx
+++ b/Source/cmXMLWriter.cxx
@@ -2,9 +2,10 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmXMLWriter.h"
 
-#include "cmsys/FStream.hxx"
 #include <cassert>
 
+#include "cmsys/FStream.hxx"
+
 cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level)
   : Output(output)
   , IndentationElement(1, '\t')
diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h
index 512e103..bc445aa 100644
--- a/Source/cmXMLWriter.h
+++ b/Source/cmXMLWriter.h
@@ -5,15 +5,16 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmXMLSafe.h"
-
 #include <chrono>
+#include <cstddef>
 #include <ctime>
 #include <ostream>
 #include <stack>
 #include <string>
 #include <vector>
 
+#include "cmXMLSafe.h"
+
 class cmXMLWriter
 {
 public:
@@ -76,14 +77,11 @@
   void CloseStartElement();
 
 private:
-  static cmXMLSafe SafeAttribute(const char* value)
-  {
-    return cmXMLSafe(value);
-  }
+  static cmXMLSafe SafeAttribute(const char* value) { return { value }; }
 
   static cmXMLSafe SafeAttribute(std::string const& value)
   {
-    return cmXMLSafe(value);
+    return { value };
   }
 
   template <typename T>
diff --git a/Source/cm_codecvt.cxx b/Source/cm_codecvt.cxx
index 821c112..122e022 100644
--- a/Source/cm_codecvt.cxx
+++ b/Source/cm_codecvt.cxx
@@ -3,9 +3,10 @@
 #include "cm_codecvt.hxx"
 
 #if defined(_WIN32)
+#  include <windows.h>
+
 #  include <assert.h>
 #  include <string.h>
-#  include <windows.h>
 #  undef max
 #  include "cmsys/Encoding.hxx"
 #endif
diff --git a/Source/cm_codecvt.hxx b/Source/cm_codecvt.hxx
index 2df3961..b2cb9e6 100644
--- a/Source/cm_codecvt.hxx
+++ b/Source/cm_codecvt.hxx
@@ -5,8 +5,8 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cwchar>
 #include <locale>
-#include <wchar.h>
 
 class codecvt : public std::codecvt<char, char, mbstate_t>
 {
@@ -18,7 +18,7 @@
     ANSI
   };
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 
   codecvt(Encoding e);
 
diff --git a/Source/cm_get_date.h b/Source/cm_get_date.h
index 6acf8de..38a690e 100644
--- a/Source/cm_get_date.h
+++ b/Source/cm_get_date.h
@@ -3,7 +3,7 @@
 #ifndef cm_get_date_h
 #define cm_get_date_h
 
-#include <time.h>
+#include <time.h> /* NOLINT(modernize-deprecated-headers) */
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/Source/cm_static_string_view.hxx b/Source/cm_static_string_view.hxx
index 1bef0c6..708ac95 100644
--- a/Source/cm_static_string_view.hxx
+++ b/Source/cm_static_string_view.hxx
@@ -5,10 +5,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cm_string_view.hxx"
-
 #include <cstddef>
 
+#include <cm/string_view>
+
 namespace cm {
 
 /** A string_view that only binds to static storage.
diff --git a/Source/cm_string_view.cxx b/Source/cm_string_view.cxx
deleted file mode 100644
index 61fa80e..0000000
--- a/Source/cm_string_view.cxx
+++ /dev/null
@@ -1,301 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-
-#include "cm_string_view.hxx"
-
-#ifndef CMake_HAVE_CXX_STRING_VIEW
-
-#  include "cm_kwiml.h"
-
-#  include <algorithm>
-#  include <ostream>
-#  include <stdexcept>
-
-namespace cm {
-
-string_view::const_reference string_view::at(size_type pos) const
-{
-  if (pos >= size_) {
-    throw std::out_of_range("Index out of range in string_view::at");
-  }
-  return data_[pos];
-}
-
-string_view::size_type string_view::copy(char* dest, size_type count,
-                                         size_type pos) const
-{
-  if (pos > size_) {
-    throw std::out_of_range("Index out of range in string_view::copy");
-  }
-  size_type const rcount = std::min(count, size_ - pos);
-  traits_type::copy(dest, data_ + pos, rcount);
-  return rcount;
-}
-
-string_view string_view::substr(size_type pos, size_type count) const
-{
-  if (pos > size_) {
-    throw std::out_of_range("Index out of range in string_view::substr");
-  }
-  size_type const rcount = std::min(count, size_ - pos);
-  return string_view(data_ + pos, rcount);
-}
-
-int string_view::compare(string_view v) const noexcept
-{
-  size_type const rlen = std::min(size_, v.size_);
-  int c = traits_type::compare(data_, v.data_, rlen);
-  if (c == 0) {
-    if (size_ < v.size_) {
-      c = -1;
-    } else if (size_ > v.size_) {
-      c = 1;
-    }
-  }
-  return c;
-}
-
-int string_view::compare(size_type pos1, size_type count1, string_view v) const
-{
-  return substr(pos1, count1).compare(v);
-}
-
-int string_view::compare(size_type pos1, size_type count1, string_view v,
-                         size_type pos2, size_type count2) const
-{
-  return substr(pos1, count1).compare(v.substr(pos2, count2));
-}
-
-int string_view::compare(const char* s) const
-{
-  return compare(string_view(s));
-}
-
-int string_view::compare(size_type pos1, size_type count1, const char* s) const
-{
-  return substr(pos1, count1).compare(string_view(s));
-}
-
-int string_view::compare(size_type pos1, size_type count1, const char* s,
-                         size_type count2) const
-{
-  return substr(pos1, count1).compare(string_view(s, count2));
-}
-
-string_view::size_type string_view::find(string_view v, size_type pos) const
-  noexcept
-{
-  for (; pos + v.size_ <= size_; ++pos) {
-    if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == 0) {
-      return pos;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find(char c, size_type pos) const noexcept
-{
-  return find(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::find(const char* s, size_type pos,
-                                         size_type count) const
-{
-  return find(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::find(const char* s, size_type pos) const
-{
-  return find(string_view(s), pos);
-}
-
-string_view::size_type string_view::rfind(string_view v, size_type pos) const
-  noexcept
-{
-  if (size_ >= v.size_) {
-    for (pos = std::min(pos, size_ - v.size_) + 1; pos > 0;) {
-      --pos;
-      if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) ==
-          0) {
-        return pos;
-      }
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::rfind(char c, size_type pos) const noexcept
-{
-  return rfind(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::rfind(const char* s, size_type pos,
-                                          size_type count) const
-{
-  return rfind(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::rfind(const char* s, size_type pos) const
-{
-  return rfind(string_view(s), pos);
-}
-
-string_view::size_type string_view::find_first_of(string_view v,
-                                                  size_type pos) const noexcept
-{
-  for (; pos < size_; ++pos) {
-    if (traits_type::find(v.data_, v.size_, data_[pos])) {
-      return pos;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_of(char c, size_type pos) const
-  noexcept
-{
-  return find_first_of(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::find_first_of(const char* s, size_type pos,
-                                                  size_type count) const
-{
-  return find_first_of(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::find_first_of(const char* s,
-                                                  size_type pos) const
-{
-  return find_first_of(string_view(s), pos);
-}
-
-string_view::size_type string_view::find_last_of(string_view v,
-                                                 size_type pos) const noexcept
-{
-  if (size_ > 0) {
-    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
-      --pos;
-      if (traits_type::find(v.data_, v.size_, data_[pos])) {
-        return pos;
-      }
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_of(char c, size_type pos) const
-  noexcept
-{
-  return find_last_of(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::find_last_of(const char* s, size_type pos,
-                                                 size_type count) const
-{
-  return find_last_of(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::find_last_of(const char* s,
-                                                 size_type pos) const
-{
-  return find_last_of(string_view(s), pos);
-}
-
-string_view::size_type string_view::find_first_not_of(string_view v,
-                                                      size_type pos) const
-  noexcept
-{
-  for (; pos < size_; ++pos) {
-    if (!traits_type::find(v.data_, v.size_, data_[pos])) {
-      return pos;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_not_of(char c,
-                                                      size_type pos) const
-  noexcept
-{
-  return find_first_not_of(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::find_first_not_of(const char* s,
-                                                      size_type pos,
-                                                      size_type count) const
-{
-  return find_first_not_of(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::find_first_not_of(const char* s,
-                                                      size_type pos) const
-{
-  return find_first_not_of(string_view(s), pos);
-}
-
-string_view::size_type string_view::find_last_not_of(string_view v,
-                                                     size_type pos) const
-  noexcept
-{
-  if (size_ > 0) {
-    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
-      --pos;
-      if (!traits_type::find(v.data_, v.size_, data_[pos])) {
-        return pos;
-      }
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_not_of(char c,
-                                                     size_type pos) const
-  noexcept
-{
-  return find_last_not_of(string_view(&c, 1), pos);
-}
-
-string_view::size_type string_view::find_last_not_of(const char* s,
-                                                     size_type pos,
-                                                     size_type count) const
-{
-  return find_last_not_of(string_view(s, count), pos);
-}
-
-string_view::size_type string_view::find_last_not_of(const char* s,
-                                                     size_type pos) const
-{
-  return find_last_not_of(string_view(s), pos);
-}
-
-std::ostream& operator<<(std::ostream& o, string_view v)
-{
-  return o.write(v.data(), v.size());
-}
-
-std::string& operator+=(std::string& s, string_view v)
-{
-  s.append(v.data(), v.size());
-  return s;
-}
-}
-
-std::hash<cm::string_view>::result_type std::hash<cm::string_view>::operator()(
-  argument_type const& s) const noexcept
-{
-  // FNV-1a hash.
-  static KWIML_INT_uint64_t const fnv_offset_basis = 0xcbf29ce484222325;
-  static KWIML_INT_uint64_t const fnv_prime = 0x100000001b3;
-  KWIML_INT_uint64_t h = fnv_offset_basis;
-  for (char const& c : s) {
-    h = h ^ KWIML_INT_uint64_t(KWIML_INT_uint8_t(c));
-    h = h * fnv_prime;
-  }
-  return result_type(h);
-}
-#else
-// Avoid empty translation unit.
-void cm_string_view_cxx()
-{
-}
-#endif
diff --git a/Source/cm_string_view.hxx b/Source/cm_string_view.hxx
deleted file mode 100644
index d368ed8..0000000
--- a/Source/cm_string_view.hxx
+++ /dev/null
@@ -1,217 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cm_string_view_hxx
-#define cm_string_view_hxx
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
-#  define CMake_HAVE_CXX_STRING_VIEW
-#endif
-
-#ifdef CMake_HAVE_CXX_STRING_VIEW
-#  include <string_view>
-namespace cm {
-using std::string_view;
-}
-#else
-#  include <cstddef>
-#  include <functional>
-#  include <iosfwd>
-#  include <iterator>
-#  include <string>
-
-namespace cm {
-
-class string_view
-{
-public:
-  using traits_type = std::string::traits_type;
-  using value_type = char;
-  using pointer = char*;
-  using const_pointer = const char*;
-  using reference = char&;
-  using const_reference = char const&;
-  using const_iterator = const char*;
-  using iterator = const_iterator;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using reverse_iterator = const_reverse_iterator;
-  using size_type = std::string::size_type;
-  using difference_type = std::string::difference_type;
-
-  static size_type const npos = static_cast<size_type>(-1);
-
-  string_view() noexcept = default;
-  string_view(string_view const&) noexcept = default;
-
-  string_view(const char* s, size_t count) noexcept
-    : data_(s)
-    , size_(count)
-  {
-  }
-
-  string_view(const char* s) noexcept
-    : data_(s)
-    , size_(traits_type::length(s))
-  {
-  }
-
-  // C++17 does not define this constructor.  Instead it defines
-  // a conversion operator on std::string to create a string_view.
-  // Since this implementation is used in C++11, std::string does
-  // not have that conversion.
-  string_view(std::string const& s) noexcept
-    : data_(s.data())
-    , size_(s.size())
-  {
-  }
-
-  // C++17 does not define this conversion.  Instead it defines
-  // a constructor on std::string that can take a string_view.
-  // Since this implementation is used in C++11, std::string does
-  // not have that constructor.
-  explicit operator std::string() const { return std::string(data_, size_); }
-
-  string_view& operator=(string_view const&) = default;
-
-  const_iterator begin() const noexcept { return data_; }
-  const_iterator end() const noexcept { return data_ + size_; }
-  const_iterator cbegin() const noexcept { return begin(); }
-  const_iterator cend() const noexcept { return end(); }
-
-  const_reverse_iterator rbegin() const noexcept
-  {
-    return const_reverse_iterator(end());
-  }
-  const_reverse_iterator rend() const noexcept
-  {
-    return const_reverse_iterator(begin());
-  }
-  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-  const_reverse_iterator crend() const noexcept { return rend(); }
-
-  const_reference operator[](size_type pos) const noexcept
-  {
-    return data_[pos];
-  }
-  const_reference at(size_type pos) const;
-  const_reference front() const noexcept { return data_[0]; }
-  const_reference back() const noexcept { return data_[size_ - 1]; }
-  const_pointer data() const noexcept { return data_; }
-
-  size_type size() const noexcept { return size_; }
-  size_type length() const noexcept { return size_; }
-  size_type max_size() const noexcept { return npos - 1; }
-  bool empty() const noexcept { return size_ == 0; }
-
-  void remove_prefix(size_type n) noexcept
-  {
-    data_ += n;
-    size_ -= n;
-  }
-  void remove_suffix(size_type n) noexcept { size_ -= n; }
-  void swap(string_view& v) noexcept
-  {
-    string_view tmp = v;
-    v = *this;
-    *this = tmp;
-  }
-
-  size_type copy(char* dest, size_type count, size_type pos = 0) const;
-  string_view substr(size_type pos = 0, size_type count = npos) const;
-
-  int compare(string_view v) const noexcept;
-  int compare(size_type pos1, size_type count1, string_view v) const;
-  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
-              size_type count2) const;
-  int compare(const char* s) const;
-  int compare(size_type pos1, size_type count1, const char* s) const;
-  int compare(size_type pos1, size_type count1, const char* s,
-              size_type count2) const;
-
-  size_type find(string_view v, size_type pos = 0) const noexcept;
-  size_type find(char c, size_type pos = 0) const noexcept;
-  size_type find(const char* s, size_type pos, size_type count) const;
-  size_type find(const char* s, size_type pos = 0) const;
-
-  size_type rfind(string_view v, size_type pos = npos) const noexcept;
-  size_type rfind(char c, size_type pos = npos) const noexcept;
-  size_type rfind(const char* s, size_type pos, size_type count) const;
-  size_type rfind(const char* s, size_type pos = npos) const;
-
-  size_type find_first_of(string_view v, size_type pos = 0) const noexcept;
-  size_type find_first_of(char c, size_type pos = 0) const noexcept;
-  size_type find_first_of(const char* s, size_type pos, size_type count) const;
-  size_type find_first_of(const char* s, size_type pos = 0) const;
-
-  size_type find_last_of(string_view v, size_type pos = npos) const noexcept;
-  size_type find_last_of(char c, size_type pos = npos) const noexcept;
-  size_type find_last_of(const char* s, size_type pos, size_type count) const;
-  size_type find_last_of(const char* s, size_type pos = npos) const;
-
-  size_type find_first_not_of(string_view v, size_type pos = 0) const noexcept;
-  size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
-  size_type find_first_not_of(const char* s, size_type pos,
-                              size_type count) const;
-  size_type find_first_not_of(const char* s, size_type pos = 0) const;
-
-  size_type find_last_not_of(string_view v, size_type pos = npos) const
-    noexcept;
-  size_type find_last_not_of(char c, size_type pos = npos) const noexcept;
-  size_type find_last_not_of(const char* s, size_type pos,
-                             size_type count) const;
-  size_type find_last_not_of(const char* s, size_type pos = npos) const;
-
-private:
-  const char* data_ = nullptr;
-  size_type size_ = 0;
-};
-
-std::ostream& operator<<(std::ostream& o, string_view v);
-
-std::string& operator+=(std::string& s, string_view v);
-
-inline bool operator==(string_view l, string_view r) noexcept
-{
-  return l.compare(r) == 0;
-}
-
-inline bool operator!=(string_view l, string_view r) noexcept
-{
-  return l.compare(r) != 0;
-}
-
-inline bool operator<(string_view l, string_view r) noexcept
-{
-  return l.compare(r) < 0;
-}
-
-inline bool operator<=(string_view l, string_view r) noexcept
-{
-  return l.compare(r) <= 0;
-}
-
-inline bool operator>(string_view l, string_view r) noexcept
-{
-  return l.compare(r) > 0;
-}
-
-inline bool operator>=(string_view l, string_view r) noexcept
-{
-  return l.compare(r) >= 0;
-}
-}
-
-namespace std {
-
-template <>
-struct hash<cm::string_view>
-{
-  typedef cm::string_view argument_type;
-  typedef size_t result_type;
-  result_type operator()(argument_type const& s) const noexcept;
-};
-}
-
-#endif
-#endif
diff --git a/Source/cm_sys_stat.h b/Source/cm_sys_stat.h
index 796f027..9194286 100644
--- a/Source/cm_sys_stat.h
+++ b/Source/cm_sys_stat.h
@@ -4,16 +4,11 @@
 #define cm_sys_stat_h
 
 #if defined(_MSC_VER)
-typedef unsigned short mode_t;
-#endif
-
-#if defined(WIN32)
-typedef unsigned short uid_t;
-typedef unsigned short gid_t;
+using mode_t = unsigned short;
 #endif
 
 #include <sys/types.h>
 // include sys/stat.h after sys/types.h
-#include <sys/stat.h>
+#include <sys/stat.h> // IWYU pragma: export
 
 #endif
diff --git a/Source/cm_thread.hxx b/Source/cm_thread.hxx
deleted file mode 100644
index b1f0645..0000000
--- a/Source/cm_thread.hxx
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef CM_THREAD_HXX
-#define CM_THREAD_HXX
-
-#include "cmConfigure.h" // IWYU pragma: keep
-#include "cm_uv.h"
-
-namespace cm {
-
-class shared_mutex
-{
-  uv_rwlock_t _M_;
-
-public:
-  shared_mutex() { uv_rwlock_init(&_M_); }
-  ~shared_mutex() { uv_rwlock_destroy(&_M_); }
-
-  shared_mutex(shared_mutex const&) = delete;
-  shared_mutex& operator=(shared_mutex const&) = delete;
-
-  void lock() { uv_rwlock_wrlock(&_M_); }
-  void unlock() { uv_rwlock_wrunlock(&_M_); }
-
-  void lock_shared() { uv_rwlock_rdlock(&_M_); }
-  void unlock_shared() { uv_rwlock_rdunlock(&_M_); }
-};
-
-template <typename T>
-class shared_lock
-{
-  T& _mutex;
-
-public:
-  shared_lock(T& m)
-    : _mutex(m)
-  {
-    _mutex.lock_shared();
-  }
-
-  ~shared_lock() { _mutex.unlock_shared(); }
-
-  shared_lock(shared_lock const&) = delete;
-  shared_lock& operator=(shared_lock const&) = delete;
-};
-}
-
-#endif
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 3772f09..50f47af 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2,6 +2,14 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmake.h"
 
+#include <cm/memory>
+#include <cm/string_view>
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
+#  include <cm/iterator>
+#endif
+
+#include "cm_sys_stat.h"
+
 #include "cmAlgorithms.h"
 #include "cmCommands.h"
 #include "cmDocumentation.h"
@@ -19,28 +27,29 @@
 #include "cmMessenger.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
 #include "cmUtils.hxx"
 #include "cmVersionConfig.h"
 #include "cmWorkingDirectory.h"
-#include "cm_sys_stat.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
+#  include <unordered_map>
+
 #  include "cm_jsoncpp_writer.h"
 
 #  include "cmFileAPI.h"
 #  include "cmGraphVizWriter.h"
 #  include "cmVariableWatch.h"
-#  include <unordered_map>
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  define CMAKE_USE_ECLIPSE
 #endif
 
-#if defined(__MINGW32__) && !defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(__MINGW32__) && defined(CMAKE_BOOTSTRAP)
 #  define CMAKE_BOOT_MINGW
 #endif
 
@@ -68,7 +77,7 @@
 #  include "cmGlobalWatcomWMakeGenerator.h"
 #endif
 #include "cmGlobalUnixMakefileGenerator3.h"
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmGlobalNinjaGenerator.h"
 #endif
 #include "cmExtraCodeLiteGenerator.h"
@@ -88,7 +97,7 @@
 #endif
 
 #if defined(__APPLE__)
-#  if defined(CMAKE_BUILD_WITH_CMAKE)
+#  if !defined(CMAKE_BOOTSTRAP)
 #    include "cmGlobalXCodeGenerator.h"
 
 #    define CMAKE_USE_XCODE 1
@@ -97,24 +106,23 @@
 #  include <sys/time.h>
 #endif
 
-#include "cmsys/FStream.hxx"
-#include "cmsys/Glob.hxx"
-#include "cmsys/RegularExpression.hxx"
 #include <algorithm>
+#include <cstdio>
+#include <cstdlib>
 #include <cstring>
 #include <initializer_list>
 #include <iostream>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
 #include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
 #include <utility>
 
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
+#include "cmsys/RegularExpression.hxx"
+
 namespace {
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-typedef std::unordered_map<std::string, Json::Value> JsonValueMapType;
+#if !defined(CMAKE_BOOTSTRAP)
+using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
 #endif
 
 } // namespace
@@ -122,31 +130,25 @@
 static bool cmakeCheckStampFile(const std::string& stampName);
 static bool cmakeCheckStampList(const std::string& stampList);
 
-void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
-                            void* ctx, const char* /*unused*/,
-                            const cmMakefile* /*unused*/)
+static void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
+                                   void* ctx, const char* /*unused*/,
+                                   const cmMakefile* /*unused*/)
 {
   cmake* cm = reinterpret_cast<cmake*>(ctx);
   cm->MarkCliAsUsed(variable);
 }
 
 cmake::cmake(Role role, cmState::Mode mode)
+  : FileTimeCache(cm::make_unique<cmFileTimeCache>())
+#ifndef CMAKE_BOOTSTRAP
+  , 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 = new cmFileTimeCache;
-
-  this->State = new cmState;
+  this->TraceFile.close();
   this->State->SetMode(mode);
   this->CurrentSnapshot = this->State->CreateBaseSnapshot();
-  this->Messenger = new cmMessenger;
 
 #ifdef __APPLE__
   struct rlimit rlp;
@@ -158,16 +160,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 = new cmVariableWatch;
-#endif
-
   this->AddDefaultGenerators();
   this->AddDefaultExtraGenerators();
   if (role == RoleScript || role == RoleProject) {
@@ -184,58 +176,41 @@
   // Make sure we can capture the build tool output.
   cmSystemTools::EnableVSConsoleOutput();
 
-  // Set up a list of source and header extensions
-  // these are used to find files when the extension
-  // is not given
-  // The "c" extension MUST precede the "C" extension.
-  this->SourceFileExtensions.emplace_back("c");
-  this->SourceFileExtensions.emplace_back("C");
+  // Set up a list of source and header extensions.
+  // These are used to find files when the extension is not given.
+  {
+    auto setupExts = [](FileExtensions& exts,
+                        std::initializer_list<cm::string_view> extList) {
+      // Fill ordered vector
+      exts.ordered.reserve(extList.size());
+      for (cm::string_view ext : extList) {
+        exts.ordered.emplace_back(ext);
+      };
+      // Fill unordered set
+      exts.unordered.insert(exts.ordered.begin(), exts.ordered.end());
+    };
 
-  this->SourceFileExtensions.emplace_back("c++");
-  this->SourceFileExtensions.emplace_back("cc");
-  this->SourceFileExtensions.emplace_back("cpp");
-  this->SourceFileExtensions.emplace_back("cxx");
-  this->SourceFileExtensions.emplace_back("cu");
-  this->SourceFileExtensions.emplace_back("m");
-  this->SourceFileExtensions.emplace_back("M");
-  this->SourceFileExtensions.emplace_back("mm");
-
-  std::copy(this->SourceFileExtensions.begin(),
-            this->SourceFileExtensions.end(),
-            std::inserter(this->SourceFileExtensionsSet,
-                          this->SourceFileExtensionsSet.end()));
-
-  this->HeaderFileExtensions.emplace_back("h");
-  this->HeaderFileExtensions.emplace_back("hh");
-  this->HeaderFileExtensions.emplace_back("h++");
-  this->HeaderFileExtensions.emplace_back("hm");
-  this->HeaderFileExtensions.emplace_back("hpp");
-  this->HeaderFileExtensions.emplace_back("hxx");
-  this->HeaderFileExtensions.emplace_back("in");
-  this->HeaderFileExtensions.emplace_back("txx");
-
-  std::copy(this->HeaderFileExtensions.begin(),
-            this->HeaderFileExtensions.end(),
-            std::inserter(this->HeaderFileExtensionsSet,
-                          this->HeaderFileExtensionsSet.end()));
+    // The "c" extension MUST precede the "C" extension.
+    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" });
+  }
 }
 
 cmake::~cmake()
 {
-  delete this->State;
-  delete this->Messenger;
   if (this->GlobalGenerator) {
     delete this->GlobalGenerator;
     this->GlobalGenerator = nullptr;
   }
   cmDeleteAll(this->Generators);
-#ifdef CMAKE_BUILD_WITH_CMAKE
-  delete this->VariableWatch;
-#endif
-  delete this->FileTimeCache;
 }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 Json::Value cmake::ReportVersionJson() const
 {
   Json::Value version = Json::objectValue;
@@ -294,7 +269,7 @@
 std::string cmake::ReportCapabilities() const
 {
   std::string result;
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   Json::FastWriter writer;
   result = writer.write(this->ReportCapabilitiesJson());
 #else
@@ -327,7 +302,8 @@
           return false;
         }
       }
-      std::string var, value;
+      std::string var;
+      std::string value;
       cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
       if (cmState::ParseCacheEntry(entry, var, value, type)) {
         // The value is transformed if it is a filepath for example, so
@@ -460,7 +436,12 @@
         return false;
       }
       // Register fake project commands that hint misuse in script mode.
-      GetProjectCommandsInScriptMode(this->State);
+      GetProjectCommandsInScriptMode(this->GetState());
+      // Documented behaviour of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
+      // set to $PWD for -P mode.
+      this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
+      this->SetHomeOutputDirectory(
+        cmSystemTools::GetCurrentWorkingDirectory());
       this->ReadListFile(args, path);
     } else if (arg.find("--find-package", 0) == 0) {
       findPackageMode = true;
@@ -490,15 +471,9 @@
   // read in the list file to fill the cache
   if (!path.empty()) {
     this->CurrentSnapshot = this->State->Reset();
-    std::string homeDir = this->GetHomeDirectory();
-    std::string homeOutputDir = this->GetHomeOutputDirectory();
-    this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
-    this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
     cmStateSnapshot snapshot = this->GetCurrentSnapshot();
-    snapshot.GetDirectory().SetCurrentBinary(
-      cmSystemTools::GetCurrentWorkingDirectory());
-    snapshot.GetDirectory().SetCurrentSource(
-      cmSystemTools::GetCurrentWorkingDirectory());
+    snapshot.GetDirectory().SetCurrentBinary(this->GetHomeOutputDirectory());
+    snapshot.GetDirectory().SetCurrentSource(this->GetHomeDirectory());
     snapshot.SetDefaultDefinitions();
     cmMakefile mf(gg, snapshot);
     if (this->GetWorkingMode() != NORMAL_MODE) {
@@ -511,8 +486,6 @@
     if (!mf.ReadListFile(path)) {
       cmSystemTools::Error("Error processing file: " + path);
     }
-    this->SetHomeDirectory(homeDir);
-    this->SetHomeOutputDirectory(homeOutputDir);
   }
 
   // free generic one if generated
@@ -561,8 +534,7 @@
     }
   } else if (mode == "COMPILE") {
     std::string includes = mf->GetSafeDefinition("PACKAGE_INCLUDE_DIRS");
-    std::vector<std::string> includeDirs;
-    cmSystemTools::ExpandListArgument(includes, includeDirs);
+    std::vector<std::string> includeDirs = cmExpandedList(includes);
 
     gg->CreateGenerationObjects();
     cmLocalGenerator* lg = gg->LocalGenerators[0];
@@ -578,8 +550,7 @@
     tgt->SetProperty("LINKER_LANGUAGE", language.c_str());
 
     std::string libs = mf->GetSafeDefinition("PACKAGE_LIBRARIES");
-    std::vector<std::string> libList;
-    cmSystemTools::ExpandListArgument(libs, libList);
+    std::vector<std::string> libList = cmExpandedList(libs);
     for (std::string const& lib : libList) {
       tgt->AddLinkLibrary(*mf, lib, GENERAL_LibraryType);
     }
@@ -629,16 +600,15 @@
     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) {
         key = varValue;
       } else if (!this->GetIsInTryCompile()) {
-        std::string message = "Warning: Environment variable ";
-        message += name;
-        message += " will be ignored, because CMAKE_GENERATOR ";
-        message += "is not set.";
+        std::string message =
+          cmStrCat("Warning: Environment variable ", name,
+                   " will be ignored, because CMAKE_GENERATOR is not set.");
         cmSystemTools::Message(message, "Warning");
       }
     }
@@ -755,7 +725,18 @@
     } else if (arg.find("--debug-output", 0) == 0) {
       std::cout << "Running with debug output on.\n";
       this->SetDebugOutputOn(true);
+    } else if (arg.find("--log-level=", 0) == 0) {
+      const auto logLevel =
+        StringToLogLevel(arg.substr(sizeof("--log-level=") - 1));
+      if (logLevel == LogLevel::LOG_UNDEFINED) {
+        cmSystemTools::Error("Invalid level specified for --log-level");
+        return;
+      }
+      this->SetLogLevel(logLevel);
     } else if (arg.find("--loglevel=", 0) == 0) {
+      // This is supported for backward compatibility. This option only
+      // appeared in the 3.15.x release series and was renamed to
+      // --log-level in 3.16.0
       const auto logLevel =
         StringToLogLevel(arg.substr(sizeof("--loglevel=") - 1));
       if (logLevel == LogLevel::LOG_UNDEFINED) {
@@ -772,6 +753,11 @@
       cmSystemTools::ConvertToUnixSlashes(file);
       this->AddTraceSource(file);
       this->SetTrace(true);
+    } else if (arg.find("--trace-redirect=", 0) == 0) {
+      std::string file = arg.substr(strlen("--trace-redirect="));
+      cmSystemTools::ConvertToUnixSlashes(file);
+      this->SetTraceFile(file);
+      this->SetTrace(true);
     } else if (arg.find("--trace", 0) == 0) {
       std::cout << "Running with trace output on.\n";
       this->SetTrace(true);
@@ -840,8 +826,8 @@
           kdevError = "\nThe KDevelop3 generator is not supported anymore.";
         }
 
-        cmSystemTools::Error("Could not create named generator " + value +
-                             kdevError);
+        cmSystemTools::Error(
+          cmStrCat("Could not create named generator ", value, kdevError));
         this->PrintGeneratorList();
         return;
       }
@@ -902,6 +888,20 @@
   return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED;
 }
 
+void cmake::SetTraceFile(const std::string& file)
+{
+  this->TraceFile.close();
+  this->TraceFile.open(file.c_str());
+  if (!this->TraceFile) {
+    std::stringstream ss;
+    ss << "Error opening trace file " << file << ": "
+       << cmSystemTools::GetLastSystemError();
+    cmSystemTools::Error(ss.str());
+    return;
+  }
+  std::cout << "Trace will be written to " << file << "\n";
+}
+
 void cmake::SetDirectoriesFromFile(const std::string& arg)
 {
   // Check if the argument refers to a CMakeCache.txt or
@@ -912,10 +912,8 @@
   if (cmSystemTools::FileIsDirectory(arg)) {
     std::string path = cmSystemTools::CollapseFullPath(arg);
     cmSystemTools::ConvertToUnixSlashes(path);
-    std::string cacheFile = path;
-    cacheFile += "/CMakeCache.txt";
-    std::string listFile = path;
-    listFile += "/CMakeLists.txt";
+    std::string cacheFile = cmStrCat(path, "/CMakeCache.txt");
+    std::string listFile = cmStrCat(path, "/CMakeLists.txt");
     if (cmSystemTools::FileExists(cacheFile)) {
       cachePath = path;
     }
@@ -1000,7 +998,7 @@
   this->AddCacheEntry("CMAKE_COMMAND",
                       cmSystemTools::GetCMakeCommand().c_str(),
                       "Path to CMake executable.", cmStateEnums::INTERNAL);
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   this->AddCacheEntry(
     "CMAKE_CTEST_COMMAND", cmSystemTools::GetCTestCommand().c_str(),
     "Path to ctest program executable.", cmStateEnums::INTERNAL);
@@ -1026,7 +1024,7 @@
 
 void cmake::AddDefaultExtraGenerators()
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->ExtraGenerators.push_back(cmExtraCodeBlocksGenerator::GetFactory());
   this->ExtraGenerators.push_back(cmExtraCodeLiteGenerator::GetFactory());
   this->ExtraGenerators.push_back(cmExtraSublimeTextGenerator::GetFactory());
@@ -1100,20 +1098,18 @@
     const std::vector<std::string> generators =
       i->GetSupportedGlobalGenerators();
     if (i->GetName() == name) { // Match aliases
-      return std::make_pair(i->CreateExternalMakefileProjectGenerator(),
-                            generators.at(0));
+      return { i->CreateExternalMakefileProjectGenerator(), generators.at(0) };
     }
     for (std::string const& g : generators) {
       const std::string fullName =
         cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
           g, i->GetName());
       if (fullName == name) {
-        return std::make_pair(i->CreateExternalMakefileProjectGenerator(), g);
+        return { i->CreateExternalMakefileProjectGenerator(), g };
       }
     }
   }
-  return std::make_pair(
-    static_cast<cmExternalMakefileProjectGenerator*>(nullptr), name);
+  return { nullptr, name };
 }
 
 cmGlobalGenerator* cmake::CreateGlobalGenerator(const std::string& gname)
@@ -1170,12 +1166,10 @@
 {
   std::string cachePath = binaryDir;
   cmSystemTools::ConvertToUnixSlashes(cachePath);
-  std::string cacheFile = cachePath;
-  cacheFile += "/CMakeCache.txt";
+  std::string cacheFile = cmStrCat(cachePath, "/CMakeCache.txt");
   if (!cmSystemTools::FileExists(cacheFile)) {
     // search in parent directories for cache
-    std::string cmakeFiles = cachePath;
-    cmakeFiles += "/CMakeFiles";
+    std::string cmakeFiles = cmStrCat(cachePath, "/CMakeFiles");
     if (cmSystemTools::FileExists(cmakeFiles)) {
       std::string cachePathFound =
         cmSystemTools::FileExistsInParentDirectories("CMakeCache.txt",
@@ -1230,8 +1224,7 @@
 int cmake::DoPreConfigureChecks()
 {
   // Make sure the Source directory contains a CMakeLists.txt file.
-  std::string srcList = this->GetHomeDirectory();
-  srcList += "/CMakeLists.txt";
+  std::string srcList = cmStrCat(this->GetHomeDirectory(), "/CMakeLists.txt");
   if (!cmSystemTools::FileExists(srcList)) {
     std::ostringstream err;
     if (cmSystemTools::FileIsDirectory(this->GetHomeDirectory())) {
@@ -1253,17 +1246,16 @@
   // do a sanity check on some values
   if (this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY")) {
     std::string cacheStart =
-      *this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY");
-    cacheStart += "/CMakeLists.txt";
-    std::string currentStart = this->GetHomeDirectory();
-    currentStart += "/CMakeLists.txt";
+      cmStrCat(*this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"),
+               "/CMakeLists.txt");
+    std::string currentStart =
+      cmStrCat(this->GetHomeDirectory(), "/CMakeLists.txt");
     if (!cmSystemTools::SameFile(cacheStart, currentStart)) {
-      std::string message = "The source \"";
-      message += currentStart;
-      message += "\" does not match the source \"";
-      message += cacheStart;
-      message += "\" used to generate cache.  ";
-      message += "Re-run cmake with a different source directory.";
+      std::string message =
+        cmStrCat("The source \"", currentStart,
+                 "\" does not match the source \"", cacheStart,
+                 "\" used to generate cache.  Re-run cmake with a different "
+                 "source directory.");
       cmSystemTools::Error(message);
       return -2;
     }
@@ -1282,8 +1274,7 @@
 
 int cmake::HandleDeleteCacheVariables(const std::string& var)
 {
-  std::vector<std::string> argsSplit;
-  cmSystemTools::ExpandListArgument(std::string(var), argsSplit, true);
+  std::vector<std::string> argsSplit = cmExpandedList(std::string(var), true);
   // erase the property to avoid infinite recursion
   this->State->SetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_", "");
   if (this->State->GetIsInTryCompile()) {
@@ -1297,8 +1288,7 @@
     << "Configure will be re-run and you may have to reset some variables.\n"
     << "The following variables have changed:\n";
   /* clang-format on */
-  for (std::vector<std::string>::iterator i = argsSplit.begin();
-       i != argsSplit.end(); ++i) {
+  for (auto i = argsSplit.begin(); i != argsSplit.end(); ++i) {
     SaveCacheEntry save;
     save.key = *i;
     warning << *i << "= ";
@@ -1397,18 +1387,16 @@
   // so we cannot rely on command line options alone. Always ensure our
   // messenger is in sync with the cache.
   const char* value = this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
-  this->Messenger->SetSuppressDeprecatedWarnings(value &&
-                                                 cmSystemTools::IsOff(value));
+  this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(value));
 
   value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
-  this->Messenger->SetDeprecatedWarningsAsErrors(cmSystemTools::IsOn(value));
+  this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value));
 
   value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS");
-  this->Messenger->SetSuppressDevWarnings(cmSystemTools::IsOn(value));
+  this->Messenger->SetSuppressDevWarnings(cmIsOn(value));
 
   value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS");
-  this->Messenger->SetDevWarningsAsErrors(value &&
-                                          cmSystemTools::IsOff(value));
+  this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(value));
 
   int ret = this->ActualConfigure();
   const char* delCacheVars =
@@ -1468,12 +1456,11 @@
     this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
   if (genName) {
     if (!this->GlobalGenerator->MatchesGeneratorName(*genName)) {
-      std::string message = "Error: generator : ";
-      message += this->GlobalGenerator->GetName();
-      message += "\nDoes not match the generator used previously: ";
-      message += *genName;
-      message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
-                 "directory or choose a different binary directory.";
+      std::string message =
+        cmStrCat("Error: generator : ", this->GlobalGenerator->GetName(),
+                 "\nDoes not match the generator used previously: ", *genName,
+                 "\nEither remove the CMakeCache.txt file and CMakeFiles "
+                 "directory or choose a different binary directory.");
       cmSystemTools::Error(message);
       return -2;
     }
@@ -1491,12 +1478,11 @@
   if (const std::string* instance =
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_INSTANCE")) {
     if (this->GeneratorInstanceSet && this->GeneratorInstance != *instance) {
-      std::string message = "Error: generator instance: ";
-      message += this->GeneratorInstance;
-      message += "\nDoes not match the instance used previously: ";
-      message += *instance;
-      message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
-                 "directory or choose a different binary directory.";
+      std::string message =
+        cmStrCat("Error: generator instance: ", this->GeneratorInstance,
+                 "\nDoes not match the instance used previously: ", *instance,
+                 "\nEither remove the CMakeCache.txt file and CMakeFiles "
+                 "directory or choose a different binary directory.");
       cmSystemTools::Error(message);
       return -2;
     }
@@ -1510,12 +1496,11 @@
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_PLATFORM")) {
     if (this->GeneratorPlatformSet &&
         this->GeneratorPlatform != *platformName) {
-      std::string message = "Error: generator platform: ";
-      message += this->GeneratorPlatform;
-      message += "\nDoes not match the platform used previously: ";
-      message += *platformName;
-      message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
-                 "directory or choose a different binary directory.";
+      std::string message = cmStrCat(
+        "Error: generator platform: ", this->GeneratorPlatform,
+        "\nDoes not match the platform used previously: ", *platformName,
+        "\nEither remove the CMakeCache.txt file and CMakeFiles "
+        "directory or choose a different binary directory.");
       cmSystemTools::Error(message);
       return -2;
     }
@@ -1528,12 +1513,11 @@
   if (const std::string* tsName =
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_TOOLSET")) {
     if (this->GeneratorToolsetSet && this->GeneratorToolset != *tsName) {
-      std::string message = "Error: generator toolset: ";
-      message += this->GeneratorToolset;
-      message += "\nDoes not match the toolset used previously: ";
-      message += *tsName;
-      message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
-                 "directory or choose a different binary directory.";
+      std::string message =
+        cmStrCat("Error: generator toolset: ", this->GeneratorToolset,
+                 "\nDoes not match the toolset used previously: ", *tsName,
+                 "\nEither remove the CMakeCache.txt file and CMakeFiles "
+                 "directory or choose a different binary directory.");
       cmSystemTools::Error(message);
       return -2;
     }
@@ -1553,7 +1537,7 @@
     this->TruncateOutputLog("CMakeError.log");
   }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->FileAPI = cm::make_unique<cmFileAPI>(this);
   this->FileAPI->ReadQueries();
 #endif
@@ -1780,7 +1764,7 @@
       cmGlobalVisualStudioGenerator* gg =
         static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator);
       gg->CallVisualStudioMacro(cmGlobalVisualStudioGenerator::MacroStop,
-                                this->VSSolutionFile.c_str());
+                                this->VSSolutionFile);
     }
 #endif
     return ret;
@@ -1791,8 +1775,8 @@
                            "Build files cannot be regenerated correctly.");
     return ret;
   }
-  std::string message = "Build files have been written to: ";
-  message += this->GetHomeOutputDirectory();
+  std::string message = cmStrCat("Build files have been written to: ",
+                                 this->GetHomeOutputDirectory());
   this->UpdateProgress(message, -1);
   return ret;
 }
@@ -1821,7 +1805,7 @@
   // for the Visual Studio and Xcode generators.)
   this->SaveCache(this->GetHomeOutputDirectory());
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   this->FileAPI->WriteReplies();
 #endif
 
@@ -1836,15 +1820,13 @@
   this->UnwatchUnusedCli(key);
 
   if (key == "CMAKE_WARN_DEPRECATED") {
-    this->Messenger->SetSuppressDeprecatedWarnings(
-      value && cmSystemTools::IsOff(value));
+    this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(value));
   } else if (key == "CMAKE_ERROR_DEPRECATED") {
-    this->Messenger->SetDeprecatedWarningsAsErrors(cmSystemTools::IsOn(value));
+    this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value));
   } else if (key == "CMAKE_SUPPRESS_DEVELOPER_WARNINGS") {
-    this->Messenger->SetSuppressDevWarnings(cmSystemTools::IsOn(value));
+    this->Messenger->SetSuppressDevWarnings(cmIsOn(value));
   } else if (key == "CMAKE_SUPPRESS_DEVELOPER_ERRORS") {
-    this->Messenger->SetDevWarningsAsErrors(value &&
-                                            cmSystemTools::IsOff(value));
+    this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(value));
   }
 }
 
@@ -1898,12 +1880,12 @@
 
 void cmake::AddScriptingCommands()
 {
-  GetScriptingCommands(this->State);
+  GetScriptingCommands(this->GetState());
 }
 
 void cmake::AddProjectCommands()
 {
-  GetProjectCommands(this->State);
+  GetProjectCommands(this->GetState());
 }
 
 void cmake::AddDefaultGenerators()
@@ -1927,7 +1909,7 @@
   this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
 #endif
   this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  if defined(__linux__) || defined(_WIN32)
   this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
 #  endif
@@ -1953,8 +1935,8 @@
   // could we not read the cache
   if (!this->LoadCache(this->GetHomeOutputDirectory())) {
     // if it does exist, but isn't readable then warn the user
-    std::string cacheFile = this->GetHomeOutputDirectory();
-    cacheFile += "/CMakeCache.txt";
+    std::string cacheFile =
+      cmStrCat(this->GetHomeOutputDirectory(), "/CMakeCache.txt");
     if (cmSystemTools::FileExists(cacheFile)) {
       cmSystemTools::Error(
         "There is a CMakeCache.txt file for the current binary tree but "
@@ -2040,8 +2022,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;
     }
@@ -2087,7 +2068,7 @@
 
 void cmake::PrintGeneratorList()
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmDocumentation doc;
   auto generators = this->GetGeneratorsDocumentation();
   doc.AppendSection("Generators", generators);
@@ -2109,7 +2090,8 @@
                            ". CMake can not open file.");
       cmSystemTools::ReportLastSystemError("CMake can not open file.");
     } else {
-      std::string a, b;
+      std::string a;
+      std::string b;
       while (!table.eof()) {
         // two entries per line
         table >> a;
@@ -2132,9 +2114,7 @@
   // If no file is provided for the check, we have to rerun.
   if (this->CheckBuildSystemArgument.empty()) {
     if (verbose) {
-      std::ostringstream msg;
-      msg << "Re-run cmake no build system arguments\n";
-      cmSystemTools::Stdout(msg.str());
+      cmSystemTools::Stdout("Re-run cmake no build system arguments\n");
     }
     return 1;
   }
@@ -2192,7 +2172,7 @@
   // If any byproduct of makefile generation is missing we must re-run.
   std::vector<std::string> products;
   if (const char* productStr = mf.GetDefinition("CMAKE_MAKEFILE_PRODUCTS")) {
-    cmSystemTools::ExpandListArgument(productStr, products);
+    cmExpandList(productStr, products);
   }
   for (std::string const& p : products) {
     if (!(cmSystemTools::FileExists(p) || cmSystemTools::FileIsSymlink(p))) {
@@ -2211,22 +2191,20 @@
   const char* dependsStr = mf.GetDefinition("CMAKE_MAKEFILE_DEPENDS");
   const char* outputsStr = mf.GetDefinition("CMAKE_MAKEFILE_OUTPUTS");
   if (dependsStr && outputsStr) {
-    cmSystemTools::ExpandListArgument(dependsStr, depends);
-    cmSystemTools::ExpandListArgument(outputsStr, outputs);
+    cmExpandList(dependsStr, depends);
+    cmExpandList(outputsStr, outputs);
   }
   if (depends.empty() || outputs.empty()) {
     // Not enough information was provided to do the test.  Just rerun.
     if (verbose) {
-      std::ostringstream msg;
-      msg << "Re-run cmake no CMAKE_MAKEFILE_DEPENDS "
-             "or CMAKE_MAKEFILE_OUTPUTS :\n";
-      cmSystemTools::Stdout(msg.str());
+      cmSystemTools::Stdout("Re-run cmake no CMAKE_MAKEFILE_DEPENDS "
+                            "or CMAKE_MAKEFILE_OUTPUTS :\n");
     }
     return 1;
   }
 
   // Find the newest dependency.
-  std::vector<std::string>::iterator dep = depends.begin();
+  auto dep = depends.begin();
   std::string dep_newest = *dep++;
   for (; dep != depends.end(); ++dep) {
     int result = 0;
@@ -2236,16 +2214,15 @@
       }
     } else {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Re-run cmake: build system dependency is missing\n";
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(
+          "Re-run cmake: build system dependency is missing\n");
       }
       return 1;
     }
   }
 
   // Find the oldest output.
-  std::vector<std::string>::iterator out = outputs.begin();
+  auto out = outputs.begin();
   std::string out_oldest = *out++;
   for (; out != outputs.end(); ++out) {
     int result = 0;
@@ -2255,9 +2232,8 @@
       }
     } else {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Re-run cmake: build system output is missing\n";
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(
+          "Re-run cmake: build system output is missing\n");
       }
       return 1;
     }
@@ -2284,9 +2260,7 @@
 
 void cmake::TruncateOutputLog(const char* fname)
 {
-  std::string fullPath = this->GetHomeOutputDirectory();
-  fullPath += "/";
-  fullPath += fname;
+  std::string fullPath = cmStrCat(this->GetHomeOutputDirectory(), '/', fname);
   struct stat st;
   if (::stat(fullPath.c_str(), &st)) {
     return;
@@ -2303,14 +2277,6 @@
   }
 }
 
-inline std::string removeQuotes(const std::string& s)
-{
-  if (s.front() == '\"' && s.back() == '\"') {
-    return s.substr(1, s.size() - 2);
-  }
-  return s;
-}
-
 void cmake::MarkCliAsUsed(const std::string& variable)
 {
   this->UsedCliVariables[variable] = true;
@@ -2318,13 +2284,13 @@
 
 void cmake::GenerateGraphViz(const std::string& fileName) const
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmGraphVizWriter gvWriter(this->GetGlobalGenerator());
 
-  std::string settingsFile = this->GetHomeOutputDirectory();
-  settingsFile += "/CMakeGraphVizOptions.cmake";
-  std::string fallbackSettingsFile = this->GetHomeDirectory();
-  fallbackSettingsFile += "/CMakeGraphVizOptions.cmake";
+  std::string settingsFile =
+    cmStrCat(this->GetHomeOutputDirectory(), "/CMakeGraphVizOptions.cmake");
+  std::string fallbackSettingsFile =
+    cmStrCat(this->GetHomeDirectory(), "/CMakeGraphVizOptions.cmake");
 
   gvWriter.ReadSettings(settingsFile, fallbackSettingsFile);
   gvWriter.WritePerTargetFiles(fileName);
@@ -2358,8 +2324,7 @@
 cmInstalledFile* cmake::GetOrCreateInstalledFile(cmMakefile* mf,
                                                  const std::string& name)
 {
-  std::map<std::string, cmInstalledFile>::iterator i =
-    this->InstalledFiles.find(name);
+  auto i = this->InstalledFiles.find(name);
 
   if (i != this->InstalledFiles.end()) {
     cmInstalledFile& file = i->second;
@@ -2372,8 +2337,7 @@
 
 cmInstalledFile const* cmake::GetInstalledFile(const std::string& name) const
 {
-  std::map<std::string, cmInstalledFile>::const_iterator i =
-    this->InstalledFiles.find(name);
+  auto i = this->InstalledFiles.find(name);
 
   if (i != this->InstalledFiles.end()) {
     cmInstalledFile const& file = i->second;
@@ -2421,8 +2385,7 @@
     // no option assume it is the output file
     else {
       if (!cmSystemTools::FileIsFullPath(arg)) {
-        resultFile = cwd;
-        resultFile += "/";
+        resultFile = cmStrCat(cwd, '/');
       }
       resultFile += arg;
       writeToStdout = false;
@@ -2431,12 +2394,10 @@
 
   // we have to find the module directory, so we can copy the files
   this->AddCMakePaths();
-  std::string modulesPath = cmSystemTools::GetCMakeRoot();
-  modulesPath += "/Modules";
-  std::string inFile = modulesPath;
-  inFile += "/SystemInformation.cmake";
-  std::string outFile = destPath;
-  outFile += "/CMakeLists.txt";
+  std::string modulesPath =
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules");
+  std::string inFile = cmStrCat(modulesPath, "/SystemInformation.cmake");
+  std::string outFile = cmStrCat(destPath, "/CMakeLists.txt");
 
   // Copy file
   if (!cmsys::SystemTools::CopyFileAlways(inFile, outFile)) {
@@ -2447,8 +2408,7 @@
 
   // do we write to a file or to stdout?
   if (resultFile.empty()) {
-    resultFile = cwd;
-    resultFile += "/__cmake_systeminformation/results.txt";
+    resultFile = cmStrCat(cwd, "/__cmake_systeminformation/results.txt");
   }
 
   {
@@ -2504,8 +2464,7 @@
   // conjunction with cmLocalVisualStudio7Generator to avoid
   // repeatedly re-running CMake when the user rebuilds the entire
   // solution.
-  std::string stampDepends = stampName;
-  stampDepends += ".depend";
+  std::string stampDepends = cmStrCat(stampName, ".depend");
 #if defined(_WIN32) || defined(__CYGWIN__)
   cmsys::ifstream fin(stampDepends.c_str(), std::ios::in | std::ios::binary);
 #else
@@ -2596,7 +2555,7 @@
   if (const char* config_list =
         this->State->GetGlobalProperty("DEBUG_CONFIGURATIONS")) {
     // Expand the specified list and convert to upper-case.
-    cmSystemTools::ExpandListArgument(config_list, configs);
+    cmExpandList(config_list, configs);
     std::transform(configs.begin(), configs.end(), configs.begin(),
                    cmSystemTools::UpperCase);
   }
@@ -2607,11 +2566,6 @@
   return configs;
 }
 
-cmMessenger* cmake::GetMessenger() const
-{
-  return this->Messenger;
-}
-
 int cmake::Build(int jobs, const std::string& dir,
                  const std::vector<std::string>& targets,
                  const std::string& config,
@@ -2672,7 +2626,7 @@
 
   const char* cachedVerbose =
     this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE");
-  if (cmSystemTools::IsOn(cachedVerbose)) {
+  if (cmIsOn(cachedVerbose)) {
     verbose = true;
   }
 
@@ -2720,8 +2674,8 @@
                                "Build files cannot be regenerated correctly.");
         return ret;
       }
-      std::string message = "Build files have been written to: ";
-      message += this->GetHomeOutputDirectory();
+      std::string message = cmStrCat("Build files have been written to: ",
+                                     this->GetHomeOutputDirectory());
       this->UpdateProgress(message, -1);
 
       // Restore the previously set directories to their original value.
@@ -2782,9 +2736,9 @@
 
 void cmake::WatchUnusedCli(const std::string& var)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   this->VariableWatch->AddWatch(var, cmWarnUnusedCliWarning, this);
-  if (this->UsedCliVariables.find(var) == this->UsedCliVariables.end()) {
+  if (!cmContains(this->UsedCliVariables, var)) {
     this->UsedCliVariables[var] = false;
   }
 #endif
@@ -2792,7 +2746,7 @@
 
 void cmake::UnwatchUnusedCli(const std::string& var)
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   this->VariableWatch->RemoveWatch(var, cmWarnUnusedCliWarning);
   this->UsedCliVariables.erase(var);
 #endif
@@ -2800,7 +2754,7 @@
 
 void cmake::RunCheckForUnusedVariables()
 {
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   bool haveUnused = false;
   std::ostringstream msg;
   msg << "Manually-specified variables were not used by the project:";
diff --git a/Source/cmake.h b/Source/cmake.h
index fa4409a..687c105 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -7,12 +7,13 @@
 
 #include <functional>
 #include <map>
-#include <memory> // IWYU pragma: keep
+#include <memory>
 #include <set>
 #include <string>
 #include <unordered_set>
 #include <vector>
 
+#include "cmGeneratedFileStream.h"
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
@@ -20,7 +21,7 @@
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cm_jsoncpp_value.h"
 #endif
 
@@ -121,7 +122,18 @@
     bool isAlias;
   };
 
-  typedef std::map<std::string, cmInstalledFile> InstalledFilesMap;
+  struct FileExtensions
+  {
+    bool Test(std::string const& ext) const
+    {
+      return (this->unordered.find(ext) != this->unordered.end());
+    }
+
+    std::vector<std::string> ordered;
+    std::unordered_set<std::string> unordered;
+  };
+
+  using InstalledFilesMap = std::map<std::string, cmInstalledFile>;
 
   static const int NO_BUILD_PARALLEL_LEVEL = -1;
   static const int DEFAULT_BUILD_PARALLEL_LEVEL = 0;
@@ -134,7 +146,7 @@
   cmake(cmake const&) = delete;
   cmake& operator=(cmake const&) = delete;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   Json::Value ReportVersionJson() const;
   Json::Value ReportCapabilitiesJson() const;
 #endif
@@ -233,24 +245,42 @@
 
   const std::vector<std::string>& GetSourceExtensions() const
   {
-    return this->SourceFileExtensions;
+    return this->SourceFileExtensions.ordered;
   }
 
   bool IsSourceExtension(const std::string& ext) const
   {
-    return this->SourceFileExtensionsSet.find(ext) !=
-      this->SourceFileExtensionsSet.end();
+    return this->SourceFileExtensions.Test(ext);
   }
 
   const std::vector<std::string>& GetHeaderExtensions() const
   {
-    return this->HeaderFileExtensions;
+    return this->HeaderFileExtensions.ordered;
   }
 
   bool IsHeaderExtension(const std::string& ext) const
   {
-    return this->HeaderFileExtensionsSet.find(ext) !=
-      this->HeaderFileExtensionsSet.end();
+    return this->HeaderFileExtensions.Test(ext);
+  }
+
+  const std::vector<std::string>& GetCudaExtensions() const
+  {
+    return this->CudaFileExtensions.ordered;
+  }
+
+  bool IsCudaExtension(const std::string& ext) const
+  {
+    return this->CudaFileExtensions.Test(ext);
+  }
+
+  const std::vector<std::string>& GetFortranExtensions() const
+  {
+    return this->FortranFileExtensions.ordered;
+  }
+
+  bool IsFortranExtension(const std::string& ext) const
+  {
+    return this->FortranFileExtensions.Test(ext);
   }
 
   // Strips the extension (if present and known) from a filename
@@ -305,9 +335,9 @@
   //! this is called by generators to update the progress
   void UpdateProgress(const std::string& msg, float prog);
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
   //! Get the variable watch object
-  cmVariableWatch* GetVariableWatch() { return this->VariableWatch; }
+  cmVariableWatch* GetVariableWatch() { return this->VariableWatch.get(); }
 #endif
 
   std::vector<cmDocumentationEntry> GetGeneratorsDocumentation();
@@ -348,18 +378,18 @@
   /**
    * Get the file comparison class
    */
-  cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache; }
+  cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache.get(); }
 
-  // Get the selected log level for `message()` commands during the cmake run.
+  //! Get the selected log level for `message()` commands during the cmake run.
   LogLevel GetLogLevel() const { return this->MessageLogLevel; }
   void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; }
   static LogLevel StringToLogLevel(const std::string& levelStr);
 
-  // Do we want debug output during the cmake run.
+  //! Do we want debug output during the cmake run.
   bool GetDebugOutput() { return this->DebugOutput; }
   void SetDebugOutputOn(bool b) { this->DebugOutput = b; }
 
-  // Do we want trace output during the cmake run.
+  //! Do we want trace output during the cmake run.
   bool GetTrace() { return this->Trace; }
   void SetTrace(bool b) { this->Trace = b; }
   bool GetTraceExpand() { return this->TraceExpand; }
@@ -372,6 +402,9 @@
   {
     return this->TraceOnlyThisSources;
   }
+  cmGeneratedFileStream& GetTraceFile() { return this->TraceFile; }
+  void SetTraceFile(std::string const& file);
+
   bool GetWarnUninitialized() { return this->WarnUninitialized; }
   void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; }
   bool GetWarnUnused() { return this->WarnUnused; }
@@ -396,31 +429,31 @@
     return this->CMakeEditCommand;
   }
 
-  cmMessenger* GetMessenger() const;
+  cmMessenger* GetMessenger() const { return this->Messenger.get(); }
 
-  /*
+  /**
    * Get the state of the suppression of developer (author) warnings.
    * Returns false, by default, if developer warnings should be shown, true
    * otherwise.
    */
   bool GetSuppressDevWarnings() const;
-  /*
+  /**
    * Set the state of the suppression of developer (author) warnings.
    */
   void SetSuppressDevWarnings(bool v);
 
-  /*
+  /**
    * Get the state of the suppression of deprecated warnings.
    * Returns false, by default, if deprecated warnings should be shown, true
    * otherwise.
    */
   bool GetSuppressDeprecatedWarnings() const;
-  /*
+  /**
    * Set the state of the suppression of deprecated warnings.
    */
   void SetSuppressDeprecatedWarnings(bool v);
 
-  /*
+  /**
    * Get the state of treating developer (author) warnings as errors.
    * Returns false, by default, if warnings should not be treated as errors,
    * true otherwise.
@@ -431,7 +464,7 @@
    */
   void SetDevWarningsAsErrors(bool v);
 
-  /*
+  /**
    * Get the state of treating deprecated warnings as errors.
    * Returns false, by default, if warnings should not be treated as errors,
    * true otherwise.
@@ -459,7 +492,7 @@
   void UnwatchUnusedCli(const std::string& var);
   void WatchUnusedCli(const std::string& var);
 
-  cmState* GetState() const { return this->State; }
+  cmState* GetState() const { return this->State.get(); }
   void SetCurrentSnapshot(cmStateSnapshot const& snapshot)
   {
     this->CurrentSnapshot = snapshot;
@@ -470,24 +503,24 @@
   void RunCheckForUnusedVariables();
   int HandleDeleteCacheVariables(const std::string& var);
 
-  typedef std::vector<cmGlobalGeneratorFactory*> RegisteredGeneratorsVector;
+  using RegisteredGeneratorsVector = std::vector<cmGlobalGeneratorFactory*>;
   RegisteredGeneratorsVector Generators;
-  typedef std::vector<cmExternalMakefileProjectGeneratorFactory*>
-    RegisteredExtraGeneratorsVector;
+  using RegisteredExtraGeneratorsVector =
+    std::vector<cmExternalMakefileProjectGeneratorFactory*>;
   RegisteredExtraGeneratorsVector ExtraGenerators;
   void AddScriptingCommands();
   void AddProjectCommands();
   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,
@@ -514,14 +547,15 @@
 
 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;
+  cmGeneratedFileStream TraceFile;
+  bool WarnUninitialized = false;
+  bool WarnUnused = false;
+  bool WarnUnusedCli = true;
+  bool CheckSystemVars = false;
   std::map<std::string, bool> UsedCliVariables;
   std::string CMakeEditCommand;
   std::string CXXEnvironment;
@@ -531,24 +565,24 @@
   std::string CheckStampList;
   std::string VSSolutionFile;
   std::string EnvironmentGenerator;
-  std::vector<std::string> SourceFileExtensions;
-  std::unordered_set<std::string> SourceFileExtensionsSet;
-  std::vector<std::string> HeaderFileExtensions;
-  std::unordered_set<std::string> HeaderFileExtensionsSet;
-  bool ClearBuildSystem;
-  bool DebugTryCompile;
-  cmFileTimeCache* FileTimeCache;
+  FileExtensions SourceFileExtensions;
+  FileExtensions HeaderFileExtensions;
+  FileExtensions CudaFileExtensions;
+  FileExtensions FortranFileExtensions;
+  bool ClearBuildSystem = false;
+  bool DebugTryCompile = false;
+  std::unique_ptr<cmFileTimeCache> FileTimeCache;
   std::string GraphVizFile;
   InstalledFilesMap InstalledFiles;
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-  cmVariableWatch* VariableWatch;
+#if !defined(CMAKE_BOOTSTRAP)
+  std::unique_ptr<cmVariableWatch> VariableWatch;
   std::unique_ptr<cmFileAPI> FileAPI;
 #endif
 
-  cmState* State;
+  std::unique_ptr<cmState> State;
   cmStateSnapshot CurrentSnapshot;
-  cmMessenger* Messenger;
+  std::unique_ptr<cmMessenger> Messenger;
 
   std::vector<std::string> TraceOnlyThisSources;
 
@@ -556,7 +590,7 @@
 
   void UpdateConversionPathTable();
 
-  // Print a list of valid generators to stderr.
+  //! Print a list of valid generators to stderr.
   void PrintGeneratorList();
 
   std::unique_ptr<cmGlobalGenerator> EvaluateDefaultGlobalGenerator();
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index a6348b3..6d3e6ee 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -7,37 +7,39 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 #include "cmcmd.h"
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
 #  include "cmDocumentation.h"
 #  include "cmDynamicLoader.h"
 #endif
 
-#include "cm_uv.h"
-
 #include "cmsys/Encoding.hxx"
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+
+#include "cm_uv.h"
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
 #  include "cmsys/ConsoleBuf.hxx"
 #endif
 
 #include <cassert>
+#include <cctype>
 #include <climits>
-#include <ctype.h>
+#include <cstring>
 #include <iostream>
-#include <string.h>
 #include <string>
 #include <vector>
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
-static const char* cmDocumentationName[][2] = {
+namespace {
+#ifndef CMAKE_BOOTSTRAP
+const char* cmDocumentationName[][2] = {
   { nullptr, "  cmake - Cross-Platform Makefile Generator." },
   { nullptr, nullptr }
 };
 
-static const char* cmDocumentationUsage[][2] = {
+const char* cmDocumentationUsage[][2] = {
   { nullptr,
     "  cmake [options] <path-to-source>\n"
     "  cmake [options] <path-to-existing-build>\n"
@@ -49,40 +51,12 @@
   { nullptr, nullptr }
 };
 
-static const char* cmDocumentationUsageNote[][2] = {
+const char* cmDocumentationUsageNote[][2] = {
   { nullptr, "Run 'cmake --help' for more information." },
   { nullptr, nullptr }
 };
 
-#  define CMAKE_BUILD_OPTIONS                                                 \
-    "  <dir>          = Project binary directory to be built.\n"              \
-    "  --parallel [<jobs>], -j [<jobs>]\n"                                    \
-    "                 = Build in parallel using the given number of jobs. \n" \
-    "                   If <jobs> is omitted the native build tool's \n"      \
-    "                   default number is used.\n"                            \
-    "                   The CMAKE_BUILD_PARALLEL_LEVEL environment "          \
-    "variable\n"                                                              \
-    "                   specifies a default parallel level when this "        \
-    "option\n"                                                                \
-    "                   is not given.\n"                                      \
-    "  --target <tgt>..., -t <tgt>... \n"                                     \
-    "                 = Build <tgt> instead of default targets.\n"            \
-    "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n"       \
-    "  --clean-first  = Build target 'clean' first, then build.\n"            \
-    "                   (To clean only, use --target 'clean'.)\n"             \
-    "  --verbose, -v  = Enable verbose output - if supported - including\n"   \
-    "                   the build commands to be executed. \n"                \
-    "  --             = Pass remaining options to the native tool.\n"
-
-#  define CMAKE_INSTALL_OPTIONS                                               \
-    "  <dir>              = Project binary directory to install.\n"           \
-    "  --config <cfg>     = For multi-configuration tools, choose <cfg>.\n"   \
-    "  --component <comp> = Component-based install. Only install <comp>.\n"  \
-    "  --prefix <prefix>  = The installation prefix CMAKE_INSTALL_PREFIX.\n"  \
-    "  --strip            = Performing install/strip.\n"                      \
-    "  -v --verbose       = Enable verbose output.\n"
-
-static const char* cmDocumentationOptions[][2] = {
+const char* cmDocumentationOptions[][2] = {
   CMAKE_STANDARD_OPTIONS_TABLE,
   { "-E", "CMake command mode." },
   { "-L[A][H]", "List non-advanced cached variables." },
@@ -96,8 +70,9 @@
     "Generate graphviz of dependencies, see "
     "CMakeGraphVizOptions.cmake for more." },
   { "--system-information [file]", "Dump information about this system." },
-  { "--loglevel=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
-    "Set the verbosity of messages from CMake files." },
+  { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
+    "Set the verbosity of messages from CMake files. "
+    "--loglevel is also accepted for backward compatibility reasons." },
   { "--debug-trycompile",
     "Do not delete the try_compile build tree. Only "
     "useful on one try_compile at a time." },
@@ -106,6 +81,8 @@
   { "--trace-expand", "Put cmake in trace mode with variable expansion." },
   { "--trace-source=<file>",
     "Trace only this CMake file/module. Multiple options allowed." },
+  { "--trace-redirect=<file>",
+    "Redirect trace output to a file instead of stderr." },
   { "--warn-uninitialized", "Warn about uninitialized values." },
   { "--warn-unused-vars", "Warn about unused variables." },
   { "--no-warn-unused-cli", "Don't warn about command line options." },
@@ -117,7 +94,7 @@
 
 #endif
 
-static int do_command(int ac, char const* const* av)
+int do_command(int ac, char const* const* av)
 {
   std::vector<std::string> args;
   args.reserve(ac - 1);
@@ -126,12 +103,7 @@
   return cmcmd::ExecuteCMakeCommand(args);
 }
 
-int do_cmake(int ac, char const* const* av);
-static int do_build(int ac, char const* const* av);
-static int do_install(int ac, char const* const* av);
-static int do_open(int ac, char const* const* av);
-
-static cmMakefile* cmakemainGetMakefile(cmake* cm)
+cmMakefile* cmakemainGetMakefile(cmake* cm)
 {
   if (cm && cm->GetDebugOutput()) {
     cmGlobalGenerator* gg = cm->GetGlobalGenerator();
@@ -142,7 +114,7 @@
   return nullptr;
 }
 
-static std::string cmakemainGetStack(cmake* cm)
+std::string cmakemainGetStack(cmake* cm)
 {
   std::string msg;
   cmMakefile* mf = cmakemainGetMakefile(cm);
@@ -156,70 +128,25 @@
   return msg;
 }
 
-static void cmakemainMessageCallback(const std::string& m,
-                                     const char* /*unused*/, cmake* cm)
+void cmakemainMessageCallback(const std::string& m, const char* /*unused*/,
+                              cmake* cm)
 {
-  std::cerr << m << cmakemainGetStack(cm) << std::endl << std::flush;
+  std::cerr << m << cmakemainGetStack(cm) << std::endl;
 }
 
-static void cmakemainProgressCallback(const std::string& m, float prog,
-                                      cmake* cm)
+void cmakemainProgressCallback(const std::string& m, float prog, cmake* cm)
 {
   cmMakefile* mf = cmakemainGetMakefile(cm);
   std::string dir;
   if (mf && cmHasLiteralPrefix(m, "Configuring") && (prog < 0)) {
-    dir = " ";
-    dir += mf->GetCurrentSourceDirectory();
+    dir = cmStrCat(' ', mf->GetCurrentSourceDirectory());
   } else if (mf && cmHasLiteralPrefix(m, "Generating")) {
-    dir = " ";
-    dir += mf->GetCurrentBinaryDirectory();
+    dir = cmStrCat(' ', mf->GetCurrentBinaryDirectory());
   }
 
   if ((prog < 0) || (!dir.empty())) {
     std::cout << "-- " << m << dir << cmakemainGetStack(cm) << std::endl;
   }
-
-  std::cout.flush();
-}
-
-int main(int ac, char const* const* av)
-{
-  cmSystemTools::EnsureStdPipes();
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
-  // Replace streambuf so we can output Unicode to console
-  cmsys::ConsoleBuf::Manager consoleOut(std::cout);
-  consoleOut.SetUTF8Pipes();
-  cmsys::ConsoleBuf::Manager consoleErr(std::cerr, true);
-  consoleErr.SetUTF8Pipes();
-#endif
-  cmsys::Encoding::CommandLineArguments args =
-    cmsys::Encoding::CommandLineArguments::Main(ac, av);
-  ac = args.argc();
-  av = args.argv();
-
-  cmSystemTools::EnableMSVCDebugHook();
-  cmSystemTools::InitializeLibUV();
-  cmSystemTools::FindCMakeResources(av[0]);
-  if (ac > 1) {
-    if (strcmp(av[1], "--build") == 0) {
-      return do_build(ac, av);
-    }
-    if (strcmp(av[1], "--install") == 0) {
-      return do_install(ac, av);
-    }
-    if (strcmp(av[1], "--open") == 0) {
-      return do_open(ac, av);
-    }
-    if (strcmp(av[1], "-E") == 0) {
-      return do_command(ac, av);
-    }
-  }
-  int ret = do_cmake(ac, av);
-#ifdef CMAKE_BUILD_WITH_CMAKE
-  cmDynamicLoader::FlushCache();
-#endif
-  uv_loop_close(uv_default_loop());
-  return ret;
 }
 
 int do_cmake(int ac, char const* const* av)
@@ -230,7 +157,7 @@
     return 1;
   }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   cmDocumentation doc;
   doc.addCMakeStandardDocSections();
   if (doc.CheckOptions(ac, av)) {
@@ -381,7 +308,6 @@
   return 0;
 }
 
-namespace {
 int extract_job_number(int& index, char const* current, char const* next,
                        int len_of_flag)
 {
@@ -396,7 +322,7 @@
   unsigned long numJobs = 0;
   if (jobString.empty()) {
     jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
-  } else if (cmSystemTools::StringToULong(jobString.c_str(), &numJobs)) {
+  } else if (cmStrToULong(jobString, &numJobs)) {
     if (numJobs == 0) {
       std::cerr
         << "The <jobs> value requires a positive integer argument.\n\n";
@@ -411,11 +337,10 @@
   }
   return jobs;
 }
-}
 
-static int do_build(int ac, char const* const* av)
+int do_build(int ac, char const* const* av)
 {
-#ifndef CMAKE_BUILD_WITH_CMAKE
+#ifdef CMAKE_BOOTSTRAP
   std::cerr << "This cmake does not support --build\n";
   return -1;
 #else
@@ -515,7 +440,7 @@
         jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL;
       } else {
         unsigned long numJobs = 0;
-        if (cmSystemTools::StringToULong(parallel.c_str(), &numJobs)) {
+        if (cmStrToULong(parallel, &numJobs)) {
           if (numJobs == 0) {
             std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable "
                          "requires a positive integer argument.\n\n";
@@ -541,7 +466,24 @@
     std::cerr <<
       "Usage: cmake --build <dir> [options] [-- [native-options]]\n"
       "Options:\n"
-      CMAKE_BUILD_OPTIONS
+      "  <dir>          = Project binary directory to be built.\n"
+      "  --parallel [<jobs>], -j [<jobs>]\n"
+      "                 = Build in parallel using the given number of jobs. \n"
+      "                   If <jobs> is omitted the native build tool's \n"
+      "                   default number is used.\n"
+      "                   The CMAKE_BUILD_PARALLEL_LEVEL environment "
+      "variable\n"
+      "                   specifies a default parallel level when this "
+      "option\n"
+      "                   is not given.\n"
+      "  --target <tgt>..., -t <tgt>... \n"
+      "                 = Build <tgt> instead of default targets.\n"
+      "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n"
+      "  --clean-first  = Build target 'clean' first, then build.\n"
+      "                   (To clean only, use --target 'clean'.)\n"
+      "  --verbose, -v  = Enable verbose output - if supported - including\n"
+      "                   the build commands to be executed. \n"
+      "  --             = Pass remaining options to the native tool.\n"
       ;
     /* clang-format on */
     return 1;
@@ -560,9 +502,9 @@
 #endif
 }
 
-static int do_install(int ac, char const* const* av)
+int do_install(int ac, char const* const* av)
 {
-#ifndef CMAKE_BUILD_WITH_CMAKE
+#ifdef CMAKE_BOOTSTRAP
   std::cerr << "This cmake does not support --install\n";
   return -1;
 #else
@@ -627,8 +569,18 @@
   }
 
   if (dir.empty()) {
-    std::cerr << "Usage: cmake --install <dir> "
-                 "[options]\nOptions:\n" CMAKE_INSTALL_OPTIONS;
+    /* clang-format off */
+    std::cerr <<
+      "Usage: cmake --install <dir> [options]\n"
+      "Options:\n"
+      "  <dir>              = Project binary directory to install.\n"
+      "  --config <cfg>     = For multi-configuration tools, choose <cfg>.\n"
+      "  --component <comp> = Component-based install. Only install <comp>.\n"
+      "  --prefix <prefix>  = The installation prefix CMAKE_INSTALL_PREFIX.\n"
+      "  --strip            = Performing install/strip.\n"
+      "  -v --verbose       = Enable verbose output.\n"
+      ;
+    /* clang-format on */
     return 1;
   }
 
@@ -671,9 +623,9 @@
 #endif
 }
 
-static int do_open(int ac, char const* const* av)
+int do_open(int ac, char const* const* av)
 {
-#ifndef CMAKE_BUILD_WITH_CMAKE
+#ifdef CMAKE_BOOTSTRAP
   std::cerr << "This cmake does not support --open\n";
   return -1;
 #else
@@ -713,3 +665,44 @@
   return cm.Open(dir, false) ? 0 : 1;
 #endif
 }
+} // namespace
+
+int main(int ac, char const* const* av)
+{
+  cmSystemTools::EnsureStdPipes();
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
+  // Replace streambuf so we can output Unicode to console
+  cmsys::ConsoleBuf::Manager consoleOut(std::cout);
+  consoleOut.SetUTF8Pipes();
+  cmsys::ConsoleBuf::Manager consoleErr(std::cerr, true);
+  consoleErr.SetUTF8Pipes();
+#endif
+  cmsys::Encoding::CommandLineArguments args =
+    cmsys::Encoding::CommandLineArguments::Main(ac, av);
+  ac = args.argc();
+  av = args.argv();
+
+  cmSystemTools::EnableMSVCDebugHook();
+  cmSystemTools::InitializeLibUV();
+  cmSystemTools::FindCMakeResources(av[0]);
+  if (ac > 1) {
+    if (strcmp(av[1], "--build") == 0) {
+      return do_build(ac, av);
+    }
+    if (strcmp(av[1], "--install") == 0) {
+      return do_install(ac, av);
+    }
+    if (strcmp(av[1], "--open") == 0) {
+      return do_open(ac, av);
+    }
+    if (strcmp(av[1], "-E") == 0) {
+      return do_command(ac, av);
+    }
+  }
+  int ret = do_cmake(ac, av);
+#ifndef CMAKE_BOOTSTRAP
+  cmDynamicLoader::FlushCache();
+#endif
+  uv_loop_close(uv_default_loop());
+  return ret;
+}
diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx
index 19d0d38..caf6453 100644
--- a/Source/cmcldeps.cxx
+++ b/Source/cmcldeps.cxx
@@ -18,13 +18,15 @@
 // /showIncludes is equivalent to -MD, not -MMD, that is, system headers are
 // included.
 
-#include "cmSystemTools.h"
-#include "cmsys/Encoding.hxx"
-
 #include <algorithm>
 #include <sstream>
+
 #include <windows.h>
 
+#include "cmsys/Encoding.hxx"
+
+#include "cmSystemTools.h"
+
 // We don't want any wildcard expansion.
 // See http://msdn.microsoft.com/en-us/library/zay8tzh6(v=vs.85).aspx
 void _setargv()
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 86082e5..d05e3c8 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -13,41 +13,46 @@
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmUtils.hxx"
 #include "cmVersion.h"
 #include "cmake.h"
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
 #  include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback.
 #  include "cmServer.h"
 #  include "cmServerConnection.h"
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32)
-#  include "bindexplib.h"
+#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32)
 #  include "cmsys/ConsoleBuf.hxx"
+
+#  include "cmFileTime.h"
+
+#  include "bindexplib.h"
 #endif
 
-#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__)
 #  include "cmVisualStudioWCEPlatformParser.h"
 #endif
 
+#include <array>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include <cm/string_view>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Process.h"
 #include "cmsys/Terminal.h"
-#include <algorithm>
-#include <array>
-#include <iostream>
-#include <iterator>
-#include <memory> // IWYU pragma: keep
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <utility>
 
 class cmConnection;
 
@@ -60,7 +65,7 @@
 {
   std::ostringstream errorStream;
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
   /* clang-format off */
   errorStream
     << "cmake version " << cmVersion::GetCMakeVersion() << "\n";
@@ -114,6 +119,8 @@
     << "  touch <file>...           - touch a <file>.\n"
     << "  touch_nocreate <file>...  - touch a <file> but do not create it.\n"
     << "  create_symlink old new    - create a symbolic link new -> old\n"
+    << "  true                      - do nothing with an exit code of 0\n"
+    << "  false                     - do nothing with an exit code of 1\n"
 #if defined(_WIN32) && !defined(__CYGWIN__)
     << "Available on Windows only:\n"
     << "  delete_regv key           - delete registry value\n"
@@ -171,8 +178,7 @@
 {
   // Construct the iwyu command line by taking what was given
   // and adding all the arguments we give to the compiler.
-  std::vector<std::string> iwyu_cmd;
-  cmSystemTools::ExpandListArgument(runCmd, iwyu_cmd, true);
+  std::vector<std::string> iwyu_cmd = cmExpandedList(runCmd, true);
   cmAppend(iwyu_cmd, orig_cmd.begin() + 1, orig_cmd.end());
   // Run the iwyu command line.  Capture its stderr and hide its stdout.
   // Ignore its return code because the tool always returns non-zero.
@@ -201,8 +207,7 @@
   // automatically skip over the compiler itself and extract the
   // options.
   int ret;
-  std::vector<std::string> tidy_cmd =
-    cmSystemTools::ExpandedListArgument(runCmd, true);
+  std::vector<std::string> tidy_cmd = cmExpandedList(runCmd, true);
   tidy_cmd.push_back(sourceFile);
   tidy_cmd.emplace_back("--");
   cmAppend(tidy_cmd, orig_cmd);
@@ -262,8 +267,7 @@
                          const std::vector<std::string>&)
 {
   // Construct the cpplint command line.
-  std::vector<std::string> cpplint_cmd;
-  cmSystemTools::ExpandListArgument(runCmd, cpplint_cmd, true);
+  std::vector<std::string> cpplint_cmd = cmExpandedList(runCmd, true);
   cpplint_cmd.push_back(sourceFile);
 
   // Run the cpplint command line.  Capture its output.
@@ -291,8 +295,7 @@
                           const std::vector<std::string>& orig_cmd)
 {
   // Construct the cpplint command line.
-  std::vector<std::string> cppcheck_cmd;
-  cmSystemTools::ExpandListArgument(runCmd, cppcheck_cmd, true);
+  std::vector<std::string> cppcheck_cmd = cmExpandedList(runCmd, true);
   // extract all the -D, -U, and -I options from the compile line
   for (auto const& opt : orig_cmd) {
     if (opt.size() > 2) {
@@ -342,8 +345,8 @@
   return ret;
 }
 
-typedef int (*CoCompileHandler)(const std::string&, const std::string&,
-                                const std::vector<std::string>&);
+using CoCompileHandler = int (*)(const std::string&, const std::string&,
+                                 const std::vector<std::string>&);
 
 struct CoCompiler
 {
@@ -405,7 +408,7 @@
         if (cmHasLiteralPrefix(arg, "--source=")) {
           sourceFile = arg.substr(9);
         } else if (cmHasLiteralPrefix(arg, "--launcher=")) {
-          cmSystemTools::ExpandListArgument(arg.substr(11), launchers, true);
+          cmExpandList(arg.substr(11), launchers, true);
         } else {
           // if it was not a co-compiler or --source/--launcher then error
           std::cerr << "__run_co_compile given unknown argument: " << arg
@@ -558,17 +561,11 @@
       return 0;
     }
 
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
     else if (args[1] == "__create_def") {
       if (args.size() < 4) {
-        std::cerr
-          << "__create_def Usage: -E __create_def outfile.def objlistfile\n";
-        return 1;
-      }
-      FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+");
-      if (!fout) {
-        std::cerr << "could not open output .def file: " << args[2].c_str()
-                  << "\n";
+        std::cerr << "__create_def Usage: -E __create_def outfile.def "
+                     "objlistfile [-nm=nm-path]\n";
         return 1;
       }
       cmsys::ifstream fin(args[3].c_str(), std::ios::in | std::ios::binary);
@@ -577,9 +574,41 @@
                   << "\n";
         return 1;
       }
-      std::string file;
+      std::vector<std::string> files;
+      {
+        std::string file;
+        cmFileTime outTime;
+        bool outValid = outTime.Load(args[2]);
+        while (cmSystemTools::GetLineFromStream(fin, file)) {
+          files.push_back(file);
+          if (outValid) {
+            cmFileTime inTime;
+            outValid = inTime.Load(file) && inTime.Older(outTime);
+          }
+        }
+        if (outValid) {
+          // The def file already exists and all input files are older than the
+          // existing def file.
+          return 0;
+        }
+      }
+      FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+");
+      if (!fout) {
+        std::cerr << "could not open output .def file: " << args[2].c_str()
+                  << "\n";
+        return 1;
+      }
       bindexplib deffile;
-      while (cmSystemTools::GetLineFromStream(fin, file)) {
+      if (args.size() >= 5) {
+        auto a = args[4];
+        if (cmHasLiteralPrefix(a, "--nm=")) {
+          deffile.SetNmPath(a.substr(5));
+          std::cerr << a.substr(5) << "\n";
+        } else {
+          std::cerr << "unknown argument: " << a << "\n";
+        }
+      }
+      for (auto const& file : files) {
         std::string const& ext = cmSystemTools::GetFilenameLastExtension(file);
         if (cmSystemTools::LowerCase(ext) == ".def") {
           if (!deffile.AddDefinitionFile(file.c_str())) {
@@ -651,7 +680,7 @@
       return 1;
     }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
     if (args[1] == "environment") {
       for (auto const& env : cmSystemTools::GetEnvironmentVariables()) {
         std::cout << env << std::endl;
@@ -676,10 +705,17 @@
       // If an error occurs, we want to continue removing directories.
       bool return_value = false;
       for (auto const& arg : cmMakeRange(args).advance(2)) {
-        if (cmSystemTools::FileIsDirectory(arg) &&
-            !cmSystemTools::RemoveADirectory(arg)) {
-          std::cerr << "Error removing directory \"" << arg << "\".\n";
-          return_value = true;
+        if (cmSystemTools::FileIsDirectory(arg)) {
+          if (cmSystemTools::FileIsSymlink(arg)) {
+            if (!cmSystemTools::RemoveFile(arg)) {
+              std::cerr << "Error removing directory symlink \"" << arg
+                        << "\".\n";
+              return_value = true;
+            }
+          } else if (!cmSystemTools::RemoveADirectory(arg)) {
+            std::cerr << "Error removing directory \"" << arg << "\".\n";
+            return_value = true;
+          }
         }
       }
       return return_value;
@@ -763,8 +799,10 @@
     if (args[1] == "time" && args.size() > 2) {
       std::vector<std::string> command(args.begin() + 2, args.end());
 
-      clock_t clock_start, clock_finish;
-      time_t time_start, time_finish;
+      clock_t clock_start;
+      clock_t clock_finish;
+      time_t time_start;
+      time_t time_finish;
 
       time(&time_start);
       clock_start = clock();
@@ -835,8 +873,7 @@
     // Command to start progress for a build
     if (args[1] == "cmake_progress_start" && args.size() == 4) {
       // basically remove the directory
-      std::string dirName = args[2];
-      dirName += "/Progress";
+      std::string dirName = cmStrCat(args[2], "/Progress");
       cmSystemTools::RemoveADirectory(dirName);
 
       // is the last argument a filename that exists?
@@ -853,8 +890,7 @@
       if (count) {
         cmSystemTools::MakeDirectory(dirName);
         // write the count into the directory
-        std::string fName = dirName;
-        fName += "/count.txt";
+        std::string fName = cmStrCat(dirName, "/count.txt");
         FILE* progFile = cmsys::SystemTools::Fopen(fName, "w");
         if (progFile) {
           fprintf(progFile, "%i\n", count);
@@ -891,6 +927,16 @@
       return 0;
     }
 
+    // Command to do nothing with an exit code of 0.
+    if (args[1] == "true") {
+      return 0;
+    }
+
+    // Command to do nothing with an exit code of 1.
+    if (args[1] == "false") {
+      return 1;
+    }
+
     // Internal CMake shared library support.
     if (args[1] == "cmake_symlink_library" && args.size() == 5) {
       return cmcmd::SymlinkLibrary(args);
@@ -933,8 +979,7 @@
         if (args.size() >= 9 && args[8].length() >= 8 &&
             args[8].substr(0, 8) == "--color=") {
           // Enable or disable color based on the switch value.
-          color =
-            (args[8].size() == 8 || cmSystemTools::IsOn(args[8].substr(8)));
+          color = (args[8].size() == 8 || cmIsOn(args[8].substr(8)));
         }
       } else {
         // Support older signature for existing makefiles:
@@ -981,7 +1026,7 @@
       return cmcmd::ExecuteLinkScript(args);
     }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
     // Internal CMake ninja dependency scanning support.
     if (args[1] == "cmake_ninja_depends") {
       return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end());
@@ -1016,21 +1061,17 @@
       return cmcmd::ExecuteEchoColor(args);
     }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
+#ifndef CMAKE_BOOTSTRAP
     if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
-      cmQtAutoMocUic autoGen;
-      std::string const& infoDir = args[2];
-      std::string const& config = args[3];
-      return autoGen.Run(infoDir, config) ? 0 : 1;
+      cm::string_view const infoFile = args[2];
+      cm::string_view const config = args[3];
+      return cmQtAutoMocUic(infoFile, config) ? 0 : 1;
     }
     if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
-      cmQtAutoRcc autoGen;
-      std::string const& infoFile = args[2];
-      std::string config;
-      if (args.size() > 3) {
-        config = args[3];
-      }
-      return autoGen.Run(infoFile, config) ? 0 : 1;
+      cm::string_view const infoFile = args[2];
+      cm::string_view const config =
+        (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view();
+      return cmQtAutoRcc(infoFile, config) ? 0 : 1;
     }
 #endif
 
@@ -1063,11 +1104,7 @@
             }
           } else if (cmHasLiteralPrefix(arg, "--format=")) {
             format = arg.substr(9);
-            bool isKnown =
-              std::find(cm::cbegin(knownFormats), cm::cend(knownFormats),
-                        format) != cm::cend(knownFormats);
-
-            if (!isKnown) {
+            if (!cmContains(knownFormats, format)) {
               cmSystemTools::Error("Unknown -E tar --format= argument: " +
                                    format);
               return 1;
@@ -1197,7 +1234,7 @@
           return 1;
         }
       }
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
       cmConnection* conn;
       if (isDebug) {
         conn = new cmServerStdIoConnection;
@@ -1218,7 +1255,7 @@
       return 1;
     }
 
-#if defined(CMAKE_BUILD_WITH_CMAKE)
+#if !defined(CMAKE_BOOTSTRAP)
     // Internal CMake Fortran module support.
     if (args[1] == "cmake_copy_f90_mod" && args.size() >= 4) {
       return cmDependsFortran::CopyModule(args) ? 0 : 1;
@@ -1339,14 +1376,12 @@
 
 static void cmcmdProgressReport(std::string const& dir, std::string const& num)
 {
-  std::string dirName = dir;
-  dirName += "/Progress";
+  std::string dirName = cmStrCat(dir, "/Progress");
   std::string fName;
   FILE* progFile;
 
   // read the count
-  fName = dirName;
-  fName += "/count.txt";
+  fName = cmStrCat(dirName, "/count.txt");
   progFile = cmsys::SystemTools::Fopen(fName, "r");
   int count = 0;
   if (!progFile) {
@@ -1361,8 +1396,7 @@
   for (const char* c = last;; ++c) {
     if (*c == ',' || *c == '\0') {
       if (c != last) {
-        fName = dirName;
-        fName += "/";
+        fName = cmStrCat(dirName, '/');
         fName.append(last, c - last);
         progFile = cmsys::SystemTools::Fopen(fName, "w");
         if (progFile) {
@@ -1399,7 +1433,7 @@
       // Enable or disable color based on the switch value.
       std::string value = arg.substr(9);
       if (!value.empty()) {
-        enabled = cmSystemTools::IsOn(value);
+        enabled = cmIsOn(value);
       }
     } else if (cmHasLiteralPrefix(arg, "--progress-dir=")) {
       progressDir = arg.substr(15);
@@ -1451,7 +1485,7 @@
   bool verbose = false;
   if (args.size() >= 4) {
     if (args[3].find("--verbose=") == 0) {
-      if (!cmSystemTools::IsOff(args[3].substr(10))) {
+      if (!cmIsOff(args[3].substr(10))) {
         verbose = true;
       }
     }
@@ -1534,7 +1568,7 @@
 
 int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name)
 {
-#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__)
   cmVisualStudioWCEPlatformParser parser(name.c_str());
   parser.ParseVersion(version);
   if (parser.Found()) {
@@ -1592,7 +1626,7 @@
 // still works.
 int cmcmd::VisualStudioLink(std::vector<std::string> const& args, int type)
 {
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
   // Replace streambuf so we output in the system codepage. CMake is set up
   // to output in Unicode (see SetUTF8Pipes) but the Visual Studio linker
   // outputs using the system codepage so we need to change behavior when
@@ -1698,7 +1732,7 @@
 {
   // Parse our own arguments.
   std::string intDir;
-  std::vector<std::string>::const_iterator arg = argBeg;
+  auto arg = argBeg;
   while (arg != argEnd && cmHasLiteralPrefix(*arg, "-")) {
     if (*arg == "--") {
       ++arg;
diff --git a/Source/cmcmd.h b/Source/cmcmd.h
index 69a7ecb..17f2f9a 100644
--- a/Source/cmcmd.h
+++ b/Source/cmcmd.h
@@ -4,11 +4,12 @@
 #define cmcmd_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmCryptoHash.h"
 
 #include <string>
 #include <vector>
 
+#include "cmCryptoHash.h"
+
 class cmcmd
 {
 public:
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index 3b3630f..91ee598 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -1,18 +1,19 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-#include "CTest/cmCTestLaunch.h"
-#include "CTest/cmCTestScriptHandler.h"
+#include "cmsys/Encoding.hxx"
+
 #include "cmCTest.h"
 #include "cmDocumentation.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/Encoding.hxx"
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#include "CTest/cmCTestLaunch.h"
+#include "CTest/cmCTestScriptHandler.h"
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
 #  include "cmsys/ConsoleBuf.hxx"
 #endif
+#include <cstring>
 #include <iostream>
-#include <string.h>
 #include <string>
 #include <vector>
 
@@ -83,7 +84,9 @@
   { "-T <action>, --test-action <action>",
     "Sets the dashboard action to "
     "perform" },
-  { "--track <track>", "Specify the track to submit dashboard to" },
+  { "--group <group>",
+    "Specify what build group on the dashboard you'd like to "
+    "submit results to." },
   { "-S <script>, --script <script>",
     "Execute a dashboard for a "
     "configuration" },
@@ -100,6 +103,7 @@
     "times without failing in order to pass" },
   { "--max-width <width>", "Set the max width for a test name to output" },
   { "--interactive-debug-mode [0|1]", "Set the interactive mode to 0 or 1." },
+  { "--hardware-spec-file <file>", "Set the hardware spec file to use." },
   { "--no-label-summary", "Disable timing summary information for labels." },
   { "--no-subproject-summary",
     "Disable timing summary information for "
@@ -144,7 +148,7 @@
 int main(int argc, char const* const* argv)
 {
   cmSystemTools::EnsureStdPipes();
-#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
+#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
   // Replace streambuf so we can output Unicode to console
   cmsys::ConsoleBuf::Manager consoleOut(std::cout);
   consoleOut.SetUTF8Pipes();
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 79e813e..09bcdb9 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -121,8 +121,8 @@
   set(CMAKE_CXX_STANDARD "${KWSYS_CXX_STANDARD}")
 elseif(NOT DEFINED CMAKE_CXX_STANDARD AND NOT DEFINED KWSYS_CXX_STANDARD)
   if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
-     AND "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"
-     AND "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU"
+     AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"
+     AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU"
     )
     set(CMAKE_CXX_STANDARD 14)
   else()
@@ -1013,7 +1013,7 @@
 
 # Disable deprecation warnings for standard C functions.
 IF(MSVC OR (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "Intel" OR
-   (CMAKE_C_COMPILER_ID STREQUAL "Clang"  AND "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"))))
+   (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))))
   ADD_DEFINITIONS(
     -D_CRT_NONSTDC_NO_DEPRECATE
     -D_CRT_SECURE_NO_DEPRECATE
@@ -1104,7 +1104,7 @@
       SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
         testConsoleBuf.cxx
         )
-      IF("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" AND
+      IF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND
          CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "19.0.23506")
         set_property(SOURCE testConsoleBuf.cxx testConsoleBufChild.cxx PROPERTY COMPILE_FLAGS /utf-8)
       ENDIF()
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx
index a97f7a8..3fd1955 100644
--- a/Source/kwsys/CommandLineArguments.cxx
+++ b/Source/kwsys/CommandLineArguments.cxx
@@ -67,10 +67,10 @@
 {
 public:
   CommandLineArgumentsInternal()
+    : UnknownArgumentCallback{ nullptr }
+    , ClientData{ nullptr }
+    , LastArgument{ 0 }
   {
-    this->UnknownArgumentCallback = KWSYS_NULLPTR;
-    this->ClientData = KWSYS_NULLPTR;
-    this->LastArgument = 0;
   }
 
   typedef CommandLineArgumentsVectorOfStrings VectorOfStrings;
@@ -187,7 +187,7 @@
       switch (cs->ArgumentType) {
         case NO_ARGUMENT:
           // No value
-          if (!this->PopulateVariable(cs, KWSYS_NULLPTR)) {
+          if (!this->PopulateVariable(cs, nullptr)) {
             return 0;
           }
           break;
@@ -340,7 +340,7 @@
   s.Callback = callback;
   s.CallData = call_data;
   s.VariableType = CommandLineArguments::NO_VARIABLE_TYPE;
-  s.Variable = KWSYS_NULLPTR;
+  s.Variable = nullptr;
   s.Help = help;
 
   this->Internals->Callbacks[argument] = s;
@@ -355,8 +355,8 @@
   CommandLineArgumentsCallbackStructure s;
   s.Argument = argument;
   s.ArgumentType = type;
-  s.Callback = KWSYS_NULLPTR;
-  s.CallData = KWSYS_NULLPTR;
+  s.Callback = nullptr;
+  s.CallData = nullptr;
   s.VariableType = vtype;
   s.Variable = variable;
   s.Help = help;
@@ -427,7 +427,7 @@
   CommandLineArguments::Internal::CallbacksMap::iterator it =
     this->Internals->Callbacks.find(arg);
   if (it == this->Internals->Callbacks.end()) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
 
   // Since several arguments may point to the same argument, find the one this
@@ -621,7 +621,7 @@
 void CommandLineArguments::PopulateVariable(int* variable,
                                             const std::string& value)
 {
-  char* res = KWSYS_NULLPTR;
+  char* res = nullptr;
   *variable = static_cast<int>(strtol(value.c_str(), &res, 10));
   // if ( res && *res )
   //  {
@@ -632,7 +632,7 @@
 void CommandLineArguments::PopulateVariable(double* variable,
                                             const std::string& value)
 {
-  char* res = KWSYS_NULLPTR;
+  char* res = nullptr;
   *variable = strtod(value.c_str(), &res);
   // if ( res && *res )
   //  {
@@ -669,7 +669,7 @@
 void CommandLineArguments::PopulateVariable(std::vector<int>* variable,
                                             const std::string& value)
 {
-  char* res = KWSYS_NULLPTR;
+  char* res = nullptr;
   variable->push_back(static_cast<int>(strtol(value.c_str(), &res, 10)));
   // if ( res && *res )
   //  {
@@ -680,7 +680,7 @@
 void CommandLineArguments::PopulateVariable(std::vector<double>* variable,
                                             const std::string& value)
 {
-  char* res = KWSYS_NULLPTR;
+  char* res = nullptr;
   variable->push_back(strtod(value.c_str(), &res));
   // if ( res && *res )
   //  {
diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in
index 92ffea3..29a2dd1 100644
--- a/Source/kwsys/Configure.hxx.in
+++ b/Source/kwsys/Configure.hxx.in
@@ -58,7 +58,6 @@
 #  define KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H                                   \
     @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H
 #  define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH
-#  define KWSYS_NULLPTR @KWSYS_NAMESPACE@_NULLPTR
 #  define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP                               \
     @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP
 #endif
diff --git a/Source/kwsys/ConsoleBuf.hxx.in b/Source/kwsys/ConsoleBuf.hxx.in
index 73a1efb..49dbdf7 100644
--- a/Source/kwsys/ConsoleBuf.hxx.in
+++ b/Source/kwsys/ConsoleBuf.hxx.in
@@ -116,7 +116,7 @@
         DWORD charsWritten;
         success =
           ::WriteConsoleW(m_hOutput, wbuffer.c_str(), (DWORD)wbuffer.size(),
-                          &charsWritten, NULL) == 0
+                          &charsWritten, nullptr) == 0
           ? false
           : true;
       } else {
@@ -124,8 +124,9 @@
         std::string buffer;
         success = encodeOutputBuffer(wbuffer, buffer);
         if (success) {
-          success = ::WriteFile(m_hOutput, buffer.c_str(),
-                                (DWORD)buffer.size(), &bytesWritten, NULL) == 0
+          success =
+            ::WriteFile(m_hOutput, buffer.c_str(), (DWORD)buffer.size(),
+                        &bytesWritten, nullptr) == 0
             ? false
             : true;
         }
@@ -152,7 +153,7 @@
         DWORD charsRead;
         if (ReadConsoleW(m_hInput, wbuffer,
                          (sizeof(wbuffer) / sizeof(wbuffer[0])), &charsRead,
-                         NULL) == 0 ||
+                         nullptr) == 0 ||
             charsRead == 0) {
           _setg(true);
           return Traits::eof();
@@ -168,7 +169,7 @@
           return Traits::eof();
         }
         char* buffer = new char[size.LowPart];
-        while (ReadFile(m_hInput, buffer, size.LowPart, &bytesRead, NULL) ==
+        while (ReadFile(m_hInput, buffer, size.LowPart, &bytesRead, nullptr) ==
                0) {
           if (GetLastError() == ERROR_MORE_DATA) {
             strbuffer += std::string(buffer, bytesRead);
@@ -327,11 +328,12 @@
     }
     const int length =
       WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(),
-                          (int)wbuffer.size(), NULL, 0, NULL, NULL);
+                          (int)wbuffer.size(), nullptr, 0, nullptr, nullptr);
     char* buf = new char[length];
     const bool success =
       WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(),
-                          (int)wbuffer.size(), buf, length, NULL, NULL) > 0
+                          (int)wbuffer.size(), buf, length, nullptr,
+                          nullptr) > 0
       ? true
       : false;
     buffer = std::string(buf, length);
@@ -356,7 +358,7 @@
       length -= BOMsize;
     }
     const size_t wlength = static_cast<size_t>(MultiByteToWideChar(
-      actualCodepage, 0, data, static_cast<int>(length), NULL, 0));
+      actualCodepage, 0, data, static_cast<int>(length), nullptr, 0));
     wchar_t* wbuf = new wchar_t[wlength];
     const bool success =
       MultiByteToWideChar(actualCodepage, 0, data, static_cast<int>(length),
diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx
index 59530a4..e379182 100644
--- a/Source/kwsys/Directory.cxx
+++ b/Source/kwsys/Directory.cxx
@@ -48,7 +48,7 @@
 const char* Directory::GetFile(unsigned long dindex) const
 {
   if (dindex >= this->Internal->Files.size()) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   return this->Internal->Files[dindex].c_str();
 }
diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx
index b93a215..a4b8641 100644
--- a/Source/kwsys/DynamicLoader.cxx
+++ b/Source/kwsys/DynamicLoader.cxx
@@ -223,15 +223,15 @@
 DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
   const std::string& libname, int flags)
 {
-  CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, NULL);
+  CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, nullptr);
 
   DWORD llFlags = 0;
   if (flags & SearchBesideLibrary) {
     llFlags |= LOAD_WITH_ALTERED_SEARCH_PATH;
   }
 
-  return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(), NULL,
-                        llFlags);
+  return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(),
+                        nullptr, llFlags);
 }
 
 int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
@@ -289,9 +289,9 @@
 
   DWORD error = GetLastError();
   DWORD length = FormatMessageW(
-    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
+    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error,
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
-    lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, NULL);
+    lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, nullptr);
 
   static char str[DYNLOAD_ERROR_BUFFER_SIZE + 1];
 
@@ -305,7 +305,7 @@
   }
 
   if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, str,
-                           DYNLOAD_ERROR_BUFFER_SIZE, NULL, NULL)) {
+                           DYNLOAD_ERROR_BUFFER_SIZE, nullptr, nullptr)) {
     /* WideCharToMultiByte failed.  Use a default message.  */
     _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE,
               "DynamicLoader encountered error 0x%X.  "
@@ -372,7 +372,7 @@
     DynamicLoader::SymbolPointer psym;
   } result;
 
-  result.psym = NULL;
+  result.psym = nullptr;
 
   if (!lib) {
     last_dynamic_err = B_BAD_VALUE;
@@ -384,7 +384,7 @@
       get_image_symbol(lib - 1, sym.c_str(), B_SYMBOL_TYPE_ANY, &result.pvoid);
     if (rc != B_OK) {
       last_dynamic_err = rc;
-      result.psym = NULL;
+      result.psym = nullptr;
     }
   }
   return result.psym;
@@ -412,7 +412,7 @@
 DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
   const std::string& libname, int flags)
 {
-  CHECK_OPEN_FLAGS(flags, 0, NULL);
+  CHECK_OPEN_FLAGS(flags, 0, nullptr);
 
   char* name = (char*)calloc(1, libname.size() + 1);
   dld_init(program_invocation_name);
@@ -458,7 +458,7 @@
 DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
   const std::string& libname, int flags)
 {
-  CHECK_OPEN_FLAGS(flags, 0, NULL);
+  CHECK_OPEN_FLAGS(flags, 0, nullptr);
 
   return dlopen(libname.c_str(), RTLD_LAZY);
 }
diff --git a/Source/kwsys/EncodingCXX.cxx b/Source/kwsys/EncodingCXX.cxx
index 251deef..4593c92 100644
--- a/Source/kwsys/EncodingCXX.cxx
+++ b/Source/kwsys/EncodingCXX.cxx
@@ -65,7 +65,7 @@
   for (int i = 0; i < ac; i++) {
     this->argv_[i] = strdup(av[i]);
   }
-  this->argv_[ac] = KWSYS_NULLPTR;
+  this->argv_[ac] = nullptr;
 }
 
 Encoding::CommandLineArguments::CommandLineArguments(int ac,
@@ -75,7 +75,7 @@
   for (int i = 0; i < ac; i++) {
     this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]);
   }
-  this->argv_[ac] = KWSYS_NULLPTR;
+  this->argv_[ac] = nullptr;
 }
 
 Encoding::CommandLineArguments::~CommandLineArguments()
@@ -90,7 +90,7 @@
 {
   this->argv_.resize(other.argv_.size());
   for (size_t i = 0; i < this->argv_.size(); i++) {
-    this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : KWSYS_NULLPTR;
+    this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr;
   }
 }
 
@@ -105,7 +105,7 @@
 
     this->argv_.resize(other.argv_.size());
     for (i = 0; i < this->argv_.size(); i++) {
-      this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : KWSYS_NULLPTR;
+      this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr;
     }
   }
 
@@ -128,8 +128,9 @@
 {
   std::wstring wstr;
 #  if defined(_WIN32)
-  const int wlength = MultiByteToWideChar(
-    KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), NULL, 0);
+  const int wlength =
+    MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
+                        int(str.size()), nullptr, 0);
   if (wlength > 0) {
     wchar_t* wdata = new wchar_t[wlength];
     int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
@@ -162,12 +163,12 @@
 #  if defined(_WIN32)
   int length =
     WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
-                        int(str.size()), NULL, 0, NULL, NULL);
+                        int(str.size()), nullptr, 0, nullptr, nullptr);
   if (length > 0) {
     char* data = new char[length];
     int r =
       WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
-                          int(str.size()), data, length, NULL, NULL);
+                          int(str.size()), data, length, nullptr, nullptr);
     if (r > 0) {
       nstr = std::string(data, length);
     }
@@ -193,7 +194,7 @@
 std::wstring Encoding::ToWide(const char* cstr)
 {
   std::wstring wstr;
-  size_t length = kwsysEncoding_mbstowcs(KWSYS_NULLPTR, cstr, 0) + 1;
+  size_t length = kwsysEncoding_mbstowcs(nullptr, cstr, 0) + 1;
   if (length > 0) {
     std::vector<wchar_t> wchars(length);
     if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) {
@@ -206,7 +207,7 @@
 std::string Encoding::ToNarrow(const wchar_t* wcstr)
 {
   std::string str;
-  size_t length = kwsysEncoding_wcstombs(KWSYS_NULLPTR, wcstr, 0) + 1;
+  size_t length = kwsysEncoding_wcstombs(nullptr, wcstr, 0) + 1;
   if (length > 0) {
     std::vector<char> chars(length);
     if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) {
@@ -227,9 +228,9 @@
 
   /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that
    * won't return a large enough buffer size if the input is too small */
-  wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3;
+  wfull_len = GetFullPathNameW(wsource.c_str(), 0, nullptr, nullptr) + 3;
   std::vector<wchar_t> wfull(wfull_len);
-  GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL);
+  GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr);
 
   /* This should get the correct size without any extra padding from the
    * previous size workaround. */
diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx
index 829c138..34bb0d0 100644
--- a/Source/kwsys/Glob.cxx
+++ b/Source/kwsys/Glob.cxx
@@ -431,7 +431,7 @@
 const char* Glob::GetRelative()
 {
   if (this->Relative.empty()) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   return this->Relative.c_str();
 }
diff --git a/Source/kwsys/Glob.hxx.in b/Source/kwsys/Glob.hxx.in
index 4c3bde1..170766f 100644
--- a/Source/kwsys/Glob.hxx.in
+++ b/Source/kwsys/Glob.hxx.in
@@ -54,7 +54,7 @@
   ~Glob();
 
   //! Find all files that match the pattern.
-  bool FindFiles(const std::string& inexpr, GlobMessages* messages = 0);
+  bool FindFiles(const std::string& inexpr, GlobMessages* messages = nullptr);
 
   //! Return the list of files that matched.
   std::vector<std::string>& GetFiles();
diff --git a/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx
index 5f84b19..5e6f8da 100644
--- a/Source/kwsys/RegularExpression.cxx
+++ b/Source/kwsys/RegularExpression.cxx
@@ -37,7 +37,7 @@
 RegularExpression::RegularExpression(const RegularExpression& rxp)
 {
   if (!rxp.program) {
-    this->program = KWSYS_NULLPTR;
+    this->program = nullptr;
     return;
   }
   int ind;
@@ -48,7 +48,7 @@
   // Copy pointers into last successful "find" operation
   this->regmatch = rxp.regmatch;
   this->regmust = rxp.regmust; // Copy field
-  if (rxp.regmust != KWSYS_NULLPTR) {
+  if (rxp.regmust != nullptr) {
     char* dum = rxp.program;
     ind = 0;
     while (dum != rxp.regmust) {
@@ -69,7 +69,7 @@
     return *this;
   }
   if (!rxp.program) {
-    this->program = KWSYS_NULLPTR;
+    this->program = nullptr;
     return *this;
   }
   int ind;
@@ -81,7 +81,7 @@
   // Copy pointers into last successful "find" operation
   this->regmatch = rxp.regmatch;
   this->regmust = rxp.regmust; // Copy field
-  if (rxp.regmust != KWSYS_NULLPTR) {
+  if (rxp.regmust != nullptr) {
     char* dum = rxp.program;
     ind = 0;
     while (dum != rxp.regmust) {
@@ -164,8 +164,8 @@
  *
  * regstart     char that must begin a match; '\0' if none obvious
  * reganch      is the match anchored (at beginning-of-line only)?
- * regmust      string (pointer into program) that match must include, or NULL
- * regmlen      length of regmust string
+ * regmust      string (pointer into program) that match must include, or
+ * nullptr regmlen      length of regmust string
  *
  * Regstart and reganch permit very fast decisions on suitable starting points
  * for a match, cutting down the work a lot.  Regmust permits fast rejection
@@ -337,10 +337,9 @@
 {
   const char* scan;
   const char* longest;
-  size_t len;
   int flags;
 
-  if (exp == KWSYS_NULLPTR) {
+  if (exp == nullptr) {
     // RAISE Error, SYM(RegularExpression), SYM(No_Expr),
     printf("RegularExpression::compile(): No expression supplied.\n");
     return false;
@@ -368,13 +367,13 @@
 
   // Allocate space.
   //#ifndef _WIN32
-  if (this->program != KWSYS_NULLPTR)
+  if (this->program != nullptr)
     delete[] this->program;
   //#endif
   this->program = new char[comp.regsize];
   this->progsize = static_cast<int>(comp.regsize);
 
-  if (this->program == KWSYS_NULLPTR) {
+  if (this->program == nullptr) {
     // RAISE Error, SYM(RegularExpression), SYM(Out_Of_Memory),
     printf("RegularExpression::compile(): Out of memory.\n");
     return false;
@@ -390,7 +389,7 @@
   // Dig out information for optimizations.
   this->regstart = '\0'; // Worst-case defaults.
   this->reganch = 0;
-  this->regmust = KWSYS_NULLPTR;
+  this->regmust = nullptr;
   this->regmlen = 0;
   scan = this->program + 1;       // First BRANCH.
   if (OP(regnext(scan)) == END) { // Only one top-level choice.
@@ -411,9 +410,9 @@
     // absence of others.
     //
     if (flags & SPSTART) {
-      longest = KWSYS_NULLPTR;
-      len = 0;
-      for (; scan != KWSYS_NULLPTR; scan = regnext(scan))
+      longest = nullptr;
+      size_t len = 0;
+      for (; scan != nullptr; scan = regnext(scan))
         if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
           longest = OPERAND(scan);
           len = strlen(OPERAND(scan));
@@ -449,19 +448,19 @@
     if (regnpar >= RegularExpressionMatch::NSUBEXP) {
       // RAISE Error, SYM(RegularExpression), SYM(Too_Many_Parens),
       printf("RegularExpression::compile(): Too many parentheses.\n");
-      return KWSYS_NULLPTR;
+      return nullptr;
     }
     parno = regnpar;
     regnpar++;
     ret = regnode(static_cast<char>(OPEN + parno));
   } else
-    ret = KWSYS_NULLPTR;
+    ret = nullptr;
 
   // Pick up the branches, linking them together.
   br = regbranch(&flags);
-  if (br == KWSYS_NULLPTR)
-    return (KWSYS_NULLPTR);
-  if (ret != KWSYS_NULLPTR)
+  if (br == nullptr)
+    return (nullptr);
+  if (ret != nullptr)
     regtail(ret, br); // OPEN -> first.
   else
     ret = br;
@@ -471,8 +470,8 @@
   while (*regparse == '|') {
     regparse++;
     br = regbranch(&flags);
-    if (br == KWSYS_NULLPTR)
-      return (KWSYS_NULLPTR);
+    if (br == nullptr)
+      return (nullptr);
     regtail(ret, br); // BRANCH -> BRANCH.
     if (!(flags & HASWIDTH))
       *flagp &= ~HASWIDTH;
@@ -484,23 +483,23 @@
   regtail(ret, ender);
 
   // Hook the tails of the branches to the closing node.
-  for (br = ret; br != KWSYS_NULLPTR; br = regnext(br))
+  for (br = ret; br != nullptr; br = regnext(br))
     regoptail(br, ender);
 
   // Check for proper termination.
   if (paren && *regparse++ != ')') {
     // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens),
     printf("RegularExpression::compile(): Unmatched parentheses.\n");
-    return KWSYS_NULLPTR;
+    return nullptr;
   } else if (!paren && *regparse != '\0') {
     if (*regparse == ')') {
       // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens),
       printf("RegularExpression::compile(): Unmatched parentheses.\n");
-      return KWSYS_NULLPTR;
+      return nullptr;
     } else {
       // RAISE Error, SYM(RegularExpression), SYM(Internal_Error),
       printf("RegularExpression::compile(): Internal error.\n");
-      return KWSYS_NULLPTR;
+      return nullptr;
     }
     // NOTREACHED
   }
@@ -522,19 +521,19 @@
   *flagp = WORST; // Tentatively.
 
   ret = regnode(BRANCH);
-  chain = KWSYS_NULLPTR;
+  chain = nullptr;
   while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
     latest = regpiece(&flags);
-    if (latest == KWSYS_NULLPTR)
-      return (KWSYS_NULLPTR);
+    if (latest == nullptr)
+      return (nullptr);
     *flagp |= flags & HASWIDTH;
-    if (chain == KWSYS_NULLPTR) // First piece.
+    if (chain == nullptr) // First piece.
       *flagp |= flags & SPSTART;
     else
       regtail(chain, latest);
     chain = latest;
   }
-  if (chain == KWSYS_NULLPTR) // Loop ran zero times.
+  if (chain == nullptr) // Loop ran zero times.
     regnode(NOTHING);
 
   return (ret);
@@ -557,8 +556,8 @@
   int flags;
 
   ret = regatom(&flags);
-  if (ret == KWSYS_NULLPTR)
-    return (KWSYS_NULLPTR);
+  if (ret == nullptr)
+    return (nullptr);
 
   op = *regparse;
   if (!ISMULT(op)) {
@@ -569,7 +568,7 @@
   if (!(flags & HASWIDTH) && op != '?') {
     // RAISE Error, SYM(RegularExpression), SYM(Empty_Operand),
     printf("RegularExpression::compile() : *+ operand could be empty.\n");
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH);
 
@@ -603,7 +602,7 @@
   if (ISMULT(*regparse)) {
     // RAISE Error, SYM(RegularExpression), SYM(Nested_Operand),
     printf("RegularExpression::compile(): Nested *?+.\n");
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   return (ret);
 }
@@ -656,7 +655,7 @@
             if (rxpclass > rxpclassend + 1) {
               // RAISE Error, SYM(RegularExpression), SYM(Invalid_Range),
               printf("RegularExpression::compile(): Invalid range in [].\n");
-              return KWSYS_NULLPTR;
+              return nullptr;
             }
             for (; rxpclass <= rxpclassend; rxpclass++)
               regc(static_cast<char>(rxpclass));
@@ -669,15 +668,15 @@
       if (*regparse != ']') {
         // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Bracket),
         printf("RegularExpression::compile(): Unmatched [].\n");
-        return KWSYS_NULLPTR;
+        return nullptr;
       }
       regparse++;
       *flagp |= HASWIDTH | SIMPLE;
     } break;
     case '(':
       ret = reg(1, &flags);
-      if (ret == KWSYS_NULLPTR)
-        return (KWSYS_NULLPTR);
+      if (ret == nullptr)
+        return (nullptr);
       *flagp |= flags & (HASWIDTH | SPSTART);
       break;
     case '\0':
@@ -685,18 +684,18 @@
     case ')':
       // RAISE Error, SYM(RegularExpression), SYM(Internal_Error),
       printf("RegularExpression::compile(): Internal error.\n"); // Never here
-      return KWSYS_NULLPTR;
+      return nullptr;
     case '?':
     case '+':
     case '*':
       // RAISE Error, SYM(RegularExpression), SYM(No_Operand),
       printf("RegularExpression::compile(): ?+* follows nothing.\n");
-      return KWSYS_NULLPTR;
+      return nullptr;
     case '\\':
       if (*regparse == '\0') {
         // RAISE Error, SYM(RegularExpression), SYM(Trailing_Backslash),
         printf("RegularExpression::compile(): Trailing backslash.\n");
-        return KWSYS_NULLPTR;
+        return nullptr;
       }
       ret = regnode(EXACTLY);
       regc(*regparse++);
@@ -712,7 +711,7 @@
       if (len <= 0) {
         // RAISE Error, SYM(RegularExpression), SYM(Internal_Error),
         printf("RegularExpression::compile(): Internal error.\n");
-        return KWSYS_NULLPTR;
+        return nullptr;
       }
       ender = *(regparse + len);
       if (len > 1 && ISMULT(ender))
@@ -810,7 +809,7 @@
   scan = p;
   for (;;) {
     temp = regnext(scan);
-    if (temp == KWSYS_NULLPTR)
+    if (temp == nullptr)
       break;
     scan = temp;
   }
@@ -829,7 +828,7 @@
 void RegExpCompile::regoptail(char* p, const char* val)
 {
   // "Operandless" and "op != BRANCH" are synonymous in practice.
-  if (p == KWSYS_NULLPTR || p == regdummyptr || OP(p) != BRANCH)
+  if (p == nullptr || p == regdummyptr || OP(p) != BRANCH)
     return;
   regtail(OPERAND(p), val);
 }
@@ -879,14 +878,14 @@
   }
 
   // If there is a "must appear" string, look for it.
-  if (this->regmust != KWSYS_NULLPTR) {
+  if (this->regmust != nullptr) {
     s = string;
-    while ((s = strchr(s, this->regmust[0])) != KWSYS_NULLPTR) {
+    while ((s = strchr(s, this->regmust[0])) != nullptr) {
       if (strncmp(s, this->regmust, this->regmlen) == 0)
         break; // Found it.
       s++;
     }
-    if (s == KWSYS_NULLPTR) // Not present.
+    if (s == nullptr) // Not present.
       return false;
   }
 
@@ -904,7 +903,7 @@
   s = string;
   if (this->regstart != '\0')
     // We know what char it must start with.
-    while ((s = strchr(s, this->regstart)) != KWSYS_NULLPTR) {
+    while ((s = strchr(s, this->regstart)) != nullptr) {
       if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program))
         return true;
       s++;
@@ -938,8 +937,8 @@
   sp1 = start;
   ep = end;
   for (i = RegularExpressionMatch::NSUBEXP; i > 0; i--) {
-    *sp1++ = KWSYS_NULLPTR;
-    *ep++ = KWSYS_NULLPTR;
+    *sp1++ = nullptr;
+    *ep++ = nullptr;
   }
   if (regmatch(prog + 1)) {
     start[0] = string;
@@ -967,7 +966,7 @@
 
   scan = prog;
 
-  while (scan != KWSYS_NULLPTR) {
+  while (scan != nullptr) {
 
     next = regnext(scan);
 
@@ -999,14 +998,12 @@
         reginput += len;
       } break;
       case ANYOF:
-        if (*reginput == '\0' ||
-            strchr(OPERAND(scan), *reginput) == KWSYS_NULLPTR)
+        if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == nullptr)
           return (0);
         reginput++;
         break;
       case ANYBUT:
-        if (*reginput == '\0' ||
-            strchr(OPERAND(scan), *reginput) != KWSYS_NULLPTR)
+        if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != nullptr)
           return (0);
         reginput++;
         break;
@@ -1035,7 +1032,7 @@
           // Don't set startp if some later invocation of the
           // same parentheses already has.
           //
-          if (regstartp[no] == KWSYS_NULLPTR)
+          if (regstartp[no] == nullptr)
             regstartp[no] = save;
           return (1);
         } else
@@ -1063,7 +1060,7 @@
           // Don't set endp if some later invocation of the
           // same parentheses already has.
           //
-          if (regendp[no] == KWSYS_NULLPTR)
+          if (regendp[no] == nullptr)
             regendp[no] = save;
           return (1);
         } else
@@ -1083,7 +1080,7 @@
               return (1);
             reginput = save;
             scan = regnext(scan);
-          } while (scan != KWSYS_NULLPTR && OP(scan) == BRANCH);
+          } while (scan != nullptr && OP(scan) == BRANCH);
           return (0);
           // NOTREACHED
         }
@@ -1161,13 +1158,13 @@
       }
       break;
     case ANYOF:
-      while (*scan != '\0' && strchr(opnd, *scan) != KWSYS_NULLPTR) {
+      while (*scan != '\0' && strchr(opnd, *scan) != nullptr) {
         count++;
         scan++;
       }
       break;
     case ANYBUT:
-      while (*scan != '\0' && strchr(opnd, *scan) == KWSYS_NULLPTR) {
+      while (*scan != '\0' && strchr(opnd, *scan) == nullptr) {
         count++;
         scan++;
       }
@@ -1189,11 +1186,11 @@
   int offset;
 
   if (p == regdummyptr)
-    return (KWSYS_NULLPTR);
+    return (nullptr);
 
   offset = NEXT(p);
   if (offset == 0)
-    return (KWSYS_NULLPTR);
+    return (nullptr);
 
   if (OP(p) == BACK)
     return (p - offset);
@@ -1206,11 +1203,11 @@
   int offset;
 
   if (p == regdummyptr)
-    return (KWSYS_NULLPTR);
+    return (nullptr);
 
   offset = NEXT(p);
   if (offset == 0)
-    return (KWSYS_NULLPTR);
+    return (nullptr);
 
   if (OP(p) == BACK)
     return (p - offset);
diff --git a/Source/kwsys/RegularExpression.hxx.in b/Source/kwsys/RegularExpression.hxx.in
index b7b93f9..df7eb45 100644
--- a/Source/kwsys/RegularExpression.hxx.in
+++ b/Source/kwsys/RegularExpression.hxx.in
@@ -71,9 +71,9 @@
  */
 inline RegularExpressionMatch::RegularExpressionMatch()
 {
-  startp[0] = 0;
-  endp[0] = 0;
-  searchstring = 0;
+  startp[0] = nullptr;
+  endp[0] = nullptr;
+  searchstring = nullptr;
 }
 
 /**
@@ -81,7 +81,7 @@
  */
 inline bool RegularExpressionMatch::isValid() const
 {
-  return (this->startp[0] != 0);
+  return (this->startp[0] != nullptr);
 }
 
 /**
@@ -89,9 +89,9 @@
  */
 inline void RegularExpressionMatch::clear()
 {
-  startp[0] = 0;
-  endp[0] = 0;
-  searchstring = 0;
+  startp[0] = nullptr;
+  endp[0] = nullptr;
+  searchstring = nullptr;
 }
 
 /**
@@ -135,7 +135,7 @@
  */
 inline std::string RegularExpressionMatch::match(int n) const
 {
-  if (this->startp[n] == 0) {
+  if (this->startp[n] == nullptr) {
     return std::string();
   } else {
     return std::string(
@@ -230,10 +230,11 @@
  * into the object's private data fields.  The == and  != operators only check
  * the  to see  if   the compiled  regular  expression   is the same, and  the
  * deep_equal functions also checks  to see if the  start and end pointers are
- * the same.  The is_valid  function returns false if  program is set to NULL,
- * (i.e. there is no valid compiled  expression).  The  set_invalid   function
- * sets the program to NULL (Warning:  this deletes the compiled  expression).
- * The following examples may help clarify regular expression usage:
+ * the same.  The is_valid  function returns false if  program is set to
+ * nullptr, (i.e. there is no valid compiled  expression).  The  set_invalid
+ * function sets the program to nullptr (Warning:  this deletes the compiled
+ * expression). The following examples may help clarify regular expression
+ * usage:
  *
  *   *  The regular expression  "^hello" matches  a "hello"  only at  the
  *      beginning of a  line.  It would match "hello  there" but not "hi,
@@ -288,7 +289,7 @@
 {
 public:
   /**
-   * Instantiate RegularExpression with program=NULL.
+   * Instantiate RegularExpression with program=nullptr.
    */
   inline RegularExpression();
 
@@ -407,8 +408,12 @@
  * Create an empty regular expression.
  */
 inline RegularExpression::RegularExpression()
+  : regstart{}
+  , reganch{}
+  , regmust{}
+  , program{ nullptr }
+  , progsize{}
 {
-  this->program = 0;
 }
 
 /**
@@ -416,8 +421,12 @@
  * compiles s.
  */
 inline RegularExpression::RegularExpression(const char* s)
+  : regstart{}
+  , reganch{}
+  , regmust{}
+  , program{ nullptr }
+  , progsize{}
 {
-  this->program = 0;
   if (s) {
     this->compile(s);
   }
@@ -428,8 +437,12 @@
  * compiles s.
  */
 inline RegularExpression::RegularExpression(const std::string& s)
+  : regstart{}
+  , reganch{}
+  , regmust{}
+  , program{ nullptr }
+  , progsize{}
 {
-  this->program = 0;
   this->compile(s);
 }
 
@@ -533,7 +546,7 @@
  */
 inline bool RegularExpression::is_valid() const
 {
-  return (this->program != 0);
+  return (this->program != nullptr);
 }
 
 inline void RegularExpression::set_invalid()
@@ -541,7 +554,7 @@
   //#ifndef _WIN32
   delete[] this->program;
   //#endif
-  this->program = 0;
+  this->program = nullptr;
 }
 
 } // namespace @KWSYS_NAMESPACE@
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index 7dc6cf4..6ec6e48 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -847,31 +847,16 @@
 
 // SystemInformationImplementation starts here
 
-#define STORE_TLBCACHE_INFO(x, y) x = (x < (y)) ? (y) : x
-#define TLBCACHE_INFO_UNITS (15)
-#define CLASSICAL_CPU_FREQ_LOOP 10000000
-#define RDTSC_INSTRUCTION _asm _emit 0x0f _asm _emit 0x31
+#if USE_CPUID
+#  define STORE_TLBCACHE_INFO(x, y) x = (x < (y)) ? (y) : x
+#  define TLBCACHE_INFO_UNITS (15)
+#endif
 
-// Status Flag
-#define HT_NOT_CAPABLE 0
-#define HT_ENABLED 1
-#define HT_DISABLED 2
-#define HT_SUPPORTED_NOT_ENABLED 3
-#define HT_CANNOT_DETECT 4
+#if USE_ASM_INSTRUCTIONS
+#  define CLASSICAL_CPU_FREQ_LOOP 10000000
+#  define RDTSC_INSTRUCTION _asm _emit 0x0f _asm _emit 0x31
+#endif
 
-// EDX[28]  Bit 28 is set if HT is supported
-#define HT_BIT 0x10000000
-
-// EAX[11:8] Bit 8-11 contains family processor ID.
-#define FAMILY_ID 0x0F00
-#define PENTIUM4_ID 0x0F00
-// EAX[23:20] Bit 20-23 contains extended family processor ID
-#define EXT_FAMILY_ID 0x0F00000
-// EBX[23:16] Bit 16-23 in ebx contains the number of logical
-#define NUM_LOGICAL_BITS 0x00FF0000
-// processors per physical processor when execute cpuid with
-// eax set to 1
-// EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
 #define INITIAL_APIC_ID_BITS 0xFF000000
 // initial APIC ID for the processor this code is running on.
 // Default value = 0xff if HT is not supported
@@ -888,7 +873,7 @@
   char buf[bufSize] = { '\0' };
   while (!feof(file) && !ferror(file)) {
     errno = 0;
-    if (fgets(buf, bufSize, file) == KWSYS_NULLPTR) {
+    if (fgets(buf, bufSize, file) == nullptr) {
       if (ferror(file) && (errno == EINTR)) {
         clearerr(file);
       }
@@ -952,7 +937,7 @@
     return -1;
   }
   int i = 0;
-  while (fieldNames[i] != NULL) {
+  while (fieldNames[i] != nullptr) {
     int ierr = NameValue(fields, fieldNames[i], values[i]);
     if (ierr) {
       return -(i + 2);
@@ -966,7 +951,7 @@
 template <typename T>
 int GetFieldFromFile(const char* fileName, const char* fieldName, T& value)
 {
-  const char* fieldNames[2] = { fieldName, NULL };
+  const char* fieldNames[2] = { fieldName, nullptr };
   T values[1] = { T(0) };
   int ierr = GetFieldsFromFile(fileName, fieldNames, values);
   if (ierr) {
@@ -984,7 +969,7 @@
                          T* values)
 {
   FILE* file = popen(command, "r");
-  if (file == KWSYS_NULLPTR) {
+  if (file == nullptr) {
     return -1;
   }
   std::vector<std::string> fields;
@@ -994,7 +979,7 @@
     return -1;
   }
   int i = 0;
-  while (fieldNames[i] != KWSYS_NULLPTR) {
+  while (fieldNames[i] != nullptr) {
     int ierr = NameValue(fields, fieldNames[i], values[i]);
     if (ierr) {
       return -(i + 2);
@@ -1030,8 +1015,7 @@
       break;
 
     case SIGFPE:
-      oss << "Caught SIGFPE at "
-          << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "")
+      oss << "Caught SIGFPE at " << (sigInfo->si_addr == nullptr ? "0x" : "")
           << sigInfo->si_addr << " ";
       switch (sigInfo->si_code) {
 #    if defined(FPE_INTDIV)
@@ -1079,8 +1063,7 @@
       break;
 
     case SIGSEGV:
-      oss << "Caught SIGSEGV at "
-          << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "")
+      oss << "Caught SIGSEGV at " << (sigInfo->si_addr == nullptr ? "0x" : "")
           << sigInfo->si_addr << " ";
       switch (sigInfo->si_code) {
         case SEGV_MAPERR:
@@ -1098,8 +1081,7 @@
       break;
 
     case SIGBUS:
-      oss << "Caught SIGBUS at "
-          << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "")
+      oss << "Caught SIGBUS at " << (sigInfo->si_addr == nullptr ? "0x" : "")
           << sigInfo->si_addr << " ";
       switch (sigInfo->si_code) {
         case BUS_ADRALN:
@@ -1139,8 +1121,7 @@
       break;
 
     case SIGILL:
-      oss << "Caught SIGILL at "
-          << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "")
+      oss << "Caught SIGILL at " << (sigInfo->si_addr == nullptr ? "0x" : "")
           << sigInfo->si_addr << " ";
       switch (sigInfo->si_code) {
         case ILL_ILLOPC:
@@ -1324,8 +1305,8 @@
   // not using an initializer list
   // to avoid some PGI compiler warnings
   this->SetBinary("???");
-  this->SetBinaryBaseAddress(KWSYS_NULLPTR);
-  this->Address = KWSYS_NULLPTR;
+  this->SetBinaryBaseAddress(nullptr);
+  this->Address = nullptr;
   this->SetSourceFile("???");
   this->SetFunction("???");
   this->SetLineNumber(-1);
@@ -1682,7 +1663,7 @@
     return -2;
   }
 
-  for (ifa = ifas; ifa != KWSYS_NULLPTR; ifa = ifa->ifa_next) {
+  for (ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) {
     int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1;
     // Skip Loopback interfaces
     if (((fam == AF_INET) || (fam == AF_INET6)) &&
@@ -1693,7 +1674,7 @@
                                              : sizeof(struct sockaddr_in6));
 
       ierr = getnameinfo(ifa->ifa_addr, static_cast<socklen_t>(addrlen), host,
-                         NI_MAXHOST, KWSYS_NULLPTR, 0, NI_NAMEREQD);
+                         NI_MAXHOST, nullptr, 0, NI_NAMEREQD);
       if (ierr) {
         // don't report the failure now since we may succeed on another
         // interface. If all attempts fail then return the failure code.
@@ -2577,7 +2558,7 @@
   // If RDTSC is not supported, we fallback to trying to read this value
   // from the registry:
   if (!retrieved) {
-    HKEY hKey = NULL;
+    HKEY hKey = nullptr;
     LONG err =
       RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                     L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
@@ -2597,7 +2578,7 @@
       }
 
       RegCloseKey(hKey);
-      hKey = NULL;
+      hKey = nullptr;
     }
   }
 #endif
@@ -3628,7 +3609,7 @@
 #elif defined(__APPLE__)
   uint64_t mem;
   size_t len = sizeof(mem);
-  int ierr = sysctlbyname("hw.memsize", &mem, &len, KWSYS_NULLPTR, 0);
+  int ierr = sysctlbyname("hw.memsize", &mem, &len, nullptr, 0);
   if (ierr) {
     return -1;
   }
@@ -3745,12 +3726,12 @@
 #  endif
 #elif defined(__linux)
   // First try to use MemAvailable, but it only works on newer kernels
-  const char* names2[3] = { "MemTotal:", "MemAvailable:", NULL };
+  const char* names2[3] = { "MemTotal:", "MemAvailable:", nullptr };
   SystemInformation::LongLong values2[2] = { SystemInformation::LongLong(0) };
   int ierr = GetFieldsFromFile("/proc/meminfo", names2, values2);
   if (ierr) {
     const char* names4[5] = { "MemTotal:", "MemFree:", "Buffers:", "Cached:",
-                              NULL };
+                              nullptr };
     SystemInformation::LongLong values4[4] = { SystemInformation::LongLong(
       0) };
     ierr = GetFieldsFromFile("/proc/meminfo", names4, values4);
@@ -3771,8 +3752,7 @@
   if (psz < 1) {
     return -1;
   }
-  const char* names[3] = { "Pages wired down:", "Pages active:",
-                           KWSYS_NULLPTR };
+  const char* names[3] = { "Pages wired down:", "Pages active:", nullptr };
   SystemInformation::LongLong values[2] = { SystemInformation::LongLong(0) };
   int ierr = GetFieldsFromCommand("vm_stat", names, values);
   if (ierr) {
@@ -3820,7 +3800,7 @@
   std::ostringstream oss;
   oss << "ps -o rss= -p " << pid;
   FILE* file = popen(oss.str().c_str(), "r");
-  if (file == KWSYS_NULLPTR) {
+  if (file == nullptr) {
     return -1;
   }
   oss.str("");
@@ -3920,9 +3900,9 @@
 
   void* stack[TRACE_MAX_STACK_FRAMES];
   HANDLE process = GetCurrentProcess();
-  SymInitialize(process, NULL, TRUE);
+  SymInitialize(process, nullptr, TRUE);
   WORD numberOfFrames =
-    CaptureStackBackTrace(firstFrame, TRACE_MAX_STACK_FRAMES, stack, NULL);
+    CaptureStackBackTrace(firstFrame, TRACE_MAX_STACK_FRAMES, stack, nullptr);
   SYMBOL_INFO* symbol = static_cast<SYMBOL_INFO*>(
     malloc(sizeof(SYMBOL_INFO) +
            (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)));
@@ -3933,7 +3913,7 @@
   line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
   for (int i = 0; i < numberOfFrames; i++) {
     DWORD64 address = reinterpret_cast<DWORD64>(stack[i]);
-    SymFromAddr(process, address, NULL, symbol);
+    SymFromAddr(process, address, nullptr, symbol);
     if (SymGetLineFromAddr64(process, address, &displacement, &line)) {
       oss << " at " << symbol->Name << " in " << line.FileName << " line "
           << line.LineNumber << std::endl;
@@ -4000,13 +3980,13 @@
 
   if (enable && !saOrigValid) {
     // save the current actions
-    sigaction(SIGABRT, KWSYS_NULLPTR, &saABRTOrig);
-    sigaction(SIGSEGV, KWSYS_NULLPTR, &saSEGVOrig);
-    sigaction(SIGTERM, KWSYS_NULLPTR, &saTERMOrig);
-    sigaction(SIGINT, KWSYS_NULLPTR, &saINTOrig);
-    sigaction(SIGILL, KWSYS_NULLPTR, &saILLOrig);
-    sigaction(SIGBUS, KWSYS_NULLPTR, &saBUSOrig);
-    sigaction(SIGFPE, KWSYS_NULLPTR, &saFPEOrig);
+    sigaction(SIGABRT, nullptr, &saABRTOrig);
+    sigaction(SIGSEGV, nullptr, &saSEGVOrig);
+    sigaction(SIGTERM, nullptr, &saTERMOrig);
+    sigaction(SIGINT, nullptr, &saINTOrig);
+    sigaction(SIGILL, nullptr, &saILLOrig);
+    sigaction(SIGBUS, nullptr, &saBUSOrig);
+    sigaction(SIGFPE, nullptr, &saFPEOrig);
 
     // enable read, disable write
     saOrigValid = 1;
@@ -4020,22 +4000,22 @@
 #  endif
     sigemptyset(&sa.sa_mask);
 
-    sigaction(SIGABRT, &sa, KWSYS_NULLPTR);
-    sigaction(SIGSEGV, &sa, KWSYS_NULLPTR);
-    sigaction(SIGTERM, &sa, KWSYS_NULLPTR);
-    sigaction(SIGINT, &sa, KWSYS_NULLPTR);
-    sigaction(SIGILL, &sa, KWSYS_NULLPTR);
-    sigaction(SIGBUS, &sa, KWSYS_NULLPTR);
-    sigaction(SIGFPE, &sa, KWSYS_NULLPTR);
+    sigaction(SIGABRT, &sa, nullptr);
+    sigaction(SIGSEGV, &sa, nullptr);
+    sigaction(SIGTERM, &sa, nullptr);
+    sigaction(SIGINT, &sa, nullptr);
+    sigaction(SIGILL, &sa, nullptr);
+    sigaction(SIGBUS, &sa, nullptr);
+    sigaction(SIGFPE, &sa, nullptr);
   } else if (!enable && saOrigValid) {
     // restore previous actions
-    sigaction(SIGABRT, &saABRTOrig, KWSYS_NULLPTR);
-    sigaction(SIGSEGV, &saSEGVOrig, KWSYS_NULLPTR);
-    sigaction(SIGTERM, &saTERMOrig, KWSYS_NULLPTR);
-    sigaction(SIGINT, &saINTOrig, KWSYS_NULLPTR);
-    sigaction(SIGILL, &saILLOrig, KWSYS_NULLPTR);
-    sigaction(SIGBUS, &saBUSOrig, KWSYS_NULLPTR);
-    sigaction(SIGFPE, &saFPEOrig, KWSYS_NULLPTR);
+    sigaction(SIGABRT, &saABRTOrig, nullptr);
+    sigaction(SIGSEGV, &saSEGVOrig, nullptr);
+    sigaction(SIGTERM, &saTERMOrig, nullptr);
+    sigaction(SIGINT, &saINTOrig, nullptr);
+    sigaction(SIGILL, &saILLOrig, nullptr);
+    sigaction(SIGBUS, &saBUSOrig, nullptr);
+    sigaction(SIGFPE, &saFPEOrig, nullptr);
 
     // enable write, disable read
     saOrigValid = 0;
@@ -4417,7 +4397,7 @@
   std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> ProcInfo;
   {
     DWORD Length = 0;
-    DWORD rc = pGetLogicalProcessorInformation(NULL, &Length);
+    DWORD rc = pGetLogicalProcessorInformation(nullptr, &Length);
     assert(FALSE == rc);
     (void)rc; // Silence unused variable warning in Borland C++ 5.81
     assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
@@ -4471,7 +4451,7 @@
   int err = 0;
   uint64_t value = 0;
   size_t len = sizeof(value);
-  sysctlbyname("hw.memsize", &value, &len, KWSYS_NULLPTR, 0);
+  sysctlbyname("hw.memsize", &value, &len, nullptr, 0);
   this->TotalPhysicalMemory = static_cast<size_t>(value / 1048576);
 
   // Parse values for Mac
@@ -4481,7 +4461,7 @@
   if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat,
                       &count) == KERN_SUCCESS) {
     len = sizeof(value);
-    err = sysctlbyname("hw.pagesize", &value, &len, KWSYS_NULLPTR, 0);
+    err = sysctlbyname("hw.pagesize", &value, &len, nullptr, 0);
     int64_t available_memory =
       (vmstat.free_count + vmstat.inactive_count) * value;
     this->AvailablePhysicalMemory =
@@ -4491,10 +4471,11 @@
 #  ifdef VM_SWAPUSAGE
   // Virtual memory.
   int mib[2] = { CTL_VM, VM_SWAPUSAGE };
-  size_t miblen = sizeof(mib) / sizeof(mib[0]);
+  unsigned int miblen =
+    static_cast<unsigned int>(sizeof(mib) / sizeof(mib[0]));
   struct xsw_usage swap;
   len = sizeof(swap);
-  err = sysctl(mib, miblen, &swap, &len, KWSYS_NULLPTR, 0);
+  err = sysctl(mib, miblen, &swap, &len, nullptr, 0);
   if (err == 0) {
     this->AvailableVirtualMemory =
       static_cast<size_t>(swap.xsu_avail / 1048576);
@@ -4507,75 +4488,72 @@
 
   // CPU Info
   len = sizeof(this->NumberOfPhysicalCPU);
-  sysctlbyname("hw.physicalcpu", &this->NumberOfPhysicalCPU, &len,
-               KWSYS_NULLPTR, 0);
+  sysctlbyname("hw.physicalcpu", &this->NumberOfPhysicalCPU, &len, nullptr, 0);
   len = sizeof(this->NumberOfLogicalCPU);
-  sysctlbyname("hw.logicalcpu", &this->NumberOfLogicalCPU, &len, KWSYS_NULLPTR,
-               0);
+  sysctlbyname("hw.logicalcpu", &this->NumberOfLogicalCPU, &len, nullptr, 0);
 
   int cores_per_package = 0;
   len = sizeof(cores_per_package);
   err = sysctlbyname("machdep.cpu.cores_per_package", &cores_per_package, &len,
-                     KWSYS_NULLPTR, 0);
+                     nullptr, 0);
   // That name was not found, default to 1
   this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical =
     err != 0 ? 1 : static_cast<unsigned char>(cores_per_package);
 
   len = sizeof(value);
-  sysctlbyname("hw.cpufrequency", &value, &len, KWSYS_NULLPTR, 0);
+  sysctlbyname("hw.cpufrequency", &value, &len, nullptr, 0);
   this->CPUSpeedInMHz = static_cast<float>(value) / 1000000;
 
   // Chip family
   len = sizeof(this->ChipID.Family);
   // Seems only the intel chips will have this name so if this fails it is
   // probably a PPC machine
-  err = sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len,
-                     KWSYS_NULLPTR, 0);
+  err =
+    sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, nullptr, 0);
   if (err != 0) // Go back to names we know but are less descriptive
   {
     this->ChipID.Family = 0;
     ::memset(retBuf, 0, 128);
     len = 32;
-    err = sysctlbyname("hw.machine", &retBuf, &len, KWSYS_NULLPTR, 0);
+    err = sysctlbyname("hw.machine", &retBuf, &len, nullptr, 0);
     std::string machineBuf(retBuf);
     if (machineBuf.find_first_of("Power") != std::string::npos) {
       this->ChipID.Vendor = "IBM";
       len = sizeof(this->ChipID.Family);
-      err = sysctlbyname("hw.cputype", &this->ChipID.Family, &len,
-                         KWSYS_NULLPTR, 0);
+      err = sysctlbyname("hw.cputype", &this->ChipID.Family, &len, nullptr, 0);
       len = sizeof(this->ChipID.Model);
-      err = sysctlbyname("hw.cpusubtype", &this->ChipID.Model, &len,
-                         KWSYS_NULLPTR, 0);
+      err =
+        sysctlbyname("hw.cpusubtype", &this->ChipID.Model, &len, nullptr, 0);
       this->FindManufacturer();
     }
   } else // Should be an Intel Chip.
   {
     len = sizeof(this->ChipID.Family);
     err = sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len,
-                       KWSYS_NULLPTR, 0);
+                       nullptr, 0);
 
     ::memset(retBuf, 0, 128);
     len = 128;
-    err = sysctlbyname("machdep.cpu.vendor", retBuf, &len, KWSYS_NULLPTR, 0);
+    err = sysctlbyname("machdep.cpu.vendor", retBuf, &len, nullptr, 0);
     // Chip Vendor
     this->ChipID.Vendor = retBuf;
     this->FindManufacturer();
 
     // Chip Model
     len = sizeof(value);
-    err = sysctlbyname("machdep.cpu.model", &value, &len, KWSYS_NULLPTR, 0);
+    err = sysctlbyname("machdep.cpu.model", &value, &len, nullptr, 0);
     this->ChipID.Model = static_cast<int>(value);
 
     // Chip Stepping
     len = sizeof(value);
     value = 0;
-    err = sysctlbyname("machdep.cpu.stepping", &value, &len, KWSYS_NULLPTR, 0);
+    err = sysctlbyname("machdep.cpu.stepping", &value, &len, nullptr, 0);
     if (!err) {
       this->ChipID.Revision = static_cast<int>(value);
     }
 
     // feature string
-    char* buf = KWSYS_NULLPTR;
+    char* buf = nullptr;
     size_t allocSize = 128;
 
     err = 0;
@@ -4592,8 +4570,7 @@
       }
       buf[0] = ' ';
       len = allocSize - 2; // keep space for leading and trailing space
-      err =
-        sysctlbyname("machdep.cpu.features", buf + 1, &len, KWSYS_NULLPTR, 0);
+      err = sysctlbyname("machdep.cpu.features", buf + 1, &len, nullptr, 0);
     }
     if (!err && buf && len) {
       // now we can match every flags as space + flag + space
@@ -4634,8 +4611,7 @@
   // brand string
   ::memset(retBuf, 0, sizeof(retBuf));
   len = sizeof(retBuf);
-  err =
-    sysctlbyname("machdep.cpu.brand_string", retBuf, &len, KWSYS_NULLPTR, 0);
+  err = sysctlbyname("machdep.cpu.brand_string", retBuf, &len, nullptr, 0);
   if (!err) {
     this->ChipID.ProcessorName = retBuf;
     this->ChipID.ModelName = retBuf;
@@ -4643,10 +4619,10 @@
 
   // Cache size
   len = sizeof(value);
-  err = sysctlbyname("hw.l1icachesize", &value, &len, KWSYS_NULLPTR, 0);
+  err = sysctlbyname("hw.l1icachesize", &value, &len, nullptr, 0);
   this->Features.L1CacheSize = static_cast<int>(value);
   len = sizeof(value);
-  err = sysctlbyname("hw.l2cachesize", &value, &len, KWSYS_NULLPTR, 0);
+  err = sysctlbyname("hw.l2cachesize", &value, &len, nullptr, 0);
   this->Features.L2CacheSize = static_cast<int>(value);
 
   return true;
@@ -4683,7 +4659,7 @@
 
   kwsysProcess_Execute(gp);
 
-  char* data = KWSYS_NULLPTR;
+  char* data = nullptr;
   int length;
   double timeout = 255;
   int pipe; // pipe id as returned by kwsysProcess_WaitForData()
@@ -4695,7 +4671,7 @@
   {
     buffer.append(data, length);
   }
-  kwsysProcess_WaitForExit(gp, KWSYS_NULLPTR);
+  kwsysProcess_WaitForExit(gp, nullptr);
 
   int result = 0;
   switch (kwsysProcess_GetState(gp)) {
@@ -4766,7 +4742,7 @@
   for (size_t i = 0; i < args_string.size(); ++i) {
     args.push_back(args_string[i].c_str());
   }
-  args.push_back(KWSYS_NULLPTR);
+  args.push_back(nullptr);
 
   std::string buffer = this->RunProcess(args);
 
@@ -4965,7 +4941,7 @@
 #  endif
   size_t sz = sizeof(k);
 
-  if (sysctl(ctrl, 2, &k, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5036,7 +5012,7 @@
   size_t sz = sizeof(k);
   int ctrl[2] = { CTL_HW, HW_NCPU };
 
-  if (sysctl(ctrl, 2, &k, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5046,7 +5022,7 @@
 #  if defined(HW_CPUSPEED)
   ctrl[1] = HW_CPUSPEED;
 
-  if (sysctl(ctrl, 2, &k, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5057,7 +5033,7 @@
   ctrl[0] = CTL_MACHDEP;
   ctrl[1] = CPU_SSE;
 
-  if (sysctl(ctrl, 2, &k, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5068,7 +5044,7 @@
   ctrl[0] = CTL_MACHDEP;
   ctrl[1] = CPU_SSE2;
 
-  if (sysctl(ctrl, 2, &k, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5081,7 +5057,7 @@
   char vbuf[25];
   ::memset(vbuf, 0, sizeof(vbuf));
   sz = sizeof(vbuf) - 1;
-  if (sysctl(ctrl, 2, vbuf, &sz, NULL, 0) != 0) {
+  if (sysctl(ctrl, 2, vbuf, &sz, nullptr, 0) != 0) {
     return false;
   }
 
@@ -5293,7 +5269,7 @@
         RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                       L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0,
                       KEY_QUERY_VALUE, &hKey);
-        RegQueryValueExW(hKey, L"ProductType", NULL, NULL,
+        RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
                          (LPBYTE)szProductType, &dwBufLen);
         RegCloseKey(hKey);
 
@@ -5335,13 +5311,13 @@
 
         // Load the Kernel32 DLL.
         hKernelDLL = LoadLibraryW(L"kernel32");
-        if (hKernelDLL != NULL) {
+        if (hKernelDLL != nullptr) {
           // Only XP and .NET Server support IsWOW64Process so... Load
           // dynamically!
           DLLProc = (LPFNPROC)GetProcAddress(hKernelDLL, "IsWow64Process");
 
           // If the function address is valid, call the function.
-          if (DLLProc != NULL)
+          if (DLLProc != nullptr)
             (DLLProc)(GetCurrentProcess(), &bIsWindows64Bit);
           else
             bIsWindows64Bit = false;
@@ -5456,7 +5432,7 @@
   std::vector<const char*> args;
   args.push_back("sw_vers");
   args.push_back(arg);
-  args.push_back(KWSYS_NULLPTR);
+  args.push_back(nullptr);
   ver = this->RunProcess(args);
   this->TrimNewline(ver);
 #else
diff --git a/Source/kwsys/SystemInformation.hxx.in b/Source/kwsys/SystemInformation.hxx.in
index 5e93878..fc42e9d 100644
--- a/Source/kwsys/SystemInformation.hxx.in
+++ b/Source/kwsys/SystemInformation.hxx.in
@@ -115,8 +115,8 @@
   // returns an informative general description if the installed and
   // available ram on this system. See the GetHostMemoryTotal, and
   // Get{Host,Proc}MemoryAvailable methods for more information.
-  std::string GetMemoryDescription(const char* hostLimitEnvVarName = NULL,
-                                   const char* procLimitEnvVarName = NULL);
+  std::string GetMemoryDescription(const char* hostLimitEnvVarName = nullptr,
+                                   const char* procLimitEnvVarName = nullptr);
 
   // Retrieve amount of physical memory installed on the system in KiB
   // units.
@@ -128,7 +128,7 @@
   // parallel. The amount of memory reported may differ from the host
   // total if a host wide resource limit is applied. Such reource limits
   // are reported to us via an application specified environment variable.
-  LongLong GetHostMemoryAvailable(const char* hostLimitEnvVarName = NULL);
+  LongLong GetHostMemoryAvailable(const char* hostLimitEnvVarName = nullptr);
 
   // Get total system RAM in units of KiB available to this process.
   // This may differ from the host available if a per-process resource
@@ -136,8 +136,8 @@
   // system via rlimit API. Resource limits that are not imposed via
   // rlimit API may be reported to us via an application specified
   // environment variable.
-  LongLong GetProcMemoryAvailable(const char* hostLimitEnvVarName = NULL,
-                                  const char* procLimitEnvVarName = NULL);
+  LongLong GetProcMemoryAvailable(const char* hostLimitEnvVarName = nullptr,
+                                  const char* procLimitEnvVarName = nullptr);
 
   // Get the system RAM used by all processes on the host, in units of KiB.
   LongLong GetHostMemoryUsed();
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index ae7a18a..ce4d6ef 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -192,15 +192,15 @@
 {
   const size_t maxlen = KWSYS_SYSTEMTOOLS_MAXPATH;
   snprintf(resolved_path, maxlen, "%s", path);
-  BPath normalized(resolved_path, NULL, true);
+  BPath normalized(resolved_path, nullptr, true);
   const char* resolved = normalized.Path();
-  if (resolved != NULL) // NULL == No such file.
+  if (resolved != nullptr) // nullptr == No such file.
   {
     if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) {
       return resolved_path;
     }
   }
-  return NULL; // something went wrong.
+  return nullptr; // something went wrong.
 }
 #endif
 
@@ -273,12 +273,12 @@
     if (bufferLen) {
       *errorMessage = "Destination path buffer size too small.";
     } else if (unsigned int errorId = GetLastError()) {
-      LPSTR message = NULL;
+      LPSTR message = nullptr;
       DWORD size = FormatMessageA(
         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
           FORMAT_MESSAGE_IGNORE_INSERTS,
-        NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-        (LPSTR)&message, 0, NULL);
+        nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPSTR)&message, 0, nullptr);
       *errorMessage = std::string(message, size);
       LocalFree(message);
     } else {
@@ -313,7 +313,7 @@
   return chdir(dir.c_str());
 }
 inline void Realpath(const std::string& path, std::string& resolved_path,
-                     std::string* errorMessage = KWSYS_NULLPTR)
+                     std::string* errorMessage = nullptr)
 {
   char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];
 
@@ -359,7 +359,7 @@
           11644473600.0);
 #else
   struct timeval t;
-  gettimeofday(&t, KWSYS_NULLPTR);
+  gettimeofday(&t, nullptr);
   return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec);
 #endif
 }
@@ -389,8 +389,8 @@
 #else
     const char* leq = strchr(l, '=');
     const char* req = strchr(r, '=');
-    size_t llen = leq ? (leq - l) : strlen(l);
-    size_t rlen = req ? (req - r) : strlen(r);
+    size_t llen = leq ? static_cast<size_t>(leq - l) : strlen(l);
+    size_t rlen = req ? static_cast<size_t>(req - r) : strlen(r);
     if (llen == rlen) {
       return strncmp(l, r, llen) < 0;
     } else {
@@ -420,7 +420,7 @@
 
   const envchar* Release(const envchar* env)
   {
-    const envchar* old = KWSYS_NULLPTR;
+    const envchar* old = nullptr;
     iterator i = this->find(env);
     if (i != this->end()) {
       old = *i;
@@ -630,7 +630,7 @@
     }
     return menv.c_str();
   }
-  return KWSYS_NULLPTR;
+  return nullptr;
 }
 #endif
 
@@ -684,7 +684,7 @@
 #else
   const char* v = getenv(key);
 #endif
-  return v != KWSYS_NULLPTR;
+  return v != nullptr;
 }
 
 bool SystemTools::HasEnv(const std::string& key)
@@ -915,7 +915,7 @@
   while ((pos = dir.find('/', pos)) != std::string::npos) {
     topdir = dir.substr(0, pos);
 
-    if (Mkdir(topdir) == 0 && mode != KWSYS_NULLPTR) {
+    if (Mkdir(topdir) == 0 && mode != nullptr) {
       SystemTools::SetPermissions(topdir, *mode);
     }
 
@@ -934,7 +934,7 @@
     ) {
       return false;
     }
-  } else if (mode != KWSYS_NULLPTR) {
+  } else if (mode != nullptr) {
     SystemTools::SetPermissions(topdir, *mode);
   }
 
@@ -1055,7 +1055,7 @@
   // only add the modes when on a system that supports Wow64.
   static FARPROC wow64p =
     GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process");
-  if (wow64p == NULL) {
+  if (wow64p == nullptr) {
     return mode;
   }
 
@@ -1136,7 +1136,7 @@
     DWORD dwType, dwSize;
     dwSize = 1023;
     wchar_t data[1024];
-    if (RegQueryValueExW(hKey, Encoding::ToWide(valuename).c_str(), NULL,
+    if (RegQueryValueExW(hKey, Encoding::ToWide(valuename).c_str(), nullptr,
                          &dwType, (BYTE*)data, &dwSize) == ERROR_SUCCESS) {
       if (dwType == REG_SZ) {
         value = Encoding::ToNarrow(data);
@@ -1186,7 +1186,7 @@
   wchar_t lpClass[] = L"";
   if (RegCreateKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, lpClass,
                       REG_OPTION_NON_VOLATILE,
-                      SystemToolsMakeRegistryMode(KEY_WRITE, view), NULL,
+                      SystemToolsMakeRegistryMode(KEY_WRITE, view), nullptr,
                       &hKey, &dwDummy) != ERROR_SUCCESS) {
     return false;
   }
@@ -1252,10 +1252,10 @@
 
   hFile1 =
     CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ,
-                NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
   hFile2 =
     CreateFileW(Encoding::ToWide(file2).c_str(), GENERIC_READ, FILE_SHARE_READ,
-                NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
   if (hFile1 == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE) {
     if (hFile1 != INVALID_HANDLE_VALUE) {
       CloseHandle(hFile1);
@@ -1347,7 +1347,7 @@
     // even if we do not have permission to read the file itself
     HANDLE handle =
       CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(), 0, 0,
-                  NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                  nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
 
     if (handle == INVALID_HANDLE_VALUE) {
       return false;
@@ -1493,12 +1493,12 @@
   CloseHandle(h);
 #elif KWSYS_CXX_HAS_UTIMENSAT
   // utimensat is only available on newer Unixes and macOS 10.13+
-  if (utimensat(AT_FDCWD, filename.c_str(), NULL, 0) < 0) {
+  if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) {
     return false;
   }
 #else
   // fall back to utimes
-  if (utimes(filename.c_str(), NULL) < 0) {
+  if (utimes(filename.c_str(), nullptr) < 0) {
     return false;
   }
 #endif
@@ -1653,7 +1653,7 @@
   size_t len1 = strlen(str1);
   char* newstr = new char[len1 + strlen(str2) + 1];
   if (!newstr) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   strcpy(newstr, str1);
   strcat(newstr + len1, str2);
@@ -1676,7 +1676,7 @@
   size_t len1 = strlen(str1), len2 = strlen(str2);
   char* newstr = new char[len1 + len2 + strlen(str3) + 1];
   if (!newstr) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   strcpy(newstr, str1);
   strcat(newstr + len1, str2);
@@ -1726,7 +1726,7 @@
 char* SystemTools::RemoveChars(const char* str, const char* toremove)
 {
   if (!str) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   char* clean_str = new char[strlen(str) + 1];
   char* ptr = clean_str;
@@ -1748,7 +1748,7 @@
 char* SystemTools::RemoveCharsButUpperHex(const char* str)
 {
   if (!str) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
   char* clean_str = new char[strlen(str) + 1];
   char* ptr = clean_str;
@@ -1829,7 +1829,7 @@
 const char* SystemTools::FindLastString(const char* str1, const char* str2)
 {
   if (!str1 || !str2) {
-    return KWSYS_NULLPTR;
+    return nullptr;
   }
 
   size_t len1 = strlen(str1), len2 = strlen(str2);
@@ -1842,7 +1842,7 @@
     } while (ptr-- != str1);
   }
 
-  return KWSYS_NULLPTR;
+  return nullptr;
 }
 
 // Duplicate string
@@ -1852,7 +1852,7 @@
     char* newstr = new char[strlen(str) + 1];
     return strcpy(newstr, str);
   }
-  return KWSYS_NULLPTR;
+  return nullptr;
 }
 
 // Return a cropped string
@@ -2169,24 +2169,24 @@
   return ret;
 }
 
+/**
+ * Append the filename from the path source to the directory name dir.
+ */
+static std::string FileInDir(const std::string& source, const std::string& dir)
+{
+  std::string new_destination = dir;
+  SystemTools::ConvertToUnixSlashes(new_destination);
+  return new_destination + '/' + SystemTools::GetFilenameName(source);
+}
+
 bool SystemTools::CopyFileIfDifferent(const std::string& source,
                                       const std::string& destination)
 {
   // special check for a destination that is a directory
   // FilesDiffer does not handle file to directory compare
   if (SystemTools::FileIsDirectory(destination)) {
-    std::string new_destination = destination;
-    SystemTools::ConvertToUnixSlashes(new_destination);
-    new_destination += '/';
-    std::string source_name = source;
-    new_destination += SystemTools::GetFilenameName(source_name);
-    if (SystemTools::FilesDiffer(source, new_destination)) {
-      return SystemTools::CopyFileAlways(source, destination);
-    } else {
-      // the files are the same so the copy is done return
-      // true
-      return true;
-    }
+    const std::string new_destination = FileInDir(source, destination);
+    return SystemTools::CopyFileIfDifferent(source, new_destination);
   }
   // source and destination are files so do a copy if they
   // are different
@@ -2612,101 +2612,6 @@
   return strerror(e);
 }
 
-#ifdef _WIN32
-
-static bool IsJunction(const std::wstring& source)
-{
-#  ifdef FSCTL_GET_REPARSE_POINT
-  const DWORD JUNCTION_ATTRS =
-    FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
-  DWORD attrs = GetFileAttributesW(source.c_str());
-  if (attrs == INVALID_FILE_ATTRIBUTES) {
-    return false;
-  }
-  if ((attrs & JUNCTION_ATTRS) != JUNCTION_ATTRS) {
-    return false;
-  }
-
-  // Adjust privileges so that we can succefully open junction points.
-  HANDLE token;
-  TOKEN_PRIVILEGES privs;
-  OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token);
-  LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &privs.Privileges[0].Luid);
-  privs.PrivilegeCount = 1;
-  privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-  AdjustTokenPrivileges(token, FALSE, &privs, sizeof(TOKEN_PRIVILEGES), NULL,
-                        NULL);
-  CloseHandle(token);
-
-  HANDLE dir = CreateFileW(
-    source.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
-    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
-  if (dir == INVALID_HANDLE_VALUE) {
-    return false;
-  }
-
-  // Query whether this is a reparse point or not.
-  BYTE buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-  REPARSE_GUID_DATA_BUFFER* reparse_buffer = (REPARSE_GUID_DATA_BUFFER*)buffer;
-  DWORD sentinel;
-
-  BOOL success =
-    DeviceIoControl(dir, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse_buffer,
-                    MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &sentinel, NULL);
-
-  CloseHandle(dir);
-
-  return (success &&
-          (reparse_buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT));
-#  else
-  return false;
-#  endif
-}
-
-static bool DeleteJunction(const std::wstring& source)
-{
-#  ifdef FSCTL_DELETE_REPARSE_POINT
-  // Adjust privileges so that we can succefully open junction points as
-  // read/write.
-  HANDLE token;
-  TOKEN_PRIVILEGES privs;
-  OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token);
-  LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &privs.Privileges[0].Luid);
-  privs.PrivilegeCount = 1;
-  privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-  AdjustTokenPrivileges(token, FALSE, &privs, sizeof(TOKEN_PRIVILEGES), NULL,
-                        NULL);
-  CloseHandle(token);
-
-  HANDLE dir = CreateFileW(
-    source.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
-    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
-  if (dir == INVALID_HANDLE_VALUE) {
-    return false;
-  }
-
-  // Set up the structure so that we can delete the junction.
-  std::vector<BYTE> buffer(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 0);
-  REPARSE_GUID_DATA_BUFFER* reparse_buffer =
-    (REPARSE_GUID_DATA_BUFFER*)&buffer[0];
-  DWORD sentinel;
-
-  reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
-
-  BOOL success = DeviceIoControl(
-    dir, FSCTL_DELETE_REPARSE_POINT, reparse_buffer,
-    REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &sentinel, NULL);
-
-  CloseHandle(dir);
-
-  return !!success;
-#  else
-  return false;
-#  endif
-}
-
-#endif
-
 bool SystemTools::RemoveFile(const std::string& source)
 {
 #ifdef _WIN32
@@ -2728,9 +2633,7 @@
     SetLastError(err);
     return false;
   }
-  if (IsJunction(ws) && DeleteJunction(ws)) {
-    return true;
-  }
+
   const DWORD DIRECTORY_SOFT_LINK_ATTRS =
     FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
   DWORD attrs = GetFileAttributesW(ws.c_str());
@@ -3115,16 +3018,16 @@
       // * a file or directory that has an associated reparse point, or
       // * a file that is a symbolic link.
       HANDLE hFile = CreateFileW(
-        path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
-        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
       if (hFile == INVALID_HANDLE_VALUE) {
         return false;
       }
       byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
       DWORD bytesReturned = 0;
-      if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer,
+      if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
                            MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
-                           NULL)) {
+                           nullptr)) {
         CloseHandle(hFile);
         // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be
         // a symbolic link if it is not a reparse point.
@@ -3155,7 +3058,7 @@
 #if defined(_WIN32)
   HANDLE hFile =
     CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
-                NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
   if (hFile == INVALID_HANDLE_VALUE) {
     return false;
   }
@@ -3316,7 +3219,7 @@
 
 std::string SystemTools::CollapseFullPath(const std::string& in_relative)
 {
-  return SystemTools::CollapseFullPath(in_relative, KWSYS_NULLPTR);
+  return SystemTools::CollapseFullPath(in_relative, nullptr);
 }
 
 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
@@ -3403,11 +3306,7 @@
         out_components.emplace_back(std::move(*i));
       }
     } else if (!i->empty() && *i != cur) {
-#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
-      out_components.push_back(std::move(*i));
-#else
-      out_components.push_back(*i);
-#endif
+      out_components.emplace_back(std::move(*i));
     }
   }
 }
@@ -4114,7 +4013,7 @@
   }
 
   std::wstring wtempPath = Encoding::ToWide(tempPath);
-  DWORD ret = GetShortPathNameW(wtempPath.c_str(), NULL, 0);
+  DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
   std::vector<wchar_t> buffer(ret);
   if (ret != 0) {
     ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
@@ -4522,7 +4421,7 @@
           return 0;
         }
 
-        lRet = RegQueryValueExW(hKey, L"ProductType", NULL, NULL,
+        lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
                                 (LPBYTE)szProductType, &dwBufLen);
 
         if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
@@ -4743,7 +4642,7 @@
       // Test progressively shorter logical-to-physical mappings.
       std::string cwd_str = cwd;
       std::string pwd_path;
-      Realpath(pwd_str.c_str(), pwd_path);
+      Realpath(pwd_str, pwd_path);
       while (cwd_str == pwd_path && cwd_str != pwd_str) {
         // The current pair of paths is a working logical mapping.
         cwd_changed = cwd_str;
@@ -4753,7 +4652,7 @@
         // mapping still works.
         pwd_str = SystemTools::GetFilenamePath(pwd_str);
         cwd_str = SystemTools::GetFilenamePath(cwd_str);
-        Realpath(pwd_str.c_str(), pwd_path);
+        Realpath(pwd_str, pwd_path);
       }
 
       // Add the translation to keep the logical path name.
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in
index dd1266b..c4ab9d4 100644
--- a/Source/kwsys/SystemTools.hxx.in
+++ b/Source/kwsys/SystemTools.hxx.in
@@ -400,9 +400,10 @@
    *  installPrefix is a possibly null pointer to the install directory.
    */
   static bool FindProgramPath(const char* argv0, std::string& pathOut,
-                              std::string& errorMsg, const char* exeName = 0,
-                              const char* buildDir = 0,
-                              const char* installPrefix = 0);
+                              std::string& errorMsg,
+                              const char* exeName = nullptr,
+                              const char* buildDir = nullptr,
+                              const char* installPrefix = nullptr);
 
   /**
    * Given a path to a file or directory, convert it to a full path.
@@ -420,11 +421,11 @@
    * Get the real path for a given path, removing all symlinks.  In
    * the event of an error (non-existent path, permissions issue,
    * etc.) the original path is returned if errorMessage pointer is
-   * NULL.  Otherwise empty string is returned and errorMessage
+   * nullptr.  Otherwise empty string is returned and errorMessage
    * contains error description.
    */
   static std::string GetRealPath(const std::string& path,
-                                 std::string* errorMessage = 0);
+                                 std::string* errorMessage = nullptr);
 
   /**
    * Split a path name into its root component and the rest of the
@@ -442,7 +443,7 @@
    * given.
    */
   static const char* SplitPathRootComponent(const std::string& p,
-                                            std::string* root = 0);
+                                            std::string* root = nullptr);
 
   /**
    * Split a path name into its basic components.  The first component
@@ -528,7 +529,8 @@
    * be true when the line read had a newline character.
    */
   static bool GetLineFromStream(std::istream& istr, std::string& line,
-                                bool* has_newline = 0, long sizeLimit = -1);
+                                bool* has_newline = nullptr,
+                                long sizeLimit = -1);
 
   /**
    * Get the parent directory of the directory or file
@@ -563,8 +565,9 @@
    * can make a full path even if none of the directories existed
    * prior to calling this function.
    */
-  static bool MakeDirectory(const char* path, const mode_t* mode = 0);
-  static bool MakeDirectory(const std::string& path, const mode_t* mode = 0);
+  static bool MakeDirectory(const char* path, const mode_t* mode = nullptr);
+  static bool MakeDirectory(const std::string& path,
+                            const mode_t* mode = nullptr);
 
   /**
    * Copy the source file to the destination file only
@@ -842,7 +845,8 @@
    *  string vector passed in.  If env is set then the value
    *  of env will be used instead of PATH.
    */
-  static void GetPath(std::vector<std::string>& path, const char* env = 0);
+  static void GetPath(std::vector<std::string>& path,
+                      const char* env = nullptr);
 
   /**
    * Read an environment variable
diff --git a/Source/kwsys/hashtable.hxx.in b/Source/kwsys/hashtable.hxx.in
index 0981c66..8c4b002 100644
--- a/Source/kwsys/hashtable.hxx.in
+++ b/Source/kwsys/hashtable.hxx.in
@@ -354,7 +354,7 @@
     return end();
   }
 
-  iterator end() { return iterator(0, this); }
+  iterator end() { return iterator(nullptr, this); }
 
   const_iterator begin() const
   {
@@ -364,7 +364,7 @@
     return end();
   }
 
-  const_iterator end() const { return const_iterator(0, this); }
+  const_iterator end() const { return const_iterator(nullptr, this); }
 
   friend bool operator==<>(const hashtable&, const hashtable&);
 
@@ -510,7 +510,7 @@
   {
     const size_type __n_buckets = _M_next_size(__n);
     _M_buckets.reserve(__n_buckets);
-    _M_buckets.insert(_M_buckets.end(), __n_buckets, (_Node*)0);
+    _M_buckets.insert(_M_buckets.end(), __n_buckets, (_Node*)nullptr);
     _M_num_elements = 0;
   }
 
@@ -544,7 +544,7 @@
   _Node* _M_new_node(const value_type& __obj)
   {
     _Node* __n = _M_get_node();
-    __n->_M_next = 0;
+    __n->_M_next = nullptr;
     try {
       construct(&__n->_M_val, __obj);
       return __n;
@@ -839,9 +839,9 @@
   else if (__f_bucket == __l_bucket)
     _M_erase_bucket(__f_bucket, __first._M_cur, __last._M_cur);
   else {
-    _M_erase_bucket(__f_bucket, __first._M_cur, 0);
+    _M_erase_bucket(__f_bucket, __first._M_cur, nullptr);
     for (size_type __n = __f_bucket + 1; __n < __l_bucket; ++__n)
-      _M_erase_bucket(__n, 0);
+      _M_erase_bucket(__n, nullptr);
     if (__l_bucket != _M_buckets.size())
       _M_erase_bucket(__l_bucket, __last._M_cur);
   }
@@ -873,7 +873,8 @@
   if (__num_elements_hint > __old_n) {
     const size_type __n = _M_next_size(__num_elements_hint);
     if (__n > __old_n) {
-      _M_buckets_type __tmp(__n, (_Node*)(0), _M_buckets.get_allocator());
+      _M_buckets_type __tmp(__n, (_Node*)(nullptr),
+                            _M_buckets.get_allocator());
       try {
         for (size_type __bucket = 0; __bucket < __old_n; ++__bucket) {
           _Node* __first = _M_buckets[__bucket];
@@ -940,12 +941,12 @@
 {
   for (size_type __i = 0; __i < _M_buckets.size(); ++__i) {
     _Node* __cur = _M_buckets[__i];
-    while (__cur != 0) {
+    while (__cur != nullptr) {
       _Node* __next = __cur->_M_next;
       _M_delete_node(__cur);
       __cur = __next;
     }
-    _M_buckets[__i] = 0;
+    _M_buckets[__i] = nullptr;
   }
   _M_num_elements = 0;
 }
@@ -956,7 +957,7 @@
 {
   _M_buckets.clear();
   _M_buckets.reserve(__ht._M_buckets.size());
-  _M_buckets.insert(_M_buckets.end(), __ht._M_buckets.size(), (_Node*)0);
+  _M_buckets.insert(_M_buckets.end(), __ht._M_buckets.size(), (_Node*)nullptr);
   try {
     for (size_type __i = 0; __i < __ht._M_buckets.size(); ++__i) {
       const _Node* __cur = __ht._M_buckets[__i];
diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx
index 15f9c02..1778a9b 100644
--- a/Source/kwsys/testCommandLineArguments.cxx
+++ b/Source/kwsys/testCommandLineArguments.cxx
@@ -76,7 +76,7 @@
 
   int some_int_variable = 10;
   double some_double_variable = 10.10;
-  char* some_string_variable = KWSYS_NULLPTR;
+  char* some_string_variable = nullptr;
   std::string some_stl_string_variable;
   bool some_bool_variable = false;
   bool some_bool_variable1 = false;
@@ -203,7 +203,7 @@
 
   for (cc = 0; cc < strings_argument.size(); ++cc) {
     delete[] strings_argument[cc];
-    strings_argument[cc] = KWSYS_NULLPTR;
+    strings_argument[cc] = nullptr;
   }
   return res;
 }
diff --git a/Source/kwsys/testCommandLineArguments1.cxx b/Source/kwsys/testCommandLineArguments1.cxx
index 9895008..64561b1 100644
--- a/Source/kwsys/testCommandLineArguments1.cxx
+++ b/Source/kwsys/testCommandLineArguments1.cxx
@@ -21,7 +21,7 @@
   arg.Initialize(argc, argv);
 
   int n = 0;
-  char* m = KWSYS_NULLPTR;
+  char* m = nullptr;
   std::string p;
   int res = 0;
 
@@ -55,11 +55,11 @@
     delete[] m;
   }
 
-  char** newArgv = KWSYS_NULLPTR;
+  char** newArgv = nullptr;
   int newArgc = 0;
   arg.GetUnusedArguments(&newArgc, &newArgv);
   int cc;
-  const char* valid_unused_args[9] = { KWSYS_NULLPTR,
+  const char* valid_unused_args[9] = { nullptr,
                                        "--ignored",
                                        "--second-ignored",
                                        "third-ignored",
diff --git a/Source/kwsys/testConsoleBuf.cxx b/Source/kwsys/testConsoleBuf.cxx
index b6ad118..4b7ddf0 100644
--- a/Source/kwsys/testConsoleBuf.cxx
+++ b/Source/kwsys/testConsoleBuf.cxx
@@ -51,7 +51,7 @@
   LPWSTR message;
   if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM,
-                     NULL, errorCode, 0, (LPWSTR)&message, 0, NULL)) {
+                     nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) {
     std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message)
               << std::endl;
     HeapFree(GetProcessHeap(), 0, message);
@@ -124,7 +124,7 @@
   }
 
   WCHAR cmd[MAX_PATH];
-  if (GetModuleFileNameW(NULL, cmd, MAX_PATH) == 0) {
+  if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) {
     std::cerr << "GetModuleFileName failed!" << std::endl;
     return false;
   }
@@ -136,14 +136,14 @@
   wcscat(cmd, L".exe");
 
   bool success =
-    CreateProcessW(NULL,            // No module name (use command line)
+    CreateProcessW(nullptr,         // No module name (use command line)
                    cmd,             // Command line
-                   NULL,            // Process handle not inheritable
-                   NULL,            // Thread handle not inheritable
+                   nullptr,         // Process handle not inheritable
+                   nullptr,         // Thread handle not inheritable
                    bInheritHandles, // Set handle inheritance
                    dwCreationFlags,
-                   NULL,         // Use parent's environment block
-                   NULL,         // Use parent's starting directory
+                   nullptr,      // Use parent's environment block
+                   nullptr,      // Use parent's starting directory
                    &startupInfo, // Pointer to STARTUPINFO structure
                    &processInfo) !=
     0; // Pointer to PROCESS_INFORMATION structure
@@ -174,7 +174,7 @@
   SECURITY_ATTRIBUTES securityAttributes;
   securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
   securityAttributes.bInheritHandle = TRUE;
-  securityAttributes.lpSecurityDescriptor = NULL;
+  securityAttributes.lpSecurityDescriptor = nullptr;
   return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false
                                                                       : true;
 }
@@ -194,7 +194,7 @@
   SECURITY_ATTRIBUTES securityAttributes;
   securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
   securityAttributes.bInheritHandle = TRUE;
-  securityAttributes.lpSecurityDescriptor = NULL;
+  securityAttributes.lpSecurityDescriptor = nullptr;
 
   HANDLE file =
     CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE,
@@ -202,7 +202,7 @@
                 &securityAttributes,
                 CREATE_ALWAYS, // overwrite existing
                 FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
-                NULL); // no template
+                nullptr); // no template
   if (file == INVALID_HANDLE_VALUE) {
     DWORD lastError = GetLastError();
     std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")"
@@ -288,7 +288,7 @@
     DWORD bytesWritten = 0;
     if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(),
                    (DWORD)encodedInputTestString.size(), &bytesWritten,
-                   NULL) ||
+                   nullptr) ||
         bytesWritten == 0) {
       throw std::runtime_error("WriteFile failed!");
     }
@@ -305,7 +305,8 @@
           throw std::runtime_error("WaitForSingleObject failed!");
         }
         DWORD bytesRead = 0;
-        if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead, NULL) ||
+        if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead,
+                      nullptr) ||
             bytesRead == 0) {
           throw std::runtime_error("ReadFile#1 failed!");
         }
@@ -313,7 +314,7 @@
         if ((bytesRead <
                encodedTestString.size() + 1 + encodedInputTestString.size() &&
              !ReadFile(outPipeRead, buffer + bytesRead,
-                       sizeof(buffer) - bytesRead, &bytesRead, NULL)) ||
+                       sizeof(buffer) - bytesRead, &bytesRead, nullptr)) ||
             bytesRead == 0) {
           throw std::runtime_error("ReadFile#2 failed!");
         }
@@ -324,7 +325,7 @@
                    encodedInputTestString.size()) == 0) {
           bytesRead = 0;
           if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead,
-                        NULL) ||
+                        nullptr) ||
               bytesRead == 0) {
             throw std::runtime_error("ReadFile#3 failed!");
           }
@@ -383,13 +384,13 @@
     char buffer2[200];
 
     int length;
-    if ((length =
-           WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, -1,
-                               buffer, sizeof(buffer), NULL, NULL)) == 0) {
+    if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString,
+                                      -1, buffer, sizeof(buffer), nullptr,
+                                      nullptr)) == 0) {
       throw std::runtime_error("WideCharToMultiByte failed!");
     }
     buffer[length - 1] = '\n';
-    if (!WriteFile(inFile, buffer, length, &bytesWritten, NULL) ||
+    if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) ||
         bytesWritten == 0) {
       throw std::runtime_error("WriteFile failed!");
     }
@@ -413,7 +414,7 @@
             INVALID_SET_FILE_POINTER) {
           throw std::runtime_error("SetFilePointer#1 failed!");
         }
-        if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, NULL) ||
+        if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) ||
             bytesRead == 0) {
           throw std::runtime_error("ReadFile#1 failed!");
         }
@@ -429,7 +430,8 @@
             throw std::runtime_error("SetFilePointer#2 failed!");
           }
 
-          if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead, NULL) ||
+          if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead,
+                        nullptr) ||
               bytesRead == 0) {
             throw std::runtime_error("ReadFile#2 failed!");
           }
@@ -519,12 +521,12 @@
     if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE,
                       &hConsoleKey) == ERROR_SUCCESS) {
       DWORD dwordSize = sizeof(DWORD);
-      if (RegQueryValueExW(hConsoleKey, L"FontFamily", NULL, NULL,
+      if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr,
                            (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
         if (FontFamily != TestFontFamily) {
-          RegQueryValueExW(hConsoleKey, L"FaceName", NULL, NULL,
+          RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr,
                            (LPBYTE)FaceName, &FaceNameSize);
-          RegQueryValueExW(hConsoleKey, L"FontSize", NULL, NULL,
+          RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr,
                            (LPBYTE)&FontSize, &dwordSize);
 
           RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
@@ -557,10 +559,10 @@
     SECURITY_ATTRIBUTES securityAttributes;
     securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
     securityAttributes.bInheritHandle = TRUE;
-    securityAttributes.lpSecurityDescriptor = NULL;
+    securityAttributes.lpSecurityDescriptor = nullptr;
     hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
-                      OPEN_EXISTING, 0, NULL);
+                      OPEN_EXISTING, 0, nullptr);
     if (hIn == INVALID_HANDLE_VALUE) {
       DWORD lastError = GetLastError();
       std::cerr << "CreateFile(CONIN$)" << std::endl;
@@ -568,7 +570,7 @@
     }
     hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
-                       OPEN_EXISTING, 0, NULL);
+                       OPEN_EXISTING, 0, nullptr);
     if (hOut == INVALID_HANDLE_VALUE) {
       DWORD lastError = GetLastError();
       std::cerr << "CreateFile(CONOUT$)" << std::endl;
@@ -630,7 +632,7 @@
   }
 #  endif
 
-  if (createProcess(NULL, NULL, NULL)) {
+  if (createProcess(nullptr, nullptr, nullptr)) {
     try {
       DWORD status;
       if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) !=
@@ -746,7 +748,7 @@
   int ret = 0;
 
 #if defined(_WIN32)
-  beforeInputEvent = CreateEventW(NULL,
+  beforeInputEvent = CreateEventW(nullptr,
                                   FALSE, // auto-reset event
                                   FALSE, // initial state is nonsignaled
                                   BeforeInputEventName); // object name
@@ -755,7 +757,7 @@
     return 1;
   }
 
-  afterOutputEvent = CreateEventW(NULL, FALSE, FALSE, AfterOutputEventName);
+  afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName);
   if (!afterOutputEvent) {
     std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl;
     return 1;
diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx
index eff2ed7..2421ac0 100644
--- a/Source/kwsys/testDynamicLoader.cxx
+++ b/Source/kwsys/testDynamicLoader.cxx
@@ -21,7 +21,7 @@
 // left on disk.
 #include <testSystemTools.h>
 
-static std::string GetLibName(const char* lname, const char* subdir = NULL)
+static std::string GetLibName(const char* lname, const char* subdir = nullptr)
 {
   // Construct proper name of lib
   std::string slname;
diff --git a/Source/kwsys/testEncoding.cxx b/Source/kwsys/testEncoding.cxx
index fdad1cd..988697b 100644
--- a/Source/kwsys/testEncoding.cxx
+++ b/Source/kwsys/testEncoding.cxx
@@ -84,7 +84,7 @@
   // this conversion could fail
   std::wstring wstr = kwsys::Encoding::ToWide(cstr);
 
-  wstr = kwsys::Encoding::ToWide(KWSYS_NULLPTR);
+  wstr = kwsys::Encoding::ToWide(nullptr);
   if (wstr != L"") {
     const wchar_t* wcstr = wstr.c_str();
     std::cout << "ToWide(NULL) returned";
@@ -112,7 +112,7 @@
   std::string win_str = kwsys::Encoding::ToNarrow(cwstr);
 #endif
 
-  std::string str = kwsys::Encoding::ToNarrow(KWSYS_NULLPTR);
+  std::string str = kwsys::Encoding::ToNarrow(nullptr);
   if (str != "") {
     std::cout << "ToNarrow(NULL) returned " << str << std::endl;
     ret++;
diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c
index f139f58..39aaa23 100644
--- a/Source/kwsys/testProcess.c
+++ b/Source/kwsys/testProcess.c
@@ -477,7 +477,7 @@
       printf("Error in administrating child process: [%s]\n",
              kwsysProcess_GetErrorString(kp));
       break;
-  };
+  }
 
   if (result) {
     if (exception != kwsysProcess_GetExitException(kp)) {
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index ffa6a29..1f3a15b 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -52,7 +52,7 @@
   { "\\\\usr\\local\\bin\\passwd", "//usr/local/bin/passwd" },
   { "\\\\usr\\lo cal\\bin\\pa sswd", "//usr/lo cal/bin/pa sswd" },
   { "\\\\usr\\lo\\ cal\\bin\\pa\\ sswd", "//usr/lo/ cal/bin/pa/ sswd" },
-  { KWSYS_NULLPTR, KWSYS_NULLPTR }
+  { nullptr, nullptr }
 };
 
 static bool CheckConvertToUnixSlashes(std::string const& input,
@@ -71,7 +71,7 @@
 static const char* checkEscapeChars[][4] = {
   { "1 foo 2 bar 2", "12", "\\", "\\1 foo \\2 bar \\2" },
   { " {} ", "{}", "#", " #{#} " },
-  { KWSYS_NULLPTR, KWSYS_NULLPTR, KWSYS_NULLPTR, KWSYS_NULLPTR }
+  { nullptr, nullptr, nullptr, nullptr }
 };
 
 static bool CheckEscapeChars(std::string const& input,
@@ -160,7 +160,7 @@
     res = false;
   }
   // calling with 0 pointer should return false
-  if (kwsys::SystemTools::MakeDirectory(KWSYS_NULLPTR)) {
+  if (kwsys::SystemTools::MakeDirectory(nullptr)) {
     std::cerr << "Problem with MakeDirectory(0)" << std::endl;
     res = false;
   }
@@ -218,11 +218,11 @@
   }
 
   // calling with 0 pointer should return false
-  if (kwsys::SystemTools::FileExists(KWSYS_NULLPTR)) {
+  if (kwsys::SystemTools::FileExists(nullptr)) {
     std::cerr << "Problem with FileExists(0)" << std::endl;
     res = false;
   }
-  if (kwsys::SystemTools::FileExists(KWSYS_NULLPTR, true)) {
+  if (kwsys::SystemTools::FileExists(nullptr, true)) {
     std::cerr << "Problem with FileExists(0) as file" << std::endl;
     res = false;
   }
@@ -999,30 +999,45 @@
   return true;
 }
 
+static std::string readFile(const char* fileName)
+{
+  kwsys::ifstream in(fileName, std::ios::binary);
+  std::stringstream sstr;
+  sstr << in.rdbuf();
+  std::string data = sstr.str();
+  if (!in) {
+    std::cerr << "Failed to read file: " << fileName << std::endl;
+    return std::string();
+  }
+  return data;
+}
+
+struct
+{
+  const char* a;
+  const char* b;
+  bool differ;
+} diff_test_cases[] = { { "one", "one", false },
+                        { "one", "two", true },
+                        { "", "", false },
+                        { "\n", "\r\n", false },
+                        { "one\n", "one\n", false },
+                        { "one\r\n", "one\n", false },
+                        { "one\n", "one", false },
+                        { "one\ntwo", "one\ntwo", false },
+                        { "one\ntwo", "one\r\ntwo", false } };
+
 static bool CheckTextFilesDiffer()
 {
-  struct
-  {
-    const char* a;
-    const char* b;
-    bool differ;
-  } test_cases[] = { { "one", "one", false },
-                     { "one", "two", true },
-                     { "", "", false },
-                     { "\n", "\r\n", false },
-                     { "one\n", "one\n", false },
-                     { "one\r\n", "one\n", false },
-                     { "one\n", "one", false },
-                     { "one\ntwo", "one\ntwo", false },
-                     { "one\ntwo", "one\r\ntwo", false } };
-  const int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
+  const int num_test_cases =
+    sizeof(diff_test_cases) / sizeof(diff_test_cases[0]);
   for (int i = 0; i < num_test_cases; ++i) {
-    if (!writeFile("file_a", test_cases[i].a) ||
-        !writeFile("file_b", test_cases[i].b)) {
+    if (!writeFile("file_a", diff_test_cases[i].a) ||
+        !writeFile("file_b", diff_test_cases[i].b)) {
       return false;
     }
     if (kwsys::SystemTools::TextFilesDiffer("file_a", "file_b") !=
-        test_cases[i].differ) {
+        diff_test_cases[i].differ) {
       std::cerr << "Incorrect TextFilesDiffer result for test case " << i + 1
                 << "." << std::endl;
       return false;
@@ -1032,6 +1047,36 @@
   return true;
 }
 
+static bool CheckCopyFileIfDifferent()
+{
+  bool ret = true;
+  const int num_test_cases =
+    sizeof(diff_test_cases) / sizeof(diff_test_cases[0]);
+  for (int i = 0; i < num_test_cases; ++i) {
+    if (!writeFile("file_a", diff_test_cases[i].a) ||
+        !writeFile("file_b", diff_test_cases[i].b)) {
+      return false;
+    }
+    const char* cptarget =
+      i < 4 ? TEST_SYSTEMTOOLS_BINARY_DIR "/file_b" : "file_b";
+    if (!kwsys::SystemTools::CopyFileIfDifferent("file_a", cptarget)) {
+      std::cerr << "CopyFileIfDifferent() returned false for test case "
+                << i + 1 << "." << std::endl;
+      ret = false;
+      continue;
+    }
+    std::string bdata = readFile("file_b");
+    if (diff_test_cases[i].a != bdata) {
+      std::cerr << "Incorrect CopyFileIfDifferent file contents in test case "
+                << i + 1 << "." << std::endl;
+      ret = false;
+      continue;
+    }
+  }
+
+  return ret;
+}
+
 int testSystemTools(int, char* [])
 {
   bool res = true;
@@ -1077,5 +1122,7 @@
 
   res &= CheckTextFilesDiffer();
 
+  res &= CheckCopyFileIfDifferent();
+
   return res ? 0 : 1;
 }
diff --git a/Templates/TestDriver.cxx.in b/Templates/TestDriver.cxx.in
index c58ef71..ad8bfb0 100644
--- a/Templates/TestDriver.cxx.in
+++ b/Templates/TestDriver.cxx.in
@@ -13,9 +13,15 @@
 @CMAKE_FORWARD_DECLARE_TESTS@
 
 #ifdef __cplusplus
-#define CM_CAST(TYPE, EXPR) static_cast<TYPE>(EXPR)
+#  define CM_CAST(TYPE, EXPR) static_cast<TYPE>(EXPR)
+#  if __cplusplus >= 201103L
+#    define CM_NULL nullptr
+#  else
+#    define CM_NULL NULL
+#  endif
 #else
-#define CM_CAST(TYPE, EXPR) (TYPE)(EXPR)
+#  define CM_CAST(TYPE, EXPR) (TYPE)(EXPR)
+#  define CM_NULL NULL
 #endif
 
 /* Create map.  */
@@ -29,7 +35,7 @@
 
 static functionMapEntry cmakeGeneratedFunctionMapEntries[] = {
   @CMAKE_FUNCTION_TABLE_ENTIRES@
-  { NULL, NULL } /* NOLINT */
+  { CM_NULL, CM_NULL } /* NOLINT */
 };
 
 static const int NumTests = CM_CAST(int,
@@ -45,8 +51,8 @@
   stringSize = CM_CAST(size_t, strlen(string) + 1);
   new_string = CM_CAST(char*, malloc(sizeof(char) * stringSize));
 
-  if (new_string == NULL) { /* NOLINT */
-    return NULL;            /* NOLINT */
+  if (new_string == CM_NULL) { /* NOLINT */
+    return CM_NULL;            /* NOLINT */
   }
   strcpy(new_string, string);
   for (p = new_string; *p != 0; ++p) {
@@ -86,7 +92,7 @@
     av++;
   }
   partial_match = 0;
-  arg = NULL; /* NOLINT */
+  arg = CM_NULL; /* NOLINT */
   /* If partial match is requested.  */
   if (testToRun == -1 && ac > 1) {
     partial_match = (strcmp(av[1], "-R") == 0) ? 1 : 0;
@@ -100,7 +106,7 @@
   }
   for (i = 0; i < NumTests && testToRun == -1; ++i) {
     char *test_name = lowercase(cmakeGeneratedFunctionMapEntries[i].name);
-    if (partial_match != 0 && strstr(test_name, arg) != NULL) { /* NOLINT */
+    if (partial_match != 0 && strstr(test_name, arg) != CM_NULL) { /* NOLINT */
       testToRun = i;
       ac -= 2;
       av += 2;
diff --git a/Tests/Assembler/CMakeLists.txt b/Tests/Assembler/CMakeLists.txt
index fb17ebb..21b265c 100644
--- a/Tests/Assembler/CMakeLists.txt
+++ b/Tests/Assembler/CMakeLists.txt
@@ -7,9 +7,10 @@
 
 # (at least) the following toolchains can process assembler files directly
 # and also generate assembler files from C:
-if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode" AND
+if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode|Ninja" AND
     NOT CMAKE_OSX_ARCHITECTURES)
-  if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang|HP|SunPro|XL)$") OR (CMAKE_C_COMPILER_ID STREQUAL "Intel"  AND  UNIX))
+  if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang|HP|SunPro|XL)$") OR (CMAKE_C_COMPILER_ID STREQUAL "Intel"  AND  UNIX)
+     AND NOT (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"))
     set(C_FLAGS "${CMAKE_C_FLAGS}")
     separate_arguments(C_FLAGS)
     if(CMAKE_OSX_SYSROOT AND CMAKE_C_SYSROOT_FLAG AND NOT ";${C_FLAGS};" MATCHES ";${CMAKE_C_SYSROOT_FLAG};")
diff --git a/Tests/BuildDepends/Project/ninjadep.cpp b/Tests/BuildDepends/Project/ninjadep.cpp
index e794448..d3447e2 100644
--- a/Tests/BuildDepends/Project/ninjadep.cpp
+++ b/Tests/BuildDepends/Project/ninjadep.cpp
@@ -1,6 +1,7 @@
-#include "dir/header.h"
 #include <stdio.h>
 
+#include "dir/header.h"
+
 int main()
 {
   printf("HEADER_STRING: %s\n", HEADER_STRING);
diff --git a/Tests/BundleTest/BundleLib.cxx b/Tests/BundleTest/BundleLib.cxx
index 2e4325c..b7fd70d 100644
--- a/Tests/BundleTest/BundleLib.cxx
+++ b/Tests/BundleTest/BundleLib.cxx
@@ -1,10 +1,9 @@
+#include <CoreFoundation/CoreFoundation.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <CoreFoundation/CoreFoundation.h>
-
 int fileExists(char* filename)
 {
 #ifndef R_OK
diff --git a/Tests/BundleTest/BundleSubDir/CMakeLists.txt b/Tests/BundleTest/BundleSubDir/CMakeLists.txt
index 2f7f2c4..5f91f20 100644
--- a/Tests/BundleTest/BundleSubDir/CMakeLists.txt
+++ b/Tests/BundleTest/BundleSubDir/CMakeLists.txt
@@ -35,7 +35,11 @@
 # installed into a location that uses this output name this will fail if the
 # bundle does not respect the name.  Also the executable will not be found by
 # the test driver if this does not work.
-set_target_properties(SecondBundle PROPERTIES OUTPUT_NAME SecondBundleExe)
+set_target_properties(SecondBundle PROPERTIES
+  OUTPUT_NAME SecondBundleExe
+  XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+  XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
+  )
 
 # Express one app bundle in terms of another's SOURCES to verify that
 # the generators do not expose the Info.plist of one to the other.
diff --git a/Tests/BundleTest/BundleTest.cxx b/Tests/BundleTest/BundleTest.cxx
index a5e8f59..9b70e52 100644
--- a/Tests/BundleTest/BundleTest.cxx
+++ b/Tests/BundleTest/BundleTest.cxx
@@ -1,6 +1,5 @@
-#include <stdio.h>
-
 #include <CoreFoundation/CoreFoundation.h>
+#include <stdio.h>
 
 extern int foo(char* exec);
 
diff --git a/Tests/BundleTest/CMakeLists.txt b/Tests/BundleTest/CMakeLists.txt
index 853da35..1bedc70 100644
--- a/Tests/BundleTest/CMakeLists.txt
+++ b/Tests/BundleTest/CMakeLists.txt
@@ -56,7 +56,11 @@
 # installed into a location that uses this output name this will fail if the
 # bundle does not respect the name.  Also the executable will not be found by
 # the test driver if this does not work.
-set_target_properties(BundleTest PROPERTIES OUTPUT_NAME BundleTestExe)
+set_target_properties(BundleTest PROPERTIES
+  OUTPUT_NAME BundleTestExe
+  XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+  XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
+  )
 
 # Test executable versioning if it is supported.
 if(NOT XCODE)
diff --git a/Tests/BundleUtilities/framework.cpp b/Tests/BundleUtilities/framework.cpp
index abda195..b5a95d8 100644
--- a/Tests/BundleUtilities/framework.cpp
+++ b/Tests/BundleUtilities/framework.cpp
@@ -1,5 +1,6 @@
 
 #include "framework.h"
+
 #include "stdio.h"
 
 void framework()
diff --git a/Tests/BundleUtilities/module.cpp b/Tests/BundleUtilities/module.cpp
index 51730d9..64bc369 100644
--- a/Tests/BundleUtilities/module.cpp
+++ b/Tests/BundleUtilities/module.cpp
@@ -1,5 +1,6 @@
 
 #include "module.h"
+
 #include "shared2.h"
 #include "stdio.h"
 
diff --git a/Tests/BundleUtilities/shared.cpp b/Tests/BundleUtilities/shared.cpp
index e5e7dc5..13791c2 100644
--- a/Tests/BundleUtilities/shared.cpp
+++ b/Tests/BundleUtilities/shared.cpp
@@ -1,5 +1,6 @@
 
 #include "shared.h"
+
 #include "stdio.h"
 
 void shared()
diff --git a/Tests/BundleUtilities/shared2.cpp b/Tests/BundleUtilities/shared2.cpp
index 84af5d0..bed1904 100644
--- a/Tests/BundleUtilities/shared2.cpp
+++ b/Tests/BundleUtilities/shared2.cpp
@@ -1,5 +1,6 @@
 
 #include "shared2.h"
+
 #include "stdio.h"
 
 void shared2()
diff --git a/Tests/CFBundleTest/CMakeLists.txt b/Tests/CFBundleTest/CMakeLists.txt
index 0fe6bb7..b2b1b73 100644
--- a/Tests/CFBundleTest/CMakeLists.txt
+++ b/Tests/CFBundleTest/CMakeLists.txt
@@ -50,6 +50,8 @@
 set_target_properties(CFBundleTest PROPERTIES
     BUNDLE 1
     BUNDLE_EXTENSION plugin
+    XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+    XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
     XCODE_ATTRIBUTE_MACH_O_TYPE mh_bundle
     XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/Info.plist
     MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist
diff --git a/Tests/CMakeCommands/target_include_directories/consumer.cpp b/Tests/CMakeCommands/target_include_directories/consumer.cpp
index 267b0a3..d349650 100644
--- a/Tests/CMakeCommands/target_include_directories/consumer.cpp
+++ b/Tests/CMakeCommands/target_include_directories/consumer.cpp
@@ -1,5 +1,6 @@
 
 #include "consumer.h"
+
 #include "common.h"
 #include "cxx_only.h"
 #include "interfaceinclude.h"
diff --git a/Tests/CMakeCommands/target_link_libraries/cmp0022/cmp0022lib.h b/Tests/CMakeCommands/target_link_libraries/cmp0022/cmp0022lib.h
index 3235b9b..e939013 100644
--- a/Tests/CMakeCommands/target_link_libraries/cmp0022/cmp0022lib.h
+++ b/Tests/CMakeCommands/target_link_libraries/cmp0022/cmp0022lib.h
@@ -1,6 +1,5 @@
 
-#include "cmp0022lib_export.h"
-
 #include "cmp0022ifacelib.h"
+#include "cmp0022lib_export.h"
 
 CMP0022Iface CMP0022LIB_EXPORT cmp0022();
diff --git a/Tests/CMakeCommands/target_link_libraries/depB.cpp b/Tests/CMakeCommands/target_link_libraries/depB.cpp
index 4f46552..276a91a 100644
--- a/Tests/CMakeCommands/target_link_libraries/depB.cpp
+++ b/Tests/CMakeCommands/target_link_libraries/depB.cpp
@@ -2,7 +2,6 @@
 #include "depB.h"
 
 #include "depA.h"
-
 #include "libgenex.h"
 
 int DepB::foo()
diff --git a/Tests/CMakeCommands/target_link_libraries/depC.h b/Tests/CMakeCommands/target_link_libraries/depC.h
index 01c9e06..af7bfa3 100644
--- a/Tests/CMakeCommands/target_link_libraries/depC.h
+++ b/Tests/CMakeCommands/target_link_libraries/depC.h
@@ -1,7 +1,6 @@
 
-#include "depc_export.h"
-
 #include "depA.h"
+#include "depc_export.h"
 
 struct DEPC_EXPORT DepC
 {
diff --git a/Tests/CMakeCommands/target_link_libraries/depD.h b/Tests/CMakeCommands/target_link_libraries/depD.h
index d24ff5f..e3700c3 100644
--- a/Tests/CMakeCommands/target_link_libraries/depD.h
+++ b/Tests/CMakeCommands/target_link_libraries/depD.h
@@ -1,7 +1,6 @@
 
-#include "depd_export.h"
-
 #include "depA.h"
+#include "depd_export.h"
 
 struct DEPD_EXPORT DepD
 {
diff --git a/Tests/CMakeCommands/target_link_libraries/newsignature1.cpp b/Tests/CMakeCommands/target_link_libraries/newsignature1.cpp
index 1caa516..b23b7a0 100644
--- a/Tests/CMakeCommands/target_link_libraries/newsignature1.cpp
+++ b/Tests/CMakeCommands/target_link_libraries/newsignature1.cpp
@@ -2,7 +2,6 @@
 #include "depB.h"
 #include "depC.h"
 #include "depIfaceOnly.h"
-
 #include "subdirlib.h"
 
 int main(int, char**)
diff --git a/Tests/CMakeCommands/target_link_libraries/targetA.cpp b/Tests/CMakeCommands/target_link_libraries/targetA.cpp
index 1caa516..b23b7a0 100644
--- a/Tests/CMakeCommands/target_link_libraries/targetA.cpp
+++ b/Tests/CMakeCommands/target_link_libraries/targetA.cpp
@@ -2,7 +2,6 @@
 #include "depB.h"
 #include "depC.h"
 #include "depIfaceOnly.h"
-
 #include "subdirlib.h"
 
 int main(int, char**)
diff --git a/Tests/CMakeCommands/target_link_libraries/targetC.cpp b/Tests/CMakeCommands/target_link_libraries/targetC.cpp
index cae02b9..11efb3d 100644
--- a/Tests/CMakeCommands/target_link_libraries/targetC.cpp
+++ b/Tests/CMakeCommands/target_link_libraries/targetC.cpp
@@ -1,7 +1,6 @@
 
-#include "depG.h"
-
 #include "bar.h"
+#include "depG.h"
 #include "foo.h"
 
 #ifndef TEST_DEF
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index a25f25a..bc2079f 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -2,14 +2,21 @@
   ${CMAKE_CURRENT_BINARY_DIR}
   ${CMake_BINARY_DIR}/Source
   ${CMake_SOURCE_DIR}/Source
+  ${CMake_SOURCE_DIR}/Source/CTest
   )
 
 set(CMakeLib_TESTS
   testArgumentParser.cxx
+  testCTestBinPacker.cxx
+  testCTestProcesses.cxx
+  testCTestHardwareAllocator.cxx
+  testCTestHardwareSpec.cxx
   testGeneratedFileStream.cxx
   testRST.cxx
   testRange.cxx
+  testOptional.cxx
   testString.cxx
+  testStringAlgorithms.cxx
   testSystemTools.cxx
   testUTF8.cxx
   testXMLParser.cxx
@@ -25,6 +32,7 @@
 set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
 set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>)
 set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>)
+set(testCTestHardwareSpec_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
 
 if(WIN32)
   list(APPEND CMakeLib_TESTS
@@ -39,7 +47,7 @@
 
 create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
 add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
-target_link_libraries(CMakeLibTests CMakeLib)
+target_link_libraries(CMakeLibTests CMakeLib CTestLib)
 
 set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "")
 set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "")
diff --git a/Tests/CMakeLib/run_compile_commands.cxx b/Tests/CMakeLib/run_compile_commands.cxx
index b49803b..2c77acc 100644
--- a/Tests/CMakeLib/run_compile_commands.cxx
+++ b/Tests/CMakeLib/run_compile_commands.cxx
@@ -1,13 +1,14 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmsys/FStream.hxx"
+#include <cstdlib>
 #include <iostream>
 #include <map>
-#include <stdlib.h>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "cmsys/FStream.hxx"
+
 #include "cmSystemTools.h"
 
 class CompileCommandParser
@@ -18,7 +19,7 @@
   public:
     std::string const& at(std::string const& k) const
     {
-      const_iterator i = this->find(k);
+      auto i = this->find(k);
       if (i != this->end()) {
         return i->second;
       }
@@ -26,7 +27,7 @@
       return emptyString;
     }
   };
-  typedef std::vector<CommandType> TranslationUnitsType;
+  using TranslationUnitsType = std::vector<CommandType>;
 
   CompileCommandParser(std::istream& input)
     : Input(input)
diff --git a/Tests/CMakeLib/testAffinity.cxx b/Tests/CMakeLib/testAffinity.cxx
index 4b82280..6c68570 100644
--- a/Tests/CMakeLib/testAffinity.cxx
+++ b/Tests/CMakeLib/testAffinity.cxx
@@ -1,11 +1,11 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmAffinity.h"
-
 #include <cstddef>
 #include <iostream>
 #include <set>
 
+#include "cmAffinity.h"
+
 int main()
 {
   std::set<size_t> cpus = cmAffinity::GetProcessorsAvailable();
diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx
index 788fece..20f98c2 100644
--- a/Tests/CMakeLib/testArgumentParser.cxx
+++ b/Tests/CMakeLib/testArgumentParser.cxx
@@ -1,16 +1,17 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-#include "cmArgumentParser.h"
-
-#include "cm_static_string_view.hxx"
-#include "cm_string_view.hxx"
-
 #include <initializer_list>
 #include <iostream>
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
+#include "cmArgumentParser.h"
+
 namespace {
 
 struct Result
diff --git a/Tests/CMakeLib/testCTestBinPacker.cxx b/Tests/CMakeLib/testCTestBinPacker.cxx
new file mode 100644
index 0000000..62ea55b
--- /dev/null
+++ b/Tests/CMakeLib/testCTestBinPacker.cxx
@@ -0,0 +1,300 @@
+#include <cstddef>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestBinPacker.h"
+#include "cmCTestHardwareAllocator.h"
+
+struct ExpectedPackResult
+{
+  std::vector<int> SlotsNeeded;
+  std::map<std::string, cmCTestHardwareAllocator::Resource> Hardware;
+  bool ExpectedReturnValue;
+  std::vector<cmCTestBinPackerAllocation> ExpectedRoundRobinAllocations;
+  std::vector<cmCTestBinPackerAllocation> ExpectedBlockAllocations;
+};
+
+static const std::vector<ExpectedPackResult> expectedResults
+{
+  /* clang-format off */
+  {
+    { 2, 2, 2, 2 },
+    { { "0", { 4, 0 } }, { "1", { 4, 0 } }, { "2", { 4, 0 } },
+      { "3", { 4, 0 } } },
+    true,
+    {
+      { 0, 2, "0" },
+      { 1, 2, "1" },
+      { 2, 2, "2" },
+      { 3, 2, "3" },
+    },
+    {
+      { 0, 2, "0" },
+      { 1, 2, "0" },
+      { 2, 2, "1" },
+      { 3, 2, "1" },
+    },
+  },
+  {
+    { 2, 3, 2 },
+    { { "0", { 5, 0 } }, { "1", { 2, 0 } } },
+    true,
+    {
+      { 0, 2, "0" },
+      { 1, 3, "0" },
+      { 2, 2, "1" },
+    },
+    {
+      { 0, 2, "0" },
+      { 1, 3, "0" },
+      { 2, 2, "1" },
+    },
+  },
+  {
+    { 1, 2, 3 },
+    { { "0", { 1, 0 } }, { "1", { 2, 0 } }, { "2", { 2, 0 } } },
+    false,
+    { },
+    { },
+  },
+  {
+    { 48, 21, 31, 10, 40 },
+    { { "0", { 81, 0 } }, { "1", { 68, 0 } }, { "2", { 20, 0 } },
+      { "3", { 13, 0 } } },
+    true,
+    {
+      { 0, 48, "0" },
+      { 1, 21, "1" },
+      { 2, 31, "0" },
+      { 3, 10, "2" },
+      { 4, 40, "1" },
+    },
+    {
+      { 0, 48, "0" },
+      { 1, 21, "1" },
+      { 2, 31, "0" },
+      { 3, 10, "2" },
+      { 4, 40, "1" },
+    },
+  },
+  {
+    { 30, 31, 39, 67 },
+    { { "0", { 16, 0 } }, { "1", { 81, 0 } }, { "2", { 97, 0 } } },
+    true,
+    {
+      { 0, 30, "2" },
+      { 1, 31, "1" },
+      { 2, 39, "1" },
+      { 3, 67, "2" },
+    },
+    {
+      { 0, 30, "2" },
+      { 1, 31, "1" },
+      { 2, 39, "1" },
+      { 3, 67, "2" },
+    },
+  },
+  {
+    { 63, 47, 1, 9 },
+    { { "0", { 18, 0 } }, { "1", { 29, 0 } }, { "2", { 9, 0 } },
+      { "3", { 52, 0 } } },
+    false,
+    { },
+    { },
+  },
+  {
+    { 22, 29, 46, 85 },
+    { { "0", { 65, 0 } }, { "1", { 85, 0 } }, { "2", { 65, 0 } },
+      { "3", { 78, 0 } } },
+    true,
+    {
+      { 0, 22, "2" },
+      { 1, 29, "0" },
+      { 2, 46, "3" },
+      { 3, 85, "1" },
+    },
+    {
+      { 0, 22, "0" },
+      { 1, 29, "3" },
+      { 2, 46, "3" },
+      { 3, 85, "1" },
+    },
+  },
+  {
+    { 66, 11, 34, 21 },
+    { { "0", { 24, 0 } }, { "1", { 57, 0 } }, { "2", { 61, 0 } },
+      { "3", { 51, 0 } } },
+    false,
+    { },
+    { },
+  },
+  {
+    { 72, 65, 67, 45 },
+    { { "0", { 29, 0 } }, { "1", { 77, 0 } }, { "2", { 98, 0 } },
+      { "3", { 58, 0 } } },
+    false,
+    { },
+    { },
+  },
+  /*
+   * The following is a contrived attack on the bin-packing algorithm that
+   * causes it to execute with n! complexity, where n is the number of
+   * resources. This case is very unrepresentative of real-world usage, and
+   * has been documented but disabled. The bin-packing problem is NP-hard, and
+   * we may not be able to fix this case at all.
+   */
+#if 0
+  {
+    { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 19 },
+    { { "0", { 1000, 0 } }, { "1", { 1001, 0 } }, { "2", { 1002, 0 } },
+      { "3", { 1003, 0 } }, { "4", { 1004, 0 } }, { "5", { 1005, 0 } },
+      { "6", { 1006, 0 } }, { "7", { 1007, 0 } }, { "8", { 1008, 0 } },
+      { "9", { 1009, 0 } } },
+    false,
+    { },
+    { },
+  },
+#endif
+  /*
+   * These cases are more representative of real-world usage (the resource
+   * sizes are all the same.)
+   */
+  {
+    { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 10 },
+    { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
+      { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
+      { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
+      { "9", { 1000, 0 } } },
+    false,
+    { },
+    { },
+  },
+  {
+    { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 9 },
+    { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
+      { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
+      { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
+      { "9", { 1000, 0 } } },
+    true,
+    {
+      { 0, 1000, "0" },
+      { 1, 999, "1" },
+      { 2, 998, "2" },
+      { 3, 997, "3" },
+      { 4, 996, "4" },
+      { 5, 995, "5" },
+      { 6, 994, "6" },
+      { 7, 993, "7" },
+      { 8, 992, "8" },
+      { 9, 991, "9" },
+      { 10, 9, "9" },
+    },
+    {
+      { 0, 1000, "0" },
+      { 1, 999, "1" },
+      { 2, 998, "2" },
+      { 3, 997, "3" },
+      { 4, 996, "4" },
+      { 5, 995, "5" },
+      { 6, 994, "6" },
+      { 7, 993, "7" },
+      { 8, 992, "8" },
+      { 9, 991, "9" },
+      { 10, 9, "9" },
+    },
+  },
+  /* clang-format on */
+};
+
+struct AllocationComparison
+{
+  cmCTestBinPackerAllocation First;
+  cmCTestBinPackerAllocation Second;
+  bool Equal;
+};
+
+static const std::vector<AllocationComparison> comparisons{
+  /* clang-format off */
+  { { 0, 1, "0" }, { 0, 1, "0" }, true },
+  { { 0, 1, "0" }, { 1, 1, "0" }, false },
+  { { 0, 1, "0" }, { 0, 2, "0" }, false },
+  { { 0, 1, "0" }, { 0, 1, "1" }, false },
+  /* clang-format on */
+};
+
+bool TestExpectedPackResult(const ExpectedPackResult& expected)
+{
+  std::vector<cmCTestBinPackerAllocation> roundRobinAllocations;
+  roundRobinAllocations.reserve(expected.SlotsNeeded.size());
+  std::size_t index = 0;
+  for (auto const& n : expected.SlotsNeeded) {
+    roundRobinAllocations.push_back({ index++, n, "" });
+  }
+
+  bool roundRobinResult = cmAllocateCTestHardwareRoundRobin(
+    expected.Hardware, roundRobinAllocations);
+  if (roundRobinResult != expected.ExpectedReturnValue) {
+    std::cout
+      << "cmAllocateCTestHardwareRoundRobin did not return expected value"
+      << std::endl;
+    return false;
+  }
+
+  if (roundRobinResult &&
+      roundRobinAllocations != expected.ExpectedRoundRobinAllocations) {
+    std::cout << "cmAllocateCTestHardwareRoundRobin did not return expected "
+                 "allocations"
+              << std::endl;
+    return false;
+  }
+
+  std::vector<cmCTestBinPackerAllocation> blockAllocations;
+  blockAllocations.reserve(expected.SlotsNeeded.size());
+  index = 0;
+  for (auto const& n : expected.SlotsNeeded) {
+    blockAllocations.push_back({ index++, n, "" });
+  }
+
+  bool blockResult =
+    cmAllocateCTestHardwareBlock(expected.Hardware, blockAllocations);
+  if (blockResult != expected.ExpectedReturnValue) {
+    std::cout << "cmAllocateCTestHardwareBlock did not return expected value"
+              << std::endl;
+    return false;
+  }
+
+  if (blockResult && blockAllocations != expected.ExpectedBlockAllocations) {
+    std::cout << "cmAllocateCTestHardwareBlock did not return expected"
+                 " allocations"
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+int testCTestBinPacker(int /*unused*/, char* /*unused*/ [])
+{
+  int retval = 0;
+
+  for (auto const& comparison : comparisons) {
+    if ((comparison.First == comparison.Second) != comparison.Equal) {
+      std::cout << "Comparison did not match expected" << std::endl;
+      retval = 1;
+    }
+    if ((comparison.First != comparison.Second) == comparison.Equal) {
+      std::cout << "Comparison did not match expected" << std::endl;
+      retval = 1;
+    }
+  }
+
+  for (auto const& expected : expectedResults) {
+    if (!TestExpectedPackResult(expected)) {
+      retval = 1;
+    }
+  }
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testCTestHardwareAllocator.cxx b/Tests/CMakeLib/testCTestHardwareAllocator.cxx
new file mode 100644
index 0000000..6f05d03
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareAllocator.cxx
@@ -0,0 +1,426 @@
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareAllocator.h"
+#include "cmCTestHardwareSpec.h"
+
+static const cmCTestHardwareSpec spec{ { {
+  /* clang-format off */
+  { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } } },
+  /* clang-format on */
+} } };
+
+bool testInitializeFromHardwareSpec()
+{
+  bool retval = true;
+
+  cmCTestHardwareAllocator allocator;
+  allocator.InitializeFromHardwareSpec(spec);
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 0 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.GetResources() != expected) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  return retval;
+}
+
+bool testAllocateResource()
+{
+  bool retval = true;
+
+  cmCTestHardwareAllocator allocator;
+  allocator.InitializeFromHardwareSpec(spec);
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected1{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 2 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (!allocator.AllocateResource("gpus", "0", 2)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+         "true\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected1) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected2{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (!allocator.AllocateResource("gpus", "0", 2)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+         "true\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected2) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected3{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.AllocateResource("gpus", "0", 1)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"0\", 1) returned true, should be "
+         "false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected3) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected4{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 7 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (!allocator.AllocateResource("gpus", "1", 7)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"1\", 7) returned false, should be "
+         "true\n";
+    retval = false;
+  }
+  if (allocator.AllocateResource("gpus", "1", 2)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"1\", 2) returned true, should be "
+         "false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected4) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected5{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 7 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.AllocateResource("gpus", "2", 1)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"2\", 1) returned true, should be "
+         "false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected5) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected6{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 7 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.AllocateResource("gpus", "4", 1)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"4\", 1) returned true, should be "
+         "false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected6) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected7{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 4 } },
+        { "1", { 8, 7 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.AllocateResource("threads", "0", 1)) {
+    std::cout
+      << "AllocateResource(\"threads\", \"0\", 1) returned true, should be"
+         " false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected7) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  return retval;
+}
+
+bool testDeallocateResource()
+{
+  bool retval = true;
+
+  cmCTestHardwareAllocator allocator;
+  allocator.InitializeFromHardwareSpec(spec);
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected1{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 1 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (!allocator.AllocateResource("gpus", "0", 2)) {
+    std::cout
+      << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+         "true\n";
+    retval = false;
+  }
+  if (!allocator.DeallocateResource("gpus", "0", 1)) {
+    std::cout
+      << "DeallocateResource(\"gpus\", \"0\", 1) returned false, should be"
+         " true\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected1) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected2{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 1 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.DeallocateResource("gpus", "0", 2)) {
+    std::cout
+      << "DeallocateResource(\"gpus\", \"0\", 2) returned true, should be"
+         " false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected2) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected3{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 0 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (!allocator.DeallocateResource("gpus", "0", 1)) {
+    std::cout
+      << "DeallocateResource(\"gpus\", \"0\", 1) returned false, should be"
+         " true\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected3) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected4{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 0 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.DeallocateResource("gpus", "0", 1)) {
+    std::cout
+      << "DeallocateResource(\"gpus\", \"0\", 1) returned true, should be"
+         " false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected4) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected5{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 0 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.DeallocateResource("gpus", "4", 1)) {
+    std::cout
+      << "DeallocateResource(\"gpus\", \"4\", 1) returned true, should be"
+         " false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected5) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  static const std::map<
+    std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+    expected6{
+      /* clang-format off */
+      { "gpus", {
+        { "0", { 4, 0 } },
+        { "1", { 8, 0 } },
+        { "2", { 0, 0 } },
+        { "3", { 8, 0 } },
+      } },
+      /* clang-format on */
+    };
+  if (allocator.DeallocateResource("threads", "0", 1)) {
+    std::cout
+      << "DeallocateResource(\"threads\", \"0\", 1) returned true, should be"
+         " false\n";
+    retval = false;
+  }
+  if (allocator.GetResources() != expected6) {
+    std::cout << "GetResources() did not return expected value\n";
+    retval = false;
+  }
+
+  return retval;
+}
+
+bool testResourceFree()
+{
+  bool retval = true;
+
+  const cmCTestHardwareAllocator::Resource r1{ 5, 0 };
+  if (r1.Free() != 5) {
+    std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+                 "expected value for { 5, 0 }\n";
+    retval = false;
+  }
+
+  const cmCTestHardwareAllocator::Resource r2{ 3, 2 };
+  if (r2.Free() != 1) {
+    std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+                 "expected value for { 3, 2 }\n";
+    retval = false;
+  }
+
+  const cmCTestHardwareAllocator::Resource r3{ 4, 4 };
+  if (r3.Free() != 0) {
+    std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+                 "expected value for { 4, 4 }\n";
+    retval = false;
+  }
+
+  return retval;
+}
+
+int testCTestHardwareAllocator(int, char** const)
+{
+  int retval = 0;
+
+  if (!testInitializeFromHardwareSpec()) {
+    std::cout << "in testInitializeFromHardwareSpec()\n";
+    retval = -1;
+  }
+
+  if (!testAllocateResource()) {
+    std::cout << "in testAllocateResource()\n";
+    retval = -1;
+  }
+
+  if (!testDeallocateResource()) {
+    std::cout << "in testDeallocateResource()\n";
+    retval = -1;
+  }
+
+  if (!testResourceFree()) {
+    std::cout << "in testResourceFree()\n";
+    retval = -1;
+  }
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec.cxx b/Tests/CMakeLib/testCTestHardwareSpec.cxx
new file mode 100644
index 0000000..3e3eccc
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec.cxx
@@ -0,0 +1,84 @@
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareSpec.h"
+
+struct ExpectedSpec
+{
+  std::string Path;
+  bool ParseResult;
+  cmCTestHardwareSpec Expected;
+};
+
+static const std::vector<ExpectedSpec> expectedHardwareSpecs = {
+  /* clang-format off */
+  {"spec1.json", true, {{{
+    {"gpus", {
+      {"2", 4},
+      {"e", 1},
+    }},
+    {"threads", {
+    }},
+  }}}},
+  {"spec2.json", true, {{{
+  }}}},
+  {"spec3.json", false, {{{}}}},
+  {"spec4.json", false, {{{}}}},
+  {"spec5.json", false, {{{}}}},
+  {"spec6.json", false, {{{}}}},
+  {"spec7.json", false, {{{}}}},
+  {"spec8.json", false, {{{}}}},
+  {"spec9.json", false, {{{}}}},
+  {"spec10.json", false, {{{}}}},
+  {"spec11.json", false, {{{}}}},
+  {"spec12.json", false, {{{}}}},
+  {"spec13.json", false, {{{}}}},
+  {"spec14.json", true, {{{}}}},
+  {"spec15.json", true, {{{}}}},
+  {"spec16.json", true, {{{}}}},
+  {"spec17.json", false, {{{}}}},
+  {"spec18.json", false, {{{}}}},
+  {"noexist.json", false, {{{}}}},
+  /* clang-format on */
+};
+
+static bool testSpec(const std::string& path, bool expectedResult,
+                     const cmCTestHardwareSpec& expected)
+{
+  cmCTestHardwareSpec actual;
+  bool result = actual.ReadFromJSONFile(path);
+  if (result != expectedResult) {
+    std::cout << "ReadFromJSONFile(\"" << path << "\") returned " << result
+              << ", should be " << expectedResult << std::endl;
+    return false;
+  }
+
+  if (result && actual != expected) {
+    std::cout << "ReadFromJSONFile(\"" << path
+              << "\") did not give expected spec" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+int testCTestHardwareSpec(int argc, char** const argv)
+{
+  if (argc < 2) {
+    std::cout << "Invalid arguments.\n";
+    return -1;
+  }
+
+  int retval = 0;
+  for (auto const& spec : expectedHardwareSpecs) {
+    std::string path = argv[1];
+    path += "/testCTestHardwareSpec_data/";
+    path += spec.Path;
+    if (!testSpec(path, spec.ParseResult, spec.Expected)) {
+      retval = -1;
+    }
+  }
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json
new file mode 100644
index 0000000..ee3d0ce
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json
@@ -0,0 +1,23 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+          "id": "2",
+          "slots": 4
+        },
+        {
+          "id": "e"
+        }
+      ],
+      ".reserved": [
+        {
+          "id": "a",
+          "slots": 3
+        }
+      ],
+      "threads": [
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json
new file mode 100644
index 0000000..22105d7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json
@@ -0,0 +1,11 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+          "id": 4
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json
new file mode 100644
index 0000000..1e37ef5
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json
@@ -0,0 +1,12 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+          "id": "4",
+          "slots": "giraffe"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json
@@ -0,0 +1 @@
+[]
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json
new file mode 100644
index 0000000..6b7a9f4
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json
@@ -0,0 +1 @@
+not json
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json
new file mode 100644
index 0000000..ce708c7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json
@@ -0,0 +1,8 @@
+{
+  "local": [
+    {
+      "0": [
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json
new file mode 100644
index 0000000..78b6990
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json
@@ -0,0 +1,8 @@
+{
+  "local": [
+    {
+      "-": [
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json
new file mode 100644
index 0000000..95c7d26
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json
@@ -0,0 +1,8 @@
+{
+  "local": [
+    {
+      "A": [
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json
new file mode 100644
index 0000000..1b6ab4b
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json
@@ -0,0 +1,11 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+          "id": "A"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json
new file mode 100644
index 0000000..1a17df7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json
@@ -0,0 +1,11 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+          "id": "-"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json
new file mode 100644
index 0000000..6175b1a
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json
@@ -0,0 +1,4 @@
+{
+  "local": [
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json
new file mode 100644
index 0000000..82453ec
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json
@@ -0,0 +1,8 @@
+{
+  "local": [
+    {
+    },
+    {
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json
new file mode 100644
index 0000000..05e73d7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json
@@ -0,0 +1,4 @@
+{
+  "local": {
+  }
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json
new file mode 100644
index 0000000..93c790d
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json
@@ -0,0 +1,5 @@
+{
+  "local": [
+    []
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json
new file mode 100644
index 0000000..28b6a4f
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json
@@ -0,0 +1,8 @@
+{
+  "local": [
+    {
+      "gpus": {
+      }
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json
new file mode 100644
index 0000000..79bd224
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json
@@ -0,0 +1,9 @@
+{
+  "local": [
+    {
+      "gpus": [
+        []
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json
new file mode 100644
index 0000000..6bb1def
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json
@@ -0,0 +1,10 @@
+{
+  "local": [
+    {
+      "gpus": [
+        {
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/CMakeLib/testCTestProcesses.cxx b/Tests/CMakeLib/testCTestProcesses.cxx
new file mode 100644
index 0000000..acf4f67
--- /dev/null
+++ b/Tests/CMakeLib/testCTestProcesses.cxx
@@ -0,0 +1,137 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "cmCTestTestHandler.h"
+
+struct ExpectedParseResult
+{
+  std::string String;
+  bool ExpectedReturnValue;
+  std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+    ExpectedValue;
+};
+
+static const std::vector<ExpectedParseResult> expectedResults{
+  /* clang-format off */
+  { "threads:2", true, {
+    { { "threads", 2, 1 } },
+  } },
+  { "3,threads:2", true, {
+    { { "threads", 2, 1 } },
+    { { "threads", 2, 1 } },
+    { { "threads", 2, 1 } },
+  } },
+  { "3,threads:2,gpus:4", true, {
+    { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+    { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+    { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+  } },
+  { "2,threads:2;gpus:4", true, {
+    { { "threads", 2, 1 } },
+    { { "threads", 2, 1 } },
+    { { "gpus", 4, 1 } },
+  } },
+  { "threads:2;2,gpus:4", true, {
+    { { "threads", 2, 1 } },
+    { { "gpus", 4, 1 } },
+    { { "gpus", 4, 1 } },
+  } },
+  { "threads:2;gpus:4", true, {
+    { { "threads", 2, 1 } },
+    { { "gpus", 4, 1 } },
+  } },
+  { "1,threads:2;0,gpus:4", true, {
+    { { "threads", 2, 1 } },
+  } },
+  { "1,_:1", true, {
+    { { "_", 1, 1 } },
+  } },
+  { "1,a:1", true, {
+    { { "a", 1, 1 } },
+  } },
+  { "2", true, {
+    {},
+    {},
+  } },
+  { "1;2,threads:1", true, {
+    {},
+    { { "threads", 1, 1 } },
+    { { "threads", 1, 1 } },
+  } },
+  { "1,,threads:1", true, {
+    { { "threads", 1, 1 } },
+  } },
+  { ";1,threads:1", true, {
+    { { "threads", 1, 1 } },
+  } },
+  { "1,threads:1;", true, {
+    { { "threads", 1, 1 } },
+  } },
+  { "1,threads:1,", true, {
+    { { "threads", 1, 1 } },
+  } },
+  { "threads:1;;threads:2", true, {
+    { { "threads", 1, 1 } },
+    { { "threads", 2, 1 } },
+  } },
+  { "1,", true, {
+    {},
+  } },
+  { ";", true, {} },
+  { "", true, {} },
+  { ",", false, {} },
+  { "1,0:1", false, {} },
+  { "1,A:1", false, {} },
+  { "1,a-b:1", false, {} },
+  { "invalid", false, {} },
+  { ",1,invalid:1", false, {} },
+  { "1,1", false, {} },
+  { "-1,invalid:1", false, {} },
+  { "1,invalid:*", false, {} },
+  { "1,invalid:-1", false, {} },
+  { "1,invalid:-", false, {} },
+  { "1,invalid:ab2", false, {} },
+  { "1,invalid :2", false, {} },
+  { "1, invalid:2", false, {} },
+  { "1,invalid:ab", false, {} },
+  /* clang-format on */
+};
+
+bool TestExpectedParseResult(const ExpectedParseResult& expected)
+{
+  std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+    result;
+  bool retval;
+  if ((retval = cmCTestTestHandler::ParseProcessesProperty(
+         expected.String, result)) != expected.ExpectedReturnValue) {
+    std::cout << "ParseProcessesProperty(\"" << expected.String
+              << "\") returned " << retval << ", should be "
+              << expected.ExpectedReturnValue << std::endl;
+    return false;
+  }
+
+  if (result != expected.ExpectedValue) {
+    std::cout << "ParseProcessesProperty(\"" << expected.String
+              << "\") did not yield expected set of processes" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+int testCTestProcesses(int /*unused*/, char* /*unused*/ [])
+{
+  int retval = 0;
+
+  for (auto const& expected : expectedResults) {
+    if (!TestExpectedParseResult(expected)) {
+      retval = 1;
+    }
+  }
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testEncoding.cxx b/Tests/CMakeLib/testEncoding.cxx
index d608e86..4936898 100644
--- a/Tests/CMakeLib/testEncoding.cxx
+++ b/Tests/CMakeLib/testEncoding.cxx
@@ -1,7 +1,8 @@
-#include "cmsys/FStream.hxx"
 #include <iostream>
 #include <string>
 
+#include "cmsys/FStream.hxx"
+
 #ifdef _WIN32
 #  include "cmsys/ConsoleBuf.hxx"
 #endif
diff --git a/Tests/CMakeLib/testGeneratedFileStream.cxx b/Tests/CMakeLib/testGeneratedFileStream.cxx
index 1d2ec32..de44a0b 100644
--- a/Tests/CMakeLib/testGeneratedFileStream.cxx
+++ b/Tests/CMakeLib/testGeneratedFileStream.cxx
@@ -1,11 +1,11 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmGeneratedFileStream.h"
-#include "cmSystemTools.h"
-
 #include <iostream>
 #include <string>
 
+#include "cmGeneratedFileStream.h"
+#include "cmSystemTools.h"
+
 #define cmFailed(m1, m2)                                                      \
   std::cout << "FAILED: " << (m1) << (m2) << "\n";                            \
   failed = 1
diff --git a/Tests/CMakeLib/testOptional.cxx b/Tests/CMakeLib/testOptional.cxx
new file mode 100644
index 0000000..3050332
--- /dev/null
+++ b/Tests/CMakeLib/testOptional.cxx
@@ -0,0 +1,689 @@
+#include <iostream>
+#include <type_traits>
+#include <vector>
+
+#include <cm/optional>
+#include <cm/utility>
+
+class EventLogger;
+
+class Event
+{
+public:
+  enum EventType
+  {
+    DEFAULT_CONSTRUCT,
+    COPY_CONSTRUCT,
+    MOVE_CONSTRUCT,
+    VALUE_CONSTRUCT,
+
+    DESTRUCT,
+
+    COPY_ASSIGN,
+    MOVE_ASSIGN,
+    VALUE_ASSIGN,
+
+    REFERENCE,
+    CONST_REFERENCE,
+    RVALUE_REFERENCE,
+    CONST_RVALUE_REFERENCE,
+
+    SWAP,
+  };
+
+  EventType Type;
+  const EventLogger* Logger1;
+  const EventLogger* Logger2;
+  int Value;
+
+  bool operator==(const Event& other) const;
+  bool operator!=(const Event& other) const;
+};
+
+bool Event::operator==(const Event& other) const
+{
+  return this->Type == other.Type && this->Logger1 == other.Logger1 &&
+    this->Logger2 == other.Logger2 && this->Value == other.Value;
+}
+
+bool Event::operator!=(const Event& other) const
+{
+  return !(*this == other);
+}
+
+static std::vector<Event> events;
+
+class EventLogger
+{
+public:
+  EventLogger();
+  EventLogger(const EventLogger& other);
+  EventLogger(EventLogger&& other);
+  EventLogger(int value);
+
+  ~EventLogger();
+
+  EventLogger& operator=(const EventLogger& other);
+  EventLogger& operator=(EventLogger&& other);
+  EventLogger& operator=(int value);
+
+  void Reference() &;
+  void Reference() const&;
+  void Reference() &&;
+  void Reference() const&&;
+
+  int Value = 0;
+};
+
+// Certain builds of GCC generate false -Wmaybe-uninitialized warnings when
+// doing a release build with the system version of std::optional. These
+// warnings do not manifest when using our own cm::optional implementation.
+// Silence these false warnings.
+#if defined(__GNUC__) && !defined(__clang__)
+#  define BEGIN_IGNORE_UNINITIALIZED                                          \
+    _Pragma("GCC diagnostic push")                                            \
+      _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+#  define END_IGNORE_UNINITIALIZED _Pragma("GCC diagnostic pop")
+#else
+#  define BEGIN_IGNORE_UNINITIALIZED
+#  define END_IGNORE_UNINITIALIZED
+#endif
+
+void swap(EventLogger& e1, EventLogger& e2)
+{
+  BEGIN_IGNORE_UNINITIALIZED
+  events.push_back({ Event::SWAP, &e1, &e2, e2.Value });
+  END_IGNORE_UNINITIALIZED
+  auto tmp = e1.Value;
+  e1.Value = e2.Value;
+  e2.Value = tmp;
+}
+
+EventLogger::EventLogger()
+  : Value(0)
+{
+  events.push_back({ Event::DEFAULT_CONSTRUCT, this, nullptr, 0 });
+}
+
+EventLogger::EventLogger(const EventLogger& other)
+  : Value(other.Value)
+{
+  events.push_back({ Event::COPY_CONSTRUCT, this, &other, other.Value });
+}
+
+BEGIN_IGNORE_UNINITIALIZED
+EventLogger::EventLogger(EventLogger&& other)
+  : Value(other.Value)
+{
+  events.push_back({ Event::MOVE_CONSTRUCT, this, &other, other.Value });
+}
+END_IGNORE_UNINITIALIZED
+
+EventLogger::EventLogger(int value)
+  : Value(value)
+{
+  events.push_back({ Event::VALUE_CONSTRUCT, this, nullptr, value });
+}
+
+EventLogger::~EventLogger()
+{
+  BEGIN_IGNORE_UNINITIALIZED
+  events.push_back({ Event::DESTRUCT, this, nullptr, this->Value });
+  END_IGNORE_UNINITIALIZED
+}
+
+EventLogger& EventLogger::operator=(const EventLogger& other)
+{
+  events.push_back({ Event::COPY_ASSIGN, this, &other, other.Value });
+  this->Value = other.Value;
+  return *this;
+}
+
+EventLogger& EventLogger::operator=(EventLogger&& other)
+{
+  events.push_back({ Event::MOVE_ASSIGN, this, &other, other.Value });
+  this->Value = other.Value;
+  return *this;
+}
+
+EventLogger& EventLogger::operator=(int value)
+{
+  events.push_back({ Event::VALUE_ASSIGN, this, nullptr, value });
+  this->Value = value;
+  return *this;
+}
+
+void EventLogger::Reference() &
+{
+  events.push_back({ Event::REFERENCE, this, nullptr, this->Value });
+}
+
+void EventLogger::Reference() const&
+{
+  events.push_back({ Event::CONST_REFERENCE, this, nullptr, this->Value });
+}
+
+void EventLogger::Reference() &&
+{
+  events.push_back({ Event::RVALUE_REFERENCE, this, nullptr, this->Value });
+}
+
+void EventLogger::Reference() const&&
+{
+  events.push_back(
+    { Event::CONST_RVALUE_REFERENCE, this, nullptr, this->Value });
+}
+
+static bool testDefaultConstruct(std::vector<Event>& expected)
+{
+  const cm::optional<EventLogger> o{};
+
+  expected = {};
+  return true;
+}
+
+static bool testNulloptConstruct(std::vector<Event>& expected)
+{
+  const cm::optional<EventLogger> o{ cm::nullopt };
+
+  expected = {};
+  return true;
+}
+
+static bool testValueConstruct(std::vector<Event>& expected)
+{
+  const cm::optional<EventLogger> o{ 4 };
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
+    { Event::DESTRUCT, &*o, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testInPlaceConstruct(std::vector<Event>& expected)
+{
+  const cm::optional<EventLogger> o1{ cm::in_place, 4 };
+  const cm::optional<EventLogger> o2{ cm::in_place_t{}, 4 };
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testCopyConstruct(std::vector<Event>& expected)
+{
+  const cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{ o1 };
+  const cm::optional<EventLogger> o3{};
+  const cm::optional<EventLogger> o4{ o3 };
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::COPY_CONSTRUCT, &*o2, &o1.value(), 4 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testMoveConstruct(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{ std::move(o1) };
+  cm::optional<EventLogger> o3{};
+  const cm::optional<EventLogger> o4{ std::move(o3) };
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::MOVE_CONSTRUCT, &*o2, &o1.value(), 4 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testNulloptAssign(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{ 4 };
+  o1 = cm::nullopt;
+  cm::optional<EventLogger> o2{};
+  o2 = cm::nullopt;
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testCopyAssign(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{};
+  const cm::optional<EventLogger> o2{ 4 };
+  o1 = o2;
+  const cm::optional<EventLogger> o3{ 5 };
+  o1 = o3;
+  const cm::optional<EventLogger> o4{};
+  o1 = o4;
+  o1 = o4; // Intentionally duplicated to test assigning an empty optional to
+  // an empty optional
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
+    { Event::COPY_CONSTRUCT, &*o1, &*o2, 4 },
+    { Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
+    { Event::COPY_ASSIGN, &*o1, &*o3, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 5 },
+    { Event::DESTRUCT, &o3.value(), nullptr, 5 },
+    { Event::DESTRUCT, &o2.value(), nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testMoveAssign(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{};
+  cm::optional<EventLogger> o2{ 4 };
+  o1 = std::move(o2);
+  cm::optional<EventLogger> o3{ 5 };
+  o1 = std::move(o3);
+  cm::optional<EventLogger> o4{};
+  o1 = std::move(o4);
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
+    { Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
+    { Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
+    { Event::MOVE_ASSIGN, &*o1, &*o3, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 5 },
+    { Event::DESTRUCT, &*o3, nullptr, 5 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testPointer(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{ 5 };
+
+  o1->Reference();
+  o2->Reference();
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
+    { Event::REFERENCE, &*o1, nullptr, 4 },
+    { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+#if !__GNUC__ || __GNUC__ > 4
+#  define ALLOW_CONST_RVALUE
+#endif
+
+static bool testDereference(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{ 5 };
+
+  (*o1).Reference();
+  (*o2).Reference();
+  (*std::move(o1)).Reference();
+#ifdef ALLOW_CONST_RVALUE
+  (*std::move(o2)).Reference(); // Broken in GCC 4.9.0. Sigh...
+#endif
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
+    { Event::REFERENCE, &*o1, nullptr, 4 },
+    { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
+    { Event::RVALUE_REFERENCE, &*o1, nullptr, 4 },
+#ifdef ALLOW_CONST_RVALUE
+    { Event::CONST_RVALUE_REFERENCE, &*o2, nullptr, 5 },
+#endif
+    { Event::DESTRUCT, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testHasValue(std::vector<Event>& expected)
+{
+  bool retval = true;
+
+  const cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{};
+
+  if (!o1.has_value()) {
+    std::cout << "o1 should have a value" << std::endl;
+    retval = false;
+  }
+
+  if (!o1) {
+    std::cout << "(bool)o1 should be true" << std::endl;
+    retval = false;
+  }
+
+  if (o2.has_value()) {
+    std::cout << "o2 should not have a value" << std::endl;
+    retval = false;
+  }
+
+  if (o2) {
+    std::cout << "(bool)o2 should be false" << std::endl;
+    retval = false;
+  }
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return retval;
+}
+
+static bool testValue(std::vector<Event>& expected)
+{
+  bool retval = true;
+
+  cm::optional<EventLogger> o1{ 4 };
+  const cm::optional<EventLogger> o2{ 5 };
+  cm::optional<EventLogger> o3{};
+  const cm::optional<EventLogger> o4{};
+
+  o1.value().Reference();
+  o2.value().Reference();
+
+  bool thrown = false;
+  try {
+    (void)o3.value();
+  } catch (cm::bad_optional_access&) {
+    thrown = true;
+  }
+  if (!thrown) {
+    std::cout << "o3.value() did not throw" << std::endl;
+    retval = false;
+  }
+
+  thrown = false;
+  try {
+    (void)o4.value();
+  } catch (cm::bad_optional_access&) {
+    thrown = true;
+  }
+  if (!thrown) {
+    std::cout << "o4.value() did not throw" << std::endl;
+    retval = false;
+  }
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
+    { Event::REFERENCE, &*o1, nullptr, 4 },
+    { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+  };
+  return retval;
+}
+
+static bool testValueOr()
+{
+  bool retval = true;
+
+  const cm::optional<EventLogger> o1{ 4 };
+  cm::optional<EventLogger> o2{ 5 };
+  const cm::optional<EventLogger> o3{};
+  cm::optional<EventLogger> o4{};
+
+  EventLogger e1{ 6 };
+  EventLogger e2{ 7 };
+  EventLogger e3{ 8 };
+  EventLogger e4{ 9 };
+
+  EventLogger r1 = o1.value_or(e1);
+  if (r1.Value != 4) {
+    std::cout << "r1.Value should be 4" << std::endl;
+    retval = false;
+  }
+  EventLogger r2 = std::move(o2).value_or(e2);
+  if (r2.Value != 5) {
+    std::cout << "r2.Value should be 5" << std::endl;
+    retval = false;
+  }
+  EventLogger r3 = o3.value_or(e3);
+  if (r3.Value != 8) {
+    std::cout << "r3.Value should be 8" << std::endl;
+    retval = false;
+  }
+  EventLogger r4 = std::move(o4).value_or(e4);
+  if (r4.Value != 9) {
+    std::cout << "r4.Value should be 9" << std::endl;
+    retval = false;
+  }
+
+  return retval;
+}
+
+static bool testSwap(std::vector<Event>& expected)
+{
+  bool retval = true;
+
+  cm::optional<EventLogger> o1{ 4 };
+  cm::optional<EventLogger> o2{};
+
+  o1.swap(o2);
+
+  if (o1.has_value()) {
+    std::cout << "o1 should not have value" << std::endl;
+    retval = false;
+  }
+  if (!o2.has_value()) {
+    std::cout << "o2 should have value" << std::endl;
+    retval = false;
+  }
+  if (o2.value().Value != 4) {
+    std::cout << "value of o2 should be 4" << std::endl;
+    retval = false;
+  }
+
+  o1.swap(o2);
+
+  if (!o1.has_value()) {
+    std::cout << "o1 should have value" << std::endl;
+    retval = false;
+  }
+  if (o1.value().Value != 4) {
+    std::cout << "value of o1 should be 4" << std::endl;
+    retval = false;
+  }
+  if (o2.has_value()) {
+    std::cout << "o2 should not have value" << std::endl;
+    retval = false;
+  }
+
+  o2.emplace(5);
+  o1.swap(o2);
+
+  if (!o1.has_value()) {
+    std::cout << "o1 should have value" << std::endl;
+    retval = false;
+  }
+  if (o1.value().Value != 5) {
+    std::cout << "value of o1 should be 5" << std::endl;
+    retval = false;
+  }
+  if (!o2.has_value()) {
+    std::cout << "o2 should not have value" << std::endl;
+    retval = false;
+  }
+  if (o2.value().Value != 4) {
+    std::cout << "value of o2 should be 4" << std::endl;
+    retval = false;
+  }
+
+  o1.reset();
+  o2.reset();
+  o1.swap(o2);
+
+  if (o1.has_value()) {
+    std::cout << "o1 should not have value" << std::endl;
+    retval = false;
+  }
+  if (o2.has_value()) {
+    std::cout << "o2 should not have value" << std::endl;
+    retval = false;
+  }
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
+    { Event::MOVE_CONSTRUCT, &*o2, &*o1, 4 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+    { Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
+    { Event::SWAP, &*o1, &*o2, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 5 },
+    { Event::DESTRUCT, &*o2, nullptr, 4 },
+  };
+  return retval;
+}
+
+static bool testReset(std::vector<Event>& expected)
+{
+  bool retval = true;
+
+  cm::optional<EventLogger> o{ 4 };
+
+  o.reset();
+
+  if (o.has_value()) {
+    std::cout << "o should not have value" << std::endl;
+    retval = false;
+  }
+
+  o.reset();
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
+    { Event::DESTRUCT, &*o, nullptr, 4 },
+  };
+  return retval;
+}
+
+static bool testEmplace(std::vector<Event>& expected)
+{
+  cm::optional<EventLogger> o{ 4 };
+
+  o.emplace(5);
+  o.reset();
+  o.emplace();
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
+    { Event::DESTRUCT, &*o, nullptr, 4 },
+    { Event::VALUE_CONSTRUCT, &*o, nullptr, 5 },
+    { Event::DESTRUCT, &*o, nullptr, 5 },
+    { Event::DEFAULT_CONSTRUCT, &*o, nullptr, 0 },
+    { Event::DESTRUCT, &*o, nullptr, 0 },
+  };
+  return true;
+}
+
+static bool testMakeOptional(std::vector<Event>& expected)
+{
+  EventLogger e{ 4 };
+  cm::optional<EventLogger> o1 = cm::make_optional<EventLogger>(e);
+  cm::optional<EventLogger> o2 = cm::make_optional<EventLogger>(5);
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &e, nullptr, 4 },
+    { Event::COPY_CONSTRUCT, &*o1, &e, 4 },
+    { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o2, nullptr, 5 },
+    { Event::DESTRUCT, &*o1, nullptr, 4 },
+    { Event::DESTRUCT, &e, nullptr, 4 },
+  };
+  return true;
+}
+
+static bool testMemoryRange(std::vector<Event>& expected)
+{
+  bool retval = true;
+
+  cm::optional<EventLogger> o{ 4 };
+
+  auto* ostart = &o;
+  auto* oend = ostart + 1;
+  auto* estart = &o.value();
+  auto* eend = estart + 1;
+
+  if (static_cast<void*>(estart) < static_cast<void*>(ostart) ||
+      static_cast<void*>(eend) > static_cast<void*>(oend)) {
+    std::cout << "value is not within memory range of optional" << std::endl;
+    retval = false;
+  }
+
+  expected = {
+    { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
+    { Event::DESTRUCT, &*o, nullptr, 4 },
+  };
+  return retval;
+}
+
+int testOptional(int /*unused*/, char* /*unused*/ [])
+{
+  int retval = 0;
+
+#define DO_EVENT_TEST(name)                                                   \
+  do {                                                                        \
+    events.clear();                                                           \
+    std::vector<Event> expected;                                              \
+    if (!name(expected)) {                                                    \
+      std::cout << "in " #name << std::endl;                                  \
+      retval = 1;                                                             \
+    } else if (expected != events) {                                          \
+      std::cout << #name " did not produce expected events" << std::endl;     \
+      retval = 1;                                                             \
+    }                                                                         \
+  } while (0)
+
+#define DO_TEST(name)                                                         \
+  do {                                                                        \
+    if (!name()) {                                                            \
+      std::cout << "in " #name << std::endl;                                  \
+      retval = 1;                                                             \
+    }                                                                         \
+  } while (0)
+
+  DO_EVENT_TEST(testDefaultConstruct);
+  DO_EVENT_TEST(testNulloptConstruct);
+  DO_EVENT_TEST(testValueConstruct);
+  DO_EVENT_TEST(testInPlaceConstruct);
+  DO_EVENT_TEST(testCopyConstruct);
+  DO_EVENT_TEST(testMoveConstruct);
+  DO_EVENT_TEST(testNulloptAssign);
+  DO_EVENT_TEST(testCopyAssign);
+  DO_EVENT_TEST(testMoveAssign);
+  DO_EVENT_TEST(testPointer);
+  DO_EVENT_TEST(testDereference);
+  DO_EVENT_TEST(testHasValue);
+  DO_EVENT_TEST(testValue);
+  DO_TEST(testValueOr);
+  DO_EVENT_TEST(testSwap);
+  DO_EVENT_TEST(testReset);
+  DO_EVENT_TEST(testEmplace);
+  DO_EVENT_TEST(testMakeOptional);
+  DO_EVENT_TEST(testMemoryRange);
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testRST.cxx b/Tests/CMakeLib/testRST.cxx
index 8891276..28d80a5 100644
--- a/Tests/CMakeLib/testRST.cxx
+++ b/Tests/CMakeLib/testRST.cxx
@@ -1,12 +1,13 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmRST.h"
-#include "cmSystemTools.h"
-
-#include "cmsys/FStream.hxx"
 #include <iostream>
 #include <string>
 
+#include "cmsys/FStream.hxx"
+
+#include "cmRST.h"
+#include "cmSystemTools.h"
+
 void reportLine(std::ostream& os, bool ret, std::string const& line, bool eol)
 {
   if (ret) {
diff --git a/Tests/CMakeLib/testRange.cxx b/Tests/CMakeLib/testRange.cxx
index b26b07b..4efe98e 100644
--- a/Tests/CMakeLib/testRange.cxx
+++ b/Tests/CMakeLib/testRange.cxx
@@ -1,12 +1,12 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-#include "cmRange.h"
-
 #include <iostream>
 #include <string>
 #include <vector>
 
+#include "cmRange.h"
+
 #define ASSERT_TRUE(x)                                                        \
   do {                                                                        \
     if (!(x)) {                                                               \
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index af5e41e..d7b3200 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -1,11 +1,7 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-#include "cmString.hxx"
-
-#include "cm_static_string_view.hxx"
-#include "cm_string_view.hxx"
-
+#include <cstddef>
 #include <cstring>
 #include <iostream>
 #include <iterator>
@@ -15,6 +11,12 @@
 #include <type_traits>
 #include <utility>
 
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
+#include "cmString.hxx"
+
 #define ASSERT_TRUE(x)                                                        \
   do {                                                                        \
     if (!(x)) {                                                               \
diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx
new file mode 100644
index 0000000..63826cf
--- /dev/null
+++ b/Tests/CMakeLib/testStringAlgorithms.cxx
@@ -0,0 +1,230 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <cmConfigure.h> // IWYU pragma: keep
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cm/string_view>
+
+#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 cmTrimWhitespace
+  {
+    std::string base = "base";
+    std::string spaces = "  \f\f\n\n\r\r\t\t\v\v";
+    assert_string(cmTrimWhitespace(spaces + base), base,
+                  "cmTrimWhitespace front");
+    assert_string(cmTrimWhitespace(base + spaces), base,
+                  "cmTrimWhitespace back");
+    assert_string(cmTrimWhitespace(spaces + base + spaces), base,
+                  "cmTrimWhitespace front and back");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmRemoveQuotes
+  {
+    auto test = [&assert_string](cm::string_view source,
+                                 cm::string_view expected,
+                                 cm::string_view title) {
+      assert_string(cmRemoveQuotes(source), expected, title);
+    };
+
+    test("", "", "cmRemoveQuotes empty");
+    test("\"", "\"", "cmRemoveQuotes single quote");
+    test("\"\"", "", "cmRemoveQuotes double quote");
+    test("\"a", "\"a", "cmRemoveQuotes quote char");
+    test("\"ab", "\"ab", "cmRemoveQuotes quote char char");
+    test("a\"", "a\"", "cmRemoveQuotes char quote");
+    test("ab\"", "ab\"", "cmRemoveQuotes char char quote");
+    test("a", "a", "cmRemoveQuotes single char");
+    test("ab", "ab", "cmRemoveQuotes two chars");
+    test("abc", "abc", "cmRemoveQuotes three chars");
+    test("\"abc\"", "abc", "cmRemoveQuotes quoted chars");
+    test("\"\"abc\"\"", "\"abc\"", "cmRemoveQuotes quoted quoted chars");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmEscapeQuotes
+  {
+    assert_string(cmEscapeQuotes("plain"), "plain", "cmEscapeQuotes plain");
+    std::string base = "\"base\"\"";
+    std::string result = "\\\"base\\\"\\\"";
+    assert_string(cmEscapeQuotes(base), result, "cmEscapeQuotes escaped");
+  }
+
+  // ----------------------------------------------------------------------
+  // 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 cmTokenize
+  {
+    typedef std::vector<std::string> VT;
+    assert_ok(cmTokenize("", ";") == VT{ "" }, "cmTokenize empty");
+    assert_ok(cmTokenize(";", ";") == VT{ "" }, "cmTokenize sep");
+    assert_ok(cmTokenize("abc", ";") == VT{ "abc" }, "cmTokenize item");
+    assert_ok(cmTokenize("abc;", ";") == VT{ "abc" }, "cmTokenize item sep");
+    assert_ok(cmTokenize(";abc", ";") == VT{ "abc" }, "cmTokenize sep item");
+    assert_ok(cmTokenize("abc;;efg", ";") == VT{ "abc", "efg" },
+              "cmTokenize item sep sep item");
+    assert_ok(cmTokenize("a1;a2;a3;a4", ";") == VT{ "a1", "a2", "a3", "a4" },
+              "cmTokenize multiple items");
+  }
+
+  // ----------------------------------------------------------------------
+  // 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");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmStrToLong
+  {
+    long value;
+    assert_ok(cmStrToLong("1", &value) && value == 1,
+              "cmStrToLong parses a positive decimal integer.");
+    assert_ok(cmStrToLong(" 1", &value) && value == 1,
+              "cmStrToLong parses a decimal integer after whitespace.");
+
+    assert_ok(cmStrToLong("-1", &value) && value == -1,
+              "cmStrToLong parses a negative decimal integer.");
+    assert_ok(
+      cmStrToLong(" -1", &value) && value == -1,
+      "cmStrToLong parses a negative decimal integer after whitespace.");
+
+    assert_ok(!cmStrToLong("1x", &value),
+              "cmStrToLong rejects trailing content.");
+  }
+
+  // ----------------------------------------------------------------------
+  // Test cmStrToULong
+  {
+    unsigned long value;
+    assert_ok(cmStrToULong("1", &value) && value == 1,
+              "cmStrToULong parses a decimal integer.");
+    assert_ok(cmStrToULong(" 1", &value) && value == 1,
+              "cmStrToULong parses a decimal integer after whitespace.");
+    assert_ok(!cmStrToULong("-1", &value),
+              "cmStrToULong rejects a negative number.");
+    assert_ok(!cmStrToULong(" -1", &value),
+              "cmStrToULong rejects a negative number after whitespace.");
+    assert_ok(!cmStrToULong("1x", &value),
+              "cmStrToULong rejects trailing content.");
+  }
+
+  return failed;
+}
diff --git a/Tests/CMakeLib/testSystemTools.cxx b/Tests/CMakeLib/testSystemTools.cxx
index 121e639..92f5275 100644
--- a/Tests/CMakeLib/testSystemTools.cxx
+++ b/Tests/CMakeLib/testSystemTools.cxx
@@ -4,10 +4,11 @@
 #include <cmConfigure.h> // IWYU pragma: keep
 
 #include <iostream>
-#include <stddef.h>
 #include <string>
 #include <vector>
 
+#include <stddef.h>
+
 #include "cmSystemTools.h"
 
 #define cmPassed(m) std::cout << "Passed: " << (m) << "\n"
@@ -94,21 +95,5 @@
     cmPassed("cmSystemTools::strverscmp working");
   }
 
-  // ----------------------------------------------------------------------
-  // Test cmSystemTools::StringToULong
-  {
-    unsigned long value;
-    cmAssert(cmSystemTools::StringToULong("1", &value) && value == 1,
-             "StringToULong parses a decimal integer.");
-    cmAssert(cmSystemTools::StringToULong(" 1", &value) && value == 1,
-             "StringToULong parses a decimal integer after whitespace.");
-    cmAssert(!cmSystemTools::StringToULong("-1", &value),
-             "StringToULong rejects a negative number.");
-    cmAssert(!cmSystemTools::StringToULong(" -1", &value),
-             "StringToULong rejects a negative number after whitespace.");
-    cmAssert(!cmSystemTools::StringToULong("1x", &value),
-             "StringToULong rejects trailing content.");
-  }
-
   return failed;
 }
diff --git a/Tests/CMakeLib/testUTF8.cxx b/Tests/CMakeLib/testUTF8.cxx
index a0bb5cd..986f595 100644
--- a/Tests/CMakeLib/testUTF8.cxx
+++ b/Tests/CMakeLib/testUTF8.cxx
@@ -1,8 +1,9 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include <cm_utf8.h>
 #include <stdio.h>
 
+#include <cm_utf8.h>
+
 typedef char test_utf8_char[5];
 
 static void test_utf8_char_print(test_utf8_char const c)
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx
index 72ae602..71a1764 100644
--- a/Tests/CMakeLib/testUVProcessChain.cxx
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -1,20 +1,19 @@
-#include "cmUVProcessChain.h"
-
-#include "cmAlgorithms.h"
-#include "cmGetPipes.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVStreambuf.h"
-
-#include "cm_uv.h"
-
 #include <algorithm>
+#include <csignal>
 #include <functional>
 #include <iostream>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include <csignal>
+#include <cm/memory>
+
+#include "cm_uv.h"
+
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStreambuf.h"
 
 struct ExpectedStatus
 {
diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx
index 263665d..9c25834 100644
--- a/Tests/CMakeLib/testUVProcessChainHelper.cxx
+++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx
@@ -1,13 +1,12 @@
+#include <cctype>
 #include <chrono>
+#include <cstdlib>
 #include <iostream>
 #include <set>
 #include <sstream>
 #include <string>
 #include <thread>
 
-#include <cctype>
-#include <cstdlib>
-
 std::string getStdin()
 {
   char buffer[1024];
@@ -44,7 +43,7 @@
   }
   if (command == "dedup") {
     // Use a nested scope to free all resources before aborting below.
-    {
+    try {
       std::string input = getStdin();
       std::set<char> seen;
       std::string output;
@@ -56,6 +55,7 @@
       }
       std::cout << output << std::flush;
       std::cerr << "3" << std::flush;
+    } catch (...) {
     }
 
     // On Windows, the exit code of abort() is different between debug and
diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx
index 2aeaf2c..cb05ace 100644
--- a/Tests/CMakeLib/testUVRAII.cxx
+++ b/Tests/CMakeLib/testUVRAII.cxx
@@ -1,5 +1,3 @@
-#include "cmUVHandlePtr.h"
-
 #include <chrono>
 #include <iostream>
 #include <thread>
@@ -7,6 +5,8 @@
 
 #include "cm_uv.h"
 
+#include "cmUVHandlePtr.h"
+
 static void signal_reset_fn(uv_async_t* handle)
 {
   auto ptr = static_cast<cm::uv_async_ptr*>(handle->data);
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
index 39655f3..cd9c9d4 100644
--- a/Tests/CMakeLib/testUVStreambuf.cxx
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -1,18 +1,16 @@
-#include "cmUVStreambuf.h"
-
-#include "cmGetPipes.h"
-#include "cmUVHandlePtr.h"
-
-#include "cm_uv.h"
-
+#include <cstring>
 #include <iostream>
 #include <string>
 #include <vector>
 
-#include <cstring>
-
 #include <stdint.h>
 
+#include "cm_uv.h"
+
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStreambuf.h"
+
 #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
 #define TEST_STR_LINE_2 "that we can test CMake's std::streambuf integration"
 #define TEST_STR_LINE_3 "with libuv's uv_stream_t."
diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.cxx b/Tests/CMakeLib/testVisualStudioSlnParser.cxx
index c7fd585..3c06960 100644
--- a/Tests/CMakeLib/testVisualStudioSlnParser.cxx
+++ b/Tests/CMakeLib/testVisualStudioSlnParser.cxx
@@ -1,10 +1,10 @@
 #include "testVisualStudioSlnParser.h"
 
+#include <iostream>
+
 #include "cmVisualStudioSlnData.h"
 #include "cmVisualStudioSlnParser.h"
 
-#include <iostream>
-
 static bool parsedRight(cmVisualStudioSlnParser& parser,
                         const std::string& file, cmSlnData& data,
                         cmVisualStudioSlnParser::ParseResult expected =
diff --git a/Tests/CMakeLib/testXMLParser.cxx b/Tests/CMakeLib/testXMLParser.cxx
index d5e9764..8617cc1 100644
--- a/Tests/CMakeLib/testXMLParser.cxx
+++ b/Tests/CMakeLib/testXMLParser.cxx
@@ -1,9 +1,9 @@
 #include "testXMLParser.h"
 
-#include "cmXMLParser.h"
-
 #include <iostream>
 
+#include "cmXMLParser.h"
+
 int testXMLParser(int /*unused*/, char* /*unused*/ [])
 {
   // TODO: Derive from parser and check attributes.
diff --git a/Tests/CMakeLib/testXMLSafe.cxx b/Tests/CMakeLib/testXMLSafe.cxx
index 21bb952..dc62eb9 100644
--- a/Tests/CMakeLib/testXMLSafe.cxx
+++ b/Tests/CMakeLib/testXMLSafe.cxx
@@ -4,9 +4,10 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <sstream>
-#include <stdio.h>
 #include <string>
 
+#include <stdio.h>
+
 #include "cmXMLSafe.h"
 
 struct test_pair
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index e73b277..32b580b 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -14,8 +14,8 @@
     ${build_generator_args}
     --build-project ${proj}
     ${${NAME}_CTEST_OPTIONS}
-    --build-options ${build_options}
-    ${${NAME}_BUILD_OPTIONS}
+    --build-options
+      ${${NAME}_BUILD_OPTIONS}
     ${_test_command})
   unset(_test_command)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${dir}")
@@ -127,9 +127,10 @@
       )
   endif()
 
-  set(build_options)
   if(CMake_TEST_EXPLICIT_MAKE_PROGRAM)
-    list(APPEND build_options -DCMAKE_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM})
+    list(APPEND build_generator_args
+      --build-makeprogram ${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
+      )
   endif()
 
   # Look for rpmbuild to use for tests.
@@ -415,7 +416,6 @@
   ADD_TEST_MACRO(COnly COnly)
   ADD_TEST_MACRO(CxxOnly CxxOnly)
   ADD_TEST_MACRO(CxxSubdirC CxxSubdirC)
-  ADD_TEST_MACRO(IPO COnly/COnly)
   ADD_TEST_MACRO(OutDir runtime/OutDir)
   ADD_TEST_MACRO(OutName exe.OutName.exe)
   ADD_TEST_MACRO(ObjectLibrary UseCshared)
@@ -451,8 +451,8 @@
   ADD_TEST_MACRO(StagingPrefix StagingPrefix)
   ADD_TEST_MACRO(ImportedSameName ImportedSameName)
   ADD_TEST_MACRO(InterfaceLibrary InterfaceLibrary)
-  if (CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
-    set(ConfigSources_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  if(NOT _isMultiConfig)
+    set(ConfigSources_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=$<CONFIGURATION>)
     ADD_TEST_MACRO(ConfigSources ConfigSources)
   endif()
   ADD_TEST_MACRO(SourcesProperty SourcesProperty)
@@ -528,7 +528,6 @@
       "${CMake_BINARY_DIR}/Tests/BundleUtilities"
       ${build_generator_args}
       --build-project BundleUtilities
-      --build-options ${build_options}
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BundleUtilities")
 
@@ -541,9 +540,9 @@
         "${CMake_BINARY_DIR}/Tests/Qt4Deploy"
         ${build_generator_args}
         --build-project Qt4Deploy
-        --build-options ${build_options}
-        -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-        -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+        --build-options
+          -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+          -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Qt4Deploy")
     endif()
@@ -590,7 +589,7 @@
     --build-project ExternalDataTest
     --build-noclean
     --force-new-ctest-process
-    --build-options ${build_options}
+    --build-options
       -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES}
     --test-command ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE} -V
     )
@@ -633,7 +632,6 @@
       "${CMake_BINARY_DIR}/Tests/Visibility"
       ${build_generator_args}
       --build-project Visibility
-      --build-options ${build_options}
     )
     list(APPEND TEST_BUILD_DIRS
       "${CMake_BINARY_DIR}/Tests/Visibility"
@@ -648,7 +646,7 @@
     ${build_generator_args}
     --build-project LinkFlags
     --build-target LinkFlags
-    --build-options ${build_options}
+    --build-options
       -DTEST_CONFIG=\${CTEST_CONFIGURATION_TYPE}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/LinkFlags")
@@ -709,7 +707,6 @@
           --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}"
           --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}"
           --build-project Simple
-          --build-options ${build_options}
           --test-command Simple)
         list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${extraGeneratorTestName}")
       endif ()
@@ -729,7 +726,6 @@
       --build-project SubProject
       ${build_generator_args}
       --build-target car
-      --build-options ${build_options}
       --test-command car
       )
 
@@ -755,7 +751,6 @@
       --build-project foo
       --build-target foo
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/SubProject/foo"
-      --build-options ${build_options}
       --test-command foo
       )
     set_tests_properties ( SubProject-Stage2 PROPERTIES DEPENDS SubProject)
@@ -768,7 +763,8 @@
     set(_TEST_DIR "${CMake_BINARY_DIR}/Tests/${name}")
     file(MAKE_DIRECTORY "${_TEST_DIR}")
     file(WRITE "${_TEST_DIR}/nightly-cmake.sh"
-      "cd ${_TEST_DIR}
+      "set -e
+cd ${_TEST_DIR}
 ${CMake_BINARY_DIR}/bin/cmake -DCMAKE_CREATE_VERSION=nightly -P ${CMake_SOURCE_DIR}/Utilities/Release/${script}
 ${CMake_SOURCE_DIR}/Utilities/Release/push.bash --dir dev -- '${CMake_BUILD_NIGHTLY_RELEASES}'
     ")
@@ -784,8 +780,6 @@
       win64_release.cmake)
     ADD_NIGHTLY_BUILD_TEST(CMakeNightlyOSX
       osx_release.cmake)
-    ADD_NIGHTLY_BUILD_TEST(CMakeNightlyLinux64
-      linux64_release.cmake)
     set_property(TEST CMakeNightlyWin64 PROPERTY DEPENDS CMakeNightlyWin32)
   endif()
 
@@ -797,7 +791,7 @@
     --build-two-config
     ${build_generator_args}
     --build-project Framework
-    --build-options ${build_options}
+    --build-options
     "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/Framework/Install"
     --test-command bar)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Framework")
@@ -809,7 +803,6 @@
     --build-two-config
     ${build_generator_args}
     --build-project TargetName
-    --build-options ${build_options}
     --test-command ${CMAKE_CMAKE_COMMAND} -E compare_files
     ${CMake_SOURCE_DIR}/Tests/TargetName/scripts/hello_world
     ${CMake_BINARY_DIR}/Tests/TargetName/scripts/hello_world)
@@ -823,7 +816,6 @@
     ${build_generator_args}
     --build-project LibName
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/LibName/lib"
-    --build-options ${build_options}
     --test-command foobar
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/LibName")
@@ -836,7 +828,6 @@
     ${build_generator_args}
     --build-project CustComDepend
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/CustComDepend/bin"
-    --build-options ${build_options}
     --test-command foo bar.c
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CustComDepend")
@@ -848,7 +839,6 @@
     ${build_generator_args}
     --build-project ArgumentExpansion
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/ArgumentExpansion/bin"
-    --build-options ${build_options}
     )
   set_tests_properties(ArgumentExpansion PROPERTIES
     FAIL_REGULAR_EXPRESSION "Unexpected: ")
@@ -861,7 +851,7 @@
     "${CMake_BINARY_DIR}/Tests/GeneratorExpression"
     ${build_generator_args}
     --build-project GeneratorExpression
-    --build-options ${build_options}
+    --build-options
       -DCMAKE_BUILD_TYPE=\${CTEST_CONFIGURATION_TYPE}
     --test-command ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE} -V
     )
@@ -875,7 +865,7 @@
     ${build_generator_args}
     --build-project CustomCommand
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/CustomCommand/bin"
-    --build-options ${build_options}
+    --build-options
     --test-command CustomCommand
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CustomCommand")
@@ -893,7 +883,6 @@
     --build-two-config
     ${build_generator_args}
     --build-project TestWorkingDir
-    --build-options ${build_options}
     --test-command working
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CustomCommandWorkingDirectory")
@@ -905,7 +894,6 @@
     ${build_generator_args}
     --build-project OutOfSource
     --build-two-config
-    --build-options ${build_options}
     --test-command
     "${CMake_BINARY_DIR}/Tests/OutOfSource/SubDir/OutOfSourceSubdir/simple")
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/OutOfSource")
@@ -918,7 +906,6 @@
     "${CMake_BINARY_DIR}/Tests/BuildDepends"
     ${build_generator_args}
     --build-project BuildDepends
-    --build-options ${build_options}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BuildDepends")
 
@@ -931,7 +918,7 @@
     ${build_generator_args}
     --build-project TestMissingInstall
     --build-two-config
-    --build-options ${build_options}
+    --build-options
     "-DCMAKE_INSTALL_PREFIX:PATH=${MissingInstallInstallDir}")
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/MissingInstall")
 
@@ -977,7 +964,7 @@
         "${CMake_BINARY_DIR}/Tests/CPackWiXGenerator"
         ${build_generator_args}
         --build-project CPackWiXGenerator
-        --build-options ${build_options}
+        --build-options
         --test-command ${CMAKE_CMAKE_COMMAND}
           "-DCPackWiXGenerator_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackWiXGenerator"
           "-Dconfig=\${CTEST_CONFIGURATION_TYPE}"
@@ -997,7 +984,7 @@
       ${build_generator_args}
       --build-project CPackUseDefaultVersion
       --build-two-config
-      --build-options ${build_options}
+      --build-options
         ${CPackUseDefaultVersion_BUILD_OPTIONS})
     set_tests_properties(CPackUseDefaultVersion PROPERTIES PASS_REGULAR_EXPRESSION "CPACK_PACKAGE_VERSION=0\\.1\\.1")
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CPackUseDefaultVersion")
@@ -1009,7 +996,7 @@
       ${build_generator_args}
       --build-project CPackUseProjectVersion
       --build-two-config
-      --build-options ${build_options}
+      --build-options
         ${CPackUseProjectVersion_BUILD_OPTIONS})
     set_tests_properties(CPackUseProjectVersion PROPERTIES PASS_REGULAR_EXPRESSION "CPACK_PACKAGE_VERSION=1\\.2\\.3")
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CPackUseProjectVersion")
@@ -1021,7 +1008,7 @@
       ${build_generator_args}
       --build-project CPackUseShortProjectVersion
       --build-two-config
-      --build-options ${build_options}
+      --build-options
         ${CPackUseProjectVersion_BUILD_OPTIONS})
     set_tests_properties(CPackUseShortProjectVersion PROPERTIES PASS_REGULAR_EXPRESSION "CPACK_PACKAGE_VERSION=2")
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CPackUseShortProjectVersion")
@@ -1049,7 +1036,7 @@
       --build-project CPackComponents
       --build-two-config
       --build-target package
-      --build-options ${build_options}
+      --build-options
         -DCPACK_BINARY_DEB:BOOL=${CPACK_BINARY_DEB}
         -DCPACK_BINARY_RPM:BOOL=${CPACK_BINARY_RPM}
         ${CPackComponents_BUILD_OPTIONS}
@@ -1109,7 +1096,7 @@
           "${CMake_BINARY_DIR}/Tests/CPackComponentsForAll/build${CPackGen}-${CPackComponentWay}"
           ${build_generator_args}
           --build-project CPackComponentsForAll
-          --build-options ${build_options}
+          --build-options
              -DCPACK_GENERATOR:STRING=${CPACK_GENERATOR_STRING_${CPackGen}}
              -DCPACK_BINARY_${CPackGen}:BOOL=ON
              ${CPackRun_CPackComponentWay}
@@ -1148,7 +1135,7 @@
               "${CMake_BINARY_DIR}/Tests/${DEB_TEST_NAMES}/build${CPackGen}-${CPackDEBConfiguration}"
               ${build_generator_args}
           --build-project CPackComponentsDEB
-          --build-options ${build_options}
+          --build-options
               -DCPACK_GENERATOR:STRING=${CPackGen}
               -DCPACK_BINARY_${CPackGen}:BOOL=ON
               ${CPackRun_CPackDEBConfiguration}
@@ -1188,7 +1175,6 @@
       "${CMake_BINARY_DIR}/Tests/CPackTestAllGenerators"
       ${build_generator_args}
       --build-project CPackTestAllGenerators
-      --build-options ${build_options}
       --test-command
       ${CMAKE_CMAKE_COMMAND}
         -D dir=${CMake_BINARY_DIR}/Tests/CPackTestAllGenerators
@@ -1219,7 +1205,7 @@
       --build-project CPackComponentsPrefix
       --build-two-config
       --build-target package
-      --build-options ${build_options}
+      --build-options
         -DCPACK_BINARY_DEB:BOOL=${CPACK_BINARY_DEB}
         -DCPACK_BINARY_RPM:BOOL=${CPACK_BINARY_RPM}
         -DCPACK_BINARY_ZIP:BOOL=ON
@@ -1242,7 +1228,6 @@
     --build-project UseX11
     --build-two-config
     ${X11_build_target_arg}
-    --build-options ${build_options}
     --test-command  UseX11)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/X11")
 
@@ -1285,7 +1270,6 @@
       "${CMake_BINARY_DIR}/Tests/LoadCommandOneConfig"
       ${build_generator_args}
       --build-project LoadCommand
-      --build-options ${build_options}
       --test-command  LoadedCommand
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/LoadCommandOneConfig")
@@ -1300,7 +1284,6 @@
     ${build_generator_args}
     --build-project Complex
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Complex/bin"
-    --build-options ${build_options}
     -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
     --test-command complex
     )
@@ -1313,7 +1296,6 @@
     ${build_generator_args}
     --build-project Complex
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/ComplexOneConfig/bin"
-    --build-options ${build_options}
     -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
     --test-command complex)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ComplexOneConfig")
@@ -1328,7 +1310,6 @@
     --build-project EnvironmentProj
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Environment"
     --force-new-ctest-process
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Environment")
@@ -1339,7 +1320,7 @@
     "${CMake_BINARY_DIR}/Tests/QtAutomocNoQt"
     ${build_generator_args}
     --build-project QtAutomocNoQt
-    --build-options ${build_options}
+    --build-options
       -DCMAKE_BUILD_TYPE=\${CTEST_CONFIGURATION_TYPE}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/QtAutomocNoQt")
@@ -1364,7 +1345,7 @@
       --build-project Qt4Targets
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/Qt4Targets"
       --force-new-ctest-process
-      --build-options ${build_options}
+      --build-options
         -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
       --test-command ${CMAKE_CTEST_COMMAND} -V
       )
@@ -1379,7 +1360,6 @@
         --build-project Qt4And5Automoc
         --build-exe-dir "${CMake_BINARY_DIR}/Tests/Qt4And5AutomocForward"
         --force-new-ctest-process
-        --build-options ${build_options}
         --test-command ${CMAKE_CTEST_COMMAND} -V
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Qt4And5AutomocForward")
@@ -1391,191 +1371,75 @@
         --build-project Qt4And5Automoc
         --build-exe-dir "${CMake_BINARY_DIR}/Tests/Qt4And5AutomocReverse"
         --force-new-ctest-process
-        --build-options ${build_options} -DQT_REVERSE_FIND_ORDER=1
+        --build-options -DQT_REVERSE_FIND_ORDER=1
         --test-command ${CMAKE_CTEST_COMMAND} -V
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Qt4And5AutomocReverse")
     endif()
   endif()
 
-  if(CMake_TEST_FindALSA)
-    add_subdirectory(FindALSA)
-  endif()
+  # test for Find modules, simple cases
+  foreach(_mod IN ITEMS
+            ALSA
+            Boost
+            BZip2
+            CURL
+            Cups
+            Doxygen
+            EnvModules
+            EXPAT
+            Fontconfig
+            Freetype
+            GDAL
+            GIF
+            Git
+            GLEW
+            GnuTLS
+            GSL
+            GTK2
+            Iconv
+            ICU
+            JPEG
+            JsonCpp
+            LibLZMA
+            LibRHash
+            Libinput
+            LibUV
+            LibXml2
+            LTTngUST
+            ODBC
+            OpenACC
+            OpenCL
+            OpenGL
+            OpenMP
+            OpenSSL
+            MPI
+            PNG
+            Patch
+            PostgreSQL
+            Protobuf
+            SQLite3
+            TIFF
+            Vulkan
+            X11
+            XalanC
+            XercesC
+         )
+    if(CMake_TEST_Find${_mod})
+      add_subdirectory(Find${_mod})
+    endif()
+  endforeach()
 
   if(CMake_TEST_CUDA)
     add_subdirectory(Cuda)
     add_subdirectory(CudaOnly)
   endif()
 
-  if(CMake_TEST_FindBoost)
-    add_subdirectory(FindBoost)
-  endif()
-
-  if(CMake_TEST_FindBZip2)
-    add_subdirectory(FindBZip2)
-  endif()
-
-  if(CMake_TEST_FindCURL)
-    add_subdirectory(FindCURL)
-  endif()
-
-  if(CMake_TEST_FindCups)
-    add_subdirectory(FindCups)
-  endif()
-
-  if(CMake_TEST_FindDoxygen)
-    add_subdirectory(FindDoxygen)
-  endif()
-
-  if(CMake_TEST_FindEnvModules)
-    add_subdirectory(FindEnvModules)
-  endif()
-
-  if(CMake_TEST_FindEXPAT)
-    add_subdirectory(FindEXPAT)
-  endif()
-
-  if(CMake_TEST_FindFontconfig)
-    add_subdirectory(FindFontconfig)
-  endif()
-
-  if(CMake_TEST_FindFreetype)
-    add_subdirectory(FindFreetype)
-  endif()
-
-  if(CMake_TEST_FindGDAL)
-    add_subdirectory(FindGDAL)
-  endif()
-
-  if(CMake_TEST_FindGIF)
-    add_subdirectory(FindGIF)
-  endif()
-
-  if(CMake_TEST_FindGit)
-    add_subdirectory(FindGit)
-  endif()
-
-  if(CMake_TEST_FindGLEW)
-    add_subdirectory(FindGLEW)
-  endif()
-
-  if(CMake_TEST_FindGSL)
-    add_subdirectory(FindGSL)
-  endif()
-
   if(CMake_TEST_FindGTest)
     add_subdirectory(FindGTest)
     add_subdirectory(GoogleTest)
   endif()
 
-  if(CMake_TEST_FindGTK2)
-    add_subdirectory(FindGTK2)
-  endif()
-
-  if(CMake_TEST_FindIconv)
-    add_subdirectory(FindIconv)
-  endif()
-
-  if(CMake_TEST_FindICU)
-    add_subdirectory(FindICU)
-  endif()
-
-  if(CMake_TEST_FindJPEG)
-    add_subdirectory(FindJPEG)
-  endif()
-
-  if(CMake_TEST_FindJsonCpp)
-    add_subdirectory(FindJsonCpp)
-  endif()
-
-  if(CMake_TEST_FindLibLZMA)
-    add_subdirectory(FindLibLZMA)
-  endif()
-
-  if(CMake_TEST_FindLibRHash)
-    add_subdirectory(FindLibRHash)
-  endif()
-
-  if(CMake_TEST_FindLibinput)
-    add_subdirectory(FindLibinput)
-  endif()
-
-  if(CMake_TEST_FindLibUV)
-    add_subdirectory(FindLibUV)
-  endif()
-
-  if(CMake_TEST_FindLibXml2)
-    add_subdirectory(FindLibXml2)
-  endif()
-
-  if(CMake_TEST_FindLTTngUST)
-    add_subdirectory(FindLTTngUST)
-  endif()
-
-  if(CMake_TEST_FindODBC)
-    add_subdirectory(FindODBC)
-  endif()
-
-  if(CMake_TEST_FindOpenCL)
-    add_subdirectory(FindOpenCL)
-  endif()
-
-  if(CMake_TEST_FindOpenGL)
-    add_subdirectory(FindOpenGL)
-  endif()
-
-  if(CMake_TEST_FindOpenMP)
-    add_subdirectory(FindOpenMP)
-  endif()
-
-  if(CMake_TEST_FindOpenSSL)
-    add_subdirectory(FindOpenSSL)
-  endif()
-
-  if(CMake_TEST_FindMPI)
-    add_subdirectory(FindMPI)
-  endif()
-
-  if(CMake_TEST_FindPNG)
-    add_subdirectory(FindPNG)
-  endif()
-
-  if(CMake_TEST_FindPatch)
-    add_subdirectory(FindPatch)
-  endif()
-
-  if(CMake_TEST_FindPostgreSQL)
-    add_subdirectory(FindPostgreSQL)
-  endif()
-
-  if(CMake_TEST_FindProtobuf)
-    add_subdirectory(FindProtobuf)
-  endif()
-
-  if(CMake_TEST_FindSQLite3)
-    add_subdirectory(FindSQLite3)
-  endif()
-
-  if(CMake_TEST_FindTIFF)
-    add_subdirectory(FindTIFF)
-  endif()
-
-  if(CMake_TEST_FindVulkan)
-    add_subdirectory(FindVulkan)
-  endif()
-
-  if(CMake_TEST_FindX11)
-    add_subdirectory(FindX11)
-  endif()
-
-  if(CMake_TEST_FindXalanC)
-    add_subdirectory(FindXalanC)
-  endif()
-
-  if(CMake_TEST_FindXercesC)
-    add_subdirectory(FindXercesC)
-  endif()
-
   if(CMake_TEST_FindPython OR CMake_TEST_FindPython_NumPy)
     add_subdirectory(FindPython)
   endif()
@@ -1591,16 +1455,16 @@
   # CMake_TEST_FindMatlab_ROOT_DIR: indicates an optional root directory for Matlab, allows to select a version.
   # CMake_TEST_FindMatlab_MCR: indicates the MCR is installed
   # CMake_TEST_FindMatlab_MCR_ROOT_DIR: indicates an optional root directory for the MCR, required on Linux
-  if(CMake_TEST_FindMatlab OR (NOT "${CMake_TEST_FindMatlab_ROOT_DIR}" STREQUAL "") OR
-     CMake_TEST_FindMatlab_MCR OR (NOT "${CMake_TEST_FindMatlab_MCR_ROOT_DIR}" STREQUAL ""))
+  if(CMake_TEST_FindMatlab OR CMake_TEST_FindMatlab_ROOT_DIR OR
+     CMake_TEST_FindMatlab_MCR OR CMake_TEST_FindMatlab_MCR_ROOT_DIR)
     set(FindMatlab_additional_test_options )
-    if(CMake_TEST_FindMatlab_MCR OR NOT "${CMake_TEST_FindMatlab_MCR_ROOT_DIR}" STREQUAL "")
+    if(CMake_TEST_FindMatlab_MCR OR CMake_TEST_FindMatlab_MCR_ROOT_DIR)
       set(FindMatlab_additional_test_options -DIS_MCR=TRUE)
     endif()
-    if(NOT "${CMake_TEST_FindMatlab_ROOT_DIR}" STREQUAL "")
+    if(CMake_TEST_FindMatlab_ROOT_DIR)
       set(FindMatlab_additional_test_options ${FindMatlab_additional_test_options} "-DMatlab_ROOT_DIR=${CMake_TEST_FindMatlab_ROOT_DIR}")
     endif()
-    if(NOT "${CMake_TEST_FindMatlab_MCR_ROOT_DIR}" STREQUAL "")
+    if(CMake_TEST_FindMatlab_MCR_ROOT_DIR)
       set(FindMatlab_additional_test_options ${FindMatlab_additional_test_options} "-DMCR_ROOT:FILEPATH=${CMake_TEST_FindMatlab_MCR_ROOT_DIR}")
     endif()
     set(FindMatlab.basic_checks_BUILD_OPTIONS ${FindMatlab_additional_test_options})
@@ -1623,7 +1487,6 @@
     --build-project ExternalProjectTest
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/ExternalProject"
     --force-new-ctest-process
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProject")
@@ -1639,7 +1502,6 @@
     ${build_generator_args}
     --build-project ExternalProjectSubdir
     --force-new-ctest-process
-    --build-options ${build_options}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectSubdir")
 
@@ -1651,7 +1513,6 @@
     ${build_generator_args}
     --build-project ExternalProjectSourceSubdir
     --force-new-ctest-process
-    --build-options ${build_options}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectSourceSubdir")
 
@@ -1663,7 +1524,6 @@
     ${build_generator_args}
     --build-project ExternalProjectSourceSubdirNotCMake
     --force-new-ctest-process
-    --build-options ${build_options}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectSourceSubdirNotCMake")
 
@@ -1675,7 +1535,6 @@
     --build-project ExternalProjectLocalTest
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/ExternalProjectLocal"
     --force-new-ctest-process
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectLocal")
@@ -1691,7 +1550,6 @@
     --build-project ExternalProjectUpdateTest
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate"
     --force-new-ctest-process
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ExternalProjectUpdate")
@@ -1729,17 +1587,17 @@
     endif()
     add_test(${tutorial_test_name} ${CMAKE_CTEST_COMMAND}
       --build-and-test
-      "${CMake_SOURCE_DIR}/Tests/Tutorial/${step_name}"
+      "${CMake_SOURCE_DIR}/Help/guide/tutorial/${step_name}"
       ${tutorial_build_dir}_Build
       ${build_generator_args}
       --build-project Tutorial
-      --build-options ${build_options} ${tutorial_build_options}
+      --build-options ${tutorial_build_options}
       --test-command Tutorial 25.0)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/${tutorial_build_dir}_Build")
   endfunction()
 
   if(NOT CMake_TEST_EXTERNAL_CMAKE)
-    foreach(STP RANGE 1 11)
+    foreach(STP RANGE 2 11)
       add_tutorial_test(Step${STP} TRUE)
     endforeach()
     add_tutorial_test(Complete TRUE)
@@ -1755,7 +1613,6 @@
     "${CMake_BINARY_DIR}/Tests/Testing"
     ${build_generator_args}
     --build-project Testing
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE}
     )
   set_tests_properties(testing PROPERTIES PASS_REGULAR_EXPRESSION "Passed")
@@ -1768,7 +1625,6 @@
     ${build_generator_args}
     --build-project Wrapping
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Wrapping/bin"
-    --build-options ${build_options}
     --test-command wrapping
     )
   add_test(qtwrapping  ${CMAKE_CTEST_COMMAND}
@@ -1778,7 +1634,6 @@
     ${build_generator_args}
     --build-project Wrapping
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Wrapping/bin"
-    --build-options ${build_options}
       --test-command qtwrapping
       )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Wrapping")
@@ -1790,7 +1645,6 @@
     ${build_generator_args}
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Wrapping/bin"
     --build-project TestDriverTest
-    --build-options ${build_options}
     --test-command TestDriverTest test1
     )
 
@@ -1801,7 +1655,6 @@
     ${build_generator_args}
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Wrapping/bin"
     --build-project TestDriverTest
-    --build-options ${build_options}
     --test-command TestDriverTest test2
     )
 
@@ -1812,7 +1665,6 @@
     ${build_generator_args}
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Wrapping/bin"
     --build-project TestDriverTest
-    --build-options ${build_options}
     --test-command TestDriverTest subdir/test3
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/TestDriver")
@@ -1824,7 +1676,6 @@
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/Dependency/Exec"
     ${build_generator_args}
     --build-project Dependency
-    --build-options ${build_options}
     --test-command exec
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Dependency")
@@ -1854,8 +1705,8 @@
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/Jump/WithLibOut/Executable"
       --build-project Jump
       ${build_generator_args}
-      --build-options ${build_options}
-      -DLIBRARY_OUTPUT_PATH:PATH=${CMake_BINARY_DIR}/Tests/Jump/WithLibOut/Lib
+      --build-options
+        -DLIBRARY_OUTPUT_PATH:PATH=${CMake_BINARY_DIR}/Tests/Jump/WithLibOut/Lib
       --test-command jumpExecutable
       )
 
@@ -1867,7 +1718,6 @@
       --build-run-dir "${CMake_BINARY_DIR}/Tests/Jump/NoLibOut/Executable"
       --build-project Jump
       ${build_generator_args}
-      --build-options ${build_options}
       --test-command jumpExecutable
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Jump")
@@ -1879,7 +1729,6 @@
       ${build_generator_args}
       --build-project Plugin
       --build-two-config
-      --build-options ${build_options}
       --test-command bin/example)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Plugin")
 
@@ -1895,7 +1744,7 @@
       "${CMake_BINARY_DIR}/Tests/MacRuntimePath"
       ${build_generator_args}
       --build-project MacRuntimePath
-      --build-options ${build_options}
+      --build-options
         -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
       )
   endif()
@@ -1913,7 +1762,6 @@
     "${CMake_BINARY_DIR}/Tests/LinkLineOrder"
     ${build_generator_args}
     --build-project LinkLineOrder
-    --build-options ${build_options}
     --test-command Exec1
     )
 
@@ -1923,7 +1771,6 @@
     "${CMake_BINARY_DIR}/Tests/LinkLineOrder"
     ${build_generator_args}
     --build-project LinkLineOrder
-    --build-options ${build_options}
     --test-command Exec2
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/LinkLineOrder")
@@ -1943,7 +1790,7 @@
       "${CMake_BINARY_DIR}/Tests/LinkStatic"
       ${build_generator_args}
       --build-project LinkStatic
-      --build-options ${build_options}
+      --build-options
         -DMATH_LIBRARY:FILEPATH=/usr/lib/libm.a
       --test-command LinkStatic
       )
@@ -1958,7 +1805,6 @@
       "${CMake_BINARY_DIR}/Tests/SubDirSpaces/Executable Sources"
       ${build_generator_args}
       --build-project SUBDIR
-      --build-options ${build_options}
       --test-command test
       "${CMake_BINARY_DIR}/Tests/SubDirSpaces/ShouldBeHere"
       "${CMake_BINARY_DIR}/Tests/SubDirSpaces/testfromsubdir.obj"
@@ -1974,7 +1820,6 @@
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/SubDir/Executable"
       ${build_generator_args}
       --build-project SUBDIR
-      --build-options ${build_options}
       --test-command test
       "${CMake_BINARY_DIR}/Tests/SubDir/ShouldBeHere"
       "${CMake_BINARY_DIR}/Tests/SubDir/testfromsubdir.obj"
@@ -1987,7 +1832,6 @@
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/SubDir/Executable"
       ${build_generator_args}
       --build-project SUBDIR
-      --build-options ${build_options}
       --test-command test
       "${CMake_BINARY_DIR}/Tests/SubDir/ShouldBeHere"
       "${CMake_BINARY_DIR}/Tests/SubDir/testfromsubdir.o"
@@ -1995,9 +1839,11 @@
   endif ()
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/SubDir")
 
-  if(MSVC)
-    ADD_TEST_MACRO(ForceInclude foo)
+  if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC"))
     ADD_TEST_MACRO(PDBDirectoryAndName myexe)
+    if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" OR NOT "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+      ADD_TEST_MACRO(ForceInclude foo)
+    endif()
     if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
       ADD_TEST_MACRO(PrecompiledHeader foo)
     endif()
@@ -2023,7 +1869,6 @@
       ${build_generator_args}
       --build-project MakeClean
       --build-exe-dir "${CMake_BINARY_DIR}/MakeClean"
-      --build-options ${build_options}
       --test-command check_clean
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/MakeClean")
@@ -2132,7 +1977,6 @@
       --build-two-config
       ${build_generator_args}
       --build-project mfc_driver
-      --build-options ${build_options}
       --test-command ${CMAKE_CTEST_COMMAND}
         -C \${CTEST_CONFIGURATION_TYPE} -VV)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/MFC")
@@ -2162,7 +2006,6 @@
       --build-two-config
       ${build_generator_args}
       --build-project VSExternalInclude
-      --build-options ${build_options}
       --test-command VSExternalInclude)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSExternalInclude")
 
@@ -2173,7 +2016,6 @@
       --build-two-config
       ${build_generator_args}
       --build-project VSMidl
-      --build-options ${build_options}
       --test-command VSMidl)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSMidl")
 
@@ -2237,6 +2079,9 @@
         --build-config $<CONFIGURATION>
         --build-options -DCMAKE_SYSTEM_NAME=${systemName}
                         -DCMAKE_SYSTEM_VERSION=${systemVersion}
+        --test-command
+          ${CMAKE_CMAKE_COMMAND} -DAPP_PACKAGE_DIR="${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}"
+                                 -P "${CMake_SOURCE_DIR}/Tests/VSWinStorePhone/VerifyAppPackage.cmake"
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}")
     endmacro()
@@ -2287,11 +2132,11 @@
     macro(add_test_VSWinCE name generator systemName systemVersion generatorPlatform)
       # TODO: Fix the tutorial to make it work in cross compile
       # currently the MakeTable is build for target and can not be used on the host
-      # This happens in part 5 so we build only part 1-4 of the tutorial
-      foreach(STP RANGE 1 4)
+      # This happens in part 5 so we build only through part 4 of the tutorial.
+      foreach(STP RANGE 2 4)
         add_test(NAME "TutorialStep${STP}.${name}" COMMAND ${CMAKE_CTEST_COMMAND}
           --build-and-test
-          "${CMake_SOURCE_DIR}/Tests/Tutorial/Step${STP}"
+          "${CMake_SOURCE_DIR}/Help/guide/tutorial/Step${STP}"
           "${CMake_BINARY_DIR}/Tests/Tutorial/Step${STP}_${name}"
           --build-generator "${generator}"
           --build-project Tutorial
@@ -2460,7 +2305,7 @@
         --build-project BundleTest
         --build-target install
 #       --build-target package
-        --build-options ${build_options}
+        --build-options
         "-DCMAKE_INSTALL_PREFIX:PATH=${BundleTestInstallDir}"
         "-DCMake_SOURCE_DIR:PATH=${CMake_SOURCE_DIR}"
         --test-command
@@ -2475,7 +2320,6 @@
         ${build_generator_args}
         --build-project CFBundleTest
         --build-config $<CONFIGURATION>
-        --build-options ${build_options}
         --test-command
         ${CMAKE_CMAKE_COMMAND} -DCTEST_CONFIGURATION_TYPE=$<CONFIGURATION>
         -Ddir=${CMake_BINARY_DIR}/Tests/CFBundleTest
@@ -2484,6 +2328,79 @@
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CFBundleTest")
 
       ADD_TEST_MACRO(ObjC++ ObjC++)
+
+      add_test(Objective-C.simple-build-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C/simple-build-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C/simple-build-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project simple-build-test
+        --build-options ${build_options}
+        --test-command simple-build-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C/simple-build-test")
+
+      add_test(Objective-C.c-file-extension-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C/c-file-extension-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C/c-file-extension-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project c-file-extension-test
+        --build-options ${build_options}
+        --test-command c-file-extension-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C/c-file-extension-test")
+
+      add_test(Objective-C.objc-file-extension-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C/objc-file-extension-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C/objc-file-extension-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project objc-file-extension-test
+        --build-options ${build_options}
+        --test-command objc-file-extension-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMAKE_BINARY_DIR}/Tests/Objective-C/objc-file-extension-test")
+
+      add_test(Objective-CXX.simple-build-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C++/simple-build-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C++/simple-build-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project simple-build-test
+        --build-options ${build_options}
+        --test-command simple-build-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C++/simple-build-test")
+
+      add_test(Objective-CXX.cxx-file-extension-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C++/cxx-file-extension-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C++/cxx-file-extension-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project cxx-file-extension-test
+        --build-options ${build_options}
+        --test-command cxx-file-extension-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Objective-C++/cxx-file-extension-test")
+
+      add_test(Objective-CXX.objcxx-file-extension-test ${CMAKE_CTEST_COMMAND}
+        --build-and-test
+        "${CMake_SOURCE_DIR}/Tests/Objective-C++/objcxx-file-extension-test"
+        "${CMake_BINARY_DIR}/Tests/Objective-C++/objcxx-file-extension-test"
+        --build-two-config
+        ${build_generator_args}
+        --build-project objcxx-file-extension-test
+        --build-options ${build_options}
+        --test-command objcxx-file-extension-test
+      )
+      list(APPEND TEST_BUILD_DIRS "${CMAKE_BINARY_DIR}/Tests/Objective-C++/objcxx-file-extension-test")
+
     endif ()
   endif ()
 
@@ -2496,7 +2413,7 @@
       ${build_generator_args}
       --build-project BundleGeneratorTest
       --build-target package
-      --build-options ${build_options}
+      --build-options
         "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/BundleGeneratorTest/InstallDirectory"
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BundleGeneratorTest")
@@ -2509,7 +2426,7 @@
     ${build_generator_args}
     --build-noclean
     --build-project WarnUnusedUnusedViaSet
-    --build-options ${build_options}
+    --build-options
       "--warn-unused-vars")
   set_tests_properties(WarnUnusedUnusedViaSet PROPERTIES
     PASS_REGULAR_EXPRESSION "unused variable \\(changing definition\\) 'UNUSED_VARIABLE'")
@@ -2524,7 +2441,7 @@
     ${build_generator_args}
     --build-noclean
     --build-project WarnUnusedUnusedViaUnset
-    --build-options ${build_options}
+    --build-options
       "--warn-unused-vars")
   set_tests_properties(WarnUnusedUnusedViaUnset PROPERTIES
     PASS_REGULAR_EXPRESSION "CMake Warning \\(dev\\) at CMakeLists.txt:7 \\(set\\):")
@@ -2538,7 +2455,7 @@
     "${CMake_BINARY_DIR}/Tests/WarnUnusedCliUnused"
     ${build_generator_args}
     --build-project WarnUnusedCliUnused
-    --build-options ${build_options}
+    --build-options
       "-DUNUSED_CLI_VARIABLE=Unused")
   set_tests_properties(WarnUnusedCliUnused PROPERTIES
     PASS_REGULAR_EXPRESSION "CMake Warning:.*Manually-specified variables were not used by the project:.*  UNUSED_CLI_VARIABLE")
@@ -2551,7 +2468,7 @@
     ${build_generator_args}
     --build-noclean
     --build-project WarnUnusedCliUsed
-    --build-options ${build_options}
+    --build-options
       "-DUSED_VARIABLE=Usage proven")
   set_tests_properties(WarnUnusedCliUsed PROPERTIES
     PASS_REGULAR_EXPRESSION "Usage proven")
@@ -2566,7 +2483,7 @@
     ${build_generator_args}
     --build-noclean
     --build-project WarnUninitialized
-    --build-options ${build_options}
+    --build-options
       "--warn-uninitialized")
   set_tests_properties(WarnUninitialized PROPERTIES
     PASS_REGULAR_EXPRESSION "uninitialized variable 'USED_VARIABLE'")
@@ -2581,7 +2498,6 @@
     --build-project TestsWorkingDirectoryProj
     --build-exe-dir "${CMake_BINARY_DIR}/Tests/TestsWorkingDirectory"
     --force-new-ctest-process
-    --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V -C \${CTEST_CONFIGURATION_TYPE}
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/TestsWorkingDirectory")
@@ -2778,16 +2694,18 @@
     PASS_REGULAR_EXPRESSION "Could not find executable"
     FAIL_REGULAR_EXPRESSION "SegFault")
 
-  configure_file(
-    "${CMake_SOURCE_DIR}/Tests/CTestTestUpload/test.cmake.in"
-    "${CMake_BINARY_DIR}/Tests/CTestTestUpload/test.cmake"
-    @ONLY ESCAPE_QUOTES)
-  add_test(CTestTestUpload ${CMAKE_CTEST_COMMAND}
-    -S "${CMake_BINARY_DIR}/Tests/CTestTestUpload/test.cmake" -V
-    --output-log "${CMake_BINARY_DIR}/Tests/CTestTestUpload/testOut.log"
-    )
-  set_tests_properties(CTestTestUpload PROPERTIES
-    PASS_REGULAR_EXPRESSION "Upload\\.xml")
+  if(NOT CMake_TEST_NO_NETWORK)
+    configure_file(
+      "${CMake_SOURCE_DIR}/Tests/CTestTestUpload/test.cmake.in"
+      "${CMake_BINARY_DIR}/Tests/CTestTestUpload/test.cmake"
+      @ONLY ESCAPE_QUOTES)
+    add_test(CTestTestUpload ${CMAKE_CTEST_COMMAND}
+      -S "${CMake_BINARY_DIR}/Tests/CTestTestUpload/test.cmake" -V
+      --output-log "${CMake_BINARY_DIR}/Tests/CTestTestUpload/testOut.log"
+      )
+    set_tests_properties(CTestTestUpload PROPERTIES
+      PASS_REGULAR_EXPRESSION "Upload\\.xml")
+  endif()
 
   configure_file(
     "${CMake_SOURCE_DIR}/Tests/CTestCoverageCollectGCOV/test.cmake.in"
@@ -3379,7 +3297,6 @@
       ${build_generator_args}
       --build-project testf
       --build-two-config
-      --build-options ${build_options}
       --test-command testf)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Fortran")
 
@@ -3390,7 +3307,7 @@
         "${CMake_BINARY_DIR}/Tests/FortranModules"
         ${build_generator_args}
         --build-project FortranModules
-        --build-options ${build_options}
+        --build-options
           -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
           -DCMake_TEST_Fortran_SUBMODULES:BOOL=${CMake_TEST_Fortran_SUBMODULES}
           ${CMake_TEST_FortranModules_BUILD_OPTIONS}
@@ -3413,7 +3330,6 @@
         ${build_generator_args}
         --build-project FortranC
         --build-two-config
-        --build-options ${build_options}
         --test-command CMakeFiles/FortranCInterface/FortranCInterface)
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/FortranC")
     endif()
@@ -3433,86 +3349,45 @@
     if(JNI_H AND EXISTS "${JNI_H}") # in case jni.h is a broken symlink
       file(READ "${JNI_H}" JNI_FILE)
       if("${JNI_FILE}" MATCHES "JDK1_2")
-        add_test(Java.Jar ${CMAKE_CTEST_COMMAND}
+        add_test(NAME Java.Jar COMMAND ${CMAKE_CTEST_COMMAND}
           --build-and-test
           "${CMake_SOURCE_DIR}/Tests/Java"
           "${CMake_BINARY_DIR}/Tests/JavaJar"
           ${build_generator_args}
           --build-project hello
-          --build-target hello
-          --build-two-config
           --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJar/"
-          --build-options ${build_options}
-          --test-command ${JAVA_RUNTIME} -classpath hello.jar HelloWorld)
+          --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
         list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJar")
-        add_test(Java.JarSourceList ${CMAKE_CTEST_COMMAND}
-          --build-and-test
-          "${CMake_SOURCE_DIR}/Tests/Java"
-          "${CMake_BINARY_DIR}/Tests/JavaJarSourceList"
-          ${build_generator_args}
-          --build-project hello
-          --build-target hello2
-          --build-two-config
-          --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJarSourceList/"
-          --build-options ${build_options}
-          --test-command ${JAVA_RUNTIME} -classpath hello2.jar HelloWorld)
-        list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceList")
-        add_test(Java.JarSourceListAndOutput ${CMAKE_CTEST_COMMAND}
-          --build-and-test
-          "${CMake_SOURCE_DIR}/Tests/Java"
-          "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput"
-          ${build_generator_args}
-          --build-project hello
-          --build-target hello3
-          --build-two-config
-          --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput/hello3"
-          --build-options ${build_options}
-          --test-command ${JAVA_RUNTIME} -classpath hello3.jar HelloWorld)
-        list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput")
 
         # For next tests, java tool must have same architecture as toolchain
         math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8")
         execute_process(
-          COMMAND "${Java_JAVA_EXECUTABLE}" -d${_object_mode} -version
-          OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE _result
+          COMMAND "${Java_JAVA_EXECUTABLE}" -version
+          OUTPUT_VARIABLE _version ERROR_VARIABLE _version RESULT_VARIABLE _result
           )
-        if(_result EQUAL 0)
+        if(_result EQUAL 0 AND _version MATCHES "${_object_mode}-Bit")
           ## next test is valid only if Java version is less than 1.10
           if ("${Java_VERSION}" VERSION_LESS 1.10)
-            if(_isMultiConfig)
-              set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah/$<CONFIGURATION>)
-            else()
-              set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah)
-            endif()
             add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND}
               --build-and-test
               "${CMake_SOURCE_DIR}/Tests/JavaJavah"
               "${CMake_BINARY_DIR}/Tests/JavaJavah"
               ${build_generator_args}
               --build-project helloJavah
-              --build-two-config
               --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/"
-              --build-options ${build_options}
-              --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVAH_LIBRARY_PATH} -classpath hello3.jar HelloWorld2)
+              --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
             list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah")
           endif()
           ## next test is valid only if Java is, at least, version 1.8
           if (NOT "${Java_VERSION}" VERSION_LESS 1.8)
-            if(_isMultiConfig)
-              set (JAVANATIVEHEADERS_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/$<CONFIGURATION>)
-            else()
-              set (JAVANATIVEHEADERS_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaNativeHeaders)
-            endif()
             add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND}
               --build-and-test
               "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders"
               "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders"
               ${build_generator_args}
               --build-project helloJavaNativeHeaders
-              --build-two-config
               --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/"
-              --build-options ${build_options}
-              --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVANATIVEHEADERS_LIBRARY_PATH} -classpath hello4.jar HelloWorld3)
+              --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
             list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders")
           endif()
         endif()
@@ -3533,9 +3408,9 @@
         "${CMake_BINARY_DIR}/Tests/SimpleCOnly_sdcc"
         ${build_generator_args}
         --build-project SimpleC
-        --build-options ${build_options}
-        "-DCMAKE_SYSTEM_NAME=Generic"
-        "-DCMAKE_C_COMPILER=${SDCC_EXECUTABLE}")
+        --build-options
+          "-DCMAKE_SYSTEM_NAME=Generic"
+          "-DCMAKE_C_COMPILER=${SDCC_EXECUTABLE}")
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/SimpleCOnly_sdcc")
     endif()
 
@@ -3551,11 +3426,11 @@
         "${CMake_BINARY_DIR}/Tests/Simple_Mingw_Linux2Win"
         ${build_generator_args}
         --build-project Simple
-        --build-options ${build_options}
-        "-DCMAKE_SYSTEM_NAME=Windows"
-        "-DCMAKE_C_COMPILER=${MINGW_CC_LINUX2WIN_EXECUTABLE}"
-        "-DCMAKE_CXX_COMPILER=${MINGW_CXX_LINUX2WIN_EXECUTABLE}"
-        "-DCMAKE_RC_COMPILER=${MINGW_RC_LINUX2WIN_EXECUTABLE}"
+        --build-options
+          "-DCMAKE_SYSTEM_NAME=Windows"
+          "-DCMAKE_C_COMPILER=${MINGW_CC_LINUX2WIN_EXECUTABLE}"
+          "-DCMAKE_CXX_COMPILER=${MINGW_CXX_LINUX2WIN_EXECUTABLE}"
+          "-DCMAKE_RC_COMPILER=${MINGW_RC_LINUX2WIN_EXECUTABLE}"
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Simple_Mingw_Linux2Win")
     endif()
@@ -3641,7 +3516,6 @@
     --build-two-config
     ${build_generator_args}
     --build-project IncludeDirectories
-    --build-options ${build_options}
     --test-command IncludeDirectories)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/IncludeDirectories")
 
@@ -3655,8 +3529,7 @@
       "${CMake_BINARY_DIR}/Tests/IncludeDirectoriesCPATH"
       --build-two-config
       ${build_generator_args}
-      --build-project IncludeDirectoriesCPATH
-      --build-options ${build_options})
+      --build-project IncludeDirectoriesCPATH)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/IncludeDirectoriesCPATH")
     set_tests_properties(IncludeDirectoriesCPATH
       PROPERTIES
@@ -3670,7 +3543,6 @@
     --build-two-config
     ${build_generator_args}
     --build-project InterfaceLinkLibraries
-    --build-options ${build_options}
     --test-command InterfaceLinkLibraries)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/InterfaceLinkLibraries")
 
diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt
index 1aeab8b..03babd2 100644
--- a/Tests/CMakeOnly/CMakeLists.txt
+++ b/Tests/CMakeOnly/CMakeLists.txt
@@ -30,6 +30,17 @@
 
 add_CMakeOnly_test(CompilerIdC)
 add_CMakeOnly_test(CompilerIdCXX)
+
+if(CMAKE_OBJC_COMPILER)
+  add_CMakeOnly_test(CompilerIdOBJC)
+  add_CMakeOnly_test(CheckOBJCCompilerFlag)
+endif()
+
+if(CMAKE_OBJCXX_COMPILER)
+  add_CMakeOnly_test(CompilerIdOBJCXX)
+  add_CMakeOnly_test(CheckOBJCXXCompilerFlag)
+endif()
+
 if(CMAKE_Fortran_COMPILER)
   add_CMakeOnly_test(CompilerIdFortran)
 endif()
@@ -84,5 +95,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/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
index f058c19..2028a13 100644
--- a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
@@ -49,6 +49,15 @@
   message(STATUS "errno found in <cerrno>")
 endif ()
 
+check_cxx_symbol_exists("std::fopen" "cstdio" CSE_RESULT_FOPEN)
+if (NOT CSE_RESULT_FOPEN)
+  if(NOT ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10))
+    message(SEND_ERROR "CheckCXXSymbolExists did not find std::fopen in <cstdio>")
+  endif()
+else()
+  message(STATUS "std::fopen found in <cstdio>")
+endif()
+
 if (CMAKE_COMPILER_IS_GNUCXX)
   string(APPEND CMAKE_CXX_FLAGS " -O3")
   unset(CSE_RESULT_O3 CACHE)
@@ -60,3 +69,8 @@
     message(SEND_ERROR "CheckCXXSymbolExists reported a nonexistent symbol as existing with optimization -O3")
   endif ()
 endif ()
+
+check_cxx_symbol_exists("std::non_existent_function_for_symbol_test<int*>" "algorithm" CSE_RESULT_NON_SYMBOL)
+if (CSE_RESULT_NON_SYMBOL)
+  message(SEND_ERROR "CheckCXXSymbolExists reported a nonexistent symbol.")
+endif()
diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
index ca4becb..90aa921 100644
--- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
@@ -5,10 +5,20 @@
 set(langs )
 set(expect_C 1)
 set(expect_CXX 1)
+
+if(APPLE)
+  set(expect_OBJC 1)
+  set(expect_OBJCXX 1)
+endif()
 unset(expect_Fortran)
 set(expect_NoSuchLanguage 0)
 
-foreach(lang C CXX Fortran CUDA NoSuchLanguage)
+set(LANGUAGES C CXX Fortran CUDA NoSuchLanguage)
+if(APPLE)
+  list(APPEND LANGUAGES OBJC OBJCXX)
+endif()
+
+foreach(lang ${LANGUAGES})
   check_language(${lang})
   if(NOT DEFINED CMAKE_${lang}_COMPILER)
     message(FATAL_ERROR "check_language(${lang}) did not set result")
diff --git a/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt
new file mode 100644
index 0000000..a9a96ee
--- /dev/null
+++ b/Tests/CMakeOnly/CheckOBJCCompilerFlag/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(CheckOBJCCompilerFlag)
+
+include(CheckOBJCCompilerFlag)
+
+if(CMAKE_COMPILER_IS_GNUOBJC)
+  set(COMPILER_FLAG -fobjc-direct-dispatch)
+else()
+  set(COMPILER_FLAG -fobjc-gc)
+endif()
+
+CHECK_OBJC_COMPILER_FLAGS(${COMPILER_FLAG} HAS_COMPILER_FLAG)
+
+if(NOT HAS_COMPILER_FLAG)
+  message(SEND_ERROR "Test fail: HAS_COMPILER_FLAG: ${COMPILER_FLAG}")
+endif
diff --git a/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt
new file mode 100644
index 0000000..f83b738
--- /dev/null
+++ b/Tests/CMakeOnly/CheckOBJCXXCompilerFlag/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(CheckOBJCXXCompilerFlag)
+
+include(CheckOBJCXXCompilerFlag)
+
+if(CMAKE_COMPILER_IS_GNUOBJCXX)
+  set(COMPILER_FLAG -fobjc-direct-dispatch)
+else()
+  set(COMPILER_FLAG -fobjc-gc)
+endif()
+
+CHECK_OBJCXX_COMPILER_FLAGS(${COMPILER_FLAG} HAS_COMPILER_FLAG)
+
+if(NOT HAS_COMPILER_FLAG)
+  message(SEND_ERROR "Test fail: HAS_COMPILER_FLAG: ${COMPILER_FLAG}")
+endif()
diff --git a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt
new file mode 100644
index 0000000..8f13787
--- /dev/null
+++ b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(CompilerIdOBJC OBJC)
+
+foreach(v
+    CMAKE_OBJC_COMPILER
+    CMAKE_OBJC_COMPILER_ID
+    CMAKE_OBJC_COMPILER_VERSION
+    )
+  if(${v})
+    message(STATUS "${v}=[${${v}}]")
+  else()
+    message(SEND_ERROR "${v} not set!")
+  endif()
+endforeach()
diff --git a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt
new file mode 100644
index 0000000..8f41db0
--- /dev/null
+++ b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(CompilerIdOBJCXX OBJCXX)
+
+foreach(v
+    CMAKE_OBJCXX_COMPILER
+    CMAKE_OBJCXX_COMPILER_ID
+    CMAKE_OBJCXX_COMPILER_VERSION
+    )
+  if(${v})
+    message(STATUS "${v}=[${${v}}]")
+  else()
+    message(SEND_ERROR "${v} not set!")
+  endif()
+endforeach()
diff --git a/Tests/CMakeServerLib/testServerBuffering.cpp b/Tests/CMakeServerLib/testServerBuffering.cpp
index 7330ead..6f22940 100644
--- a/Tests/CMakeServerLib/testServerBuffering.cpp
+++ b/Tests/CMakeServerLib/testServerBuffering.cpp
@@ -1,9 +1,11 @@
-#include "cmConnection.h"
-#include "cmServerConnection.h"
 #include <iostream>
+#include <memory>
 #include <string>
 #include <vector>
 
+#include "cmConnection.h"
+#include "cmServerConnection.h"
+
 void print_error(const std::vector<std::string>& input,
                  const std::vector<std::string>& output)
 {
diff --git a/Tests/CMakeTests/ELFTest.cmake.in b/Tests/CMakeTests/ELFTest.cmake.in
index 4635778..85c2360 100644
--- a/Tests/CMakeTests/ELFTest.cmake.in
+++ b/Tests/CMakeTests/ELFTest.cmake.in
@@ -25,11 +25,36 @@
   # Change the RPATH.
   file(RPATH_CHANGE FILE "${f}"
     OLD_RPATH "/sample/rpath"
-    NEW_RPATH "/rpath/sample")
+    NEW_RPATH "/path1:/path2")
+  set(rpath)
+  file(STRINGS "${f}" rpath REGEX "/path1:/path2" LIMIT_COUNT 1)
+  if(NOT rpath)
+    message(FATAL_ERROR "RPATH not changed in ${f}")
+  endif()
+
+  # Change the RPATH without compiler defined rpath removed
+  file(RPATH_CHANGE FILE "${f}"
+    OLD_RPATH "/path2"
+    NEW_RPATH "/path3")
+  set(rpath)
+  file(STRINGS "${f}" rpath REGEX "/path1:/path3" LIMIT_COUNT 1)
+  if(NOT rpath)
+    message(FATAL_ERROR "RPATH not updated in ${f}")
+  endif()
+
+  # Change the RPATH with compiler defined rpath removed
+  file(RPATH_CHANGE FILE "${f}"
+    OLD_RPATH "/path3"
+    NEW_RPATH "/rpath/sample"
+    INSTALL_REMOVE_ENVIRONMENT_RPATH)
   set(rpath)
   file(STRINGS "${f}" rpath REGEX "/rpath/sample" LIMIT_COUNT 1)
   if(NOT rpath)
-    message(FATAL_ERROR "RPATH not changed in ${f}")
+    message(FATAL_ERROR "RPATH not updated in ${f}")
+  endif()
+  file(STRINGS "${f}" rpath REGEX "/path1" LIMIT_COUNT 1)
+  if(rpath)
+    message(FATAL_ERROR "RPATH not removed in ${f}")
   endif()
 
   # Remove the RPATH.
diff --git a/Tests/COnly/CMakeLists.txt b/Tests/COnly/CMakeLists.txt
index 3037f13..20615fe 100644
--- a/Tests/COnly/CMakeLists.txt
+++ b/Tests/COnly/CMakeLists.txt
@@ -13,11 +13,5 @@
 endif()
 string(ASCII 35 32 67 77 97 107 101 ASCII_STRING)
 message(STATUS "String: ${ASCII_STRING}")
-get_source_file_property(LANG conly.c LANGUAGE)
-if("${LANG}" STREQUAL "C")
-  message("Language is C")
-else()
-  message(FATAL_ERROR "Bad language for file conly.c")
-endif()
 
 add_library(testCModule MODULE testCModule.c)
diff --git a/Tests/COnly/conly.c b/Tests/COnly/conly.c
index 7bd8e8e..2ae8a1a 100644
--- a/Tests/COnly/conly.c
+++ b/Tests/COnly/conly.c
@@ -1,10 +1,9 @@
-#include "foo.h"
+#include <stdio.h>
 
+#include "foo.h"
 #include "libc1.h"
 #include "libc2.h"
 
-#include <stdio.h>
-
 int main()
 {
   int class = 0;
diff --git a/Tests/CPackComponents/mylib.cpp b/Tests/CPackComponents/mylib.cpp
index 8ddac19..8d63071 100644
--- a/Tests/CPackComponents/mylib.cpp
+++ b/Tests/CPackComponents/mylib.cpp
@@ -1,4 +1,5 @@
 #include "mylib.h"
+
 #include "stdio.h"
 
 void mylib_function()
diff --git a/Tests/CPackComponentsDEB/CMakeLists.txt b/Tests/CPackComponentsDEB/CMakeLists.txt
index 9d4b5e9..bc5b6a9 100644
--- a/Tests/CPackComponentsDEB/CMakeLists.txt
+++ b/Tests/CPackComponentsDEB/CMakeLists.txt
@@ -4,8 +4,8 @@
 # application (mylibapp). We create a binary installer (a CPack Generator)
 # which supports CPack components.
 
-cmake_minimum_required(VERSION 2.8.3.20101130 FATAL_ERROR)
-project(CPackComponentsDEB)
+cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
+project(CPackComponentsDEB VERSION 1.0.3)
 
 # Use GNUInstallDirs in order to enforce lib64 if needed
 include(GNUInstallDirs)
@@ -44,10 +44,6 @@
 set(CPACK_PACKAGE_CONTACT "None")
 set(CPACK_PACKAGE_VENDOR "CMake.org")
 set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyLib - CPack Component Installation Example")
-set(CPACK_PACKAGE_VERSION "1.0.2")
-set(CPACK_PACKAGE_VERSION_MAJOR "1")
-set(CPACK_PACKAGE_VERSION_MINOR "0")
-set(CPACK_PACKAGE_VERSION_PATCH "2")
 set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example")
 set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/license.txt)
 
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in
index 74d816c..67b108b 100644
--- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in
+++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description1.cmake.in
@@ -15,8 +15,9 @@
 #set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
 
 # overriding previous descriptions
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY           "main description")
-set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION    "applications_description")
-set(CPACK_COMPONENT_HEADERS_DESCRIPTION         "headers_description")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description") # This become a summary line (the first one) of all descriptions
+set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "applications_description")
+set(CPACK_COMPONENT_HEADERS_DESCRIPTION "headers_description")
 # libraries does not have any description and should inherit from CPACK_PACKAGE_DESCRIPTION_SUMMARY
+# plus content of the `CPACK_PACKAGE_DESCRIPTION_FILE`.
 unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION)
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in
index cda79bc..d877325 100644
--- a/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in
+++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-components-description2.cmake.in
@@ -15,12 +15,12 @@
 #set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
 
 # overriding previous descriptions
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY           "main description 2")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "main description 2")
 
-# Components do not have any description
+# Components do not have any description.
+# So, content of `CPACK_PACKAGE_DESCRIPTION_FILE` gonna used
+# after summary line.
 unset(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION)
 unset(CPACK_COMPONENT_HEADERS_DESCRIPTION)
-unset(CPACK_COMPONENT_LIBRARIES_DESCRIPTION)
 
-
-set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION       "library description")
+set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "library description")
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake
index 73fd0ab..beccc46 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake
@@ -6,7 +6,7 @@
 
 
 # expected results
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 
@@ -36,7 +36,6 @@
 # dpkg-deb checks for the dependencies of the packages
 find_program(DPKGDEB_EXECUTABLE dpkg-deb)
 if(DPKGDEB_EXECUTABLE)
-  set(dpkgdeb_output_errors_all "")
   foreach(_f IN LISTS actual_output)
 
     # extracts the metadata from the package
@@ -54,32 +53,23 @@
 
     message(STATUS "package='${dpkg_package_name}', dependencies='${dpkg_depends}'")
 
-    if("${dpkg_package_name}" STREQUAL "mylib-applications")
-      if(NOT "${dpkg_depends}" STREQUAL "depend-application")
-        set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-application'\n")
+    if(dpkg_package_name STREQUAL "mylib-applications")
+      if(NOT dpkg_depends STREQUAL "depend-application")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-application'\n")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-headers")
-      if(NOT "${dpkg_depends}" STREQUAL "mylib-libraries (= 1.0.2), depend-headers")
-        set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'mylib-libraries (= 1.0.2), depend-headers'\n")
+    elseif(dpkg_package_name STREQUAL "mylib-headers")
+      if(NOT dpkg_depends STREQUAL "mylib-libraries (= 1.0.3), depend-headers")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'mylib-libraries (= 1.0.3), depend-headers'\n")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-libraries")
-      if(NOT "${dpkg_depends}" STREQUAL "depend-default")
-        set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-default'\n")
+    elseif(dpkg_package_name STREQUAL "mylib-libraries")
+      if(NOT dpkg_depends STREQUAL "depend-default")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-default'\n")
       endif()
     else()
-      set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                    "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n")
+      message(SEND_ERROR "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n")
     endif()
 
   endforeach()
-
-
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
-    message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
-  endif()
 else()
   message("dpkg-deb executable not found - skipping dpkg-deb test")
 endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake
index 81dbbc5..88f3248 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake
@@ -6,7 +6,7 @@
 
 
 # expected results
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 set(config_verbose -V)
@@ -36,7 +36,6 @@
 # dpkg-deb checks for the summary of the packages
 find_program(DPKGDEB_EXECUTABLE dpkg-deb)
 if(DPKGDEB_EXECUTABLE)
-  set(dpkgdeb_output_errors_all "")
   foreach(_f IN LISTS actual_output)
 
     # extracts the metadata from the package
@@ -54,13 +53,11 @@
 
     message(STATUS "package='${dpkg_package_name}', dependencies='${dpkg_depends}'")
 
-    if("${dpkg_package_name}" STREQUAL "mylib-applications")
+    if(dpkg_package_name STREQUAL "mylib-applications")
       find_program(DPKG_SHLIBDEP_EXECUTABLE dpkg-shlibdeps)
       if(DPKG_SHLIBDEP_EXECUTABLE)
-        string(FIND "${dpkg_depends}" "lib" index_libwhatever)
-        if(NOT index_libwhatever GREATER "-1")
-          set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                        "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' does not contain any 'lib'\n")
+        if(NOT dpkg_depends MATCHES "lib")
+          message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' does not contain any 'lib'\n")
         endif()
       else()
         message("dpkg-shlibdeps executable not found - skipping dpkg-shlibdeps test")
@@ -69,29 +66,20 @@
       # should not contain the default
       string(FIND "${dpkg_depends}" "depend-default" index_default)
       if(index_default GREATER "0")
-        set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' does contains 'depend-default'\n")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' does contains 'depend-default'\n")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-headers")
-      if(NOT "${dpkg_depends}" STREQUAL "mylib-libraries (= 1.0.2), depend-headers")
-        set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'mylib-libraries (= 1.0.2), depend-headers'\n")
+    elseif(dpkg_package_name STREQUAL "mylib-headers")
+      if(NOT dpkg_depends STREQUAL "mylib-libraries (= 1.0.3), depend-headers")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'mylib-libraries (= 1.0.3), depend-headers'\n")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-libraries")
-      if(NOT "${dpkg_depends}" STREQUAL "depend-default")
-        set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                      "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-default'\n")
+    elseif(dpkg_package_name STREQUAL "mylib-libraries")
+      if(NOT dpkg_depends STREQUAL "depend-default")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect dependencies for package ${dpkg_package_name}: '${dpkg_depends}' != 'depend-default'\n")
       endif()
     else()
-      set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                    "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n")
+      message(SEND_ERROR "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n")
     endif()
-
   endforeach()
-
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
-    message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
-  endif()
 else()
   message("dpkg-deb executable not found - skipping dpkg-deb test")
 endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake
index ad52f56..86a74b2 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake
@@ -6,7 +6,7 @@
 
 
 # expected results
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 
@@ -48,26 +48,26 @@
                                       DPKGDEB_OUTPUT "${dpkg_output}"
                                       METAENTRY "Package:")
 
-    dpkgdeb_return_specific_metaentry(dpkg_description
-                                      DPKGDEB_OUTPUT "${dpkg_output}"
-                                      METAENTRY "Description:")
+    get_package_description("${dpkg_output}" dpkg_description)
 
     message(STATUS "package='${dpkg_package_name}', description='${dpkg_description}'")
 
-    if("${dpkg_package_name}" STREQUAL "mylib-applications")
-      if(NOT "${dpkg_description}" STREQUAL "applications_description")
+    if(dpkg_package_name STREQUAL "mylib-applications")
+      set(expected_description "main description\n  applications_description")
+      if(NOT dpkg_description STREQUAL expected_description)
         set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != applications_description")
+                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-headers")
-      if(NOT "${dpkg_description}" STREQUAL "headers_description")
+    elseif(dpkg_package_name STREQUAL "mylib-headers")
+      set(expected_description "main description\n  headers_description")
+      if(NOT dpkg_description STREQUAL expected_description)
         set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != headers_description")
+                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-libraries")
-      if(NOT "${dpkg_description}" STREQUAL "main description")
+    elseif(dpkg_package_name STREQUAL "mylib-libraries")
+      if(NOT dpkg_description MATCHES "main description\n.*")
         set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != 'main description'")
+                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` =~ `main description.*`")
       endif()
     else()
       set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
@@ -77,7 +77,7 @@
   endforeach()
 
 
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
+  if(NOT dpkgdeb_output_errors_all STREQUAL "")
     message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
   endif()
 else()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake
index af27c51..d53c73d 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake
@@ -7,7 +7,7 @@
 
 
 # expected results
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 
@@ -48,26 +48,20 @@
                                       DPKGDEB_OUTPUT "${dpkg_output}"
                                       METAENTRY "Package:")
 
-    dpkgdeb_return_specific_metaentry(dpkg_description
-                                      DPKGDEB_OUTPUT "${dpkg_output}"
-                                      METAENTRY "Description:")
+    get_package_description("${dpkg_output}" dpkg_description)
 
     message(STATUS "package='${dpkg_package_name}', description='${dpkg_description}'")
 
-    if("${dpkg_package_name}" STREQUAL "mylib-applications")
-      if(NOT "${dpkg_description}" STREQUAL "main description 2")
+    if(dpkg_package_name STREQUAL "mylib-applications" OR dpkg_package_name STREQUAL "mylib-headers")
+      if(NOT dpkg_description MATCHES "main description 2\n.*")
         set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != applications_description")
+                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` =~ `main description 2`")
       endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-headers")
-      if(NOT "${dpkg_description}" STREQUAL "main description 2")
+    elseif(dpkg_package_name STREQUAL "mylib-libraries")
+      set(expected_description "main description 2\n  library description")
+      if(NOT dpkg_description STREQUAL expected_description)
         set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != headers_description")
-      endif()
-    elseif("${dpkg_package_name}" STREQUAL "mylib-libraries")
-      if(NOT "${dpkg_description}" STREQUAL "library description")
-        set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
-                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: ${dpkg_description} != 'main description'")
+                                      "dpkg-deb: ${_f}: Incorrect description for package ${dpkg_package_name}: `${dpkg_description}` != `${expected_description}`")
       endif()
     else()
       set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
@@ -77,7 +71,7 @@
   endforeach()
 
 
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
+  if(NOT dpkgdeb_output_errors_all STREQUAL "")
     message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
   endif()
 else()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake
index ec75d61..7cfbb16 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake
@@ -5,7 +5,7 @@
 include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake)
 
 # TODO: currently debian doesn't produce lower cased names
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 
@@ -44,7 +44,7 @@
     string(APPEND lintian_output_errors_all "${lintian_output_errors}")
   endforeach()
 
-  if(NOT "${lintian_output_errors_all}" STREQUAL "")
+  if(NOT lintian_output_errors_all STREQUAL "")
     message(FATAL_ERROR "Lintian checks failed:\n${lintian_output_errors_all}")
   endif()
 else()
@@ -64,13 +64,13 @@
                                       DPKGDEB_OUTPUT "${dpkg_output}"
                                       METAENTRY "Maintainer:")
 
-    if(NOT "${dpkgentry}" STREQUAL "None")
+    if(NOT dpkgentry STREQUAL "None")
       set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
           "dpkg-deb: ${_f}: Incorrect value for Maintainer: ${dpkgentry} != None\n")
     endif()
   endforeach()
 
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
+  if(NOT dpkgdeb_output_errors_all STREQUAL "")
     message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
   endif()
 else()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake
index e57488c..6eff3db 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake
@@ -9,7 +9,7 @@
 # requirements
 
 # debian now produces lower case names
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 
@@ -39,7 +39,7 @@
 # dpkg-deb checks for the summary of the packages
 find_program(DPKGDEB_EXECUTABLE dpkg-deb)
 if(DPKGDEB_EXECUTABLE)
-  set(dpkgdeb_output_errors_all)
+  set(dpkgdeb_output_errors_all "")
   foreach(_f IN LISTS actual_output)
 
     # extracts the metadata from the package
@@ -53,11 +53,11 @@
 
     message(STATUS "package='${dpkg_package_name}'")
 
-    if("${dpkg_package_name}" STREQUAL "mylib-applications")
+    if(dpkg_package_name STREQUAL "mylib-applications")
       # pass
-    elseif("${dpkg_package_name}" STREQUAL "mylib-headers")
+    elseif(dpkg_package_name STREQUAL "mylib-headers")
       # pass
-    elseif("${dpkg_package_name}" STREQUAL "mylib-libraries")
+    elseif(dpkg_package_name STREQUAL "mylib-libraries")
       # pass
     else()
       set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
@@ -67,7 +67,7 @@
   endforeach()
 
 
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
+  if(NOT dpkgdeb_output_errors_all STREQUAL "")
     message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
   endif()
 else()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake
index 5ee057a..3454dca 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake
@@ -6,7 +6,7 @@
 
 
 # expected results
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
 set(expected_count 3)
 
 set(config_verbose -V)
@@ -36,7 +36,6 @@
 # dpkg-deb checks for the summary of the packages
 find_program(DPKGDEB_EXECUTABLE dpkg-deb)
 if(DPKGDEB_EXECUTABLE)
-  set(dpkgdeb_output_errors_all "")
   foreach(_f IN LISTS actual_output)
 
     # extracts the metadata from the package
@@ -54,22 +53,16 @@
 
     message(STATUS "package='${_f}', source='${dpkg_package_source}'")
 
-    if(NOT ("${dpkg_package_name}" STREQUAL "mylib-applications"))
-      if(NOT ("${dpkg_package_source}" STREQUAL "test-source"))
-          set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                        "dpkg-deb: ${_f}: Incorrect source for package '${dpkg_package_name}': '${dpkg_package_source}' instead of 'test-source'\n")
+    if(NOT dpkg_package_name STREQUAL "mylib-applications")
+      if(NOT dpkg_package_source STREQUAL "test-source")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect source for package '${dpkg_package_name}': '${dpkg_package_source}' instead of 'test-source'\n")
       endif()
     else()
-      if(NOT ("${dpkg_package_source}" STREQUAL "test-other-source"))
-          set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                        "dpkg-deb: ${_f}: Incorrect source for package '${dpkg_package_name}': '${dpkg_package_source}' instead of 'test-other-source'\n")
+      if(NOT dpkg_package_source STREQUAL "test-other-source")
+        message(SEND_ERROR "dpkg-deb: ${_f}: Incorrect source for package '${dpkg_package_name}': '${dpkg_package_source}' instead of 'test-other-source'\n")
       endif()
     endif()
   endforeach()
-
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
-    message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
-  endif()
 else()
   message("dpkg-deb executable not found - skipping dpkg-deb test")
 endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake
index 8c0bc4b..764fe9d 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake
@@ -5,7 +5,7 @@
 include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake)
 
 # TODO: currently debian doesn't produce lower cased names
-set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib_1.0.2_*.deb")
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib_1.0.3_*.deb")
 set(expected_count 1)
 
 set(actual_output)
@@ -33,22 +33,16 @@
 # dpkg-deb checks
 find_program(DPKGDEB_EXECUTABLE dpkg-deb)
 if(DPKGDEB_EXECUTABLE)
-  set(dpkgdeb_output_errors_all "")
   foreach(_f IN LISTS actual_output)
     run_dpkgdeb(dpkg_output
                 FILENAME "${_f}"
                 )
 
     # message(FATAL_ERROR "output = '${dpkg_output}'")
-    if("${dpkg_output}" STREQUAL "")
-      set(dpkgdeb_output_errors_all "${dpkgdeb_output_errors_all}"
-                                    "dpkg-deb: ${_f}: empty content returned by dpkg-deb")
+    if(dpkg_output STREQUAL "")
+      message(SEND_ERROR "dpkg-deb: ${_f}: empty content returned by dpkg-deb")
     endif()
   endforeach()
-
-  if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
-    message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
-  endif()
 else()
   message("dpkg-deb executable not found - skipping dpkg-deb test")
 endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
index 2f9e2fc..b172da2 100644
--- a/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
@@ -119,13 +119,13 @@
   # regex to avoid
   foreach(_s IN LISTS lintian_check_specific_errors_deb_ERROR_REGEX_STRINGS)
 
-    if("${_s}" STREQUAL "")
+    if(_s STREQUAL "")
        continue()
     endif()
 
     string(REGEX MATCHALL "${_s}" "_TMP_CHECK_ERROR" "${lintian_output}")
 
-    if(NOT "${_TMP_CHECK_ERROR}" STREQUAL "")
+    if(NOT _TMP_CHECK_ERROR STREQUAL "")
       string(APPEND ERROR_ACC "\nlintian: ${_f}: output contains an undesirable regex:\n\t${_TMP_CHECK_ERROR}")
     endif()
   endforeach()
@@ -167,7 +167,7 @@
       ERROR_VARIABLE DPKGDEB_ERROR
       OUTPUT_STRIP_TRAILING_WHITESPACE )
 
-    if(NOT ("${DPKGDEB_RESULT}" EQUAL "0"))
+    if(NOT DPKGDEB_RESULT EQUAL "0")
       message(FATAL_ERROR "Error '${DPKGDEB_RESULT}' returned by dpkg-deb: '${DPKGDEB_ERROR}'")
     endif()
 
@@ -200,4 +200,29 @@
   endif()
 endfunction()
 
+function(get_package_description DPKG_OUTPUT RESULT_VAR)
+  string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1)
+  string(REPLACE ";" "${uuid}" DPKG_OUTPUT "${DPKG_OUTPUT}")
+  string(REPLACE "\n" ";" DPKG_OUTPUT "${DPKG_OUTPUT}")
+
+  unset(_actual_description)
+  set(_parse_description FALSE)
+  foreach(_line IN LISTS DPKG_OUTPUT)
+    if(_line MATCHES " Description:.*")
+      set(_parse_description TRUE)
+      string(REPLACE " Description: " "" _line "${_line}")
+      list(APPEND _actual_description "${_line}")
+    elseif(_parse_description)
+      if(_line MATCHES " [A-Z][A-Za-z\-]+: .*")
+        set(_parse_description FALSE)
+      else()
+        list(APPEND _actual_description "${_line}")
+      endif()
+    endif()
+  endforeach()
+  list(JOIN _actual_description "\n" _actual_description)
+
+  set(${RESULT_VAR} "${_actual_description}" PARENT_SCOPE)
+endfunction()
+
 cmake_policy(POP)
diff --git a/Tests/CPackComponentsDEB/mylib.cpp b/Tests/CPackComponentsDEB/mylib.cpp
index 8ddac19..8d63071 100644
--- a/Tests/CPackComponentsDEB/mylib.cpp
+++ b/Tests/CPackComponentsDEB/mylib.cpp
@@ -1,4 +1,5 @@
 #include "mylib.h"
+
 #include "stdio.h"
 
 void mylib_function()
diff --git a/Tests/CPackComponentsForAll/mylib.cpp b/Tests/CPackComponentsForAll/mylib.cpp
index 8ddac19..8d63071 100644
--- a/Tests/CPackComponentsForAll/mylib.cpp
+++ b/Tests/CPackComponentsForAll/mylib.cpp
@@ -1,4 +1,5 @@
 #include "mylib.h"
+
 #include "stdio.h"
 
 void mylib_function()
diff --git a/Tests/CPackWiXGenerator/mylib.cpp b/Tests/CPackWiXGenerator/mylib.cpp
index 8ddac19..8d63071 100644
--- a/Tests/CPackWiXGenerator/mylib.cpp
+++ b/Tests/CPackWiXGenerator/mylib.cpp
@@ -1,4 +1,5 @@
 #include "mylib.h"
+
 #include "stdio.h"
 
 void mylib_function()
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt
index 060fb49..ef9198d 100644
--- a/Tests/CompileFeatures/CMakeLists.txt
+++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -130,6 +130,12 @@
 endif()
 
 if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
+  if (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"
+      AND CMAKE_CXX_SIMULATE_VERSION VERSION_LESS 19.10)
+    list(REMOVE_ITEM CXX_non_features
+      cxx_relaxed_constexpr
+      )
+  endif()
   if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0)
     if (CMAKE_CXX_COMIPLER_VERSION VERSION_EQUAL 15.0)
       list(REMOVE_ITEM CXX_non_features
diff --git a/Tests/CompileFeatures/default_dialect.cpp b/Tests/CompileFeatures/default_dialect.cpp
index 3ee60a6..e6b3ff6 100644
--- a/Tests/CompileFeatures/default_dialect.cpp
+++ b/Tests/CompileFeatures/default_dialect.cpp
@@ -2,7 +2,17 @@
 template <long l>
 struct Outputter;
 
-#if defined(_MSC_VER) && defined(_MSVC_LANG)
+#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L
+#  if defined(__INTEL_CXX11_MODE__)
+#    if defined(__cpp_aggregate_nsdmi)
+#      define CXX_STD 201402L
+#    else
+#      define CXX_STD 201103L
+#    endif
+#  else
+#    define CXX_STD 199711L
+#  endif
+#elif defined(_MSC_VER) && defined(_MSVC_LANG)
 #  define CXX_STD _MSVC_LANG
 #else
 #  define CXX_STD __cplusplus
diff --git a/Tests/Complex/Executable/complex.cxx b/Tests/Complex/Executable/complex.cxx
index 9ddf005..49e97d5 100644
--- a/Tests/Complex/Executable/complex.cxx
+++ b/Tests/Complex/Executable/complex.cxx
@@ -1,20 +1,22 @@
-#include "Aout.h"
-#include "ExtraSources/file1.h"
 #include "cmTestConfigure.h"
 #include "cmTestConfigureEscape.h"
 #include "cmTestGeneratedHeader.h"
 #include "cmVersion.h"
+
+#include "Aout.h"
+#include "ExtraSources/file1.h"
 #include "file2.h"
 #include "sharedFile.h"
 extern "C" {
 #include "testConly.h"
 }
 #include <iostream>
-#include <string.h>
 #include <string>
 #include <vector>
 
 #include <stdio.h>
+#include <string.h>
+
 #include <sys/stat.h>
 #if !defined(S_ISDIR)
 #  define S_ISDIR(mode) ((mode)&_S_IFDIR)
diff --git a/Tests/Complex/Library/testConly.c b/Tests/Complex/Library/testConly.c
index a7d20b0..eb933a2 100644
--- a/Tests/Complex/Library/testConly.c
+++ b/Tests/Complex/Library/testConly.c
@@ -1,4 +1,5 @@
 #include "testConly.h"
+
 #include <stdio.h>
 
 int CsharedFunction()
diff --git a/Tests/ComplexOneConfig/Executable/complex.cxx b/Tests/ComplexOneConfig/Executable/complex.cxx
index 5b50899..54c18f4 100644
--- a/Tests/ComplexOneConfig/Executable/complex.cxx
+++ b/Tests/ComplexOneConfig/Executable/complex.cxx
@@ -1,20 +1,22 @@
-#include "Aout.h"
-#include "ExtraSources/file1.h"
 #include "cmTestConfigure.h"
 #include "cmTestConfigureEscape.h"
 #include "cmTestGeneratedHeader.h"
 #include "cmVersion.h"
+
+#include "Aout.h"
+#include "ExtraSources/file1.h"
 #include "file2.h"
 #include "sharedFile.h"
 extern "C" {
 #include "testConly.h"
 }
 #include <iostream>
-#include <string.h>
 #include <string>
 #include <vector>
 
 #include <stdio.h>
+#include <string.h>
+
 #include <sys/stat.h>
 #if !defined(S_ISDIR)
 #  define S_ISDIR(mode) ((mode)&_S_IFDIR)
diff --git a/Tests/ComplexOneConfig/Library/testConly.c b/Tests/ComplexOneConfig/Library/testConly.c
index a7d20b0..eb933a2 100644
--- a/Tests/ComplexOneConfig/Library/testConly.c
+++ b/Tests/ComplexOneConfig/Library/testConly.c
@@ -1,4 +1,5 @@
 #include "testConly.h"
+
 #include <stdio.h>
 
 int CsharedFunction()
diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt
index 748aad8..f5dd276 100644
--- a/Tests/ConfigSources/CMakeLists.txt
+++ b/Tests/ConfigSources/CMakeLists.txt
@@ -1,17 +1,21 @@
-
 cmake_minimum_required(VERSION 3.0)
-
-project(ConfigSources)
+project(ConfigSources CXX)
 
 add_library(iface INTERFACE)
-set_property(TARGET iface PROPERTY INTERFACE_SOURCES
+target_sources(iface INTERFACE
   "${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp"
   "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/iface_debug_src.cpp>"
-  "$<$<CONFIG:Release>:${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist.cpp>"
-)
+  "$<$<NOT:$<CONFIG:Debug>>:${CMAKE_CURRENT_SOURCE_DIR}/iface_other_src.cpp>"
+  "$<$<CONFIG:NotAConfig>:${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist.cpp>"
+  )
+target_compile_definitions(iface INTERFACE
+  "$<$<CONFIG:Debug>:CFG_DEBUG>"
+  "$<$<NOT:$<CONFIG:Debug>>:CFG_OTHER>"
+  )
 
 add_executable(ConfigSources
-  $<$<CONFIG:Debug>:main.cpp>
-  $<$<CONFIG:Release>:does_not_exist.cpp>
-)
+  $<$<CONFIG:Debug>:main_debug.cpp>
+  $<$<NOT:$<CONFIG:Debug>>:main_other.cpp>
+  $<$<CONFIG:NotAConfig>:does_not_exist.cpp>
+  )
 target_link_libraries(ConfigSources iface)
diff --git a/Tests/ConfigSources/iface.h b/Tests/ConfigSources/iface.h
new file mode 100644
index 0000000..810456c
--- /dev/null
+++ b/Tests/ConfigSources/iface.h
@@ -0,0 +1,10 @@
+
+int iface_src();
+
+#ifdef CFG_DEBUG
+int iface_debug();
+#endif
+
+#ifdef CFG_OTHER
+int iface_other();
+#endif
diff --git a/Tests/ConfigSources/iface_debug.h b/Tests/ConfigSources/iface_debug.h
deleted file mode 100644
index a23d737..0000000
--- a/Tests/ConfigSources/iface_debug.h
+++ /dev/null
@@ -1,4 +0,0 @@
-
-int iface_src();
-
-int iface_debug();
diff --git a/Tests/ConfigSources/iface_debug_src.cpp b/Tests/ConfigSources/iface_debug_src.cpp
index 63b22fc..010059f 100644
--- a/Tests/ConfigSources/iface_debug_src.cpp
+++ b/Tests/ConfigSources/iface_debug_src.cpp
@@ -1,5 +1,11 @@
+#ifndef CFG_DEBUG
+#  error "This source should only be compiled in a Debug configuration."
+#endif
+#ifdef CFG_OTHER
+#  error "This source should not be compiled in a non-Debug configuration."
+#endif
 
-#include "iface_debug.h"
+#include "iface.h"
 
 int iface_debug()
 {
diff --git a/Tests/ConfigSources/iface_other_src.cpp b/Tests/ConfigSources/iface_other_src.cpp
new file mode 100644
index 0000000..8ffdfbb
--- /dev/null
+++ b/Tests/ConfigSources/iface_other_src.cpp
@@ -0,0 +1,13 @@
+#ifndef CFG_OTHER
+#  error "This source should only be compiled in a non-Debug configuration."
+#endif
+#ifdef CFG_DEBUG
+#  error "This source should not be compiled in a Debug configuration."
+#endif
+
+#include "iface.h"
+
+int iface_other()
+{
+  return 0;
+}
diff --git a/Tests/ConfigSources/main.cpp b/Tests/ConfigSources/main.cpp
deleted file mode 100644
index 71af72f..0000000
--- a/Tests/ConfigSources/main.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#include "iface_debug.h"
-
-int main(int argc, char** argv)
-{
-  return iface_src() + iface_debug();
-}
diff --git a/Tests/ConfigSources/main_debug.cpp b/Tests/ConfigSources/main_debug.cpp
new file mode 100644
index 0000000..9b1e68a
--- /dev/null
+++ b/Tests/ConfigSources/main_debug.cpp
@@ -0,0 +1,13 @@
+#ifndef CFG_DEBUG
+#  error "This source should only be compiled in a Debug configuration."
+#endif
+#ifdef CFG_OTHER
+#  error "This source should not be compiled in a non-Debug configuration."
+#endif
+
+#include "iface.h"
+
+int main(int argc, char** argv)
+{
+  return iface_src() + iface_debug();
+}
diff --git a/Tests/ConfigSources/main_other.cpp b/Tests/ConfigSources/main_other.cpp
new file mode 100644
index 0000000..3184a19
--- /dev/null
+++ b/Tests/ConfigSources/main_other.cpp
@@ -0,0 +1,13 @@
+#ifndef CFG_OTHER
+#  error "This source should only be compiled in a non-Debug configuration."
+#endif
+#ifdef CFG_DEBUG
+#  error "This source should not be compiled in a Debug configuration."
+#endif
+
+#include "iface.h"
+
+int main(int argc, char** argv)
+{
+  return iface_src() + iface_other();
+}
diff --git a/Tests/Cuda/Complex/dynamic.cu b/Tests/Cuda/Complex/dynamic.cu
index c3d8aff..9da8853 100644
--- a/Tests/Cuda/Complex/dynamic.cu
+++ b/Tests/Cuda/Complex/dynamic.cu
@@ -1,8 +1,9 @@
 
-#include <cuda.h>
 #include <iostream>
 #include <string>
 
+#include <cuda.h>
+
 #ifdef _WIN32
 #  define EXPORT __declspec(dllexport)
 #else
diff --git a/Tests/Cuda/ObjectLibrary/Conflicts/static.cu b/Tests/Cuda/ObjectLibrary/Conflicts/static.cu
index 586e8c6..30aa121 100644
--- a/Tests/Cuda/ObjectLibrary/Conflicts/static.cu
+++ b/Tests/Cuda/ObjectLibrary/Conflicts/static.cu
@@ -1,7 +1,8 @@
 
+#include <iostream>
+
 #include <cuda.h>
 #include <cuda_runtime.h>
-#include <iostream>
 
 int __host__ cu2_sq_func(int x)
 {
diff --git a/Tests/Cuda/ObjectLibrary/static.cu b/Tests/Cuda/ObjectLibrary/static.cu
index 37bb839..530a114 100644
--- a/Tests/Cuda/ObjectLibrary/static.cu
+++ b/Tests/Cuda/ObjectLibrary/static.cu
@@ -1,7 +1,8 @@
 
+#include <iostream>
+
 #include <cuda.h>
 #include <cuda_runtime.h>
-#include <iostream>
 
 int __host__ cu1_sq_func(int x)
 {
diff --git a/Tests/Cuda/ProperDeviceLibraries/main.cu b/Tests/Cuda/ProperDeviceLibraries/main.cu
index 8ceb0cc..397804c 100644
--- a/Tests/Cuda/ProperDeviceLibraries/main.cu
+++ b/Tests/Cuda/ProperDeviceLibraries/main.cu
@@ -1,7 +1,8 @@
 
+#include <iostream>
+
 #include <cublas_v2.h>
 #include <cuda_runtime.h>
-#include <iostream>
 
 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
 
diff --git a/Tests/Cuda/WithC/cuda.cu b/Tests/Cuda/WithC/cuda.cu
index 06bd7b9..d1be2d4 100644
--- a/Tests/Cuda/WithC/cuda.cu
+++ b/Tests/Cuda/WithC/cuda.cu
@@ -1,7 +1,7 @@
-#include <cuda.h>
-
 #include <iostream>
 
+#include <cuda.h>
+
 extern "C" int use_cuda(void)
 {
   int nDevices = 0;
diff --git a/Tests/CudaOnly/DontResolveDeviceSymbols/CMakeLists.txt b/Tests/CudaOnly/DontResolveDeviceSymbols/CMakeLists.txt
index 6190089..6e3697f 100644
--- a/Tests/CudaOnly/DontResolveDeviceSymbols/CMakeLists.txt
+++ b/Tests/CudaOnly/DontResolveDeviceSymbols/CMakeLists.txt
@@ -27,12 +27,12 @@
 string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=[compute_30] -gencode arch=compute_50,code=\\\"compute_50\\\"")
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CUDA_STANDARD 11)
+set(CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS OFF)
 
 add_library(CUDANoDeviceResolve SHARED file1.cu)
 set_target_properties(CUDANoDeviceResolve
                       PROPERTIES
                       CUDA_SEPARABLE_COMPILATION ON
-                      CUDA_RESOLVE_DEVICE_SYMBOLS OFF
                       POSITION_INDEPENDENT_CODE ON)
 if(MSVC)
   target_link_options(CUDANoDeviceResolve PRIVATE "/FORCE:UNRESOLVED")
diff --git a/Tests/CudaOnly/GPUDebugFlag/main.cu b/Tests/CudaOnly/GPUDebugFlag/main.cu
index 1f3fc12..ced3789 100644
--- a/Tests/CudaOnly/GPUDebugFlag/main.cu
+++ b/Tests/CudaOnly/GPUDebugFlag/main.cu
@@ -1,6 +1,7 @@
+#include <iostream>
+
 #include <cuda.h>
 #include <cuda_runtime.h>
-#include <iostream>
 
 static __global__ void debug_kernel(bool* has_debug)
 {
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt
index 796e133..64845c5 100644
--- a/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt
@@ -16,21 +16,29 @@
 endif()
 
 #Goal for this example:
-# Build a static library that defines multiple methods and kernels that
-# use each other.
-# Resolve the device symbols into that static library
-# Verify that we can't use those device symbols from anything that links
+# 1. Build two static libraries that defines multiple methods and kernels
+# 2. Resolve the device symbols into the second static library, therefore
+# confirming that the first static library is on the device link line
+# 3. Verify that we can't use those device symbols from anything that links
 # to the static library
-string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=[compute_30] -gencode arch=compute_50,code=\\\"compute_50\\\"")
+string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=[sm_30] -gencode arch=compute_50,code=\\\"compute_50\\\"")
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CUDA_STANDARD 11)
 
-add_library(CUDAResolveDeviceLib STATIC file1.cu file2.cu)
+add_library(CUDAResolveDeviceDepsA STATIC file1.cu)
+add_library(CUDAResolveDeviceDepsB STATIC file2.cu)
+set_target_properties(CUDAResolveDeviceDepsA CUDAResolveDeviceDepsB
+                      PROPERTIES
+                      CUDA_SEPARABLE_COMPILATION ON
+                      POSITION_INDEPENDENT_CODE ON)
+
+add_library(CUDAResolveDeviceLib STATIC file2_launch.cu)
 set_target_properties(CUDAResolveDeviceLib
                       PROPERTIES
                       CUDA_SEPARABLE_COMPILATION ON
                       CUDA_RESOLVE_DEVICE_SYMBOLS ON
                       POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(CUDAResolveDeviceLib PRIVATE CUDAResolveDeviceDepsA CUDAResolveDeviceDepsB)
 
 if(dump_command)
 add_custom_command(TARGET CUDAResolveDeviceLib POST_BUILD
@@ -45,7 +53,8 @@
 add_executable(CudaOnlyResolveDeviceSymbols main.cu)
 set_target_properties(CudaOnlyResolveDeviceSymbols
                       PROPERTIES
-                      CUDA_SEPARABLE_COMPILATION ON)
+                      CUDA_SEPARABLE_COMPILATION OFF
+                      CUDA_RESOLVE_DEVICE_SYMBOLS OFF)
 
 target_link_libraries(CudaOnlyResolveDeviceSymbols PRIVATE CUDAResolveDeviceLib)
 
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file1.h b/Tests/CudaOnly/ResolveDeviceSymbols/file1.h
index ff1945c..b33bcae 100644
--- a/Tests/CudaOnly/ResolveDeviceSymbols/file1.h
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/file1.h
@@ -1,7 +1,10 @@
 
 #pragma once
+
 struct result_type
 {
   int input;
   int sum;
 };
+
+result_type __device__ file1_func(int x);
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu b/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu
index 278fd6c..0e5e7aa 100644
--- a/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu
@@ -1,25 +1,9 @@
 
 #include "file2.h"
 
-result_type __device__ file1_func(int x);
-
 result_type_dynamic __device__ file2_func(int x)
 {
   const result_type r = file1_func(x);
   const result_type_dynamic rd{ r.input, r.sum, true };
   return rd;
 }
-
-static __global__ void file2_kernel(result_type_dynamic& r, int x)
-{
-  // call static_func which is a method that is defined in the
-  // static library that is always out of date
-  r = file2_func(x);
-}
-
-int file2_launch_kernel(int x)
-{
-  result_type_dynamic r;
-  file2_kernel<<<1, 1>>>(r, x);
-  return r.sum;
-}
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file2.h b/Tests/CudaOnly/ResolveDeviceSymbols/file2.h
index d2dbaa4..c6e2875 100644
--- a/Tests/CudaOnly/ResolveDeviceSymbols/file2.h
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/file2.h
@@ -8,3 +8,5 @@
   int sum;
   bool from_static;
 };
+
+result_type_dynamic __device__ file2_func(int x);
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file2_launch.cu b/Tests/CudaOnly/ResolveDeviceSymbols/file2_launch.cu
new file mode 100644
index 0000000..4e8da13
--- /dev/null
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/file2_launch.cu
@@ -0,0 +1,18 @@
+
+#include "file2.h"
+
+static __global__ void file2_kernel(result_type_dynamic& r, int x)
+{
+  // call static_func which is a method that is defined in the
+  // static library that is always out of date
+  r = file2_func(x);
+}
+
+static __global__ void file2_kernel(result_type_dynamic& r, int x);
+
+int file2_launch_kernel(int x)
+{
+  result_type_dynamic r;
+  file2_kernel<<<1, 1>>>(r, x);
+  return r.sum;
+}
diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/main.cu b/Tests/CudaOnly/ResolveDeviceSymbols/main.cu
index d464f96..ea842cc 100644
--- a/Tests/CudaOnly/ResolveDeviceSymbols/main.cu
+++ b/Tests/CudaOnly/ResolveDeviceSymbols/main.cu
@@ -1,26 +1,10 @@
 
 #include <iostream>
 
-#include "file1.h"
 #include "file2.h"
 
 int file2_launch_kernel(int x);
 
-result_type_dynamic __device__ file2_func(int x);
-static __global__ void main_kernel(result_type_dynamic& r, int x)
-{
-  // call function that was not device linked to us, this will cause
-  // a runtime failure of "invalid device function"
-  r = file2_func(x);
-}
-
-int main_launch_kernel(int x)
-{
-  result_type_dynamic r;
-  main_kernel<<<1, 1>>>(r, x);
-  return r.sum;
-}
-
 int choose_cuda_device()
 {
   int nDevices = 0;
@@ -62,12 +46,10 @@
     return 0;
   }
 
-  main_launch_kernel(1);
+  file2_launch_kernel(1);
   cudaError_t err = cudaGetLastError();
-  if (err == cudaSuccess) {
-    // This kernel launch should fail as the file2_func was device linked
-    // into the static library and is not usable by the executable
-    std::cerr << "main_launch_kernel: kernel launch should have failed"
+  if (err != cudaSuccess) {
+    std::cerr << "file2_launch_kernel: kernel launch should have passed"
               << std::endl;
     return 1;
   }
diff --git a/Tests/CudaOnly/WithDefs/main.notcu b/Tests/CudaOnly/WithDefs/main.notcu
index 68a296b..a5f4ed6 100644
--- a/Tests/CudaOnly/WithDefs/main.notcu
+++ b/Tests/CudaOnly/WithDefs/main.notcu
@@ -1,7 +1,7 @@
-#include <cuda.h>
-#include <cuda_runtime.h>
 #include <iostream>
 
+#include <cuda.h>
+#include <cuda_runtime.h>
 #include <inc_cuda.h>
 #ifndef INC_CUDA
 #  error "INC_CUDA not defined!"
diff --git a/Tests/CustomCommandByproducts/CMakeLists.txt b/Tests/CustomCommandByproducts/CMakeLists.txt
index d0bf648..08c897c 100644
--- a/Tests/CustomCommandByproducts/CMakeLists.txt
+++ b/Tests/CustomCommandByproducts/CMakeLists.txt
@@ -14,20 +14,21 @@
 
 # Generate a byproduct in a rule that runs in a dependency of the consumer.
 add_custom_command(
-  OUTPUT timestamp2.txt
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../CustomCommandByproducts/timestamp2.txt
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct2.c.in byproduct2.c
-  BYPRODUCTS byproduct2.c
+  BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/../CustomCommandByproducts/byproduct2.c
   COMMAND ${CMAKE_COMMAND} -E touch timestamp2.txt
   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct2.c.in
   )
-add_custom_target(Producer2 DEPENDS timestamp2.txt)
+add_custom_target(Producer2 DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/timestamp2.txt)
 
 # Generate a byproduct in a custom target.
 add_custom_target(Producer3_4
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct3.c.in byproduct3.c
-  BYPRODUCTS byproduct3.c
+  BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/../CustomCommandByproducts/byproduct3.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct3.c.in
   )
 
 # Generate a byproduct in a custom target POST_BUILD command.
@@ -35,33 +36,37 @@
   TARGET Producer3_4 POST_BUILD
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct4.c.in byproduct4.c
-  BYPRODUCTS byproduct4.c
+  BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/../CustomCommandByproducts/byproduct4.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct4.c.in
   )
 
-add_executable(ProducerExe ProducerExe.c)
+add_executable(ProducerExe5_6_7 ProducerExe.c)
 
 # Generate a byproduct in an executable POST_BUILD command.
 add_custom_command(
-  TARGET ProducerExe POST_BUILD
+  TARGET ProducerExe5_6_7 POST_BUILD
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct5.c.in byproduct5.c
   BYPRODUCTS byproduct5.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct5.c.in
   )
 
 # Generate a byproduct in an executable PRE_LINK command.
 add_custom_command(
-  TARGET ProducerExe PRE_LINK
+  TARGET ProducerExe5_6_7 PRE_LINK
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct6.c.in byproduct6.c
   BYPRODUCTS byproduct6.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct6.c.in
   )
 
 # Generate a byproduct in an executable PRE_BUILD command.
 add_custom_command(
-  TARGET ProducerExe PRE_BUILD
+  TARGET ProducerExe5_6_7 PRE_BUILD
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct7.c.in byproduct7.c
   BYPRODUCTS byproduct7.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct7.c.in
   )
 
 # Generate a byproduct in a custom command that consumes other byproducts.
@@ -80,6 +85,25 @@
     ${CMAKE_CURRENT_SOURCE_DIR}/byproduct8.c.in
   )
 
+add_executable(ProducerExe9 ProducerExe.c)
+
+# Generate a byproduct in a custom target which depends on a byproduct of a
+# POST_BUILD command (test if dependency of custom target Producer9 to
+# ProducerExe9 is added).
+add_custom_command(
+  TARGET ProducerExe9 POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -E copy_if_different
+    ${CMAKE_CURRENT_SOURCE_DIR}/byproduct9.c.in byproduct9a.c
+  BYPRODUCTS byproduct9a.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct9.c.in
+  )
+add_custom_target(Producer9
+  COMMAND ${CMAKE_COMMAND} -E copy_if_different
+    byproduct9a.c byproduct9.c
+  BYPRODUCTS byproduct9.c
+  DEPENDS byproduct9a.c
+  )
+
 # Generate the library file of an imported target as a byproduct
 # of an external project.
 get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
@@ -136,10 +160,13 @@
   byproduct6.c
   byproduct7.c
   byproduct8.c timestamp8.txt
+  byproduct9.c
   )
+
+# Dependencies to byproducts of custom commands other than build events are not
+# yet traced (see issue #19005).
 add_dependencies(CustomCommandByproducts Producer2)
-add_dependencies(CustomCommandByproducts Producer3_4)
-add_dependencies(CustomCommandByproducts ProducerExe)
+
 target_link_libraries(CustomCommandByproducts ExternalLibrary)
 
 if(CMAKE_GENERATOR STREQUAL "Ninja")
diff --git a/Tests/CustomCommandByproducts/CustomCommandByproducts.c b/Tests/CustomCommandByproducts/CustomCommandByproducts.c
index 02ad7ea..0658d05 100644
--- a/Tests/CustomCommandByproducts/CustomCommandByproducts.c
+++ b/Tests/CustomCommandByproducts/CustomCommandByproducts.c
@@ -6,10 +6,11 @@
 extern int byproduct6(void);
 extern int byproduct7(void);
 extern int byproduct8(void);
+extern int byproduct9(void);
 extern int ExternalLibrary(void);
 int main(void)
 {
   return (byproduct1() + byproduct2() + byproduct3() + byproduct4() +
           byproduct5() + byproduct6() + byproduct7() + byproduct8() +
-          ExternalLibrary() + 0);
+          byproduct9() + ExternalLibrary() + 0);
 }
diff --git a/Tests/CustomCommandByproducts/byproduct9.c.in b/Tests/CustomCommandByproducts/byproduct9.c.in
new file mode 100644
index 0000000..11eed2c
--- /dev/null
+++ b/Tests/CustomCommandByproducts/byproduct9.c.in
@@ -0,0 +1 @@
+int byproduct9(void) { return 0; }
diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt
index c6b7dbc..5cf04e8 100644
--- a/Tests/ExportImport/Export/CMakeLists.txt
+++ b/Tests/ExportImport/Export/CMakeLists.txt
@@ -313,6 +313,8 @@
 )
 cmake_policy(POP)
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0041 NEW)
 add_library(testSharedLibDepends SHARED testSharedLibDepends.cpp)
 set_property(TARGET testSharedLibDepends APPEND PROPERTY
   INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}"
@@ -329,9 +331,10 @@
     DESTINATION include/testSharedLibDepends
 )
 set_property(TARGET testSharedLibDepends APPEND PROPERTY
-  INTERFACE_INCLUDE_DIRECTORIES "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/testSharedLibDepends>"
+  INTERFACE_INCLUDE_DIRECTORIES "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/$<1:include>/testSharedLibDepends>"
                                 "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}>"
 )
+cmake_policy(POP)
 
 # LINK_PRIVATE because the LINK_INTERFACE_LIBRARIES is specified above.
 target_link_libraries(testSharedLibDepends LINK_PRIVATE testSharedLibRequired)
diff --git a/Tests/ExportImport/Export/Interface/CMakeLists.txt b/Tests/ExportImport/Export/Interface/CMakeLists.txt
index 22a4ef6..43b7217 100644
--- a/Tests/ExportImport/Export/Interface/CMakeLists.txt
+++ b/Tests/ExportImport/Export/Interface/CMakeLists.txt
@@ -6,6 +6,12 @@
 )
 set_property(TARGET headeronly PROPERTY INTERFACE_COMPILE_DEFINITIONS "HEADERONLY_DEFINE")
 
+add_library(pch_iface INTERFACE)
+target_precompile_headers(pch_iface INTERFACE
+  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pch/pch.h>"
+  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/pch/pch.h>"
+  )
+
 include(GenerateExportHeader)
 add_library(sharedlib SHARED sharedlib.cpp)
 generate_export_header(sharedlib)
@@ -45,7 +51,7 @@
 set_property(TARGET cmakeonly PROPERTY EXPORT_PROPERTIES custom_property)
 
 install(TARGETS headeronly sharediface use_auto_type use_c_restrict source_target
-                cmakeonly
+                pch_iface cmakeonly
   EXPORT expInterface
 )
 install(TARGETS sharedlib
@@ -61,6 +67,10 @@
   DESTINATION include/headeronly
 )
 install(FILES
+  pch/pch.h
+  DESTINATION include/pch
+)
+install(FILES
   sharedlib/sharedlib.h
   "${CMAKE_CURRENT_BINARY_DIR}/sharedlib_export.h"
   DESTINATION include/sharedlib
diff --git a/Tests/ExportImport/Export/Interface/pch/pch.h b/Tests/ExportImport/Export/Interface/pch/pch.h
new file mode 100644
index 0000000..bc50727
--- /dev/null
+++ b/Tests/ExportImport/Export/Interface/pch/pch.h
@@ -0,0 +1 @@
+#define PCH_PCH_H_INCLUDED
diff --git a/Tests/ExportImport/Export/testSharedLibDepends.h b/Tests/ExportImport/Export/testSharedLibDepends.h
index e84fb54..73dafae 100644
--- a/Tests/ExportImport/Export/testSharedLibDepends.h
+++ b/Tests/ExportImport/Export/testSharedLibDepends.h
@@ -2,10 +2,9 @@
 #ifndef TESTSHAREDLIBDEPENDS_H
 #define TESTSHAREDLIBDEPENDS_H
 
-#include "testsharedlibdepends_export.h"
-
 #include "renamed.h"
 #include "testSharedLibRequired.h"
+#include "testsharedlibdepends_export.h"
 
 struct TESTSHAREDLIBDEPENDS_EXPORT TestSharedLibDepends
 {
diff --git a/Tests/ExportImport/Export/testSharedLibRequiredUser2.h b/Tests/ExportImport/Export/testSharedLibRequiredUser2.h
index a132940..e1c8a05 100644
--- a/Tests/ExportImport/Export/testSharedLibRequiredUser2.h
+++ b/Tests/ExportImport/Export/testSharedLibRequiredUser2.h
@@ -2,9 +2,8 @@
 #ifndef TESTSHAREDLIBREQUIREDUSER2_H
 #define TESTSHAREDLIBREQUIREDUSER2_H
 
-#include "testsharedlibrequireduser2_export.h"
-
 #include "testSharedLibRequired.h"
+#include "testsharedlibrequireduser2_export.h"
 
 struct TESTSHAREDLIBREQUIREDUSER2_EXPORT TestSharedLibRequiredUser2
 {
diff --git a/Tests/ExportImport/Import/A/deps_iface.c b/Tests/ExportImport/Import/A/deps_iface.c
index e2d973c..afb1af0 100644
--- a/Tests/ExportImport/Import/A/deps_iface.c
+++ b/Tests/ExportImport/Import/A/deps_iface.c
@@ -1,9 +1,4 @@
 
-#include "testLibIncludeRequired1.h"
-#include "testLibIncludeRequired2.h"
-#include "testLibIncludeRequired6.h"
-#include "testLibIncludeRequired7.h"
-
 #include "installIncludesTest.h"
 #include "installIncludesTest2.h"
 #include "installIncludesTest3.h"
@@ -12,6 +7,10 @@
 #include "installIncludesTest6.h"
 #include "installIncludesTest7.h"
 #include "installIncludesTest8.h"
+#include "testLibIncludeRequired1.h"
+#include "testLibIncludeRequired2.h"
+#include "testLibIncludeRequired6.h"
+#include "testLibIncludeRequired7.h"
 
 #ifndef testLibRequired_IFACE_DEFINE
 #  error Expected testLibRequired_IFACE_DEFINE
diff --git a/Tests/ExportImport/Import/Interface/CMakeLists.txt b/Tests/ExportImport/Import/Interface/CMakeLists.txt
index a07a5b3..ef666b1 100644
--- a/Tests/ExportImport/Import/Interface/CMakeLists.txt
+++ b/Tests/ExportImport/Import/Interface/CMakeLists.txt
@@ -98,6 +98,17 @@
 add_executable(interfacetest_exp interfacetest.cpp)
 target_link_libraries(interfacetest_exp exp::sharediface)
 
+if(NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]" OR CMAKE_GENERATOR STREQUAL "Xcode")
+  add_executable(pch_iface_test_bld pch_iface_test.cpp)
+  target_link_libraries(pch_iface_test_bld bld::pch_iface)
+  add_executable(pch_iface_test_exp pch_iface_test.cpp)
+  target_link_libraries(pch_iface_test_exp exp::pch_iface)
+  if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH)
+    target_compile_definitions(pch_iface_test_bld PRIVATE EXPECT_PCH)
+    target_compile_definitions(pch_iface_test_exp PRIVATE EXPECT_PCH)
+  endif()
+endif()
+
 do_try_compile(exp)
 
 foreach(ns exp bld)
diff --git a/Tests/ExportImport/Import/Interface/pch_iface_test.cpp b/Tests/ExportImport/Import/Interface/pch_iface_test.cpp
new file mode 100644
index 0000000..a460d0d
--- /dev/null
+++ b/Tests/ExportImport/Import/Interface/pch_iface_test.cpp
@@ -0,0 +1,16 @@
+#ifdef EXPECT_PCH
+// Verify that pch/pch.h was included via '-include ...' or equivalent.
+#  ifndef PCH_PCH_H_INCLUDED
+#    error "Expected PCH_PCH_H_INCLUDED."
+#  endif
+#elif defined(__PGIC__) || defined(__ibmxl__)
+// No PCH expected but these compilers define macros below.
+#elif defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER) ||  \
+  defined(_MSC_VER)
+#  error "Expected EXPECT_PCH for this compiler."
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt
index 5adcbd9..093391e 100644
--- a/Tests/ExternalProject/CMakeLists.txt
+++ b/Tests/ExternalProject/CMakeLists.txt
@@ -421,7 +421,7 @@
 
   set(local_git_repo "../../LocalRepositories/GIT-with-submodules")
 
-  set(proj TS1-GIT-no-GIT_SUBMODULES)
+  set(proj TS1-GIT-all-GIT_SUBMODULES)
   ExternalProject_Add(${proj}
     GIT_REPOSITORY "${local_git_repo}"
     CMAKE_GENERATOR "${CMAKE_GENERATOR}"
@@ -435,7 +435,8 @@
   )
   set_property(TARGET ${proj} PROPERTY FOLDER "GIT")
 
-  set(proj TS1-GIT-empty-GIT_SUBMODULES)
+  set(proj TS1-GIT-all-GIT_SUBMODULES-via-CMP0097-OLD)
+  cmake_policy(SET CMP0097 OLD)
   ExternalProject_Add(${proj}
     GIT_REPOSITORY "${local_git_repo}"
     GIT_SUBMODULES ""
@@ -450,6 +451,22 @@
   )
   set_property(TARGET ${proj} PROPERTY FOLDER "GIT")
 
+  set(proj TS1-GIT-no-GIT_SUBMODULES)
+  cmake_policy(SET CMP0097 NEW)
+  ExternalProject_Add(${proj}
+    GIT_REPOSITORY "${local_git_repo}"
+    GIT_SUBMODULES ""
+    CMAKE_GENERATOR "${CMAKE_GENERATOR}"
+    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+               -DWITH_m1:BOOL=OFF
+               -DWITH_m2:BOOL=OFF
+    BUILD_COMMAND ""
+    INSTALL_COMMAND ""
+    DEPENDS "SetupLocalGITRepository"
+            "SetupLocalGITRepositoryWithSubmodules"
+  )
+  set_property(TARGET ${proj} PROPERTY FOLDER "GIT")
+
   set(proj TS1-GIT-some-GIT_SUBMODULES)
   ExternalProject_Add(${proj}
     GIT_REPOSITORY "${local_git_repo}"
diff --git a/Tests/ExternalProjectLocal/Step5/MathFunctions/mysqrt.cxx b/Tests/ExternalProjectLocal/Step5/MathFunctions/mysqrt.cxx
index 458ed63..a44aba0 100644
--- a/Tests/ExternalProjectLocal/Step5/MathFunctions/mysqrt.cxx
+++ b/Tests/ExternalProjectLocal/Step5/MathFunctions/mysqrt.cxx
@@ -1,12 +1,13 @@
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
 #include <stdio.h>
 
-// include the generated table
-#include "Table.h"
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
 
+// include the generated table
 #include <math.h>
 
+#include "Table.h"
+
 // a hack square root calculation using simple operations
 double mysqrt(double x)
 {
diff --git a/Tests/ExternalProjectLocal/Step5/tutorial.cxx b/Tests/ExternalProjectLocal/Step5/tutorial.cxx
index 37f6ac4..8d077b2 100644
--- a/Tests/ExternalProjectLocal/Step5/tutorial.cxx
+++ b/Tests/ExternalProjectLocal/Step5/tutorial.cxx
@@ -1,9 +1,10 @@
 // A simple program that computes the square root of a number
-#include "TutorialConfig.h"
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "TutorialConfig.h"
+
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
 #endif
diff --git a/Tests/FindDoxygen/CMakeLists.txt b/Tests/FindDoxygen/CMakeLists.txt
index 7ce98d5..41e6255 100644
--- a/Tests/FindDoxygen/CMakeLists.txt
+++ b/Tests/FindDoxygen/CMakeLists.txt
@@ -28,6 +28,16 @@
   --test-command ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
 )
 
+add_test(NAME FindDoxygen.StampFile COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindDoxygen/StampFile"
+  "${CMake_BINARY_DIR}/Tests/FindDoxygen/StampFile"
+  --build-target allDocTargets
+  ${build_generator_args}
+  --build-options ${build_options}
+)
+
 if(CMake_TEST_FindDoxygen_Dot)
   add_test(NAME FindDoxygen.DotComponentTest COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
diff --git a/Tests/FindDoxygen/StampFile/CMakeLists.txt b/Tests/FindDoxygen/StampFile/CMakeLists.txt
new file mode 100644
index 0000000..2d06540
--- /dev/null
+++ b/Tests/FindDoxygen/StampFile/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.8)
+project(TestFindDoxygen VERSION 1.0 LANGUAGES NONE)
+
+find_package(Doxygen REQUIRED)
+
+doxygen_add_docs(docsWithoutFilesWithStamp USE_STAMP_FILE)
+if(NOT EXISTS "${PROJECT_BINARY_DIR}/Doxyfile.docsWithoutFilesWithStamp")
+    message(FATAL_ERROR "Missing generated file: Doxyfile.docsWithoutFilesWithStamp")
+endif()
+if(NOT TARGET docsWithoutFilesWithStamp)
+    message(FATAL_ERROR "Target docsWithoutFilesWithStamp not created")
+endif()
+
+doxygen_add_docs(docsWithFilesWithStamp main.cpp main2.cpp USE_STAMP_FILE)
+if(NOT EXISTS "${PROJECT_BINARY_DIR}/Doxyfile.docsWithFilesWithStamp")
+    message(FATAL_ERROR "Missing generated file: Doxyfile.docsWithFilesWithStamp")
+endif()
+if(NOT TARGET docsWithFilesWithStamp)
+    message(FATAL_ERROR "Target docsWithFilesWithStamp not created")
+endif()
+
+
+add_custom_target(allDocTargets)
+add_dependencies(allDocTargets docsWithoutFilesWithStamp docsWithFilesWithStamp)
diff --git a/Tests/FindDoxygen/StampFile/main.cpp b/Tests/FindDoxygen/StampFile/main.cpp
new file mode 100644
index 0000000..925f0af
--- /dev/null
+++ b/Tests/FindDoxygen/StampFile/main.cpp
@@ -0,0 +1,4 @@
+/**
+ * \file
+ * \brief One C++ file w/ sample Doxygen comment just to produce any docs...
+ */
diff --git a/Tests/FindDoxygen/StampFile/main2.cpp b/Tests/FindDoxygen/StampFile/main2.cpp
new file mode 100644
index 0000000..925f0af
--- /dev/null
+++ b/Tests/FindDoxygen/StampFile/main2.cpp
@@ -0,0 +1,4 @@
+/**
+ * \file
+ * \brief One C++ file w/ sample Doxygen comment just to produce any docs...
+ */
diff --git a/Tests/FindEnvModules/EnvModules.cmake b/Tests/FindEnvModules/EnvModules.cmake
index 0c81bf2..21b0042 100644
--- a/Tests/FindEnvModules/EnvModules.cmake
+++ b/Tests/FindEnvModules/EnvModules.cmake
@@ -18,18 +18,16 @@
 
   message("module list")
   env_module_list(loaded_mods)
+  set(mod0_found FALSE)
   foreach(mod IN LISTS loaded_mods)
     message("  ${mod}")
+    if(NOT mod0_found AND mod MATCHES "^${mod0}")
+      set(mod0_found ${mod})
+    endif()
   endforeach()
 
-  list(LENGTH loaded_mods num_loaded_mods)
-  message("Number of modules loaded: ${num_loaded_mods}")
-  if(NOT num_loaded_mods EQUAL 1)
-    message(FATAL_ERROR "Exactly 1 module should be loaded.  Found ${num_loaded_mods}")
+  if(NOT mod0_found)
+    message(FATAL_ERROR "Requested module ${mod0} not found in loaded modules")
   endif()
-
-  list(GET loaded_mods 0 mod0_actual)
-  if(NOT (mod0_actual MATCHES "^${mod0}"))
-    message(FATAL_ERROR "Loaded module does not match ${mod0}.  Actual: ${mod0_actual}")
-  endif()
+  message("module ${mod0} found loaded as ${mod0_found}")
 endif()
diff --git a/Tests/FindGIF/Test/main.c b/Tests/FindGIF/Test/main.c
index 4ed72ec..656a99c 100644
--- a/Tests/FindGIF/Test/main.c
+++ b/Tests/FindGIF/Test/main.c
@@ -1,9 +1,8 @@
 #include <assert.h>
+#include <gif_lib.h>
 #include <stdio.h>
 #include <string.h>
 
-#include <gif_lib.h>
-
 // GIFLIB before version 5 didn't know this macro
 #ifndef GIFLIB_MAJOR
 #  define GIFLIB_MAJOR 4
diff --git a/Tests/FindGSL/rng/main.cc b/Tests/FindGSL/rng/main.cc
index af13125..050caac 100644
--- a/Tests/FindGSL/rng/main.cc
+++ b/Tests/FindGSL/rng/main.cc
@@ -1,6 +1,7 @@
-#include "gsl/gsl_rng.h"
 #include <math.h>
 
+#include "gsl/gsl_rng.h"
+
 int main()
 {
   // return code
diff --git a/Tests/FindGTK2/cairomm/main.cpp b/Tests/FindGTK2/cairomm/main.cpp
index 4432601..48e5e39 100644
--- a/Tests/FindGTK2/cairomm/main.cpp
+++ b/Tests/FindGTK2/cairomm/main.cpp
@@ -8,13 +8,13 @@
 #  define _USE_MATH_DEFINES
 #endif
 
-#include <cairomm/context.h>
-#include <cairomm/surface.h>
-#include <cairommconfig.h>
+#include <cmath>
 #include <iostream>
 #include <string>
 
-#include <cmath>
+#include <cairomm/context.h>
+#include <cairomm/surface.h>
+#include <cairommconfig.h>
 
 int main()
 {
diff --git a/Tests/FindGTK2/gtkmm/helloworld.cpp b/Tests/FindGTK2/gtkmm/helloworld.cpp
index 5f4e0ae..45c9422 100644
--- a/Tests/FindGTK2/gtkmm/helloworld.cpp
+++ b/Tests/FindGTK2/gtkmm/helloworld.cpp
@@ -1,4 +1,5 @@
 #include "helloworld.h"
+
 #include <iostream>
 
 HelloWorld::HelloWorld()
diff --git a/Tests/FindGTK2/gtkmm/main.cpp b/Tests/FindGTK2/gtkmm/main.cpp
index 1637da2..29e23fd 100644
--- a/Tests/FindGTK2/gtkmm/main.cpp
+++ b/Tests/FindGTK2/gtkmm/main.cpp
@@ -1,6 +1,7 @@
-#include "helloworld.h"
 #include <gtkmm.h>
 
+#include "helloworld.h"
+
 int main(int argc, char* argv[])
 {
   Gtk::Main kit(argc, argv);
diff --git a/Tests/FindGTK2/sigc++/main.cpp b/Tests/FindGTK2/sigc++/main.cpp
index e56b910..4dae4f4 100644
--- a/Tests/FindGTK2/sigc++/main.cpp
+++ b/Tests/FindGTK2/sigc++/main.cpp
@@ -1,6 +1,7 @@
 // Taken from https://developer.gnome.org/libsigc++-tutorial/stable/ch02.html
 
 #include <iostream>
+
 #include <sigc++/sigc++.h>
 
 class AlienDetector
diff --git a/Tests/FindGnuTLS/CMakeLists.txt b/Tests/FindGnuTLS/CMakeLists.txt
new file mode 100644
index 0000000..059ac7b
--- /dev/null
+++ b/Tests/FindGnuTLS/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_test(NAME FindGnuTLS.Test COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindGnuTLS/Test"
+  "${CMake_BINARY_DIR}/Tests/FindGnuTLS/Test"
+  ${build_generator_args}
+  --build-project TestFindGnuTLS
+  --build-options ${build_options}
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
diff --git a/Tests/FindGnuTLS/Test/CMakeLists.txt b/Tests/FindGnuTLS/Test/CMakeLists.txt
new file mode 100644
index 0000000..c5a9819
--- /dev/null
+++ b/Tests/FindGnuTLS/Test/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.4)
+project(TestFindGnuTLS C)
+include(CTest)
+
+find_package(GnuTLS REQUIRED)
+
+add_definitions(-DCMAKE_EXPECTED_GNUTLS_VERSION="${GNUTLS_VERSION}")
+
+add_executable(test_tgt main.c)
+target_link_libraries(test_tgt GnuTLS::GnuTLS)
+add_test(NAME test_tgt COMMAND test_tgt)
+
+add_executable(test_var main.c)
+target_include_directories(test_var PRIVATE ${GNUTLS_INCLUDE_DIRS})
+target_link_libraries(test_var PRIVATE ${GNUTLS_LIBRARIES})
+target_compile_definitions(test_var PRIVATE ${GNUTLS_DEFINITIONS})
+add_test(NAME test_var COMMAND test_var)
diff --git a/Tests/FindGnuTLS/Test/main.c b/Tests/FindGnuTLS/Test/main.c
new file mode 100644
index 0000000..1105358
--- /dev/null
+++ b/Tests/FindGnuTLS/Test/main.c
@@ -0,0 +1,21 @@
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <stdio.h>
+#include <string.h>
+
+int main()
+{
+  // test the linker
+  gnutls_session_t session;
+  if (gnutls_init(&session, GNUTLS_CLIENT)) {
+    gnutls_deinit(session);
+  }
+
+  // check the version
+  char gnutls_version_string[16];
+  snprintf(gnutls_version_string, 16, "%i.%i.%i", GNUTLS_VERSION_MAJOR,
+           GNUTLS_VERSION_MINOR, GNUTLS_VERSION_PATCH);
+  assert(strcmp(gnutls_version_string, CMAKE_EXPECTED_GNUTLS_VERSION) == 0);
+
+  return 0;
+}
diff --git a/Tests/FindICU/Test/main.cpp b/Tests/FindICU/Test/main.cpp
index 64cc5d3..606e94e 100644
--- a/Tests/FindICU/Test/main.cpp
+++ b/Tests/FindICU/Test/main.cpp
@@ -1,10 +1,9 @@
-#include <unicode/uclean.h>
-#include <unicode/ustring.h>
-#include <unicode/utypes.h>
-
 #include <unicode/ucal.h>
+#include <unicode/uclean.h>
 #include <unicode/ucnv.h>
 #include <unicode/udat.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
 
 int main()
 {
diff --git a/Tests/FindJPEG/Test/main.c b/Tests/FindJPEG/Test/main.c
index 0e23eff..5a67faa 100644
--- a/Tests/FindJPEG/Test/main.c
+++ b/Tests/FindJPEG/Test/main.c
@@ -1,7 +1,8 @@
 #include <assert.h>
+// clang-format off
 #include <stdio.h>
-
 #include <jpeglib.h>
+// clang-format on
 
 int main()
 {
diff --git a/Tests/FindMatlab/basic_checks/CMakeLists.txt b/Tests/FindMatlab/basic_checks/CMakeLists.txt
index c5be1ea..c0c752a 100644
--- a/Tests/FindMatlab/basic_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/basic_checks/CMakeLists.txt
@@ -71,3 +71,15 @@
         )
     set_tests_properties(${PROJECT_NAME}_matlabtest-4 PROPERTIES WILL_FAIL TRUE)
 endif()
+
+
+# checking correct flags passed
+# EXCLUDE_FROM_ALL appears after a multiargs (like SRC)
+matlab_add_mex(
+    # target name
+    NAME cmake_matlab_test_exclude_from_all
+    # output name
+    OUTPUT_NAME cmake_matlab_mex_dummy
+    SRC ${CMAKE_CURRENT_SOURCE_DIR}/../matlab_wrapper_failure.cpp
+    EXCLUDE_FROM_ALL
+    )
diff --git a/Tests/FindMatlab/matlab_wrapper_failure.cpp b/Tests/FindMatlab/matlab_wrapper_failure.cpp
new file mode 100644
index 0000000..3fe437b
--- /dev/null
+++ b/Tests/FindMatlab/matlab_wrapper_failure.cpp
@@ -0,0 +1,13 @@
+// This should not link, as the mex function is missing.
+// This is mostly for checking we are passing the right arguments to the
+// add_library
+
+#include <algorithm>
+
+#include "mex.h"
+
+void mexFunctionXX(const int nlhs, mxArray* plhs[], const int nrhs,
+                   const mxArray* prhs[])
+{
+  mexErrMsgTxt("Should not be running");
+}
diff --git a/Tests/FindOpenACC/CMakeLists.txt b/Tests/FindOpenACC/CMakeLists.txt
new file mode 100644
index 0000000..ef7de65
--- /dev/null
+++ b/Tests/FindOpenACC/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+set(langs C CXX)
+if(NOT CMAKE_GENERATOR STREQUAL "Ninja")
+  list(APPEND langs Fortran)
+endif()
+
+foreach(lang IN LISTS langs)
+  if(CMAKE_${lang}_COMPILER)
+    add_test(NAME FindOpenACC.Test${lang} COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/FindOpenACC/${lang}Test"
+      "${CMake_BINARY_DIR}/Tests/FindOpenACC/${lang}Test"
+      ${build_generator_args}
+      --build-project TestFindOpenACC
+      --build-options ${build_options}
+      --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+      )
+  endif()
+endforeach()
diff --git a/Tests/FindOpenACC/CTest/CMakeLists.txt b/Tests/FindOpenACC/CTest/CMakeLists.txt
new file mode 100644
index 0000000..c8d0968
--- /dev/null
+++ b/Tests/FindOpenACC/CTest/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.14)
+project(VerifyFindOpenAcc C)
+
+set(CMAKE_C_STANDARD 11)
+
+find_package(OpenACC REQUIRED)
+
+add_executable(UsesOpenACC main.c)
+target_link_libraries(UsesOpenACC PRIVATE OpenACC::OpenACC_C)
+
+add_executable(UsesOpenACCVars main.c)
+target_link_options(UsesOpenACCVars PRIVATE ${OpenACC_C_OPTIONS})
+target_compile_options(UsesOpenACCVars PRIVATE ${OpenACC_C_OPTIONS})
diff --git a/Tests/FindOpenACC/CTest/main.c b/Tests/FindOpenACC/CTest/main.c
new file mode 100644
index 0000000..53b6cae
--- /dev/null
+++ b/Tests/FindOpenACC/CTest/main.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+void vecaddgpu(float* r, float* a, float* b, int n)
+{
+#pragma acc kernels loop present(r, a, b)
+  for (int i = 0; i < n; ++i)
+    r[i] = a[i] + b[i];
+}
+
+int main()
+{
+  int n = 100000; /* vector length */
+  float* a;       /* input vector 1 */
+  float* b;       /* input vector 2 */
+  float* r;       /* output vector */
+  float* e;       /* expected output values */
+  int i, errs;
+
+  a = (float*)malloc(n * sizeof(float));
+  b = (float*)malloc(n * sizeof(float));
+  r = (float*)malloc(n * sizeof(float));
+  e = (float*)malloc(n * sizeof(float));
+  for (i = 0; i < n; ++i) {
+    a[i] = (float)(i + 1);
+    b[i] = (float)(1000 * i);
+  }
+/* compute on the GPU */
+#pragma acc data copyin(a [0:n], b [0:n]) copyout(r [0:n])
+  {
+    vecaddgpu(r, a, b, n);
+  }
+  /* compute on the host to compare */
+  for (i = 0; i < n; ++i)
+    e[i] = a[i] + b[i];
+  /* compare results */
+  errs = 0;
+  for (i = 0; i < n; ++i) {
+    if (r[i] != e[i]) {
+      ++errs;
+    }
+  }
+  return errs;
+}
diff --git a/Tests/FindOpenACC/CXXTest/CMakeLists.txt b/Tests/FindOpenACC/CXXTest/CMakeLists.txt
new file mode 100644
index 0000000..a6caf7b
--- /dev/null
+++ b/Tests/FindOpenACC/CXXTest/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.14)
+project(VerifyFindOpenAcc CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+
+find_package(OpenACC REQUIRED)
+
+add_executable(UsesOpenACC main.cxx)
+target_link_libraries(UsesOpenACC PRIVATE OpenACC::OpenACC_CXX)
+
+add_executable(UsesOpenACCVars main.cxx)
+target_link_options(UsesOpenACCVars PRIVATE ${OpenACC_CXX_OPTIONS})
+target_compile_options(UsesOpenACCVars PRIVATE ${OpenACC_CXX_OPTIONS})
diff --git a/Tests/FindOpenACC/CXXTest/main.cxx b/Tests/FindOpenACC/CXXTest/main.cxx
new file mode 100644
index 0000000..7369045
--- /dev/null
+++ b/Tests/FindOpenACC/CXXTest/main.cxx
@@ -0,0 +1,43 @@
+
+#include <vector>
+
+void vecaddgpu(float* r, float* a, float* b, std::size_t n)
+{
+#pragma acc kernels loop present(r, a, b)
+  for (std::size_t i = 0; i < n; ++i)
+    r[i] = a[i] + b[i];
+}
+
+int main(int, char* [])
+{
+  const std::size_t n = 100000; /* vector length */
+  std::vector<float> a(n);      /* input vector 1 */
+  std::vector<float> b(n);      /* input vector 2 */
+  std::vector<float> r(n);      /* output vector */
+  std::vector<float> e(n);      /* expected output values */
+
+  for (std::size_t i = 0; i < n; ++i) {
+    a[i] = static_cast<float>(i + 1);
+    b[i] = static_cast<float>(1000 * i);
+  }
+
+  /* compute on the GPU */
+  auto a_ptr = a.data();
+  auto b_ptr = b.data();
+  auto r_ptr = r.data();
+#pragma acc data copyin(a_ptr [0:n], b_ptr [0:n]) copyout(r_ptr [0:n])
+  {
+    vecaddgpu(r_ptr, a_ptr, b_ptr, n);
+  }
+  /* compute on the host to compare */
+  for (std::size_t i = 0; i < n; ++i)
+    e[i] = a[i] + b[i];
+  /* compare results */
+  int errs = 0;
+  for (std::size_t i = 0; i < n; ++i) {
+    if (r[i] != e[i]) {
+      ++errs;
+    }
+  }
+  return errs;
+}
diff --git a/Tests/FindOpenACC/FortranTest/CMakeLists.txt b/Tests/FindOpenACC/FortranTest/CMakeLists.txt
new file mode 100644
index 0000000..12e3503
--- /dev/null
+++ b/Tests/FindOpenACC/FortranTest/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.14)
+project(VerifyFindOpenAcc Fortran)
+
+find_package(OpenACC REQUIRED)
+
+add_executable(UsesOpenACC main.f90)
+target_link_libraries(UsesOpenACC PRIVATE OpenACC::OpenACC_Fortran)
+
+add_executable(UsesOpenACCVars main.f90)
+target_link_options(UsesOpenACCVars PRIVATE ${OpenACC_Fortran_OPTIONS})
+target_compile_options(UsesOpenACCVars PRIVATE ${OpenACC_Fortran_OPTIONS})
diff --git a/Tests/FindOpenACC/FortranTest/main.f90 b/Tests/FindOpenACC/FortranTest/main.f90
new file mode 100644
index 0000000..2ff1ba0
--- /dev/null
+++ b/Tests/FindOpenACC/FortranTest/main.f90
@@ -0,0 +1,9 @@
+program t
+integer(4) a(10000)
+a = [ (1+i,i=1,10000) ]
+!$acc kernels
+do i = 1, 10000
+  if (a(i)/3000*3000.eq.a(i)) print *," located ",i,a(i),i.gt.5000,a(i)/5.0
+end do
+!$acc end kernels
+end
diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt
index 1b9c973..7217f43 100644
--- a/Tests/FindPackageTest/CMakeLists.txt
+++ b/Tests/FindPackageTest/CMakeLists.txt
@@ -391,16 +391,44 @@
 message(STATUS "Searching for export(PACKAGE) test project")
 set(CMakeTestExportPackage_DIR "" CACHE FILEPATH
   "Wipe out find results for testing." FORCE)
-find_package(CMakeTestExportPackage 1.${version} EXACT REQUIRED)
 
-message(STATUS "Searching for export(PACKAGE) test project with CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=TRUE")
+message(STATUS "Searching for export(PACKAGE) with CMAKE_FIND_USE_PACKAGE_REGISTRY=TRUE")
+set(CMAKE_FIND_USE_PACKAGE_REGISTRY TRUE)
+find_package(CMakeTestExportPackage 1.${version} EXACT REQUIRED)
+if(NOT CMakeTestExportPackage_FOUND)
+  message(SEND_ERROR "CMakeTestExportPackage should be FOUND!")
+endif()
+unset(CMAKE_FIND_USE_PACKAGE_REGISTRY)
+
+message(STATUS "Searching for export(PACKAGE) with CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=FALSE")
+set(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY FALSE)
+find_package(CMakeTestExportPackage 1.${version} EXACT REQUIRED)
+if(NOT CMakeTestExportPackage_FOUND)
+  message(SEND_ERROR "CMakeTestExportPackage should be FOUND!")
+endif()
+unset(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY)
+
+message(STATUS "Searching for export(PACKAGE) with CMAKE_FIND_USE_PACKAGE_REGISTRY=TRUE and CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=TRUE")
+set(CMAKE_FIND_USE_PACKAGE_REGISTRY TRUE)
 set(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY TRUE)
+set(CMakeTestExportPackage_DIR FALSE)
+find_package(CMakeTestExportPackage 1.${version} EXACT REQUIRED)
+if(NOT CMakeTestExportPackage_FOUND)
+  message(SEND_ERROR "CMakeTestExportPackage should be FOUND!")
+endif()
+unset(CMAKE_FIND_USE_PACKAGE_REGISTRY)
+unset(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY)
+
+message(STATUS "Searching for export(PACKAGE) with CMAKE_FIND_USE_PACKAGE_REGISTRY=FALSE and CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=FALSE")
+set(CMAKE_FIND_USE_PACKAGE_REGISTRY FALSE)
+set(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY FALSE)
 set(CMakeTestExportPackage_DIR "" CACHE FILEPATH
   "Wipe out find results for testing." FORCE)
-find_package(CMakeTestExportPackage  1.${version} EXACT QUIET)
+find_package(CMakeTestExportPackage 1.${version} EXACT QUIET)
 if(CMakeTestExportPackage_FOUND)
-  message(SEND_ERROR "CMakeTestExportPackage should not be FOUND!")
+  message(SEND_ERROR "CMakeTestExportPackage should be not FOUND!")
 endif()
+unset(CMAKE_FIND_USE_PACKAGE_REGISTRY)
 unset(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY)
 
 message(STATUS "Remove export(PACKAGE) test project")
diff --git a/Tests/FindProtobuf/Test/CMakeLists.txt b/Tests/FindProtobuf/Test/CMakeLists.txt
index bc89190..fc6b37e 100644
--- a/Tests/FindProtobuf/Test/CMakeLists.txt
+++ b/Tests/FindProtobuf/Test/CMakeLists.txt
@@ -29,6 +29,7 @@
 add_executable(test_var_protoc main-protoc.cxx)
 target_include_directories(test_var_protoc PRIVATE ${Protobuf_INCLUDE_DIRS})
 target_link_libraries(test_var_protoc PRIVATE ${Protobuf_PROTOC_LIBRARIES})
+target_compile_features(test_var_protoc PRIVATE cxx_std_11)
 add_test(NAME test_var_protoc COMMAND test_var_protoc)
 
 add_test(NAME test_tgt_protoc_version COMMAND protobuf::protoc --version)
@@ -37,14 +38,17 @@
 PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER msgs/example.proto)
 PROTOBUF_GENERATE_CPP(DESC_PROTO_SRC DESC_PROTO_HEADER DESCRIPTORS DESC_PROTO_DESC msgs/example_desc.proto)
 add_library(msgs ${PROTO_SRC} ${PROTO_HEADER})
+target_compile_features(msgs PRIVATE cxx_std_11)
 
 add_executable(test_generate main-generate.cxx ${PROTO_SRC})
 target_include_directories(test_generate PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 target_link_libraries(test_generate msgs ${Protobuf_LIBRARIES})
+target_compile_features(test_generate PRIVATE cxx_std_11)
 add_test(NAME test_generate COMMAND test_generate)
 
 add_executable(test_desc main-desc.cxx ${DESC_PROTO_SRC})
 target_compile_features(test_desc PRIVATE cxx_std_11)
 target_include_directories(test_desc PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 target_link_libraries(test_desc msgs ${Protobuf_LIBRARIES})
+target_compile_features(test_desc PRIVATE cxx_std_11)
 add_test(NAME test_desc COMMAND test_desc ${DESC_PROTO_DESC})
diff --git a/Tests/FindProtobuf/Test/main-desc.cxx b/Tests/FindProtobuf/Test/main-desc.cxx
index a26e562..dc768ab 100644
--- a/Tests/FindProtobuf/Test/main-desc.cxx
+++ b/Tests/FindProtobuf/Test/main-desc.cxx
@@ -1,11 +1,12 @@
 #include <fstream>
-#include <google/protobuf/descriptor.h>
 #include <google/protobuf/descriptor.pb.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/text_format.h>
 #include <iostream>
 #include <string>
 
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/text_format.h>
+
 int main(int argc, char* argv[])
 {
   std::ifstream fs;
diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt
index 4be2f22..868cfe0 100644
--- a/Tests/FindPython/CMakeLists.txt
+++ b/Tests/FindPython/CMakeLists.txt
@@ -121,6 +121,19 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
+  add_test(NAME FindPython.RequiredArtifacts COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts"
+    "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts"
+    ${build_generator_args}
+    --build-project TestRequiredArtifacts
+    --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
+    "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
+    "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
 endif()
 
 if(CMake_TEST_FindPython_NumPy)
diff --git a/Tests/FindPython/FindPythonScript.cmake b/Tests/FindPython/FindPythonScript.cmake
index 9450092..bc7e0d1 100644
--- a/Tests/FindPython/FindPythonScript.cmake
+++ b/Tests/FindPython/FindPythonScript.cmake
@@ -1 +1,9 @@
-find_package(${PYTHON_PACKAGE_NAME} REQUIRED QUIET)
+
+if (PYTHON_MUST_NOT_BE_FOUND)
+  find_package(${PYTHON_PACKAGE_NAME} QUIET)
+  if (${PYTHON_PACKAGE_NAME}_FOUND)
+    message(FATAL_ERROR "${PYTHON_PACKAGE_NAME}: unexpectedly founded.")
+  endif()
+else()
+  find_package(${PYTHON_PACKAGE_NAME} REQUIRED QUIET)
+endif()
diff --git a/Tests/FindPython/NumPy/arraytest.c b/Tests/FindPython/NumPy/arraytest.c
index db259e5..51db7bc 100644
--- a/Tests/FindPython/NumPy/arraytest.c
+++ b/Tests/FindPython/NumPy/arraytest.c
@@ -1,10 +1,10 @@
 #include "Python.h"
 
 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include "numpy/arrayobject.h"
-
 #include <math.h>
 
+#include "numpy/arrayobject.h"
+
 static PyObject* vecsq(PyObject* self, PyObject* args);
 
 static PyMethodDef arraytestMethods[] = { { "vecsq", vecsq, METH_VARARGS },
diff --git a/Tests/FindPython/Python2/CMakeLists.txt b/Tests/FindPython/Python2/CMakeLists.txt
index 274745a..cf77ca2 100644
--- a/Tests/FindPython/Python2/CMakeLists.txt
+++ b/Tests/FindPython/Python2/CMakeLists.txt
@@ -34,4 +34,5 @@
 
 add_test(NAME findpython2_script
          COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python2
+         -DPython2_FIND_STRATEGY=${Python2_FIND_STRATEGY}
          -P "${CMAKE_CURRENT_LIST_DIR}/../FindPythonScript.cmake")
diff --git a/Tests/FindPython/Python3/CMakeLists.txt b/Tests/FindPython/Python3/CMakeLists.txt
index b21a15b..6691a48 100644
--- a/Tests/FindPython/Python3/CMakeLists.txt
+++ b/Tests/FindPython/Python3/CMakeLists.txt
@@ -34,4 +34,57 @@
 
 add_test(NAME findpython3_script
          COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python3
+         -DPython3_FIND_STRATEGY=${Python3_FIND_STRATEGY}
+         -P "${CMAKE_CURRENT_LIST_DIR}/../FindPythonScript.cmake")
+
+
+## Try a new search specifying only expected ABI
+# retrieve ABI of python interpreter
+execute_process (COMMAND "${Python3_EXECUTABLE}" -c
+                         "import sys; sys.stdout.write(sys.abiflags)"
+                 RESULT_VARIABLE result
+                 OUTPUT_VARIABLE abi
+                 ERROR_QUIET
+                 OUTPUT_STRIP_TRAILING_WHITESPACE)
+if (result)
+  # assume ABI is not supported
+  set (abi "")
+endif()
+
+# define FIND_ABI variable
+if (abi MATCHES "d")
+  set (Python3_VALID_ABI "ON")
+else()
+  set (Python3_VALID_ABI "OFF")
+endif()
+if (abi MATCHES "m")
+  list (APPEND Python3_VALID_ABI "ON")
+else()
+  list (APPEND Python3_VALID_ABI "OFF")
+endif()
+if (abi MATCHES "u")
+  list (APPEND Python3_VALID_ABI "ON")
+else()
+  list (APPEND Python3_VALID_ABI "OFF")
+endif()
+# build an invalid pattern for ABI
+set (Python3_INVALID_ABI)
+foreach (abi IN LISTS Python3_VALID_ABI)
+  if (abi)
+    list (APPEND Python3_INVALID_ABI "OFF")
+  else()
+    list (APPEND Python3_INVALID_ABI "ON")
+  endif()
+endforeach()
+
+add_test(NAME python3_find_valid_abi
+         COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python3
+         -DPython3_FIND_STRATEGY=${Python3_FIND_STRATEGY}
+         "-DPython3_FIND_ABI=${Python3_VALID_ABI}"
+         -P "${CMAKE_CURRENT_LIST_DIR}/../FindPythonScript.cmake")
+add_test(NAME python3_find_invalid_abi
+         COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python3
+         -DPYTHON_MUST_NOT_BE_FOUND=ON
+         -DPython3_FIND_STRATEGY=${Python3_FIND_STRATEGY}
+         "-DPython3_FIND_ABI=${Python3_INVALID_ABI}"
          -P "${CMAKE_CURRENT_LIST_DIR}/../FindPythonScript.cmake")
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
new file mode 100644
index 0000000..39e8ea5
--- /dev/null
+++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -0,0 +1,110 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(TestRequiredArtifacts LANGUAGES C)
+
+include(CTest)
+
+find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
+if (NOT Python2_FOUND)
+  message (FATAL_ERROR "Fail to found Python 2")
+endif()
+find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
+if (NOT Python3_FOUND)
+  message (FATAL_ERROR "Fail to found Python 3")
+endif()
+
+
+add_test(NAME FindPython.RequiredArtifacts.Interpreter.VALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Interpreter.VALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=TRUE -DCHECK_INTERPRETER=ON
+                  "-DPython3_EXECUTABLE=${Python3_EXECUTABLE}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+add_test(NAME FindPython.RequiredArtifacts.Interpreter.INVALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Interpreter.INVALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_INTERPRETER=ON
+                  "-DPython3_EXECUTABLE=${Python3_EXECUTABLE}-bad${CMAKE_EXECUTABLE_SUFFIX}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+
+add_test(NAME FindPython.RequiredArtifacts.Library.VALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Library.VALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=TRUE -DCHECK_LIBRARY=ON
+                  "-DPython3_LIBRARY=${Python3_LIBRARY_RELEASE}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+add_test(NAME FindPython.RequiredArtifacts.Library.INVALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Library.INVALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_LIBRARY=ON
+                  "-DPython3_LIBRARY=${Python2_LIBRARY_RELEASE}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+
+add_test(NAME FindPython.RequiredArtifacts.Include.VALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Include.VALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=TRUE -DCHECK_INCLUDE=ON
+                  "-DPython3_INCLUDE_DIR=${Python3_INCLUDE_DIRS}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+add_test(NAME FindPython.RequiredArtifacts.Include.INVALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Include.INVALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_INCLUDE=ON
+                  "-DPython3_INCLUDE_DIR=${Python2_INCLUDE_DIRS}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+
+add_test(NAME FindPython.RequiredArtifacts.Interpreter-Library.INVALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Interpreter-Library.INVALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_INTERPRETER=ON -DCHECK_LIBRARY=ON
+                  "-DPython3_EXECUTABLE=${Python3_EXECUTABLE}"
+                  "-DPython3_LIBRARY=${Python2_LIBRARY_RELEASE}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
+
+add_test(NAME FindPython.RequiredArtifacts.Library-Include.INVALID COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindPython/RequiredArtifacts/Check"
+  "${CMake_BINARY_DIR}/Tests/FindPython/RequiredArtifacts/Library-Include.INVALID"
+  ${build_generator_args}
+  --build-project TestRequiredArtifacts.Check
+  --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_LIBRARY=ON -DCHECK_INCLUDE=ON
+                  "-DPython3_LIBRARY=${Python3_LIBRARY_RELEASE}"
+                  "-DPython3_INCLUDE_DIR=${Python2_INCLUDE_DIRS}"
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
diff --git a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
new file mode 100644
index 0000000..b859ac5
--- /dev/null
+++ b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(TestRequiredArtifacts.Check LANGUAGES C)
+
+set (components)
+if (CHECK_INTERPRETER)
+  set (required_interpreter "${Python3_EXECUTABLE}")
+  list (APPEND components Interpreter)
+endif()
+if (CHECK_LIBRARY OR CHECK_INCLUDE)
+  list (APPEND components Development)
+  if (CHECK_LIBRARY)
+    set (required_library "${Python3_LIBRARY}")
+  endif()
+  if (CHECK_INCLUDE)
+    set (required_include "${Python3_INCLUDE_DIR}")
+  endif()
+endif()
+
+find_package (Python3 COMPONENTS ${components})
+
+
+if (PYTHON_IS_FOUND AND NOT Python3_FOUND)
+  message (FATAL_ERROR "Python3 unexpectedly not found")
+endif()
+if (NOT PYTHON_IS_FOUND AND Python3_FOUND)
+  message (FATAL_ERROR "Python3 unexpectedly found")
+endif()
+
+
+if (CHECK_INTERPRETER AND NOT Python3_EXECUTABLE STREQUAL required_interpreter)
+  message (FATAL_ERROR "Fail to use input variable Python3_EXECUTABLE")
+endif()
+
+if (CHECK_LIBRARY AND NOT Python3_LIBRARY_RELEASE STREQUAL required_library)
+  message (FATAL_ERROR "Fail to use input variable Python3_LIBRARY")
+endif()
+
+if (CHECK_INCLUDE AND NOT Python3_INCLUDE_DIRS STREQUAL required_include)
+  message (FATAL_ERROR "Fail to use input variable Python3_INCLUDE_DIR")
+endif()
diff --git a/Tests/FindSQLite3/Test/main.c b/Tests/FindSQLite3/Test/main.c
index aeb4940..fb17c67 100644
--- a/Tests/FindSQLite3/Test/main.c
+++ b/Tests/FindSQLite3/Test/main.c
@@ -1,6 +1,5 @@
-#include <string.h>
-
 #include <sqlite3.h>
+#include <string.h>
 
 int main()
 {
diff --git a/Tests/FindX11/Test/main.c b/Tests/FindX11/Test/main.c
index 044bfa2..c8144e0 100644
--- a/Tests/FindX11/Test/main.c
+++ b/Tests/FindX11/Test/main.c
@@ -167,12 +167,12 @@
 #endif
 
 #ifdef HAVE_X11_xkbfile
+// clang-format off
 #  include <stdio.h>
-
 #  include <X11/XKBlib.h>
 #  include <X11/extensions/XKBfile.h>
-
 #  include <stdlib.h>
+// clang-format on
 
 static void test_xkbfile(void)
 {
@@ -184,7 +184,6 @@
 
 #ifdef HAVE_X11_Xmu
 #  include <X11/Xmu/Xmu.h>
-
 #  include <stdlib.h>
 
 static Bool test_Xmu(void)
diff --git a/Tests/FortranModules/Library/a.f90 b/Tests/FortranModules/Library/a.f90
index 3031c07..c180cc0 100644
--- a/Tests/FortranModules/Library/a.f90
+++ b/Tests/FortranModules/Library/a.f90
@@ -1,3 +1,6 @@
 MODULE libraryModuleA
         USE libraryModuleB
+CONTAINS
+        SUBROUTINE libA
+        END SUBROUTINE
 END MODULE
diff --git a/Tests/FortranModules/Library/b.f90 b/Tests/FortranModules/Library/b.f90
index ae1edcb..f550996 100644
--- a/Tests/FortranModules/Library/b.f90
+++ b/Tests/FortranModules/Library/b.f90
@@ -1,2 +1,5 @@
 MODULE libraryModuleB
+CONTAINS
+        SUBROUTINE libB
+        END SUBROUTINE
 END MODULE
diff --git a/Tests/FortranModules/Subdir/subdir.f90 b/Tests/FortranModules/Subdir/subdir.f90
index 68955f6..5288b06 100644
--- a/Tests/FortranModules/Subdir/subdir.f90
+++ b/Tests/FortranModules/Subdir/subdir.f90
@@ -1,2 +1,5 @@
 MODULE subdirModuleA
+CONTAINS
+        SUBROUTINE subdirLibA
+        END SUBROUTINE
 END MODULE
diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt
index 45372dd..de887fa 100644
--- a/Tests/FortranOnly/CMakeLists.txt
+++ b/Tests/FortranOnly/CMakeLists.txt
@@ -112,3 +112,11 @@
       )
   endif()
 endif()
+
+# Test that with Intel Fortran we always compile with preprocessor
+# defines even if splitting the preprocessing and compilation steps.
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
+  add_executable(IntelIfDef IntelIfDef.f)
+  set_property(TARGET IntelIfDef PROPERTY Fortran_FORMAT FIXED)
+  target_compile_definitions(IntelIfDef PRIVATE SOME_DEF)
+endif()
diff --git a/Tests/FortranOnly/IntelIfDef.f b/Tests/FortranOnly/IntelIfDef.f
new file mode 100644
index 0000000..d7a73d1
--- /dev/null
+++ b/Tests/FortranOnly/IntelIfDef.f
@@ -0,0 +1,3 @@
+        INCLUDE 'IntelIfDef.inc'
+        PROGRAM IntelIfDef
+        END
diff --git a/Tests/FortranOnly/IntelIfDef.inc b/Tests/FortranOnly/IntelIfDef.inc
new file mode 100644
index 0000000..52edafa
--- /dev/null
+++ b/Tests/FortranOnly/IntelIfDef.inc
@@ -0,0 +1,3 @@
+CDEC$   IF .NOT. DEFINED(SOME_DEF)
+CDEC$     INCLUDE 'SOME_DEF not defined'
+CDEC$   END IF
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt
index 3ff2b85..9d51342 100644
--- a/Tests/GeneratorExpression/CMakeLists.txt
+++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -86,7 +86,7 @@
     -Dtest_colons_4=$<1:C:\\CMake>
     -Dtest_colons_5=$<1:C:/CMake>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part1.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 1 of 4)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 1 of 5)"
   VERBATIM
   )
 
@@ -157,7 +157,7 @@
     -Dtest_arbitrary_content_comma_9=$<1:a,,b,,>
     -Dtest_arbitrary_content_comma_10=$<1:,,a,,b,,>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part2.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 4)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 2 of 5)"
   VERBATIM
 )
 
@@ -251,7 +251,7 @@
     -Dequal22=$<EQUAL:10,-012>
     -Dequal23=$<EQUAL:-10,-012>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part3.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 3 of 4)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 3 of 5)"
   VERBATIM
   )
 
@@ -277,7 +277,27 @@
     -DWIN32=${WIN32}
     -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check-part4.cmake
-  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 4 of 4)"
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 4 of 5)"
+  VERBATIM
+  )
+
+add_custom_target(check-part5 ALL
+  COMMAND ${CMAKE_COMMAND} -E echo "check done (part 5 of 5)"
+  DEPENDS check-part5.stamp
+  VERBATIM
+  )
+
+add_custom_command(
+  OUTPUT check-part5.stamp
+  DEPENDS $<FILTER:file.foo.bar,EXCLUDE,\\.foo\\.bar$>
+  COMMAND ${CMAKE_COMMAND} -E sleep 0
+  VERBATIM
+  )
+set_property(SOURCE check-part5.stamp PROPERTY SYMBOLIC 1)
+
+add_custom_command(
+  OUTPUT file.foo.bar
+  COMMAND ${CMAKE_COMMAND} -P check-part5.cmake
   VERBATIM
   )
 
diff --git a/Tests/GeneratorExpression/check-part5.cmake b/Tests/GeneratorExpression/check-part5.cmake
new file mode 100644
index 0000000..77d1387
--- /dev/null
+++ b/Tests/GeneratorExpression/check-part5.cmake
@@ -0,0 +1 @@
+message(SEND_ERROR "$<FILTER:file.foo.bar,EXCLUDE,\\.foo\\.bar$> genex in DEPENDS argument of 'add_custom_command()' is not empty")
diff --git a/Tests/GoogleTest/Test/main1.cxx b/Tests/GoogleTest/Test/main1.cxx
index 03d604b..c353d91 100644
--- a/Tests/GoogleTest/Test/main1.cxx
+++ b/Tests/GoogleTest/Test/main1.cxx
@@ -1,7 +1,7 @@
-#include <gtest/gtest.h>
-
 #include <string>
 
+#include <gtest/gtest.h>
+
 namespace {
 bool shouldFail = false;
 }
diff --git a/Tests/IPO/CMakeLists.txt b/Tests/IPO/CMakeLists.txt
deleted file mode 100644
index 6dabf86..0000000
--- a/Tests/IPO/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-cmake_minimum_required (VERSION 2.8)
-project(IPO NONE)
-
-set_property(DIRECTORY PROPERTY INTERPROCEDURAL_OPTIMIZATION 1)
-
-add_subdirectory(../COnly COnly)
-add_subdirectory(../CxxOnly CxxOnly)
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp b/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
index be18ebf..a13f08f 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
@@ -1,7 +1,6 @@
 
-#include "upstream.h"
-
 #include "config_iface.h"
+#include "upstream.h"
 
 int consumer()
 {
diff --git a/Tests/InterfaceLibrary/libsdir/sharedlib/sharedlib.h b/Tests/InterfaceLibrary/libsdir/sharedlib/sharedlib.h
index 5b3c7db..3e18941 100644
--- a/Tests/InterfaceLibrary/libsdir/sharedlib/sharedlib.h
+++ b/Tests/InterfaceLibrary/libsdir/sharedlib/sharedlib.h
@@ -2,9 +2,8 @@
 #ifndef SHAREDLIB_H
 #define SHAREDLIB_H
 
-#include "sharedlib_export.h"
-
 #include "shareddependlib.h"
+#include "sharedlib_export.h"
 
 struct SHAREDLIB_EXPORT SharedLibObject
 {
diff --git a/Tests/Java/CMakeLists.txt b/Tests/Java/CMakeLists.txt
index 0b8269b..aea4282 100644
--- a/Tests/Java/CMakeLists.txt
+++ b/Tests/Java/CMakeLists.txt
@@ -3,6 +3,8 @@
 cmake_minimum_required (VERSION 2.6)
 set(CMAKE_VERBOSE_MAKEFILE 1)
 
+include(CTest)
+
 find_package(Java COMPONENTS Development)
 include (UseJava)
 
@@ -14,3 +16,10 @@
 
 # use listing file to specify sources and specify output directory (issue #17316)
 add_jar(hello3 @${CMAKE_CURRENT_BINARY_DIR}/java_fileslist OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/hello3")
+
+add_test (NAME Java.Jar
+          COMMAND "${Java_JAVA_EXECUTABLE}" -classpath hello.jar HelloWorld)
+add_test (NAME Java.JarSourceList
+          COMMAND "${Java_JAVA_EXECUTABLE}" -classpath hello2.jar HelloWorld)
+add_test (NAME Java.JarSourceListAndOutput
+          COMMAND "${Java_JAVA_EXECUTABLE}" -classpath "${CMAKE_CURRENT_BINARY_DIR}/hello3/hello3.jar" HelloWorld)
diff --git a/Tests/JavaJavah/B.cpp b/Tests/JavaJavah/B.cpp
index 491107e..86d8aa8 100644
--- a/Tests/JavaJavah/B.cpp
+++ b/Tests/JavaJavah/B.cpp
@@ -1,9 +1,9 @@
 
+#include "B.h"
+
 #include <jni.h>
 #include <stdio.h>
 
-#include "B.h"
-
 JNIEXPORT void JNICALL Java_B_printName(JNIEnv*, jobject)
 {
   printf("B\n");
diff --git a/Tests/JavaJavah/C.cpp b/Tests/JavaJavah/C.cpp
index 0a3178f..569eab5 100644
--- a/Tests/JavaJavah/C.cpp
+++ b/Tests/JavaJavah/C.cpp
@@ -1,9 +1,9 @@
 
+#include "C.h"
+
 #include <jni.h>
 #include <stdio.h>
 
-#include "C.h"
-
 JNIEXPORT void JNICALL Java_C_printName(JNIEnv*, jobject)
 {
   printf("C\n");
diff --git a/Tests/JavaJavah/CMakeLists.txt b/Tests/JavaJavah/CMakeLists.txt
index 071bf20..77c292a 100644
--- a/Tests/JavaJavah/CMakeLists.txt
+++ b/Tests/JavaJavah/CMakeLists.txt
@@ -3,6 +3,8 @@
 cmake_minimum_required (VERSION 2.6)
 set(CMAKE_VERBOSE_MAKEFILE 1)
 
+include(CTest)
+
 find_package(Java COMPONENTS Development)
 include (UseJava)
 
@@ -21,3 +23,6 @@
 target_include_directories(B PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
                                      ${JAVA_INCLUDE_PATH}
                                      ${JAVA_INCLUDE_PATH2})
+
+add_test (NAME Java.Javah
+          COMMAND "${Java_JAVA_EXECUTABLE}" -Djava.library.path=$<TARGET_FILE_DIR:B> -classpath hello3.jar HelloWorld2)
diff --git a/Tests/JavaNativeHeaders/CMakeLists.txt b/Tests/JavaNativeHeaders/CMakeLists.txt
index 7dc2679..2023d25 100644
--- a/Tests/JavaNativeHeaders/CMakeLists.txt
+++ b/Tests/JavaNativeHeaders/CMakeLists.txt
@@ -3,6 +3,8 @@
 cmake_minimum_required (VERSION 2.6)
 set(CMAKE_VERBOSE_MAKEFILE 1)
 
+include (CTest)
+
 find_package(Java COMPONENTS Development)
 include (UseJava)
 
@@ -16,3 +18,7 @@
 
 add_library(D SHARED D.cpp E.cpp)
 target_link_libraries (D PRIVATE D1-native E1-native)
+
+
+add_test (NAME Java.NativeHeaders
+          COMMAND "${Java_JAVA_EXECUTABLE}" -Djava.library.path=$<TARGET_FILE_DIR:D> -classpath hello4.jar HelloWorld3)
diff --git a/Tests/JavaNativeHeaders/D.cpp b/Tests/JavaNativeHeaders/D.cpp
index 2a90a08..018386c 100644
--- a/Tests/JavaNativeHeaders/D.cpp
+++ b/Tests/JavaNativeHeaders/D.cpp
@@ -1,9 +1,9 @@
 
+#include "D.h"
+
 #include <jni.h>
 #include <stdio.h>
 
-#include "D.h"
-
 JNIEXPORT void JNICALL Java_D_printName(JNIEnv*, jobject)
 {
   printf("D\n");
diff --git a/Tests/JavaNativeHeaders/E.cpp b/Tests/JavaNativeHeaders/E.cpp
index fb98946..3a8e0ac 100644
--- a/Tests/JavaNativeHeaders/E.cpp
+++ b/Tests/JavaNativeHeaders/E.cpp
@@ -1,9 +1,9 @@
 
+#include "E.h"
+
 #include <jni.h>
 #include <stdio.h>
 
-#include "E.h"
-
 JNIEXPORT void JNICALL Java_E_printName(JNIEnv*, jobject)
 {
   printf("E\n");
diff --git a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c
index 3558f89..99f0de9 100644
--- a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c
+++ b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c
@@ -1,8 +1,9 @@
-#include "cmCPluginAPI.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "cmCPluginAPI.h"
+
 typedef struct
 {
   char* LibraryName;
diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c
index 3558f89..99f0de9 100644
--- a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c
+++ b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c
@@ -1,8 +1,9 @@
-#include "cmCPluginAPI.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "cmCPluginAPI.h"
+
 typedef struct
 {
   char* LibraryName;
diff --git a/Tests/MFC/mfc1/ChildFrm.cpp b/Tests/MFC/mfc1/ChildFrm.cpp
index a415229..0422d2a 100644
--- a/Tests/MFC/mfc1/ChildFrm.cpp
+++ b/Tests/MFC/mfc1/ChildFrm.cpp
@@ -1,11 +1,13 @@
 // ChildFrm.cpp : implementation of the CChildFrame class
 //
 
+// clang-format off
 #include "stdafx.h"
 
 #include "mfc1.h"
 
 #include "ChildFrm.h"
+// clang-format on
 
 #ifdef _DEBUG
 #  define new DEBUG_NEW
diff --git a/Tests/MFC/mfc1/MainFrm.cpp b/Tests/MFC/mfc1/MainFrm.cpp
index 6bd2b3d..7f82e26 100644
--- a/Tests/MFC/mfc1/MainFrm.cpp
+++ b/Tests/MFC/mfc1/MainFrm.cpp
@@ -1,11 +1,13 @@
 // MainFrm.cpp : implementation of the CMainFrame class
 //
 
+// clang-format off
 #include "stdafx.h"
 
 #include "mfc1.h"
 
 #include "MainFrm.h"
+// clang-format on
 
 #ifdef _DEBUG
 #  define new DEBUG_NEW
diff --git a/Tests/MFC/mfc1/mfc1.cpp b/Tests/MFC/mfc1/mfc1.cpp
index 892a89d..87406b6 100644
--- a/Tests/MFC/mfc1/mfc1.cpp
+++ b/Tests/MFC/mfc1/mfc1.cpp
@@ -1,6 +1,7 @@
 // mfc1.cpp : Defines the class behaviors for the application.
 //
 
+// clang-format off
 #include "stdafx.h"
 
 #include "MainFrm.h"
@@ -9,6 +10,7 @@
 #include "ChildFrm.h"
 #include "mfc1Doc.h"
 #include "mfc1View.h"
+// clang-format on
 
 #ifdef _DEBUG
 #  define new DEBUG_NEW
diff --git a/Tests/MFC/mfc1/mfc1Doc.cpp b/Tests/MFC/mfc1/mfc1Doc.cpp
index e69b61c..ef8b6af 100644
--- a/Tests/MFC/mfc1/mfc1Doc.cpp
+++ b/Tests/MFC/mfc1/mfc1Doc.cpp
@@ -1,11 +1,13 @@
 // mfc1Doc.cpp : implementation of the Cmfc1Doc class
 //
 
+// clang-format off
 #include "stdafx.h"
 
 #include "mfc1.h"
 
 #include "mfc1Doc.h"
+// clang-format on
 
 #ifdef _DEBUG
 #  define new DEBUG_NEW
diff --git a/Tests/MFC/mfc1/mfc1View.cpp b/Tests/MFC/mfc1/mfc1View.cpp
index 3de55cf..55dcb8e 100644
--- a/Tests/MFC/mfc1/mfc1View.cpp
+++ b/Tests/MFC/mfc1/mfc1View.cpp
@@ -1,12 +1,14 @@
 // mfc1View.cpp : implementation of the Cmfc1View class
 //
 
+// clang-format off
 #include "stdafx.h"
 
 #include "mfc1.h"
 
 #include "mfc1Doc.h"
 #include "mfc1View.h"
+// clang-format on
 
 #ifdef _DEBUG
 #  define new DEBUG_NEW
diff --git a/Tests/MFC/mfc1/stdafx.h b/Tests/MFC/mfc1/stdafx.h
index 2680f26..b369085 100644
--- a/Tests/MFC/mfc1/stdafx.h
+++ b/Tests/MFC/mfc1/stdafx.h
@@ -61,11 +61,10 @@
 // messages
 #define _AFX_ALL_WARNINGS
 
-#include <afxdisp.h> // MFC Automation classes
-#include <afxext.h>  // MFC extensions
-#include <afxwin.h>  // MFC core and standard components
-
+#include <afxdisp.h>  // MFC Automation classes
 #include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#include <afxext.h>   // MFC extensions
+#include <afxwin.h>   // MFC core and standard components
 #ifndef _AFX_NO_AFXCMN_SUPPORT
 #  include <afxcmn.h> // MFC support for Windows Common Controls
 #endif                // _AFX_NO_AFXCMN_SUPPORT
diff --git a/Tests/MacRuntimePath/A/framework.cpp b/Tests/MacRuntimePath/A/framework.cpp
index abda195..b5a95d8 100644
--- a/Tests/MacRuntimePath/A/framework.cpp
+++ b/Tests/MacRuntimePath/A/framework.cpp
@@ -1,5 +1,6 @@
 
 #include "framework.h"
+
 #include "stdio.h"
 
 void framework()
diff --git a/Tests/MacRuntimePath/A/framework2.cpp b/Tests/MacRuntimePath/A/framework2.cpp
index d3c2c45..0a1beec 100644
--- a/Tests/MacRuntimePath/A/framework2.cpp
+++ b/Tests/MacRuntimePath/A/framework2.cpp
@@ -1,5 +1,6 @@
 
 #include "framework2.h"
+
 #include "stdio.h"
 
 void framework2()
diff --git a/Tests/MacRuntimePath/A/shared.cpp b/Tests/MacRuntimePath/A/shared.cpp
index e5e7dc5..13791c2 100644
--- a/Tests/MacRuntimePath/A/shared.cpp
+++ b/Tests/MacRuntimePath/A/shared.cpp
@@ -1,5 +1,6 @@
 
 #include "shared.h"
+
 #include "stdio.h"
 
 void shared()
diff --git a/Tests/MathTest/CMakeLists.txt b/Tests/MathTest/CMakeLists.txt
index 5403d29..396f633 100644
--- a/Tests/MathTest/CMakeLists.txt
+++ b/Tests/MathTest/CMakeLists.txt
@@ -16,6 +16,8 @@
   "1000 -12*5"
   "1000 +12*-5"
   "1000 -12*-5"
+  "~~1"
+  "1000 & ~0"
   )
 
 set(FILE_EXPRESSIONS "extern void test_expression(int x, int y, const char * text);\n")
diff --git a/Tests/Module/CheckIPOSupported-C/CMakeLists.txt b/Tests/Module/CheckIPOSupported-C/CMakeLists.txt
index 4a41a98..c5cd03e 100644
--- a/Tests/Module/CheckIPOSupported-C/CMakeLists.txt
+++ b/Tests/Module/CheckIPOSupported-C/CMakeLists.txt
@@ -13,8 +13,18 @@
 endif()
 
 add_library(foo foo.c)
+if(NOT CYGWIN AND (NOT WIN32 OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
+  add_library(bar SHARED bar.c)
+  if(WIN32)
+    # Bindexplib for clang supports LTO objects
+    set_target_properties(bar PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
+  endif()
+else()
+  # TODO: bindexplib doesn't support exporting IPO symbols with other compilers on Windows
+  add_library(bar STATIC bar.c)
+endif()
 add_executable(CheckIPOSupported-C main.c)
-target_link_libraries(CheckIPOSupported-C PUBLIC foo)
+target_link_libraries(CheckIPOSupported-C PUBLIC foo bar)
 
 enable_testing()
 add_test(NAME CheckIPOSupported-C COMMAND CheckIPOSupported-C)
diff --git a/Tests/Module/CheckIPOSupported-C/bar.c b/Tests/Module/CheckIPOSupported-C/bar.c
new file mode 100644
index 0000000..680f213
--- /dev/null
+++ b/Tests/Module/CheckIPOSupported-C/bar.c
@@ -0,0 +1,4 @@
+int bar()
+{
+  return 0x42;
+}
diff --git a/Tests/Module/CheckIPOSupported-C/main.c b/Tests/Module/CheckIPOSupported-C/main.c
index 99204ab..28ab26f 100644
--- a/Tests/Module/CheckIPOSupported-C/main.c
+++ b/Tests/Module/CheckIPOSupported-C/main.c
@@ -1,8 +1,9 @@
 int foo();
+int bar();
 
 int main()
 {
-  if (foo() == 0) {
+  if (foo() != bar()) {
     return 1;
   }
   return 0;
diff --git a/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt b/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt
index 1bb2b84..237bf1d 100644
--- a/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt
+++ b/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt
@@ -12,9 +12,20 @@
   message(FATAL_ERROR "IPO expected to work, but the check failed:\n  ${ipo_output}")
 endif()
 
-add_library(foo foo.cpp)
+
+add_library(foo STATIC foo.cpp)
+if(NOT CYGWIN AND (NOT WIN32 OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang"))
+  add_library(bar SHARED bar.cpp)
+  if(WIN32)
+    # Bindexplib for clang supports LTO objects
+    set_target_properties(bar PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
+  endif()
+else()
+  # TODO: bindexplib doesn't support exporting IPO symbols with other compilers on Windows
+  add_library(bar STATIC bar.cpp)
+endif()
 add_executable(CheckIPOSupported-CXX main.cpp)
-target_link_libraries(CheckIPOSupported-CXX PUBLIC foo)
+target_link_libraries(CheckIPOSupported-CXX PUBLIC foo bar)
 
 enable_testing()
 add_test(NAME CheckIPOSupported-CXX COMMAND CheckIPOSupported-CXX)
diff --git a/Tests/Module/CheckIPOSupported-CXX/bar.cpp b/Tests/Module/CheckIPOSupported-CXX/bar.cpp
new file mode 100644
index 0000000..680f213
--- /dev/null
+++ b/Tests/Module/CheckIPOSupported-CXX/bar.cpp
@@ -0,0 +1,4 @@
+int bar()
+{
+  return 0x42;
+}
diff --git a/Tests/Module/CheckIPOSupported-CXX/main.cpp b/Tests/Module/CheckIPOSupported-CXX/main.cpp
index 99204ab..28ab26f 100644
--- a/Tests/Module/CheckIPOSupported-CXX/main.cpp
+++ b/Tests/Module/CheckIPOSupported-CXX/main.cpp
@@ -1,8 +1,9 @@
 int foo();
+int bar();
 
 int main()
 {
-  if (foo() == 0) {
+  if (foo() != bar()) {
     return 1;
   }
   return 0;
diff --git a/Tests/NewlineArgs/cxxonly.cxx b/Tests/NewlineArgs/cxxonly.cxx
index 9e6f918..33b26dc 100644
--- a/Tests/NewlineArgs/cxxonly.cxx
+++ b/Tests/NewlineArgs/cxxonly.cxx
@@ -1,8 +1,8 @@
+#include <stdio.h>
+
 #include "libcxx1.h"
 #include "libcxx2.h"
 
-#include <stdio.h>
-
 int main()
 {
   if (LibCxx1Class::Method() != 2.0) {
diff --git a/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt b/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt
new file mode 100644
index 0000000..0b33875
--- /dev/null
+++ b/Tests/Objective-C++/cxx-file-extension-test/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(cxx-file-extension-test CXX)
+
+add_executable(cxx-file-extension-test main.mm)
diff --git a/Tests/Objective-C++/cxx-file-extension-test/main.mm b/Tests/Objective-C++/cxx-file-extension-test/main.mm
new file mode 100644
index 0000000..1c159a9
--- /dev/null
+++ b/Tests/Objective-C++/cxx-file-extension-test/main.mm
@@ -0,0 +1,8 @@
+#ifndef __OBJC__
+#  error "Compiler cannot compile Objective-C"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt b/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt
new file mode 100644
index 0000000..eda7bba
--- /dev/null
+++ b/Tests/Objective-C++/objcxx-file-extension-test/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(objcxx-file-extension-test OBJCXX CXX)
+
+add_executable(objcxx-file-extension-test main.mm)
+target_link_libraries(objcxx-file-extension-test "-framework Foundation")
diff --git a/Tests/Objective-C++/objcxx-file-extension-test/main.mm b/Tests/Objective-C++/objcxx-file-extension-test/main.mm
new file mode 100644
index 0000000..d4aa1bb
--- /dev/null
+++ b/Tests/Objective-C++/objcxx-file-extension-test/main.mm
@@ -0,0 +1,14 @@
+#ifndef __OBJC__
+#  error "Compiler is not an Objective-C compiler."
+#endif
+
+#import <Foundation/Foundation.h>
+#include <iostream>
+
+int main()
+{
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+  std::cout << "Hello World" << std::endl;
+  [pool release];
+  return 0;
+}
diff --git a/Tests/Objective-C++/simple-build-test/CMakeLists.txt b/Tests/Objective-C++/simple-build-test/CMakeLists.txt
new file mode 100644
index 0000000..cf27683
--- /dev/null
+++ b/Tests/Objective-C++/simple-build-test/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(CMAKE_MACOSX_RPATH OFF)
+
+project(simple-build-test OBJCXX)
+
+add_library(foo SHARED foo.mm)
+target_link_libraries(foo "-framework Foundation")
+
+add_executable(simple-build-test main.mm)
+target_link_libraries(simple-build-test "-framework Foundation" foo)
diff --git a/Tests/Objective-C++/simple-build-test/foo.h b/Tests/Objective-C++/simple-build-test/foo.h
new file mode 100644
index 0000000..b3fb084
--- /dev/null
+++ b/Tests/Objective-C++/simple-build-test/foo.h
@@ -0,0 +1,9 @@
+#import <Foundation/Foundation.h>
+
+@interface Foo : NSObject {
+  NSNumber* age;
+}
+
+@property (nonatomic, retain) NSNumber* age;
+
+@end
diff --git a/Tests/Objective-C++/simple-build-test/foo.mm b/Tests/Objective-C++/simple-build-test/foo.mm
new file mode 100644
index 0000000..2d452a8
--- /dev/null
+++ b/Tests/Objective-C++/simple-build-test/foo.mm
@@ -0,0 +1,7 @@
+#import "foo.h"
+
+@implementation Foo
+
+@synthesize age;
+
+@end
diff --git a/Tests/Objective-C++/simple-build-test/main.mm b/Tests/Objective-C++/simple-build-test/main.mm
new file mode 100644
index 0000000..7c85551
--- /dev/null
+++ b/Tests/Objective-C++/simple-build-test/main.mm
@@ -0,0 +1,14 @@
+#import <Foundation/Foundation.h>
+#import "foo.h"
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+  Foo *theFoo = [[Foo alloc] init];
+  theFoo.age = [NSNumber numberWithInt:argc];
+  NSLog(@"%d\n",[theFoo.age intValue]);
+  std::cout << [theFoo.age intValue] << std::endl;
+  [pool release];
+  return 0;
+}
diff --git a/Tests/Objective-C/c-file-extension-test/CMakeLists.txt b/Tests/Objective-C/c-file-extension-test/CMakeLists.txt
new file mode 100644
index 0000000..e091448
--- /dev/null
+++ b/Tests/Objective-C/c-file-extension-test/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(c-file-extension-test C)
+
+add_executable(c-file-extension-test main.m)
diff --git a/Tests/Objective-C/c-file-extension-test/main.m b/Tests/Objective-C/c-file-extension-test/main.m
new file mode 100644
index 0000000..1c159a9
--- /dev/null
+++ b/Tests/Objective-C/c-file-extension-test/main.m
@@ -0,0 +1,8 @@
+#ifndef __OBJC__
+#  error "Compiler cannot compile Objective-C"
+#endif
+
+int main()
+{
+  return 0;
+}
diff --git a/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt b/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt
new file mode 100644
index 0000000..27e88be
--- /dev/null
+++ b/Tests/Objective-C/objc-file-extension-test/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(objc-file-extension-test OBJC CXX)
+
+add_executable(objc-file-extension-test main.m)
+target_link_libraries(objc-file-extension-test "-framework Foundation")
diff --git a/Tests/Objective-C/objc-file-extension-test/main.m b/Tests/Objective-C/objc-file-extension-test/main.m
new file mode 100644
index 0000000..2ec3917
--- /dev/null
+++ b/Tests/Objective-C/objc-file-extension-test/main.m
@@ -0,0 +1,12 @@
+#ifndef __OBJC__
+#  error "Compiler is not an Objective-C compiler."
+#endif
+
+#import <Foundation/Foundation.h>
+
+int main()
+{
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+  [pool release];
+  return 0;
+}
diff --git a/Tests/Objective-C/simple-build-test/CMakeLists.txt b/Tests/Objective-C/simple-build-test/CMakeLists.txt
new file mode 100644
index 0000000..5ab46ac
--- /dev/null
+++ b/Tests/Objective-C/simple-build-test/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(CMAKE_MACOSX_RPATH OFF)
+
+project(simple-build-test OBJC)
+
+add_library(foo SHARED foo.m)
+target_link_libraries(foo "-framework Foundation")
+
+add_executable(simple-build-test main.m)
+target_link_libraries(simple-build-test "-framework Foundation" foo)
diff --git a/Tests/Objective-C/simple-build-test/foo.h b/Tests/Objective-C/simple-build-test/foo.h
new file mode 100644
index 0000000..b3fb084
--- /dev/null
+++ b/Tests/Objective-C/simple-build-test/foo.h
@@ -0,0 +1,9 @@
+#import <Foundation/Foundation.h>
+
+@interface Foo : NSObject {
+  NSNumber* age;
+}
+
+@property (nonatomic, retain) NSNumber* age;
+
+@end
diff --git a/Tests/Objective-C/simple-build-test/foo.m b/Tests/Objective-C/simple-build-test/foo.m
new file mode 100644
index 0000000..2d452a8
--- /dev/null
+++ b/Tests/Objective-C/simple-build-test/foo.m
@@ -0,0 +1,7 @@
+#import "foo.h"
+
+@implementation Foo
+
+@synthesize age;
+
+@end
diff --git a/Tests/Objective-C/simple-build-test/main.m b/Tests/Objective-C/simple-build-test/main.m
new file mode 100644
index 0000000..970d554
--- /dev/null
+++ b/Tests/Objective-C/simple-build-test/main.m
@@ -0,0 +1,12 @@
+#import <Foundation/Foundation.h>
+#import "foo.h"
+
+int main(int argc, char **argv)
+{
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+  Foo *theFoo = [[Foo alloc] init];
+  theFoo.age = [NSNumber numberWithInt:argc];
+  NSLog(@"%d\n",[theFoo.age intValue]);
+  [pool release];
+  return 0;
+}
diff --git a/Tests/PDBDirectoryAndName/CMakeLists.txt b/Tests/PDBDirectoryAndName/CMakeLists.txt
index 6d8d6cc..44194ca 100644
--- a/Tests/PDBDirectoryAndName/CMakeLists.txt
+++ b/Tests/PDBDirectoryAndName/CMakeLists.txt
@@ -3,8 +3,8 @@
 project(PDBDirectoryAndName C)
 
 # Make sure the proper compiler is in use.
-if(NOT MSVC AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
-  message(FATAL_ERROR "The PDBDirectoryAndName test works only with MSVC or Intel")
+if(NOT MSVC AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel" AND NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+  message(FATAL_ERROR "The PDBDirectoryAndName test works only with MSVC, Clang or Intel")
 endif()
 
 # Intel 11.1 does not support /Fd but Intel 14.0 does.
diff --git a/Tests/Plugin/CMakeLists.txt b/Tests/Plugin/CMakeLists.txt
index 8e8fa07..729bba3 100644
--- a/Tests/Plugin/CMakeLists.txt
+++ b/Tests/Plugin/CMakeLists.txt
@@ -10,14 +10,6 @@
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${Plugin_BINARY_DIR}/lib/plugin)
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${Plugin_BINARY_DIR}/lib/static)
 
-# We need the dynamic loader support from KWSys to load the plugin in
-# the executable.
-set(KWSYS_NAMESPACE kwsys)
-set(KWSYS_HEADER_ROOT ${Plugin_BINARY_DIR}/include)
-set(KWSYS_USE_DynamicLoader 1)
-set(KWSYS_ENCODING_DEFAULT_CODEPAGE CP_UTF8)
-add_subdirectory(${Plugin_SOURCE_DIR}/../../Source/kwsys src/kwsys)
-
 # Configure the location of plugins.
 configure_file(${Plugin_SOURCE_DIR}/src/example_exe.h.in
                ${Plugin_BINARY_DIR}/include/example_exe.h @ONLY)
@@ -36,14 +28,14 @@
 endif()
 
 # Create an executable that exports an API for use by plugins.
-add_executable(example_exe src/example_exe.cxx)
+add_executable(example_exe src/example_exe.cxx src/DynamicLoader.cxx)
 set_target_properties(example_exe PROPERTIES
   ENABLE_EXPORTS 1
   OUTPUT_NAME example
   # Test placing exe import library in unique directory.
   ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/exe
   )
-target_link_libraries(example_exe kwsys)
+target_link_libraries(example_exe ${CMAKE_DL_LIBS})
 
 # Create a plugin that uses the API provided by the executable.
 # This module "links" to the executable to use the symbols.
diff --git a/Tests/Plugin/include/DynamicLoader.hxx b/Tests/Plugin/include/DynamicLoader.hxx
new file mode 100644
index 0000000..20b37de
--- /dev/null
+++ b/Tests/Plugin/include/DynamicLoader.hxx
@@ -0,0 +1,49 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.
+   See https://cmake.org/licensing#kwsys for details.  */
+#ifndef DynamicLoader_hxx
+#define DynamicLoader_hxx
+
+#include <string>
+
+#if defined(__hpux)
+#  include <dl.h>
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+#  include <windows.h>
+#elif defined(__APPLE__)
+#  include <AvailabilityMacros.h>
+#  if MAC_OS_X_VERSION_MAX_ALLOWED < 1030
+#    include <mach-o/dyld.h>
+#  endif
+#elif defined(__BEOS__)
+#  include <be/kernel/image.h>
+#endif
+
+class DynamicLoader
+{
+public:
+#if defined(__hpux)
+  typedef shl_t LibraryHandle;
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+  typedef HMODULE LibraryHandle;
+#elif defined(__APPLE__)
+#  if MAC_OS_X_VERSION_MAX_ALLOWED < 1030
+  typedef NSModule LibraryHandle;
+#  else
+  typedef void* LibraryHandle;
+#  endif
+#elif defined(__BEOS__)
+  typedef image_id LibraryHandle;
+#else // POSIX
+  typedef void* LibraryHandle;
+#endif
+
+  typedef void (*SymbolPointer)();
+
+  static LibraryHandle OpenLibrary(const std::string&);
+
+  static int CloseLibrary(LibraryHandle);
+
+  static SymbolPointer GetSymbolAddress(LibraryHandle, const std::string&);
+};
+
+#endif
diff --git a/Tests/Plugin/src/DynamicLoader.cxx b/Tests/Plugin/src/DynamicLoader.cxx
new file mode 100644
index 0000000..d4a2637
--- /dev/null
+++ b/Tests/Plugin/src/DynamicLoader.cxx
@@ -0,0 +1,263 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.
+   See https://cmake.org/licensing#kwsys for details.  */
+#if defined(_WIN32)
+#  define NOMINMAX // hide min,max to not conflict with <limits>
+#endif
+
+#include <DynamicLoader.hxx>
+
+#if defined(__hpux)
+#  include <dl.h>
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  return shl_load(libname.c_str(), BIND_DEFERRED | DYNAMIC_PATH, 0L);
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  if (!lib) {
+    return 0;
+  }
+  return !shl_unload(lib);
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  void* addr;
+  int status;
+
+  /* TYPE_PROCEDURE Look for a function or procedure. (This used to be default)
+   * TYPE_DATA      Look for a symbol in the data segment (for example,
+   * variables).
+   * TYPE_UNDEFINED Look for any symbol.
+   */
+  status = shl_findsym(&lib, sym.c_str(), TYPE_UNDEFINED, &addr);
+  void* result = (status < 0) ? (void*)0 : addr;
+
+  // Hack to cast pointer-to-data to pointer-to-function.
+  return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
+}
+
+#elif defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1030)
+#  include <mach-o/dyld.h>
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  NSObjectFileImageReturnCode rc;
+  NSObjectFileImage image = 0;
+
+  rc = NSCreateObjectFileImageFromFile(libname.c_str(), &image);
+  // rc == NSObjectFileImageInappropriateFile when trying to load a dylib file
+  if (rc != NSObjectFileImageSuccess) {
+    return 0;
+  }
+  NSModule handle = NSLinkModule(image, libname.c_str(),
+                                 NSLINKMODULE_OPTION_BINDNOW |
+                                   NSLINKMODULE_OPTION_RETURN_ON_ERROR);
+  NSDestroyObjectFileImage(image);
+  return handle;
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  bool success = NSUnLinkModule(lib, NSUNLINKMODULE_OPTION_NONE);
+  return success;
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  void* result = 0;
+  // Need to prepend symbols with '_' on Apple-gcc compilers
+  std::string rsym = '_' + sym;
+
+  NSSymbol symbol = NSLookupSymbolInModule(lib, rsym.c_str());
+  if (symbol) {
+    result = NSAddressOfSymbol(symbol);
+  }
+
+  // Hack to cast pointer-to-data to pointer-to-function.
+  return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+#  include <windows.h>
+
+#  include <stdio.h>
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  DynamicLoader::LibraryHandle lh;
+  int length = MultiByteToWideChar(CP_UTF8, 0, libname.c_str(), -1, NULL, 0);
+  wchar_t* wchars = new wchar_t[length + 1];
+  wchars[0] = '\0';
+  MultiByteToWideChar(CP_UTF8, 0, libname.c_str(), -1, wchars, length);
+  lh = LoadLibraryW(wchars);
+  delete[] wchars;
+  return lh;
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  return (int)FreeLibrary(lib);
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  void* result;
+#  if defined(__BORLANDC__) || defined(__WATCOMC__)
+  // Need to prepend symbols with '_'
+  std::string ssym = '_' + sym;
+  const char* rsym = ssym.c_str();
+#  else
+  const char* rsym = sym.c_str();
+#  endif
+  result = (void*)GetProcAddress(lib, rsym);
+// Hack to cast pointer-to-data to pointer-to-function.
+#  ifdef __WATCOMC__
+  return *(DynamicLoader::SymbolPointer*)(&result);
+#  else
+  return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
+#  endif
+}
+
+#elif defined(__BEOS__)
+#  include <be/kernel/image.h>
+#  include <be/support/Errors.h>
+
+static image_id last_dynamic_err = B_OK;
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  // image_id's are integers, errors are negative. Add one just in case we
+  //  get a valid image_id of zero (is that even possible?).
+  image_id rc = load_add_on(libname.c_str());
+  if (rc < 0) {
+    last_dynamic_err = rc;
+    return 0;
+  }
+
+  return rc + 1;
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  if (!lib) {
+    last_dynamic_err = B_BAD_VALUE;
+    return 0;
+  } else {
+    // The function dlclose() returns 0 on success, and non-zero on error.
+    status_t rc = unload_add_on(lib - 1);
+    if (rc != B_OK) {
+      last_dynamic_err = rc;
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  // Hack to cast pointer-to-data to pointer-to-function.
+  union
+  {
+    void* pvoid;
+    DynamicLoader::SymbolPointer psym;
+  } result;
+
+  result.psym = NULL;
+
+  if (!lib) {
+    last_dynamic_err = B_BAD_VALUE;
+  } else {
+    // !!! FIXME: BeOS can do function-only lookups...does this ever
+    // !!! FIXME:  actually _want_ a data symbol lookup, or was this union
+    // !!! FIXME:  a leftover of dlsym()? (s/ANY/TEXT for functions only).
+    status_t rc =
+      get_image_symbol(lib - 1, sym.c_str(), B_SYMBOL_TYPE_ANY, &result.pvoid);
+    if (rc != B_OK) {
+      last_dynamic_err = rc;
+      result.psym = NULL;
+    }
+  }
+  return result.psym;
+}
+
+#elif defined(__MINT__)
+#  define _GNU_SOURCE /* for program_invocation_name */
+#  include <dld.h>
+#  include <errno.h>
+#  include <malloc.h>
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  char* name = (char*)calloc(1, libname.size() + 1);
+  dld_init(program_invocation_name);
+  strncpy(name, libname.c_str(), libname.size());
+  dld_link(libname.c_str());
+  return (void*)name;
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  dld_unlink_by_file((char*)lib, 0);
+  free(lib);
+  return 0;
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  // Hack to cast pointer-to-data to pointer-to-function.
+  union
+  {
+    void* pvoid;
+    DynamicLoader::SymbolPointer psym;
+  } result;
+  result.pvoid = dld_get_symbol(sym.c_str());
+  return result.psym;
+}
+
+#else
+#  include <dlfcn.h>
+
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
+  const std::string& libname)
+{
+  return dlopen(libname.c_str(), RTLD_LAZY);
+}
+
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+  if (lib) {
+    // The function dlclose() returns 0 on success, and non-zero on error.
+    return !dlclose(lib);
+  }
+  // else
+  return 0;
+}
+
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+  DynamicLoader::LibraryHandle lib, const std::string& sym)
+{
+  // Hack to cast pointer-to-data to pointer-to-function.
+  union
+  {
+    void* pvoid;
+    DynamicLoader::SymbolPointer psym;
+  } result;
+  result.pvoid = dlsym(lib, sym.c_str());
+  return result.psym;
+}
+
+#endif
diff --git a/Tests/Plugin/src/example_exe.cxx b/Tests/Plugin/src/example_exe.cxx
index 257a35c..bc4c727 100644
--- a/Tests/Plugin/src/example_exe.cxx
+++ b/Tests/Plugin/src/example_exe.cxx
@@ -1,14 +1,12 @@
-#include <kwsys/DynamicLoader.hxx>
-
-#include <example.h>
-
-#include <example_exe.h>
-
 #include <iostream>
 #include <string>
 
+#include <example.h>
+#include <example_exe.h>
 #include <stdio.h>
 
+#include "DynamicLoader.hxx"
+
 // Implement the ABI used by plugins.
 extern "C" int example_exe_function()
 {
@@ -24,20 +22,17 @@
 
 int main()
 {
-  std::string libName = EXAMPLE_EXE_PLUGIN_DIR CONFIG_DIR "/";
-  libName += kwsys::DynamicLoader::LibPrefix();
-  libName += "example_mod_1";
-  libName += kwsys::DynamicLoader::LibExtension();
-  kwsys::DynamicLoader::LibraryHandle handle =
-    kwsys::DynamicLoader::OpenLibrary(libName.c_str());
+  std::string const libName = EXAMPLE_EXE_PLUGIN_DIR CONFIG_DIR
+    "/" EXAMPLE_EXE_MOD_PREFIX "example_mod_1" EXAMPLE_EXE_MOD_SUFFIX;
+  DynamicLoader::LibraryHandle handle = DynamicLoader::OpenLibrary(libName);
   if (!handle) {
     // Leave the .c_str() on this one.  It is needed on OpenWatcom.
     std::cerr << "Could not open plugin \"" << libName.c_str() << "\"!"
               << std::endl;
     return 1;
   }
-  kwsys::DynamicLoader::SymbolPointer sym =
-    kwsys::DynamicLoader::GetSymbolAddress(handle, "example_mod_1_function");
+  DynamicLoader::SymbolPointer sym =
+    DynamicLoader::GetSymbolAddress(handle, "example_mod_1_function");
   if (!sym) {
     std::cerr << "Could not get plugin symbol \"example_mod_1_function\"!"
               << std::endl;
@@ -52,6 +47,6 @@
     std::cerr << "Incorrect return value from plugin!" << std::endl;
     return 1;
   }
-  kwsys::DynamicLoader::CloseLibrary(handle);
+  DynamicLoader::CloseLibrary(handle);
   return 0;
 }
diff --git a/Tests/Plugin/src/example_exe.h.in b/Tests/Plugin/src/example_exe.h.in
index 62f0d9f..af71021 100644
--- a/Tests/Plugin/src/example_exe.h.in
+++ b/Tests/Plugin/src/example_exe.h.in
@@ -2,5 +2,7 @@
 #define example_exe_h
 
 #define EXAMPLE_EXE_PLUGIN_DIR "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@"
+#define EXAMPLE_EXE_MOD_PREFIX "@CMAKE_SHARED_MODULE_PREFIX@"
+#define EXAMPLE_EXE_MOD_SUFFIX "@CMAKE_SHARED_MODULE_SUFFIX@"
 
 #endif
diff --git a/Tests/Plugin/src/example_mod_1.c b/Tests/Plugin/src/example_mod_1.c
index 87b559d..2b740b8 100644
--- a/Tests/Plugin/src/example_mod_1.c
+++ b/Tests/Plugin/src/example_mod_1.c
@@ -1,5 +1,4 @@
 #include <example.h>
-
 #include <stdio.h>
 
 #if defined(_WIN32)
diff --git a/Tests/Preprocess/CMakeLists.txt b/Tests/Preprocess/CMakeLists.txt
index 588af03..bce1b3f 100644
--- a/Tests/Preprocess/CMakeLists.txt
+++ b/Tests/Preprocess/CMakeLists.txt
@@ -29,7 +29,10 @@
   set(PP_VS 1)
 endif()
 if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND
-   "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC" AND
+   "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
+   set(CLANG_MSVC_WINDOWS 1)
+endif()
+if(CLANG_MSVC_WINDOWS AND
    "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
    set(CLANG_GNULIKE_WINDOWS 1)
 endif()
@@ -106,7 +109,7 @@
 set(EXPR_OP1 "/")
 if((NOT MSVC OR PP_NMAKE) AND
    NOT CMAKE_C_COMPILER_ID STREQUAL "Intel" AND
-   NOT CLANG_GNULIKE_WINDOWS)
+   NOT CLANG_MSVC_WINDOWS)
   # MSVC cl, Intel icl: %
   # When the cl compiler is invoked from the command line then % must
   # be written %% (to distinguish from %ENV% syntax).  However cl does
diff --git a/Tests/Qt4Targets/main.cpp b/Tests/Qt4Targets/main.cpp
index f8eacdc..fc7f580 100644
--- a/Tests/Qt4Targets/main.cpp
+++ b/Tests/Qt4Targets/main.cpp
@@ -1,8 +1,7 @@
 
 #include <QApplication>
-#include <QWidget>
-
 #include <QString>
+#include <QWidget>
 
 #ifndef QT_CORE_LIB
 #  error Expected QT_CORE_LIB
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp
index e498969..c7c6863 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.cpp
@@ -1,5 +1,6 @@
 
 #include "a_qt.hpp"
+
 #include <a_mc.hpp>
 
 namespace a_qt {
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp
index e2387ee..fafc8a2 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/a_qt.hpp
@@ -1,10 +1,12 @@
 #ifndef A_QT_HPP
 #define A_QT_HPP
 
-#include <QObject>
-#include <config.hpp>
 #include <string>
 
+#include <config.hpp>
+
+#include <QObject>
+
 namespace a_qt {
 
 /// @brief A header local QObject based class
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp
index f72f6ca..c5adaeb 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.cpp
@@ -1,5 +1,6 @@
 
 #include "b_qt.hpp"
+
 #include <b_mc.hpp>
 
 namespace b_qt {
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp
index d7f0311..b798e71 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/b_qt.hpp
@@ -1,10 +1,12 @@
 #ifndef B_QT_HPP
 #define B_QT_HPP
 
-#include <QObject>
-#include <config.hpp>
 #include <string>
 
+#include <config.hpp>
+
+#include <QObject>
+
 namespace b_qt {
 
 /// @brief A header local QObject based class
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp b/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp
index a3425f1..3fb6c70 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/main.cpp
@@ -1,7 +1,8 @@
 
+#include <string>
+
 #include <a_qt.hpp>
 #include <b_qt.hpp>
-#include <string>
 
 int main()
 {
diff --git a/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp
index 408335b..55457d2 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp
+++ b/Tests/QtAutogen/AutogenOriginDependsOn/testGenLib.hpp
@@ -1,9 +1,10 @@
 #ifndef TEST3_HPP
 #define TEST3_HPP
 
-#include "simpleLib.hpp"
 #include <QObject>
 
+#include "simpleLib.hpp"
+
 // This object triggers the AUTOMOC on this file
 class LObject : public QObject
 {
diff --git a/Tests/QtAutogen/Complex/Adir/libA.h b/Tests/QtAutogen/Complex/Adir/libA.h
index c4eb9f7..a8ca209 100644
--- a/Tests/QtAutogen/Complex/Adir/libA.h
+++ b/Tests/QtAutogen/Complex/Adir/libA.h
@@ -2,10 +2,10 @@
 #ifndef LIBA_H
 #define LIBA_H
 
-#include "liba_export.h"
-
 #include <QObject>
 
+#include "liba_export.h"
+
 class LIBA_EXPORT LibA : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/Complex/Bdir/libB.h b/Tests/QtAutogen/Complex/Bdir/libB.h
index e4ab788..38dae87 100644
--- a/Tests/QtAutogen/Complex/Bdir/libB.h
+++ b/Tests/QtAutogen/Complex/Bdir/libB.h
@@ -2,10 +2,10 @@
 #ifndef LIBB_H
 #define LIBB_H
 
-#include "libb_export.h"
+#include <QObject>
 
 #include "libA.h"
-#include <QObject>
+#include "libb_export.h"
 
 class LIBB_EXPORT LibB : public QObject
 {
diff --git a/Tests/QtAutogen/Complex/abc.cpp b/Tests/QtAutogen/Complex/abc.cpp
index 2929b92..4c7dc52 100644
--- a/Tests/QtAutogen/Complex/abc.cpp
+++ b/Tests/QtAutogen/Complex/abc.cpp
@@ -1,10 +1,11 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "abc.h"
-#include "abc_p.h"
 
 #include <stdio.h>
 
+#include "abc_p.h"
+
 class PrintAbc : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/Complex/abc_p.h b/Tests/QtAutogen/Complex/abc_p.h
index be98487..1f6102c 100644
--- a/Tests/QtAutogen/Complex/abc_p.h
+++ b/Tests/QtAutogen/Complex/abc_p.h
@@ -3,10 +3,10 @@
 #ifndef ABC_P_H
 #define ABC_P_H
 
-#include <QObject>
-
 #include <stdio.h>
 
+#include <QObject>
+
 class AbcP : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/Complex/calwidget.cpp b/Tests/QtAutogen/Complex/calwidget.cpp
index 8ce53f1..f58b182 100644
--- a/Tests/QtAutogen/Complex/calwidget.cpp
+++ b/Tests/QtAutogen/Complex/calwidget.cpp
@@ -38,6 +38,8 @@
  **
  ****************************************************************************/
 
+#include "calwidget.h"
+
 #include <QCalendarWidget>
 #include <QCheckBox>
 #include <QComboBox>
@@ -47,8 +49,6 @@
 #include <QLabel>
 #include <QTextCharFormat>
 
-#include "calwidget.h"
-
 #include "ui_calwidget.h"
 #ifdef UI_CALWIDGET_H
 #  error Definition of UI_CALWIDGET_H should be disabled by file option.
diff --git a/Tests/QtAutogen/Complex/codeeditor.cpp b/Tests/QtAutogen/Complex/codeeditor.cpp
index 0caf8a7..bb6f405 100644
--- a/Tests/QtAutogen/Complex/codeeditor.cpp
+++ b/Tests/QtAutogen/Complex/codeeditor.cpp
@@ -38,10 +38,10 @@
  **
  ****************************************************************************/
 
-#include <QtGui>
-
 #include "codeeditor.h"
 
+#include <QtGui>
+
 CodeEditor::CodeEditor(QWidget* parent)
   : QPlainTextEdit(parent)
 {
diff --git a/Tests/QtAutogen/Complex/debug_class.cpp b/Tests/QtAutogen/Complex/debug_class.cpp
index 46b09e7..3aaf79a 100644
--- a/Tests/QtAutogen/Complex/debug_class.cpp
+++ b/Tests/QtAutogen/Complex/debug_class.cpp
@@ -1,5 +1,6 @@
 
 #include "debug_class.h"
+
 #include "ui_debug_class.h"
 
 DebugClass::DebugClass(QWidget* parent)
diff --git a/Tests/QtAutogen/Complex/libC.h b/Tests/QtAutogen/Complex/libC.h
index 3bc2bad..51ea48d 100644
--- a/Tests/QtAutogen/Complex/libC.h
+++ b/Tests/QtAutogen/Complex/libC.h
@@ -2,10 +2,10 @@
 #ifndef LIBC_H
 #define LIBC_H
 
-#include "libc_export.h"
+#include <QObject>
 
 #include "libB.h"
-#include <QObject>
+#include "libc_export.h"
 
 class LIBC_EXPORT LibC : public QObject
 {
diff --git a/Tests/QtAutogen/Complex/main.cpp b/Tests/QtAutogen/Complex/main.cpp
index b5b6ed1..12f649f 100644
--- a/Tests/QtAutogen/Complex/main.cpp
+++ b/Tests/QtAutogen/Complex/main.cpp
@@ -52,8 +52,9 @@
 #include "xyz.h"
 #include "yaf.h"
 #ifdef TEST_DEBUG_CLASS
-#  include "debug_class.h"
 #  include <iostream>
+
+#  include "debug_class.h"
 #endif
 
 int main(int argv, char** args)
diff --git a/Tests/QtAutogen/Complex/second_widget.cpp b/Tests/QtAutogen/Complex/second_widget.cpp
index c575f10..9f51a80 100644
--- a/Tests/QtAutogen/Complex/second_widget.cpp
+++ b/Tests/QtAutogen/Complex/second_widget.cpp
@@ -1,5 +1,6 @@
 
 #include "second_widget.h"
+
 #include "ui_second_widget.h"
 
 SecondWidget::SecondWidget(QWidget* parent)
diff --git a/Tests/QtAutogen/Complex/yaf.cpp b/Tests/QtAutogen/Complex/yaf.cpp
index 70e26aa..10448c1 100644
--- a/Tests/QtAutogen/Complex/yaf.cpp
+++ b/Tests/QtAutogen/Complex/yaf.cpp
@@ -1,10 +1,11 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "yaf.h"
-#include "yaf_p.h"
 
 #include <stdio.h>
 
+#include "yaf_p.h"
+
 Yaf::Yaf()
 {
 }
diff --git a/Tests/QtAutogen/Complex/yaf_p.h b/Tests/QtAutogen/Complex/yaf_p.h
index ea5eed6..48fdd30 100644
--- a/Tests/QtAutogen/Complex/yaf_p.h
+++ b/Tests/QtAutogen/Complex/yaf_p.h
@@ -3,10 +3,10 @@
 #ifndef YAF_P_H
 #define YAF_P_H
 
-#include <QObject>
-
 #include <stdio.h>
 
+#include <QObject>
+
 class YafP : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/LowMinimumVersion/item.cpp b/Tests/QtAutogen/LowMinimumVersion/item.cpp
index e2f19b2..511a1ca 100644
--- a/Tests/QtAutogen/LowMinimumVersion/item.cpp
+++ b/Tests/QtAutogen/LowMinimumVersion/item.cpp
@@ -1,4 +1,5 @@
 #include "item.hpp"
+
 #include <ui_view.h>
 
 class MocLocal : public QObject
diff --git a/Tests/QtAutogen/MacOsFW/test/testMacosFWLib.cpp b/Tests/QtAutogen/MacOsFW/test/testMacosFWLib.cpp
index 3476d61..edc9376 100644
--- a/Tests/QtAutogen/MacOsFW/test/testMacosFWLib.cpp
+++ b/Tests/QtAutogen/MacOsFW/test/testMacosFWLib.cpp
@@ -1,8 +1,9 @@
+#include "testMacosFWLib.h"
+
 #include <QObject>
 #include <QString>
 
 #include "macos_fw_lib.h"
-#include "testMacosFWLib.h"
 
 class TestMacosFWLib : public QObject
 {
diff --git a/Tests/QtAutogen/MocCMP0071/Obj.cpp b/Tests/QtAutogen/MocCMP0071/Obj.cpp
index 1ae50ed..25a291d 100644
--- a/Tests/QtAutogen/MocCMP0071/Obj.cpp
+++ b/Tests/QtAutogen/MocCMP0071/Obj.cpp
@@ -1,4 +1,5 @@
 #include "Obj.hpp"
+
 #include "Obj_p.h"
 
 ObjPrivate::ObjPrivate()
diff --git a/Tests/QtAutogen/MocInclude/CMakeLists.txt b/Tests/QtAutogen/MocInclude/CMakeLists.txt
new file mode 100644
index 0000000..04c8baf
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/CMakeLists.txt
@@ -0,0 +1,112 @@
+cmake_minimum_required(VERSION 3.15)
+project(MocInclude)
+get_filename_component(CS_REAL ${CMAKE_CURRENT_SOURCE_DIR} REALPATH)
+include("${CS_REAL}/../AutogenCoreTest.cmake")
+
+# Test moc include patterns
+
+set(COM_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Common")
+
+macro(addCopyCommand from to)
+    add_custom_command(
+      OUTPUT ${to}
+      COMMAND ${CMAKE_COMMAND} -E copy ${from} ${to}
+      DEPENDS ${from})
+endmacro()
+
+# Create an executable
+function(makeExecutable TARGET_NAME)
+    # Utility variables
+    set(CB_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+
+    # Copy directory
+    file(REMOVE_RECURSE "${CB_DIR}/InIncludes")
+    file(COPY "${COM_DIR}/InIncludes.in" DESTINATION "${CB_DIR}")
+    file(RENAME "${CB_DIR}/InIncludes.in" "${CB_DIR}/InIncludes")
+
+    # Generate .moc file from the header externally and
+    # enabled SKIP_AUTOMOC on the source file
+    qtx_wrap_cpp(ExternDotMOC ${COM_DIR}/ExternDot.hpp OPTIONS "-p" "./")
+    addCopyCommand(${ExternDotMOC}
+                   ${CB_DIR}/ExternDot.moc)
+    set_property(
+      SOURCE ${COM_DIR}/ExternDot.cpp
+      PROPERTY SKIP_AUTOMOC ON)
+
+    # Generate .moc file from the GENERATED header externally
+    # and enabled SKIP_AUTOMOC on the source file
+    addCopyCommand(${COM_DIR}/ExternDotGenerated.hpp.in
+                   ${CB_DIR}/ExternDotGenerated.hpp)
+    addCopyCommand(${COM_DIR}/ExternDotGenerated.cpp.in
+                   ${CB_DIR}/ExternDotGenerated.cpp)
+    qtx_wrap_cpp(ExternDotGeneratedMOC
+                 ${CB_DIR}/ExternDotGenerated.hpp
+                 OPTIONS "-p" "./")
+    addCopyCommand(${ExternDotGeneratedMOC}
+                   ${CB_DIR}/ExternDotGenerated.moc)
+    set_property(
+      SOURCE ${CB_DIR}/ExternDotGenerated.cpp
+      PROPERTY SKIP_AUTOMOC ON)
+
+    # Generate header moc file externally with a custom name
+    # and enabled SKIP_AUTOMOC on the header
+    qtx_wrap_cpp(MixedCustomMOC
+                 ${COM_DIR}/MixedCustom.hpp
+                 OPTIONS "-p" "./")
+    addCopyCommand(${MixedCustomMOC}
+                   ${CB_DIR}/MixedCustom_extMoc.cpp)
+    set_property(
+      SOURCE ${COM_DIR}/MixedCustom.hpp
+      PROPERTY SKIP_AUTOMOC ON)
+    # Custom target to depend on
+    add_custom_target("${TARGET_NAME}_MixedCustom"
+      DEPENDS ${CB_DIR}/MixedCustom_extMoc.cpp
+      BYPRODUCTS ${CB_DIR}/moc_MixedCustom.cpp
+      COMMAND ${CMAKE_COMMAND} -E copy
+        ${COM_DIR}/moc_MixedCustom.cpp.in
+        ${CB_DIR}/moc_MixedCustom.cpp)
+
+    add_executable(${TARGET_NAME}
+      # Test own "*.moc" and "moc_*.cpp" includes
+      ${COM_DIR}/None.cpp
+      ${COM_DIR}/OwnDot.cpp
+      ${COM_DIR}/OwnUnderscore.cpp
+      ${COM_DIR}/OwnDotUnderscore.cpp
+
+      # Test "moc_*.cpp" includes of other files
+      ${COM_DIR}/OtherUnderscore.cpp
+      ${COM_DIR}/OtherUnderscoreExtra.cpp
+      ${COM_DIR}/OtherUnderscoreSub.cpp
+      ${COM_DIR}/OtherUnderscoreSubDir/SubExtra.cpp
+
+      # Test relative ../../ path for moc includes
+      ${COM_DIR}/DualSub/Second/Second.cpp
+      ${COM_DIR}/DualSubMocked.cpp
+
+      # Test externally generated moc files
+      ${COM_DIR}/ExternDot.cpp
+      ${CB_DIR}/ExternDot.moc
+
+      # Test externally generated moc files for GENERATED source
+      ${CB_DIR}/ExternDotGenerated.cpp
+      ${CB_DIR}/ExternDotGenerated.moc
+
+      # Test externally generated moc files and SKIP_AUTOMOC enabled header
+      ${COM_DIR}/MixedSkipped.cpp
+      ${COM_DIR}/MixedCustom.hpp
+      ${COM_DIR}/MixedCustom.cpp
+
+      # Test sources in a subdirectory
+      ${CB_DIR}/InIncludes/SubOwnDot.cpp
+      ${COM_DIR}/InIncludesMoc.cpp
+    )
+    add_dependencies(${TARGET_NAME} "${TARGET_NAME}_MixedCustom")
+    target_include_directories(${TARGET_NAME} PRIVATE "${COM_DIR}")
+    target_include_directories(${TARGET_NAME} PRIVATE "${CB_DIR}")
+    target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+    target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES})
+    set_target_properties(${TARGET_NAME} PROPERTIES AUTOMOC ON)
+endfunction()
+
+add_subdirectory(Strict)
+add_subdirectory(Relaxed)
diff --git a/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.cpp b/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.cpp
new file mode 100644
index 0000000..453add1
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.cpp
@@ -0,0 +1,11 @@
+#include "Second.hpp"
+
+Second::Second()
+{
+}
+
+Second::~Second()
+{
+}
+
+#include "../../moc_DualSubMocked.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.hpp b/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.hpp
new file mode 100644
index 0000000..e1f3eac
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/DualSub/Second/Second.hpp
@@ -0,0 +1,14 @@
+#ifndef Second_HPP
+#define Second_HPP
+
+#include <QObject>
+
+class Second : public QObject
+{
+  Q_OBJECT
+public:
+  Second();
+  ~Second();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/DualSubMocked.cpp b/Tests/QtAutogen/MocInclude/Common/DualSubMocked.cpp
new file mode 100644
index 0000000..1d4658d
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/DualSubMocked.cpp
@@ -0,0 +1,9 @@
+#include "DualSubMocked.hpp"
+
+DualSubMocked::DualSubMocked()
+{
+}
+
+DualSubMocked::~DualSubMocked()
+{
+}
diff --git a/Tests/QtAutogen/MocInclude/Common/DualSubMocked.hpp b/Tests/QtAutogen/MocInclude/Common/DualSubMocked.hpp
new file mode 100644
index 0000000..58cb571
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/DualSubMocked.hpp
@@ -0,0 +1,15 @@
+#ifndef DualSubMocked_HPP
+#define DualSubMocked_HPP
+
+#include <QObject>
+
+// Header moc file is included by DualSub/Second/Second.cpp
+class DualSubMocked : public QObject
+{
+  Q_OBJECT
+public:
+  DualSubMocked();
+  ~DualSubMocked();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/ExternDot.cpp b/Tests/QtAutogen/MocInclude/Common/ExternDot.cpp
new file mode 100644
index 0000000..2495aa7
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/ExternDot.cpp
@@ -0,0 +1,11 @@
+#include "ExternDot.hpp"
+
+ExternDot::ExternDot()
+{
+}
+
+ExternDot::~ExternDot()
+{
+}
+
+#include "ExternDot.moc"
diff --git a/Tests/QtAutogen/MocInclude/Common/ExternDot.hpp b/Tests/QtAutogen/MocInclude/Common/ExternDot.hpp
new file mode 100644
index 0000000..7eaab2a
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/ExternDot.hpp
@@ -0,0 +1,15 @@
+#ifndef ExternDot_HPP
+#define ExternDot_HPP
+
+#include <QObject>
+
+// Object source includes externally generated .moc file
+class ExternDot : public QObject
+{
+  Q_OBJECT
+public:
+  ExternDot();
+  ~ExternDot();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.cpp.in b/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.cpp.in
new file mode 100644
index 0000000..09ce5cd
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.cpp.in
@@ -0,0 +1,11 @@
+#include "ExternDotGenerated.hpp"
+
+ExternDotGenerated::ExternDotGenerated()
+{
+}
+
+ExternDotGenerated::~ExternDotGenerated()
+{
+}
+
+#include "ExternDotGenerated.moc"
diff --git a/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.hpp.in b/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.hpp.in
new file mode 100644
index 0000000..21c69be
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/ExternDotGenerated.hpp.in
@@ -0,0 +1,15 @@
+#ifndef ExternDotGenerated_HPP
+#define ExternDotGenerated_HPP
+
+#include <QObject>
+
+// GENERATED Object source includes externally generated .moc file
+class ExternDotGenerated : public QObject
+{
+  Q_OBJECT
+public:
+  ExternDotGenerated();
+  ~ExternDotGenerated();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.cpp b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.cpp
new file mode 100644
index 0000000..275754d
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.cpp
@@ -0,0 +1,44 @@
+#include "SubOwnDot.hpp"
+
+#include "SubOwnDot_p.hpp"
+
+namespace InIncludes {
+
+class SubOwnDotLocal : public QObject
+{
+  Q_OBJECT
+public:
+  SubOwnDotLocal();
+  ~SubOwnDotLocal();
+};
+
+SubOwnDotLocal::SubOwnDotLocal()
+{
+}
+
+SubOwnDotLocal::~SubOwnDotLocal()
+{
+}
+
+SubOwnDotPrivate::SubOwnDotPrivate()
+{
+}
+
+SubOwnDotPrivate::~SubOwnDotPrivate()
+{
+}
+
+SubOwnDot::SubOwnDot()
+{
+  SubOwnDotPrivate privateObj;
+  SubOwnDotLocal localObj;
+}
+
+SubOwnDot::~SubOwnDot()
+{
+}
+
+} // End of namespace
+
+// For the local QObject
+#include "SubOwnDot.moc"
diff --git a/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.hpp b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.hpp
new file mode 100644
index 0000000..038ddfa
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot.hpp
@@ -0,0 +1,17 @@
+#ifndef InIncludes_SubOwnDot_HPP
+#define InIncludes_SubOwnDot_HPP
+
+#include <QObject>
+
+namespace InIncludes {
+
+class SubOwnDot : public QObject
+{
+  Q_OBJECT
+public:
+  SubOwnDot();
+  ~SubOwnDot();
+};
+}
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot_p.hpp b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot_p.hpp
new file mode 100644
index 0000000..626a9a8
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/InIncludes.in/SubOwnDot_p.hpp
@@ -0,0 +1,18 @@
+#ifndef InIncludes_SubOwnDot_P_HPP
+#define InIncludes_SubOwnDot_P_HPP
+
+#include <QObject>
+
+namespace InIncludes {
+
+class SubOwnDotPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  SubOwnDotPrivate();
+  ~SubOwnDotPrivate();
+};
+
+} // End of namespace
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/InIncludesMoc.cpp b/Tests/QtAutogen/MocInclude/Common/InIncludesMoc.cpp
new file mode 100644
index 0000000..88f53a4
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/InIncludesMoc.cpp
@@ -0,0 +1,4 @@
+
+// Moc a header that is not in the sources but in a directory that
+// is in the list of include directories.
+#include "InIncludes/moc_SubOwnDot.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/MixedCustom.cpp b/Tests/QtAutogen/MocInclude/Common/MixedCustom.cpp
new file mode 100644
index 0000000..557cc62
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/MixedCustom.cpp
@@ -0,0 +1,32 @@
+#include "MixedCustom.hpp"
+
+class MixedCustomLocal : public QObject
+{
+  Q_OBJECT
+
+public:
+  MixedCustomLocal();
+  ~MixedCustomLocal();
+};
+
+MixedCustomLocal::MixedCustomLocal()
+{
+}
+
+MixedCustomLocal::~MixedCustomLocal()
+{
+}
+
+MixedCustom::MixedCustom()
+{
+  MixedCustomLocal local;
+}
+
+MixedCustom::~MixedCustom()
+{
+}
+
+// AUTOMOC generated source moc
+#include "MixedCustom.moc"
+// Externally generated header moc
+#include "MixedCustom_extMoc.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/MixedCustom.hpp b/Tests/QtAutogen/MocInclude/Common/MixedCustom.hpp
new file mode 100644
index 0000000..6e8ff88
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/MixedCustom.hpp
@@ -0,0 +1,20 @@
+#ifndef MixedCustom_HPP
+#define MixedCustom_HPP
+
+#include <QObject>
+
+// Object source includes
+// - externally generated header moc file
+// - AUTOMOC generated source .moc file
+class MixedCustom : public QObject
+{
+  Q_OBJECT
+public:
+  MixedCustom();
+  ~MixedCustom();
+};
+
+// Function forward declaration
+void moc_MixedCustom(MixedCustom const& arg);
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/MixedSkipped.cpp b/Tests/QtAutogen/MocInclude/Common/MixedSkipped.cpp
new file mode 100644
index 0000000..6919ebc
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/MixedSkipped.cpp
@@ -0,0 +1,40 @@
+#include "MixedSkipped.hpp"
+
+#include "MixedCustom.hpp"
+
+class MixedSkippedLocal : public QObject
+{
+  Q_OBJECT
+
+public:
+  MixedSkippedLocal();
+  ~MixedSkippedLocal();
+};
+
+MixedSkippedLocal::MixedSkippedLocal()
+{
+}
+
+MixedSkippedLocal::~MixedSkippedLocal()
+{
+}
+
+MixedSkipped::MixedSkipped()
+{
+  MixedSkippedLocal local;
+  MixedCustom externCutom;
+  // Call moc named function
+  moc_MixedCustom(externCutom);
+}
+
+MixedSkipped::~MixedSkipped()
+{
+}
+
+// Include AUTOMOC generated moc files
+#include "MixedSkipped.moc"
+#include "moc_MixedSkipped.cpp"
+
+// Include externally generated moc_ named file that is not a moc file
+// and for which the relevant header is SKIP_AUTOMOC enabled
+#include "moc_MixedCustom.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/MixedSkipped.hpp b/Tests/QtAutogen/MocInclude/Common/MixedSkipped.hpp
new file mode 100644
index 0000000..5f6c664
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/MixedSkipped.hpp
@@ -0,0 +1,17 @@
+#ifndef MixedSkipped_HPP
+#define MixedSkipped_HPP
+
+#include <QObject>
+
+// Object source includes
+// - Own moc_ and .moc files.
+// - externally generated moc_ file from a SKIP_AUTOMOC enabled header
+class MixedSkipped : public QObject
+{
+  Q_OBJECT
+public:
+  MixedSkipped();
+  ~MixedSkipped();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/None.cpp b/Tests/QtAutogen/MocInclude/Common/None.cpp
new file mode 100644
index 0000000..286ddb6
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/None.cpp
@@ -0,0 +1,21 @@
+#include "None.hpp"
+
+#include "None_p.h"
+
+NonePrivate::NonePrivate()
+{
+}
+
+NonePrivate::~NonePrivate()
+{
+}
+
+None::None()
+  : d(new NonePrivate)
+{
+}
+
+None::~None()
+{
+  delete d;
+}
diff --git a/Tests/QtAutogen/MocInclude/Common/None.hpp b/Tests/QtAutogen/MocInclude/Common/None.hpp
new file mode 100644
index 0000000..ca0713e
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/None.hpp
@@ -0,0 +1,19 @@
+#ifndef None_HPP
+#define None_HPP
+
+#include <QObject>
+
+// Object source comes without any _moc/.moc includes
+class NonePrivate;
+class None : public QObject
+{
+  Q_OBJECT
+public:
+  None();
+  ~None();
+
+private:
+  NonePrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/None_p.h b/Tests/QtAutogen/MocInclude/Common/None_p.h
new file mode 100644
index 0000000..e209aeb
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/None_p.h
@@ -0,0 +1,14 @@
+#ifndef None_P_HPP
+#define None_P_HPP
+
+#include <QObject>
+
+class NonePrivate : public QObject
+{
+  Q_OBJECT
+public:
+  NonePrivate();
+  ~NonePrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.cpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.cpp
new file mode 100644
index 0000000..df1c428
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.cpp
@@ -0,0 +1,45 @@
+#include "OtherUnderscore.hpp"
+
+#include "OtherUnderscoreExtra.hpp"
+#include "OtherUnderscore_p.hpp"
+
+class OtherUnderscoreLocal : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreLocal();
+  ~OtherUnderscoreLocal();
+};
+
+OtherUnderscoreLocal::OtherUnderscoreLocal()
+{
+}
+
+OtherUnderscoreLocal::~OtherUnderscoreLocal()
+{
+}
+
+OtherUnderscorePrivate::OtherUnderscorePrivate()
+{
+  OtherUnderscoreLocal localObj;
+  OtherUnderscoreExtra extraObj;
+}
+
+OtherUnderscorePrivate::~OtherUnderscorePrivate()
+{
+}
+
+OtherUnderscore::OtherUnderscore()
+  : d(new OtherUnderscorePrivate)
+{
+}
+
+OtherUnderscore::~OtherUnderscore()
+{
+  delete d;
+}
+
+// For OtherUnderscoreLocal
+#include "OtherUnderscore.moc"
+// - Not the own header
+#include "moc_OtherUnderscoreExtra.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.hpp
new file mode 100644
index 0000000..a4ff603
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore.hpp
@@ -0,0 +1,19 @@
+#ifndef OtherUnderscore_HPP
+#define OtherUnderscore_HPP
+
+#include <QObject>
+
+// Sources includes a moc_ includes of an extra object
+class OtherUnderscorePrivate;
+class OtherUnderscore : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscore();
+  ~OtherUnderscore();
+
+private:
+  OtherUnderscorePrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.cpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.cpp
new file mode 100644
index 0000000..11ebd81
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.cpp
@@ -0,0 +1,21 @@
+#include "OtherUnderscoreExtra.hpp"
+
+#include "OtherUnderscoreExtra_p.hpp"
+
+OtherUnderscoreExtraPrivate::OtherUnderscoreExtraPrivate()
+{
+}
+
+OtherUnderscoreExtraPrivate::~OtherUnderscoreExtraPrivate()
+{
+}
+
+OtherUnderscoreExtra::OtherUnderscoreExtra()
+  : d(new OtherUnderscoreExtraPrivate)
+{
+}
+
+OtherUnderscoreExtra::~OtherUnderscoreExtra()
+{
+  delete d;
+}
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.hpp
new file mode 100644
index 0000000..5afe48c
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra.hpp
@@ -0,0 +1,18 @@
+#ifndef OtherUnderscoreEXTRA_HPP
+#define OtherUnderscoreEXTRA_HPP
+
+#include <QObject>
+
+class OtherUnderscoreExtraPrivate;
+class OtherUnderscoreExtra : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreExtra();
+  ~OtherUnderscoreExtra();
+
+private:
+  OtherUnderscoreExtraPrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra_p.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra_p.hpp
new file mode 100644
index 0000000..2066ac3
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreExtra_p.hpp
@@ -0,0 +1,14 @@
+#ifndef OtherUnderscoreEXTRA_P_HPP
+#define OtherUnderscoreEXTRA_P_HPP
+
+#include <QObject>
+
+class OtherUnderscoreExtraPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreExtraPrivate();
+  ~OtherUnderscoreExtraPrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.cpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.cpp
new file mode 100644
index 0000000..712c540
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.cpp
@@ -0,0 +1,46 @@
+#include "OtherUnderscoreSub.hpp"
+
+#include "OtherUnderscoreSubDir/SubExtra.hpp"
+#include "OtherUnderscoreSub_p.hpp"
+
+class OtherUnderscoreSubLocal : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreSubLocal();
+  ~OtherUnderscoreSubLocal();
+};
+
+OtherUnderscoreSubLocal::OtherUnderscoreSubLocal()
+{
+}
+
+OtherUnderscoreSubLocal::~OtherUnderscoreSubLocal()
+{
+}
+
+OtherUnderscoreSubPrivate::OtherUnderscoreSubPrivate()
+{
+  OtherUnderscoreSubLocal localObj;
+  SubExtra extraObj;
+}
+
+OtherUnderscoreSubPrivate::~OtherUnderscoreSubPrivate()
+{
+}
+
+OtherUnderscoreSub::OtherUnderscoreSub()
+  : d(new OtherUnderscoreSubPrivate)
+{
+}
+
+OtherUnderscoreSub::~OtherUnderscoreSub()
+{
+  delete d;
+}
+
+// For OtherUnderscoreSubLocal
+#include "OtherUnderscoreSub.moc"
+// - Not the own header
+// - in a subdirectory
+#include "OtherUnderscoreSubDir/moc_SubExtra.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.hpp
new file mode 100644
index 0000000..7feaa46
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub.hpp
@@ -0,0 +1,19 @@
+#ifndef OtherUnderscoreSub_HPP
+#define OtherUnderscoreSub_HPP
+
+#include <QObject>
+
+// Sources includes a moc_ includes of an extra object in a subdirectory
+class OtherUnderscoreSubPrivate;
+class OtherUnderscoreSub : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreSub();
+  ~OtherUnderscoreSub();
+
+private:
+  OtherUnderscoreSubPrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.cpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.cpp
new file mode 100644
index 0000000..22501e4
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.cpp
@@ -0,0 +1,21 @@
+#include "SubExtra.hpp"
+
+#include "SubExtra_p.hpp"
+
+SubExtraPrivate::SubExtraPrivate()
+{
+}
+
+SubExtraPrivate::~SubExtraPrivate()
+{
+}
+
+SubExtra::SubExtra()
+  : d(new SubExtraPrivate)
+{
+}
+
+SubExtra::~SubExtra()
+{
+  delete d;
+}
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.hpp
new file mode 100644
index 0000000..5700634
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra.hpp
@@ -0,0 +1,18 @@
+#ifndef SubExtra_HPP
+#define SubExtra_HPP
+
+#include <QObject>
+
+class SubExtraPrivate;
+class SubExtra : public QObject
+{
+  Q_OBJECT
+public:
+  SubExtra();
+  ~SubExtra();
+
+private:
+  SubExtraPrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra_p.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra_p.hpp
new file mode 100644
index 0000000..5a14a2d
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSubDir/SubExtra_p.hpp
@@ -0,0 +1,14 @@
+#ifndef SubExtra_P_HPP
+#define SubExtra_P_HPP
+
+#include <QObject>
+
+class SubExtraPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  SubExtraPrivate();
+  ~SubExtraPrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub_p.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub_p.hpp
new file mode 100644
index 0000000..7d5999c
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscoreSub_p.hpp
@@ -0,0 +1,14 @@
+#ifndef OtherUnderscoreSub_P_HPP
+#define OtherUnderscoreSub_P_HPP
+
+#include <QObject>
+
+class OtherUnderscoreSubPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscoreSubPrivate();
+  ~OtherUnderscoreSubPrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OtherUnderscore_p.hpp b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore_p.hpp
new file mode 100644
index 0000000..96906cf
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OtherUnderscore_p.hpp
@@ -0,0 +1,14 @@
+#ifndef OtherUnderscore_P_HPP
+#define OtherUnderscore_P_HPP
+
+#include <QObject>
+
+class OtherUnderscorePrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OtherUnderscorePrivate();
+  ~OtherUnderscorePrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDot.cpp b/Tests/QtAutogen/MocInclude/Common/OwnDot.cpp
new file mode 100644
index 0000000..b7b7d85
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDot.cpp
@@ -0,0 +1,40 @@
+#include "OwnDot.hpp"
+
+#include "OwnDot_p.h"
+
+class OwnDotLocal : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDotLocal();
+  ~OwnDotLocal();
+};
+
+OwnDotLocal::OwnDotLocal()
+{
+}
+
+OwnDotLocal::~OwnDotLocal()
+{
+}
+
+OwnDotPrivate::OwnDotPrivate()
+{
+  OwnDotLocal localObj;
+}
+
+OwnDotPrivate::~OwnDotPrivate()
+{
+}
+
+OwnDot::OwnDot()
+  : d(new OwnDotPrivate)
+{
+}
+
+OwnDot::~OwnDot()
+{
+  delete d;
+}
+
+#include "OwnDot.moc"
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDot.hpp b/Tests/QtAutogen/MocInclude/Common/OwnDot.hpp
new file mode 100644
index 0000000..6f49f12
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDot.hpp
@@ -0,0 +1,19 @@
+#ifndef OwnDot_HPP
+#define OwnDot_HPP
+
+#include <QObject>
+
+// Object source comes with a .moc include
+class OwnDotPrivate;
+class OwnDot : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDot();
+  ~OwnDot();
+
+private:
+  OwnDotPrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.cpp b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.cpp
new file mode 100644
index 0000000..056c0db
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.cpp
@@ -0,0 +1,41 @@
+#include "OwnDotUnderscore.hpp"
+
+#include "OwnDotUnderscore_p.h"
+
+class OwnDotUnderscoreLocal : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDotUnderscoreLocal();
+  ~OwnDotUnderscoreLocal();
+};
+
+OwnDotUnderscoreLocal::OwnDotUnderscoreLocal()
+{
+}
+
+OwnDotUnderscoreLocal::~OwnDotUnderscoreLocal()
+{
+}
+
+OwnDotUnderscorePrivate::OwnDotUnderscorePrivate()
+{
+  OwnDotUnderscoreLocal localObj;
+}
+
+OwnDotUnderscorePrivate::~OwnDotUnderscorePrivate()
+{
+}
+
+OwnDotUnderscore::OwnDotUnderscore()
+  : d(new OwnDotUnderscorePrivate)
+{
+}
+
+OwnDotUnderscore::~OwnDotUnderscore()
+{
+  delete d;
+}
+
+#include "OwnDotUnderscore.moc"
+#include "moc_OwnDotUnderscore.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.hpp b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.hpp
new file mode 100644
index 0000000..478955c
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore.hpp
@@ -0,0 +1,19 @@
+#ifndef LOwnDotUnderscore_HPP
+#define LOwnDotUnderscore_HPP
+
+#include <QObject>
+
+// Object source comes with a .moc and a _moc include
+class OwnDotUnderscorePrivate;
+class OwnDotUnderscore : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDotUnderscore();
+  ~OwnDotUnderscore();
+
+private:
+  OwnDotUnderscorePrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore_p.h b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore_p.h
new file mode 100644
index 0000000..6950b7f
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDotUnderscore_p.h
@@ -0,0 +1,14 @@
+#ifndef OwnDotUnderscore_P_HPP
+#define OwnDotUnderscore_P_HPP
+
+#include <QObject>
+
+class OwnDotUnderscorePrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDotUnderscorePrivate();
+  ~OwnDotUnderscorePrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnDot_p.h b/Tests/QtAutogen/MocInclude/Common/OwnDot_p.h
new file mode 100644
index 0000000..f3aff32
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnDot_p.h
@@ -0,0 +1,14 @@
+#ifndef OwnDot_P_HPP
+#define OwnDot_P_HPP
+
+#include <QObject>
+
+class OwnDotPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OwnDotPrivate();
+  ~OwnDotPrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.cpp b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.cpp
new file mode 100644
index 0000000..cb8f12c
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.cpp
@@ -0,0 +1,23 @@
+#include "OwnUnderscore.hpp"
+
+#include "OwnUnderscore_p.h"
+
+OwnUnderscorePrivate::OwnUnderscorePrivate()
+{
+}
+
+OwnUnderscorePrivate::~OwnUnderscorePrivate()
+{
+}
+
+OwnUnderscore::OwnUnderscore()
+  : d(new OwnUnderscorePrivate)
+{
+}
+
+OwnUnderscore::~OwnUnderscore()
+{
+  delete d;
+}
+
+#include "moc_OwnUnderscore.cpp"
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.hpp b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.hpp
new file mode 100644
index 0000000..e6a6a6e
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore.hpp
@@ -0,0 +1,19 @@
+#ifndef OwnUnderscore_HPP
+#define OwnUnderscore_HPP
+
+#include <QObject>
+
+// Object source comes with a _moc include
+class OwnUnderscorePrivate;
+class OwnUnderscore : public QObject
+{
+  Q_OBJECT
+public:
+  OwnUnderscore();
+  ~OwnUnderscore();
+
+private:
+  OwnUnderscorePrivate* const d;
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/OwnUnderscore_p.h b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore_p.h
new file mode 100644
index 0000000..a3a6b00
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/OwnUnderscore_p.h
@@ -0,0 +1,14 @@
+#ifndef OwnUnderscore_P_HPP
+#define OwnUnderscore_P_HPP
+
+#include <QObject>
+
+class OwnUnderscorePrivate : public QObject
+{
+  Q_OBJECT
+public:
+  OwnUnderscorePrivate();
+  ~OwnUnderscorePrivate();
+};
+
+#endif
diff --git a/Tests/QtAutogen/MocInclude/Common/common.cpp.in b/Tests/QtAutogen/MocInclude/Common/common.cpp.in
new file mode 100644
index 0000000..b53e93d
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/common.cpp.in
@@ -0,0 +1,32 @@
+#include "DualSub/Second/Second.hpp"
+#include "DualSubMocked.hpp"
+#include "ExternDot.hpp"
+#include "ExternDotGenerated.hpp"
+#include "None.hpp"
+#include "OtherUnderscore.hpp"
+#include "OtherUnderscoreSub.hpp"
+#include "OwnDot.hpp"
+#include "OwnDotUnderscore.hpp"
+#include "OwnUnderscore.hpp"
+#include "InIncludes/SubOwnDot.hpp"
+
+bool @COMMON_FUNCTION_NAME@()
+{
+  None objNone;
+  OwnUnderscore objOwnUnderscore;
+  OwnDot objOwnDot;
+  OwnDotUnderscore objOwnDotUnderscore;
+
+  OtherUnderscore objOtherUnderscore;
+  OtherUnderscoreSub objOtherUnderscoreSub;
+
+  Second second;
+  DualSubMocked dualSubMocked;
+
+  ExternDot objExternDot;
+  ExternDotGenerated objGeneratedExternDot;
+
+  InIncludes::SubOwnDot subOwnDot;
+
+  return true;
+}
diff --git a/Tests/QtAutogen/MocInclude/Common/moc_MixedCustom.cpp.in b/Tests/QtAutogen/MocInclude/Common/moc_MixedCustom.cpp.in
new file mode 100644
index 0000000..6c44793
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Common/moc_MixedCustom.cpp.in
@@ -0,0 +1,5 @@
+
+void moc_MixedCustom(MixedCustom const & arg)
+{
+    (void)arg;
+}
diff --git a/Tests/QtAutogen/MocInclude/EObjA.cpp b/Tests/QtAutogen/MocInclude/EObjA.cpp
deleted file mode 100644
index 7681c29..0000000
--- a/Tests/QtAutogen/MocInclude/EObjA.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "EObjA.hpp"
-#include "EObjAExtra.hpp"
-#include "EObjA_p.hpp"
-
-class EObjALocal : public QObject
-{
-  Q_OBJECT
-public:
-  EObjALocal();
-  ~EObjALocal();
-};
-
-EObjALocal::EObjALocal()
-{
-}
-
-EObjALocal::~EObjALocal()
-{
-}
-
-EObjAPrivate::EObjAPrivate()
-{
-  EObjALocal localObj;
-  EObjAExtra extraObj;
-}
-
-EObjAPrivate::~EObjAPrivate()
-{
-}
-
-EObjA::EObjA()
-  : d(new EObjAPrivate)
-{
-}
-
-EObjA::~EObjA()
-{
-  delete d;
-}
-
-// For EObjALocal
-#include "EObjA.moc"
-// - Not the own header
-#include "moc_EObjAExtra.cpp"
diff --git a/Tests/QtAutogen/MocInclude/EObjA.hpp b/Tests/QtAutogen/MocInclude/EObjA.hpp
deleted file mode 100644
index 0939ab6..0000000
--- a/Tests/QtAutogen/MocInclude/EObjA.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef EOBJA_HPP
-#define EOBJA_HPP
-
-#include <QObject>
-
-// Sources includes a moc_ includes of an extra object
-class EObjAPrivate;
-class EObjA : public QObject
-{
-  Q_OBJECT
-public:
-  EObjA();
-  ~EObjA();
-
-private:
-  EObjAPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra.cpp b/Tests/QtAutogen/MocInclude/EObjAExtra.cpp
deleted file mode 100644
index 369ca8f..0000000
--- a/Tests/QtAutogen/MocInclude/EObjAExtra.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "EObjAExtra.hpp"
-#include "EObjAExtra_p.hpp"
-
-EObjAExtraPrivate::EObjAExtraPrivate()
-{
-}
-
-EObjAExtraPrivate::~EObjAExtraPrivate()
-{
-}
-
-EObjAExtra::EObjAExtra()
-  : d(new EObjAExtraPrivate)
-{
-}
-
-EObjAExtra::~EObjAExtra()
-{
-  delete d;
-}
diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra.hpp b/Tests/QtAutogen/MocInclude/EObjAExtra.hpp
deleted file mode 100644
index b10681d..0000000
--- a/Tests/QtAutogen/MocInclude/EObjAExtra.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef EOBJAEXTRA_HPP
-#define EOBJAEXTRA_HPP
-
-#include <QObject>
-
-class EObjAExtraPrivate;
-class EObjAExtra : public QObject
-{
-  Q_OBJECT
-public:
-  EObjAExtra();
-  ~EObjAExtra();
-
-private:
-  EObjAExtraPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp
deleted file mode 100644
index d8bf284..0000000
--- a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef EOBJAEXTRA_P_HPP
-#define EOBJAEXTRA_P_HPP
-
-#include <QObject>
-
-class EObjAExtraPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  EObjAExtraPrivate();
-  ~EObjAExtraPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/EObjA_p.hpp b/Tests/QtAutogen/MocInclude/EObjA_p.hpp
deleted file mode 100644
index 9ef5624..0000000
--- a/Tests/QtAutogen/MocInclude/EObjA_p.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef EOBJA_P_HPP
-#define EOBJA_P_HPP
-
-#include <QObject>
-
-class EObjAPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  EObjAPrivate();
-  ~EObjAPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/EObjB.cpp b/Tests/QtAutogen/MocInclude/EObjB.cpp
deleted file mode 100644
index 3068c68..0000000
--- a/Tests/QtAutogen/MocInclude/EObjB.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "EObjB.hpp"
-#include "EObjB_p.hpp"
-#include "subExtra/EObjBExtra.hpp"
-
-class EObjBLocal : public QObject
-{
-  Q_OBJECT
-public:
-  EObjBLocal();
-  ~EObjBLocal();
-};
-
-EObjBLocal::EObjBLocal()
-{
-}
-
-EObjBLocal::~EObjBLocal()
-{
-}
-
-EObjBPrivate::EObjBPrivate()
-{
-  EObjBLocal localObj;
-  EObjBExtra extraObj;
-}
-
-EObjBPrivate::~EObjBPrivate()
-{
-}
-
-EObjB::EObjB()
-  : d(new EObjBPrivate)
-{
-}
-
-EObjB::~EObjB()
-{
-  delete d;
-}
-
-// For EObjBLocal
-#include "EObjB.moc"
-// - Not the own header
-// - in a subdirectory
-#include "subExtra/moc_EObjBExtra.cpp"
diff --git a/Tests/QtAutogen/MocInclude/EObjB.hpp b/Tests/QtAutogen/MocInclude/EObjB.hpp
deleted file mode 100644
index 6632bdb..0000000
--- a/Tests/QtAutogen/MocInclude/EObjB.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef EOBJB_HPP
-#define EOBJB_HPP
-
-#include <QObject>
-
-// Sources includes a moc_ includes of an extra object in a subdirectory
-class EObjBPrivate;
-class EObjB : public QObject
-{
-  Q_OBJECT
-public:
-  EObjB();
-  ~EObjB();
-
-private:
-  EObjBPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/EObjB_p.hpp b/Tests/QtAutogen/MocInclude/EObjB_p.hpp
deleted file mode 100644
index 84b1ea2..0000000
--- a/Tests/QtAutogen/MocInclude/EObjB_p.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef EOBJB_P_HPP
-#define EOBJB_P_HPP
-
-#include <QObject>
-
-class EObjBPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  EObjBPrivate();
-  ~EObjBPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/LObjA.cpp b/Tests/QtAutogen/MocInclude/LObjA.cpp
deleted file mode 100644
index 9aae991..0000000
--- a/Tests/QtAutogen/MocInclude/LObjA.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "LObjA.hpp"
-#include "LObjA_p.h"
-
-class LObjALocal : public QObject
-{
-  Q_OBJECT
-public:
-  LObjALocal();
-  ~LObjALocal();
-};
-
-LObjALocal::LObjALocal()
-{
-}
-
-LObjALocal::~LObjALocal()
-{
-}
-
-LObjAPrivate::LObjAPrivate()
-{
-  LObjALocal localObj;
-}
-
-LObjAPrivate::~LObjAPrivate()
-{
-}
-
-LObjA::LObjA()
-  : d(new LObjAPrivate)
-{
-}
-
-LObjA::~LObjA()
-{
-  delete d;
-}
-
-#include "LObjA.moc"
diff --git a/Tests/QtAutogen/MocInclude/LObjA.hpp b/Tests/QtAutogen/MocInclude/LObjA.hpp
deleted file mode 100644
index aac670c..0000000
--- a/Tests/QtAutogen/MocInclude/LObjA.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef LOBJA_HPP
-#define LOBJA_HPP
-
-#include <QObject>
-
-// Object source comes with a .moc include
-class LObjAPrivate;
-class LObjA : public QObject
-{
-  Q_OBJECT
-public:
-  LObjA();
-  ~LObjA();
-
-private:
-  LObjAPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/LObjA_p.h b/Tests/QtAutogen/MocInclude/LObjA_p.h
deleted file mode 100644
index 97113d6..0000000
--- a/Tests/QtAutogen/MocInclude/LObjA_p.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef LOBJA_P_HPP
-#define LOBJA_P_HPP
-
-#include <QObject>
-
-class LObjAPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  LObjAPrivate();
-  ~LObjAPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/LObjB.cpp b/Tests/QtAutogen/MocInclude/LObjB.cpp
deleted file mode 100644
index 7485d8f..0000000
--- a/Tests/QtAutogen/MocInclude/LObjB.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "LObjB.hpp"
-#include "LObjB_p.h"
-
-class LObjBLocal : public QObject
-{
-  Q_OBJECT
-public:
-  LObjBLocal();
-  ~LObjBLocal();
-};
-
-LObjBLocal::LObjBLocal()
-{
-}
-
-LObjBLocal::~LObjBLocal()
-{
-}
-
-LObjBPrivate::LObjBPrivate()
-{
-  LObjBLocal localObj;
-}
-
-LObjBPrivate::~LObjBPrivate()
-{
-}
-
-LObjB::LObjB()
-  : d(new LObjBPrivate)
-{
-}
-
-LObjB::~LObjB()
-{
-  delete d;
-}
-
-#include "LObjB.moc"
-#include "moc_LObjB.cpp"
diff --git a/Tests/QtAutogen/MocInclude/LObjB.hpp b/Tests/QtAutogen/MocInclude/LObjB.hpp
deleted file mode 100644
index eb4e58d..0000000
--- a/Tests/QtAutogen/MocInclude/LObjB.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef LLObjB_HPP
-#define LLObjB_HPP
-
-#include <QObject>
-
-// Object source comes with a .moc and a _moc include
-class LObjBPrivate;
-class LObjB : public QObject
-{
-  Q_OBJECT
-public:
-  LObjB();
-  ~LObjB();
-
-private:
-  LObjBPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/LObjB_p.h b/Tests/QtAutogen/MocInclude/LObjB_p.h
deleted file mode 100644
index b88f40e..0000000
--- a/Tests/QtAutogen/MocInclude/LObjB_p.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef LOBJB_P_HPP
-#define LOBJB_P_HPP
-
-#include <QObject>
-
-class LObjBPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  LObjBPrivate();
-  ~LObjBPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/ObjA.cpp b/Tests/QtAutogen/MocInclude/ObjA.cpp
deleted file mode 100644
index 6f6b90e..0000000
--- a/Tests/QtAutogen/MocInclude/ObjA.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "ObjA.hpp"
-#include "ObjA_p.h"
-
-ObjAPrivate::ObjAPrivate()
-{
-}
-
-ObjAPrivate::~ObjAPrivate()
-{
-}
-
-ObjA::ObjA()
-  : d(new ObjAPrivate)
-{
-}
-
-ObjA::~ObjA()
-{
-  delete d;
-}
diff --git a/Tests/QtAutogen/MocInclude/ObjA.hpp b/Tests/QtAutogen/MocInclude/ObjA.hpp
deleted file mode 100644
index f16c924..0000000
--- a/Tests/QtAutogen/MocInclude/ObjA.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef OBJA_HPP
-#define OBJA_HPP
-
-#include <QObject>
-
-// Object source comes without any _moc/.moc includes
-class ObjAPrivate;
-class ObjA : public QObject
-{
-  Q_OBJECT
-public:
-  ObjA();
-  ~ObjA();
-
-private:
-  ObjAPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/ObjA_p.h b/Tests/QtAutogen/MocInclude/ObjA_p.h
deleted file mode 100644
index d944bc6..0000000
--- a/Tests/QtAutogen/MocInclude/ObjA_p.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef OBJA_P_HPP
-#define OBJA_P_HPP
-
-#include <QObject>
-
-class ObjAPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  ObjAPrivate();
-  ~ObjAPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/ObjB.cpp b/Tests/QtAutogen/MocInclude/ObjB.cpp
deleted file mode 100644
index a6f2509..0000000
--- a/Tests/QtAutogen/MocInclude/ObjB.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "ObjB.hpp"
-#include "ObjB_p.h"
-
-ObjBPrivate::ObjBPrivate()
-{
-}
-
-ObjBPrivate::~ObjBPrivate()
-{
-}
-
-ObjB::ObjB()
-  : d(new ObjBPrivate)
-{
-}
-
-ObjB::~ObjB()
-{
-  delete d;
-}
-
-#include "moc_ObjB.cpp"
diff --git a/Tests/QtAutogen/MocInclude/ObjB.hpp b/Tests/QtAutogen/MocInclude/ObjB.hpp
deleted file mode 100644
index 2ac8d17..0000000
--- a/Tests/QtAutogen/MocInclude/ObjB.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ObjB_HPP
-#define ObjB_HPP
-
-#include <QObject>
-
-// Object source comes with a _moc include
-class ObjBPrivate;
-class ObjB : public QObject
-{
-  Q_OBJECT
-public:
-  ObjB();
-  ~ObjB();
-
-private:
-  ObjBPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/ObjB_p.h b/Tests/QtAutogen/MocInclude/ObjB_p.h
deleted file mode 100644
index 61ba604..0000000
--- a/Tests/QtAutogen/MocInclude/ObjB_p.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef OBJB_P_HPP
-#define OBJB_P_HPP
-
-#include <QObject>
-
-class ObjBPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  ObjBPrivate();
-  ~ObjBPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/Relaxed/CMakeLists.txt b/Tests/QtAutogen/MocInclude/Relaxed/CMakeLists.txt
new file mode 100644
index 0000000..048b79c
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Relaxed/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Enable relaxed mode
+set(CMAKE_AUTOMOC_RELAXED_MODE TRUE)
+
+# Common test
+set(COMMON_FUNCTION_NAME commonRelaxed)
+configure_file(
+  "${COM_DIR}/common.cpp.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/commonRelaxed.cpp")
+
+makeExecutable(libRelaxed)
+target_sources(libRelaxed PRIVATE
+  "${CMAKE_CURRENT_BINARY_DIR}/commonRelaxed.cpp"
+  RObjA.cpp
+  RObjB.cpp
+  RObjC.cpp
+  relaxed.cpp
+  )
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjA.cpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjA.cpp
similarity index 100%
rename from Tests/QtAutogen/MocIncludeRelaxed/RObjA.cpp
rename to Tests/QtAutogen/MocInclude/Relaxed/RObjA.cpp
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjA.hpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjA.hpp
similarity index 100%
rename from Tests/QtAutogen/MocIncludeRelaxed/RObjA.hpp
rename to Tests/QtAutogen/MocInclude/Relaxed/RObjA.hpp
diff --git a/Tests/QtAutogen/MocInclude/Relaxed/RObjB.cpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjB.cpp
new file mode 100644
index 0000000..57d7daf
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Relaxed/RObjB.cpp
@@ -0,0 +1,23 @@
+#include "RObjB.hpp"
+
+#include "RObjBExtra.hpp"
+
+RObjBExtra::RObjBExtra()
+{
+}
+
+RObjBExtra::~RObjBExtra()
+{
+}
+
+RObjB::RObjB()
+{
+  RObjBExtra extraObject;
+}
+
+RObjB::~RObjB()
+{
+}
+
+// Relaxed mode should run moc on RObjBExtra.hpp instead
+#include "RObjBExtra.moc"
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjB.hpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjB.hpp
similarity index 100%
rename from Tests/QtAutogen/MocIncludeRelaxed/RObjB.hpp
rename to Tests/QtAutogen/MocInclude/Relaxed/RObjB.hpp
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjBExtra.hpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjBExtra.hpp
similarity index 100%
rename from Tests/QtAutogen/MocIncludeRelaxed/RObjBExtra.hpp
rename to Tests/QtAutogen/MocInclude/Relaxed/RObjBExtra.hpp
diff --git a/Tests/QtAutogen/MocInclude/Relaxed/RObjC.cpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjC.cpp
new file mode 100644
index 0000000..3275216
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Relaxed/RObjC.cpp
@@ -0,0 +1,31 @@
+#include "RObjC.hpp"
+
+#include <QObject>
+
+class RObjCPrivate : public QObject
+{
+  Q_OBJECT
+public:
+  RObjCPrivate();
+  ~RObjCPrivate();
+};
+
+RObjCPrivate::RObjCPrivate()
+{
+}
+
+RObjCPrivate::~RObjCPrivate()
+{
+}
+
+RObjC::RObjC()
+{
+  RObjCPrivate privateObject;
+}
+
+RObjC::~RObjC()
+{
+}
+
+// Relaxed include should moc this source instead of the header
+#include "moc_RObjC.cpp"
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjC.hpp b/Tests/QtAutogen/MocInclude/Relaxed/RObjC.hpp
similarity index 100%
rename from Tests/QtAutogen/MocIncludeRelaxed/RObjC.hpp
rename to Tests/QtAutogen/MocInclude/Relaxed/RObjC.hpp
diff --git a/Tests/QtAutogen/MocInclude/Relaxed/relaxed.cpp b/Tests/QtAutogen/MocInclude/Relaxed/relaxed.cpp
new file mode 100644
index 0000000..5a511b6
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Relaxed/relaxed.cpp
@@ -0,0 +1,21 @@
+// AUTOMOC relaxed mode objects
+#include "RObjA.hpp"
+#include "RObjB.hpp"
+#include "RObjC.hpp"
+
+// Forward declaration
+bool commonRelaxed();
+
+int main(int argv, char** args)
+{
+  // Common tests
+  if (!commonRelaxed()) {
+    return -1;
+  }
+
+  // Relaxed tests
+  RObjA rObjA;
+  RObjB rObjB;
+  RObjC rObjC;
+  return 0;
+}
diff --git a/Tests/QtAutogen/MocInclude/SObjA.cpp b/Tests/QtAutogen/MocInclude/SObjA.cpp
deleted file mode 100644
index 7e75bf9..0000000
--- a/Tests/QtAutogen/MocInclude/SObjA.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "SObjA.hpp"
-
-SObjA::SObjA()
-{
-}
-
-SObjA::~SObjA()
-{
-}
-
-#include "SObjA.moc"
diff --git a/Tests/QtAutogen/MocInclude/SObjA.hpp b/Tests/QtAutogen/MocInclude/SObjA.hpp
deleted file mode 100644
index 1436abc..0000000
--- a/Tests/QtAutogen/MocInclude/SObjA.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SOBJA_HPP
-#define SOBJA_HPP
-
-#include <QObject>
-
-// Object source includes externally generated .moc file
-class SObjA : public QObject
-{
-  Q_OBJECT
-public:
-  SObjA();
-  ~SObjA();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/SObjB.cpp.in b/Tests/QtAutogen/MocInclude/SObjB.cpp.in
deleted file mode 100644
index b1cc12a..0000000
--- a/Tests/QtAutogen/MocInclude/SObjB.cpp.in
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "SObjB.hpp"
-
-SObjB::SObjB()
-{
-}
-
-SObjB::~SObjB()
-{
-}
-
-#include "SObjB.moc"
diff --git a/Tests/QtAutogen/MocInclude/SObjB.hpp.in b/Tests/QtAutogen/MocInclude/SObjB.hpp.in
deleted file mode 100644
index 5e396ae..0000000
--- a/Tests/QtAutogen/MocInclude/SObjB.hpp.in
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SOBJB_HPP
-#define SOBJB_HPP
-
-#include <QObject>
-
-// Object source includes externally generated .moc file
-class SObjB : public QObject
-{
-  Q_OBJECT
-public:
-  SObjB();
-  ~SObjB();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/SObjC.cpp b/Tests/QtAutogen/MocInclude/SObjC.cpp
deleted file mode 100644
index 1e8d397..0000000
--- a/Tests/QtAutogen/MocInclude/SObjC.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "SObjC.hpp"
-
-void SObjCLocalFunction();
-
-class SObjCLocal : public QObject
-{
-  Q_OBJECT
-
-public:
-  SObjCLocal();
-  ~SObjCLocal();
-};
-
-SObjCLocal::SObjCLocal()
-{
-}
-
-SObjCLocal::~SObjCLocal()
-{
-}
-
-SObjC::SObjC()
-{
-  SObjCLocal localObject;
-  SObjCLocalFunction();
-}
-
-SObjC::~SObjC()
-{
-}
-
-#include "SObjC.moc"
-#include "moc_SObjC.cpp"
-// Include moc_ file for which the header is SKIP_AUTOMOC enabled
-#include "moc_SObjCExtra.cpp"
diff --git a/Tests/QtAutogen/MocInclude/SObjC.hpp b/Tests/QtAutogen/MocInclude/SObjC.hpp
deleted file mode 100644
index def0f9d..0000000
--- a/Tests/QtAutogen/MocInclude/SObjC.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SOBJC_HPP
-#define SOBJC_HPP
-
-#include <QObject>
-
-// Object source includes externally generated .moc file
-class SObjC : public QObject
-{
-  Q_OBJECT
-public:
-  SObjC();
-  ~SObjC();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/SObjCExtra.cpp b/Tests/QtAutogen/MocInclude/SObjCExtra.cpp
deleted file mode 100644
index 55dd1c3..0000000
--- a/Tests/QtAutogen/MocInclude/SObjCExtra.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "SObjCExtra.hpp"
-
-class SObjCLocalExtra : public QObject
-{
-  Q_OBJECT
-
-public:
-  SObjCLocalExtra();
-  ~SObjCLocalExtra();
-};
-
-SObjCLocalExtra::SObjCLocalExtra()
-{
-}
-
-SObjCLocalExtra::~SObjCLocalExtra()
-{
-}
-
-SObjCExtra::SObjCExtra()
-{
-}
-
-SObjCExtra::~SObjCExtra()
-{
-}
-
-// Externally generated header moc
-#include "SObjCExtra_extMoc.cpp"
-// AUTOMOC generated source moc
-#include "SObjCExtra.moc"
diff --git a/Tests/QtAutogen/MocInclude/SObjCExtra.hpp b/Tests/QtAutogen/MocInclude/SObjCExtra.hpp
deleted file mode 100644
index 08545ac..0000000
--- a/Tests/QtAutogen/MocInclude/SObjCExtra.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SOBJCEXTRA_HPP
-#define SOBJCEXTRA_HPP
-
-#include <QObject>
-
-// Object source includes externally generated .moc file
-class SObjCExtra : public QObject
-{
-  Q_OBJECT
-public:
-  SObjCExtra();
-  ~SObjCExtra();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/SObjCExtra.moc.in b/Tests/QtAutogen/MocInclude/SObjCExtra.moc.in
deleted file mode 100644
index 00fc4aa..0000000
--- a/Tests/QtAutogen/MocInclude/SObjCExtra.moc.in
+++ /dev/null
@@ -1,4 +0,0 @@
-
-void SObjCLocalFunction()
-{
-}
diff --git a/Tests/QtAutogen/MocInclude/Strict/CMakeLists.txt b/Tests/QtAutogen/MocInclude/Strict/CMakeLists.txt
new file mode 100644
index 0000000..12c503f
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Strict/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Disable relaxed mode
+set(CMAKE_AUTOMOC_RELAXED_MODE FALSE)
+
+# Common test
+set(COMMON_FUNCTION_NAME commonStrict)
+configure_file(
+  "${COM_DIR}/common.cpp.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/commonStrict.cpp")
+
+makeExecutable(libStrict)
+target_sources(libStrict PRIVATE
+  "${CMAKE_CURRENT_BINARY_DIR}/commonStrict.cpp"
+  strict.cpp
+  )
diff --git a/Tests/QtAutogen/MocInclude/Strict/strict.cpp b/Tests/QtAutogen/MocInclude/Strict/strict.cpp
new file mode 100644
index 0000000..dd24bb0
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/Strict/strict.cpp
@@ -0,0 +1,7 @@
+// Forward declaration
+bool commonStrict();
+
+int main(int argv, char** args)
+{
+  return commonStrict() ? 0 : -1;
+}
diff --git a/Tests/QtAutogen/MocInclude/main.cpp b/Tests/QtAutogen/MocInclude/main.cpp
new file mode 100644
index 0000000..371c5fd
--- /dev/null
+++ b/Tests/QtAutogen/MocInclude/main.cpp
@@ -0,0 +1,9 @@
+
+// Forward declaration
+bool libStrict();
+bool libRelaxed();
+
+int main(int argv, char** args)
+{
+  return (libStrict() && libRelaxed()) ? 0 : -1;
+}
diff --git a/Tests/QtAutogen/MocInclude/shared.cmake b/Tests/QtAutogen/MocInclude/shared.cmake
deleted file mode 100644
index 2ca2841..0000000
--- a/Tests/QtAutogen/MocInclude/shared.cmake
+++ /dev/null
@@ -1,71 +0,0 @@
-# Test moc include patterns
-include_directories("../MocInclude")
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-# Generate .moc file externally and enabled SKIP_AUTOMOC on the file
-qtx_generate_moc(
-  ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjA.hpp
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjA.moc)
-set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjA.cpp PROPERTY SKIP_AUTOMOC ON)
-
-# Generate .moc file externally from generated source file
-# and enabled SKIP_AUTOMOC on the source file
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SObjB.hpp
-  COMMAND ${CMAKE_COMMAND} -E copy
-    ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjB.hpp.in
-    ${CMAKE_CURRENT_BINARY_DIR}/SObjB.hpp)
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SObjB.cpp
-  COMMAND ${CMAKE_COMMAND} -E copy
-    ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjB.cpp.in
-    ${CMAKE_CURRENT_BINARY_DIR}/SObjB.cpp)
-qtx_generate_moc(
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjB.hpp
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjB.moc)
-set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/SObjB.cpp PROPERTY SKIP_AUTOMOC ON)
-
-# Generate moc file externally and enabled SKIP_AUTOMOC on the header
-qtx_generate_moc(
-  ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjCExtra.hpp
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjCExtra_extMoc.cpp)
-set_property(
-  SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjCExtra.hpp
-  PROPERTY SKIP_AUTOMOC ON)
-# Custom target to depend on
-set(SOBJC_MOC ${CMAKE_CURRENT_BINARY_DIR}/moc_SObjCExtra.cpp)
-add_custom_target("${MOC_INCLUDE_NAME}_SOBJC"
-  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/SObjCExtra_extMoc.cpp
-  BYPRODUCTS ${SOBJC_MOC}
-  COMMAND ${CMAKE_COMMAND} -E copy
-    ${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/SObjCExtra.moc.in
-    ${SOBJC_MOC})
-
-# MOC_INCLUDE_NAME must be defined by the includer
-add_executable(${MOC_INCLUDE_NAME}
-  # Common sources
-  ../MocInclude/ObjA.cpp
-  ../MocInclude/ObjB.cpp
-
-  ../MocInclude/LObjA.cpp
-  ../MocInclude/LObjB.cpp
-
-  ../MocInclude/EObjA.cpp
-  ../MocInclude/EObjAExtra.cpp
-  ../MocInclude/EObjB.cpp
-  ../MocInclude/subExtra/EObjBExtra.cpp
-
-  ../MocInclude/SObjA.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjA.moc
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjB.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/SObjB.moc
-  ../MocInclude/SObjC.cpp
-  ../MocInclude/SObjCExtra.hpp
-  ../MocInclude/SObjCExtra.cpp
-
-  ../MocInclude/subGlobal/GObj.cpp
-  main.cpp
-)
-add_dependencies(${MOC_INCLUDE_NAME} "${MOC_INCLUDE_NAME}_SOBJC")
-target_link_libraries(${MOC_INCLUDE_NAME} ${QT_LIBRARIES})
-set_target_properties(${MOC_INCLUDE_NAME} PROPERTIES AUTOMOC ON)
diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.cpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.cpp
deleted file mode 100644
index c697866..0000000
--- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "EObjBExtra.hpp"
-#include "EObjBExtra_p.hpp"
-
-EObjBExtraPrivate::EObjBExtraPrivate()
-{
-}
-
-EObjBExtraPrivate::~EObjBExtraPrivate()
-{
-}
-
-EObjBExtra::EObjBExtra()
-  : d(new EObjBExtraPrivate)
-{
-}
-
-EObjBExtra::~EObjBExtra()
-{
-  delete d;
-}
diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.hpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.hpp
deleted file mode 100644
index 3798d7f..0000000
--- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef EOBJBEXTRA_HPP
-#define EOBJBEXTRA_HPP
-
-#include <QObject>
-
-class EObjBExtraPrivate;
-class EObjBExtra : public QObject
-{
-  Q_OBJECT
-public:
-  EObjBExtra();
-  ~EObjBExtra();
-
-private:
-  EObjBExtraPrivate* const d;
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp
deleted file mode 100644
index 3231fac..0000000
--- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef EOBJBEXTRA_P_HPP
-#define EOBJBEXTRA_P_HPP
-
-#include <QObject>
-
-class EObjBExtraPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  EObjBExtraPrivate();
-  ~EObjBExtraPrivate();
-};
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj.cpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj.cpp
deleted file mode 100644
index 6b92f21..0000000
--- a/Tests/QtAutogen/MocInclude/subGlobal/GObj.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "GObj.hpp"
-#include "GObj_p.hpp"
-
-namespace subGlobal {
-
-class GObjLocal : public QObject
-{
-  Q_OBJECT
-public:
-  GObjLocal();
-  ~GObjLocal();
-};
-
-GObjLocal::GObjLocal()
-{
-}
-
-GObjLocal::~GObjLocal()
-{
-}
-
-GObjPrivate::GObjPrivate()
-{
-}
-
-GObjPrivate::~GObjPrivate()
-{
-}
-
-GObj::GObj()
-{
-  GObjLocal localObj;
-}
-
-GObj::~GObj()
-{
-}
-}
-
-// For the local QObject
-#include "GObj.moc"
diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj.hpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj.hpp
deleted file mode 100644
index 2f9ee82..0000000
--- a/Tests/QtAutogen/MocInclude/subGlobal/GObj.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef GOBJ_HPP
-#define GOBJ_HPP
-
-#include <QObject>
-
-namespace subGlobal {
-
-class GObj : public QObject
-{
-  Q_OBJECT
-public:
-  GObj();
-  ~GObj();
-};
-}
-
-#endif
diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp
deleted file mode 100644
index 4a43755..0000000
--- a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef GOBJ_P_HPP
-#define GOBJ_P_HPP
-
-#include <QObject>
-
-namespace subGlobal {
-
-class GObjPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  GObjPrivate();
-  ~GObjPrivate();
-};
-}
-
-#endif
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/CMakeLists.txt b/Tests/QtAutogen/MocIncludeRelaxed/CMakeLists.txt
deleted file mode 100644
index 8b4da34..0000000
--- a/Tests/QtAutogen/MocIncludeRelaxed/CMakeLists.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-cmake_minimum_required(VERSION 3.10)
-project(MocIncludeRelaxed)
-include("../AutogenCoreTest.cmake")
-
-# Test moc include patterns
-set(CMAKE_AUTOMOC_RELAXED_MODE TRUE)
-
-# Shared executable
-set(MOC_INCLUDE_NAME "mocIncludeRelaxed")
-include(${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/shared.cmake)
-
-# Relaxed only executable
-add_executable(mocIncludeRelaxedOnly
-  RObjA.cpp
-  RObjB.cpp
-  RObjC.cpp
-  RMain.cpp
-)
-target_link_libraries(mocIncludeRelaxedOnly ${QT_LIBRARIES})
-set_target_properties(mocIncludeRelaxedOnly PROPERTIES AUTOMOC ON)
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RMain.cpp b/Tests/QtAutogen/MocIncludeRelaxed/RMain.cpp
deleted file mode 100644
index 5b2c070..0000000
--- a/Tests/QtAutogen/MocIncludeRelaxed/RMain.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Relaxed AUTOMOC objects
-#include "RObjA.hpp"
-#include "RObjB.hpp"
-#include "RObjC.hpp"
-
-int main(int argv, char** args)
-{
-  RObjA rObjA;
-  RObjB rObjB;
-  RObjC rObjC;
-  return 0;
-}
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjB.cpp b/Tests/QtAutogen/MocIncludeRelaxed/RObjB.cpp
deleted file mode 100644
index c56d10f..0000000
--- a/Tests/QtAutogen/MocIncludeRelaxed/RObjB.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "RObjB.hpp"
-#include "RObjBExtra.hpp"
-
-RObjBExtra::RObjBExtra()
-{
-}
-
-RObjBExtra::~RObjBExtra()
-{
-}
-
-RObjB::RObjB()
-{
-  RObjBExtra extraObject;
-}
-
-RObjB::~RObjB()
-{
-}
-
-// Relaxed mode should run moc on RObjBExtra.hpp instead
-#include "RObjBExtra.moc"
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/RObjC.cpp b/Tests/QtAutogen/MocIncludeRelaxed/RObjC.cpp
deleted file mode 100644
index 4ba32f5..0000000
--- a/Tests/QtAutogen/MocIncludeRelaxed/RObjC.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "RObjC.hpp"
-#include <QObject>
-
-class RObjCPrivate : public QObject
-{
-  Q_OBJECT
-public:
-  RObjCPrivate();
-  ~RObjCPrivate();
-};
-
-RObjCPrivate::RObjCPrivate()
-{
-}
-
-RObjCPrivate::~RObjCPrivate()
-{
-}
-
-RObjC::RObjC()
-{
-  RObjCPrivate privateObject;
-}
-
-RObjC::~RObjC()
-{
-}
-
-// Relaxed include should moc this source instead of the header
-#include "moc_RObjC.cpp"
diff --git a/Tests/QtAutogen/MocIncludeRelaxed/main.cpp b/Tests/QtAutogen/MocIncludeRelaxed/main.cpp
deleted file mode 100644
index 5a3148d..0000000
--- a/Tests/QtAutogen/MocIncludeRelaxed/main.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "EObjA.hpp"
-#include "EObjB.hpp"
-#include "LObjA.hpp"
-#include "LObjB.hpp"
-#include "ObjA.hpp"
-#include "ObjB.hpp"
-#include "SObjA.hpp"
-#include "SObjB.hpp"
-#include "subGlobal/GObj.hpp"
-
-int main(int argv, char** args)
-{
-  subGlobal::GObj gObj;
-  ObjA objA;
-  ObjB objB;
-  LObjA lObjA;
-  LObjB lObjB;
-  EObjA eObjA;
-  EObjB eObjB;
-  SObjA sObjA;
-  SObjB sObjB;
-  return 0;
-}
-
-// Header in global subdirectory
-#include "subGlobal/moc_GObj.cpp"
diff --git a/Tests/QtAutogen/MocIncludeStrict/CMakeLists.txt b/Tests/QtAutogen/MocIncludeStrict/CMakeLists.txt
deleted file mode 100644
index d0aaebf..0000000
--- a/Tests/QtAutogen/MocIncludeStrict/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-cmake_minimum_required(VERSION 3.10)
-project(MocIncludeStrict)
-include("../AutogenCoreTest.cmake")
-
-# Test moc include patterns
-set(CMAKE_AUTOMOC_RELAXED_MODE FALSE)
-
-# Shared executable
-set(MOC_INCLUDE_NAME "mocIncludeStrict")
-include(${CMAKE_CURRENT_SOURCE_DIR}/../MocInclude/shared.cmake)
diff --git a/Tests/QtAutogen/MocIncludeStrict/main.cpp b/Tests/QtAutogen/MocIncludeStrict/main.cpp
deleted file mode 100644
index 5a3148d..0000000
--- a/Tests/QtAutogen/MocIncludeStrict/main.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "EObjA.hpp"
-#include "EObjB.hpp"
-#include "LObjA.hpp"
-#include "LObjB.hpp"
-#include "ObjA.hpp"
-#include "ObjB.hpp"
-#include "SObjA.hpp"
-#include "SObjB.hpp"
-#include "subGlobal/GObj.hpp"
-
-int main(int argv, char** args)
-{
-  subGlobal::GObj gObj;
-  ObjA objA;
-  ObjB objB;
-  LObjA lObjA;
-  LObjB lObjB;
-  EObjA eObjA;
-  EObjB eObjB;
-  SObjA sObjA;
-  SObjB sObjB;
-  return 0;
-}
-
-// Header in global subdirectory
-#include "subGlobal/moc_GObj.cpp"
diff --git a/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt b/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt
new file mode 100644
index 0000000..1627b39
--- /dev/null
+++ b/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt
@@ -0,0 +1,80 @@
+cmake_minimum_required(VERSION 3.15)
+project(MocIncludeSymlink)
+include("../AutogenCoreTest.cmake")
+
+#
+# Tests if MocInclude can be build when
+# - The source directory is a symbolic link
+# - The build directory is a symbolic link
+#
+
+# -- Utility variables
+set(CS_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+set(CB_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+
+# Absolute MocInclude path
+get_filename_component(MocIncludePath "../MocInclude" ABSOLUTE)
+message("MocIncludePath: ${MocIncludePath}")
+
+# Use nested subdirectories to ensure relatives paths are correct as well
+set(BUILD_DIR_NORMAL "${CB_DIR}/Build/Normal")
+set(BUILD_DIR_LINKED "${CB_DIR}/Build/Linked")
+
+set(SL_SOURCE_DIR "${CB_DIR}/SL_Source")
+set(SL_BUILD_DIR "${CB_DIR}/SL_Build")
+
+# -- Utility macros
+function(makeSymLink origin link)
+    message("Creating symbolic link\n  Link: ${link}\n  To:   ${origin}")
+    file(CREATE_LINK ${origin} ${link} RESULT res SYMBOLIC)
+    if(NOT (${res} STREQUAL "0"))
+      message("Symlink creation failed.\n  Link: ${link}\n  To:   ${origin}\n  Result: ${res}")
+    endif()
+endfunction()
+
+# -- Make source directory symlink
+makeSymLink(${MocIncludePath} ${SL_SOURCE_DIR} linkResult)
+if(NOT EXISTS ${SL_SOURCE_DIR})
+  message("Directory symlink can't be created.  Skipping test.")
+  return()
+endif()
+
+# -- Make normal build directory
+file(REMOVE_RECURSE ${BUILD_DIR_NORMAL})
+file(MAKE_DIRECTORY ${BUILD_DIR_NORMAL})
+
+# -- Make linked build directory and symlink
+file(REMOVE_RECURSE ${BUILD_DIR_LINKED})
+file(MAKE_DIRECTORY ${BUILD_DIR_LINKED})
+makeSymLink(${BUILD_DIR_LINKED} ${SL_BUILD_DIR} linkResult)
+if(NOT EXISTS ${SL_BUILD_DIR})
+  message("Directory symlink can't be created.  Skipping test.")
+  return()
+endif()
+
+
+# -- Building
+macro(buildMocInclude sourceDir binaryDir)
+  message("Building MocInclude\n  - source dir: ${sourceDir}\n  - binary dir: ${binaryDir}\n")
+  try_compile(result
+    "${binaryDir}"
+    "${sourceDir}"
+    MocInclude
+    CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
+                "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+                "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+    OUTPUT_VARIABLE output
+  )
+  if (result)
+    message(STATUS "--- Build success. ---")
+  else()
+    message(STATUS "\n### Building MocInclude failed. ###\n\n--- Output ---\n${output}")
+    message(FATAL_ERROR "--- Building MocInclude failed. End of output. ---")
+  endif()
+endmacro()
+
+message("\nTry building with\n  - symbolic link source dir\n  - non symbolic build dir\n")
+buildMocInclude(${SL_SOURCE_DIR} ${BUILD_DIR_NORMAL})
+
+message("\nTry building with\n  - symbolic link source dir\n  - symbolic link build dir\n")
+buildMocInclude(${SL_SOURCE_DIR} ${SL_BUILD_DIR})
diff --git a/Tests/QtAutogen/MocOnly/main.cpp b/Tests/QtAutogen/MocOnly/main.cpp
index b83b806..ec8da21 100644
--- a/Tests/QtAutogen/MocOnly/main.cpp
+++ b/Tests/QtAutogen/MocOnly/main.cpp
@@ -1,8 +1,9 @@
+#include <iostream>
+
 #include "IncA.hpp"
 #include "IncB.hpp"
 #include "StyleA.hpp"
 #include "StyleB.hpp"
-#include <iostream>
 
 int main(int argv, char** args)
 {
diff --git a/Tests/QtAutogen/MocOsMacros/TestClass.cpp b/Tests/QtAutogen/MocOsMacros/TestClass.cpp
index 13f8fd9..babc08b 100644
--- a/Tests/QtAutogen/MocOsMacros/TestClass.cpp
+++ b/Tests/QtAutogen/MocOsMacros/TestClass.cpp
@@ -1,4 +1,5 @@
 #include "TestClass.hpp"
+
 #include <iostream>
 
 void TestClass::open()
diff --git a/Tests/QtAutogen/MocOsMacros/main.cpp b/Tests/QtAutogen/MocOsMacros/main.cpp
index f8eec3c..c427345 100644
--- a/Tests/QtAutogen/MocOsMacros/main.cpp
+++ b/Tests/QtAutogen/MocOsMacros/main.cpp
@@ -1,6 +1,7 @@
-#include "TestClass.hpp"
 #include <QtGlobal>
 
+#include "TestClass.hpp"
+
 int main()
 {
   TestClass a;
diff --git a/Tests/QtAutogen/ObjectLibrary/a/classa.cpp b/Tests/QtAutogen/ObjectLibrary/a/classa.cpp
index 4f08fda..a548652 100644
--- a/Tests/QtAutogen/ObjectLibrary/a/classa.cpp
+++ b/Tests/QtAutogen/ObjectLibrary/a/classa.cpp
@@ -1,4 +1,5 @@
 #include "classa.h"
+
 #include <QDebug>
 
 void ClassA::slotDoSomething()
diff --git a/Tests/QtAutogen/ObjectLibrary/b/classb.cpp b/Tests/QtAutogen/ObjectLibrary/b/classb.cpp
index 26e0926..e5cc2e7 100644
--- a/Tests/QtAutogen/ObjectLibrary/b/classb.cpp
+++ b/Tests/QtAutogen/ObjectLibrary/b/classb.cpp
@@ -1,4 +1,5 @@
 #include "classb.h"
+
 #include <QDebug>
 
 void ClassB::slotDoSomething()
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp
index 35158a4..e1fdf0b 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp
@@ -1,9 +1,10 @@
 #ifndef STYLEA_HPP
 #define STYLEA_HPP
 
-#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
+#include "UtilityMacros.hpp"
+
 class StyleA : public QStylePlugin
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp
index 15b79c5..7550d0c 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp
@@ -1,9 +1,10 @@
 #ifndef STYLEB_HPP
 #define STYLEB_HPP
 
-#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
+#include "UtilityMacros.hpp"
+
 class StyleB : public QStylePlugin
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp
index b0a4115..ec71bec 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp
@@ -1,9 +1,10 @@
 #ifndef STYLEC_HPP
 #define STYLEC_HPP
 
-#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
+#include "UtilityMacros.hpp"
+
 class StyleC : public QStylePlugin
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp
index 9696aaa..3c093b9 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp
@@ -1,9 +1,10 @@
 #ifndef STYLED_HPP
 #define STYLED_HPP
 
-#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
+#include "UtilityMacros.hpp"
+
 class StyleD : public QStylePlugin
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp
index f9734db..5f10fb4 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp
@@ -1,9 +1,10 @@
 #ifndef STYLEE_INCLUDE_HPP
 #define STYLEE_INCLUDE_HPP
 
-#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
+#include "UtilityMacros.hpp"
+
 class StyleE : public QStylePlugin
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/SameName/CMakeLists.txt b/Tests/QtAutogen/SameName/CMakeLists.txt
index 0a80d5e..1919cc7 100644
--- a/Tests/QtAutogen/SameName/CMakeLists.txt
+++ b/Tests/QtAutogen/SameName/CMakeLists.txt
@@ -18,9 +18,11 @@
   ccc/data.qrc
   item.cpp
   object.h
+  object.hh
   object.h++
   object.hpp
   object.hxx
+  object_upper_ext.H
   data.qrc
   main.cpp
 )
diff --git a/Tests/QtAutogen/SameName/main.cpp b/Tests/QtAutogen/SameName/main.cpp
index 92f15cd..725f4cd 100644
--- a/Tests/QtAutogen/SameName/main.cpp
+++ b/Tests/QtAutogen/SameName/main.cpp
@@ -6,8 +6,10 @@
 #include "item.hpp"
 #include "object.h"
 #include "object.h++"
+#include "object.hh"
 #include "object.hpp"
 #include "object.hxx"
+#include "object_upper_ext.H"
 
 int main(int argv, char** args)
 {
@@ -20,8 +22,10 @@
   ::ccc::Item ccc_item;
   // Object instances
   ::Object_h obj_h;
+  ::Object_hh obj_hh;
   ::Object_hplpl obj_hplpl;
   ::Object_hpp obj_hpp;
   ::Object_hxx obj_hxx;
+  ::Object_Upper_Ext_H obj_upper_ext_h;
   return 0;
 }
diff --git a/Tests/QtAutogen/SameName/object.hh b/Tests/QtAutogen/SameName/object.hh
new file mode 100644
index 0000000..3e16f83
--- /dev/null
+++ b/Tests/QtAutogen/SameName/object.hh
@@ -0,0 +1,13 @@
+#ifndef OBJECT_HH
+#define OBJECT_HH
+
+#include <QObject>
+
+class Object_hh : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go(){};
+};
+
+#endif
diff --git a/Tests/QtAutogen/SameName/object_upper_ext.H b/Tests/QtAutogen/SameName/object_upper_ext.H
new file mode 100644
index 0000000..3266087
--- /dev/null
+++ b/Tests/QtAutogen/SameName/object_upper_ext.H
@@ -0,0 +1,13 @@
+#ifndef OBJECT_UPPER_EXT_H
+#define OBJECT_UPPER_EXT_H
+
+#include <QObject>
+
+class Object_Upper_Ext_H : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go(){};
+};
+
+#endif
diff --git a/Tests/QtAutogen/StaticLibraryCycle/a.cpp b/Tests/QtAutogen/StaticLibraryCycle/a.cpp
index faa52e6..8dd1803 100644
--- a/Tests/QtAutogen/StaticLibraryCycle/a.cpp
+++ b/Tests/QtAutogen/StaticLibraryCycle/a.cpp
@@ -1,4 +1,5 @@
 #include "a.h"
+
 #include "b.h"
 
 bool A::recursed = false;
diff --git a/Tests/QtAutogen/StaticLibraryCycle/b.cpp b/Tests/QtAutogen/StaticLibraryCycle/b.cpp
index a807d89..dee2617 100644
--- a/Tests/QtAutogen/StaticLibraryCycle/b.cpp
+++ b/Tests/QtAutogen/StaticLibraryCycle/b.cpp
@@ -1,4 +1,5 @@
 #include "b.h"
+
 #include "c.h"
 
 B::B()
diff --git a/Tests/QtAutogen/StaticLibraryCycle/c.cpp b/Tests/QtAutogen/StaticLibraryCycle/c.cpp
index 7d427c2..2871f62 100644
--- a/Tests/QtAutogen/StaticLibraryCycle/c.cpp
+++ b/Tests/QtAutogen/StaticLibraryCycle/c.cpp
@@ -1,4 +1,5 @@
 #include "c.h"
+
 #include "a.h"
 
 C::C()
diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake
index 6771828..2b001d4 100644
--- a/Tests/QtAutogen/Tests.cmake
+++ b/Tests/QtAutogen/Tests.cmake
@@ -32,8 +32,8 @@
 
 if(QT_TEST_ALLOW_QT_MACROS)
   ADD_AUTOGEN_TEST(MocCMP0071)
-  ADD_AUTOGEN_TEST(MocIncludeRelaxed mocIncludeRelaxed)
-  ADD_AUTOGEN_TEST(MocIncludeStrict mocIncludeStrict)
+  ADD_AUTOGEN_TEST(MocInclude)
+  ADD_AUTOGEN_TEST(MocIncludeSymlink)
   ADD_AUTOGEN_TEST(MocSkipSource)
 endif()
 
diff --git a/Tests/QtAutogen/UicInterface/libwidget.h b/Tests/QtAutogen/UicInterface/libwidget.h
index a7ad14f..532ff1d 100644
--- a/Tests/QtAutogen/UicInterface/libwidget.h
+++ b/Tests/QtAutogen/UicInterface/libwidget.h
@@ -2,9 +2,10 @@
 #ifndef LIBWIDGET_H
 #define LIBWIDGET_H
 
-#include <QWidget>
 #include <memory>
 
+#include <QWidget>
+
 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
 #  include <klocalizedstring.h>
 #endif
diff --git a/Tests/QtAutogen/UicInterface/mywidget.h b/Tests/QtAutogen/UicInterface/mywidget.h
index 1d31ce7..320d433 100644
--- a/Tests/QtAutogen/UicInterface/mywidget.h
+++ b/Tests/QtAutogen/UicInterface/mywidget.h
@@ -2,9 +2,10 @@
 #ifndef MYWIDGET_H
 #define MYWIDGET_H
 
-#include <QWidget>
 #include <memory>
 
+#include <QWidget>
+
 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
 #  include <klocalizedstring.h>
 #endif
diff --git a/Tests/QtAutogen/UicSkipSource/skipUicGen.cpp b/Tests/QtAutogen/UicSkipSource/skipUicGen.cpp
index d2a55a6..ab3c454 100644
--- a/Tests/QtAutogen/UicSkipSource/skipUicGen.cpp
+++ b/Tests/QtAutogen/UicSkipSource/skipUicGen.cpp
@@ -1,5 +1,6 @@
 
 #include "skipUicGen.hpp"
+
 #include "ui_uigen2.h"
 
 void skipGen()
diff --git a/Tests/QtAutogen/UicSkipSource/skipUicNoGen1.cpp b/Tests/QtAutogen/UicSkipSource/skipUicNoGen1.cpp
index f591a42..d648d94 100644
--- a/Tests/QtAutogen/UicSkipSource/skipUicNoGen1.cpp
+++ b/Tests/QtAutogen/UicSkipSource/skipUicNoGen1.cpp
@@ -1,5 +1,6 @@
 
 #include "skipUicNoGen1.hpp"
+
 #include "ui_nogen2.h"
 
 void skipNoGen1()
diff --git a/Tests/QtAutogen/UicSkipSource/skipUicNoGen2.cpp b/Tests/QtAutogen/UicSkipSource/skipUicNoGen2.cpp
index 8c1c324..aca58a4 100644
--- a/Tests/QtAutogen/UicSkipSource/skipUicNoGen2.cpp
+++ b/Tests/QtAutogen/UicSkipSource/skipUicNoGen2.cpp
@@ -1,5 +1,6 @@
 
 #include "skipUicNoGen2.hpp"
+
 #include "ui_nogen2.h"
 
 void skipNoGen2()
diff --git a/Tests/RunCMake/Android/common.cmake b/Tests/RunCMake/Android/common.cmake
index aaa7c89..d96ab86 100644
--- a/Tests/RunCMake/Android/common.cmake
+++ b/Tests/RunCMake/Android/common.cmake
@@ -6,8 +6,6 @@
 endif()
 
 foreach(f
-    "${CMAKE_C_ANDROID_TOOLCHAIN_PREFIX}gcc${CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX}"
-    "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}g++${CMAKE_CXX_ANDROID_TOOLCHAIN_SUFFIX}"
     "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}ar${CMAKE_CXX_ANDROID_TOOLCHAIN_SUFFIX}"
     "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}ld${CMAKE_CXX_ANDROID_TOOLCHAIN_SUFFIX}"
     )
@@ -51,23 +49,26 @@
   endif()
 endif()
 
-execute_process(
-  COMMAND "${CMAKE_C_ANDROID_TOOLCHAIN_PREFIX}gcc${CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX}" -dumpmachine
-  OUTPUT_VARIABLE _out OUTPUT_STRIP_TRAILING_WHITESPACE
-  ERROR_VARIABLE _err
-  RESULT_VARIABLE _res
-  )
-if(NOT _res EQUAL 0)
-  message(SEND_ERROR "Failed to run 'gcc -dumpmachine':\n ${_res}")
-endif()
-string(REPLACE "--" "-" _out_check "${_out}")
-if(NOT _out_check STREQUAL "${CMAKE_C_ANDROID_TOOLCHAIN_MACHINE}"
-    AND NOT (_out STREQUAL "arm--linux-android" AND CMAKE_C_ANDROID_TOOLCHAIN_MACHINE STREQUAL "arm-linux-androideabi"))
-  message(SEND_ERROR "'gcc -dumpmachine' produced:\n"
-    " ${_out}\n"
-    "which does not match CMAKE_C_ANDROID_TOOLCHAIN_MACHINE:\n"
-    " ${CMAKE_C_ANDROID_TOOLCHAIN_MACHINE}"
+set(gcc ${CMAKE_C_ANDROID_TOOLCHAIN_PREFIX}gcc${CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX})
+if(EXISTS "${gcc}")
+  execute_process(
+    COMMAND "${CMAKE_C_ANDROID_TOOLCHAIN_PREFIX}gcc${CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX}" -dumpmachine
+    OUTPUT_VARIABLE _out OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_VARIABLE _err
+    RESULT_VARIABLE _res
     )
+  if(NOT _res EQUAL 0)
+    message(SEND_ERROR "Failed to run 'gcc -dumpmachine':\n ${_res}")
+  endif()
+  string(REPLACE "--" "-" _out_check "${_out}")
+  if(NOT _out_check STREQUAL "${CMAKE_C_ANDROID_TOOLCHAIN_MACHINE}"
+      AND NOT (_out STREQUAL "arm--linux-android" AND CMAKE_C_ANDROID_TOOLCHAIN_MACHINE STREQUAL "arm-linux-androideabi"))
+    message(SEND_ERROR "'gcc -dumpmachine' produced:\n"
+      " ${_out}\n"
+      "which does not match CMAKE_C_ANDROID_TOOLCHAIN_MACHINE:\n"
+      " ${CMAKE_C_ANDROID_TOOLCHAIN_MACHINE}"
+      )
+  endif()
 endif()
 
 if(CMAKE_ANDROID_STL_TYPE STREQUAL "none")
diff --git a/Tests/RunCMake/Android/ndk-arm64-v8a-stdout.txt b/Tests/RunCMake/Android/ndk-arm64-v8a-stdout.txt
index 8d0bdc2..a228ccc 100644
--- a/Tests/RunCMake/Android/ndk-arm64-v8a-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-arm64-v8a-stdout.txt
@@ -1,2 +1,2 @@
 -- Android: Targeting API '[0-9]+' with architecture 'arm64', ABI 'arm64-v8a', and processor 'aarch64'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
diff --git a/Tests/RunCMake/Android/ndk-armeabi-arm-stdout.txt b/Tests/RunCMake/Android/ndk-armeabi-arm-stdout.txt
index 3741da3..72ec00e 100644
--- a/Tests/RunCMake/Android/ndk-armeabi-arm-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-armeabi-arm-stdout.txt
@@ -1,3 +1,3 @@
 -- Android: Targeting API '[0-9]+' with architecture 'arm', ABI 'armeabi', and processor 'armv5te'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
 .*-- CMAKE_ANDROID_ARM_MODE=1
diff --git a/Tests/RunCMake/Android/ndk-armeabi-v7a-neon-stdout.txt b/Tests/RunCMake/Android/ndk-armeabi-v7a-neon-stdout.txt
index ac2bfd5..8bd87fa 100644
--- a/Tests/RunCMake/Android/ndk-armeabi-v7a-neon-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-armeabi-v7a-neon-stdout.txt
@@ -1,3 +1,3 @@
 -- Android: Targeting API '[0-9]+' with architecture 'arm', ABI 'armeabi-v7a', and processor 'armv7-a'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
 .*-- CMAKE_ANDROID_ARM_NEON=1
diff --git a/Tests/RunCMake/Android/ndk-armeabi-v7a-stdout.txt b/Tests/RunCMake/Android/ndk-armeabi-v7a-stdout.txt
index 0edb4f7..554548e 100644
--- a/Tests/RunCMake/Android/ndk-armeabi-v7a-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-armeabi-v7a-stdout.txt
@@ -1,3 +1,3 @@
 -- Android: Targeting API '[0-9]+' with architecture 'arm', ABI 'armeabi-v7a', and processor 'armv7-a'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
 .*-- CMAKE_ANDROID_ARM_NEON=0
diff --git a/Tests/RunCMake/Android/ndk-badver-stderr.txt b/Tests/RunCMake/Android/ndk-badver-stderr.txt
index df2c5e6..ce6bc4e 100644
--- a/Tests/RunCMake/Android/ndk-badver-stderr.txt
+++ b/Tests/RunCMake/Android/ndk-badver-stderr.txt
@@ -1,11 +1,12 @@
 ^CMake Error at .*/Modules/Platform/Android/Determine-Compiler-NDK.cmake:[0-9]+ \(message\):
-  Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value 'badver' is not one
+  Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value 'badver' is not(
+  supported by this NDK.  It must be 'clang' or not set at all\.| one
   of the allowed forms:
 
     <major>.<minor>       = GCC of specified version
     clang<major>.<minor>  = Clang of specified version
     clang                 = Clang of most recent available version
-
+)
 Call Stack \(most recent call first\):
 .*
   ndk-badver.cmake:1 \(enable_language\)
diff --git a/Tests/RunCMake/Android/ndk-badvernum-stderr.txt b/Tests/RunCMake/Android/ndk-badvernum-stderr.txt
index adacaf1..aec91d9 100644
--- a/Tests/RunCMake/Android/ndk-badvernum-stderr.txt
+++ b/Tests/RunCMake/Android/ndk-badvernum-stderr.txt
@@ -1,4 +1,6 @@
-^CMake Error at .*/Modules/Platform/Android/Determine-Compiler-NDK.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Modules/Platform/Android/Determine-Compiler-NDK.cmake:[0-9]+ \(message\):(
+  Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value '1.0' is not
+  supported by this NDK.  It must be 'clang' or not set at all.|
   Android: No toolchain for ABI 'armeabi(-v7a)?' found in the NDK:
 
     .*
@@ -6,7 +8,7 @@
   of the version specified by CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION:
 
     1\.0
-
+)
 Call Stack \(most recent call first\):
 .*
   ndk-badvernum.cmake:1 \(enable_language\)
diff --git a/Tests/RunCMake/Android/ndk-mips-stdout.txt b/Tests/RunCMake/Android/ndk-mips-stdout.txt
index c744683..8ce544d 100644
--- a/Tests/RunCMake/Android/ndk-mips-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-mips-stdout.txt
@@ -1,2 +1,2 @@
 -- Android: Targeting API '[0-9]+' with architecture 'mips', ABI 'mips', and processor 'mips'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
diff --git a/Tests/RunCMake/Android/ndk-mips64-stdout.txt b/Tests/RunCMake/Android/ndk-mips64-stdout.txt
index 839ddfd..1d7edea 100644
--- a/Tests/RunCMake/Android/ndk-mips64-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-mips64-stdout.txt
@@ -1,2 +1,2 @@
 -- Android: Targeting API '[0-9]+' with architecture 'mips64', ABI 'mips64', and processor 'mips64'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
diff --git a/Tests/RunCMake/Android/ndk-x86-stdout.txt b/Tests/RunCMake/Android/ndk-x86-stdout.txt
index 2dbb2f0..8d710fe 100644
--- a/Tests/RunCMake/Android/ndk-x86-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-x86-stdout.txt
@@ -1,2 +1,2 @@
 -- Android: Targeting API '[0-9]+' with architecture 'x86', ABI 'x86', and processor 'i686'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
diff --git a/Tests/RunCMake/Android/ndk-x86_64-stdout.txt b/Tests/RunCMake/Android/ndk-x86_64-stdout.txt
index a7ae91d..695a088 100644
--- a/Tests/RunCMake/Android/ndk-x86_64-stdout.txt
+++ b/Tests/RunCMake/Android/ndk-x86_64-stdout.txt
@@ -1,2 +1,2 @@
 -- Android: Targeting API '[0-9]+' with architecture 'x86_64', ABI 'x86_64', and processor 'x86_64'
--- Android: Selected (Clang toolchain '[^']+' with )?GCC toolchain '[^']+'
+-- Android: Selected (unified Clang toolchain|(Clang toolchain '[^']+' with )?GCC toolchain '[^']+')
diff --git a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
index 27a609d..6c9be4b 100644
--- a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
+++ b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
@@ -7,9 +7,13 @@
 run_cmake(AutoExport)
 unset(RunCMake_TEST_OPTIONS)
 # don't run this test on Watcom or Borland make as it is not supported
-if("${RunCMake_GENERATOR}" MATCHES "Watcom WMake|Borland Makefiles")
+if(RunCMake_GENERATOR MATCHES "Watcom WMake|Borland Makefiles")
   return()
 endif()
+if(RunCMake_GENERATOR MATCHES "Ninja|Visual Studio" AND
+   CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+  set(EXPORTS TRUE)
+endif()
 # we build debug so the say.exe will be found in Debug/say.exe for
 # Visual Studio generators
 if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
@@ -18,9 +22,36 @@
 # build AutoExport
 run_cmake_command(AutoExportBuild ${CMAKE_COMMAND} --build
   ${RunCMake_TEST_BINARY_DIR} --config Debug --clean-first)
+# save the current timestamp of exports.def
+if(EXPORTS)
+  set(EXPORTS_DEF "${RunCMake_TEST_BINARY_DIR}/say.dir/${INTDIR}exports.def")
+  if(NOT EXISTS "${EXPORTS_DEF}")
+    set(EXPORTS_DEF
+      "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/say.dir/${INTDIR}exports.def")
+  endif()
+  file(TIMESTAMP "${EXPORTS_DEF}" timestamp)
+  if(NOT timestamp)
+    message(SEND_ERROR "Could not get timestamp for \"${EXPORTS_DEF}\"")
+  endif()
+endif()
 # run the executable that uses symbols from the dll
 if(WIN32)
   set(EXE_EXT ".exe")
 endif()
 run_cmake_command(AutoExportRun
-  ${RunCMake_BINARY_DIR}/AutoExport-build/bin/${INTDIR}say${EXE_EXT})
+  ${RunCMake_TEST_BINARY_DIR}/bin/${INTDIR}say${EXE_EXT})
+# build AutoExport again without modification
+run_cmake_command(AutoExportBuildAgain ${CMAKE_COMMAND} --build
+  ${RunCMake_TEST_BINARY_DIR} --config Debug)
+# compare timestamps of exports.def to make sure it has not been updated
+if(EXPORTS)
+  file(TIMESTAMP "${EXPORTS_DEF}" timestamp_after)
+  if(NOT timestamp_after)
+    message(SEND_ERROR "Could not get timestamp for \"${EXPORTS_DEF}\"")
+  endif()
+  if (timestamp_after STREQUAL timestamp)
+    message(STATUS "AutoExportTimeStamp - PASSED")
+  else()
+    message(SEND_ERROR "\"${EXPORTS_DEF}\" has been updated.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/AutoExportDll/hello.cxx b/Tests/RunCMake/AutoExportDll/hello.cxx
index 533fd3e..74e7a4e 100644
--- a/Tests/RunCMake/AutoExportDll/hello.cxx
+++ b/Tests/RunCMake/AutoExportDll/hello.cxx
@@ -1,4 +1,5 @@
 #include "hello.h"
+
 #include <stdio.h>
 int Hello::Data = 0;
 void Hello::real()
diff --git a/Tests/RunCMake/AutoExportDll/say.cxx b/Tests/RunCMake/AutoExportDll/say.cxx
index c8bfcc7..654b5e0 100644
--- a/Tests/RunCMake/AutoExportDll/say.cxx
+++ b/Tests/RunCMake/AutoExportDll/say.cxx
@@ -1,5 +1,6 @@
-#include "hello.h"
 #include <stdio.h>
+
+#include "hello.h"
 #ifdef _MSC_VER
 #  include "windows.h"
 #else
diff --git a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
index 254a4ec..e86b50e 100644
--- a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
@@ -1,8 +1,11 @@
 include(RunCMake)
 
 run_cmake(OLDBad1)
-run_cmake(OLDBad2)
-run_cmake(NEWBad)
+if(NOT CMAKE_SYSTEM_NAME STREQUAL "AIX")
+  # Tests with ENABLE_EXPORTS ON.  For AIX we do not use the flags at all.
+  run_cmake(OLDBad2)
+  run_cmake(NEWBad)
+endif()
 run_cmake(NEWGood)
 run_cmake(WARN-OFF)
 run_cmake(WARN-ON)
diff --git a/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt b/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt
new file mode 100644
index 0000000..a736129
--- /dev/null
+++ b/Tests/RunCMake/CMP0068/CMP0068-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0068-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0068 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt b/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt
new file mode 100644
index 0000000..f51a6f4
--- /dev/null
+++ b/Tests/RunCMake/CMP0069/CMP0069-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0069-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0069 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 69f8162..0925c0e 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -120,7 +120,7 @@
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
 # generators ignore.  The policy will have no effect on those generators.
 if(NOT CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
-  add_RunCMake_test(CMP0065)
+  add_RunCMake_test(CMP0065 -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 endif()
 if(CMAKE_GENERATOR MATCHES "Make")
   add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU})
@@ -167,7 +167,7 @@
   add_RunCMake_test(CompilerChange)
 endif()
 add_RunCMake_test(CompilerNotFound)
-add_RunCMake_test(Configure)
+add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
 add_RunCMake_test(DisallowedCommands)
 add_RunCMake_test(ExternalData)
 add_RunCMake_test(FeatureSummary)
@@ -201,6 +201,7 @@
 endif()
 add_RunCMake_test(ObjectLibrary)
 add_RunCMake_test(ParseImplicitIncludeInfo)
+add_RunCMake_test(ParseImplicitLinkInfo)
 if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
   add_RunCMake_test(RuntimePath)
 endif()
@@ -248,7 +249,8 @@
 add_RunCMake_test(cmake_minimum_required)
 add_RunCMake_test(cmake_parse_arguments)
 add_RunCMake_test(continue)
-add_RunCMake_test(ctest_build)
+add_executable(color_warning color_warning.c)
+add_RunCMake_test(ctest_build -DCOLOR_WARNING=$<TARGET_FILE:color_warning>)
 add_RunCMake_test(ctest_cmake_error)
 add_RunCMake_test(ctest_configure)
 if(COVERAGE_COMMAND)
@@ -333,6 +335,44 @@
 add_RunCMake_test(configure_file)
 add_RunCMake_test(CTestTimeoutAfterMatch)
 
+# cthwalloc links against CMakeLib and CTestLib, which means it can't be built
+# if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.)
+# So, it has to be provided in the original build tree.
+if(CMake_TEST_EXTERNAL_CMAKE)
+  set(no_package_root_path)
+  if(NOT CMAKE_VERSION VERSION_LESS 3.12)
+    set(no_package_root_path NO_PACKAGE_ROOT_PATH)
+  endif()
+  find_program(cthwalloc cthwalloc PATHS ${CMake_TEST_EXTERNAL_CMAKE}
+    NO_DEFAULT_PATH
+    ${no_package_root_path}
+    NO_CMAKE_PATH
+    NO_CMAKE_ENVIRONMENT_PATH
+    NO_SYSTEM_ENVIRONMENT_PATH
+    NO_CMAKE_SYSTEM_PATH
+    NO_CMAKE_FIND_ROOT_PATH
+    )
+  if(cthwalloc)
+    add_executable(cthwalloc IMPORTED)
+    set_property(TARGET cthwalloc PROPERTY IMPORTED_LOCATION ${cthwalloc})
+  endif()
+else()
+  add_executable(cthwalloc CTestHardwareAllocation/cthwalloc.cxx)
+  target_link_libraries(cthwalloc CTestLib)
+  target_include_directories(cthwalloc PRIVATE
+    ${CMake_BINARY_DIR}/Source
+    ${CMake_SOURCE_DIR}/Source
+    ${CMake_SOURCE_DIR}/Source/CTest
+    )
+  set_property(TARGET cthwalloc PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMake_BIN_DIR})
+endif()
+
+if(TARGET cthwalloc)
+  add_RunCMake_test(CTestHardwareAllocation -DCTHWALLOC_COMMAND=$<TARGET_FILE:cthwalloc>)
+else()
+  message(WARNING "Could not find or build cthwalloc")
+endif()
+
 find_package(Qt4 QUIET)
 find_package(Qt5Core QUIET)
 if (QT4_FOUND AND Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.1.0)
@@ -408,7 +448,12 @@
   set(NO_NAMELINK 0)
 endif()
 
-add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN})
+add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+  -DCMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN=${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}
+  -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+  -DCMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG=${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}
+  -DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT})
+
 add_RunCMake_test(CPackCommandLine)
 add_RunCMake_test(CPackConfig)
 add_RunCMake_test(CPackInstallProperties)
@@ -427,8 +472,8 @@
   add_RunCMake_test(CPackSymlinks)
 endif()
 
-set(IfacePaths_INCLUDE_DIRECTORIES_ARGS -DTEST_PROP=INCLUDE_DIRECTORIES)
-add_RunCMake_test(IfacePaths_INCLUDE_DIRECTORIES TEST_DIR IfacePaths)
+set(IfacePaths_INCDIRS_ARGS -DTEST_PROP=INCLUDE_DIRECTORIES)
+add_RunCMake_test(IfacePaths_INCDIRS TEST_DIR IfacePaths)
 
 set(IfacePaths_SOURCES_ARGS -DTEST_PROP=SOURCES)
 add_RunCMake_test(IfacePaths_SOURCES TEST_DIR IfacePaths)
@@ -503,6 +548,7 @@
   DEB.TIMESTAMPS
   DEB.MD5SUMS
   DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY
+  DEB.DEB_DESCRIPTION
 
   RPM.CUSTOM_BINARY_SPEC_FILE
   RPM.CUSTOM_NAMES
@@ -536,7 +582,7 @@
 add_RunCMake_test_group(CPack "${cpack_tests}")
 # add a test to make sure symbols are exported from a shared library
 # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used
-add_RunCMake_test(AutoExportDll)
+add_RunCMake_test(AutoExportDll -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID})
 
 add_RunCMake_test(AndroidMK)
 
@@ -565,3 +611,8 @@
   add_RunCMake_test(CSharpCustomCommand)
   add_RunCMake_test(CSharpReferenceImport)
 endif()
+
+add_RunCMake_test("CTestCommandExpandLists")
+
+add_RunCMake_test(PrecompileHeaders)
+add_RunCMake_test("UnityBuild")
diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake
index 37df57c..b0b7a99 100644
--- a/Tests/RunCMake/CPack/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake
@@ -33,6 +33,16 @@
 unset(ENVIRONMENT)
 run_cpack_test(USER_FILELIST "RPM.USER_FILELIST" false "MONOLITHIC")
 run_cpack_test(MD5SUMS "DEB.MD5SUMS" false "MONOLITHIC;COMPONENT")
-run_cpack_test(CPACK_INSTALL_SCRIPT "ZIP" false "MONOLITHIC")
+run_cpack_test_subtests(CPACK_INSTALL_SCRIPTS "singular;plural;both" "ZIP" false "MONOLITHIC")
 run_cpack_test(DEB_PACKAGE_VERSION_BACK_COMPATIBILITY "DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY" false "MONOLITHIC;COMPONENT")
 run_cpack_test_subtests(EXTERNAL "none;good;good_multi;bad_major;bad_minor;invalid_good;invalid_bad;stage_and_package" "External" false "MONOLITHIC;COMPONENT")
+if(RunCMake_GENERATOR MATCHES "Visual Studio|Xcode")
+  run_cpack_test(CPACK_INSTALL_CMAKE_CONFIGURATIONS "ZIP" false "MONOLITHIC")
+endif()
+run_cpack_test_subtests(
+  DEB_DESCRIPTION
+  "CPACK_DEBIAN_PACKAGE_DESCRIPTION;CPACK_PACKAGE_DESCRIPTION;CPACK_PACKAGE_DESCRIPTION_FILE"
+  "DEB.DEB_DESCRIPTION"
+  false
+  "MONOLITHIC;COMPONENT"
+)
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ExpectedFiles.cmake
new file mode 100644
index 0000000..34c7f8a
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ExpectedFiles.cmake
@@ -0,0 +1,3 @@
+set(EXPECTED_FILES_COUNT "1")
+
+set(EXPECTED_FILE_CONTENT_1_LIST "foo;foo/debug.txt;foo/release.txt")
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ZIP-stdout.txt b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ZIP-stdout.txt
new file mode 100644
index 0000000..2a3aa9e
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/ZIP-stdout.txt
@@ -0,0 +1,3 @@
+CPack: Install projects
+CPack: - Install project: CPACK_INSTALL_CMAKE_CONFIGURATIONS-MONOLITHIC-type \[Debug\]
+CPack: - Install project: CPACK_INSTALL_CMAKE_CONFIGURATIONS-MONOLITHIC-type \[Release\]
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/test.cmake b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/test.cmake
new file mode 100644
index 0000000..4f562cf
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_CMAKE_CONFIGURATIONS/test.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_CONFIGURATION_TYPES Debug Release)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/debug.txt" "debug content")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/release.txt" "release content")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/debug.txt" DESTINATION "foo" CONFIGURATIONS Debug)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/release.txt" DESTINATION "foo" CONFIGURATIONS Release)
+
+set(CPACK_INSTALL_CMAKE_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES})
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPT/test.cmake b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPT/test.cmake
deleted file mode 100644
index e3fe0ca..0000000
--- a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPT/test.cmake
+++ /dev/null
@@ -1,11 +0,0 @@
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/abc.txt" "test content")
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/user-script.cmake"
-  "file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/foo\"
-    TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/abc.txt\")")
-set(CPACK_INSTALL_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/user-script.cmake")
-
-function(run_after_include_cpack)
-  file(READ "${CPACK_OUTPUT_CONFIG_FILE}" conf_file_)
-  string(REGEX REPLACE "SET\\(CPACK_INSTALL_CMAKE_PROJECTS [^)]*\\)" "" conf_file_ "${conf_file_}")
-  file(WRITE "${CPACK_OUTPUT_CONFIG_FILE}" "${conf_file_}")
-endfunction()
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPT/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/ExpectedFiles.cmake
similarity index 100%
rename from Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPT/ExpectedFiles.cmake
rename to Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/ExpectedFiles.cmake
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/both-stderr.txt b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/both-stderr.txt
new file mode 100644
index 0000000..666030e
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/both-stderr.txt
@@ -0,0 +1 @@
+CPack Warning: Both CPACK_INSTALL_SCRIPTS and CPACK_INSTALL_SCRIPT are set, the latter will be ignored.
diff --git a/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/test.cmake b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/test.cmake
new file mode 100644
index 0000000..249d2e6
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/CPACK_INSTALL_SCRIPTS/test.cmake
@@ -0,0 +1,26 @@
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/abc.txt" "test content")
+set(user_script_ "${CMAKE_CURRENT_BINARY_DIR}/user-script.cmake")
+file(WRITE "${user_script_}"
+  "file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/foo\"
+    TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/abc.txt\")")
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "both")
+    set(CPACK_INSTALL_SCRIPT "${user_script_}")
+    set(CPACK_INSTALL_SCRIPTS "${CPACK_INSTALL_SCRIPT}")
+
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "singular")
+    set(CPACK_INSTALL_SCRIPT "${user_script_}")
+
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "plural")
+    set(CPACK_INSTALL_SCRIPTS "${user_script_}")
+
+else()
+    message(FATAL_ERROR "Unexpected subtest name: ${RunCMake_SUBTEST_SUFFIX}")
+
+endif()
+
+function(run_after_include_cpack)
+  file(READ "${CPACK_OUTPUT_CONFIG_FILE}" conf_file_)
+  string(REGEX REPLACE "SET\\(CPACK_INSTALL_CMAKE_PROJECTS [^)]*\\)" "" conf_file_ "${conf_file_}")
+  file(WRITE "${CPACK_OUTPUT_CONFIG_FILE}" "${conf_file_}")
+endfunction()
diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake
new file mode 100644
index 0000000..39f18a3
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/ExpectedFiles.cmake
@@ -0,0 +1,16 @@
+set(EXPECTED_FILES_COUNT_MONOLITHIC "1")
+set(EXPECTED_FILES_COUNT_COMPONENT "2")
+set(EXPECTED_FILES_COUNT "${EXPECTED_FILES_COUNT_${PACKAGING_TYPE}}")
+
+if(PACKAGING_TYPE STREQUAL "COMPONENT")
+  set(EXPECTED_FILE_1 "deb_description-0.1.1-*-satu.deb")
+  set(EXPECTED_FILE_2 "deb_description-0.1.1-*-dua.deb")
+  set(EXPECTED_FILE_CONTENT_1_LIST "/satu;/satu/CMakeLists.txt")
+  set(EXPECTED_FILE_CONTENT_2_LIST "/dua;/dua/CMakeLists.txt")
+
+elseif(PACKAGING_TYPE STREQUAL "MONOLITHIC")
+  set(EXPECTED_FILE_CONTENT_1_LIST "/dua;/dua/CMakeLists.txt;/satu;/satu/CMakeLists.txt")
+
+endif()
+
+# kate: indent-width 2;
diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake
new file mode 100644
index 0000000..e9ac13a
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/VerifyResult.cmake
@@ -0,0 +1,65 @@
+function(checkPackageDescription FILE EXPECTED_DESCRIPTION)
+  getPackageInfo("${FILE}" "_file_info")
+  string(UUID uuid NAMESPACE 00000000-0000-0000-0000-000000000000 TYPE SHA1)
+  string(REPLACE ";" "${uuid}" _file_info "${_file_info}")
+  string(REPLACE ";" "${uuid}" EXPECTED_DESCRIPTION "${EXPECTED_DESCRIPTION}")
+  string(REPLACE "\n" ";" _file_info "${_file_info}")
+
+  set(_actual_description)
+  set(_parse_description FALSE)
+  foreach(_line IN LISTS _file_info)
+    if(_line MATCHES " Description:.*")
+      set(_parse_description TRUE)
+      list(APPEND _actual_description "${_line}")
+    elseif(_parse_description)
+      if(_line MATCHES " [A-Z][A-Za-z\-]+: .*")
+        set(_parse_description FALSE)
+      else()
+        list(APPEND _actual_description "${_line}")
+      endif()
+    endif()
+  endforeach()
+  list(JOIN _actual_description "\n" _actual_description)
+
+  if(NOT _actual_description STREQUAL EXPECTED_DESCRIPTION)
+    set(_error "---[BEGIN Expected description]---\n${EXPECTED_DESCRIPTION}---[END Expected description]---\n")
+    string(APPEND _error "---[BEGIN Actual description]---\n${_actual_description}---[END Actual description]---\n")
+    string(REPLACE "${uuid}"  ";" _error "${_error}")
+    message(FATAL_ERROR "${_error}")
+  endif()
+endfunction()
+
+# ALERT The output of `dpkg -I *.deb` indented by one space
+set(_expected_description [[ Description: This is the summary line
+  This is the Debian package multiline description.
+  .
+  It must be formatted properly! Otherwise, the result `*.deb`
+  package become broken and cant be installed!
+  .
+  It may contains `;` characters (even like this `;;;;`). Example:
+  .
+    - one;
+    - two;
+    - three;
+  .
+  ... and they are properly handled by the automatic description formatter!
+  .
+  See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description]])
+
+# ATTENTION The code in `cmCPackGenerator.cxx` to read `CPACK_PACKAGE_DESCRIPTION_FILE`
+# has a BUG: it appends the `\n` character to every line of the
+# input, even if there was no EOL (e.g. at the last line of the file).
+# That is WHY for this sub-test the one more pre-formatted "empty"
+# line required!
+# NOTE For component based installers content of the file gonna read by
+# `CPackDeb` module and the `file(READ...)` command so no the mentioned
+# workaround required!
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION_FILE" AND PACKAGING_TYPE STREQUAL "MONOLITHIC")
+  string(APPEND _expected_description "\n  ." )
+endif()
+
+foreach(_file_no RANGE 1 ${EXPECTED_FILES_COUNT})
+  checkPackageDescription("${FOUND_FILE_${_file_no}}" "${_expected_description}")
+endforeach()
+
+# kate: indent-width 2;
diff --git a/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake
new file mode 100644
index 0000000..ce3f651
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DEB_DESCRIPTION/test.cmake
@@ -0,0 +1,45 @@
+install(FILES CMakeLists.txt DESTINATION satu COMPONENT satu)
+install(FILES CMakeLists.txt DESTINATION dua COMPONENT dua)
+
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "This is the summary line")
+set(_description [[This is the Debian package multiline description.
+
+It must be formatted properly! Otherwise, the result `*.deb`
+package become broken and cant be installed!
+
+It may contains `;` characters (even like this `;;;;`). Example:
+
+  - one;
+  - two;
+  - three;
+
+... and they are properly handled by the automatic description formatter!
+
+See also: https://www.debian.org/doc/debian-policy/ch-controlfields.html#description]])
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_DEBIAN_PACKAGE_DESCRIPTION")
+  if(PACKAGING_TYPE STREQUAL "COMPONENT")
+    set(CPACK_DEBIAN_SATU_DESCRIPTION "${_description}")
+    set(CPACK_DEBIAN_DUA_DESCRIPTION "${_description}")
+  else()
+    set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${_description}")
+  endif()
+
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION")
+  # NOTE Documented fallback variable
+  if(PACKAGING_TYPE STREQUAL "COMPONENT")
+    set(CPACK_COMPONENT_SATU_DESCRIPTION "${_description}")
+    set(CPACK_COMPONENT_DUA_DESCRIPTION "${_description}")
+  else()
+    set(CPACK_PACKAGE_DESCRIPTION "${_description}")
+  endif()
+
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "CPACK_PACKAGE_DESCRIPTION_FILE")
+  # NOTE Getting the description from the file
+  set(_file "${CMAKE_CURRENT_BINARY_DIR}/description.txt")
+  file(WRITE "${_file}" "${_description}")
+  set(CPACK_PACKAGE_DESCRIPTION_FILE "${_file}")
+
+endif()
+
+# kate: indent-width 2;
diff --git a/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake
index b4bdb61..c47b40e 100644
--- a/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake
+++ b/Tests/RunCMake/CPack/tests/PER_COMPONENT_FIELDS/VerifyResult.cmake
@@ -8,6 +8,9 @@
 if(GENERATOR_TYPE STREQUAL "DEB")
   set(name_ "Package")
   set(group_ "Section")
+  # NOTE For a Debian package the first line of the `Description`
+  # field is generated by CMake and gonna be ignored
+  set(ignore_rest_cond_ ".*\n")
 elseif(GENERATOR_TYPE STREQUAL "RPM")
   set(name_ "Name")
   set(group_ "Group")
@@ -33,6 +36,6 @@
 endif()
 
 # check package description
-checkPackageInfo_("description" "${FOUND_FILE_1}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_1")
-checkPackageInfo_("description" "${FOUND_FILE_2}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_2")
-checkPackageInfo_("description" "${FOUND_FILE_3}" ".*Description${whitespaces_}:${whitespaces_}Description for pkg_3")
+checkPackageInfo_("description" "${FOUND_FILE_1}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_1")
+checkPackageInfo_("description" "${FOUND_FILE_2}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_2")
+checkPackageInfo_("description" "${FOUND_FILE_3}" ".*Description${whitespaces_}:${ignore_rest_cond_}${whitespaces_}Description for pkg_3")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
new file mode 100644
index 0000000..3e470a2
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
new file mode 100644
index 0000000..7d56c90
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(@CASE_NAME@ NONE)
+include("@RunCMake_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
new file mode 100644
index 0000000..7c3779e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCTest)
+
+run_ctest(expandGeneratorExpressionResult)
+run_ctest(expandEmptyCommand)
+run_cmake(multipleExpandOptions)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
new file mode 100644
index 0000000..a32e579
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
@@ -0,0 +1,14 @@
+set(range 1 2 3 4 5 6 7 8 9 10)
+set(aargs "")
+set(bargs "")
+foreach(n IN LISTS range)
+  set(aval "${A${n}ARG}")
+  set(bval "${B${n}ARG}")
+  if(aval OR bval)
+    list(APPEND aargs "\"${aval}\"")
+    list(APPEND bargs "\"${bval}\"")
+  endif()
+endforeach()
+if(NOT "${aargs}" STREQUAL "${bargs}")
+  message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
new file mode 100644
index 0000000..c656b4c
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
@@ -0,0 +1 @@
+Unable to find executable:
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
new file mode 100644
index 0000000..0752580
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
@@ -0,0 +1,13 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-build
+.* +Start 1: CommandExpandEmptyList
+Could not find executable +
+Looked in the following places:
+.*
+1/1 Test #1: CommandExpandEmptyList +\.+\*\*\*Not Run +[0-9.]+ sec
++
+0% tests passed, 1 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
++
+The following tests FAILED:
+.* +1 - CommandExpandEmptyList \(Not Run\)$
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
new file mode 100644
index 0000000..b75828e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
@@ -0,0 +1,10 @@
+include(CTest)
+
+set(argv /bin/true)
+list(POP_BACK argv)
+
+add_test(
+  NAME CommandExpandEmptyList
+  COMMAND "$<JOIN:${argv},;>"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
new file mode 100644
index 0000000..2f21592
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
@@ -0,0 +1,7 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-build
+.* +Start 1: CommandExpandList
+1/1 Test #1: CommandExpandList +\.+ +Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
new file mode 100644
index 0000000..20608ae
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
@@ -0,0 +1,19 @@
+include(CTest)
+
+
+set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile"
+  "4ARG=content")
+set(AARGS "")
+foreach(arg IN LISTS cmp_args)
+  list(APPEND AARGS "-DA${arg}")
+endforeach()
+
+
+
+add_test(
+  NAME CommandExpandList
+  COMMAND ${CMAKE_COMMAND} ${AARGS} -V
+  "-DB$<JOIN:${cmp_args},;-DB>"
+  "-P" "${CMAKE_CURRENT_LIST_DIR}/compare_options.cmake"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
new file mode 100644
index 0000000..e48513f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at multipleExpandOptions\.cmake:3 \(add_test\):
+ +add_test may be given at most one COMMAND_EXPAND_LISTS\.
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
new file mode 100644
index 0000000..55bb894
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
@@ -0,0 +1,2 @@
+-- Configuring incomplete, errors occurred!
+See also ".*/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-build/CMakeFiles/CMakeOutput\.log".
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
new file mode 100644
index 0000000..dcf2dc4
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
@@ -0,0 +1,8 @@
+include(CTest)
+
+add_test(
+  NAME MultipleExpandOptions
+  COMMAND /bin/true
+  COMMAND_EXPAND_LISTS
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
new file mode 100644
index 0000000..d9a8ccb
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.14)
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+
+ctest_start(Experimental)
+ctest_configure()
+ctest_build()
+ctest_test()
diff --git a/Tests/RunCMake/CTestCommandLine/FailRegexFound-check.cmake b/Tests/RunCMake/CTestCommandLine/FailRegexFound-check.cmake
new file mode 100644
index 0000000..1097788
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/FailRegexFound-check.cmake
@@ -0,0 +1,13 @@
+set(last_test_log "${RunCMake_TEST_BINARY_DIR}/Testing/Temporary/LastTest.log")
+if(EXISTS "${last_test_log}")
+  file(READ "${last_test_log}" last_test_log_content)
+  string(REGEX REPLACE "\n+$" "" last_test_log_content "${last_test_log_content}")
+  if(NOT last_test_log_content MATCHES "
+Test Pass Reason:
+Error regular expression found in output. Regex=[[]test1]")
+    string(REPLACE "\n" "\n  " last_test_log_content "  ${last_test_log_content}")
+    set(RunCMake_TEST_FAILED "LastTest.log does not have expected content:\n${last_test_log_content}")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "LastTest.log missing:\n ${last_test_log}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandLine/RequiredRegexFound-check.cmake b/Tests/RunCMake/CTestCommandLine/RequiredRegexFound-check.cmake
new file mode 100644
index 0000000..bde60d1
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/RequiredRegexFound-check.cmake
@@ -0,0 +1,13 @@
+set(last_test_log "${RunCMake_TEST_BINARY_DIR}/Testing/Temporary/LastTest.log")
+if(EXISTS "${last_test_log}")
+  file(READ "${last_test_log}" last_test_log_content)
+  string(REGEX REPLACE "\n+$" "" last_test_log_content "${last_test_log_content}")
+  if(NOT last_test_log_content MATCHES "
+Test Pass Reason:
+Required regular expression found. Regex=[[]test1]")
+    string(REPLACE "\n" "\n  " last_test_log_content "  ${last_test_log_content}")
+    set(RunCMake_TEST_FAILED "LastTest.log does not have expected content:\n${last_test_log_content}")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "LastTest.log missing:\n ${last_test_log}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandLine/RequiredRegexNotFound-check.cmake b/Tests/RunCMake/CTestCommandLine/RequiredRegexNotFound-check.cmake
new file mode 100644
index 0000000..6d420f3
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/RequiredRegexNotFound-check.cmake
@@ -0,0 +1,16 @@
+set(last_test_log "${RunCMake_TEST_BINARY_DIR}/Testing/Temporary/LastTest.log")
+if(EXISTS "${last_test_log}")
+  file(READ "${last_test_log}" last_test_log_content)
+  string(REGEX REPLACE "\n+$" "" last_test_log_content "${last_test_log_content}")
+  if(NOT last_test_log_content MATCHES "
+Test Pass Reason:
+Required regular expression not found. Regex=[[]foo
+toast1
+bar
+]")
+    string(REPLACE "\n" "\n  " last_test_log_content "  ${last_test_log_content}")
+    set(RunCMake_TEST_FAILED "LastTest.log does not have expected content:\n${last_test_log_content}")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "LastTest.log missing:\n ${last_test_log}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
index b7f9a37..fd2c97f 100644
--- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -78,6 +78,62 @@
 
 run_LabelCount()
 
+function(run_RequiredRegexFoundTest)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RequiredRegexFound)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
+add_test(test1 \"${CMAKE_COMMAND}\" -E echo \"test1\")
+set_tests_properties(test1 PROPERTIES PASS_REGULAR_EXPRESSION \"foo;test1;bar\")
+")
+
+  run_cmake_command(RequiredRegexFound ${CMAKE_CTEST_COMMAND} -V)
+endfunction()
+run_RequiredRegexFoundTest()
+
+function(run_RequiredRegexNotFoundTest)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RequiredRegexNotFound)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
+add_test(test1 \"${CMAKE_COMMAND}\" -E echo \"test1\")
+set_tests_properties(test1 PROPERTIES PASS_REGULAR_EXPRESSION \"foo;toast1;bar\" WILL_FAIL True)
+")
+
+  run_cmake_command(RequiredRegexNotFound ${CMAKE_CTEST_COMMAND} -V)
+endfunction()
+run_RequiredRegexNotFoundTest()
+
+function(run_FailRegexFoundTest)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/FailRegexFound)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
+add_test(test1 \"${CMAKE_COMMAND}\" -E echo \"test1\")
+set_tests_properties(test1 PROPERTIES FAIL_REGULAR_EXPRESSION \"foo;test1;bar\" WILL_FAIL True)
+")
+
+  run_cmake_command(FailRegexFound ${CMAKE_CTEST_COMMAND} -V)
+endfunction()
+run_FailRegexFoundTest()
+
+function(run_SkipRegexFoundTest)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SkipRegexFound)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
+add_test(test1 \"${CMAKE_COMMAND}\" -E echo \"test1\")
+set_tests_properties(test1 PROPERTIES SKIP_REGULAR_EXPRESSION \"test1\")
+")
+
+  run_cmake_command(SkipRegexFound ${CMAKE_CTEST_COMMAND} -V)
+endfunction()
+run_SkipRegexFoundTest()
+
 function(run_SerialFailed)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SerialFailed)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -202,6 +258,7 @@
     add_test(ShowOnly \"${CMAKE_COMMAND}\" -E echo)
     set_tests_properties(ShowOnly PROPERTIES
       WILL_FAIL true
+      PROCESSES \"2,threads:2,gpus:4;gpus:2,threads:4\"
       REQUIRED_FILES RequiredFileDoesNotExist
       _BACKTRACE_TRIPLES \"file1;1;add_test;file0;;\"
       )
diff --git a/Tests/RunCMake/CTestCommandLine/SkipRegexFound-check.cmake b/Tests/RunCMake/CTestCommandLine/SkipRegexFound-check.cmake
new file mode 100644
index 0000000..1a2dfa3
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/SkipRegexFound-check.cmake
@@ -0,0 +1,13 @@
+set(last_test_log "${RunCMake_TEST_BINARY_DIR}/Testing/Temporary/LastTest.log")
+if(EXISTS "${last_test_log}")
+  file(READ "${last_test_log}" last_test_log_content)
+  string(REGEX REPLACE "\n+$" "" last_test_log_content "${last_test_log_content}")
+  if(NOT last_test_log_content MATCHES "
+Test Pass Reason:
+Skip regular expression found in output. Regex=[[]test1]")
+    string(REPLACE "\n" "\n  " last_test_log_content "  ${last_test_log_content}")
+    set(RunCMake_TEST_FAILED "LastTest.log does not have expected content:\n${last_test_log_content}")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "LastTest.log missing:\n ${last_test_log}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
index 3ad5768..6eb8624 100644
--- a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
+++ b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
@@ -80,6 +80,62 @@
     assert p["name"] == "WILL_FAIL"
     assert p["value"] == True
 
+def check_processes_property(p):
+    assert is_dict(p)
+    assert sorted(p.keys()) == ["name", "value"]
+    assert is_string(p["name"])
+    assert is_list(p["value"])
+    assert p["name"] == "PROCESSES"
+    assert len(p["value"]) == 3
+
+    assert is_dict(p["value"][0])
+    assert sorted(p["value"][0].keys()) == ["requirements"]
+    assert is_list(p["value"][0]["requirements"])
+    assert len(p["value"][0]["requirements"]) == 2
+    assert is_dict(p["value"][0]["requirements"][0])
+    assert sorted(p["value"][0]["requirements"][0].keys()) == \
+        [".type", "slots"]
+    assert is_string(p["value"][0]["requirements"][0][".type"])
+    assert p["value"][0]["requirements"][0][".type"] == "threads"
+    assert is_int(p["value"][0]["requirements"][0]["slots"])
+    assert p["value"][0]["requirements"][0]["slots"] == 2
+    assert is_string(p["value"][0]["requirements"][1][".type"])
+    assert p["value"][0]["requirements"][1][".type"] == "gpus"
+    assert is_int(p["value"][0]["requirements"][1]["slots"])
+    assert p["value"][0]["requirements"][1]["slots"] == 4
+
+    assert is_dict(p["value"][1])
+    assert sorted(p["value"][1].keys()) == ["requirements"]
+    assert is_list(p["value"][1]["requirements"])
+    assert len(p["value"][1]["requirements"]) == 2
+    assert is_dict(p["value"][1]["requirements"][0])
+    assert sorted(p["value"][1]["requirements"][0].keys()) == \
+        [".type", "slots"]
+    assert is_string(p["value"][1]["requirements"][0][".type"])
+    assert p["value"][1]["requirements"][0][".type"] == "threads"
+    assert is_int(p["value"][1]["requirements"][0]["slots"])
+    assert p["value"][1]["requirements"][0]["slots"] == 2
+    assert is_string(p["value"][1]["requirements"][1][".type"])
+    assert p["value"][1]["requirements"][1][".type"] == "gpus"
+    assert is_int(p["value"][1]["requirements"][1]["slots"])
+    assert p["value"][1]["requirements"][1]["slots"] == 4
+
+    assert is_dict(p["value"][2])
+    assert sorted(p["value"][2].keys()) == ["requirements"]
+    assert is_list(p["value"][2]["requirements"])
+    assert len(p["value"][2]["requirements"]) == 2
+    assert is_dict(p["value"][2]["requirements"][0])
+    assert sorted(p["value"][2]["requirements"][0].keys()) == \
+        [".type", "slots"]
+    assert is_string(p["value"][2]["requirements"][0][".type"])
+    assert p["value"][2]["requirements"][0][".type"] == "gpus"
+    assert is_int(p["value"][2]["requirements"][0]["slots"])
+    assert p["value"][2]["requirements"][0]["slots"] == 2
+    assert is_string(p["value"][2]["requirements"][1][".type"])
+    assert p["value"][2]["requirements"][1][".type"] == "threads"
+    assert is_int(p["value"][2]["requirements"][1]["slots"])
+    assert p["value"][2]["requirements"][1]["slots"] == 4
+
 def check_workingdir_property(p):
     assert is_dict(p)
     assert sorted(p.keys()) == ["name", "value"]
@@ -90,10 +146,11 @@
 
 def check_properties(p):
     assert is_list(p)
-    assert len(p) == 3
-    check_reqfiles_property(p[0])
-    check_willfail_property(p[1])
-    check_workingdir_property(p[2])
+    assert len(p) == 4
+    check_processes_property(p[0])
+    check_reqfiles_property(p[1])
+    check_willfail_property(p[2])
+    check_workingdir_property(p[3])
 
 def check_tests(t):
     assert is_list(t)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in b/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in
new file mode 100644
index 0000000..d6cff63
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.15)
+set(CASE_NAME "@CASE_NAME@")
+if(CASE_NAME MATCHES "^(.*)-ctest-s")
+  set(projname "${CMAKE_MATCH_1}")
+  project(${projname} NONE)
+  include(CTest)
+  include("@RunCMake_SOURCE_DIR@/HardwareCommon.cmake")
+  include("@RunCMake_SOURCE_DIR@/${projname}.cmake")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake b/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake
new file mode 100644
index 0000000..3893d40
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake
@@ -0,0 +1,23 @@
+function(setup_hardware_tests)
+  if(CTEST_HARDWARE_ALLOC_ENABLED)
+    add_test(NAME HardwareSetup COMMAND "${CMAKE_COMMAND}" -E remove -f "${CMAKE_BINARY_DIR}/cthwalloc.log")
+  endif()
+endfunction()
+
+function(add_hardware_test name sleep_time proc)
+  if(CTEST_HARDWARE_ALLOC_ENABLED)
+    add_test(NAME "${name}" COMMAND "${CTHWALLOC_COMMAND}" write "${CMAKE_BINARY_DIR}/cthwalloc.log" "${name}" "${sleep_time}" "${proc}")
+    set_property(TEST "${name}" PROPERTY DEPENDS HardwareSetup)
+  else()
+    add_test(NAME "${name}" COMMAND "${CTHWALLOC_COMMAND}" write "${CMAKE_BINARY_DIR}/cthwalloc.log" "${name}" "${sleep_time}")
+  endif()
+  set_property(TEST "${name}" PROPERTY PROCESSES "${proc}")
+  list(APPEND HARDWARE_TESTS "${name}")
+  set(HARDWARE_TESTS "${HARDWARE_TESTS}" PARENT_SCOPE)
+endfunction()
+
+function(cleanup_hardware_tests)
+  if(CTEST_HARDWARE_ALLOC_ENABLED)
+    file(WRITE "${CMAKE_BINARY_DIR}/hwtests.txt" "${HARDWARE_TESTS}")
+  endif()
+endfunction()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
new file mode 100644
index 0000000..d666922
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
@@ -0,0 +1,167 @@
+include(RunCMake)
+include(RunCTest)
+
+###############################################################################
+# Test cthwalloc itself - we want to make sure it's not just rubber-stamping
+# the test results
+###############################################################################
+
+function(cthwalloc_verify_log expected_contents)
+  if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+    string(APPEND RunCMake_TEST_FAILED "Log file was not written\n")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    return()
+  endif()
+  file(READ "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" actual_contents)
+  if(NOT actual_contents STREQUAL expected_contents)
+    string(APPEND RunCMake_TEST_FAILED "Actual log did not match expected log\n")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+function(run_cthwalloc_write_proc name proc)
+  file(REMOVE "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log")
+  run_cthwalloc_write_proc_nodel("${name}" "${proc}" "${ARGN}")
+endfunction()
+
+function(run_cthwalloc_write_proc_nodel name proc)
+  string(REPLACE ";" "\\;" proc "${proc}")
+  run_cmake_command(${name} "${CMAKE_COMMAND}" -E env "${ARGN}" "${CTHWALLOC_COMMAND}" write "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log" "${name}" 0 "${proc}")
+endfunction()
+
+function(run_cthwalloc_write_noproc name)
+  run_cmake_command(${name} "${CMAKE_COMMAND}" -E env "${ARGN}" "${CTHWALLOC_COMMAND}" write "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log" "${name}" 0)
+endfunction()
+
+function(run_cthwalloc_verify name tests)
+  string(REPLACE ";" "\\;" tests "${tests}")
+  run_cmake_command(${name} "${CTHWALLOC_COMMAND}" verify "${RunCMake_SOURCE_DIR}/${name}.log" "${CMAKE_CURRENT_LIST_DIR}/hwspec.json" "${tests}")
+endfunction()
+
+unset(ENV{CTEST_PROCESS_COUNT})
+set(RunCMake_TEST_NO_CLEAN 1)
+file(REMOVE_RECURSE "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build")
+file(MAKE_DIRECTORY "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build")
+file(WRITE "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build/cthwalloc.log"
+[[begin test1
+alloc widgets 0 1
+dealloc widgets 0 1
+end test1
+]])
+run_cthwalloc_write_proc_nodel(cthwalloc-write-proc-good1 "1,widgets:2,transmogrifiers:1;2,widgets:1,widgets:2"
+  CTEST_PROCESS_COUNT=3
+  CTEST_PROCESS_0=widgets,transmogrifiers
+  CTEST_PROCESS_0_WIDGETS=id:0,slots:2
+  CTEST_PROCESS_0_TRANSMOGRIFIERS=id:calvin,slots:1
+  CTEST_PROCESS_1=widgets
+  "CTEST_PROCESS_1_WIDGETS=id:0,slots:1\\;id:2,slots:2"
+  CTEST_PROCESS_2=widgets
+  "CTEST_PROCESS_2_WIDGETS=id:0,slots:1\\;id:2,slots:2"
+  )
+set(RunCMake_TEST_NO_CLEAN 0)
+run_cthwalloc_write_proc(cthwalloc-write-proc-good2 "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  CTEST_PROCESS_0_WIDGETS=id:3,slots:8
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nocount "widgets:8")
+run_cthwalloc_write_proc(cthwalloc-write-proc-badcount "widgets:8"
+  CTEST_PROCESS_COUNT=2
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nores "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badres "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets,transmogrifiers
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nowidgets "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets1 "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  CTEST_PROCESS_0_WIDGETS=
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets2 "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  "CTEST_PROCESS_0_WIDGETS=id:3,slots:8\\;id:0,slots:1"
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets3 "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  CTEST_PROCESS_0_WIDGETS=id:3,slots:7
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets4 "widgets:8"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  CTEST_PROCESS_0_WIDGETS=invalid
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets5 "widgets:2,widgets:2"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  "CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets6 "widgets:2"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  "CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
+  )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets7 "widgets:2,widgets:2"
+  CTEST_PROCESS_COUNT=1
+  CTEST_PROCESS_0=widgets
+  CTEST_PROCESS_0_WIDGETS=id:0,slots:2
+  )
+
+run_cthwalloc_write_noproc(cthwalloc-write-noproc-good1)
+run_cthwalloc_write_noproc(cthwalloc-write-noproc-count
+  CTEST_PROCESS_COUNT=1
+  )
+
+run_cthwalloc_verify(cthwalloc-verify-good1 "test1;test2")
+run_cthwalloc_verify(cthwalloc-verify-good2 "")
+run_cthwalloc_verify(cthwalloc-verify-nolog "")
+run_cthwalloc_verify(cthwalloc-verify-nores "")
+run_cthwalloc_verify(cthwalloc-verify-noid "")
+run_cthwalloc_verify(cthwalloc-verify-notenough "")
+run_cthwalloc_verify(cthwalloc-verify-baddealloc "")
+run_cthwalloc_verify(cthwalloc-verify-leak "")
+run_cthwalloc_verify(cthwalloc-verify-badtest1 "")
+run_cthwalloc_verify(cthwalloc-verify-badtest2 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest3 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest4 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest5 "test1")
+run_cthwalloc_verify(cthwalloc-verify-nobegin "test1")
+run_cthwalloc_verify(cthwalloc-verify-noend "test1")
+
+###############################################################################
+# Now test the hardware allocation feature of CTest
+###############################################################################
+
+function(run_ctest_hardware name parallel random)
+  run_ctest("${name}-ctest-s-hw" "-DCTEST_HARDWARE_ALLOC_ENABLED=1" "-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}" "-DCTEST_PARALLEL=${parallel}" "-DCTEST_RANDOM=${random}")
+  run_ctest("${name}-ctest-s-nohw" "-DCTEST_HARDWARE_ALLOC_ENABLED=0" "-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}" "-DCTEST_PARALLEL=${parallel}" "-DCTEST_RANDOM=${random}")
+endfunction()
+
+function(verify_ctest_hardware)
+  file(READ "${RunCMake_TEST_BINARY_DIR}/hwtests.txt" hwtests)
+  execute_process(COMMAND "${CTHWALLOC_COMMAND}" verify "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" "${CMAKE_CURRENT_LIST_DIR}/hwspec.json" "${hwtests}"
+    OUTPUT_VARIABLE output ERROR_QUIET RESULT_VARIABLE result)
+  if(result)
+    string(APPEND RunCMake_TEST_FAILED "${output}")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+run_ctest_hardware(lotsoftests 10 1)
+run_ctest_hardware(checkfree1 2 0)
+run_ctest_hardware(checkfree2 1 0)
+run_ctest_hardware(notenough1 1 0)
+run_ctest_hardware(notenough2 1 0)
+run_ctest_hardware(ensure_parallel 2 0)
+
+set(ENV{CTEST_PROCESS_COUNT} 2)
+run_ctest_hardware(process_count 1 0)
+unset(ENV{CTEST_PROCESS_COUNT})
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake
new file mode 100644
index 0000000..0e997b5
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake
@@ -0,0 +1,7 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "widgets:8")
+add_hardware_test(Test2 1 "fluxcapacitors:50;fluxcapacitors:50,widgets:8")
+add_hardware_test(Test3 1 "fluxcapacitors:121")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake
new file mode 100644
index 0000000..3c2b666
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake
@@ -0,0 +1,8 @@
+setup_hardware_tests()
+
+# This test is an attack on the hardware scheduling algorithm. It has been
+# carefully crafted to fool the algorithm into thinking there isn't sufficient
+# hardware for it.
+add_hardware_test(Test1 1 "widgets:2;4,widgets:4")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log
new file mode 100644
index 0000000..abd6bad
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log
@@ -0,0 +1,2 @@
+alloc widgets 0 1
+dealloc widgets 0 2
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log
new file mode 100644
index 0000000..605104b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log
@@ -0,0 +1 @@
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log
new file mode 100644
index 0000000..1ff1b0d
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log
@@ -0,0 +1,2 @@
+begin test1
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log
new file mode 100644
index 0000000..1925e6a
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log
@@ -0,0 +1,3 @@
+begin test1
+end test1
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log
new file mode 100644
index 0000000..3fe7da1
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log
@@ -0,0 +1,3 @@
+begin test1
+end test1
+end test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log
new file mode 100644
index 0000000..3a2e7e3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log
@@ -0,0 +1 @@
+end test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log
new file mode 100644
index 0000000..2cca0c3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log
@@ -0,0 +1,14 @@
+begin test1
+alloc widgets 3 4
+alloc widgets 4 1
+alloc transmogrifiers calvin 2
+alloc fluxcapacitors outatime 121
+begin test2
+alloc widgets 3 4
+dealloc widgets 3 4
+dealloc widgets 4 1
+dealloc transmogrifiers calvin 2
+dealloc fluxcapacitors outatime 121
+end test1
+dealloc widgets 3 4
+end test2
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good2.log
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good2.log
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log
new file mode 100644
index 0000000..b900d86
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log
@@ -0,0 +1 @@
+alloc widgets 0 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin.log
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin.log
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log
new file mode 100644
index 0000000..605104b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log
@@ -0,0 +1 @@
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log
new file mode 100644
index 0000000..c718975
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log
@@ -0,0 +1,2 @@
+alloc fluxcapacitors train 1
+dealloc fluxcapacitors train 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log
new file mode 100644
index 0000000..a18202b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log
@@ -0,0 +1,2 @@
+alloc gpus 0 1
+dealloc gpus 0 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log
new file mode 100644
index 0000000..ac78d5a
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log
@@ -0,0 +1,2 @@
+alloc widgets 0 8
+dealloc widgets 0 8
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake
new file mode 100644
index 0000000..949d2d7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake
@@ -0,0 +1,20 @@
+cthwalloc_verify_log(
+[[begin test1
+alloc widgets 0 1
+dealloc widgets 0 1
+end test1
+begin cthwalloc-write-proc-good1
+alloc transmogrifiers calvin 1
+alloc widgets 0 2
+alloc widgets 0 1
+alloc widgets 2 2
+alloc widgets 0 1
+alloc widgets 2 2
+dealloc transmogrifiers calvin 1
+dealloc widgets 0 2
+dealloc widgets 0 1
+dealloc widgets 2 2
+dealloc widgets 0 1
+dealloc widgets 2 2
+end cthwalloc-write-proc-good1
+]])
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake
new file mode 100644
index 0000000..ca0c6b8
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake
@@ -0,0 +1,6 @@
+cthwalloc_verify_log(
+[[begin cthwalloc-write-proc-good2
+alloc widgets 3 8
+dealloc widgets 3 8
+end cthwalloc-write-proc-good2
+]])
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx
new file mode 100644
index 0000000..eee2c7f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx
@@ -0,0 +1,396 @@
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "cmsys/Encoding.hxx"
+#include "cmsys/FStream.hxx"
+
+#include "cmCTestHardwareAllocator.h"
+#include "cmCTestHardwareSpec.h"
+#include "cmCTestMultiProcessHandler.h"
+#include "cmCTestTestHandler.h"
+#include "cmFileLock.h"
+#include "cmFileLockResult.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+/*
+ * This helper program is used to verify that the CTest hardware allocation
+ * feature is working correctly. It consists of two stages:
+ *
+ * 1) write - This stage receives the PROCESSES property of the test and
+ *    compares it with the values passed in the CTEST_PROCESS_* environment
+ *    variables. If it received all of the resources it expected, then it
+ *    writes this information to a log file, which will be read in the verify
+ *    stage.
+ * 2) verify - This stage compares the log file with the hardware spec file to
+ *    make sure that no resources were over-subscribed, deallocated without
+ *    being allocated, or allocated without being deallocated.
+ */
+
+static int usage(const char* argv0)
+{
+  std::cout << "Usage: " << argv0 << " (write|verify) <args...>" << std::endl;
+  return 1;
+}
+
+static int usageWrite(const char* argv0)
+{
+  std::cout << "Usage: " << argv0
+            << " write <log-file> <test-name> <sleep-time-secs>"
+               " [<processes-property>]"
+            << std::endl;
+  return 1;
+}
+
+static int usageVerify(const char* argv0)
+{
+  std::cout << "Usage: " << argv0
+            << " verify <log-file> <hardware-spec-file> [<test-names>]"
+            << std::endl;
+  return 1;
+}
+
+static int doWrite(int argc, char const* const* argv)
+{
+  if (argc < 5 || argc > 6) {
+    return usageWrite(argv[0]);
+  }
+  std::string logFile = argv[2];
+  std::string testName = argv[3];
+  unsigned int sleepTime = std::atoi(argv[4]);
+  std::vector<std::map<
+    std::string, std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>
+    hardware;
+  if (argc == 6) {
+    // Parse processes property
+    std::string processesProperty = argv[5];
+    std::vector<
+      std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+      processes;
+    bool result =
+      cmCTestTestHandler::ParseProcessesProperty(processesProperty, processes);
+    (void)result;
+    assert(result);
+
+    // Verify process count
+    const char* processCountEnv = cmSystemTools::GetEnv("CTEST_PROCESS_COUNT");
+    if (!processCountEnv) {
+      std::cout << "CTEST_PROCESS_COUNT should be defined" << std::endl;
+      return 1;
+    }
+    int processCount = std::atoi(processCountEnv);
+    if (processes.size() != std::size_t(processCount)) {
+      std::cout << "CTEST_PROCESS_COUNT does not match expected processes"
+                << std::endl
+                << "Expected: " << processes.size() << std::endl
+                << "Actual: " << processCount << std::endl;
+      return 1;
+    }
+
+    if (!cmSystemTools::Touch(logFile + ".lock", true)) {
+      std::cout << "Could not create lock file" << std::endl;
+      return 1;
+    }
+    cmFileLock lock;
+    auto lockResult =
+      lock.Lock(logFile + ".lock", static_cast<unsigned long>(-1));
+    if (!lockResult.IsOk()) {
+      std::cout << "Could not lock file" << std::endl;
+      return 1;
+    }
+    std::size_t i = 0;
+    cmsys::ofstream fout(logFile.c_str(), std::ios::app);
+    fout << "begin " << testName << std::endl;
+    for (auto& process : processes) {
+      try {
+        // Build and verify set of expected resources
+        std::set<std::string> expectedResources;
+        for (auto const& it : process) {
+          expectedResources.insert(it.ResourceType);
+        }
+
+        std::string prefix = "CTEST_PROCESS_";
+        prefix += std::to_string(i);
+        const char* actualResourcesCStr = cmSystemTools::GetEnv(prefix);
+        if (!actualResourcesCStr) {
+          std::cout << prefix << " should be defined" << std::endl;
+          return 1;
+        }
+
+        auto actualResourcesVec =
+          cmSystemTools::SplitString(actualResourcesCStr, ',');
+        std::set<std::string> actualResources;
+        for (auto const& r : actualResourcesVec) {
+          if (!r.empty()) {
+            actualResources.insert(r);
+          }
+        }
+
+        if (actualResources != expectedResources) {
+          std::cout << prefix << " did not list expected resources"
+                    << std::endl;
+          return 1;
+        }
+
+        // Verify that we got what we asked for and write it to the log
+        prefix += '_';
+        std::map<std::string,
+                 std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>
+          hwEntry;
+        for (auto const& type : actualResources) {
+          auto it = process.begin();
+
+          std::string varName = prefix;
+          varName += cmSystemTools::UpperCase(type);
+          const char* varVal = cmSystemTools::GetEnv(varName);
+          if (!varVal) {
+            std::cout << varName << " should be defined" << std::endl;
+            return 1;
+          }
+
+          auto received = cmSystemTools::SplitString(varVal, ';');
+          for (auto const& r : received) {
+            while (it->ResourceType != type || it->UnitsNeeded == 0) {
+              ++it;
+              if (it == process.end()) {
+                std::cout << varName << " did not list expected resources"
+                          << std::endl;
+                return 1;
+              }
+            }
+            auto split = cmSystemTools::SplitString(r, ',');
+            if (split.size() != 2) {
+              std::cout << varName << " was ill-formed" << std::endl;
+              return 1;
+            }
+            if (!cmHasLiteralPrefix(split[0], "id:")) {
+              std::cout << varName << " was ill-formed" << std::endl;
+              return 1;
+            }
+            auto id = split[0].substr(3);
+            if (!cmHasLiteralPrefix(split[1], "slots:")) {
+              std::cout << varName << " was ill-formed" << std::endl;
+              return 1;
+            }
+            auto slots = split[1].substr(6);
+            unsigned int amount = std::atoi(slots.c_str());
+            if (amount != static_cast<unsigned int>(it->SlotsNeeded)) {
+              std::cout << varName << " did not list expected resources"
+                        << std::endl;
+              return 1;
+            }
+            --it->UnitsNeeded;
+
+            fout << "alloc " << type << " " << id << " " << amount
+                 << std::endl;
+            hwEntry[type].push_back({ id, amount });
+          }
+
+          bool ended = false;
+          while (it->ResourceType != type || it->UnitsNeeded == 0) {
+            ++it;
+            if (it == process.end()) {
+              ended = true;
+              break;
+            }
+          }
+
+          if (!ended) {
+            std::cout << varName << " did not list expected resources"
+                      << std::endl;
+            return 1;
+          }
+        }
+        hardware.push_back(hwEntry);
+
+        ++i;
+      } catch (...) {
+        std::cout << "Unknown error while processing resources" << std::endl;
+        return 1;
+      }
+    }
+
+    auto unlockResult = lock.Release();
+    if (!unlockResult.IsOk()) {
+      std::cout << "Could not unlock file" << std::endl;
+      return 1;
+    }
+  } else {
+    if (cmSystemTools::GetEnv("CTEST_PROCESS_COUNT")) {
+      std::cout << "CTEST_PROCESS_COUNT should not be defined" << std::endl;
+      return 1;
+    }
+  }
+
+  std::this_thread::sleep_for(std::chrono::seconds(sleepTime));
+
+  if (argc == 6) {
+    if (!cmSystemTools::Touch(logFile + ".lock", true)) {
+      std::cout << "Could not create lock file" << std::endl;
+      return 1;
+    }
+    cmFileLock lock;
+    auto lockResult =
+      lock.Lock(logFile + ".lock", static_cast<unsigned long>(-1));
+    if (!lockResult.IsOk()) {
+      std::cout << "Could not lock file" << std::endl;
+      return 1;
+    }
+    cmsys::ofstream fout(logFile.c_str(), std::ios::app);
+    for (auto const& process : hardware) {
+      for (auto const& it : process) {
+        for (auto const& it2 : it.second) {
+          fout << "dealloc " << it.first << " " << it2.Id << " " << it2.Slots
+               << std::endl;
+        }
+      }
+    }
+
+    fout << "end " << testName << std::endl;
+
+    auto unlockResult = lock.Release();
+    if (!unlockResult.IsOk()) {
+      std::cout << "Could not unlock file" << std::endl;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int doVerify(int argc, char const* const* argv)
+{
+  if (argc < 4 || argc > 5) {
+    return usageVerify(argv[0]);
+  }
+  std::string logFile = argv[2];
+  std::string hwFile = argv[3];
+  std::string testNames;
+  if (argc == 5) {
+    testNames = argv[4];
+  }
+  auto testNameList = cmExpandedList(testNames, false);
+  std::set<std::string> testNameSet(testNameList.begin(), testNameList.end());
+
+  cmCTestHardwareSpec spec;
+  if (!spec.ReadFromJSONFile(hwFile)) {
+    std::cout << "Could not read hardware spec " << hwFile << std::endl;
+    return 1;
+  }
+
+  cmCTestHardwareAllocator allocator;
+  allocator.InitializeFromHardwareSpec(spec);
+
+  cmsys::ifstream fin(logFile.c_str(), std::ios::in);
+  if (!fin) {
+    std::cout << "Could not open log file " << logFile << std::endl;
+    return 1;
+  }
+
+  std::string command;
+  std::string resourceName;
+  std::string resourceId;
+  std::string testName;
+  unsigned int amount;
+  std::set<std::string> inProgressTests;
+  std::set<std::string> completedTests;
+  try {
+    while (fin >> command) {
+      if (command == "begin") {
+        if (!(fin >> testName)) {
+          std::cout << "Could not read begin line" << std::endl;
+          return 1;
+        }
+        if (!testNameSet.count(testName) || inProgressTests.count(testName) ||
+            completedTests.count(testName)) {
+          std::cout << "Could not begin test" << std::endl;
+          return 1;
+        }
+        inProgressTests.insert(testName);
+      } else if (command == "alloc") {
+        if (!(fin >> resourceName) || !(fin >> resourceId) ||
+            !(fin >> amount)) {
+          std::cout << "Could not read alloc line" << std::endl;
+          return 1;
+        }
+        if (!allocator.AllocateResource(resourceName, resourceId, amount)) {
+          std::cout << "Could not allocate resources" << std::endl;
+          return 1;
+        }
+      } else if (command == "dealloc") {
+        if (!(fin >> resourceName) || !(fin >> resourceId) ||
+            !(fin >> amount)) {
+          std::cout << "Could not read dealloc line" << std::endl;
+          return 1;
+        }
+        if (!allocator.DeallocateResource(resourceName, resourceId, amount)) {
+          std::cout << "Could not deallocate resources" << std::endl;
+          return 1;
+        }
+      } else if (command == "end") {
+        if (!(fin >> testName)) {
+          std::cout << "Could not read end line" << std::endl;
+          return 1;
+        }
+        if (!inProgressTests.erase(testName)) {
+          std::cout << "Could not end test" << std::endl;
+          return 1;
+        }
+        if (!completedTests.insert(testName).second) {
+          std::cout << "Could not end test" << std::endl;
+          return 1;
+        }
+      }
+    }
+  } catch (...) {
+    std::cout << "Unknown error while reading log file" << std::endl;
+    return 1;
+  }
+
+  auto const& avail = allocator.GetResources();
+  for (auto const& it : avail) {
+    for (auto const& it2 : it.second) {
+      if (it2.second.Locked != 0) {
+        std::cout << "Resource was not unlocked" << std::endl;
+        return 1;
+      }
+    }
+  }
+
+  if (completedTests != testNameSet) {
+    std::cout << "Tests were not ended" << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+int main(int argc, char const* const* argv)
+{
+  cmsys::Encoding::CommandLineArguments args =
+    cmsys::Encoding::CommandLineArguments::Main(argc, argv);
+  argc = args.argc();
+  argv = args.argv();
+
+  if (argc < 2) {
+    return usage(argv[0]);
+  }
+
+  std::string argv1 = argv[1];
+  if (argv1 == "write") {
+    return doWrite(argc, argv);
+  }
+  if (argv1 == "verify") {
+    return doVerify(argc, argv);
+  }
+  return usage(argv[0]);
+}
diff --git a/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..e5f6828
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake
@@ -0,0 +1,16 @@
+verify_ctest_hardware()
+
+set(expected_contents [[
+begin Test1
+alloc transmogrifiers calvin 2
+begin Test2
+alloc transmogrifiers hobbes 2
+dealloc transmogrifiers calvin 2
+end Test1
+dealloc transmogrifiers hobbes 2
+end Test2
+]])
+file(READ "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" actual_contents)
+if(NOT actual_contents STREQUAL expected_contents)
+  string(APPEND RunCMake_TEST_FAILED "cthwalloc.log contents did not match expected\n")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake
new file mode 100644
index 0000000..1dafb8f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake
@@ -0,0 +1,11 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 4 "transmogrifiers:2")
+
+# Mitigate possible race conditions to ensure that the events are logged in the
+# exact order we want
+add_test(NAME Test2Sleep COMMAND "${CMAKE_COMMAND}" -E sleep 2)
+add_hardware_test(Test2 4 "transmogrifiers:2")
+set_property(TEST Test2 APPEND PROPERTY DEPENDS Test2Sleep)
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/hwspec.json b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
new file mode 100644
index 0000000..c67fcca
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
@@ -0,0 +1,55 @@
+{
+  "local": [
+    {
+      "widgets": [
+        {
+          "id": "0",
+          "slots": 4
+        },
+        {
+          "id": "1",
+          "slots": 2
+        },
+        {
+          "id": "2",
+          "slots": 4
+        },
+        {
+          "id": "3",
+          "slots": 8
+        },
+        {
+          "id": "4",
+          "slots": 1
+        },
+        {
+          "id": "5",
+          "slots": 1
+        },
+        {
+          "id": "6",
+          "slots": 1
+        },
+        {
+          "id": "7"
+        }
+      ],
+      "transmogrifiers": [
+        {
+          "id": "calvin",
+          "slots": 2
+        },
+        {
+          "id": "hobbes",
+          "slots": 2
+        }
+      ],
+      "fluxcapacitors": [
+        {
+          "id": "outatime",
+          "slots": 121
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake
new file mode 100644
index 0000000..c684434
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake
@@ -0,0 +1,16 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 2 "widgets:8;2,widgets:2")
+add_hardware_test(Test2 5 "fluxcapacitors:40")
+add_hardware_test(Test3 1 "10,widgets:1,fluxcapacitors:2")
+add_hardware_test(Test4 4 "fluxcapacitors:121")
+
+foreach(i RANGE 5 50)
+  add_hardware_test(Test${i} 1 "2,widgets:1")
+endforeach()
+
+foreach(i RANGE 51 100)
+  add_hardware_test(Test${i} 1 "2,transmogrifiers:2")
+endforeach()
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..9c730be
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake
@@ -0,0 +1,3 @@
+if(EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+  set(RunCMake_TEST_FAILED "cthwalloc.log should not exist")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt
new file mode 100644
index 0000000..d465cd3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt
@@ -0,0 +1,4 @@
+^Insufficient hardware
+CMake Error at [^
+]*/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake
new file mode 100644
index 0000000..3e1f620
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "fluxcapacitors:200")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..9c730be
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake
@@ -0,0 +1,3 @@
+if(EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+  set(RunCMake_TEST_FAILED "cthwalloc.log should not exist")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt
new file mode 100644
index 0000000..912f0fb
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt
@@ -0,0 +1,4 @@
+^Insufficient hardware
+CMake Error at [^
+]*/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake
new file mode 100644
index 0000000..8205c95
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "terminators:2")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake b/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake
new file mode 100644
index 0000000..c969648
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "widgets:1")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in b/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in
new file mode 100644
index 0000000..5ba3587
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in
@@ -0,0 +1,23 @@
+set(CTEST_SITE "test-site")
+set(CTEST_BUILD_NAME "test-build-name")
+set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
+
+ctest_start(Experimental QUIET)
+ctest_configure(OPTIONS
+  "-DCTEST_HARDWARE_ALLOC_ENABLED=${CTEST_HARDWARE_ALLOC_ENABLED};-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}"
+  )
+ctest_build()
+
+if(CTEST_HARDWARE_ALLOC_ENABLED)
+  set(hwspec HARDWARE_SPEC_FILE "@RunCMake_SOURCE_DIR@/hwspec.json")
+endif()
+ctest_test(${hwspec} RETURN_VALUE retval PARALLEL_LEVEL ${CTEST_PARALLEL} SCHEDULE_RANDOM ${CTEST_RANDOM})
+if(retval)
+  message(FATAL_ERROR "Tests did not pass")
+endif()
diff --git a/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt b/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt
index f183594..1baa63a 100644
--- a/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt
+++ b/Tests/RunCMake/CheckIPOSupported/cmp0069-is-old-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\):
+^CMake Deprecation Warning at cmp0069-is-old.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0069 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\):
   Policy CMP0069 set to OLD
 Call Stack \(most recent call first\):
   cmp0069-is-old\.cmake:[0-9]+ \(check_ipo_supported\)
diff --git a/Tests/RunCMake/CommandLine/C_buildsrcdir-stderr.txt b/Tests/RunCMake/CommandLine/C_buildsrcdir-stderr.txt
new file mode 100644
index 0000000..0d8f72e
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/C_buildsrcdir-stderr.txt
@@ -0,0 +1,8 @@
+initial-cache.txt: CMAKE_SOURCE_DIR: .*/C_buildsrcdir/src
+initial-cache.txt: CMAKE_BINARY_DIR: .*/C_buildsrcdir-build/DummyBuildDir
+PreLoad.cmake: CMAKE_SOURCE_DIR: .*/C_buildsrcdir/src
+PreLoad.cmake: CMAKE_BINARY_DIR: .*/C_buildsrcdir-build/DummyBuildDir
+CMakeLists.txt: INITIAL_SOURCE_DIR: .*/C_buildsrcdir/src
+CMakeLists.txt: INITIAL_BINARY_DIR: .*/C_buildsrcdir-build/DummyBuildDir
+CMakeLists.txt: PRELOAD_SOURCE_DIR: .*/C_buildsrcdir/src
+CMakeLists.txt: PRELOAD_BINARY_DIR: .*/C_buildsrcdir-build/DummyBuildDir
diff --git a/Tests/RunCMake/CommandLine/C_buildsrcdir-stdout.txt b/Tests/RunCMake/CommandLine/C_buildsrcdir-stdout.txt
new file mode 100644
index 0000000..c69b11e
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/C_buildsrcdir-stdout.txt
@@ -0,0 +1,2 @@
+loading initial cache file .*/C_buildsrcdir/initial-cache.txt
+.*
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CommandLine/C_buildsrcdir.cmake
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CommandLine/C_buildsrcdir.cmake
diff --git a/Tests/RunCMake/CommandLine/C_buildsrcdir/initial-cache.txt b/Tests/RunCMake/CommandLine/C_buildsrcdir/initial-cache.txt
new file mode 100644
index 0000000..adc125b
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/C_buildsrcdir/initial-cache.txt
@@ -0,0 +1,6 @@
+# Used to verify that the values match what is passed via -S and -B, and are retained in cache.
+set(INITIAL_SOURCE_DIR "${CMAKE_SOURCE_DIR}" CACHE PATH "defined in initial.cmake")
+set(INITIAL_BINARY_DIR "${CMAKE_BINARY_DIR}" CACHE PATH "defined in initial.cmake")
+
+message("initial-cache.txt: CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
+message("initial-cache.txt: CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}")
diff --git a/Tests/RunCMake/CommandLine/C_buildsrcdir/src/CMakeLists.txt b/Tests/RunCMake/CommandLine/C_buildsrcdir/src/CMakeLists.txt
new file mode 100644
index 0000000..4893fe7
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/C_buildsrcdir/src/CMakeLists.txt
@@ -0,0 +1,6 @@
+project(C_buildsrcdir)
+
+message("CMakeLists.txt: INITIAL_SOURCE_DIR: ${INITIAL_SOURCE_DIR}")
+message("CMakeLists.txt: INITIAL_BINARY_DIR: ${INITIAL_BINARY_DIR}")
+message("CMakeLists.txt: PRELOAD_SOURCE_DIR: ${PRELOAD_SOURCE_DIR}")
+message("CMakeLists.txt: PRELOAD_BINARY_DIR: ${PRELOAD_BINARY_DIR}")
diff --git a/Tests/RunCMake/CommandLine/C_buildsrcdir/src/PreLoad.cmake b/Tests/RunCMake/CommandLine/C_buildsrcdir/src/PreLoad.cmake
new file mode 100644
index 0000000..5199219
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/C_buildsrcdir/src/PreLoad.cmake
@@ -0,0 +1,6 @@
+# Used to verify that the values match what is passed via -S and -B, and are retained in cache.
+message("PreLoad.cmake: CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
+message("PreLoad.cmake: CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}")
+
+set(PRELOAD_BINARY_DIR "${CMAKE_BINARY_DIR}" CACHE PATH "value of cmake_binary_dir during preload")
+set(PRELOAD_SOURCE_DIR "${CMAKE_SOURCE_DIR}" CACHE PATH "value of cmake_source_dir during preload")
diff --git a/Tests/RunCMake/CommandLine/E_false-extraargs-result.txt b/Tests/RunCMake/CommandLine/E_false-extraargs-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_false-extraargs-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/E_false-result.txt b/Tests/RunCMake/CommandLine/E_false-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_false-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/E_remove_directory-symlink-dir-check.cmake b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-dir-check.cmake
new file mode 100644
index 0000000..f70312c
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-dir-check.cmake
@@ -0,0 +1,6 @@
+if(EXISTS ${out}/link_dir)
+  set(RunCMake_TEST_FAILED "did not remove ${out}/link_dir")
+endif()
+if(NOT EXISTS ${out}/dir)
+  set(RunCMake_TEST_FAILED "should not have removed ${out}/dir")
+endif()
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-dir-stderr.txt
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CommandLine/E_remove_directory-symlink-dir-stderr.txt
diff --git a/Tests/RunCMake/CommandLine/E_remove_directory-symlink-file-check.cmake b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-file-check.cmake
new file mode 100644
index 0000000..23d7c6d
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-file-check.cmake
@@ -0,0 +1,6 @@
+if(NOT EXISTS ${outfile})
+  set(RunCMake_TEST_FAILED "removed non-directory ${outfile}")
+endif()
+if(NOT EXISTS ${out}/link_file_for_test.txt)
+  set(RunCMake_TEST_FAILED "removed non-directory symlink ${out}/link_file_for_test.txt")
+endif()
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CommandLine/E_remove_directory-symlink-file-stderr.txt
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CommandLine/E_remove_directory-symlink-file-stderr.txt
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index c9d3a4d..efd1cc2 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -26,6 +26,10 @@
 run_cmake_command(E_rename-no-arg ${CMAKE_COMMAND} -E rename)
 run_cmake_command(E_server-arg ${CMAKE_COMMAND} -E server --extra-arg)
 run_cmake_command(E_server-pipe ${CMAKE_COMMAND} -E server --pipe=)
+run_cmake_command(E_true ${CMAKE_COMMAND} -E true)
+run_cmake_command(E_true-extraargs ${CMAKE_COMMAND} -E true ignored)
+run_cmake_command(E_false ${CMAKE_COMMAND} -E false)
+run_cmake_command(E_false-extraargs ${CMAKE_COMMAND} -E false ignored)
 
 run_cmake_command(E_touch_nocreate-no-arg ${CMAKE_COMMAND} -E touch_nocreate)
 run_cmake_command(E_touch-nonexistent-dir ${CMAKE_COMMAND} -E touch "${RunCMake_BINARY_DIR}/touch-nonexistent-dir/foo")
@@ -344,6 +348,17 @@
   ${CMAKE_COMMAND} -E make_directory ${out}/d1 ${out}/d2 ${outfile})
 run_cmake_command(E_remove_directory-two-directories-and-file
   ${CMAKE_COMMAND} -E remove_directory ${out}/d1 ${out}/d2 ${outfile})
+
+if(UNIX)
+  file(MAKE_DIRECTORY ${out}/dir)
+  file(CREATE_LINK ${out}/dir ${out}/link_dir SYMBOLIC)
+  file(CREATE_LINK ${outfile} ${out}/link_file_for_test.txt SYMBOLIC)
+  run_cmake_command(E_remove_directory-symlink-dir
+    ${CMAKE_COMMAND} -E remove_directory ${out}/link_dir)
+  run_cmake_command(E_remove_directory-symlink-file
+    ${CMAKE_COMMAND} -E remove_directory ${out}/link_file_for_test.txt)
+endif()
+
 unset(out)
 unset(outfile)
 
@@ -387,6 +402,13 @@
 
 run_cmake_command(P_directory ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR})
 run_cmake_command(P_working-dir ${CMAKE_COMMAND} -DEXPECTED_WORKING_DIR=${RunCMake_BINARY_DIR}/P_working-dir-build -P ${RunCMake_SOURCE_DIR}/P_working-dir.cmake)
+# Documented to return the same result as above even if -S and -B are set to something else.
+# Tests the values of CMAKE_BINARY_DIR CMAKE_CURRENT_BINARY_DIR CMAKE_SOURCE_DIR CMAKE_CURRENT_SOURCE_DIR.
+run_cmake_command(P_working-dir ${CMAKE_COMMAND} -DEXPECTED_WORKING_DIR=${RunCMake_BINARY_DIR}/P_working-dir-build -P ${RunCMake_SOURCE_DIR}/P_working-dir.cmake -S something_else -B something_else_1)
+
+# CMAKE_BINARY_DIR should be determined by -B if specified, and CMAKE_SOURCE_DIR determined by -S if specified.
+run_cmake_with_options(C_buildsrcdir -B DummyBuildDir -S ${RunCMake_SOURCE_DIR}/C_buildsrcdir/src -C ${RunCMake_SOURCE_DIR}/C_buildsrcdir/initial-cache.txt)
+
 
 set(RunCMake_TEST_OPTIONS
   "-DFOO=-DBAR:BOOL=BAZ")
@@ -480,6 +502,14 @@
 run_cmake(trace-expand-warn-uninitialized)
 unset(RunCMake_TEST_OPTIONS)
 
+set(RunCMake_TEST_OPTIONS --trace-redirect=${RunCMake_BINARY_DIR}/redirected.trace)
+run_cmake(trace-redirect)
+unset(RunCMake_TEST_OPTIONS)
+
+set(RunCMake_TEST_OPTIONS --trace-redirect=/no/such/file.txt)
+run_cmake(trace-redirect-nofile)
+unset(RunCMake_TEST_OPTIONS)
+
 set(RunCMake_TEST_OPTIONS -Wno-deprecated --warn-uninitialized)
 run_cmake(warn-uninitialized)
 unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/CommandLine/trace-redirect-check.cmake b/Tests/RunCMake/CommandLine/trace-redirect-check.cmake
new file mode 100644
index 0000000..1ee0e0d
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-redirect-check.cmake
@@ -0,0 +1,13 @@
+file(READ ${RunCMake_SOURCE_DIR}/trace-stderr.txt expected_content)
+string(REGEX REPLACE "\n+$" "" expected_content "${expected_content}")
+
+file(READ ${RunCMake_BINARY_DIR}/redirected.trace actual_content)
+string(REGEX REPLACE "\r\n" "\n" actual_content "${actual_content}")
+string(REGEX REPLACE "\n+$" "" actual_content "${actual_content}")
+if(NOT "${actual_content}" MATCHES "${expected_content}")
+    set(RunCMake_TEST_FAILED
+        "Trace file content does not match that expected."
+        "Expected to match:\n${expected_content}\n"
+        "Actual content:\n${actual_content}\n"
+        )
+endif()
diff --git a/Tests/RunCMake/CommandLine/trace-redirect-nofile-result.txt b/Tests/RunCMake/CommandLine/trace-redirect-nofile-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-redirect-nofile-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/trace-redirect-nofile-stderr.txt b/Tests/RunCMake/CommandLine/trace-redirect-nofile-stderr.txt
new file mode 100644
index 0000000..edb0c8e
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-redirect-nofile-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: Error opening trace file /no/such/file.txt: .+$
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CommandLine/trace-redirect-nofile.cmake
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CommandLine/trace-redirect-nofile.cmake
diff --git a/Tests/RunCMake/CommandLine/trace-redirect-stdout.txt b/Tests/RunCMake/CommandLine/trace-redirect-stdout.txt
new file mode 100644
index 0000000..775f2b5
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/trace-redirect-stdout.txt
@@ -0,0 +1 @@
+^.*Trace will be written to .+redirected.trace.*$
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/CommandLine/trace-redirect.cmake
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
copy to Tests/RunCMake/CommandLine/trace-redirect.cmake
diff --git a/Tests/RunCMake/Configure/RerunCMake-build3-result.txt b/Tests/RunCMake/Configure/RerunCMake-build3-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/Configure/RerunCMake-build3-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt b/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt
new file mode 100644
index 0000000..dde2ea6
--- /dev/null
+++ b/Tests/RunCMake/Configure/RerunCMake-build3-stdout.txt
@@ -0,0 +1 @@
+Rerun error 3
diff --git a/Tests/RunCMake/Configure/RerunCMake-build4-result.txt b/Tests/RunCMake/Configure/RerunCMake-build4-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/Configure/RerunCMake-build4-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt b/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt
new file mode 100644
index 0000000..b8727e1
--- /dev/null
+++ b/Tests/RunCMake/Configure/RerunCMake-build4-stdout.txt
@@ -0,0 +1 @@
+Rerun error 4
diff --git a/Tests/RunCMake/Configure/RerunCMake.cmake b/Tests/RunCMake/Configure/RerunCMake.cmake
index 5a561bf..c0b0798 100644
--- a/Tests/RunCMake/Configure/RerunCMake.cmake
+++ b/Tests/RunCMake/Configure/RerunCMake.cmake
@@ -9,3 +9,9 @@
 file(READ ${depend} content)
 file(WRITE ${output} "${content}")
 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS RerunCMake.txt)
+
+set(error  ${CMAKE_CURRENT_BINARY_DIR}/CustomCMakeError.txt)
+if(EXISTS ${error})
+  file(READ ${error} content)
+  message(FATAL_ERROR "Rerun error ${content}")
+endif()
diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake
index 4a135be..76d843c 100644
--- a/Tests/RunCMake/Configure/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake
@@ -14,6 +14,7 @@
 set(stamp  "${RunCMake_TEST_BINARY_DIR}/CustomCMakeStamp.txt")
 set(depend "${RunCMake_TEST_BINARY_DIR}/CustomCMakeDepend.txt")
 set(output "${RunCMake_TEST_BINARY_DIR}/CustomCMakeOutput.txt")
+set(error  "${RunCMake_TEST_BINARY_DIR}/CustomCMakeError.txt")
 file(WRITE "${input}" "1")
 file(WRITE "${depend}" "1")
 run_cmake(RerunCMake)
@@ -22,6 +23,22 @@
 run_cmake_command(RerunCMake-build1 ${CMAKE_COMMAND} --build .)
 file(WRITE "${depend}" "2")
 run_cmake_command(RerunCMake-build2 ${CMAKE_COMMAND} --build .)
+execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution
+file(WRITE "${depend}" "3")
+file(WRITE "${error}" "3")
+set(RunCMake_TEST_OUTPUT_MERGE 1)
+run_cmake_command(RerunCMake-build3 ${CMAKE_COMMAND} --build .)
+if(MSVC_IDE)
+  # Make sure that for Visual Studio the error occurs from within the build
+  # system.
+  file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/generate.stamp.list")
+  file(WRITE "${error}" "4")
+  # With Visual Studio the error must be on stdout, otherwise the error was not
+  # emitted by ZERO_CHECK.
+  set(RunCMake_TEST_OUTPUT_MERGE 0)
+  run_cmake_command(RerunCMake-build4 ${CMAKE_COMMAND} --build .)
+endif()
+unset(RunCMake_TEST_OUTPUT_MERGE)
 unset(RunCMake_TEST_BINARY_DIR)
 unset(RunCMake_TEST_NO_CLEAN)
 
diff --git a/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake b/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake
new file mode 100644
index 0000000..e25db1a
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake
@@ -0,0 +1 @@
+# pseudo config module
diff --git a/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake b/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake
new file mode 100644
index 0000000..b7c9e18
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/CustomMessageConfigVersion.cmake
@@ -0,0 +1,4 @@
+# pseudo find_module
+
+set (PACKAGE_VERSION 2)
+set (PACKAGE_VERSION_UNSUITABLE TRUE)
diff --git a/Tests/RunCMake/FPHSA/FindCustomMessage.cmake b/Tests/RunCMake/FPHSA/FindCustomMessage.cmake
new file mode 100644
index 0000000..4d67db8
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindCustomMessage.cmake
@@ -0,0 +1,17 @@
+# pseudo find_module
+
+if (REASON_FAILURE_MESSAGE)
+  list (PREPEND REASON_FAILURE_MESSAGE "REASON_FAILURE_MESSAGE")
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+if (CONFIG_MODE)
+  find_package (CustomMessage QUIET CONFIG HINTS "${CMAKE_MODULE_PATH}")
+  find_package_handle_standard_args(CustomMessage CONFIG_MODE
+                                                  ${REASON_FAILURE_MESSAGE})
+else()
+  find_package_handle_standard_args(CustomMessage REQUIRED_VARS FOOBAR
+                                                  VERSION_VAR CustomMessage_VERSION
+                                                  ${REASON_FAILURE_MESSAGE})
+endif()
diff --git a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
index dd73cd4..f3e6c3e 100644
--- a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
@@ -39,3 +39,10 @@
 # check if searching for a version 0 works
 list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DPseudo_VERSION=0")
 run_cmake(exact_0_matching)
+
+# check custom error message
+set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DCustomMessage_VERSION=1.2.3.4")
+run_cmake(custom_message_1)
+set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DCONFIG_MODE=TRUE")
+run_cmake(custom_message_2)
+run_cmake(custom_message_3)
diff --git a/Tests/RunCMake/FPHSA/custom_message_1-result.txt b/Tests/RunCMake/FPHSA/custom_message_1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt
new file mode 100644
index 0000000..992fe39
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_1-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  Could NOT find CustomMessage \(missing: FOOBAR\) \(found suitable version
+  "1\.2\.3\.4", minimum required is "1\.2"\)
+
+      Reason given by package: Reason Failure
+
+Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/FPHSA/custom_message_1.cmake b/Tests/RunCMake/FPHSA/custom_message_1.cmake
new file mode 100644
index 0000000..330de50
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_1.cmake
@@ -0,0 +1,4 @@
+
+set (REASON_FAILURE_MESSAGE "Reason Failure")
+
+find_package(CustomMessage 1.2 REQUIRED)
diff --git a/Tests/RunCMake/FPHSA/custom_message_2-result.txt b/Tests/RunCMake/FPHSA/custom_message_2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt
new file mode 100644
index 0000000..4940752
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_2-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  Could NOT find CustomMessage \(Required is at least version "1\.2"\), checked
+  the following files:
+
+      .+/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake \(version 2\)
+      Reason given by package: Not Found Message
+
+Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/FPHSA/custom_message_2.cmake b/Tests/RunCMake/FPHSA/custom_message_2.cmake
new file mode 100644
index 0000000..a3f2d96
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_2.cmake
@@ -0,0 +1,5 @@
+
+unset (REASON_FAILURE_MESSAGE)
+set (CustomMessage_NOT_FOUND_MESSAGE "Not Found Message")
+
+find_package(CustomMessage 1.2 REQUIRED)
diff --git a/Tests/RunCMake/FPHSA/custom_message_3-result.txt b/Tests/RunCMake/FPHSA/custom_message_3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt b/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt
new file mode 100644
index 0000000..dc55843
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_3-stderr.txt
@@ -0,0 +1,9 @@
+^CMake Error at .+/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  Could NOT find CustomMessage \(Required is at least version "1\.2"\), checked
+  the following files:
+
+      .+/Tests/RunCMake/FPHSA/CustomMessageConfig.cmake \(version 2\)
+      Reason given by package: Not Found Message
+      Reason Failure
+
+Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/FPHSA/custom_message_3.cmake b/Tests/RunCMake/FPHSA/custom_message_3.cmake
new file mode 100644
index 0000000..203e012
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/custom_message_3.cmake
@@ -0,0 +1,5 @@
+
+set (REASON_FAILURE_MESSAGE "Reason Failure")
+set (CustomMessage_NOT_FOUND_MESSAGE "Not Found Message")
+
+find_package(CustomMessage 1.2 REQUIRED)
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 3096358..66c559d 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -232,17 +232,37 @@
 
             assert is_string(obj["link"]["language"], expected["link"]["language"])
 
-            # FIXME: Properly test commandFragments
             if "commandFragments" in obj["link"]:
                 link_keys.append("commandFragments")
                 assert is_list(obj["link"]["commandFragments"])
                 for f in obj["link"]["commandFragments"]:
                     assert is_dict(f)
-                    assert sorted(f.keys()) == ["fragment", "role"]
+                    assert sorted(f.keys()) == ["fragment", "role"] or sorted(f.keys()) == ["backtrace", "fragment", "role"]
                     assert is_string(f["fragment"])
                     assert is_string(f["role"])
                     assert f["role"] in ("flags", "libraries", "libraryPath", "frameworkPath")
 
+            if expected["link"]["commandFragments"] is not None:
+                def check_link_command_fragments(actual, expected):
+                    assert is_dict(actual)
+                    expected_keys = ["fragment", "role"]
+
+                    if expected["backtrace"] is not None:
+                        expected_keys.append("backtrace")
+                        assert matches(actual["fragment"], expected["fragment"])
+                        assert actual["role"] == expected["role"]
+                        check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                    assert sorted(actual.keys()) == sorted(expected_keys)
+
+                check_list_match(lambda a, e: matches(a["fragment"], e["fragment"]),
+                                 obj["link"]["commandFragments"], expected["link"]["commandFragments"],
+                                 check=check_link_command_fragments,
+                                 check_exception=lambda a, e: "Link fragment: %s" % a["fragment"],
+                                 missing_exception=lambda e: "Link fragment: %s" % e["fragment"],
+                                 extra_exception=lambda a: "Link fragment: %s" % a["fragment"],
+                                 allow_extra=True)
+
             if expected["link"]["lto"] is not None:
                 link_keys.append("lto")
                 assert is_bool(obj["link"]["lto"], expected["link"]["lto"])
@@ -327,15 +347,33 @@
                                  missing_exception=lambda e: "Source path: %s" % e,
                                  extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"])
 
-                # FIXME: Properly test compileCommandFragments
                 if "compileCommandFragments" in actual:
                     expected_keys.append("compileCommandFragments")
                     assert is_list(actual["compileCommandFragments"])
                     for f in actual["compileCommandFragments"]:
                         assert is_dict(f)
-                        assert sorted(f.keys()) == ["fragment"]
                         assert is_string(f["fragment"])
 
+                if expected["compileCommandFragments"] is not None:
+                    def check_compile_command_fragments(actual, expected):
+                        assert is_dict(actual)
+                        expected_keys = ["fragment"]
+
+                        if expected["backtrace"] is not None:
+                            expected_keys.append("backtrace")
+                            assert actual["fragment"] == expected["fragment"]
+                            check_backtrace(obj, actual["backtrace"], expected["backtrace"])
+
+                        assert sorted(actual.keys()) == sorted(expected_keys)
+
+                    check_list_match(lambda a, e: is_string(a["fragment"], e["fragment"]),
+                                     actual["compileCommandFragments"], expected["compileCommandFragments"],
+                                     check=check_compile_command_fragments,
+                                     check_exception=lambda a, e: "Compile fragment: %s" % a["fragment"],
+                                     missing_exception=lambda e: "Compile fragment: %s" % e["fragment"],
+                                     extra_exception=lambda a: "Compile fragment: %s" % a["fragment"],
+                                     allow_extra=True)
+
                 if expected["includes"] is not None:
                     expected_keys.append("includes")
 
@@ -931,6 +969,7 @@
                             "backtrace": None,
                         },
                     ],
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -979,6 +1018,11 @@
                     "_dllExtra": False,
                 },
                 {
+                    "path": "^lib/my_interface_exe\\.imp$",
+                    "_aixExtra": True,
+                    "_dllExtra": False,
+                },
+                {
                     "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib)$",
                     "_dllExtra": True,
                 },
@@ -993,6 +1037,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -1059,6 +1104,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1166,6 +1212,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1212,6 +1259,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -1312,6 +1360,7 @@
                             "backtrace": None,
                         },
                     ],
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1362,6 +1411,7 @@
             "link": {
                 "language": "C",
                 "lto": True,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -1428,6 +1478,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+					"compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1474,6 +1525,7 @@
             "link": {
                 "language": "C",
                 "lto": True,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -1569,6 +1621,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1676,6 +1729,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -1722,6 +1776,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -1974,6 +2029,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2057,6 +2113,25 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": [
+                        {
+                            "fragment" : "TargetCompileOptions",
+							"backtrace": [
+                                {
+                                    "file": "^cxx/CMakeLists\\.txt$",
+                                    "line": 17,
+                                    "command": "target_compile_options",
+                                    "hasParent": True,
+                                },
+								{
+                                    "file" : "^cxx/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        }
+                    ],
                 },
             ],
             "backtrace": [
@@ -2124,6 +2199,62 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": [
+                    {
+                        "fragment" : "TargetLinkOptions",
+                        "role" : "flags",
+                        "backtrace": [
+                            {
+                                "file": "^cxx/CMakeLists\\.txt$",
+                                "line": 18,
+                                "command": "target_link_options",
+                                "hasParent": True,
+                            },
+                            {
+                                "file" : "^cxx/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                    {
+                        "fragment" : ".*TargetLinkDir\\\"?$",
+                        "role" : "libraryPath",
+                        "backtrace": [
+                            {
+                                "file": "^cxx/CMakeLists\\.txt$",
+                                "line": 19,
+                                "command": "target_link_directories",
+                                "hasParent": True,
+                            },
+                            {
+                                "file" : "^cxx/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                    {
+                        "fragment" : ".*cxx_lib.*",
+                        "role" : "libraries",
+                        "backtrace": [
+                            {
+                                "file": "^cxx/CMakeLists\\.txt$",
+                                "line": 6,
+                                "command": "target_link_libraries",
+                                "hasParent": True,
+                            },
+                            {
+                                "file" : "^cxx/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                ],
             },
             "archive": None,
             "dependencies": [
@@ -2200,6 +2331,7 @@
                             "backtrace": None,
                         },
                     ],
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2238,6 +2370,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -2292,6 +2425,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2326,6 +2460,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -2397,6 +2532,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2480,6 +2616,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2514,6 +2651,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -2738,6 +2876,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2772,6 +2911,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -2843,6 +2983,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -2877,6 +3018,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3109,6 +3251,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3216,6 +3359,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3271,6 +3415,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3332,6 +3477,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3439,6 +3585,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3494,6 +3641,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3720,6 +3868,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3754,6 +3903,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3808,6 +3958,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3842,6 +3993,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3896,6 +4048,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -3930,6 +4083,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -3984,6 +4138,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -4018,6 +4173,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -4072,6 +4228,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -4106,6 +4263,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -4396,6 +4554,7 @@
                     ],
                     "includes": None,
                     "defines": None,
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -4430,6 +4589,7 @@
             "link": {
                 "language": "C",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -4678,7 +4838,20 @@
                         {
                             "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$",
                             "isSystem": None,
-                            "backtrace": None,
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 10,
+                                    "command": "set_property",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
                         },
                         {
                             "path": "^.*/Tests/RunCMake/FileAPIExternalSource$",
@@ -4702,11 +4875,37 @@
                     "defines": [
                         {
                             "define": "EMPTY_C=1",
-                            "backtrace": None,
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 9,
+                                    "command": "set_property",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
                         },
                         {
                             "define": "SRC_DUMMY",
-                            "backtrace": None,
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 9,
+                                    "command": "set_property",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
                         },
                         {
                             "define": "GENERATED_EXE=1",
@@ -4743,6 +4942,25 @@
                             ],
                         },
                     ],
+                    "compileCommandFragments": [
+                        {
+                            "fragment" : "SRC_COMPILE_OPTIONS_DUMMY",
+                            "backtrace": [
+                                {
+                                    "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": 13,
+                                    "command": "set_source_files_properties",
+                                    "hasParent": True,
+                                },
+                                {
+                                    "file" : "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$",
+                                    "line": None,
+                                    "command": None,
+                                    "hasParent": False,
+                                },
+                            ],
+                        }
+                    ],
                 },
                 {
                     "language": "CXX",
@@ -4805,6 +5023,7 @@
                             ],
                         },
                     ],
+                    "compileCommandFragments": None,
                 },
             ],
             "backtrace": [
@@ -4839,6 +5058,7 @@
             "link": {
                 "language": "CXX",
                 "lto": None,
+                "commandFragments": None,
             },
             "archive": None,
             "dependencies": [
@@ -4928,6 +5148,10 @@
         for e in expected:
             e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"])
 
+    if "aix" not in sys.platform:
+        for e in expected:
+            e["artifacts"] = filter_list(lambda a: not a.get("_aixExtra", False), e["artifacts"])
+
     return expected
 
 def check_targets(c, g, inSource):
diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
index 29b61b8..b0564f5 100644
--- a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
+++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
@@ -13,3 +13,7 @@
 add_library(cxx_static_lib STATIC ../empty.cxx)
 add_executable(cxx_static_exe ../empty.cxx)
 target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib)
+
+target_compile_options(cxx_exe PUBLIC TargetCompileOptions)
+target_link_options(cxx_exe PUBLIC TargetLinkOptions)
+target_link_directories(cxx_exe PUBLIC "${CMAKE_BINARY_DIR}/TargetLinkDir")
diff --git a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
index f5670a7..b3ca660 100644
--- a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
+++ b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
@@ -10,3 +10,4 @@
 set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")
 target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
 target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY)
+set_source_files_properties(empty.c PROPERTIES COMPILE_OPTIONS SRC_COMPILE_OPTIONS_DUMMY)
diff --git a/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt b/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt
index 0a67488..ebd7232 100644
--- a/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt
+++ b/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt
@@ -1,2 +1,2 @@
 -- Found Boost: [^
-]* \(found suitable version "1\.12345", minimum required is "1\.12345"\) found components:  date_time
+]* \(found suitable version "1\.12345", minimum required is "1\.12345"\) found components: date_time
diff --git a/Tests/RunCMake/FindBoost/LegacyVars-LowercaseTargetPrefix-stdout.txt b/Tests/RunCMake/FindBoost/LegacyVars-LowercaseTargetPrefix-stdout.txt
index a781dce..1175425 100644
--- a/Tests/RunCMake/FindBoost/LegacyVars-LowercaseTargetPrefix-stdout.txt
+++ b/Tests/RunCMake/FindBoost/LegacyVars-LowercaseTargetPrefix-stdout.txt
@@ -1,5 +1,5 @@
 -- Found Boost: [^
-]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components:  date_time python37 mpi_python2 *
+]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components: date_time python37 mpi_python2 *
 -- Boost_FOUND: TRUE
 -- Boost_INCLUDE_DIRS: [^
 ]*/Tests/RunCMake/FindBoost/CMakePackage[^/]*/include
diff --git a/Tests/RunCMake/FindBoost/LegacyVars-TargetsDefined-stdout.txt b/Tests/RunCMake/FindBoost/LegacyVars-TargetsDefined-stdout.txt
index a4e9c6a..101d60e 100644
--- a/Tests/RunCMake/FindBoost/LegacyVars-TargetsDefined-stdout.txt
+++ b/Tests/RunCMake/FindBoost/LegacyVars-TargetsDefined-stdout.txt
@@ -1,5 +1,5 @@
 -- Found Boost: [^
-]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components:  date_time python37 mpi_python2 *
+]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components: date_time python37 mpi_python2 *
 -- Boost_FOUND: TRUE
 -- Boost_INCLUDE_DIRS: [^
 ]*/Tests/RunCMake/FindBoost/CMakePackage[^/]*/include
diff --git a/Tests/RunCMake/FindBoost/MissingTarget-stdout.txt b/Tests/RunCMake/FindBoost/MissingTarget-stdout.txt
index 8e9d684..9853c01 100644
--- a/Tests/RunCMake/FindBoost/MissingTarget-stdout.txt
+++ b/Tests/RunCMake/FindBoost/MissingTarget-stdout.txt
@@ -1,5 +1,5 @@
 -- Found Boost: [^
-]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components:  date_time *
+]* \(found suitable version "1\.70\.42", minimum required is "1\.70"\) found components: date_time *
 -- Boost_FOUND: TRUE
 -- Boost_INCLUDE_DIRS: [^
 ]*/Tests/RunCMake/FindBoost/CMakePackage_MissingTarget/include
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_MODULE_NAME.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_MODULE_NAME.cmake
new file mode 100644
index 0000000..fc3a766
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_MODULE_NAME.cmake
@@ -0,0 +1,28 @@
+# Prepare environment to reuse bletch.pc
+file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/pc-bletch/lib/pkgconfig" PC_PATH)
+if(UNIX)
+  string(REPLACE "\\ " " " PC_PATH "${PC_PATH}")
+endif()
+set(ENV{PKG_CONFIG_PATH} "${PC_PATH}")
+
+find_package(PkgConfig REQUIRED)
+pkg_search_module(FOO REQUIRED foo bletch bar)
+
+if(NOT FOO_MODULE_NAME STREQUAL "bletch")
+  message(FATAL_ERROR "Wrong value for FOO_MODULE_NAME. Expected: bletch, got: ${FOO_MODULE_NAME}")
+endif()
+
+pkg_get_variable(FOO_JACKPOT ${FOO_MODULE_NAME} jackpot)
+
+if(NOT FOO_JACKPOT STREQUAL "bletch-lives")
+  message(FATAL_ERROR "Wrong value for FOO_JACKPOT. Expected: bletch-lives, got: ${FOO_JACKPOT}")
+endif()
+
+unset(FOO_MODULE_NAME)
+
+# verify variable get's also set on subsequent run
+pkg_search_module(FOO REQUIRED foo bletch bar)
+
+if(NOT FOO_MODULE_NAME STREQUAL "bletch")
+  message(FATAL_ERROR "Wrong value for FOO_MODULE_NAME on second run. Expected: bletch, got: ${FOO_MODULE_NAME}")
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
index e82b05f..62bb5de 100644
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
@@ -39,7 +39,7 @@
 "Name: CMakeInternalFakePackage${i}
 Description: Dummy package (${i}) for FindPkgConfig IMPORTED_TARGET test
 Version: 1.2.3
-Libs: -l${pname}
+Libs: -l${pname} -l${pname}-doesnotexist
 ")
 endforeach()
 
@@ -47,27 +47,6 @@
 # the import target find_library() calls handle the NO...PATH options correctly
 set(ENV{PKG_CONFIG_PATH} ${fakePkgDir}/lib/pkgconfig)
 
-# Confirm correct behavior of NO_CMAKE_PATH, ensuring we only find the library
-# for the imported target if we have both set CMAKE_PREFIX_PATH and have not
-# given the NO_CMAKE_PATH option
-unset(CMAKE_PREFIX_PATH)
-unset(ENV{CMAKE_PREFIX_PATH})
-pkg_check_modules(FakePackage1 QUIET IMPORTED_TARGET cmakeinternalfakepackage1)
-if (TARGET PkgConfig::FakePackage1)
-  message(FATAL_ERROR "Have import target for fake package 1 with no path prefix")
-endif()
-
-set(CMAKE_PREFIX_PATH ${fakePkgDir})
-pkg_check_modules(FakePackage1 QUIET IMPORTED_TARGET NO_CMAKE_PATH cmakeinternalfakepackage1)
-if (TARGET PkgConfig::FakePackage1)
-  message(FATAL_ERROR "Have import target for fake package 1 with ignored cmake path")
-endif()
-
-pkg_check_modules(FakePackage1 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakepackage1)
-if (NOT TARGET PkgConfig::FakePackage1)
-  message(FATAL_ERROR "No import target for fake package 1 with prefix path")
-endif()
-
 # find targets in subdir and check their visibility
 add_subdirectory(target_subdir)
 if (TARGET PkgConfig::FakePackage1_dir)
@@ -82,31 +61,35 @@
 # combination
 unset(CMAKE_PREFIX_PATH)
 unset(ENV{CMAKE_PREFIX_PATH})
-pkg_check_modules(FakePackage2 QUIET IMPORTED_TARGET cmakeinternalfakepackage2)
-if (TARGET PkgConfig::FakePackage2)
-  message(FATAL_ERROR "Have import target for fake package 2 with no path prefix")
-endif()
-
 set(ENV{CMAKE_PREFIX_PATH} ${fakePkgDir})
-pkg_check_modules(FakePackage2 QUIET IMPORTED_TARGET NO_CMAKE_ENVIRONMENT_PATH cmakeinternalfakepackage2)
-if (TARGET PkgConfig::FakePackage2)
-  message(FATAL_ERROR "Have import target for fake package 2 with ignored cmake path")
-endif()
 
 pkg_check_modules(FakePackage2 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakepackage2)
 if (NOT TARGET PkgConfig::FakePackage2)
   message(FATAL_ERROR "No import target for fake package 2 with prefix path")
 endif()
 
+# check that 2 library entries exist
+list(LENGTH FakePackage2_LINK_LIBRARIES fp2_nlibs)
+if (NOT fp2_nlibs EQUAL 2)
+  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has ${fp2_nlibs} entries but should have exactly 2")
+endif()
+
 # check that the full library path is also returned
-if (NOT FakePackage2_LINK_LIBRARIES STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a")
+list(GET FakePackage2_LINK_LIBRARIES 0 fp2_lib0)
+if (NOT fp2_lib0 STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a")
+  message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on first run: ${FakePackage2_LINK_LIBRARIES}")
+endif()
+
+# check that the library that couldn't be found still shows up
+list(GET FakePackage2_LINK_LIBRARIES 1 fp2_lib1)
+if (NOT fp2_lib1 STREQUAL "cmakeinternalfakepackage2-doesnotexist")
   message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on first run: ${FakePackage2_LINK_LIBRARIES}")
 endif()
 
 # the information in *_LINK_LIBRARIES is not cached, so ensure is also is present on second run
 unset(FakePackage2_LINK_LIBRARIES)
 pkg_check_modules(FakePackage2 REQUIRED QUIET IMPORTED_TARGET cmakeinternalfakepackage2)
-if (NOT FakePackage2_LINK_LIBRARIES STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a")
+if (NOT FakePackage2_LINK_LIBRARIES STREQUAL "${fakePkgDir}/lib/libcmakeinternalfakepackage2.a;cmakeinternalfakepackage2-doesnotexist")
   message(FATAL_ERROR "FakePackage2_LINK_LIBRARIES has bad content on second run: ${FakePackage2_LINK_LIBRARIES}")
 endif()
 
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
index 414d9b6..b77bb54 100644
--- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -19,4 +19,5 @@
   run_cmake(FindPkgConfig_cache_variables)
   run_cmake(FindPkgConfig_IMPORTED_TARGET)
   run_cmake(FindPkgConfig_VERSION_OPERATORS)
+  run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME)
 endif ()
diff --git a/Tests/RunCMake/GenerateExportHeader/exportheader_test.cpp b/Tests/RunCMake/GenerateExportHeader/exportheader_test.cpp
index 7e3e0e4..ba77679 100644
--- a/Tests/RunCMake/GenerateExportHeader/exportheader_test.cpp
+++ b/Tests/RunCMake/GenerateExportHeader/exportheader_test.cpp
@@ -1,13 +1,13 @@
 
-#include "libshared.h"
-
-#include "libstatic.h"
-
 #include <fstream>
 #include <iostream>
-#include <stdlib.h>
 #include <string>
 
+#include <stdlib.h>
+
+#include "libshared.h"
+#include "libstatic.h"
+
 void compare(const char* refName, const char* testName)
 {
   std::ifstream ref;
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX-imported-target.cmake
index 34e500a..f52776e 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX-imported-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX-imported-target.cmake
@@ -38,7 +38,7 @@
 
 string (APPEND GENERATE_CONTENT
 "\ncheck_value (\"TARGET_FILE_PREFIX executable custom\" \"$<TARGET_FILE_PREFIX:exec2>\" \"exec2_prefix\")
-check_value (\"TARGET_LINKER_FILE_PREFIX executable linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,exec2_import_prefix,exec2_prefix>\")
+check_value (\"TARGET_LINKER_FILE_PREFIX executable linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms};AIX>,exec2_import_prefix,exec2_prefix>\")
 check_value (\"TARGET_FILE_PREFIX shared custom\" \"$<TARGET_FILE_PREFIX:shared2>\" \"shared2_prefix\")
 check_value (\"TARGET_LINKER_FILE_PREFIX shared linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,shared2_import_prefix,shared2_prefix>\")
 check_value (\"TARGET_FILE_PREFIX static custom\" \"$<TARGET_FILE_PREFIX:static2>\" \"static2_prefix\")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX.cmake
index 6bb1e44..bef7bbf 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_PREFIX.cmake
@@ -38,7 +38,7 @@
 
 string (APPEND GENERATE_CONTENT
 "\ncheck_value (\"TARGET_FILE_PREFIX executable custom\" \"$<TARGET_FILE_PREFIX:exec2>\" \"exec2_prefix\")
-check_value (\"TARGET_LINKER_FILE_PREFIX executable linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,exec2_import_prefix,exec2_prefix>\")
+check_value (\"TARGET_LINKER_FILE_PREFIX executable linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms};AIX>,exec2_import_prefix,exec2_prefix>\")
 check_value (\"TARGET_FILE_PREFIX shared custom\" \"$<TARGET_FILE_PREFIX:shared2>\" \"shared2_prefix\")
 check_value (\"TARGET_LINKER_FILE_PREFIX shared linker custom\" \"$<TARGET_LINKER_FILE_PREFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,shared2_import_prefix,shared2_prefix>\")
 check_value (\"TARGET_FILE_PREFIX static custom\" \"$<TARGET_FILE_PREFIX:static2>\" \"static2_prefix\")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX-imported-target.cmake
index e1b7654..cefeb86 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX-imported-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX-imported-target.cmake
@@ -38,7 +38,7 @@
 
 string (APPEND GENERATE_CONTENT
 "\ncheck_value (\"TARGET_FILE_SUFFIX executable custom\" \"$<TARGET_FILE_SUFFIX:exec2>\" \"exec2_suffix\")
-check_value (\"TARGET_LINKER_FILE_SUFFIX executable linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,exec2_import_suffix,exec2_suffix>\")
+check_value (\"TARGET_LINKER_FILE_SUFFIX executable linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms};AIX>,exec2_import_suffix,exec2_suffix>\")
 check_value (\"TARGET_FILE_SUFFIX shared custom\" \"$<TARGET_FILE_SUFFIX:shared2>\" \"shared2_suffix\")
 check_value (\"TARGET_LINKER_FILE_SUFFIX shared linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,shared2_import_suffix,shared2_suffix>\")
 check_value (\"TARGET_FILE_SUFFIX static custom\" \"$<TARGET_FILE_SUFFIX:static2>\" \"static2_suffix\")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX.cmake
index 78afecd..39e39fd 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_SUFFIX.cmake
@@ -38,7 +38,7 @@
 
 string (APPEND GENERATE_CONTENT
 "\ncheck_value (\"TARGET_FILE_SUFFIX executable custom\" \"$<TARGET_FILE_SUFFIX:exec2>\" \"exec2_suffix\")
-check_value (\"TARGET_LINKER_FILE_SUFFIX executable linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,exec2_import_suffix,exec2_suffix>\")
+check_value (\"TARGET_LINKER_FILE_SUFFIX executable linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms};AIX>,exec2_import_suffix,exec2_suffix>\")
 check_value (\"TARGET_FILE_SUFFIX shared custom\" \"$<TARGET_FILE_SUFFIX:shared2>\" \"shared2_suffix\")
 check_value (\"TARGET_LINKER_FILE_SUFFIX shared linker custom\" \"$<TARGET_LINKER_FILE_SUFFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${win_platforms}>,shared2_import_suffix,shared2_suffix>\")
 check_value (\"TARGET_FILE_SUFFIX static custom\" \"$<TARGET_FILE_SUFFIX:static2>\" \"static2_suffix\")
diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
index ef8fd25..ae75561 100644
--- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
@@ -6,12 +6,14 @@
 if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[012456]")
   set(RunCMake_GENERATOR_TOOLSET "Test Toolset")
   run_cmake(TestToolset)
-  set(RunCMake_GENERATOR_TOOLSET "Test Toolset,cuda=Test Cuda")
+  set(RunCMake_GENERATOR_TOOLSET "Test Toolset,cuda=0.0")
   run_cmake(TestToolsetCudaBoth)
-  set(RunCMake_GENERATOR_TOOLSET ",cuda=Test Cuda")
-  run_cmake(TestToolsetCudaOnly)
-  set(RunCMake_GENERATOR_TOOLSET "cuda=Test Cuda")
-  run_cmake(TestToolsetCudaOnly)
+  set(RunCMake_GENERATOR_TOOLSET ",cuda=0.0")
+  run_cmake(TestToolsetCudaVersionOnly)
+  set(RunCMake_GENERATOR_TOOLSET "cuda=0.0")
+  run_cmake(TestToolsetCudaVersionOnly)
+  set(RunCMake_GENERATOR_TOOLSET "cuda=C:\\dummy\\cuda")
+  run_cmake(TestToolsetCudaPathOnly)
   if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[2456]")
     set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64")
     run_cmake(TestToolsetHostArchBoth)
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaBoth-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaBoth-stdout.txt
index 90503e2..f12c123 100644
--- a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaBoth-stdout.txt
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaBoth-stdout.txt
@@ -1,2 +1,2 @@
 -- CMAKE_VS_PLATFORM_TOOLSET='Test Toolset'
--- CMAKE_VS_PLATFORM_TOOLSET_CUDA='Test Cuda'
+-- CMAKE_VS_PLATFORM_TOOLSET_CUDA='0.0'
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaOnly-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaOnly-stdout.txt
deleted file mode 100644
index 94e1e43..0000000
--- a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaOnly-stdout.txt
+++ /dev/null
@@ -1,2 +0,0 @@
--- CMAKE_VS_PLATFORM_TOOLSET='(v[0-9]+|Windows7.1SDK)'
--- CMAKE_VS_PLATFORM_TOOLSET_CUDA='Test Cuda'
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-result.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-stderr.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-stderr.txt
new file mode 100644
index 0000000..b17745f
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly-stderr.txt
@@ -0,0 +1,12 @@
+CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Visual Studio .*
+
+  given toolset
+
+    cuda=C:\\dummy\\cuda\\
+
+  cannot detect Visual Studio integration files in path
+
+    C:/dummy/cuda/CUDAVisualStudioIntegration/extras/visual_studio_integration/MSBuildExtensions
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly.cmake
new file mode 100644
index 0000000..2fc38e5
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaPathOnly.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaVersionOnly-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaVersionOnly-stdout.txt
new file mode 100644
index 0000000..1717ff8
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaVersionOnly-stdout.txt
@@ -0,0 +1,2 @@
+-- CMAKE_VS_PLATFORM_TOOLSET='(v[0-9]+|Windows7.1SDK)'
+-- CMAKE_VS_PLATFORM_TOOLSET_CUDA='0.0'
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetCudaOnly.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetCudaVersionOnly.cmake
similarity index 100%
rename from Tests/RunCMake/GeneratorToolset/TestToolsetCudaOnly.cmake
rename to Tests/RunCMake/GeneratorToolset/TestToolsetCudaVersionOnly.cmake
diff --git a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
index f2d749b..d5c480d 100644
--- a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
@@ -1,6 +1,6 @@
 CMake Error in CMakeLists.txt:
   Target "testTarget" INTERFACE_INCLUDE_DIRECTORIES property contains path:
 
-    ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/BinInInstallPrefix-CMP0052-NEW-build/foo"
+    ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/BinInInstallPrefix-CMP0052-NEW-build/foo"
 
   which is prefixed in the build directory.
diff --git a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
index 3d83892..eb4d56d 100644
--- a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
@@ -6,15 +6,15 @@
 
   Directory:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/BinInInstallPrefix-CMP0052-WARN-build/foo"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/BinInInstallPrefix-CMP0052-WARN-build/foo"
 
   in INTERFACE_INCLUDE_DIRECTORIES of target "testTarget" is a subdirectory
   of the install directory:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix"
 
   however it is also a subdirectory of the build tree:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/BinInInstallPrefix-CMP0052-WARN-build"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/BinInInstallPrefix-CMP0052-WARN-build"
 
 This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/IfacePaths/BinaryDirectoryInInterface-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/BinaryDirectoryInInterface-stderr_INCLUDE_DIRECTORIES.txt
index ed7cd58..8794a87 100644
--- a/Tests/RunCMake/IfacePaths/BinaryDirectoryInInterface-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/BinaryDirectoryInInterface-stderr_INCLUDE_DIRECTORIES.txt
@@ -1,6 +1,6 @@
 CMake Error in CMakeLists.txt:
   Target "testTarget" INTERFACE_INCLUDE_DIRECTORIES property contains path:
 
-    ".*RunCMake/IfacePaths_INCLUDE_DIRECTORIES/BinaryDirectoryInInterface-build/foo"
+    ".*RunCMake/IfacePaths_INCDIRS/BinaryDirectoryInInterface-build/foo"
 
   which is prefixed in the build directory.
diff --git a/Tests/RunCMake/IfacePaths/InstallInBinDir-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/InstallInBinDir-stderr_INCLUDE_DIRECTORIES.txt
index 3d60831..4ba9360 100644
--- a/Tests/RunCMake/IfacePaths/InstallInBinDir-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/InstallInBinDir-stderr_INCLUDE_DIRECTORIES.txt
@@ -1,6 +1,6 @@
 CMake Error in CMakeLists.txt:
   Target "testTarget" INTERFACE_INCLUDE_DIRECTORIES property contains path:
 
-    ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/InstallInBinDir-build/foo"
+    ".*Tests/RunCMake/IfacePaths_INCDIRS/InstallInBinDir-build/foo"
 
   which is prefixed in the build directory.
diff --git a/Tests/RunCMake/IfacePaths/InstallInSrcDir-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/InstallInSrcDir-stderr_INCLUDE_DIRECTORIES.txt
index 11994dd..3c126df 100644
--- a/Tests/RunCMake/IfacePaths/InstallInSrcDir-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/InstallInSrcDir-stderr_INCLUDE_DIRECTORIES.txt
@@ -1,6 +1,6 @@
 CMake Error in CMakeLists.txt:
   Target "testTarget" INTERFACE_INCLUDE_DIRECTORIES property contains path:
 
-    ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/copy/foo"
+    ".*Tests/RunCMake/IfacePaths_INCDIRS/copy/foo"
 
   which is prefixed in the source directory.
diff --git a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
index ed5df34..38bfa4e 100644
--- a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-NEW-stderr_INCLUDE_DIRECTORIES.txt
@@ -1,6 +1,6 @@
 CMake Error in CMakeLists.txt:
   Target "testTarget" INTERFACE_INCLUDE_DIRECTORIES property contains path:
 
-    ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/src/foo"
+    ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/src/foo"
 
   which is prefixed in the source directory.
diff --git a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
index cb5a51c..fd6e372 100644
--- a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
+++ b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-WARN-stderr_INCLUDE_DIRECTORIES.txt
@@ -6,15 +6,15 @@
 
   Directory:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/src/foo"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/src/foo"
 
   in INTERFACE_INCLUDE_DIRECTORIES of target "testTarget" is a subdirectory
   of the install directory:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix"
 
   however it is also a subdirectory of the source tree:
 
-      ".*Tests/RunCMake/IfacePaths_INCLUDE_DIRECTORIES/prefix/src"
+      ".*Tests/RunCMake/IfacePaths_INCDIRS/prefix/src"
 
 This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/ObjectLibrary/InstallLinkedObj1-stderr.txt b/Tests/RunCMake/ObjectLibrary/InstallLinkedObj1-stderr.txt
index f2f0f94..c663707 100644
--- a/Tests/RunCMake/ObjectLibrary/InstallLinkedObj1-stderr.txt
+++ b/Tests/RunCMake/ObjectLibrary/InstallLinkedObj1-stderr.txt
@@ -1 +1 @@
-CMake Error: install\(EXPORT "exp" ...\) includes target "UseA" which requires target "A" that is not in the export set.
+CMake Error: install\(EXPORT "exp" ...\) includes target "UseA" which requires target "A" that is not in any export set.
diff --git a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.c b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.c
new file mode 100644
index 0000000..088da30
--- /dev/null
+++ b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.c
@@ -0,0 +1,17 @@
+#ifndef REQUIRED
+#  error "REQUIRED not defined"
+#endif
+
+#if defined(_WIN32)
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
+IMPORT int a(void);
+extern int required(void);
+
+int main(void)
+{
+  return required() + a();
+}
diff --git a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
index 4aa7bba..0a76932 100644
--- a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
+++ b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
@@ -1,7 +1,15 @@
 project(LinkObjLHSShared C)
 
+# Create a versioned shared library that does not build as part of "all".
 add_library(OtherLib SHARED a.c)
-target_compile_definitions(OtherLib INTERFACE REQUIRED)
+target_compile_definitions(OtherLib INTERFACE REQUIRED PRIVATE COMPILE_FOR_SHARED_LIB)
+set_target_properties(OtherLib PROPERTIES SOVERSION 0 VERSION 0.0.0 EXCLUDE_FROM_ALL ON)
 
 add_library(AnObjLib OBJECT requires.c)
-target_link_libraries(AnObjLib OtherLib)
+target_link_libraries(AnObjLib PUBLIC OtherLib)
+
+add_executable(LinkObjLHSShared LinkObjLHSShared.c)
+target_link_libraries(LinkObjLHSShared AnObjLib)
+
+# Verify that our dependency on OtherLib generated its versioning symlinks.
+add_custom_command(TARGET LinkObjLHSShared POST_BUILD COMMAND LinkObjLHSShared)
diff --git a/Tests/RunCMake/ParseImplicitData/CMakeLists.txt b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt
new file mode 100644
index 0000000..7a8570b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt
@@ -0,0 +1,91 @@
+#
+# helper CMakeLists.txt file that can be used to generate input files
+# for the Tests/RunCMake/ParseImplicit[Include|Lib]Info tests.
+#
+# usage:
+#  [create a temporary build directory and chdir to it]
+#  cmake [-D options] $CMAKE_SRC/Tests/RunCMake/ParseImplicitIncludeInfo/data
+#
+# where useful -D options include:
+#   -DLANGUAGES="C;CXX"    -- list of languages to generate inputs for
+#   -DUNAME="Darwin"       -- operating system name (def: CMAKE_SYSTEM_NAME)
+#
+
+cmake_minimum_required(VERSION 3.3)
+if(POLICY CMP0089)
+  cmake_policy(SET CMP0089 NEW)
+endif()
+
+set(lngs C CXX)
+set(LANGUAGES "${lngs}" CACHE STRING "List of languages to generate inputs for")
+
+project(gen_implicit_include_data ${LANGUAGES})
+
+set(UNAME "${CMAKE_SYSTEM_NAME}" CACHE STRING "System uname")
+string(TOLOWER "${UNAME}" UNAME)
+message("Generate input for system type: ${UNAME}")
+
+# CMAKE_<LANG>_COMPILER_* variables we save in the resultfile
+set(compvars ABI AR ARCHITECTURE_ID EXTERNAL_TOOLCHAIN ID LAUNCHER LOADED
+  RANLIB TARGET VERSION VERSION_INTERAL)
+
+foreach(lang IN ITEMS ${LANGUAGES})
+
+  if("${lang}" STREQUAL "C")
+    set(file ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c)
+  elseif("${lang}" STREQUAL "CXX")
+    set(file ${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp)
+  elseif("${lang}" STREQUAL "CUDA")
+    set(file ${CMAKE_ROOT}/Modules/CMakeCUDACompilerABI.cu)
+  elseif("${lang}" STREQUAL "Fortran")
+    set(file ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F)
+  else()
+    message(FATAL_ERROR "unknown language ${lang}")
+  endif()
+
+  set(resultfile "${CMAKE_BINARY_DIR}/")
+  string(APPEND resultfile ${UNAME}-${lang}-${CMAKE_${lang}_COMPILER_ID})
+  string(APPEND resultfile -${CMAKE_${lang}_COMPILER_VERSION})
+  string(APPEND resultfile .input)
+  message("Generate input for language ${lang}")
+  message("Input file: ${file}")
+  message("Result file: ${resultfile}")
+
+  # replicate logic from CMakeDetermineCompilerABI
+  set(outfile "${CMAKE_PLATFORM_INFO_DIR}/test${lang}.out")
+  set(CMAKE_FLAGS )
+  set(COMPILE_DEFINITIONS )
+  if(DEFINED CMAKE_${lang}_VERBOSE_FLAG)
+    set(CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${CMAKE_${lang}_VERBOSE_FLAG}")
+    set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
+  endif()
+  if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG)
+    set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}")
+  endif()
+  if(NOT "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
+    # Avoid adding our own platform standard libraries for compilers
+    # from which we might detect implicit link libraries.
+    list(APPEND CMAKE_FLAGS "-DCMAKE_${lang}_STANDARD_LIBRARIES=")
+  endif()
+
+  try_compile(rv ${CMAKE_BINARY_DIR} ${file}
+    CMAKE_FLAGS ${CMAKE_FLAGS}
+    COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS}
+    CMAKE_FLAGS ${CMAKE_FLAGS}
+    OUTPUT_VARIABLE output
+    COPY_FILE "${outfile}"
+    COPY_FILE_ERROR copy_error)
+
+  if(NOT rv)
+    message(FATAL_ERROR "${lang} compile failed!!")
+  endif()
+
+  set(result "CMAKE_LANG=${lang}\n")
+  list(APPEND result "CMAKE_LINKER=${CMAKE_LINKER}\n")
+  foreach(var IN ITEMS ${compvars})
+    list(APPEND result
+      "CMAKE_${lang}_COMPILER_${var}=${CMAKE_${lang}_COMPILER_${var}}\n")
+  endforeach()
+
+  file(WRITE ${resultfile} ${result} ${output})
+endforeach()
diff --git a/Tests/RunCMake/ParseImplicitData/README b/Tests/RunCMake/ParseImplicitData/README
new file mode 100644
index 0000000..4680254
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/README
@@ -0,0 +1,26 @@
+This directory contains sample input files for the implicit include
+directories, and link info parsers for testing.  For each configuration
+ there is one ".input" file and matching ".output" files in
+ParseImplicitIncludeInfo/results and ParseImplicitLinkInfo/results.
+
+To generate ".input" files for a system, create a temporary build
+directory and chdir to it.  Then run cmake pointing to this directory.
+The CMakeLists.txt file here will generate ".input" files in your
+build directory.  The default set of languages is C and CXX.  This
+can be changed with -DLANGUAGES=language_list.  For example:
+-DLANGUAGES=Fortran will generate Fortran parser input.
+
+The ".output" files should be generated by hand from the input files.
+The test will compare the parser output to the manually generated
+".output" file.  The two should match.
+
+For compilers that support "-nostdinc"-like flags, you can generate
+a test for this with a command like:
+cmake -DUNAME=netbsd_nostdinc \
+  -DCMAKE_C_FLAGS=-nostdinc -DCMAKE_CXX_FLAGS=-nostdinc .
+
+Here is an example for testing the XL compiler with both -I and nostdinc:
+
+env CC=xlc CXX=xlC cmake -DUNAME=linux_nostdinc_i \
+  -DCMAKE_C_FLAGS='-qnostdinc -I/tmp/ii/test_c' \
+  -DCMAKE_CXX_FLAGS='-qnostdinc -I/tmp/ii/test_c -I/tmp/ii/test_cxx' .
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XL-13.1.3.input b/Tests/RunCMake/ParseImplicitData/aix-C-XL-13.1.3.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XL-13.1.3.input
rename to Tests/RunCMake/ParseImplicitData/aix-C-XL-13.1.3.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XLClang-16.1.0.1.input b/Tests/RunCMake/ParseImplicitData/aix-C-XLClang-16.1.0.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XLClang-16.1.0.1.input
rename to Tests/RunCMake/ParseImplicitData/aix-C-XLClang-16.1.0.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XL-13.1.3.input b/Tests/RunCMake/ParseImplicitData/aix-CXX-XL-13.1.3.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XL-13.1.3.input
rename to Tests/RunCMake/ParseImplicitData/aix-CXX-XL-13.1.3.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XLClang-16.1.0.1.input b/Tests/RunCMake/ParseImplicitData/aix-CXX-XLClang-16.1.0.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XLClang-16.1.0.1.input
rename to Tests/RunCMake/ParseImplicitData/aix-CXX-XLClang-16.1.0.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-8.7.input b/Tests/RunCMake/ParseImplicitData/craype-C-Cray-8.7.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-8.7.input
rename to Tests/RunCMake/ParseImplicitData/craype-C-Cray-8.7.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-9.0-hlist-ad.input b/Tests/RunCMake/ParseImplicitData/craype-C-Cray-9.0-hlist-ad.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-9.0-hlist-ad.input
rename to Tests/RunCMake/ParseImplicitData/craype-C-Cray-9.0-hlist-ad.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/craype-C-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/craype-C-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Intel-18.0.2.20180210.input b/Tests/RunCMake/ParseImplicitData/craype-C-Intel-18.0.2.20180210.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Intel-18.0.2.20180210.input
rename to Tests/RunCMake/ParseImplicitData/craype-C-Intel-18.0.2.20180210.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-8.7.input b/Tests/RunCMake/ParseImplicitData/craype-CXX-Cray-8.7.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-8.7.input
rename to Tests/RunCMake/ParseImplicitData/craype-CXX-Cray-8.7.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-9.0-hlist-ad.input b/Tests/RunCMake/ParseImplicitData/craype-CXX-Cray-9.0-hlist-ad.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-9.0-hlist-ad.input
rename to Tests/RunCMake/ParseImplicitData/craype-CXX-Cray-9.0-hlist-ad.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/craype-CXX-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/craype-CXX-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Intel-18.0.2.20180210.input b/Tests/RunCMake/ParseImplicitData/craype-CXX-Intel-18.0.2.20180210.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Intel-18.0.2.20180210.input
rename to Tests/RunCMake/ParseImplicitData/craype-CXX-Intel-18.0.2.20180210.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-8.7.input b/Tests/RunCMake/ParseImplicitData/craype-Fortran-Cray-8.7.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-8.7.input
rename to Tests/RunCMake/ParseImplicitData/craype-Fortran-Cray-8.7.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-9.0-hlist-ad.input b/Tests/RunCMake/ParseImplicitData/craype-Fortran-Cray-9.0-hlist-ad.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-9.0-hlist-ad.input
rename to Tests/RunCMake/ParseImplicitData/craype-Fortran-Cray-9.0-hlist-ad.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/craype-Fortran-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/craype-Fortran-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Intel-18.0.2.20180210.input b/Tests/RunCMake/ParseImplicitData/craype-Fortran-Intel-18.0.2.20180210.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Intel-18.0.2.20180210.input
rename to Tests/RunCMake/ParseImplicitData/craype-Fortran-Intel-18.0.2.20180210.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-C-AppleClang-8.0.0.8000042.input b/Tests/RunCMake/ParseImplicitData/darwin-C-AppleClang-8.0.0.8000042.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-C-AppleClang-8.0.0.8000042.input
rename to Tests/RunCMake/ParseImplicitData/darwin-C-AppleClang-8.0.0.8000042.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-CXX-AppleClang-8.0.0.8000042.input b/Tests/RunCMake/ParseImplicitData/darwin-CXX-AppleClang-8.0.0.8000042.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-CXX-AppleClang-8.0.0.8000042.input
rename to Tests/RunCMake/ParseImplicitData/darwin-CXX-AppleClang-8.0.0.8000042.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.input b/Tests/RunCMake/ParseImplicitData/darwin_nostdinc-C-AppleClang-8.0.0.8000042.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.input
rename to Tests/RunCMake/ParseImplicitData/darwin_nostdinc-C-AppleClang-8.0.0.8000042.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.input b/Tests/RunCMake/ParseImplicitData/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.input
rename to Tests/RunCMake/ParseImplicitData/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-C-Clang-3.3.0.input b/Tests/RunCMake/ParseImplicitData/freebsd-C-Clang-3.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-C-Clang-3.3.0.input
rename to Tests/RunCMake/ParseImplicitData/freebsd-C-Clang-3.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-CXX-Clang-3.3.0.input b/Tests/RunCMake/ParseImplicitData/freebsd-CXX-Clang-3.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-CXX-Clang-3.3.0.input
rename to Tests/RunCMake/ParseImplicitData/freebsd-CXX-Clang-3.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-Fortran-GNU-4.6.4.input b/Tests/RunCMake/ParseImplicitData/freebsd-Fortran-GNU-4.6.4.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-Fortran-GNU-4.6.4.input
rename to Tests/RunCMake/ParseImplicitData/freebsd-Fortran-GNU-4.6.4.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.input b/Tests/RunCMake/ParseImplicitData/hand-C-empty.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.input
rename to Tests/RunCMake/ParseImplicitData/hand-C-empty.input
diff --git a/Tests/RunCMake/ParseImplicitData/hand-C-relative.input b/Tests/RunCMake/ParseImplicitData/hand-C-relative.input
new file mode 100644
index 0000000..52ac9df
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/hand-C-relative.input
@@ -0,0 +1,23 @@
+CMAKE_LANG=C
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/usr/bin/gcc-ar-7
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=GNU
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/usr/bin/gcc-ranlib-7
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=7.3.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+
+This is a hand-written test case.
+
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/include
+ ../../../adaptive/relative/include
+ /usr/include
+End of search list.
+
+/usr/bin/ld -L/usr/lib64 -L../../../adaptive/relative/lib
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-empty.input b/Tests/RunCMake/ParseImplicitData/hand-CXX-empty.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-empty.input
rename to Tests/RunCMake/ParseImplicitData/hand-CXX-empty.input
diff --git a/Tests/RunCMake/ParseImplicitData/hand-CXX-relative.input b/Tests/RunCMake/ParseImplicitData/hand-CXX-relative.input
new file mode 100644
index 0000000..0b223a1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/hand-CXX-relative.input
@@ -0,0 +1,23 @@
+CMAKE_LANG=CXX
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/usr/bin/gcc-ar-7
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=GNU
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/usr/bin/gcc-ranlib-7
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=7.3.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+
+This is a hand-written test case.
+
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/include
+ ../../../adaptive/relative/include
+ /usr/include
+End of search list.
+
+/usr/bin/ld -L/usr/lib64 -L../../../adaptive/relative/lib
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/linux-C-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-C-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-Intel-18.0.0.20170811.input b/Tests/RunCMake/ParseImplicitData/linux-C-Intel-18.0.0.20170811.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-Intel-18.0.0.20170811.input
rename to Tests/RunCMake/ParseImplicitData/linux-C-Intel-18.0.0.20170811.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux-C-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux-C-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux-C-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-C-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-16.1.0.0.input b/Tests/RunCMake/ParseImplicitData/linux-C-XL-16.1.0.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-16.1.0.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-C-XL-16.1.0.0.input
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-CLANG.input b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-CLANG.input
new file mode 100644
index 0000000..954697d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-CLANG.input
@@ -0,0 +1,242 @@
+CMAKE_LANG=CUDA
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CUDA_COMPILER_ABI=
+CMAKE_CUDA_COMPILER_AR=
+CMAKE_CUDA_COMPILER_ARCHITECTURE_ID=
+CMAKE_CUDA_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CUDA_COMPILER_ID=NVIDIA
+CMAKE_CUDA_COMPILER_LAUNCHER=
+CMAKE_CUDA_COMPILER_LOADED=1
+CMAKE_CUDA_COMPILER_RANLIB=
+CMAKE_CUDA_COMPILER_TARGET=
+CMAKE_CUDA_COMPILER_VERSION=10.1.168
+CMAKE_CUDA_COMPILER_VERSION_INTERAL=
+Change Dir: /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp
+
+Run Build Command(s):/usr/bin/ninja cmTC_e3386 && [1/2] Building CUDA object CMakeFiles/cmTC_e3386.dir/CMakeCUDACompilerABI.cu.o
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/lib/llvm-8/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCUDACompilerABI.cu -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.1 -include cuda_runtime.h -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS -D __CUDACC__ -D __NVCC__ -I /usr/local/cuda/bin/../targets/x86_64-linux/include -D __CUDACC_VER_MAJOR__=10 -D __CUDACC_VER_MINOR__=1 -D __CUDACC_VER_BUILD__=168 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward -internal-isystem /usr/include/clang/8.0.1/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o - -x c++ /opt/cmake-3.15/share/cmake-3.15/Modules/CMakeCUDACompilerABI.cu -faddrsig
+clang -cc1 version 8.0.1 based upon LLVM 8.0.1 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/include"
+ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8"
+ignoring duplicate directory "/usr/include/clang/8.0.1/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/cuda/bin/../targets/x86_64-linux/include
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward
+ /usr/include/clang/8.0.1/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/lib/llvm-8/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCUDACompilerABI.cu -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.1 -include cuda_runtime.h -D __CUDACC__ -D __NVCC__ -I /usr/local/cuda/bin/../targets/x86_64-linux/include -D __CUDACC_VER_MAJOR__=10 -D __CUDACC_VER_MINOR__=1 -D __CUDACC_VER_BUILD__=168 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward -internal-isystem /usr/include/clang/8.0.1/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o - -x c++ /opt/cmake-3.15/share/cmake-3.15/Modules/CMakeCUDACompilerABI.cu -faddrsig
+clang -cc1 version 8.0.1 based upon LLVM 8.0.1 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/include"
+ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8"
+ignoring duplicate directory "/usr/include/clang/8.0.1/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/cuda/bin/../targets/x86_64-linux/include
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward
+ /usr/include/clang/8.0.1/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/lib/llvm-8/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tmpxft_00005e34_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -coverage-notes-file /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_e3386.dir/CMakeCUDACompilerABI.cu.gcno -resource-dir /usr/lib/llvm-8/lib/clang/8.0.1 -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS -I /usr/local/cuda/bin/../targets/x86_64-linux/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward -internal-isystem /usr/include/clang/8.0.1/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o CMakeFiles/cmTC_e3386.dir/CMakeCUDACompilerABI.cu.o -x c++ /tmp/tmpxft_00005e34_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -faddrsig
+clang -cc1 version 8.0.1 based upon LLVM 8.0.1 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/include"
+ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8"
+ignoring duplicate directory "/usr/include/clang/8.0.1/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/cuda/bin/../targets/x86_64-linux/include
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward
+ /usr/include/clang/8.0.1/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/lib/llvm-8/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCUDACompilerABI.cu -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.1 -include cuda_runtime.h -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS -D __CUDACC__ -D __NVCC__ -I /usr/local/cuda/bin/../targets/x86_64-linux/include -D __CUDACC_VER_MAJOR__=10 -D __CUDACC_VER_MINOR__=1 -D __CUDACC_VER_BUILD__=168 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward -internal-isystem /usr/include/clang/8.0.1/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o - -x c++ /opt/cmake-3.15/share/cmake-3.15/Modules/CMakeCUDACompilerABI.cu -faddrsig
+clang -cc1 version 8.0.1 based upon LLVM 8.0.1 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/include"
+ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8"
+ignoring duplicate directory "/usr/include/clang/8.0.1/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/cuda/bin/../targets/x86_64-linux/include
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward
+ /usr/include/clang/8.0.1/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/lib/llvm-8/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name CMakeCUDACompilerABI.cu -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.1 -include cuda_runtime.h -D __CUDACC__ -D __NVCC__ -I /usr/local/cuda/bin/../targets/x86_64-linux/include -D __CUDACC_VER_MAJOR__=10 -D __CUDACC_VER_MINOR__=1 -D __CUDACC_VER_BUILD__=168 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward -internal-isystem /usr/include/clang/8.0.1/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/robert/Work/cmake/cuda_clang_compiler_info/CMakeFiles/CMakeTmp -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o - -x c++ /opt/cmake-3.15/share/cmake-3.15/Modules/CMakeCUDACompilerABI.cu -faddrsig
+clang -cc1 version 8.0.1 based upon LLVM 8.0.1 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/include"
+ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8"
+ignoring duplicate directory "/usr/include/clang/8.0.1/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/local/cuda/bin/../targets/x86_64-linux/include
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/x86_64-linux-gnu/c++/8
+ /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/backward
+ /usr/include/clang/8.0.1/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+[2/2] Linking CUDA executable cmTC_e3386
+clang version 8.0.1-svn369350-1~exp1~20190820121219.79 (branches/release_80)
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /usr/bin
+Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
+Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
+Candidate multilib: .@m64
+Selected multilib: .@m64
+Found CUDA installation: /usr/local/cuda-10.1, version unknown
+ "/usr/bin/ld" -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_e3386 /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crt1.o /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o /usr/bin/../lib/gcc/x86_64-linux-gnu/8/crtbegin.o -L/usr/local/cuda/targets/x86_64-linux/lib/stubs -L/usr/local/cuda/targets/x86_64-linux/lib -L/usr/bin/../lib/gcc/x86_64-linux-gnu/8 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../.. -L/usr/lib/llvm-8/bin/../lib -L/lib -L/usr/lib CMakeFiles/cmTC_e3386.dir/CMakeCUDACompilerABI.cu.o -lcudadevrt -lcudart_static -lrt -lpthread -ldl -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/bin/../lib/gcc/x86_64-linux-gnu/8/crtend.o /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-XLClang-v.input b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-XLClang-v.input
new file mode 100644
index 0000000..7b20288
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-10.1.168-XLClang-v.input
@@ -0,0 +1,51 @@
+CMAKE_LANG=CUDA
+CMAKE_LINKER=/sw/summit/xalt/1.1.3/bin/ld
+CMAKE_CUDA_COMPILER_ABI=ELF
+CMAKE_CUDA_COMPILER_AR=
+CMAKE_CUDA_COMPILER_ARCHITECTURE_ID=
+CMAKE_CUDA_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CUDA_COMPILER_ID=NVIDIA
+CMAKE_CUDA_COMPILER_LAUNCHER=
+CMAKE_CUDA_COMPILER_LOADED=1
+CMAKE_CUDA_COMPILER_RANLIB=
+CMAKE_CUDA_COMPILER_TARGET=
+CMAKE_CUDA_COMPILER_VERSION=10.1.168
+CMAKE_CUDA_COMPILER_VERSION_INTERAL=
+Change Dir: /ccs/home/rmaynard/cmake/extract_xlc_info/CMakeFiles/CMakeTmp
+
+Run Build Command(s):/usr/bin/gmake cmTC_e9f3e/fast && /usr/bin/gmake -f CMakeFiles/cmTC_e9f3e.dir/build.make CMakeFiles/cmTC_e9f3e.dir/build
+gmake[1]: Entering directory `/autofs/nccs-svm1_home1/rmaynard/cmake/extract_xlc_info/CMakeFiles/CMakeTmp'
+Building CUDA object CMakeFiles/cmTC_e9f3e.dir/CMakeCUDACompilerABI.cu.o
+/sw/summit/cuda/10.1.168/bin/nvcc -ccbin=/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin/xlc++   -v -x cu -c /ccs/home/rmaynard/cmake/src/Modules/CMakeCUDACompilerABI.cu -o CMakeFiles/cmTC_e9f3e.dir/CMakeCUDACompilerABI.cu.o
+#$ _SPACE_=
+#$ _CUDART_=cudart
+#$ _HERE_=/sw/summit/cuda/10.1.168/bin
+#$ _THERE_=/sw/summit/cuda/10.1.168/bin
+#$ _TARGET_SIZE_=
+#$ _TARGET_DIR_=
+#$ _TARGET_DIR_=targets/ppc64le-linux
+#$ TOP=/sw/summit/cuda/10.1.168/bin/..
+#$ NVVMIR_LIBRARY_DIR=/sw/summit/cuda/10.1.168/bin/../nvvm/libdevice
+#$ LD_LIBRARY_PATH=/sw/summit/cuda/10.1.168/bin/../lib:/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/xl-16.1.1-3/spectrum-mpi-10.3.0.1-20190611-aqjt3jo53mogrrhcrd2iufr435azcaha/lib:/sw/summit/xl/16.1.1-3/xlsmp/5.1.1/lib:/sw/summit/xl/16.1.1-3/xlmass/9.1.1/lib:/sw/summit/xl/16.1.1-3/xlC/16.1.1/lib:/sw/summit/xl/16.1.1-3/xlf/16.1.1/lib:/sw/summit/xl/16.1.1-3/lib:/sw/summit/cuda/10.1.168/lib64:/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-csygoqyym3m3ysoaperhxlhoiluvpa2u/lib:/opt/ibm/spectrumcomputing/lsf/10.1/linux3.10-glibc2.17-ppc64le-csm/lib
+#$ PATH=/sw/summit/cuda/10.1.168/bin/../nvvm/bin:/sw/summit/cuda/10.1.168/bin:/sw/sources/lsf-tools/2.0/summit/bin:/sw/summit/xalt/1.1.3/bin:/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/xl-16.1.1-3/spectrum-mpi-10.3.0.1-20190611-aqjt3jo53mogrrhcrd2iufr435azcaha/bin:/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin:/sw/summit/xl/16.1.1-3/xlf/16.1.1/bin:/sw/summit/cuda/10.1.168/bin:/usr/bin:/usr/sbin:/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-csygoqyym3m3ysoaperhxlhoiluvpa2u/bin:/sw/sources/hpss/bin:/opt/ibm/spectrumcomputing/lsf/10.1/linux3.10-glibc2.17-ppc64le-csm/etc:/opt/ibm/spectrumcomputing/lsf/10.1/linux3.10-glibc2.17-ppc64le-csm/bin:/opt/ibm/csm/bin:/usr/local/bin:/usr/local/sbin:/opt/ibm/flightlog/bin:/opt/ibutils/bin:/opt/ibm/spectrum_mpi/jsm_pmix/bin:/opt/puppetlabs/bin:/usr/lpp/mmfs/bin
+#$ INCLUDES="-I/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/include"
+#$ LIBRARIES=  "-L/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/lib/stubs" "-L/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/lib"
+#$ CUDAFE_FLAGS=
+#$ PTXAS_FLAGS=
+#$ "/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin"/xlc++ -D__CUDA_ARCH__=300 -E -x c++  -DCUDA_DOUBLE_MATH_FUNCTIONS -D__CUDACC__ -D__NVCC__  "-I/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/include"    -D__CUDACC_VER_MAJOR__=10 -D__CUDACC_VER_MINOR__=1 -D__CUDACC_VER_BUILD__=168 -include "cuda_runtime.h" "/ccs/home/rmaynard/cmake/src/Modules/CMakeCUDACompilerABI.cu" > "/tmp/tmpxft_0000de61_00000000-6_CMakeCUDACompilerABI.cpp1.ii"
+#$ cicc --clang --clang_version=40000 --allow_managed --unsigned_chars   -arch compute_30 -m64 -ftz=0 -prec_div=1 -prec_sqrt=1 -fmad=1 --include_file_name "tmpxft_0000de61_00000000-2_CMakeCUDACompilerABI.fatbin.c" -tused -nvvmir-library "/sw/summit/cuda/10.1.168/bin/../nvvm/libdevice/libdevice.10.bc" --gen_module_id_file --module_id_file_name "/tmp/tmpxft_0000de61_00000000-3_CMakeCUDACompilerABI.module_id" --orig_src_file_name "/ccs/home/rmaynard/cmake/src/Modules/CMakeCUDACompilerABI.cu" --gen_c_file_name "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.c" --stub_file_name "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.stub.c" --gen_device_file_name "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.gpu"  "/tmp/tmpxft_0000de61_00000000-6_CMakeCUDACompilerABI.cpp1.ii" -o "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.ptx"
+#$ ptxas -arch=sm_30 -m64  "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.ptx"  -o "/tmp/tmpxft_0000de61_00000000-7_CMakeCUDACompilerABI.sm_30.cubin"
+#$ fatbinary --create="/tmp/tmpxft_0000de61_00000000-2_CMakeCUDACompilerABI.fatbin" -64 -no-asm "--image=profile=sm_30,file=/tmp/tmpxft_0000de61_00000000-7_CMakeCUDACompilerABI.sm_30.cubin" "--image=profile=compute_30,file=/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.ptx" --embedded-fatbin="/tmp/tmpxft_0000de61_00000000-2_CMakeCUDACompilerABI.fatbin.c"
+#$ rm /tmp/tmpxft_0000de61_00000000-2_CMakeCUDACompilerABI.fatbin
+#$ "/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin"/xlc++ -E -x c++ -D__CUDACC__ -D__NVCC__  "-I/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/include"    -D__CUDACC_VER_MAJOR__=10 -D__CUDACC_VER_MINOR__=1 -D__CUDACC_VER_BUILD__=168 -include "cuda_runtime.h" "/ccs/home/rmaynard/cmake/src/Modules/CMakeCUDACompilerABI.cu" > "/tmp/tmpxft_0000de61_00000000-4_CMakeCUDACompilerABI.cpp4.ii"
+#$ cudafe++ --clang --clang_version=40000 --allow_managed --unsigned_chars  --m64 --parse_templates --gen_c_file_name "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp" --stub_file_name "tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.stub.c" --module_id_file_name "/tmp/tmpxft_0000de61_00000000-3_CMakeCUDACompilerABI.module_id" "/tmp/tmpxft_0000de61_00000000-4_CMakeCUDACompilerABI.cpp4.ii"
+#$ "/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin"/xlc++ -D__CUDA_ARCH__=300 -c -x c++  -DCUDA_DOUBLE_MATH_FUNCTIONS "-I/sw/summit/cuda/10.1.168/bin/../targets/ppc64le-linux/include"   -o "CMakeFiles/cmTC_e9f3e.dir/CMakeCUDACompilerABI.cu.o" "/tmp/tmpxft_0000de61_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp"
+Linking CUDA executable cmTC_e9f3e
+/ccs/home/rmaynard/cmake/build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_e9f3e.dir/link.txt --verbose=1
+/sw/summit/xl/16.1.1-3/xlC/16.1.1/bin/xlc++  -v CMakeFiles/cmTC_e9f3e.dir/CMakeCUDACompilerABI.cu.o -o cmTC_e9f3e  -L"/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib/stubs" -L"/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib" -lcudadevrt -lcudart_static -lrt -lpthread -ldl
+exec: export(export,XL_CONFIG=/sw/summit/xl/16.1.1-3/xlC/16.1.1/etc/xlc.cfg.rhel.7.6.gcc.4.8.5.cuda.10.1:xlc++,NULL)
+exec: /sw/summit/xalt/1.1.3/bin/ld(/sw/summit/xalt/1.1.3/bin/ld,--eh-frame-hdr,-Qy,-melf64lppc,-dynamic-linker,/lib64/ld64.so.2,--enable-new-dtags,-L/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib/stubs,-L/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib,-L/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/xl-16.1.1-3/spectrum-mpi-10.3.0.1-20190611-aqjt3jo53mogrrhcrd2iufr435azcaha/lib,-L/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-csygoqyym3m3ysoaperhxlhoiluvpa2u/lib,/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/../../../../lib64/crt1.o,/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/../../../../lib64/crti.o,/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/crtbegin.o,-L/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlsmp/5.1.1/lib,-L/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlmass/9.1.1/lib,-L/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlC/16.1.1/lib,-R/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/lib,-L/usr/lib/gcc/ppc64le-redhat-linux/4.8.5,-L/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/../../../../lib64,-L/lib/../lib64,-L/usr/lib/../lib64,-L/autofs/nccs-svm1_sw/peak/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-ytwv7xbkub6mqnpvygdthwqa7mhjqbc5/lib,-L/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/../../..,CMakeFiles/cmTC_e9f3e.dir/CMakeCUDACompilerABI.cu.o,-o,cmTC_e9f3e,-lcudadevrt,-lcudart_static,-lrt,-lpthread,-ldl,-lxlopt,-lxl,-libmc++,-lstdc++,-lm,-ldl,-lgcc_s,-lgcc,--as-needed,-lpthread,--no-as-needed,-lm,-lc,-lgcc_s,-lgcc,/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/crtend.o,/usr/lib/gcc/ppc64le-redhat-linux/4.8.5/../../../../lib64/crtn.o,NULL)
+unlink: /tmp/xlcW0by5DKQ
+unlink: /tmp/xlcW1dqSZDp
+unlink: /tmp/xlcW2nyAlxY
+gmake[1]: Leaving directory `/autofs/nccs-svm1_home1/rmaynard/cmake/extract_xlc_info/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-9.2.148-GCC.input b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-9.2.148-GCC.input
new file mode 100644
index 0000000..98aee98
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CUDA-NVIDIA-9.2.148-GCC.input
@@ -0,0 +1,125 @@
+CMAKE_LANG=CUDA
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CUDA_COMPILER_ABI=ELF
+CMAKE_CUDA_COMPILER_AR=
+CMAKE_CUDA_COMPILER_ARCHITECTURE_ID=
+CMAKE_CUDA_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CUDA_COMPILER_ID=NVIDIA
+CMAKE_CUDA_COMPILER_LAUNCHER=
+CMAKE_CUDA_COMPILER_LOADED=1
+CMAKE_CUDA_COMPILER_RANLIB=
+CMAKE_CUDA_COMPILER_TARGET=
+CMAKE_CUDA_COMPILER_VERSION=9.2.148
+CMAKE_CUDA_COMPILER_VERSION_INTERAL=
+Change Dir: /tmp/ii/CMakeFiles/CMakeTmp
+
+Run Build Command:"/usr/bin/make" "cmTC_5996d/fast"
+/usr/bin/make -f CMakeFiles/cmTC_5996d.dir/build.make CMakeFiles/cmTC_5996d.dir/build
+make[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building CUDA object CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o
+/usr/bin/nvcc    -Xcompiler=-v -x cu -c "/tmp/CMake/Modules/CMakeCUDACompilerABI.cu" -o CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o
+Using built-in specs.
+COLLECT_GCC=gcc-5
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.5.0 20171010 (Debian 5.5.0-12)
+COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-E' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
+ /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS -D __CUDACC__ -D __NVCC__ -D __CUDACC_VER_BUILD__=148 -D __CUDACC_VER_MINOR__=2 -D __CUDACC_VER_MAJOR__=9 -include cuda_runtime.h /tmp/CMake/Modules/CMakeCUDACompilerABI.cu -m64 -mtune=generic -march=x86-64 -std=c++14
+ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/include/c++/5
+ /usr/include/x86_64-linux-gnu/c++/5
+ /usr/include/c++/5/backward
+ /usr/lib/gcc/x86_64-linux-gnu/5/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-E' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
+Using built-in specs.
+COLLECT_GCC=gcc-5
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.5.0 20171010 (Debian 5.5.0-12)
+COLLECT_GCC_OPTIONS='-std=c++14' '-E' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
+ /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDACC__ -D __NVCC__ -D __CUDACC_VER_BUILD__=148 -D __CUDACC_VER_MINOR__=2 -D __CUDACC_VER_MAJOR__=9 -include cuda_runtime.h /tmp/CMake/Modules/CMakeCUDACompilerABI.cu -m64 -mtune=generic -march=x86-64 -std=c++14
+ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/include/c++/5
+ /usr/include/x86_64-linux-gnu/c++/5
+ /usr/include/c++/5/backward
+ /usr/lib/gcc/x86_64-linux-gnu/5/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-std=c++14' '-E' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
+Using built-in specs.
+COLLECT_GCC=gcc-5
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.5.0 20171010 (Debian 5.5.0-12)
+COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
+ /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS /tmp/tmpxft_00002df4_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -quiet -dumpbase tmpxft_00002df4_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -m64 -mtune=generic -march=x86-64 -auxbase-strip CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o -std=c++14 -version -o /tmp/ccSDI00n.s
+GNU C++14 (Debian 5.5.0-12) version 5.5.0 20171010 (x86_64-linux-gnu)
+	compiled by GNU C version 5.5.0 20171010, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0
+warning: MPFR header version 4.0.1 differs from library version 4.0.2-rc1.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/include/c++/5
+ /usr/include/x86_64-linux-gnu/c++/5
+ /usr/include/c++/5/backward
+ /usr/lib/gcc/x86_64-linux-gnu/5/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+GNU C++14 (Debian 5.5.0-12) version 5.5.0 20171010 (x86_64-linux-gnu)
+	compiled by GNU C version 5.5.0 20171010, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0
+warning: MPFR header version 4.0.1 differs from library version 4.0.2-rc1.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: 3caa6244fa07ed8a2b405a77ac6cb692
+COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
+ as -v --64 -o CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o /tmp/ccSDI00n.s
+GNU assembler version 2.31.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.31.1
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
+Linking CUDA device code CMakeFiles/cmTC_5996d.dir/cmake_device_link.o
+"/tmp/CMake/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_5996d.dir/dlink.txt --verbose=1
+/usr/bin/nvcc    -Xcompiler=-fPIC -Wno-deprecated-gpu-targets -shared -dlink CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o -o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o  -L"/usr/lib/x86_64-linux-gnu"
+Linking CUDA executable cmTC_5996d
+"/tmp/CMake/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_5996d.dir/link.txt --verbose=1
+/usr/lib/nvidia-cuda-toolkit/bin/g++  -v CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o -o cmTC_5996d  -L"/usr/lib/x86_64-linux-gnu/stubs" -lcudadevrt -lcudart_static -lrt -lpthread -ldl
+Using built-in specs.
+COLLECT_GCC=g++-5
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+gcc version 5.5.0 20171010 (Debian 5.5.0-12)
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_5996d' '-L/usr/lib/x86_64-linux-gnu/stubs' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
+ /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/cc85GscA.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_5996d /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/x86_64-linux-gnu/stubs -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o -lcudadevrt -lcudart_static -lrt -lpthread -ldl -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
+make[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-CXX-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-Intel-18.0.0.20170811.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-Intel-18.0.0.20170811.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-Intel-18.0.0.20170811.input
rename to Tests/RunCMake/ParseImplicitData/linux-CXX-Intel-18.0.0.20170811.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux-CXX-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-CXX-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-16.1.0.0.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-XL-16.1.0.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-16.1.0.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-CXX-XL-16.1.0.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-GNU-7.3.0.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-7.3.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-GNU-7.3.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-7.3.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux-Fortran-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-XL-14.1.0.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-XL-14.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-XL-14.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux-Fortran-XL-14.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc-C-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc-C-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc-C-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc-C-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc-CXX-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc-CXX-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc-CXX-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc-CXX-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-Fortran-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc-Fortran-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-Fortran-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc-Fortran-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-C-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc_i-C-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-C-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc_i-C-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-CXX-XL-12.1.0.input b/Tests/RunCMake/ParseImplicitData/linux_nostdinc_i-CXX-XL-12.1.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-CXX-XL-12.1.0.input
rename to Tests/RunCMake/ParseImplicitData/linux_nostdinc_i-CXX-XL-12.1.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_pgf77-Fortran-PGI-18.10.1.input b/Tests/RunCMake/ParseImplicitData/linux_pgf77-Fortran-PGI-18.10.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_pgf77-Fortran-PGI-18.10.1.input
rename to Tests/RunCMake/ParseImplicitData/linux_pgf77-Fortran-PGI-18.10.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-C-GNU-4.9.3.input b/Tests/RunCMake/ParseImplicitData/mingw.org-C-GNU-4.9.3.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-C-GNU-4.9.3.input
rename to Tests/RunCMake/ParseImplicitData/mingw.org-C-GNU-4.9.3.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-CXX-GNU-4.9.3.input b/Tests/RunCMake/ParseImplicitData/mingw.org-CXX-GNU-4.9.3.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-CXX-GNU-4.9.3.input
rename to Tests/RunCMake/ParseImplicitData/mingw.org-CXX-GNU-4.9.3.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-C-GNU-4.8.5.input b/Tests/RunCMake/ParseImplicitData/netbsd-C-GNU-4.8.5.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-C-GNU-4.8.5.input
rename to Tests/RunCMake/ParseImplicitData/netbsd-C-GNU-4.8.5.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-CXX-GNU-4.8.5.input b/Tests/RunCMake/ParseImplicitData/netbsd-CXX-GNU-4.8.5.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-CXX-GNU-4.8.5.input
rename to Tests/RunCMake/ParseImplicitData/netbsd-CXX-GNU-4.8.5.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-C-GNU-4.8.5.input b/Tests/RunCMake/ParseImplicitData/netbsd_nostdinc-C-GNU-4.8.5.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-C-GNU-4.8.5.input
rename to Tests/RunCMake/ParseImplicitData/netbsd_nostdinc-C-GNU-4.8.5.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-CXX-GNU-4.8.5.input b/Tests/RunCMake/ParseImplicitData/netbsd_nostdinc-CXX-GNU-4.8.5.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-CXX-GNU-4.8.5.input
rename to Tests/RunCMake/ParseImplicitData/netbsd_nostdinc-CXX-GNU-4.8.5.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-C-Clang-5.0.1.input b/Tests/RunCMake/ParseImplicitData/openbsd-C-Clang-5.0.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-C-Clang-5.0.1.input
rename to Tests/RunCMake/ParseImplicitData/openbsd-C-Clang-5.0.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-CXX-Clang-5.0.1.input b/Tests/RunCMake/ParseImplicitData/openbsd-CXX-Clang-5.0.1.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-CXX-Clang-5.0.1.input
rename to Tests/RunCMake/ParseImplicitData/openbsd-CXX-Clang-5.0.1.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-C-SunPro-5.13.0.input b/Tests/RunCMake/ParseImplicitData/sunos-C-SunPro-5.13.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-C-SunPro-5.13.0.input
rename to Tests/RunCMake/ParseImplicitData/sunos-C-SunPro-5.13.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-CXX-SunPro-5.13.0.input b/Tests/RunCMake/ParseImplicitData/sunos-CXX-SunPro-5.13.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-CXX-SunPro-5.13.0.input
rename to Tests/RunCMake/ParseImplicitData/sunos-CXX-SunPro-5.13.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-Fortran-SunPro-8.8.0.input b/Tests/RunCMake/ParseImplicitData/sunos-Fortran-SunPro-8.8.0.input
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-Fortran-SunPro-8.8.0.input
rename to Tests/RunCMake/ParseImplicitData/sunos-Fortran-SunPro-8.8.0.input
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
index 5880378..abd0eaa 100644
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
@@ -32,7 +32,8 @@
     linux_nostdinc-C-XL-12.1.0 linux_nostdinc-CXX-XL-12.1.0
     linux_nostdinc_i-C-XL-12.1.0 linux_nostdinc-CXX-XL-12.1.0
   linux-C-XL-16.1.0.0 linux-CXX-XL-16.1.0.0
-  linux-CUDA-NVIDIA-9.2.148
+  linux-CUDA-NVIDIA-10.1.168-CLANG linux-CUDA-NVIDIA-10.1.168-XLClang-v-empty
+    linux-CUDA-NVIDIA-9.2.148-GCC
   mingw.org-C-GNU-4.9.3 mingw.org-CXX-GNU-4.9.3
   netbsd-C-GNU-4.8.5 netbsd-CXX-GNU-4.8.5
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
@@ -97,22 +98,30 @@
 # main test loop
 #
 foreach(t ${targets})
-  set(infile "${CMAKE_SOURCE_DIR}/data/${t}.input")
-  set(outfile "${CMAKE_SOURCE_DIR}/data/${t}.output")
-  if (NOT EXISTS ${infile} OR NOT EXISTS ${outfile})
-    message("missing files for target ${t} in ${CMAKE_SOURCE_DIR}/data")
+  set(infile "${CMAKE_SOURCE_DIR}/../ParseImplicitData/${t}.input")
+  set(outfile "${CMAKE_SOURCE_DIR}/results/${t}.output")
+  if (NOT EXISTS ${infile})
+    string(REPLACE  "-empty" "" infile "${infile}")
+    if (NOT EXISTS ${infile})
+      message("missing input file for target ${t} in ${CMAKE_SOURCE_DIR}/../ParseImplicitData/")
+      continue()
+    endif()
+  elseif(NOT EXISTS ${outfile})
+    message("missing files for target ${t} in ${CMAKE_SOURCE_DIR}/results/")
     continue()
   endif()
+
   load_compiler_info(${infile} lang cmvars input)
   file(READ ${outfile} output)
   string(STRIP "${output}" output)
   cmake_parse_implicit_include_info("${input}" "${lang}" idirs log state)
+
   if(t MATCHES "-empty$")          # empty isn't supposed to parse
     if("${state}" STREQUAL "done")
       message("empty parse failed: ${idirs}, log=${log}")
     endif()
   elseif(NOT "${state}" STREQUAL "done" OR NOT "${idirs}" MATCHES "^${output}$")
-    message("parse failed: state=${state}, '${idirs}' does not match '^${output}$', log=${log}")
+    message("${t} parse failed: state=${state}, '${idirs}' does not match '^${output}$', log=${log}")
   endif()
   unload_compiler_info("${cmvars}")
 endforeach(t)
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/CMakeLists.txt b/Tests/RunCMake/ParseImplicitIncludeInfo/data/CMakeLists.txt
deleted file mode 100644
index bffe819..0000000
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/data/CMakeLists.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# helper CMakeLists.txt file that can be used to generate input files
-# for the Tests/RunCMake/ParseImplicitIncludeInfo test.
-#
-# usage:
-#  [create a temporary build directory and chdir to it]
-#  cmake [-D options] $CMAKE_SRC/Tests/RunCMake/ParseImplicitIncludeInfo/data
-#
-# where useful -D options include:
-#   -DLANGUAGES="C;CXX"    -- list of languages to generate inputs for
-#   -DUNAME="Darwin"       -- operating system name (def: CMAKE_SYSTEM_NAME)
-#
-
-cmake_minimum_required(VERSION 3.3)
-if(POLICY CMP0089)
-  cmake_policy(SET CMP0089 NEW)
-endif()
-
-set(lngs C CXX)
-set(LANGUAGES "${lngs}" CACHE STRING "List of languages to generate inputs for")
-
-project(gen_implicit_include_data ${LANGUAGES})
-
-set(UNAME "${CMAKE_SYSTEM_NAME}" CACHE STRING "System uname")
-string(TOLOWER "${UNAME}" UNAME)
-message("Generate input for system type: ${UNAME}")
-
-# CMAKE_<LANG>_COMPILER_* variables we save in the resultfile
-set(compvars ABI AR ARCHITECTURE_ID EXTERNAL_TOOLCHAIN ID LAUNCHER LOADED
-  RANLIB TARGET VERSION VERSION_INTERAL)
-
-foreach(lang IN ITEMS ${LANGUAGES})
-
-  if("${lang}" STREQUAL "C")
-    set(file ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c)
-  elseif("${lang}" STREQUAL "CXX")
-    set(file ${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp)
-  elseif("${lang}" STREQUAL "CUDA")
-    set(file ${CMAKE_ROOT}/Modules/CMakeCUDACompilerABI.cu)
-  elseif("${lang}" STREQUAL "Fortran")
-    set(file ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F)
-  else()
-    message(FATAL_ERROR "unknown language ${lang}")
-  endif()
-
-  set(resultfile "${CMAKE_BINARY_DIR}/")
-  string(APPEND resultfile ${UNAME}-${lang}-${CMAKE_${lang}_COMPILER_ID})
-  string(APPEND resultfile -${CMAKE_${lang}_COMPILER_VERSION})
-  string(APPEND resultfile .input)
-  message("Generate input for language ${lang}")
-  message("Input file: ${file}")
-  message("Result file: ${resultfile}")
-
-  # replicate logic from CMakeDetermineCompilerABI
-  set(outfile "${CMAKE_PLATFORM_INFO_DIR}/test${lang}.out")
-  set(CMAKE_FLAGS )
-  set(COMPILE_DEFINITIONS )
-  if(DEFINED CMAKE_${lang}_VERBOSE_FLAG)
-    set(CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${CMAKE_${lang}_VERBOSE_FLAG}")
-    set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
-  endif()
-  if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG)
-    set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}")
-  endif()
-  if(NOT "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
-    # Avoid adding our own platform standard libraries for compilers
-    # from which we might detect implicit link libraries.
-    list(APPEND CMAKE_FLAGS "-DCMAKE_${lang}_STANDARD_LIBRARIES=")
-  endif()
-
-  try_compile(rv ${CMAKE_BINARY_DIR} ${file}
-    CMAKE_FLAGS ${CMAKE_FLAGS}
-    COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS}
-    CMAKE_FLAGS ${CMAKE_FLAGS}
-    OUTPUT_VARIABLE output
-    COPY_FILE "${outfile}"
-    COPY_FILE_ERROR copy_error)
-
-  if(NOT rv)
-    message(FATAL_ERROR "${lang} compile failed!!")
-  endif()
-
-  set(result "CMAKE_LANG=${lang}\n")
-  foreach(var IN ITEMS ${compvars})
-    list(APPEND result
-      "CMAKE_${lang}_COMPILER_${var}=${CMAKE_${lang}_COMPILER_${var}}\n")
-  endforeach()
-
-  file(WRITE ${resultfile} ${result} ${output})
-endforeach()
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/README b/Tests/RunCMake/ParseImplicitIncludeInfo/data/README
deleted file mode 100644
index 4f19b3c..0000000
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/data/README
+++ /dev/null
@@ -1,25 +0,0 @@
-This directory contains sample input files for the implicit include
-directories parser for testing.  For each configuration there is one
-".input" file and one ".output" file.
-
-To generate ".input" files for a system, create a temporary build
-directory and chdir to it.  Then run cmake pointing to this directory.
-The CMakeLists.txt file here will generate ".input" files in your
-build directory.  The default set of languages is C and CXX.  This
-can be changed with -DLANGUAGES=language_list.  For example:
--DLANGUAGES=Fortran will generate Fortran parser input.
-
-The ".output" files should be generated by hand from the input files.
-The test will compare the parser output to the manually generated
-".output" file.  The two should match.
-
-For compilers that support "-nostdinc"-like flags, you can generate
-a test for this with a command like:
-cmake -DUNAME=netbsd_nostdinc \
-  -DCMAKE_C_FLAGS=-nostdinc -DCMAKE_CXX_FLAGS=-nostdinc .
-
-Here is an example for testing the XL compiler with both -I and nostdinc:
-
-env CC=xlc CXX=xlC cmake -DUNAME=linux_nostdinc_i \
-  -DCMAKE_C_FLAGS='-qnostdinc -I/tmp/ii/test_c' \
-  -DCMAKE_CXX_FLAGS='-qnostdinc -I/tmp/ii/test_c -I/tmp/ii/test_cxx' .
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-relative.input b/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-relative.input
deleted file mode 100644
index dd846e3..0000000
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-relative.input
+++ /dev/null
@@ -1,21 +0,0 @@
-CMAKE_LANG=C
-CMAKE_C_COMPILER_ABI=ELF
-CMAKE_C_COMPILER_AR=/usr/bin/gcc-ar-7
-CMAKE_C_COMPILER_ARCHITECTURE_ID=
-CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
-CMAKE_C_COMPILER_ID=GNU
-CMAKE_C_COMPILER_LAUNCHER=
-CMAKE_C_COMPILER_LOADED=1
-CMAKE_C_COMPILER_RANLIB=/usr/bin/gcc-ranlib-7
-CMAKE_C_COMPILER_TARGET=
-CMAKE_C_COMPILER_VERSION=7.3.0
-CMAKE_C_COMPILER_VERSION_INTERAL=
-
-This is a hand-written test case.
-
-#include "..." search starts here:
-#include <...> search starts here:
- /usr/local/include
- ../../../adaptive/relative/include
- /usr/include
-End of search list.
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-relative.input b/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-relative.input
deleted file mode 100644
index 54cc4db..0000000
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-relative.input
+++ /dev/null
@@ -1,21 +0,0 @@
-CMAKE_LANG=CXX
-CMAKE_CXX_COMPILER_ABI=ELF
-CMAKE_CXX_COMPILER_AR=/usr/bin/gcc-ar-7
-CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
-CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
-CMAKE_CXX_COMPILER_ID=GNU
-CMAKE_CXX_COMPILER_LAUNCHER=
-CMAKE_CXX_COMPILER_LOADED=1
-CMAKE_CXX_COMPILER_RANLIB=/usr/bin/gcc-ranlib-7
-CMAKE_CXX_COMPILER_TARGET=
-CMAKE_CXX_COMPILER_VERSION=7.3.0
-CMAKE_CXX_COMPILER_VERSION_INTERAL=
-
-This is a hand-written test case.
-
-#include "..." search starts here:
-#include <...> search starts here:
- /usr/local/include
- ../../../adaptive/relative/include
- /usr/include
-End of search list.
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CUDA-NVIDIA-9.2.148.input b/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CUDA-NVIDIA-9.2.148.input
deleted file mode 100644
index 5dd3650..0000000
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CUDA-NVIDIA-9.2.148.input
+++ /dev/null
@@ -1,124 +0,0 @@
-CMAKE_LANG=CUDA
-CMAKE_CUDA_COMPILER_ABI=ELF
-CMAKE_CUDA_COMPILER_AR=
-CMAKE_CUDA_COMPILER_ARCHITECTURE_ID=
-CMAKE_CUDA_COMPILER_EXTERNAL_TOOLCHAIN=
-CMAKE_CUDA_COMPILER_ID=NVIDIA
-CMAKE_CUDA_COMPILER_LAUNCHER=
-CMAKE_CUDA_COMPILER_LOADED=1
-CMAKE_CUDA_COMPILER_RANLIB=
-CMAKE_CUDA_COMPILER_TARGET=
-CMAKE_CUDA_COMPILER_VERSION=9.2.148
-CMAKE_CUDA_COMPILER_VERSION_INTERAL=
-Change Dir: /tmp/ii/CMakeFiles/CMakeTmp
-
-Run Build Command:"/usr/bin/make" "cmTC_5996d/fast"
-/usr/bin/make -f CMakeFiles/cmTC_5996d.dir/build.make CMakeFiles/cmTC_5996d.dir/build
-make[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
-Building CUDA object CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o
-/usr/bin/nvcc    -Xcompiler=-v -x cu -c "/tmp/CMake/Modules/CMakeCUDACompilerABI.cu" -o CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o
-Using built-in specs.
-COLLECT_GCC=gcc-5
-Target: x86_64-linux-gnu
-Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
-Thread model: posix
-gcc version 5.5.0 20171010 (Debian 5.5.0-12)
-COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-E' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
- /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS -D __CUDACC__ -D __NVCC__ -D __CUDACC_VER_BUILD__=148 -D __CUDACC_VER_MINOR__=2 -D __CUDACC_VER_MAJOR__=9 -include cuda_runtime.h /tmp/CMake/Modules/CMakeCUDACompilerABI.cu -m64 -mtune=generic -march=x86-64 -std=c++14
-ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
-ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
-ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
-#include "..." search starts here:
-#include <...> search starts here:
- /usr/include/c++/5
- /usr/include/x86_64-linux-gnu/c++/5
- /usr/include/c++/5/backward
- /usr/lib/gcc/x86_64-linux-gnu/5/include
- /usr/local/include
- /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
- /usr/include/x86_64-linux-gnu
- /usr/include
-End of search list.
-COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
-LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
-COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-E' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
-Using built-in specs.
-COLLECT_GCC=gcc-5
-Target: x86_64-linux-gnu
-Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
-Thread model: posix
-gcc version 5.5.0 20171010 (Debian 5.5.0-12)
-COLLECT_GCC_OPTIONS='-std=c++14' '-E' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
- /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDACC__ -D __NVCC__ -D __CUDACC_VER_BUILD__=148 -D __CUDACC_VER_MINOR__=2 -D __CUDACC_VER_MAJOR__=9 -include cuda_runtime.h /tmp/CMake/Modules/CMakeCUDACompilerABI.cu -m64 -mtune=generic -march=x86-64 -std=c++14
-ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
-ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
-ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
-#include "..." search starts here:
-#include <...> search starts here:
- /usr/include/c++/5
- /usr/include/x86_64-linux-gnu/c++/5
- /usr/include/c++/5/backward
- /usr/lib/gcc/x86_64-linux-gnu/5/include
- /usr/local/include
- /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
- /usr/include/x86_64-linux-gnu
- /usr/include
-End of search list.
-COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
-LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
-COLLECT_GCC_OPTIONS='-std=c++14' '-E' '-D' '__CUDACC__' '-D' '__NVCC__' '-v' '-D' '__CUDACC_VER_BUILD__=148' '-D' '__CUDACC_VER_MINOR__=2' '-D' '__CUDACC_VER_MAJOR__=9' '-include' 'cuda_runtime.h' '-m64' '-mtune=generic' '-march=x86-64'
-Using built-in specs.
-COLLECT_GCC=gcc-5
-Target: x86_64-linux-gnu
-Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
-Thread model: posix
-gcc version 5.5.0 20171010 (Debian 5.5.0-12)
-COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
- /usr/lib/gcc/x86_64-linux-gnu/5/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D __CUDA_ARCH__=300 -D CUDA_DOUBLE_MATH_FUNCTIONS /tmp/tmpxft_00002df4_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -quiet -dumpbase tmpxft_00002df4_00000000-5_CMakeCUDACompilerABI.cudafe1.cpp -m64 -mtune=generic -march=x86-64 -auxbase-strip CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o -std=c++14 -version -o /tmp/ccSDI00n.s
-GNU C++14 (Debian 5.5.0-12) version 5.5.0 20171010 (x86_64-linux-gnu)
-	compiled by GNU C version 5.5.0 20171010, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0
-warning: MPFR header version 4.0.1 differs from library version 4.0.2-rc1.
-GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
-ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/5"
-ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
-ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
-#include "..." search starts here:
-#include <...> search starts here:
- /usr/include/c++/5
- /usr/include/x86_64-linux-gnu/c++/5
- /usr/include/c++/5/backward
- /usr/lib/gcc/x86_64-linux-gnu/5/include
- /usr/local/include
- /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
- /usr/include/x86_64-linux-gnu
- /usr/include
-End of search list.
-GNU C++14 (Debian 5.5.0-12) version 5.5.0 20171010 (x86_64-linux-gnu)
-	compiled by GNU C version 5.5.0 20171010, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0
-warning: MPFR header version 4.0.1 differs from library version 4.0.2-rc1.
-GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
-Compiler executable checksum: 3caa6244fa07ed8a2b405a77ac6cb692
-COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
- as -v --64 -o CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o /tmp/ccSDI00n.s
-GNU assembler version 2.31.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.31.1
-COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
-LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
-COLLECT_GCC_OPTIONS='-std=c++14' '-D' '__CUDA_ARCH__=300' '-c' '-D' 'CUDA_DOUBLE_MATH_FUNCTIONS' '-v' '-m64' '-o' 'CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o' '-mtune=generic' '-march=x86-64'
-Linking CUDA device code CMakeFiles/cmTC_5996d.dir/cmake_device_link.o
-"/tmp/CMake/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_5996d.dir/dlink.txt --verbose=1
-/usr/bin/nvcc    -Xcompiler=-fPIC -Wno-deprecated-gpu-targets -shared -dlink CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o -o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o  -L"/usr/lib/x86_64-linux-gnu"
-Linking CUDA executable cmTC_5996d
-"/tmp/CMake/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_5996d.dir/link.txt --verbose=1
-/usr/lib/nvidia-cuda-toolkit/bin/g++  -v CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o -o cmTC_5996d  -L"/usr/lib/x86_64-linux-gnu/stubs" -lcudadevrt -lcudart_static -lrt -lpthread -ldl
-Using built-in specs.
-COLLECT_GCC=g++-5
-COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
-Target: x86_64-linux-gnu
-Configured with: ../src/configure -v --with-pkgversion='Debian 5.5.0-12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
-Thread model: posix
-gcc version 5.5.0 20171010 (Debian 5.5.0-12)
-COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
-LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
-COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_5996d' '-L/usr/lib/x86_64-linux-gnu/stubs' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
- /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/cc85GscA.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_5996d /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/x86_64-linux-gnu/stubs -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_5996d.dir/CMakeCUDACompilerABI.cu.o CMakeFiles/cmTC_5996d.dir/cmake_device_link.o -lcudadevrt -lcudart_static -lrt -lpthread -ldl -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
-make[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-XL-13.1.3.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XL-13.1.3.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-XL-13.1.3.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-XLClang-16.1.0.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-C-XLClang-16.1.0.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-XLClang-16.1.0.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-XL-13.1.3.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XL-13.1.3.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-XL-13.1.3.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-XLClang-16.1.0.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/aix-CXX-XLClang-16.1.0.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-XLClang-16.1.0.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-8.7.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Cray-8.7.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-8.7.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Cray-8.7.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Cray-9.0-hlist-ad.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Cray-9.0-hlist-ad.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Cray-9.0-hlist-ad.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Intel-18.0.2.20180210.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-C-Intel-18.0.2.20180210.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-C-Intel-18.0.2.20180210.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-8.7.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Cray-8.7.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-8.7.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Cray-8.7.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Cray-9.0-hlist-ad.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Intel-18.0.2.20180210.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-CXX-Intel-18.0.2.20180210.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-CXX-Intel-18.0.2.20180210.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-8.7.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Cray-8.7.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-8.7.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Cray-8.7.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Cray-9.0-hlist-ad.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/craype-Fortran-Intel-18.0.2.20180210.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-C-AppleClang-8.0.0.8000042.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin-CXX-AppleClang-8.0.0.8000042.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-C-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-C-Clang-3.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-C-Clang-3.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-C-Clang-3.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-CXX-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-CXX-Clang-3.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-CXX-Clang-3.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-CXX-Clang-3.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-Fortran-GNU-4.6.4.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-Fortran-GNU-4.6.4.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/freebsd-Fortran-GNU-4.6.4.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/freebsd-Fortran-GNU-4.6.4.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-C-empty.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-C-empty.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-relative.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-C-relative.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-relative.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-C-relative.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-empty.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-CXX-empty.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-empty.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-CXX-empty.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-relative.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-CXX-relative.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-CXX-relative.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/hand-CXX-relative.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-Intel-18.0.0.20170811.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-Intel-18.0.0.20170811.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-Intel-18.0.0.20170811.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-XL-16.1.0.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-C-XL-16.1.0.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-XL-16.1.0.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
new file mode 100644
index 0000000..84149e6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
@@ -0,0 +1 @@
+/usr/local/cuda/targets/x86_64-linux/include;/usr/include/c\+\+/8;/usr/include/x86_64-linux-gnu/c\+\+/8;/usr/include/c\+\+/8/backward;/usr/include/clang/8.0.1/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v-empty.output
similarity index 100%
copy from Tests/RunCMake/ParseImplicitIncludeInfo/data/hand-C-empty.output
copy to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v-empty.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CUDA-NVIDIA-9.2.148.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CUDA-NVIDIA-9.2.148.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-Intel-18.0.0.20170811.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-Intel-18.0.0.20170811.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-Intel-18.0.0.20170811.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-XL-16.1.0.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-CXX-XL-16.1.0.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-XL-16.1.0.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-7.3.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-GNU-7.3.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-7.3.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-XL-14.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-XL-14.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux-Fortran-XL-14.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-XL-14.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-C-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-C-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-C-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-C-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-C-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-CXX-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc-Fortran-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-C-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc_i-CXX-XL-12.1.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_nostdinc_i-CXX-XL-12.1.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_nostdinc_i-CXX-XL-12.1.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_pgf77-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/linux_pgf77-Fortran-PGI-18.10.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-C-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/mingw.org-C-GNU-4.9.3.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-C-GNU-4.9.3.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/mingw.org-C-GNU-4.9.3.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-CXX-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/mingw.org-CXX-GNU-4.9.3.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/mingw.org-CXX-GNU-4.9.3.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/mingw.org-CXX-GNU-4.9.3.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd-C-GNU-4.8.5.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-C-GNU-4.8.5.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd-C-GNU-4.8.5.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd-CXX-GNU-4.8.5.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd-CXX-GNU-4.8.5.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd-CXX-GNU-4.8.5.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-C-GNU-4.8.5.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/netbsd_nostdinc-CXX-GNU-4.8.5.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-C-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/openbsd-C-Clang-5.0.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-C-Clang-5.0.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/openbsd-C-Clang-5.0.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-CXX-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/openbsd-CXX-Clang-5.0.1.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/openbsd-CXX-Clang-5.0.1.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/openbsd-CXX-Clang-5.0.1.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-C-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-C-SunPro-5.13.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-C-SunPro-5.13.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-C-SunPro-5.13.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-CXX-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-CXX-SunPro-5.13.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-CXX-SunPro-5.13.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-CXX-SunPro-5.13.0.output
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-Fortran-SunPro-8.8.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-Fortran-SunPro-8.8.0.output
similarity index 100%
rename from Tests/RunCMake/ParseImplicitIncludeInfo/data/sunos-Fortran-SunPro-8.8.0.output
rename to Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos-Fortran-SunPro-8.8.0.output
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt
new file mode 100644
index 0000000..2897109
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
new file mode 100644
index 0000000..bb6beb2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -0,0 +1,146 @@
+cmake_minimum_required(VERSION 3.14)
+project(Minimal NONE)
+
+#
+# list of targets to test.  to add a target: put its files in the data
+# subdirectory and add it to this list...  we run each target's
+# data/*.input file through the parser and check to see if it matches
+# the corresponding data/*.output file.  note that the empty-* case
+# has special handling (it should not parse).
+#
+set(targets
+  aix-C-XL-13.1.3 aix-CXX-XL-13.1.3
+  aix-C-XLClang-16.1.0.1 aix-CXX-XLClang-16.1.0.1
+  craype-C-Cray-8.7 craype-CXX-Cray-8.7 craype-Fortran-Cray-8.7
+  craype-C-Cray-9.0-hlist-ad craype-CXX-Cray-9.0-hlist-ad craype-Fortran-Cray-9.0-hlist-ad
+  craype-C-GNU-7.3.0 craype-CXX-GNU-7.3.0 craype-Fortran-GNU-7.3.0
+  craype-C-Intel-18.0.2.20180210 craype-CXX-Intel-18.0.2.20180210
+    craype-Fortran-Intel-18.0.2.20180210
+  darwin-C-AppleClang-8.0.0.8000042 darwin-CXX-AppleClang-8.0.0.8000042
+    darwin_nostdinc-C-AppleClang-8.0.0.8000042
+    darwin_nostdinc-CXX-AppleClang-8.0.0.8000042
+  freebsd-C-Clang-3.3.0 freebsd-CXX-Clang-3.3.0 freebsd-Fortran-GNU-4.6.4
+  hand-C-empty hand-CXX-empty
+  hand-C-relative hand-CXX-relative
+  linux-C-GNU-7.3.0 linux-CXX-GNU-7.3.0 linux-Fortran-GNU-7.3.0
+  linux-C-Intel-18.0.0.20170811 linux-CXX-Intel-18.0.0.20170811
+  linux-C-PGI-18.10.1 linux-CXX-PGI-18.10.1
+    linux-Fortran-PGI-18.10.1 linux_pgf77-Fortran-PGI-18.10.1
+    linux_nostdinc-C-PGI-18.10.1 linux_nostdinc-CXX-PGI-18.10.1
+    linux_nostdinc-Fortran-PGI-18.10.1
+  linux-C-XL-12.1.0 linux-CXX-XL-12.1.0 linux-Fortran-XL-14.1.0
+    linux_nostdinc-C-XL-12.1.0 linux_nostdinc-CXX-XL-12.1.0
+    linux_nostdinc_i-C-XL-12.1.0 linux_nostdinc-CXX-XL-12.1.0
+  linux-C-XL-16.1.0.0 linux-CXX-XL-16.1.0.0
+  linux-CUDA-NVIDIA-10.1.168-CLANG linux-CUDA-NVIDIA-10.1.168-XLClang-v
+    linux-CUDA-NVIDIA-9.2.148-GCC
+  mingw.org-C-GNU-4.9.3 mingw.org-CXX-GNU-4.9.3
+  netbsd-C-GNU-4.8.5 netbsd-CXX-GNU-4.8.5
+    netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
+  openbsd-C-Clang-5.0.1 openbsd-CXX-Clang-5.0.1
+  sunos-C-SunPro-5.13.0 sunos-CXX-SunPro-5.13.0 sunos-Fortran-SunPro-8.8.0
+  )
+
+if(CMAKE_HOST_WIN32)
+  # The KWSys actual-case cache breaks case sensitivity on Windows.
+  list(FILTER targets EXCLUDE REGEX "-XL|-SunPro")
+else()
+  # Windows drive letters are not recognized as absolute on other platforms.
+  list(FILTER targets EXCLUDE REGEX "mingw")
+endif()
+
+include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
+
+#
+# load_compiler_info: read infile, parsing out cmake compiler info
+# variables as we go.  returns language, a list of variables we set
+# (so we can clear them later), and the remaining verbose output
+# from the compiler.
+#
+function(load_compiler_info infile lang_var outcmvars_var outstr_var)
+  unset(lang)
+  unset(outcmvars)
+  unset(outstr)
+  file(READ "${infile}" in)
+  string(REGEX REPLACE "\r?\n" ";" in_lines "${in}")
+  foreach(line IN LISTS in_lines)
+    # check for special CMAKE variable lines and parse them if found
+    if("${line}" MATCHES "^CMAKE_([_A-Za-z0-9]+)=(.*)$")
+      if("${CMAKE_MATCH_1}" STREQUAL "LANG")   # handle CMAKE_LANG here
+        set(lang "${CMAKE_MATCH_2}")
+      else()
+        set(CMAKE_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" PARENT_SCOPE)
+        list(APPEND outcmvars "CMAKE_${CMAKE_MATCH_1}")
+      endif()
+    else()
+      string(APPEND outstr "${line}\n")
+    endif()
+  endforeach()
+  if(NOT lang)
+    message("load_compiler_info: ${infile} no LANG info; default to C")
+    set(lang C)
+  endif()
+  set(${lang_var} "${lang}" PARENT_SCOPE)
+  set(${outcmvars_var} "${outcmvars}" PARENT_SCOPE)
+  set(${outstr_var} "${outstr}" PARENT_SCOPE)
+endfunction()
+
+#
+# unload_compiler_info: clear out any CMAKE_* vars load previously set
+#
+function(unload_compiler_info cmvars)
+  foreach(var IN LISTS cmvars)
+    unset("${var}" PARENT_SCOPE)
+  endforeach()
+endfunction()
+
+#
+# main test loop
+#
+foreach(t ${targets})
+  set(infile "${CMAKE_SOURCE_DIR}/../ParseImplicitData/${t}.input")
+  set(outfile "${CMAKE_SOURCE_DIR}/results/${t}.output")
+  if (NOT EXISTS ${infile})
+    string(REPLACE  "-empty" "" infile "${infile}")
+    if (NOT EXISTS ${infile})
+      message("missing input file for target ${t} in ${CMAKE_SOURCE_DIR}/../ParseImplicitData/")
+      continue()
+    endif()
+  elseif(NOT EXISTS ${outfile})
+    message("missing files for target ${t} in ${CMAKE_SOURCE_DIR}/results/")
+    continue()
+  endif()
+
+  load_compiler_info(${infile} lang cmvars input)
+
+  # Need to handle files with empty entries for both libs or dirs
+  set(implicit_lib_output "")
+  set(idirs_output "")
+  file(STRINGS ${outfile} outputs)
+  foreach(line IN LISTS outputs)
+    if(line MATCHES "libs=")
+      string(REPLACE "libs=" "" implicit_lib_output "${line}")
+    endif()
+    if(line MATCHES "dirs=")
+      string(REPLACE "dirs=" "" idirs_output "${line}")
+    endif()
+  endforeach()
+
+  cmake_parse_implicit_link_info("${input}" implicit_libs idirs implicit_fwks log
+      "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}")
+
+  # File format
+  # file(WRITE ${outfile} "libs=${implicit_libs}\ndirs=${idirs}\n")
+
+  if(t MATCHES "-empty$")          # empty isn't supposed to parse
+    if("${state}" STREQUAL "done")
+      message("empty parse failed: ${idirs}, log=${log}")
+    endif()
+  elseif(NOT "${idirs}" STREQUAL "${idirs_output}")
+    message("${t} parse failed: state=${state}, '${idirs}' does not match '${idirs_output}'")
+  elseif(NOT "${implicit_libs}" STREQUAL "${implicit_lib_output}")
+    message("${t} parse failed: state=${state}, '${implicit_libs}' does not match '${implicit_lib_output}'")
+  endif()
+
+  unload_compiler_info("${cmvars}")
+endforeach(t)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
new file mode 100644
index 0000000..713e2e7
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
@@ -0,0 +1,3 @@
+include(RunCMake)
+
+run_cmake(ParseImplicitLinkInfo)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output
new file mode 100644
index 0000000..4e030b3
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output
@@ -0,0 +1,2 @@
+libs=xlopt;xlipa;xl;c
+dirs=/opt/IBM/xlmass/8.1.3/lib/aix61;/opt/IBM/xlc/13.1.3/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output
new file mode 100644
index 0000000..6f677a0
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output
@@ -0,0 +1,2 @@
+libs=xlopt;xlipa;xl;c;pthreads
+dirs=/opt/IBM/xlmass/9.1.0/lib/aix61;/opt/IBM/xlc/16.1.0/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output
new file mode 100644
index 0000000..6cbc792
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output
@@ -0,0 +1,2 @@
+libs=xlopt;xlipa;xl;C;m;c
+dirs=/opt/IBM/xlmass/8.1.3/lib/aix61;/opt/IBM/xlc/13.1.3/lib;/opt/IBM/xlC/13.1.3/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output
new file mode 100644
index 0000000..2dc5832
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output
@@ -0,0 +1,2 @@
+libs=xlopt;xlipa;xl;c++;Ccore;pthreads;m;c
+dirs=/opt/IBM/xlmass/9.1.0/lib/aix61;/opt/IBM/xlc/16.1.0/lib;/opt/IBM/xlC/16.1.0/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output
new file mode 100644
index 0000000..674975c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;pthread;sci_cray_mpi_mp;m;f;sci_cray_mp;craymp;m;pthread;f;hugetlbfs;mpich_cray;rt;pthread;ugni;pmi;pgas-dmapp;fi;u;rt;dmapp;ugni;udreg;pthread;m;cray-c++-rts;stdc++;xpmem;dmapp;pthread;pmi;pthread;alpslli;pthread;wlm_detect;ugni;pthread;alpsutil;pthread;rca;udreg;quadmath;m;omp;rt;craymp;pthread;rt;dl;cray-c++-rts;stdc++;m;modules;m;rt;fi;m;quadmath;rt;craymath;m;gfortran;quadmath;rt;f;m;pthread;rt;u;rt;dl;cray-c++-rts;stdc++;m;csup;rt;atomic;stdc++;pthread;c;csup;m;gcc
+dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/dmapp/7.1.1-6.0.5.0_49.8__g1125556.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output
new file mode 100644
index 0000000..1bf2f6d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;atomic;tcmalloc_minimal;cray-c++-rts;stdc++;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
+dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output
new file mode 100644
index 0000000..267bf58
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;pthread;sci_gnu_71_mpi;sci_gnu_71;pthread;hugetlbfs;mpich_gnu_71;rt;ugni;pthread;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;xpmem;ugni;pthread;udreg;gfortran;quadmath;mvec;m;pthread;gcc;c
+dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output
new file mode 100644
index 0000000..e9e6739
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output
@@ -0,0 +1,2 @@
+libs=imf;svml;irng;m;ipgo;decimal;cilkrts;stdc++;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output
new file mode 100644
index 0000000..7daa29d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;pthread;sci_cray_mpi_mp;m;f;sci_cray_mp;craymp;m;pthread;f;hugetlbfs;mpichcxx_cray;rt;pthread;ugni;pmi;mpich_cray;rt;pthread;ugni;pmi;pgas-dmapp;fi;u;rt;dmapp;ugni;udreg;pthread;m;cray-c++-rts;stdc++;xpmem;dmapp;pthread;pmi;pthread;alpslli;pthread;wlm_detect;ugni;pthread;alpsutil;pthread;rca;udreg;quadmath;m;omp;rt;craymp;pthread;rt;dl;cray-c++-rts;stdc++;m;modules;m;rt;fi;m;quadmath;rt;craymath;m;gfortran;quadmath;rt;f;m;pthread;rt;u;rt;dl;cray-c++-rts;stdc++;m;csup;rt;atomic;cray-c++-rts;stdc++;supc++;stdc++;pthread;c;csup;m;gcc
+dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/dmapp/7.1.1-6.0.5.0_49.8__g1125556.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
new file mode 100644
index 0000000..958a6b1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;mpichcxx_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;atomic;cray-c++-rts;cray-c++-rts;stdc++;supc++;tcmalloc_minimal;cray-c++-rts;stdc++;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
+dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output
new file mode 100644
index 0000000..40d0047
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;pthread;sci_gnu_71_mpi;sci_gnu_71;pthread;hugetlbfs;mpichcxx_gnu_71;rt;ugni;pthread;pmi;mpich_gnu_71;rt;ugni;pthread;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;ugni;pthread;xpmem;udreg;gfortran;quadmath;mvec;m;pthread;stdc++;m;gcc;c
+dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output
new file mode 100644
index 0000000..6212844
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output
@@ -0,0 +1,2 @@
+libs=imf;svml;irng;stdc++;m;ipgo;decimal;cilkrts;stdc++;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output
new file mode 100644
index 0000000..9828bf3
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;rca;sci_cray_mpi_mp;sci_cray_mp;mpich_cray;mpichf90_cray;pgas-dmapp;quadmath;omp;craymp;modules;fi;craymath;f;u;csup;atomic;gfortran;tcmalloc_minimal;stdc++;pthread;c;csup;m;gcc
+dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
new file mode 100644
index 0000000..4675408
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;mpichf90_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;gfortran;tcmalloc_minimal;cray-c++-rts;stdc++;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
+dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output
new file mode 100644
index 0000000..da2e557
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=AtpSigHandler;AtpSigHCommData;rca;sci_gnu_71_mpi;sci_gnu_71;mpich_gnu_71;mpichf90_gnu_71;gfortran;quadmath;pthread;gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
new file mode 100644
index 0000000..e73cbe9
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
@@ -0,0 +1,2 @@
+libs=hugetlbfs;AtpSigHandler;AtpSigHCommData;pthread;mpichf90_intel;rt;ugni;pmi;imf;m;pthread;dl;sci_intel_mpi;sci_intel;imf;m;dl;mpich_intel;rt;ugni;pthread;pmi;imf;m;dl;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;xpmem;ugni;pthread;udreg;sci_intel;imf;m;pthread;dl;hugetlbfs;imf;m;pthread;ifport;ifcore;imf;svml;m;ipgo;irc;svml;c;gcc;irc_s;dl;c
+dirs=/opt/cray/pe/libsci/18.07.1/INTEL/16.0/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-intel/16.0/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
new file mode 100644
index 0000000..c041faa
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
@@ -0,0 +1,2 @@
+libs=
+dirs=/usr/lib;/usr/local/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
new file mode 100644
index 0000000..f75637c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
@@ -0,0 +1,2 @@
+libs=c++
+dirs=/usr/lib;/usr/local/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
new file mode 100644
index 0000000..c041faa
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
@@ -0,0 +1,2 @@
+libs=
+dirs=/usr/lib;/usr/local/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
new file mode 100644
index 0000000..f75637c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
@@ -0,0 +1,2 @@
+libs=c++
+dirs=/usr/lib;/usr/local/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output
new file mode 100644
index 0000000..4ce854a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output
@@ -0,0 +1,2 @@
+libs=gcc;gcc_s;c;gcc;gcc_s
+dirs=/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output
new file mode 100644
index 0000000..b0b8e25
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output
@@ -0,0 +1,2 @@
+libs=c++;m;gcc;gcc_s;c;gcc;gcc_s
+dirs=/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output
new file mode 100644
index 0000000..1228333
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output
@@ -0,0 +1,2 @@
+libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/local/lib/gcc46/gcc/x86_64-portbld-freebsd10.0/4.6.4;/usr/local/x86_64-portbld-freebsd10.0/lib;/usr/local/lib/gcc46
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output
new file mode 100644
index 0000000..1b14cd5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output
@@ -0,0 +1,2 @@
+libs=
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output
new file mode 100644
index 0000000..9bb651a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output
@@ -0,0 +1,2 @@
+libs=
+dirs=/usr/lib64
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output
new file mode 100644
index 0000000..1b14cd5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output
@@ -0,0 +1,2 @@
+libs=
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output
new file mode 100644
index 0000000..9bb651a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output
@@ -0,0 +1,2 @@
+libs=
+dirs=/usr/lib64
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output
new file mode 100644
index 0000000..63b51a1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=gcc;gcc_s;c;gcc;gcc_s
+dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output
new file mode 100644
index 0000000..b61e097
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output
@@ -0,0 +1,2 @@
+libs=imf;svml;irng;m;ipgo;decimal;cilkrts;stdc++;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/compilers_and_libraries_2018.0.128/linux/ipp/lib/intel64;/opt/intel/compilers_and_libraries_2018.0.128/linux/compiler/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/mkl/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/tbb/lib/intel64/gcc4.7;/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output
new file mode 100644
index 0000000..7b20915
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output
new file mode 100644
index 0000000..de407f2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output
new file mode 100644
index 0000000..de515e7
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;dl;gcc_s;pthread;gcc;m;c;gcc_s;gcc
+dirs=/opt/ibm/xlsmp/5.1.0/lib;/opt/ibm/xlmass/9.1.0/lib;/opt/ibm/xlC/16.1.0/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
new file mode 100644
index 0000000..a657b85
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
@@ -0,0 +1,2 @@
+libs=cudadevrt;cudart_static;rt;pthread;dl;stdc++;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/local/cuda/targets/x86_64-linux/lib/stubs;/usr/local/cuda/targets/x86_64-linux/lib;/usr/lib/gcc/x86_64-linux-gnu/8;/usr/lib/x86_64-linux-gnu;/lib/x86_64-linux-gnu;/lib64;/usr/lib;/usr/lib/llvm-8/lib;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output
new file mode 100644
index 0000000..58ae64d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output
@@ -0,0 +1,2 @@
+libs=cudadevrt;cudart_static;rt;pthread;dl;xlopt;xl;ibmc++;stdc++;m;dl;gcc_s;gcc;pthread;m;c;gcc_s;gcc
+dirs=/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib/stubs;/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib;/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/xl-16.1.1-3/spectrum-mpi-10.3.0.1-20190611-aqjt3jo53mogrrhcrd2iufr435azcaha/lib;/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-csygoqyym3m3ysoaperhxlhoiluvpa2u/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlsmp/5.1.1/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlmass/9.1.1/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlC/16.1.1/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/autofs/nccs-svm1_sw/peak/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-ytwv7xbkub6mqnpvygdthwqa7mhjqbc5/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
new file mode 100644
index 0000000..7c99bf6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
@@ -0,0 +1,2 @@
+libs=cudadevrt;cudart_static;rt;pthread;dl;stdc++;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/lib/x86_64-linux-gnu/stubs;/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output
new file mode 100644
index 0000000..4fc7937
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=stdc++;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output
new file mode 100644
index 0000000..5d85789
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output
@@ -0,0 +1,2 @@
+libs=imf;svml;irng;stdc++;m;ipgo;decimal;cilkrts;stdc++;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/compilers_and_libraries_2018.0.128/linux/ipp/lib/intel64;/opt/intel/compilers_and_libraries_2018.0.128/linux/compiler/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/mkl/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/tbb/lib/intel64/gcc4.7;/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output
new file mode 100644
index 0000000..db0b29c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=atomic;pgatm;stdc++;pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output
new file mode 100644
index 0000000..4466415
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;ibmc++;xlopt;xl;stdc++;m;dl;gcc_s;gcc;m;c;gcc_s;gcc;dl;gcc_s;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output
new file mode 100644
index 0000000..8124911
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;ibmc++;stdc++;m;dl;gcc_s;gcc;pthread;m;c;gcc_s;gcc
+dirs=/opt/ibm/xlsmp/5.1.0/lib;/opt/ibm/xlmass/9.1.0/lib;/opt/ibm/xlC/16.1.0/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output
new file mode 100644
index 0000000..f985a03
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output
@@ -0,0 +1,2 @@
+libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output
new file mode 100644
index 0000000..d40f81e
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=pgf90rtl;pgf90;pgf90_rpm1;pgf902;pgf90rtl;pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output
new file mode 100644
index 0000000..3c5d23b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output
@@ -0,0 +1,2 @@
+libs=xlf90;xlopt;xlomp_ser;xl;xlfmath;gcc_s;dl;rt;pthread;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/xlf/bg/14.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output
new file mode 100644
index 0000000..7b20915
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output
new file mode 100644
index 0000000..de407f2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
new file mode 100644
index 0000000..db0b29c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=atomic;pgatm;stdc++;pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
new file mode 100644
index 0000000..4466415
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;ibmc++;xlopt;xl;stdc++;m;dl;gcc_s;gcc;m;c;gcc_s;gcc;dl;gcc_s;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
new file mode 100644
index 0000000..d40f81e
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=pgf90rtl;pgf90;pgf90_rpm1;pgf902;pgf90rtl;pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
new file mode 100644
index 0000000..de407f2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
@@ -0,0 +1,2 @@
+libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
+dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
new file mode 100644
index 0000000..7b523ea
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
@@ -0,0 +1,2 @@
+libs=pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
+dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output
new file mode 100644
index 0000000..8aee7cf
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output
@@ -0,0 +1,2 @@
+libs=mingw32;gcc;moldname;mingwex;advapi32;shell32;user32;kernel32;mingw32;gcc;moldname;mingwex
+dirs=C:/DoesNotExist/mingw/lib/gcc/mingw32/4.9.3;C:/DoesNotExist/mingw/lib/gcc;C:/DoesNotExist/mingw/mingw32/lib;C:/DoesNotExist/mingw/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output
new file mode 100644
index 0000000..5e79cc1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output
@@ -0,0 +1,2 @@
+libs=stdc++;mingw32;gcc_s;gcc;moldname;mingwex;advapi32;shell32;user32;kernel32;mingw32;gcc_s;gcc;moldname;mingwex
+dirs=C:/DoesNotExist/mingw/lib/gcc/mingw32/4.9.3;C:/DoesNotExist/mingw/lib/gcc;C:/DoesNotExist/mingw/mingw32/lib;C:/DoesNotExist/mingw/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output
new file mode 100644
index 0000000..4a09c5b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output
@@ -0,0 +1,2 @@
+libs=gcc;gcc_s;c;gcc;gcc_s
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output
new file mode 100644
index 0000000..d747e5b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output
@@ -0,0 +1,2 @@
+libs=stdc++;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
new file mode 100644
index 0000000..4a09c5b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
@@ -0,0 +1,2 @@
+libs=gcc;gcc_s;c;gcc;gcc_s
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
new file mode 100644
index 0000000..d747e5b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
@@ -0,0 +1,2 @@
+libs=stdc++;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output
new file mode 100644
index 0000000..5bb5db4
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output
@@ -0,0 +1,2 @@
+libs=compiler_rt;c;compiler_rt
+dirs=/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output
new file mode 100644
index 0000000..4158973
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output
@@ -0,0 +1,2 @@
+libs=c++;c++abi;pthread;m;compiler_rt;c;compiler_rt
+dirs=/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output
new file mode 100644
index 0000000..0d636e6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output
@@ -0,0 +1,2 @@
+libs=c
+dirs=/opt/solarisstudio12.4/lib/compilers/staticlib;/opt/solarisstudio12.4/lib/compilers/sparc;/opt/solarisstudio12.4/lib/compilers;/usr/ccs/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output
new file mode 100644
index 0000000..f7c8213
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output
@@ -0,0 +1,2 @@
+libs=Cstd;Crun;m;c
+dirs=/opt/solarisstudio12.4/lib/compilers/sparc;/opt/solarisstudio12.4/lib/compilers;/opt/solarisstudio12.4/lib/sparc;/opt/solarisstudio12.4/lib;/usr/ccs/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output
new file mode 100644
index 0000000..b49557a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output
@@ -0,0 +1,2 @@
+libs=fsu;sunmath;mtsk;m;c
+dirs=/opt/developerstudio12.6/lib/compilers/sparcvis2;/opt/developerstudio12.6/lib/compilers;/opt/developerstudio12.6/lib;/usr/ccs/lib;/lib;/usr/lib
diff --git a/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt b/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
new file mode 100644
index 0000000..7dbf32e
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.15.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
new file mode 100644
index 0000000..8cf0fc9
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
@@ -0,0 +1,17 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+  return()
+endif()
+
+if (EXISTS ${foobar_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} should not exist")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
new file mode 100644
index 0000000..59ee14b
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.15)
+project(DisabledPch C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  include/foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo)
+set_target_properties(foobar PROPERTIES DISABLE_PRECOMPILE_HEADERS ON)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
new file mode 100644
index 0000000..caeb22b
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
@@ -0,0 +1,31 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+  return()
+endif()
+
+if (NOT EXISTS ${foobar_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} does not exist")
+  return()
+endif()
+
+file(STRINGS ${foo_pch_header} foo_pch_header_strings)
+
+if (NOT foo_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\"(;|$)")
+  set(RunCMake_TEST_FAILED "Generated foo pch header\n  ${foo_pch_header}\nhas bad content:\n  ${foo_pch_header_strings}")
+  return()
+endif()
+
+file(STRINGS ${foobar_pch_header} foobar_pch_header_strings)
+
+if (NOT foobar_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\";#include \"[^\"]*PrecompileHeaders/include/bar.h\"(;|$)")
+  set(RunCMake_TEST_FAILED "Generated foobar pch header\n  ${foobar_pch_header}\nhas bad content:\n  ${foobar_pch_header_strings}")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
new file mode 100644
index 0000000..a1e0792
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchInterface C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  include/foo.h
+  \"foo2.h\"
+  <stdio.h>
+  \"string.h\"
+)
+
+add_library(bar INTERFACE)
+target_include_directories(bar INTERFACE include)
+target_precompile_headers(bar INTERFACE include/bar.h)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo bar)
+
+enable_testing()
+add_test(NAME foobar COMMAND foobar)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake
new file mode 100644
index 0000000..44fe2da
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake
@@ -0,0 +1,17 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(foobar_pch_h_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+  set(foobar_pch_hxx_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.hxx")
+else()
+  set(foobar_pch_h_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+  set(foobar_pch_hxx_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.hxx")
+endif()
+
+if (NOT EXISTS ${foobar_pch_h_header})
+  set(RunCMake_TEST_FAILED "Generated foobar C pch header ${foobar_pch_h_header} does not exist")
+  return()
+endif()
+
+if (NOT EXISTS ${foobar_pch_hxx_header})
+  set(RunCMake_TEST_FAILED "Generated foobar C++ pch header ${foobar_pch_hxx_header} does not exist")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchMultilanguage.cmake b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage.cmake
new file mode 100644
index 0000000..7a837da
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage.cmake
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchMultilanguage C CXX)
+
+add_executable(foobar
+  foo.c
+  main.cpp
+)
+target_include_directories(foobar PUBLIC include)
+target_precompile_headers(foobar PRIVATE "<stddef.h>")
diff --git a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
new file mode 100644
index 0000000..ba220f3
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
@@ -0,0 +1,12 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/CMakeFiles/main.dir/cmake_pch.hxx")
+else()
+  set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/cmake_pch.hxx")
+endif()
+
+file(STRINGS ${main_pch_header} main_pch_header_strings)
+string(REGEX MATCH "#pragma warning\\(push, 0\\).*#include.*pch.h.*#pragma warning\\(pop\\)" matched_code ${main_pch_header_strings})
+if(NOT matched_code)
+  set(RunCMake_TEST_FAILED "Generated pch file doesn't include expected prologue and epilogue code")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
new file mode 100644
index 0000000..3e27620
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(PchPrologueEpilogue)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_PCH_PROLOGUE "#pragma warning(push, 0)")
+set(CMAKE_PCH_EPILOGUE "#pragma warning(pop)")
+
+add_executable(main main.cpp)
+target_precompile_headers(main PRIVATE pch.h)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFrom.cmake b/Tests/RunCMake/PrecompileHeaders/PchReuseFrom.cmake
new file mode 100644
index 0000000..4502456
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchReuseFrom.cmake
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchReuseFrom C)
+
+add_library(empty empty.c)
+target_precompile_headers(empty PUBLIC
+  <stdio.h>
+  <string.h>
+)
+target_include_directories(empty PUBLIC include)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo REUSE_FROM empty)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo )
+set_target_properties(foobar PROPERTIES PRECOMPILE_HEADERS_REUSE_FROM foo)
+
+enable_testing()
+add_test(NAME foobar COMMAND foobar)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir-build-stderr.txt b/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir-build-stderr.txt
new file mode 100644
index 0000000..8cdcfd9
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir-build-stderr.txt
@@ -0,0 +1,2 @@
+^(|Warning #670: precompiled header file [^
+]* was not generated in this directory)$
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir.cmake b/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir.cmake
new file mode 100644
index 0000000..fff74dc
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir.cmake
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchReuseFromSubdir C)
+
+add_library(empty empty.c)
+target_precompile_headers(empty PUBLIC
+  <stdio.h>
+  <string.h>
+)
+target_include_directories(empty PUBLIC include)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo REUSE_FROM empty)
+
+subdirs(subdir)
+
+enable_testing()
+add_test(NAME foobar COMMAND foobar)
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
new file mode 100644
index 0000000..ec13663
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -0,0 +1,21 @@
+cmake_policy(SET CMP0057 NEW)
+include(RunCMake)
+
+function(run_test name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  run_cmake(${name})
+  # Precompiled headers are not supported with multiple architectures.
+  if(NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug)
+    run_cmake_command(${name}-test ${CMAKE_CTEST_COMMAND} -C Debug)
+  endif()
+endfunction()
+
+run_cmake(DisabledPch)
+run_test(PchInterface)
+run_cmake(PchPrologueEpilogue)
+run_test(SkipPrecompileHeaders)
+run_test(PchReuseFrom)
+run_test(PchReuseFromSubdir)
+run_cmake(PchMultilanguage)
diff --git a/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
new file mode 100644
index 0000000..49efbfb
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(SkipPrecompileHeaders)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_executable(pch-test main.cpp non-pch.cpp)
+target_precompile_headers(pch-test PRIVATE pch.h)
+
+set_source_files_properties(non-pch.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+
+enable_testing()
+add_test(NAME pch-test COMMAND pch-test)
diff --git a/Tests/RunCMake/PrecompileHeaders/empty.c b/Tests/RunCMake/PrecompileHeaders/empty.c
new file mode 100644
index 0000000..30ae1c4
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/empty.c
@@ -0,0 +1,3 @@
+void nothing()
+{
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/foo.c b/Tests/RunCMake/PrecompileHeaders/foo.c
new file mode 100644
index 0000000..eb88726
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/foo.c
@@ -0,0 +1,13 @@
+#include "foo.h"
+
+#include "foo2.h"
+
+int foo()
+{
+  return 0;
+}
+
+int foo2()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/foobar.c b/Tests/RunCMake/PrecompileHeaders/foobar.c
new file mode 100644
index 0000000..7a135ea
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/foobar.c
@@ -0,0 +1,8 @@
+#include "bar.h"
+#include "foo.h"
+#include "foo2.h"
+
+int main()
+{
+  return foo() + foo2() + bar();
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/include/bar.h b/Tests/RunCMake/PrecompileHeaders/include/bar.h
new file mode 100644
index 0000000..5feb983
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/include/bar.h
@@ -0,0 +1,9 @@
+#ifndef bar_h
+#define bar_h
+
+static int bar()
+{
+  return 0;
+}
+
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/include/foo.h b/Tests/RunCMake/PrecompileHeaders/include/foo.h
new file mode 100644
index 0000000..fc0ae14
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/include/foo.h
@@ -0,0 +1,6 @@
+#ifndef foo_h
+#define foo_h
+
+int foo(void);
+
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/include/foo2.h b/Tests/RunCMake/PrecompileHeaders/include/foo2.h
new file mode 100644
index 0000000..4bf9c81
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/include/foo2.h
@@ -0,0 +1,6 @@
+#ifndef foo2_h
+#define foo2_h
+
+int foo2(void);
+
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/main.cpp b/Tests/RunCMake/PrecompileHeaders/main.cpp
new file mode 100644
index 0000000..f8b643a
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/non-pch.cpp b/Tests/RunCMake/PrecompileHeaders/non-pch.cpp
new file mode 100644
index 0000000..df5a79f
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/non-pch.cpp
@@ -0,0 +1,3 @@
+#ifdef PCH_INCLUDED
+#  error "PCH must not be included into this file!"
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/pch.h b/Tests/RunCMake/PrecompileHeaders/pch.h
new file mode 100644
index 0000000..81b6d9e
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/pch.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define PCH_INCLUDED 1
diff --git a/Tests/RunCMake/PrecompileHeaders/subdir/CMakeLists.txt b/Tests/RunCMake/PrecompileHeaders/subdir/CMakeLists.txt
new file mode 100644
index 0000000..fa926c4
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(foobar ../foobar.c)
+target_link_libraries(foobar foo )
+set_target_properties(foobar PROPERTIES PRECOMPILE_HEADERS_REUSE_FROM foo)
diff --git a/Tests/RunCMake/README.rst b/Tests/RunCMake/README.rst
index d8b43fe..ebe40cf 100644
--- a/Tests/RunCMake/README.rst
+++ b/Tests/RunCMake/README.rst
@@ -55,6 +55,12 @@
    ``<SubTest>-check.cmake``
     Custom result check.
 
+  Note that when a specific platform expects differing stdout or stderr that
+  can be done by adding a platform specific output file. These follow the
+  naming convention of:
+   ``<SubTest>-stdout-<platform_lower_case>.txt``
+   ``<SubTest>-stderr-<platform_lower_case>.txt``
+
    Note that trailing newlines will be stripped from actual and expected
    test output before matching against the stdout and stderr expressions.
    The code in ``<SubTest>-check.cmake`` may use variables
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index c743a02..da4d1e5 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -21,10 +21,20 @@
   else()
     set(expect_result 0)
   endif()
+
+  string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} platform_name)
+  if(platform_name MATCHES cygwin)
+    #remove all additional bits from cygwin name
+    set(platform_name cygwin)
+  endif()
+
   foreach(o out err)
     if(RunCMake-std${o}-file AND EXISTS ${top_src}/${RunCMake-std${o}-file})
       file(READ ${top_src}/${RunCMake-std${o}-file} expect_std${o})
       string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}")
+    elseif(EXISTS ${top_src}/${test}-std${o}-${platform_name}.txt)
+      file(READ ${top_src}/${test}-std${o}-${platform_name}.txt expect_std${o})
+      string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}")
     elseif(EXISTS ${top_src}/${test}-std${o}.txt)
       file(READ ${top_src}/${test}-std${o}.txt expect_std${o})
       string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}")
@@ -137,8 +147,6 @@
     "|Error kstat returned"
     "|Hit xcodebuild bug"
     "|[^\n]*xcodebuild[^\n]*warning: file type[^\n]*is based on missing file type"
-    "|ld: 0711-224 WARNING: Duplicate symbol: .__init_aix_libgcc_cxa_atexit"
-    "|ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information"
     "|[^\n]*is a member of multiple groups"
     "|[^\n]*from Time Machine by path"
     "|[^\n]*Bullseye Testing Technology"
diff --git a/Tests/RunCMake/RuntimePath/Genex.cmake b/Tests/RunCMake/RuntimePath/Genex.cmake
new file mode 100644
index 0000000..152238a
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/Genex.cmake
@@ -0,0 +1,29 @@
+enable_language(C)
+
+add_library(A STATIC A.c)
+
+add_executable(buildge main.c)
+target_link_libraries(buildge A)
+set_target_properties(buildge PROPERTIES
+  BUILD_RPATH $<1:/opt/foo/lib>
+  )
+
+add_executable(buildnoge main.c)
+target_link_libraries(buildnoge A)
+set_target_properties(buildnoge PROPERTIES
+  BUILD_RPATH /opt/foo/lib
+  )
+
+add_executable(installge main.c)
+target_link_libraries(installge A)
+set_target_properties(installge PROPERTIES
+  INSTALL_RPATH $<1:/opt/foo/lib>
+  BUILD_WITH_INSTALL_RPATH 1
+  )
+
+add_executable(installnoge main.c)
+target_link_libraries(installnoge A)
+set_target_properties(installnoge PROPERTIES
+  INSTALL_RPATH /opt/foo/lib
+  BUILD_WITH_INSTALL_RPATH 1
+  )
diff --git a/Tests/RunCMake/RuntimePath/GenexCheck.cmake b/Tests/RunCMake/RuntimePath/GenexCheck.cmake
new file mode 100644
index 0000000..07dc496
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/GenexCheck.cmake
@@ -0,0 +1,7 @@
+file(GLOB_RECURSE files "${dir}/*")
+
+foreach(file IN LISTS files)
+  if(file MATCHES "/(build|install)(no)?ge$")
+    file(RPATH_CHANGE FILE "${file}" OLD_RPATH "/opt/foo/lib" NEW_RPATH "/opt/bar/lib")
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
index 6f1baa1..4c9ddcd 100644
--- a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
+++ b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
@@ -1,32 +1,26 @@
 include(RunCMake)
 
 
-function(run_SymlinkImplicit)
+function(run_RuntimePath name)
   # Use a single build tree for a few tests without cleaning.
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SymlinkImplicit-build)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   set(RunCMake_TEST_NO_CLEAN 1)
   if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
     set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
   endif()
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-  run_cmake(SymlinkImplicit)
-  run_cmake_command(SymlinkImplicit-build ${CMAKE_COMMAND} --build . --config Debug)
-  run_cmake_command(SymlinkImplicitCheck
-    ${CMAKE_COMMAND} -Ddir=${RunCMake_TEST_BINARY_DIR} -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake)
+  run_cmake(${name})
+  run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug)
 endfunction()
-run_SymlinkImplicit()
 
-function(run_Relative)
-  # Use a single build tree for a few tests without cleaning.
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Relative-build)
-  set(RunCMake_TEST_NO_CLEAN 1)
-  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
-    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
-  endif()
-  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
-  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-  run_cmake(Relative)
-  run_cmake_command(Relative-build ${CMAKE_COMMAND} --build . --config Debug)
-endfunction()
-run_Relative()
+run_RuntimePath(SymlinkImplicit)
+run_cmake_command(SymlinkImplicitCheck
+  ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/SymlinkImplicit-build -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake)
+
+run_RuntimePath(Relative)
+# FIXME: Run RelativeCheck (appears to be broken currently)
+
+run_RuntimePath(Genex)
+run_cmake_command(GenexCheck
+  ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/Genex-build -P ${RunCMake_SOURCE_DIR}/GenexCheck.cmake)
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index 0bcf886..6d72fac 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -27,6 +27,7 @@
    \* CMP0076
    \* CMP0081
    \* CMP0083
+   \* CMP0095
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/UnityBuild/CMakeLists.txt b/Tests/RunCMake/UnityBuild/CMakeLists.txt
new file mode 100644
index 0000000..77030d6
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.15)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
new file mode 100644
index 0000000..8e484d0
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
@@ -0,0 +1,23 @@
+include(RunCMake)
+
+run_cmake(unitybuild_c)
+run_cmake(unitybuild_cxx)
+run_cmake(unitybuild_c_and_cxx)
+run_cmake(unitybuild_batchsize)
+run_cmake(unitybuild_default_batchsize)
+run_cmake(unitybuild_skip)
+run_cmake(unitybuild_code_before_and_after_include)
+run_cmake(unitybuild_c_no_unity_build)
+run_cmake(unitybuild_order)
+
+function(run_test name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake(${name})
+  run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug)
+  run_cmake_command(${name}-test ${CMAKE_CTEST_COMMAND} -C Debug)
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endfunction()
+
+run_test(unitybuild_runtest)
diff --git a/Tests/RunCMake/UnityBuild/func.c b/Tests/RunCMake/UnityBuild/func.c
new file mode 100644
index 0000000..b14c907
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/func.c
@@ -0,0 +1,6 @@
+#include "func.h"
+
+int func(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/UnityBuild/func.h b/Tests/RunCMake/UnityBuild/func.h
new file mode 100644
index 0000000..563a980
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/func.h
@@ -0,0 +1,6 @@
+#ifndef func_h
+#define func_h
+
+extern int func(void);
+
+#endif
diff --git a/Tests/RunCMake/UnityBuild/main.c b/Tests/RunCMake/UnityBuild/main.c
new file mode 100644
index 0000000..19c18d4
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/main.c
@@ -0,0 +1,6 @@
+#include "func.h"
+
+int main(void)
+{
+  return func();
+}
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_batchsize-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_batchsize-check.cmake
new file mode 100644
index 0000000..32bb8e7
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_batchsize-check.cmake
@@ -0,0 +1,11 @@
+set(unitybuild_c0 "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+set(unitybuild_c1 "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_1.c")
+if(NOT EXISTS "${unitybuild_c0}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c0} does not exist.")
+  return()
+endif()
+
+if(NOT EXISTS "${unitybuild_c1}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c1} does not exist.")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_batchsize.cmake b/Tests/RunCMake/UnityBuild/unitybuild_batchsize.cmake
new file mode 100644
index 0000000..7caf251
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_batchsize.cmake
@@ -0,0 +1,16 @@
+project(unitybuild_batchsize C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt
+  PROPERTIES
+    UNITY_BUILD ON
+    UNITY_BUILD_BATCH_SIZE 4
+)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c-check.cmake
new file mode 100644
index 0000000..c980df0
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c-check.cmake
@@ -0,0 +1,5 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+if(NOT EXISTS "${unitybuild_c}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c} does not exist.")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c.cmake
new file mode 100644
index 0000000..77a09cb
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_c C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx-check.cmake
new file mode 100644
index 0000000..32c2992
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx-check.cmake
@@ -0,0 +1,11 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+if(NOT EXISTS "${unitybuild_c}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c} does not exist.")
+  return()
+endif()
+
+set(unitybuild_cxx "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.cxx")
+if(NOT EXISTS "${unitybuild_cxx}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_cxx} does not exist.")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx.cmake
new file mode 100644
index 0000000..073aff2
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx.cmake
@@ -0,0 +1,17 @@
+project(unitybuild_c_and_cxx C CXX)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src_c "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src_c}" "int s${s}(void) { return 0; }\n")
+
+  set(src_cxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cxx")
+  file(WRITE "${src_cxx}" "int s${s}(void) { return 0; }\n")
+
+  list(APPEND srcs "${src_c}")
+  list(APPEND srcs "${src_cxx}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build-check.cmake
new file mode 100644
index 0000000..cb71ea3
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build-check.cmake
@@ -0,0 +1,5 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+if(EXISTS "${unitybuild_c}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c} should not exist.")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build.cmake
new file mode 100644
index 0000000..1185e9f
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c_no_unity_build.cmake
@@ -0,0 +1,10 @@
+project(unitybuild_c_no_unity_build C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include-check.cmake
new file mode 100644
index 0000000..8fcb18f
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include-check.cmake
@@ -0,0 +1,7 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+file(STRINGS ${unitybuild_c} unitybuild_c_strings)
+string(REGEX MATCH "#define NOMINMAX.*#include.*s1.c.*#undef NOMINMAX" matched_code ${unitybuild_c_strings})
+if(NOT matched_code)
+  set(RunCMake_TEST_FAILED "Generated unity file doesn't include expected code before and after include")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include.cmake b/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include.cmake
new file mode 100644
index 0000000..cc9cc28
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_code_before_and_after_include.cmake
@@ -0,0 +1,13 @@
+project(unitybuild_code_before_and_after_include C)
+
+set(src "${CMAKE_CURRENT_BINARY_DIR}/s1.c")
+file(WRITE "${src}" "int s1(void) { return 0; }\n")
+
+add_library(tgt SHARED ${src})
+
+set_target_properties(tgt
+  PROPERTIES
+    UNITY_BUILD ON
+    UNITY_BUILD_CODE_BEFORE_INCLUDE "#define NOMINMAX"
+    UNITY_BUILD_CODE_AFTER_INCLUDE "#undef NOMINMAX"
+)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cxx-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cxx-check.cmake
new file mode 100644
index 0000000..89a037a
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_cxx-check.cmake
@@ -0,0 +1,5 @@
+set(unitybuild_cxx "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.cxx")
+if(NOT EXISTS "${unitybuild_cxx}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_cxx} does not exist.")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cxx.cmake
new file mode 100644
index 0000000..be800d7
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_cxx.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_cxx CXX)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cxx")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize-check.cmake
new file mode 100644
index 0000000..7dfc007
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize-check.cmake
@@ -0,0 +1,7 @@
+set(unitybuild_c0 "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+file(STRINGS ${unitybuild_c0} unitybuild_c_strings REGEX "/s[0-9]+.c\"$" )
+list(LENGTH unitybuild_c_strings number_of_includes)
+if(NOT number_of_includes EQUAL 8)
+  set(RunCMake_TEST_FAILED "Generated unity doesn't include the expect number of files")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize.cmake b/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize.cmake
new file mode 100644
index 0000000..60b9875
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_default_batchsize.cmake
@@ -0,0 +1,15 @@
+project(unitybuild_default_batchsize C)
+
+set(srcs "")
+foreach(s RANGE 1 10)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt
+  PROPERTIES
+    UNITY_BUILD ON
+)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_order-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_order-check.cmake
new file mode 100644
index 0000000..533a89c
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_order-check.cmake
@@ -0,0 +1,7 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+file(STRINGS ${unitybuild_c} unitybuild_c_strings)
+string(REGEX MATCH ".*#include.*s3.c.*#include.*s1.c.*#include.*s2.c.*" matched_code ${unitybuild_c_strings})
+if(NOT matched_code)
+  set(RunCMake_TEST_FAILED "Generated unity file doesn't include expected oder of source files")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_order.cmake b/Tests/RunCMake/UnityBuild/unitybuild_order.cmake
new file mode 100644
index 0000000..819603d
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_order.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_order C)
+
+set(srcs "")
+foreach(s 3 1 2)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_runtest.cmake b/Tests/RunCMake/UnityBuild/unitybuild_runtest.cmake
new file mode 100644
index 0000000..8816299
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_runtest.cmake
@@ -0,0 +1,8 @@
+project(unitybuild_runtest C)
+
+set(CMAKE_UNITY_BUILD ON) # This tests that the variable works in addition to the property
+
+add_executable(main main.c func.c)
+
+enable_testing()
+add_test(NAME main COMMAND main)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_skip-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_skip-check.cmake
new file mode 100644
index 0000000..fdd45bc
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_skip-check.cmake
@@ -0,0 +1,14 @@
+set(unitybuild_c "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+file(STRINGS ${unitybuild_c} unitybuild_c_strings)
+
+string(REGEX MATCH "\\/s[1-6].c" matched_files_1_6 ${unitybuild_c_strings})
+if(matched_files_1_6)
+  set(RunCMake_TEST_FAILED "Generated unity contains s1.c -> s6.c which should have been skipped")
+  return()
+endif()
+
+string(REGEX MATCH "\\/s[7-8].c" matched_files_7_8 ${unitybuild_c_strings})
+if(NOT matched_files_7_8)
+  set(RunCMake_TEST_FAILED "Generated unity should have contained s7.c, s8.c!")
+  return()
+endif()
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_skip.cmake b/Tests/RunCMake/UnityBuild/unitybuild_skip.cmake
new file mode 100644
index 0000000..74524ad
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_skip.cmake
@@ -0,0 +1,30 @@
+project(unitybuild_skip C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s1.c
+  PROPERTIES GENERATED ON)
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s2.c
+  PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s3.c
+  PROPERTIES COMPILE_OPTIONS "val")
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s4.c
+  PROPERTIES COMPILE_DEFINITIONS "val")
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s5.c
+  PROPERTIES COMPILE_FLAGS "val")
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/s6.c
+  PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")
diff --git a/Tests/RunCMake/VS10Project/Dir/DirNested/foo_nested.cpp b/Tests/RunCMake/VS10Project/Dir/DirNested/foo_nested.cpp
new file mode 100644
index 0000000..3695dc9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/Dir/DirNested/foo_nested.cpp
@@ -0,0 +1,3 @@
+void foo()
+{
+}
diff --git a/Tests/RunCMake/VS10Project/Dir/foo.cpp b/Tests/RunCMake/VS10Project/Dir/foo.cpp
new file mode 100644
index 0000000..3695dc9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/Dir/foo.cpp
@@ -0,0 +1,3 @@
+void foo()
+{
+}
diff --git a/Tests/RunCMake/VS10Project/Prefixed/PrefixedNested/bar_nested.cpp b/Tests/RunCMake/VS10Project/Prefixed/PrefixedNested/bar_nested.cpp
new file mode 100644
index 0000000..3695dc9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/Prefixed/PrefixedNested/bar_nested.cpp
@@ -0,0 +1,3 @@
+void foo()
+{
+}
diff --git a/Tests/RunCMake/VS10Project/Prefixed/bar.cpp b/Tests/RunCMake/VS10Project/Prefixed/bar.cpp
new file mode 100644
index 0000000..b72a1a5
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/Prefixed/bar.cpp
@@ -0,0 +1,3 @@
+void bar()
+{
+}
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 27b81b7..44ccd6b 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -5,6 +5,7 @@
 run_cmake(ExplicitCMakeLists)
 run_cmake(RuntimeLibrary)
 run_cmake(SourceGroupCMakeLists)
+run_cmake(SourceGroupTreeCMakeLists)
 
 run_cmake(VsConfigurationType)
 run_cmake(VsTargetsFileReferences)
@@ -22,6 +23,10 @@
 run_cmake(VsGlobals)
 run_cmake(VsProjectImport)
 run_cmake(VsPackageReferences)
+run_cmake(VsDpiAware)
+run_cmake(VsDpiAwareBadParam)
+run_cmake(VsPrecompileHeaders)
+run_cmake(VsPrecompileHeadersReuseFromCompilePDBName)
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   run_cmake(VsJustMyCode)
@@ -30,3 +35,12 @@
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.20)
   run_cmake(VsSpectreMitigation)
 endif()
+
+# Visual Studio 2017 has toolset version 141
+string(REPLACE "v" "" generator_toolset "${RunCMake_GENERATOR_TOOLSET}")
+if (RunCMake_GENERATOR MATCHES "Visual Studio 1[0-4] 201[0-5]" OR
+   (RunCMake_GENERATOR_TOOLSET AND generator_toolset VERSION_LESS "141"))
+  run_cmake(UnityBuildPre2017)
+else()
+  run_cmake(UnityBuildNative)
+endif()
diff --git a/Tests/RunCMake/VS10Project/SourceGroupCMakeLists-check.cmake b/Tests/RunCMake/VS10Project/SourceGroupCMakeLists-check.cmake
index c2a94bb..616f38b 100644
--- a/Tests/RunCMake/VS10Project/SourceGroupCMakeLists-check.cmake
+++ b/Tests/RunCMake/VS10Project/SourceGroupCMakeLists-check.cmake
@@ -4,30 +4,8 @@
   return()
 endif()
 
-set(foundFileFilter 0)
-set(foundFilter 0)
 file(STRINGS "${vcFiltersFile}" lines)
-foreach(line IN LISTS lines)
-  if(line MATCHES "<Filter>CMakeListsSourceGroup</Filter>")
-    set(rule "${CMAKE_MATCH_1}")
-    if(foundFileFilter)
-      set(RunCMake_TEST_FAILED "Multiple files listed with filter for CMakeListsSourceGroup.")
-      return()
-    endif()
-    set(foundFileFilter 1)
-  endif()
-  if(line MATCHES "<Filter.*Include=\"CMakeListsSourceGroup\"")
-    set(rule "${CMAKE_MATCH_1}")
-    if(foundFilter)
-      set(RunCMake_TEST_FAILED "Multiple copies of CMakeListsSourceGroup filter listed.")
-      return()
-    endif()
-    set(foundFilter 1)
-  endif()
-endforeach()
-if(NOT foundFileFilter)
-  set(RunCMake_TEST_FAILED "File filter for CMakeListsSourceGroup not found.")
-endif()
-if(NOT foundFilter)
-  set(RunCMake_TEST_FAILED "Filter CMakeListsSourceGroup not found.")
-endif()
+
+include(${RunCMake_TEST_SOURCE_DIR}/SourceGroupHelpers.cmake)
+
+find_source_group("${lines}" CMakeListsSourceGroup)
diff --git a/Tests/RunCMake/VS10Project/SourceGroupHelpers.cmake b/Tests/RunCMake/VS10Project/SourceGroupHelpers.cmake
new file mode 100644
index 0000000..c82a66e
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/SourceGroupHelpers.cmake
@@ -0,0 +1,35 @@
+function(find_source_group LINES NAME)
+  set(foundFileFilter 0)
+  set(foundFilter 0)
+  foreach(line IN LISTS LINES)
+    if(line MATCHES "<Filter>${NAME}</Filter>")
+      if(foundFileFilter)
+        set(RunCMake_TEST_FAILED "Multiple files listed with filter for ${NAME}." PARENT_SCOPE)
+        set(FILTER_FOUND 0 PARENT_SCOPE)
+        return()
+      endif()
+      set(foundFileFilter 1)
+    endif()
+    if(line MATCHES "<Filter.*Include=\"${NAME}\"")
+      if(foundFilter)
+        set(RunCMake_TEST_FAILED "Multiple copies of ${NAME} filter listed." PARENT_SCOPE)
+        set(FILTER_FOUND 0 PARENT_SCOPE)
+        return()
+      endif()
+      set(foundFilter 1)
+    endif()
+  endforeach()
+
+  if(NOT foundFileFilter)
+    set(RunCMake_TEST_FAILED "File filter for ${NAME} not found." PARENT_SCOPE)
+    set(FILTER_FOUND 0 PARENT_SCOPE)
+    return()
+  endif()
+  if(NOT foundFilter)
+    set(RunCMake_TEST_FAILED "Filter ${NAME} not found." PARENT_SCOPE)
+    set(FILTER_FOUND 0 PARENT_SCOPE)
+    return()
+  endif()
+
+  set(FILTER_FOUND 1 PARENT_SCOPE)
+endfunction()
diff --git a/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists-check.cmake b/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists-check.cmake
new file mode 100644
index 0000000..655120a
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists-check.cmake
@@ -0,0 +1,25 @@
+cmake_policy(SET CMP0011 NEW)
+
+set(vcFiltersFile "${RunCMake_TEST_BINARY_DIR}/SourceGroupTree.vcxproj.filters")
+if(NOT EXISTS "${vcFiltersFile}")
+  set(RunCMake_TEST_FAILED "Filters file ${vcFiltersFile} does not exist.")
+  return()
+endif()
+
+file(STRINGS "${vcFiltersFile}" lines)
+
+include(${RunCMake_TEST_SOURCE_DIR}/SourceGroupHelpers.cmake)
+
+set(SOURCE_GROUPS_TO_FIND
+  "Dir"
+  "Dir\\DirNested"
+  "SourcesPrefix"
+  "SourcesPrefix\\PrefixedNested"
+)
+
+foreach(GROUP_NAME IN LISTS ${SOURCE_GROUPS_TO_FIND})
+  find_source_group("${lines}" ${GROUP_NAME})
+  if(NOT ${FILTER_FOUND})
+    return()
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists.cmake b/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists.cmake
new file mode 100644
index 0000000..83c87a9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/SourceGroupTreeCMakeLists.cmake
@@ -0,0 +1,16 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+
+set(SRC_FILES
+  ${CMAKE_CURRENT_SOURCE_DIR}/Dir/foo.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/Dir/DirNested/foo_nested.cpp
+)
+
+set(PREFIXED_SRC_FILES
+  ${CMAKE_CURRENT_SOURCE_DIR}/Prefixed/bar.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/Prefixed/PrefixedNested/bar_nested.cpp
+)
+
+add_custom_target(SourceGroupTree SOURCES ${SRC_FILES} ${PREFIXED_SRC_FILES})
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SRC_FILES})
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Prefixed PREFIX SourcesPrefix FILES ${PREFIXED_SRC_FILES})
diff --git a/Tests/RunCMake/VS10Project/UnityBuildNative-check.cmake b/Tests/RunCMake/VS10Project/UnityBuildNative-check.cmake
new file mode 100644
index 0000000..87f247d
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/UnityBuildNative-check.cmake
@@ -0,0 +1,45 @@
+set(unitybuild_c0 "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+if(NOT EXISTS "${unitybuild_c0}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c0} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/tgt.vcxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "<EnableUnitySupport>true</EnableUnitySupport>")
+    set(have_unity_support ON)
+  endif()
+
+  if (line MATCHES "<ClCompile Include=.*IncludeInUnityFile=\"false\" CustomUnityFile=\"true\"")
+    set(unity_source_line ${line})
+  endif()
+
+  if (line MATCHES "<ClCompile Include=.*IncludeInUnityFile=\"true\" CustomUnityFile=\"true\"")
+    list(APPEND sources_list ${line})
+  endif()
+endforeach()
+
+if (NOT have_unity_support)
+  set(RunCMake_TEST_FAILED "Generated project should include the <EnableUnitySupport> block.")
+  return()
+endif()
+
+string(REPLACE "\\" "/" unity_source_line "${unity_source_line}")
+string(FIND "${unity_source_line}" "CMakeFiles/tgt.dir/Unity/unity_0.c" unity_source_file_position)
+if (unity_source_file_position EQUAL "-1")
+  set(RunCMake_TEST_FAILED "Generated project should include the generated unity source file.")
+  return()
+endif()
+
+list(LENGTH sources_list number_of_sources)
+if(NOT number_of_sources EQUAL 8)
+  set(RunCMake_TEST_FAILED "Generated project doesn't include the expect number of files.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/UnityBuildNative.cmake b/Tests/RunCMake/VS10Project/UnityBuildNative.cmake
new file mode 100644
index 0000000..77a09cb
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/UnityBuildNative.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_c C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake
new file mode 100644
index 0000000..1c6bab8
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake
@@ -0,0 +1,48 @@
+set(unitybuild_c0 "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0.c")
+if(NOT EXISTS "${unitybuild_c0}")
+  set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_c0} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/tgt.vcxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "<ClCompile Include=.*/>")
+    set(unity_source_line ${line})
+  endif()
+
+  if (line MATCHES "<ClCompile Include=\"[^\"]*\">")
+    string(REGEX MATCH "<ClCompile Include=\"([^\"]*)\">" source_file ${line})
+    list(APPEND sources_list ${source_file})
+  endif()
+
+  if (line MATCHES "<ExcludedFromBuild.*</ExcludedFromBuild>")
+    list(APPEND excluded_sources_list ${source_file})
+  endif()
+endforeach()
+
+string(REPLACE "\\" "/" unity_source_line ${unity_source_line})
+string(FIND "${unity_source_line}" "CMakeFiles/tgt.dir/Unity/unity_0.c" unity_source_file_position)
+if (unity_source_file_position EQUAL "-1")
+  set(RunCMake_TEST_FAILED "Generated project should include the generated unity source file.")
+  return()
+endif()
+
+list(LENGTH sources_list number_of_sources)
+if(NOT number_of_sources EQUAL 8)
+  set(RunCMake_TEST_FAILED "Generated project doesn't include the expect number of files.")
+  return()
+endif()
+
+# Exclusions for Debug|Release|MinSizeRel|RelWithDebInfo
+list(LENGTH excluded_sources_list number_of_excluded_sources)
+if(NOT number_of_excluded_sources EQUAL 32)
+  set(RunCMake_TEST_FAILED "Generated project doesn't exclude the source files for all configurations.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/UnityBuildPre2017.cmake b/Tests/RunCMake/VS10Project/UnityBuildPre2017.cmake
new file mode 100644
index 0000000..77a09cb
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/UnityBuildPre2017.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_c C)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/VS10Project/VsConfigurationType-check.cmake b/Tests/RunCMake/VS10Project/VsConfigurationType-check.cmake
index 4690970..bbd34da 100644
--- a/Tests/RunCMake/VS10Project/VsConfigurationType-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsConfigurationType-check.cmake
@@ -9,7 +9,7 @@
 foreach(line IN LISTS lines)
   if(line MATCHES "^ *<ConfigurationType>(.*)</ConfigurationType>$")
     set(propertyFound TRUE)
-    set(expectedValue "MyValue")
+    set(expectedValue "MyValue foo")
     set(actualValue ${CMAKE_MATCH_1})
     if(NOT (${actualValue} STREQUAL ${expectedValue}))
       set(RunCMake_TEST_FAILED "ConfigurationType \"${actualValue}\" differs from expected value \"${expectedValue}\".")
diff --git a/Tests/RunCMake/VS10Project/VsConfigurationType.cmake b/Tests/RunCMake/VS10Project/VsConfigurationType.cmake
index a73dfe8..a2f544a 100644
--- a/Tests/RunCMake/VS10Project/VsConfigurationType.cmake
+++ b/Tests/RunCMake/VS10Project/VsConfigurationType.cmake
@@ -1,3 +1,3 @@
 enable_language(CXX)
 add_library(foo foo.cpp)
-set_target_properties(foo PROPERTIES VS_CONFIGURATION_TYPE "MyValue")
+set_target_properties(foo PROPERTIES VS_CONFIGURATION_TYPE "MyValue $<TARGET_PROPERTY:foo,NAME>")
diff --git a/Tests/RunCMake/VS10Project/VsDpiAware-check.cmake b/Tests/RunCMake/VS10Project/VsDpiAware-check.cmake
new file mode 100644
index 0000000..fbb64f0
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDpiAware-check.cmake
@@ -0,0 +1,41 @@
+macro(VSDpiAware_check tgt dpiaware_match_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_DPIAWARE_MATCH 0)
+  set(IN_MANIFEST_SETTINGS 0)
+
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<Manifest>")
+      set(IN_MANIFEST_SETTINGS 1)
+    elseif(line MATCHES "^ *</Manifest>")
+      set(IN_MANIFEST_SETTINGS 0)
+    elseif(IN_MANIFEST_SETTINGS AND (line MATCHES "^ *<EnableDpiAwareness>([^<>]+)</EnableDpiAwareness>"))
+      set(dpiaware_match_actual "${CMAKE_MATCH_1}")
+      if(NOT "${dpiaware_match_actual}" STREQUAL "${dpiaware_match_expect}")
+        set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has <EnableDpiAwareness> '${dpiaware_match_actual}', not '${dpiaware_match_expect}'.")
+        return()
+      endif()
+      set(HAVE_DPIAWARE_MATCH 1)
+      break()
+    endif()
+  endforeach()
+
+  if(NOT HAVE_DPIAWARE_MATCH AND NOT "${dpiaware_match_expect}" STREQUAL "")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a <EnableDpiAwareness> property group.")
+    return()
+  endif()
+endmacro()
+
+VSDpiAware_check(DPIAWARE-default-C "")
+VSDpiAware_check(DPIAWARE-default-CXX "")
+VSDpiAware_check(DPIAWARE-TGT-PERMONITOR-C "PerMonitorHighDPIAware")
+VSDpiAware_check(DPIAWARE-TGT-PERMONITOR-CXX "PerMonitorHighDPIAware")
+VSDpiAware_check(DPIAWARE-TGT-ON-C "true")
+VSDpiAware_check(DPIAWARE-TGT-ON-CXX "true")
+VSDpiAware_check(DPIAWARE-TGT-OFF-C "false")
+VSDpiAware_check(DPIAWARE-TGT-OFF-CXX "false")
diff --git a/Tests/RunCMake/VS10Project/VsDpiAware.cmake b/Tests/RunCMake/VS10Project/VsDpiAware.cmake
new file mode 100644
index 0000000..74e3d21
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDpiAware.cmake
@@ -0,0 +1,19 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+enable_language(C)
+enable_language(CXX)
+
+add_executable(DPIAWARE-default-C empty.c)
+add_executable(DPIAWARE-default-CXX empty.cxx)
+
+add_executable(DPIAWARE-TGT-PERMONITOR-C empty.c)
+set_property(TARGET DPIAWARE-TGT-PERMONITOR-C PROPERTY VS_DPI_AWARE "PerMonitor")
+add_executable(DPIAWARE-TGT-PERMONITOR-CXX empty.cxx)
+set_property(TARGET DPIAWARE-TGT-PERMONITOR-CXX PROPERTY VS_DPI_AWARE "PerMonitor")
+add_executable(DPIAWARE-TGT-ON-C empty.c)
+set_property(TARGET DPIAWARE-TGT-ON-C PROPERTY VS_DPI_AWARE ON)
+add_executable(DPIAWARE-TGT-ON-CXX empty.cxx)
+set_property(TARGET DPIAWARE-TGT-ON-CXX PROPERTY VS_DPI_AWARE ON)
+add_executable(DPIAWARE-TGT-OFF-C empty.c)
+set_property(TARGET DPIAWARE-TGT-OFF-C PROPERTY VS_DPI_AWARE OFF)
+add_executable(DPIAWARE-TGT-OFF-CXX empty.cxx)
+set_property(TARGET DPIAWARE-TGT-OFF-CXX PROPERTY VS_DPI_AWARE OFF)
diff --git a/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-result.txt b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt
new file mode 100644
index 0000000..95fc5ca
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt
@@ -0,0 +1,3 @@
+CMake Error: Bad parameter for VS_DPI_AWARE: Bar
+CMake Error: Bad parameter for VS_DPI_AWARE: Foo
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/VS10Project/VsDpiAwareBadParam.cmake b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam.cmake
new file mode 100644
index 0000000..e05452b
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam.cmake
@@ -0,0 +1,8 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+enable_language(C)
+enable_language(CXX)
+
+add_executable(DPIAWARE-TGT-BADPARAM-C empty.c)
+set_property(TARGET DPIAWARE-TGT-BADPARAM-C PROPERTY VS_DPI_AWARE "Foo")
+add_executable(DPIAWARE-TGT-BADPARAM-CXX empty.cxx)
+set_property(TARGET DPIAWARE-TGT-BADPARAM-CXX PROPERTY VS_DPI_AWARE "Bar")
diff --git a/Tests/RunCMake/VS10Project/VsPackageReferences-check.cmake b/Tests/RunCMake/VS10Project/VsPackageReferences-check.cmake
index 4ff5327..512a1c9 100644
--- a/Tests/RunCMake/VS10Project/VsPackageReferences-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsPackageReferences-check.cmake
@@ -25,7 +25,7 @@
       if(line MATCHES "^ *<PackageReference .* Version=\"${testVersion}\".*>$")
         set(Library${i}Found TRUE)
         message(STATUS "foo.vcxproj is using package reference ${testLibrary} with version ${testVersion}")
-      elseif()
+      else()
         message(STATUS "foo.vcxproj failed to define reference ${testLibrary} with version ${testVersion}")
         set(Library${i}Found FALSE)
       endif()
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
new file mode 100644
index 0000000..27842f9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
@@ -0,0 +1,66 @@
+set(pch_header "CMakeFiles/tgt.dir/cmake_pch.hxx")
+set(pch_source [=[CMakeFiles\\tgt.dir\\cmake_pch.cxx]=])
+
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${pch_source}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_source} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/tgt.vcxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "<PrecompiledHeader.*>Use</PrecompiledHeader>")
+    set(have_pch_use ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeader.*>Create</PrecompiledHeader>")
+    set(have_pch_create ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeaderFile.*>.*${pch_header}</PrecompiledHeaderFile>")
+    set(have_pch_header ON)
+  endif()
+
+  if (line MATCHES "<ForcedIncludeFiles.*>.*${pch_header}</ForcedIncludeFiles>")
+    set(have_force_pch_header ON)
+  endif()
+
+  if (line MATCHES "<ClCompile Include=.*${pch_source}\">")
+    set(have_pch_source_compile ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_use)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Use</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_create)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Create</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeaderFile>${pch_header}</PrecompiledHeaderFile> block.")
+  return()
+endif()
+
+if (NOT have_force_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ForcedIncludeFiles>${pch_header}</ForcedIncludeFiles> block.")
+  return()
+endif()
+
+if (NOT have_pch_source_compile)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ClCompile Include=\"${pch_source}\"> block.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
new file mode 100644
index 0000000..6d208c9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
@@ -0,0 +1,4 @@
+project(VsPrecompileHeaders CXX)
+
+add_library(tgt SHARED empty.cxx)
+target_precompile_headers(tgt PRIVATE stdafx.h)
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-result.txt b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-stderr.txt b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-stderr.txt
new file mode 100644
index 0000000..2ff57cd
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at VsPrecompileHeadersReuseFromCompilePDBName.cmake:6 \(add_library\):
+  PRECOMPILE_HEADERS_REUSE_FROM property is set on target \("b"\).  Reusable
+  precompile headers requires the COMPILE_PDB_NAME property to have the value
+  "a"
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName.cmake
new file mode 100644
index 0000000..ec11008
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeadersReuseFromCompilePDBName.cmake
@@ -0,0 +1,9 @@
+project(VsPrecompileHeadersReuseFromCompilePDBName CXX)
+
+add_library(a SHARED empty.cxx)
+target_precompile_headers(a PRIVATE <windows.h>)
+
+add_library(b SHARED empty.cxx)
+target_precompile_headers(b REUSE_FROM a)
+
+set_target_properties(b PROPERTIES COMPILE_PDB_NAME b)
diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake
index dab1c33..b1deb99 100644
--- a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake
+++ b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake
@@ -14,6 +14,9 @@
 set(FoundCEAdditionalFiles FALSE)
 set(FoundRemoteDirectory FALSE)
 set(FoundToolsVersion4 FALSE)
+set(FoundEnableRedirectPlatform FALSE)
+set(FoundRedirectPlatformValue FALSE)
+
 
 file(STRINGS "${vcProjectFile}" lines)
 foreach(line IN LISTS lines)
@@ -23,6 +26,10 @@
     set(FoundRemoteDirectory TRUE)
   elseif(line MATCHES " *<Project +.*ToolsVersion=\"4.0\".*> *$")
     set(FoundToolsVersion4 TRUE)
+  elseif(line MATCHES "^ *<EnableRedirectPlatform>true</EnableRedirectPlatform> *$")
+    set(FoundEnableRedirectPlatform TRUE)
+  elseif(line MATCHES "^ *<RedirectPlatformValue>.+</RedirectPlatformValue> *$")
+    set(FoundRedirectPlatformValue TRUE)
   endif()
 endforeach()
 
@@ -41,6 +48,16 @@
   return()
 endif()
 
+if(NOT FoundEnableRedirectPlatform)
+  set(RunCMake_TEST_FAILED "Failed to find EnableRedirectPlatform true property.")
+  return()
+endif()
+
+if(NOT FoundRedirectPlatformValue)
+  set(RunCMake_TEST_FAILED "Failed to find RedirectPlatformValue property.")
+  return()
+endif()
+
 #
 # Test solution file deployment items.
 #
diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
index 191f56d..1dfa8b2 100644
--- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
@@ -12,6 +12,7 @@
 run_cmake(XcodeOptimizationFlags)
 run_cmake(XcodePreserveNonOptimizationFlags)
 run_cmake(XcodePreserveObjcFlag)
+run_cmake(XcodePrecompileHeaders)
 if (NOT XCODE_VERSION VERSION_LESS 6)
   run_cmake(XcodePlatformFrameworks)
 endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
index ef772ea..8c0b470 100644
--- a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
@@ -5,6 +5,7 @@
 
 if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
   set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
 endif()
 
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
index f6c00b1..c221033 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
@@ -11,6 +11,7 @@
 endif()
 
 set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
 set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
 set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
 
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
index ec11dbb..172f2e8 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
@@ -7,6 +7,7 @@
 endif()
 
 set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
 set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
 
 add_library(foo SHARED foo.cpp)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
index 58e96b4..038a890 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
@@ -7,6 +7,7 @@
 endif()
 
 set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
 set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
 
 add_library(foo SHARED foo.cpp)
diff --git a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
new file mode 100644
index 0000000..aa3eafc
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
@@ -0,0 +1,35 @@
+set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx")
+
+if(NOT EXISTS "${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/XcodePrecompileHeaders.xcodeproj/project.pbxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "GCC_PRECOMPILE_PREFIX_HEADER = YES;")
+    set(have_pch_prefix ON)
+  endif()
+
+  string(FIND "${line}" "GCC_PREFIX_HEADER = \"${pch_header}\";" find_pos)
+  if (NOT find_pos EQUAL "-1")
+    set(have_pch_header ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_prefix)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PRECOMPILE_PREFIX_HEADER = YES; line.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PREFIX_HEADER = \"${pch_header}\"; line.")
+  return()
+endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake
new file mode 100644
index 0000000..f86bcf4
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake
@@ -0,0 +1,4 @@
+project(XcodePrecompileHeaders CXX)
+
+add_library(tgt foo.cpp)
+target_precompile_headers(tgt PRIVATE stdafx.h)
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake
index 88077b3..7d83a70 100644
--- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake
@@ -7,6 +7,13 @@
   endif()
 endfunction()
 
+function(expect_schema target)
+  set(schema "${RunCMake_TEST_BINARY_DIR}/XcodeSchemaProperty.xcodeproj/xcshareddata/xcschemes/${target}.xcscheme")
+  if(NOT EXISTS ${schema})
+    message(SEND_ERROR "Missing schema for target ${target}")
+  endif()
+endfunction()
+
 function(expect_no_schema target)
   set(schema "${RunCMake_TEST_BINARY_DIR}/XcodeSchemaProperty.xcodeproj/xcshareddata/xcschemes/${target}.xcscheme")
   if(EXISTS ${schema})
@@ -40,3 +47,6 @@
 check_property("ENVIRONMENT" [=[value="bar"]=])
 
 expect_no_schema("NoSchema")
+
+expect_schema("CustomTarget")
+expect_schema("ALL_BUILD")
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
index 73ef5ca..be219f4 100644
--- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
@@ -38,3 +38,5 @@
 
 add_executable(NoSchema main.cpp)
 set_target_properties(NoSchema PROPERTIES XCODE_GENERATE_SCHEME OFF)
+
+add_custom_target(CustomTarget)
diff --git a/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-result.txt b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-stderr.txt b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-stderr.txt
new file mode 100644
index 0000000..503385f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at AppendLiteralQuotes.cmake:2 \(add_custom_command\):
+  COMMAND may not contain literal quotes:
+
+    "c"
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/AppendLiteralQuotes.cmake b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes.cmake
new file mode 100644
index 0000000..038fb3f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/AppendLiteralQuotes.cmake
@@ -0,0 +1,2 @@
+add_custom_command(OUTPUT a COMMAND b)
+add_custom_command(OUTPUT a COMMAND "\"c\"" APPEND)
diff --git a/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt b/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt
index cd542d8..b7ee23a 100644
--- a/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt
+++ b/Tests/RunCMake/add_custom_command/AppendNotOutput-stderr.txt
@@ -1,7 +1,7 @@
 CMake Error at AppendNotOutput.cmake:1 \(add_custom_command\):
   add_custom_command given APPEND option with output
 
-  .*RunCMake/add_custom_command/AppendNotOutput-build/out.*
+    .*RunCMake/add_custom_command/AppendNotOutput-build/out
 
   which is not already a custom command output.
 Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-result.txt b/Tests/RunCMake/add_custom_command/BadByproduct-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadByproduct-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
new file mode 100644
index 0000000..086e397
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
@@ -0,0 +1,36 @@
+CMake Error at BadByproduct.cmake:2 \(add_custom_command\):
+  add_custom_command called with BYPRODUCTS containing a "#".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:3 \(add_custom_command\):
+  add_custom_command called with BYPRODUCTS containing a "<".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:4 \(add_custom_command\):
+  add_custom_command called with BYPRODUCTS containing a ">".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:5 \(add_custom_command\):
+  add_custom_command called with BYPRODUCTS containing a "<".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:6 \(add_custom_command\):
+  add_custom_command attempted to have a file
+
+    .*RunCMake/add_custom_command/f
+
+  in a source directory as an output of custom command.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct.cmake b/Tests/RunCMake/add_custom_command/BadByproduct.cmake
new file mode 100644
index 0000000..91bca52
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadByproduct.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+add_custom_command(OUTPUT a BYPRODUCTS "a#")
+add_custom_command(OUTPUT b BYPRODUCTS "a<")
+add_custom_command(OUTPUT c BYPRODUCTS "a>")
+add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/#")
+add_custom_command(OUTPUT e BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/f)
diff --git a/Tests/RunCMake/add_custom_command/BadOutput-result.txt b/Tests/RunCMake/add_custom_command/BadOutput-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadOutput-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
new file mode 100644
index 0000000..731e58d
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
@@ -0,0 +1,36 @@
+CMake Error at BadOutput.cmake:2 \(add_custom_command\):
+  add_custom_command called with OUTPUT containing a "#".  This character is
+  not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadOutput.cmake:3 \(add_custom_command\):
+  add_custom_command called with OUTPUT containing a "<".  This character is
+  not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadOutput.cmake:4 \(add_custom_command\):
+  add_custom_command called with OUTPUT containing a ">".  This character is
+  not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadOutput.cmake:5 \(add_custom_command\):
+  add_custom_command called with OUTPUT containing a "<".  This character is
+  not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadOutput.cmake:6 \(add_custom_command\):
+  add_custom_command attempted to have a file
+
+    .*RunCMake/add_custom_command/e
+
+  in a source directory as an output of custom command.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/BadOutput.cmake b/Tests/RunCMake/add_custom_command/BadOutput.cmake
new file mode 100644
index 0000000..6875fe9
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/BadOutput.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+add_custom_command(OUTPUT "a#" COMMAND a)
+add_custom_command(OUTPUT "a<" COMMAND b)
+add_custom_command(OUTPUT "a>" COMMAND c)
+add_custom_command(OUTPUT "$<CONFIG>/#" COMMAND d)
+add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/e COMMAND f)
diff --git a/Tests/RunCMake/add_custom_command/GeneratedProperty.cmake b/Tests/RunCMake/add_custom_command/GeneratedProperty.cmake
new file mode 100644
index 0000000..628134b
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/GeneratedProperty.cmake
@@ -0,0 +1,10 @@
+add_custom_command(
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../GeneratedProperty-build/a"
+  BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../GeneratedProperty-build/b"
+  COMMAND c
+  )
+get_source_file_property(GENERATED_A "${CMAKE_CURRENT_BINARY_DIR}/a" GENERATED)
+get_source_file_property(GENERATED_B "${CMAKE_CURRENT_BINARY_DIR}/b" GENERATED)
+if(NOT GENERATED_A OR NOT GENERATED_B)
+  message(FATAL_ERROR "failed")
+endif()
diff --git a/Tests/RunCMake/add_custom_command/LiteralQuotes-result.txt b/Tests/RunCMake/add_custom_command/LiteralQuotes-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/LiteralQuotes-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_command/LiteralQuotes-stderr.txt b/Tests/RunCMake/add_custom_command/LiteralQuotes-stderr.txt
new file mode 100644
index 0000000..e7d6155
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/LiteralQuotes-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at LiteralQuotes.cmake:1 \(add_custom_command\):
+  COMMAND may not contain literal quotes:
+
+    "b"
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/LiteralQuotes.cmake b/Tests/RunCMake/add_custom_command/LiteralQuotes.cmake
new file mode 100644
index 0000000..046ddd9
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/LiteralQuotes.cmake
@@ -0,0 +1 @@
+add_custom_command(OUTPUT a COMMAND "\"b\"")
diff --git a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
index 0387dbb..96642fa 100644
--- a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
+++ b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
@@ -1,14 +1,20 @@
 include(RunCMake)
 
+run_cmake(AppendLiteralQuotes)
 run_cmake(AppendNoOutput)
 run_cmake(AppendNotOutput)
 run_cmake(BadArgument)
+run_cmake(BadByproduct)
+run_cmake(BadOutput)
+run_cmake(GeneratedProperty)
+run_cmake(LiteralQuotes)
 run_cmake(NoArguments)
 run_cmake(NoOutputOrTarget)
 run_cmake(OutputAndTarget)
 run_cmake(SourceByproducts)
 run_cmake(SourceUsesTerminal)
 run_cmake(TargetImported)
+run_cmake(TargetLiteralQuotes)
 run_cmake(TargetNotInDir)
 
 if(${RunCMake_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])")
diff --git a/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-result.txt b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-stderr.txt b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-stderr.txt
new file mode 100644
index 0000000..c4589b4
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at TargetLiteralQuotes.cmake:2 \(add_custom_command\):
+  COMMAND may not contain literal quotes:
+
+    "b"
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/TargetLiteralQuotes.cmake b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes.cmake
new file mode 100644
index 0000000..5f11ed1
--- /dev/null
+++ b/Tests/RunCMake/add_custom_command/TargetLiteralQuotes.cmake
@@ -0,0 +1,2 @@
+add_library(a)
+add_custom_command(TARGET a POST_BUILD COMMAND "\"b\"")
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-result.txt b/Tests/RunCMake/add_custom_target/BadByproduct-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/BadByproduct-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
new file mode 100644
index 0000000..0f58550
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
@@ -0,0 +1,36 @@
+CMake Error at BadByproduct.cmake:2 \(add_custom_target\):
+  add_custom_target called with BYPRODUCTS containing a "#".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:3 \(add_custom_target\):
+  add_custom_target called with BYPRODUCTS containing a "<".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:4 \(add_custom_target\):
+  add_custom_target called with BYPRODUCTS containing a ">".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:5 \(add_custom_target\):
+  add_custom_target called with BYPRODUCTS containing a "<".  This character
+  is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at BadByproduct.cmake:6 \(add_custom_target\):
+  add_custom_target attempted to have a file
+
+    .*RunCMake/add_custom_target/j
+
+  in a source directory as an output of custom command.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct.cmake b/Tests/RunCMake/add_custom_target/BadByproduct.cmake
new file mode 100644
index 0000000..963d641
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/BadByproduct.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+add_custom_target(a BYPRODUCTS "a#" COMMAND b)
+add_custom_target(c BYPRODUCTS "a<" COMMAND d)
+add_custom_target(e BYPRODUCTS "a>" COMMAND f)
+add_custom_target(g BYPRODUCTS "$<CONFIG>/#" COMMAND h)
+add_custom_target(i BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/j COMMAND k)
diff --git a/Tests/RunCMake/add_custom_target/GeneratedProperty.cmake b/Tests/RunCMake/add_custom_target/GeneratedProperty.cmake
new file mode 100644
index 0000000..d034534
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/GeneratedProperty.cmake
@@ -0,0 +1,14 @@
+add_custom_command(
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../GeneratedProperty-build/a"
+  COMMAND b
+  )
+add_custom_target(CollapseFullPath
+  DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/a"
+  BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../GeneratedProperty-build/c"
+  COMMAND d
+  )
+get_source_file_property(GENERATED_A "${CMAKE_CURRENT_BINARY_DIR}/a" GENERATED)
+get_source_file_property(GENERATED_C "${CMAKE_CURRENT_BINARY_DIR}/c" GENERATED)
+if(NOT GENERATED_A OR NOT GENERATED_C)
+  message(FATAL_ERROR "failed")
+endif()
diff --git a/Tests/RunCMake/add_custom_target/LiteralQuotes-result.txt b/Tests/RunCMake/add_custom_target/LiteralQuotes-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/LiteralQuotes-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/add_custom_target/LiteralQuotes-stderr.txt b/Tests/RunCMake/add_custom_target/LiteralQuotes-stderr.txt
new file mode 100644
index 0000000..bc5187a
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/LiteralQuotes-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at LiteralQuotes.cmake:1 \(add_custom_target\):
+  COMMAND may not contain literal quotes:
+
+    "b"
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_target/LiteralQuotes.cmake b/Tests/RunCMake/add_custom_target/LiteralQuotes.cmake
new file mode 100644
index 0000000..9870608
--- /dev/null
+++ b/Tests/RunCMake/add_custom_target/LiteralQuotes.cmake
@@ -0,0 +1 @@
+add_custom_target(a COMMAND "\"b\"")
diff --git a/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake
index 2caed03..f5d5dd2 100644
--- a/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake
+++ b/Tests/RunCMake/add_custom_target/RunCMakeTest.cmake
@@ -1,9 +1,12 @@
 include(RunCMake)
 
-run_cmake(CommandExpandsEmpty)
-run_cmake(NoArguments)
+run_cmake(BadByproduct)
 run_cmake(BadTargetName)
 run_cmake(ByproductsNoCommand)
+run_cmake(CommandExpandsEmpty)
+run_cmake(GeneratedProperty)
+run_cmake(LiteralQuotes)
+run_cmake(NoArguments)
 run_cmake(UsesTerminalNoCommand)
 
 function(run_TargetOrder)
diff --git a/Tests/RunCMake/add_library/UNKNOWNwithOnlyObjectSources-stderr.txt b/Tests/RunCMake/add_library/UNKNOWNwithOnlyObjectSources-stderr.txt
index e332281..838992b 100644
--- a/Tests/RunCMake/add_library/UNKNOWNwithOnlyObjectSources-stderr.txt
+++ b/Tests/RunCMake/add_library/UNKNOWNwithOnlyObjectSources-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error at UNKNOWNwithOnlyObjectSources.cmake:[0-9]+ \(target_sources\):
-  target_sources called with non-compilable target type
+  target_sources may only set INTERFACE properties on IMPORTED targets
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
index ff676a6..57ab938 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll.cmake
@@ -11,4 +11,6 @@
 set(bar_lib \"$<TARGET_FILE:bar>\")
 set(zot_lib \"$<TARGET_FILE:zot>\")
 set(subinc_lib \"$<TARGET_FILE:subinc>\")
+set(subsub_lib \"$<TARGET_FILE:subsub>\")
+set(subsubinc_lib \"$<TARGET_FILE:subsubinc>\")
 ")
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
index 790da54..9ed9e55 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/CMakeLists.txt
@@ -1,5 +1,7 @@
 project(ExcludeFromAllSub NONE)
 
+add_subdirectory(SubSub EXCLUDE_FROM_ALL)
+
 add_library(bar STATIC EXCLUDE_FROM_ALL bar.cpp)
 
 add_library(zot STATIC zot.cpp)
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/CMakeLists.txt
new file mode 100644
index 0000000..ec28275
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(subsub STATIC subsub.cpp)
+add_library(subsubinc STATIC subsub.cpp)
+set_property(TARGET subsubinc PROPERTY EXCLUDE_FROM_ALL OFF)
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/subsub.cpp b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/subsub.cpp
new file mode 100644
index 0000000..ca689ed
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/SubSub/subsub.cpp
@@ -0,0 +1,4 @@
+int subsub()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake
index 297ad1e..d318c10 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check-sub.cmake
@@ -8,6 +8,7 @@
       "${foo_lib}"
       "${subinc_lib}"
       "${zot_lib}"
+      "${subsubinc_lib}"
       )
     if(NOT EXISTS "${file}")
       set(RunCMake_TEST_FAILED
@@ -18,6 +19,7 @@
   foreach(file
       "${main_exe}"
       "${bar_lib}"
+      "${subsub_lib}"
       )
     if(EXISTS "${file}")
       set(RunCMake_TEST_FAILED
diff --git a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
index 433c032..d4bdef2 100644
--- a/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
+++ b/Tests/RunCMake/add_subdirectory/ExcludeFromAll/check.cmake
@@ -8,6 +8,7 @@
       "${foo_lib}"
       "${subinc_lib}"
       "${main_exe}"
+      "${subsubinc_lib}"
       )
     if(EXISTS "${file}")
       # Remove for next step of test.
@@ -21,6 +22,7 @@
   foreach(file
       "${zot_lib}"
       "${bar_lib}"
+      "${subsub_lib}"
       )
     if(EXISTS "${file}")
       set(RunCMake_TEST_FAILED
diff --git a/Tests/RunCMake/color_warning.c b/Tests/RunCMake/color_warning.c
new file mode 100644
index 0000000..831abd9
--- /dev/null
+++ b/Tests/RunCMake/color_warning.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+int main(void)
+{
+  printf(
+    "/tmp/hello.c:3:2: \033[35mwarning:\033[0m Hello, World! [-W#warnings]\n");
+  return 0;
+}
diff --git a/Tests/RunCMake/ctest_build/IgnoreColor-stdout.txt b/Tests/RunCMake/ctest_build/IgnoreColor-stdout.txt
new file mode 100644
index 0000000..cd1720f
--- /dev/null
+++ b/Tests/RunCMake/ctest_build/IgnoreColor-stdout.txt
@@ -0,0 +1,2 @@
+   0 Compiler errors
+   1 Compiler warnings
diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
index 1092d2a..b2e562a 100644
--- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
@@ -1,6 +1,8 @@
 include(RunCTest)
 
 set(CASE_CTEST_BUILD_ARGS "")
+set(RunCMake_USE_LAUNCHERS TRUE)
+set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
 
 function(run_ctest_build CASE_NAME)
   set(CASE_CTEST_BUILD_ARGS "${ARGN}")
@@ -45,3 +47,9 @@
   run_ctest(BuildChangeId)
 endfunction()
 run_BuildChangeId()
+
+set(RunCMake_USE_LAUNCHERS FALSE)
+set(RunCMake_USE_CUSTOM_BUILD_COMMAND TRUE)
+set(RunCMake_BUILD_COMMAND "${COLOR_WARNING}")
+run_ctest(IgnoreColor)
+unset(RunCMake_BUILD_COMMAND)
diff --git a/Tests/RunCMake/ctest_build/test.cmake.in b/Tests/RunCMake/ctest_build/test.cmake.in
index 6f15ec9..9f7fa13 100644
--- a/Tests/RunCMake/ctest_build/test.cmake.in
+++ b/Tests/RunCMake/ctest_build/test.cmake.in
@@ -9,7 +9,10 @@
 set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
 set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
 set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
-set(CTEST_USE_LAUNCHERS                 TRUE)
+set(CTEST_USE_LAUNCHERS                 "@RunCMake_USE_LAUNCHERS@")
+if (@RunCMake_USE_CUSTOM_BUILD_COMMAND@)
+  set(CTEST_BUILD_COMMAND               "\"@RunCMake_BUILD_COMMAND@\"")
+endif()
 
 set(ctest_build_args "@CASE_CTEST_BUILD_ARGS@")
 ctest_start(Experimental)
diff --git a/Tests/RunCMake/ctest_start/AppendDifferentGroup-stderr.txt b/Tests/RunCMake/ctest_start/AppendDifferentGroup-stderr.txt
new file mode 100644
index 0000000..9e493a6
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/AppendDifferentGroup-stderr.txt
@@ -0,0 +1 @@
+^Group given in TAG does not match group given in ctest_start\(\)$
diff --git a/Tests/RunCMake/ctest_start/AppendDifferentGroup-stdout.txt b/Tests/RunCMake/ctest_start/AppendDifferentGroup-stdout.txt
new file mode 100644
index 0000000..5f83653
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/AppendDifferentGroup-stdout.txt
@@ -0,0 +1,8 @@
+Run dashboard with to-be-determined model
+   Source directory: .*/Tests/RunCMake/ctest_start/AppendDifferentGroup
+   Build directory: .*/Tests/RunCMake/ctest_start/AppendDifferentGroup-build
+   Group: ExperimentalDifferent
+   Site: test-site
+   Build name: test-build-name
+  Use existing tag: 19551112-2204 - ExperimentalDifferent
+   Use ExperimentalDifferent tag: [0-9-]+
diff --git a/Tests/RunCMake/ctest_start/AppendDifferentTrack-stderr.txt b/Tests/RunCMake/ctest_start/AppendDifferentTrack-stderr.txt
index 0d6d19d..9e493a6 100644
--- a/Tests/RunCMake/ctest_start/AppendDifferentTrack-stderr.txt
+++ b/Tests/RunCMake/ctest_start/AppendDifferentTrack-stderr.txt
@@ -1 +1 @@
-^Track given in TAG does not match track given in ctest_start\(\)$
+^Group given in TAG does not match group given in ctest_start\(\)$
diff --git a/Tests/RunCMake/ctest_start/AppendDifferentTrack-stdout.txt b/Tests/RunCMake/ctest_start/AppendDifferentTrack-stdout.txt
index 25085ef..022e2ec 100644
--- a/Tests/RunCMake/ctest_start/AppendDifferentTrack-stdout.txt
+++ b/Tests/RunCMake/ctest_start/AppendDifferentTrack-stdout.txt
@@ -1,7 +1,7 @@
 Run dashboard with to-be-determined model
    Source directory: .*/Tests/RunCMake/ctest_start/AppendDifferentTrack
    Build directory: .*/Tests/RunCMake/ctest_start/AppendDifferentTrack-build
-   Track: ExperimentalDifferent
+   Group: ExperimentalDifferent
    Site: test-site
    Build name: test-build-name
   Use existing tag: 19551112-2204 - ExperimentalDifferent
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArg-result.txt b/Tests/RunCMake/ctest_start/MissingGroupArg-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArg-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArg-stderr.txt b/Tests/RunCMake/ctest_start/MissingGroupArg-stderr.txt
new file mode 100644
index 0000000..e0480f6
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArg-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at .*/Tests/RunCMake/ctest_start/MissingGroupArg/test\.cmake:[0-9]+ \(ctest_start\):
+  ctest_start GROUP argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArgAppend-result.txt b/Tests/RunCMake/ctest_start/MissingGroupArgAppend-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArgAppend-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArgAppend-stderr.txt b/Tests/RunCMake/ctest_start/MissingGroupArgAppend-stderr.txt
new file mode 100644
index 0000000..8ae53ff
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArgAppend-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at .*/Tests/RunCMake/ctest_start/MissingGroupArgAppend/test\.cmake:[0-9]+ \(ctest_start\):
+  ctest_start GROUP argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-result.txt b/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-stderr.txt b/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-stderr.txt
new file mode 100644
index 0000000..c4f8900
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/MissingGroupArgQuiet-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at .*/Tests/RunCMake/ctest_start/MissingGroupArgQuiet/test\.cmake:[0-9]+ \(ctest_start\):
+  ctest_start GROUP argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/MissingTrackArg-stderr.txt b/Tests/RunCMake/ctest_start/MissingTrackArg-stderr.txt
index 7b42bc9..2a72a83 100644
--- a/Tests/RunCMake/ctest_start/MissingTrackArg-stderr.txt
+++ b/Tests/RunCMake/ctest_start/MissingTrackArg-stderr.txt
@@ -1,2 +1,2 @@
 ^CMake Error at .*/Tests/RunCMake/ctest_start/MissingTrackArg/test\.cmake:[0-9]+ \(ctest_start\):
-  ctest_start TRACK argument missing track name$
+  ctest_start TRACK argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/MissingTrackArgAppend-stderr.txt b/Tests/RunCMake/ctest_start/MissingTrackArgAppend-stderr.txt
index 695bfad..7ff82ab 100644
--- a/Tests/RunCMake/ctest_start/MissingTrackArgAppend-stderr.txt
+++ b/Tests/RunCMake/ctest_start/MissingTrackArgAppend-stderr.txt
@@ -1,2 +1,2 @@
 ^CMake Error at .*/Tests/RunCMake/ctest_start/MissingTrackArgAppend/test\.cmake:[0-9]+ \(ctest_start\):
-  ctest_start TRACK argument missing track name$
+  ctest_start TRACK argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/MissingTrackArgQuiet-stderr.txt b/Tests/RunCMake/ctest_start/MissingTrackArgQuiet-stderr.txt
index 9438522..c23b1bf 100644
--- a/Tests/RunCMake/ctest_start/MissingTrackArgQuiet-stderr.txt
+++ b/Tests/RunCMake/ctest_start/MissingTrackArgQuiet-stderr.txt
@@ -1,2 +1,2 @@
 ^CMake Error at .*/Tests/RunCMake/ctest_start/MissingTrackArgQuiet/test\.cmake:[0-9]+ \(ctest_start\):
-  ctest_start TRACK argument missing track name$
+  ctest_start TRACK argument missing group name$
diff --git a/Tests/RunCMake/ctest_start/NoAppendDifferentGroup-stdout.txt b/Tests/RunCMake/ctest_start/NoAppendDifferentGroup-stdout.txt
new file mode 100644
index 0000000..13a3883
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/NoAppendDifferentGroup-stdout.txt
@@ -0,0 +1,7 @@
+Run dashboard with model Experimental
+   Source directory: .*/Tests/RunCMake/ctest_start/NoAppendDifferentGroup
+   Build directory: .*/Tests/RunCMake/ctest_start/NoAppendDifferentGroup-build
+   Group: ExperimentalDifferent
+   Site: test-site
+   Build name: test-build-name
+   Use ExperimentalDifferent tag: [0-9-]+
diff --git a/Tests/RunCMake/ctest_start/NoAppendDifferentTrack-stdout.txt b/Tests/RunCMake/ctest_start/NoAppendDifferentTrack-stdout.txt
index 20a29be..c511e0d 100644
--- a/Tests/RunCMake/ctest_start/NoAppendDifferentTrack-stdout.txt
+++ b/Tests/RunCMake/ctest_start/NoAppendDifferentTrack-stdout.txt
@@ -1,7 +1,7 @@
 Run dashboard with model Experimental
    Source directory: .*/Tests/RunCMake/ctest_start/NoAppendDifferentTrack
    Build directory: .*/Tests/RunCMake/ctest_start/NoAppendDifferentTrack-build
-   Track: ExperimentalDifferent
+   Group: ExperimentalDifferent
    Site: test-site
    Build name: test-build-name
    Use ExperimentalDifferent tag: [0-9-]+
diff --git a/Tests/RunCMake/ctest_start/RunCMakeTest.cmake b/Tests/RunCMake/ctest_start/RunCMakeTest.cmake
index 905ad00..da85b39 100644
--- a/Tests/RunCMake/ctest_start/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_start/RunCMakeTest.cmake
@@ -26,18 +26,24 @@
 run_ctest_start(WriteModelToTagContinuous Continuous QUIET)
 run_ctest_start(WriteModelToTagNightly Nightly QUIET)
 run_ctest_start(WriteModelToTagNoMatchingTrack Continuous TRACK SomeWeirdTrackName QUIET)
+run_ctest_start(WriteModelToTagNoMatchingGroup Continuous GROUP SomeWeirdTrackName QUIET)
 run_ctest_start(AppendSameModel Continuous APPEND)
 run_ctest_start(AppendDifferentModel Experimental APPEND)
 run_ctest_start(AppendNoModel APPEND)
 run_ctest_start(AppendDifferentTrack TRACK ExperimentalDifferent APPEND)
+run_ctest_start(AppendDifferentGroup GROUP ExperimentalDifferent APPEND)
 run_ctest_start(NoAppendDifferentTrack Experimental TRACK ExperimentalDifferent)
+run_ctest_start(NoAppendDifferentGroup Experimental GROUP ExperimentalDifferent)
 run_ctest_start(AppendNoMatchingTrack Continuous APPEND)
 run_ctest_start(AppendOldContinuous Continuous APPEND)
 run_ctest_start(AppendOldNoModel APPEND)
 run_ctest_start(NoModel QUIET)
 run_ctest_start(MissingTrackArg Experimental TRACK)
+run_ctest_start(MissingGroupArg Experimental GROUP)
 run_ctest_start(MissingTrackArgAppend Experimental TRACK APPEND)
+run_ctest_start(MissingGroupArgAppend Experimental GROUP APPEND)
 run_ctest_start(MissingTrackArgQuiet Experimental TRACK QUIET)
+run_ctest_start(MissingGroupArgQuiet Experimental GROUP QUIET)
 run_ctest_start(TooManyArgs Experimental
                 ${RunCMake_BINARY_DIR}/TooManyArgs-build
                 ${RunCMake_BINARY_DIR}/TooManyArgs-build
diff --git a/Tests/RunCMake/ctest_start/WriteModelToTagNoMatchingGroup-check.cmake b/Tests/RunCMake/ctest_start/WriteModelToTagNoMatchingGroup-check.cmake
new file mode 100644
index 0000000..bd2862d
--- /dev/null
+++ b/Tests/RunCMake/ctest_start/WriteModelToTagNoMatchingGroup-check.cmake
@@ -0,0 +1 @@
+check_tag_contents("^[0-9-]+\nSomeWeirdTrackName\nContinuous\n$")
diff --git a/Tests/RunCMake/export/DependOnDoubleExport-result.txt b/Tests/RunCMake/export/DependOnDoubleExport-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnDoubleExport-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt b/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt
new file mode 100644
index 0000000..b78c7e4
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error in CMakeLists.txt:
+  export called with target "exported" which requires target "doubleexported"
+  that is not in this export set, but in multiple other export sets:
+  .*/Tests/RunCMake/export/DependOnDoubleExport-build/exportset.cmake,
+  .*/Tests/RunCMake/export/DependOnDoubleExport-build/manual.cmake.
+
+
+  An exported target cannot depend upon another target which is exported
+  multiple times.  Consider consolidating the exports of the "doubleexported"
+  target to a single export.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/export/DependOnDoubleExport.cmake b/Tests/RunCMake/export/DependOnDoubleExport.cmake
new file mode 100644
index 0000000..8d108d7
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnDoubleExport.cmake
@@ -0,0 +1,7 @@
+add_library(doubleexported INTERFACE)
+install(TARGETS doubleexported EXPORT exportset)
+export(TARGETS doubleexported FILE "${CMAKE_CURRENT_BINARY_DIR}/manual.cmake")
+export(EXPORT exportset FILE "${CMAKE_CURRENT_BINARY_DIR}/exportset.cmake")
+add_library(exported INTERFACE)
+target_link_libraries(exported INTERFACE doubleexported)
+export(TARGETS exported FILE "${CMAKE_CURRENT_BINARY_DIR}/exports.cmake")
diff --git a/Tests/RunCMake/export/DependOnNotExport-result.txt b/Tests/RunCMake/export/DependOnNotExport-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnNotExport-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/export/DependOnNotExport-stderr.txt b/Tests/RunCMake/export/DependOnNotExport-stderr.txt
new file mode 100644
index 0000000..80f5758
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnNotExport-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error in CMakeLists.txt:
+  export called with target "exported" which requires target "notexported"
+  that is not in any export set.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/export/DependOnNotExport.cmake b/Tests/RunCMake/export/DependOnNotExport.cmake
new file mode 100644
index 0000000..06c1ad9
--- /dev/null
+++ b/Tests/RunCMake/export/DependOnNotExport.cmake
@@ -0,0 +1,4 @@
+add_library(notexported INTERFACE)
+add_library(exported INTERFACE)
+target_link_libraries(exported INTERFACE notexported)
+export(TARGETS exported FILE "${CMAKE_CURRENT_BINARY_DIR}/exports.cmake")
diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake
index 97a0ca6..4d2f217 100644
--- a/Tests/RunCMake/export/RunCMakeTest.cmake
+++ b/Tests/RunCMake/export/RunCMakeTest.cmake
@@ -10,3 +10,5 @@
 run_cmake(ForbiddenToExportImportedProperties)
 run_cmake(ForbiddenToExportPropertyWithGenExp)
 run_cmake(ExportPropertiesUndefined)
+run_cmake(DependOnNotExport)
+run_cmake(DependOnDoubleExport)
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt
new file mode 100644
index 0000000..6912bdf
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt
@@ -0,0 +1,9 @@
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt
new file mode 100644
index 0000000..6912bdf
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt
@@ -0,0 +1,9 @@
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt
new file mode 100644
index 0000000..27a83ad
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt
@@ -0,0 +1,9 @@
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnv.cmake b/Tests/RunCMake/find_file/FromPATHEnv.cmake
new file mode 100644
index 0000000..9f058dd
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPATHEnv.cmake
@@ -0,0 +1,24 @@
+set(ENV_PATH "$ENV{PATH}")
+foreach(path "/does_not_exist" "/include" "")
+  unset(PrefixInPATH_File CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_file(PrefixInPATH_File NAMES PrefixInPATH.h)
+  message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+foreach(path "/does_not_exist" "/include" "")
+  unset(PrefixInPATH_File CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_file(PrefixInPATH_File NAMES PrefixInPATH.h)
+  message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+foreach(path "/does_not_exist" "/include" "")
+  unset(PrefixInPATH_File CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_file(PrefixInPATH_File NAMES PrefixInPATH.h NO_SYSTEM_ENVIRONMENT_PATH)
+  message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'")
+endforeach()
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt b/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt
new file mode 100644
index 0000000..4bd24aa
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt
@@ -0,0 +1,6 @@
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
+-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPrefixPath.cmake b/Tests/RunCMake/find_file/FromPrefixPath.cmake
new file mode 100644
index 0000000..63c6a07
--- /dev/null
+++ b/Tests/RunCMake/find_file/FromPrefixPath.cmake
@@ -0,0 +1,19 @@
+set(ENV_PATH "$ENV{PATH}")
+set(ENV{PATH} "")
+foreach(path "/does_not_exist" "/include" "")
+  unset(PrefixInPATH_File CACHE)
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_file(PrefixInPATH_File NAMES PrefixInPATH.h)
+  message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'")
+endforeach()
+
+set(CMAKE_FIND_USE_CMAKE_PATH OFF)
+set(CMAKE_PREFIX_PATH )
+foreach(path "/does_not_exist" "/include" "")
+  unset(PrefixInPATH_File CACHE)
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_file(PrefixInPATH_File NAMES PrefixInPATH.h)
+  message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'")
+endforeach()
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt
new file mode 100644
index 0000000..d73bc1d
--- /dev/null
+++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt
new file mode 100644
index 0000000..d73bc1d
--- /dev/null
+++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt
index d73bc1d..947a900 100644
--- a/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt
+++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt
@@ -1,4 +1,4 @@
 -- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/RunCMakeTest.cmake b/Tests/RunCMake/find_file/RunCMakeTest.cmake
index 5ce96e0..9f56a57 100644
--- a/Tests/RunCMake/find_file/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_file/RunCMakeTest.cmake
@@ -1,5 +1,5 @@
 include(RunCMake)
 
-if(WIN32 OR CYGWIN)
-  run_cmake(PrefixInPATH)
-endif()
+run_cmake(FromPATHEnv)
+run_cmake(FromPrefixPath)
+run_cmake(PrefixInPATH)
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt
new file mode 100644
index 0000000..01e2720
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt
@@ -0,0 +1,6 @@
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt
new file mode 100644
index 0000000..01e2720
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt
@@ -0,0 +1,6 @@
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt
new file mode 100644
index 0000000..4e570a6
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt
@@ -0,0 +1,6 @@
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnv.cmake b/Tests/RunCMake/find_library/FromPATHEnv.cmake
new file mode 100644
index 0000000..fec041d
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPATHEnv.cmake
@@ -0,0 +1,22 @@
+list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib)
+list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a)
+set(ENV_PATH "$ENV{PATH}")
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib/libcreated.a" "created")
+
+foreach(path "/does_not_exist" "/lib" "")
+  unset(CREATED_LIBRARY CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_BINARY_DIR}${path}")
+  find_library(CREATED_LIBRARY NAMES created)
+  message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+foreach(path "/does_not_exist" "/lib" "")
+  unset(CREATED_LIBRARY CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_BINARY_DIR}${path}")
+  find_library(CREATED_LIBRARY NAMES created)
+  message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'")
+endforeach()
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt b/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt
new file mode 100644
index 0000000..de3df1a
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt
@@ -0,0 +1,6 @@
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPrefixPath-build/lib/libcreated.a'
+-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPrefixPath-build/lib/libcreated.a'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
+-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPrefixPath.cmake b/Tests/RunCMake/find_library/FromPrefixPath.cmake
new file mode 100644
index 0000000..04763a9
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromPrefixPath.cmake
@@ -0,0 +1,24 @@
+list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib)
+list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a)
+set(ENV_PATH "$ENV{PATH}")
+set(ENV{PATH} "")
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib/libcreated.a" "created")
+
+foreach(path "/does_not_exist" "/lib" "")
+  unset(CREATED_LIBRARY CACHE)
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}${path}")
+  find_library(CREATED_LIBRARY NAMES created)
+  message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'")
+endforeach()
+
+set(CMAKE_FIND_USE_CMAKE_PATH OFF)
+set(CMAKE_PREFIX_PATH )
+foreach(path "/does_not_exist" "/lib" "")
+  unset(CREATED_LIBRARY CACHE)
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}${path}")
+  find_library(CREATED_LIBRARY NAMES created)
+  message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'")
+endforeach()
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt
new file mode 100644
index 0000000..1ab884c
--- /dev/null
+++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt
new file mode 100644
index 0000000..1ab884c
--- /dev/null
+++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt
index 1ab884c..c6ff513 100644
--- a/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt
+++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt
@@ -1,4 +1,4 @@
 -- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
+-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
+-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake
index e7e8db3..643a5b9 100644
--- a/Tests/RunCMake/find_library/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake
@@ -1,9 +1,9 @@
 include(RunCMake)
 
 run_cmake(Created)
+run_cmake(FromPrefixPath)
+run_cmake(FromPATHEnv)
 if(CMAKE_HOST_UNIX)
   run_cmake(LibArchLink)
 endif()
-if(WIN32 OR CYGWIN)
-  run_cmake(PrefixInPATH)
-endif()
+run_cmake(PrefixInPATH)
diff --git a/Tests/RunCMake/find_package/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_package/FromPATHEnv-stdout.txt
new file mode 100644
index 0000000..2ead349
--- /dev/null
+++ b/Tests/RunCMake/find_package/FromPATHEnv-stdout.txt
@@ -0,0 +1,9 @@
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='1'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
diff --git a/Tests/RunCMake/find_package/FromPATHEnv.cmake b/Tests/RunCMake/find_package/FromPATHEnv.cmake
new file mode 100644
index 0000000..4822b13
--- /dev/null
+++ b/Tests/RunCMake/find_package/FromPATHEnv.cmake
@@ -0,0 +1,27 @@
+set(ENV_PATH "$ENV{PATH}")
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved NO_SYSTEM_ENVIRONMENT_PATH QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_package/FromPrefixPath-stdout.txt b/Tests/RunCMake/find_package/FromPrefixPath-stdout.txt
new file mode 100644
index 0000000..2ead349
--- /dev/null
+++ b/Tests/RunCMake/find_package/FromPrefixPath-stdout.txt
@@ -0,0 +1,9 @@
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='1'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
+-- Resolved_FOUND='0'
diff --git a/Tests/RunCMake/find_package/FromPrefixPath.cmake b/Tests/RunCMake/find_package/FromPrefixPath.cmake
new file mode 100644
index 0000000..be853c1
--- /dev/null
+++ b/Tests/RunCMake/find_package/FromPrefixPath.cmake
@@ -0,0 +1,29 @@
+set(ENV_PATH "$ENV{PATH}")
+set(ENV{PATH} "")
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+
+set(CMAKE_FIND_USE_CMAKE_PATH OFF)
+set(CMAKE_PREFIX_PATH )
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+
+set(CMAKE_FIND_USE_CMAKE_PATH ON)
+foreach(path "/does_not_exist" "/PackageRoot" "")
+  unset(Resolved_FOUND CACHE)
+  set(Resolved_DIR "")
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_package(Resolved NO_CMAKE_PATH QUIET)
+  message(STATUS "Resolved_FOUND='${Resolved_FOUND}'")
+endforeach()
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index 066523e..208f83c 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -3,6 +3,8 @@
 run_cmake(CMP0074-WARN)
 run_cmake(CMP0074-OLD)
 run_cmake(ComponentRequiredAndOptional)
+run_cmake(FromPATHEnv)
+run_cmake(FromPrefixPath)
 run_cmake(MissingNormal)
 run_cmake(MissingNormalRequired)
 run_cmake(MissingNormalVersion)
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt
new file mode 100644
index 0000000..8f3e7ca
--- /dev/null
+++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt
@@ -0,0 +1,9 @@
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
+-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt
new file mode 100644
index 0000000..8f3e7ca
--- /dev/null
+++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt
@@ -0,0 +1,9 @@
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
+-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt
new file mode 100644
index 0000000..fd41bf4
--- /dev/null
+++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt
@@ -0,0 +1,9 @@
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
+-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnv.cmake b/Tests/RunCMake/find_path/FromPATHEnv.cmake
new file mode 100644
index 0000000..af13d09
--- /dev/null
+++ b/Tests/RunCMake/find_path/FromPATHEnv.cmake
@@ -0,0 +1,25 @@
+set(ENV_PATH "$ENV{PATH}")
+foreach(path "/does_not_exist" "/include" "")
+  unset(PATH_IN_ENV_PATH CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h)
+  message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+foreach(path "/does_not_exist" "/include" "")
+  unset(PATH_IN_ENV_PATH CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h)
+  message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'")
+endforeach()
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON)
+foreach(path "/does_not_exist" "/include" "")
+  unset(PATH_IN_ENV_PATH CACHE)
+  set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}")
+  find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h NO_SYSTEM_ENVIRONMENT_PATH)
+  message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'")
+endforeach()
+
+set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt
new file mode 100644
index 0000000..bb2ceb7
--- /dev/null
+++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt
new file mode 100644
index 0000000..bb2ceb7
--- /dev/null
+++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt
@@ -0,0 +1,4 @@
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
+-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt
index bb2ceb7..947a900 100644
--- a/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt
+++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt
@@ -1,4 +1,4 @@
 -- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
+-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/RunCMakeTest.cmake b/Tests/RunCMake/find_path/RunCMakeTest.cmake
index 3afbedc..ed55f51 100644
--- a/Tests/RunCMake/find_path/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_path/RunCMakeTest.cmake
@@ -1,9 +1,8 @@
 include(RunCMake)
 
 run_cmake(EmptyOldStyle)
-if(WIN32 OR CYGWIN)
-  run_cmake(PrefixInPATH)
-endif()
+run_cmake(FromPATHEnv)
+run_cmake(PrefixInPATH)
 
 if(APPLE)
   run_cmake(FrameworksWithSubdirs)
diff --git a/Tests/RunCMake/find_program/EnvAndHints-stdout.txt b/Tests/RunCMake/find_program/EnvAndHints-stdout.txt
index 39329b2..0051636 100644
--- a/Tests/RunCMake/find_program/EnvAndHints-stdout.txt
+++ b/Tests/RunCMake/find_program/EnvAndHints-stdout.txt
@@ -1 +1,4 @@
 -- PROG='[^']*/Tests/RunCMake/find_program/A/testAandB'
+-- PROG='PROG-NOTFOUND'
+-- PROG='[^']*/Tests/RunCMake/find_program/B/testAandB'
+-- PROG='[^']*/Tests/RunCMake/find_program/A/testAandB'
diff --git a/Tests/RunCMake/find_program/EnvAndHints.cmake b/Tests/RunCMake/find_program/EnvAndHints.cmake
index 14ebd6e..0f12eff 100644
--- a/Tests/RunCMake/find_program/EnvAndHints.cmake
+++ b/Tests/RunCMake/find_program/EnvAndHints.cmake
@@ -1,8 +1,31 @@
+
 set(ENV_PATH "$ENV{PATH}")
 set(ENV{PATH} ${CMAKE_CURRENT_SOURCE_DIR}/A)
 find_program(PROG
   NAMES testAandB
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
+
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+find_program(PROG
+  NAMES testAandB
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
+
+find_program(PROG
+  NAMES testAandB
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/B ${CMAKE_CURRENT_SOURCE_DIR}/A
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
+set(ENV{PATH} "${ENV_PATH}")
+
+find_program(PROG
+  NAMES testAandB
   HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A ${CMAKE_CURRENT_SOURCE_DIR}/B
   )
 message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
 set(ENV{PATH} "${ENV_PATH}")
diff --git a/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt b/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt
index cb3c99f..d2312e7 100644
--- a/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt
+++ b/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt
@@ -1,6 +1,8 @@
 -- PROG_ABS='PROG_ABS-NOTFOUND'
 -- PROG_ABS_NPD='PROG_ABS_NPD-NOTFOUND'
 -- PROG_CWD='PROG_CWD-NOTFOUND'
+-- PROG_CWD='PROG_CWD-NOTFOUND'
+-- PROG_CWD='[^']*/Tests/RunCMake/find_program/testCWD'
 -- PROG_CWD_NPD='PROG_CWD_NPD-NOTFOUND'
 -- PROG_CWD_DOT='[^']*/Tests/RunCMake/find_program/testCWD'
 -- PROG_CWD_DOT_NPD='[^']*/Tests/RunCMake/find_program/testCWD'
diff --git a/Tests/RunCMake/find_program/RelAndAbsPath.cmake b/Tests/RunCMake/find_program/RelAndAbsPath.cmake
index 9a42c5e..6b61980 100644
--- a/Tests/RunCMake/find_program/RelAndAbsPath.cmake
+++ b/Tests/RunCMake/find_program/RelAndAbsPath.cmake
@@ -10,7 +10,6 @@
 
 strip_windows_path_prefix("${CMAKE_CURRENT_SOURCE_DIR}" srcdir)
 
-file(MAKE_DIRECTORY "tmp${srcdir}")
 configure_file(testCWD "tmp${srcdir}/testNoSuchFile" COPYONLY)
 
 find_program(PROG_ABS
@@ -38,6 +37,28 @@
   )
 message(STATUS "PROG_CWD='${PROG_CWD}'")
 
+
+set(CMAKE_PREFIX_PATH ".")
+# On some platforms / dashboards the current working
+# directory can be in PATH or other search locations
+# so disable all searching to make sure this fails
+set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
+set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+find_program(PROG_CWD
+  NAMES testCWD
+  )
+message(STATUS "PROG_CWD='${PROG_CWD}'")
+
+set(CMAKE_PREFIX_PATH ".")
+set(CMAKE_FIND_USE_CMAKE_PATH ON)
+find_program(PROG_CWD
+  NAMES testCWD
+  )
+message(STATUS "PROG_CWD='${PROG_CWD}'")
+
 find_program(PROG_CWD_NPD
   NAMES testCWD
   NAMES_PER_DIR
diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt
index 6dd8cdf..c7e99ad 100644
--- a/Tests/RunCMake/install/CMakeLists.txt
+++ b/Tests/RunCMake/install/CMakeLists.txt
@@ -1,3 +1,6 @@
 cmake_minimum_required(VERSION 3.4)
+if(RunCMake_TEST MATCHES "^file-GET_RUNTIME_DEPENDENCIES")
+  cmake_policy(SET CMP0087 NEW)
+endif()
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index c637db1..21c320b 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -48,6 +48,22 @@
   endif()
 endfunction()
 
+# Wrapper for run_cmake() that skips platforms that are non-ELF or have no RPATH support
+function(run_cmake_ELFRPATH_only case)
+  if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+    run_cmake(${case})
+  else()
+    # Sanity check against a platform known to be ELF-based
+    if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+      message(FATAL_ERROR "Expected platform Linux to advertize itself as ELF-based, but it did not.")
+    else()
+      message(STATUS "${case} - SKIPPED (No ELF-based platform found)")
+    endif()
+  endif()
+endfunction()
+
+run_cmake(TARGETS-FILE_RPATH_CHANGE-old_rpath)
+run_cmake_ELFRPATH_only(TARGETS-FILE_RPATH_CHANGE-new_rpath)
 run_cmake(DIRECTORY-MESSAGE_NEVER)
 run_cmake(DIRECTORY-PATTERN-MESSAGE_NEVER)
 run_cmake(DIRECTORY-message)
@@ -139,6 +155,36 @@
 run_install_test(TARGETS-RPATH)
 run_install_test(InstallRequiredSystemLibraries)
 
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
+  if(NOT CMAKE_C_COMPILER_ID MATCHES "^XL")
+    run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux)
+  endif()
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-unresolved)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-conflict)
+  run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-notfile)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-project)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1)
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2)
+else()
+  run_cmake(file-GET_RUNTIME_DEPENDENCIES-unsupported)
+endif()
+
 set(run_install_test_components 1)
 run_install_test(FILES-EXCLUDE_FROM_ALL)
 run_install_test(TARGETS-EXCLUDE_FROM_ALL)
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake
new file mode 100644
index 0000000..673fdde
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake
@@ -0,0 +1,30 @@
+file(READ ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake install_script)
+#message(STATUS ${install_script})
+
+set(wsnl " *[\n\r]+ *") # whitespace + single newline + whitespace
+set(wssl " *[\n\r]+[^\n\r]*[\n\r]+ *") # ws nl skipline nl ws
+string(CONCAT prefix [[file\(RPATH_CHANGE]])
+set(_msg "cmake_install.cmake does not match ")
+
+macro(check)
+  if(NOT install_script MATCHES "${regex}")
+    message(STATUS "${test} - check \"${target}\" - FAILED:")
+    string(CONCAT RunCMake_TEST_FAILED "${_msg}" ">>>${regex}<<<")
+    return()
+  else()
+    message(STATUS "${test} - check \"${target}\" - PASSED")
+  endif()
+endmacro()
+
+macro(skip_without_rpath_change_rule)
+# Not all platforms generate a file(RPATH_CHANGE) rule
+  if(NOT install_script MATCHES [[file\(RPATH_CHANGE]])
+    # Sanity check against a platform known to generate a file(RPATH_CHANGE) rule
+    if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+      message(FATAL_ERROR "Expected generated file(RPATH_CHANGE) rule on platform Linux.")
+    else()
+      message(STATUS "${test} - All checks skipped. No file(RPATH_CHANGE) rule found on this platform.")
+      return()
+    endif()
+  endif()
+endmacro()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake
new file mode 100644
index 0000000..930ef70
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake
@@ -0,0 +1,63 @@
+include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake)
+skip_without_rpath_change_rule()
+string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]])
+
+set(target "exe1_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe1_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe1_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar]])
+check()
+
+set(target "exe2_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\$ORIGIN/../lib]])
+check()
+
+set(target "exe2_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\$ORIGIN/../lib]])
+check()
+
+set(target "exe2_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\\\$ORIGIN/../lib]])
+check()
+
+set(target "exe3_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\${ORIGIN}/../lib]])
+check()
+
+set(target "exe3_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\${ORIGIN}/../lib]])
+check()
+
+set(target "exe3_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "\\\${ORIGIN}/../lib]])
+check()
+
+set(target "exe4_cmp0095_old")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\${PLATFORM}]])
+check()
+
+set(target "exe4_cmp0095_warn")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\${PLATFORM}]])
+check()
+
+set(target "exe4_cmp0095_new")
+string(CONCAT regex "${prefix}${target}\"${wssl}"
+              [[NEW_RPATH "/foo/bar/\\\${PLATFORM}]])
+check()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt
new file mode 100644
index 0000000..1e123f6
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt
@@ -0,0 +1,23 @@
+^CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\):
+  Policy CMP0095 is not set: RPATH entries are properly escaped in the
+  intermediary CMake install script\.  Run "cmake --help-policy CMP0095" for
+  policy details\.  Use the cmake_policy command to set the policy and
+  suppress this warning\.
+
+  RPATH entries for target 'exe3_cmp0095_warn' will not be escaped in the
+  intermediary cmake_install\.cmake script\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\):
+  Policy CMP0095 is not set: RPATH entries are properly escaped in the
+  intermediary CMake install script\.  Run "cmake --help-policy CMP0095" for
+  policy details\.  Use the cmake_policy command to set the policy and
+  suppress this warning\.
+
+  RPATH entries for target 'exe4_cmp0095_warn' will not be escaped in the
+  intermediary cmake_install\.cmake script\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
new file mode 100644
index 0000000..cba04b2
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.14)
+enable_language(C)
+
+# test matrix
+#
+# A :=
+#                        | no cmake syntax | cmake syntax |
+# -----------------------+-----------------+--------------+
+# absolute install RPATH |       exe1      |     exe4     |
+# relative install RPATH |       exe2      |     exe3     |
+#
+# all := A * CMP005_OLD + A * CMP0095_WARN + A * CMP0095_NEW
+
+add_library(utils SHARED obj1.c)
+set(targets utils)
+
+set(exe1_install_rpath "/foo/bar")
+set(exe2_install_rpath "\$ORIGIN/../lib")
+set(exe3_install_rpath "\${ORIGIN}/../lib")
+set(exe4_install_rpath "/foo/bar/\${PLATFORM}")
+
+macro(A_CMP0095 policy_value)
+  cmake_policy(PUSH)
+  if(NOT "x${policy_value}x" STREQUAL "xWARNx")
+    cmake_policy(SET CMP0095 ${policy_value})
+  endif()
+  string(TOLOWER "${policy_value}" p)
+
+  # exe1: absolute install RPATH, no cmake syntax
+  set(case "exe1")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe2: relative install RPATH, no cmake syntax
+  set(case "exe2")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe3: relative install RPATH, cmake syntax
+  set(case "exe3")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  # exe4: absolute install RPATH, cmake syntax
+  set(case "exe4")
+  set(target "${case}_cmp0095_${p}")
+  list(APPEND targets ${target})
+  add_executable(${target} main.c)
+  target_link_libraries(${target} PRIVATE utils)
+  set_target_properties(${target} PROPERTIES
+    INSTALL_RPATH "${${case}_install_rpath}")
+
+  cmake_policy(POP)
+endmacro()
+
+A_CMP0095("OLD")
+A_CMP0095("WARN") # exe3 and exe4 are expected to issue an author warning
+A_CMP0095("NEW")
+
+install(TARGETS ${targets})
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake
new file mode 100644
index 0000000..814f405
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake
@@ -0,0 +1,15 @@
+include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake)
+skip_without_rpath_change_rule()
+string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]])
+
+set(target "exe1")
+string(CONCAT regex "${prefix}${target}\"${wsnl}"
+              [[OLD_RPATH "]] "${RunCMake_BINARY_DIR}")
+check()
+
+if("x${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}" STREQUAL "x\$ORIGIN")
+  set(target "exe2")
+  string(CONCAT regex "${prefix}${target}\"${wsnl}"
+                [[OLD_RPATH "\\\$ORIGIN]])
+  check()
+endif()
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
new file mode 100644
index 0000000..43ae787
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.14)
+enable_language(C)
+
+add_library(utils SHARED obj1.c)
+
+# exe1: absolute build RPATH, no cmake syntax
+set(CMAKE_BUILD_RPATH_USE_ORIGIN OFF)
+set(CMAKE_INSTALL_RPATH "/foo/bar")
+add_executable(exe1 main.c)
+target_link_libraries(exe1 PRIVATE utils)
+
+# exe2: relative build RPATH, no cmake syntax
+set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
+set(CMAKE_INSTALL_RPATH "/foo/bar")
+add_executable(exe2 main.c)
+target_link_libraries(exe2 PRIVATE utils)
+
+install(TARGETS utils exe1 exe2)
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt
new file mode 100644
index 0000000..b66d1fe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\):
+  file Unrecognized argument: "invalid"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake
new file mode 100644
index 0000000..f3b8ce4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES invalid)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt
new file mode 100644
index 0000000..94f0f46
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
+
+CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\):
+  file Keyword missing value: BUNDLE_EXECUTABLE
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake
new file mode 100644
index 0000000..138ab95
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES BUNDLE_EXECUTABLE)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake
new file mode 100644
index 0000000..ab630f0
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake
@@ -0,0 +1,44 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_rpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_runpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath/librpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_parent/librpath_parent\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search/librpath_search\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath/librunpath\.so]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search/librunpath_search\.so]]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+check_contents(deps/deps2.txt "^${_check}$")
+check_contents(deps/deps3.txt "^${_check}$")
+set(_check
+  [[librpath_unresolved\.so]]
+  [[librunpath_parent_unresolved\.so]]
+  [[librunpath_unresolved\.so]]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+check_contents(deps/udeps2.txt "^${_check}$")
+check_contents(deps/udeps3.txt "^${_check}$")
+set(_check
+  "^libconflict\\.so:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict/libconflict\\.so;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict2/libconflict\\.so\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt
new file mode 100644
index 0000000..123ae48
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt
@@ -0,0 +1,119 @@
+^CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search_postexcluded\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)
+
+*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\):
+  Dependency librunpath_search\.so found in search directory:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search
+
+  See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\.
+Call Stack \(most recent call first\):
+  cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt
new file mode 100644
index 0000000..1692348
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for librpath\.so:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath1/librpath\.so
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath2/librpath\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake
new file mode 100644
index 0000000..f719499
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake
@@ -0,0 +1,54 @@
+enable_language(C)
+
+set(test1_names rpath)
+set(test2_names rpath)
+
+file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n")
+add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c")
+install(TARGETS rpath DESTINATION lib/rpath1)
+install(TARGETS rpath DESTINATION lib/rpath2)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+set_property(TARGET test1 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath1"
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+set_property(TARGET test2 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath2"
+  )
+
+install(TARGETS test1 test2 DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^librpath\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt
new file mode 100644
index 0000000..83a87c9
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run objdump on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-build/root-all/bin/\.\./lib/libtest\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake
new file mode 100644
index 0000000..6567438
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake
@@ -0,0 +1,30 @@
+enable_language(C)
+cmake_policy(SET CMP0095 NEW)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+set_property(TARGET exe PROPERTY INSTALL_RPATH "\${ORIGIN}/../lib")
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^libtest\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt
new file mode 100644
index 0000000..eaca512
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file libunresolved\.so$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake
new file mode 100644
index 0000000..3efa305
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^libunresolved\\.so$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake
new file mode 100644
index 0000000..bd0f9f1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake
@@ -0,0 +1,169 @@
+enable_language(C)
+cmake_policy(SET CMP0095 NEW)
+
+set(test_rpath_names
+  preexcluded
+  rpath_postexcluded
+  rpath
+  rpath_parent_postexcluded
+  rpath_parent
+  rpath_origin_postexcluded
+  rpath_origin
+  rpath_search_postexcluded
+  rpath_search
+  rpath_unresolved
+  conflict
+  )
+set(test_runpath_names
+  runpath_postexcluded
+  runpath
+  runpath_origin_postexcluded
+  runpath_origin
+  runpath_parent_unresolved
+  runpath_search_postexcluded
+  runpath_search
+  runpath_unresolved
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test_rpath.c")
+add_library(test_rpath SHARED "${CMAKE_BINARY_DIR}/test_rpath.c")
+foreach(name ${test_rpath_names})
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "void test_rpath(void)\n{\n")
+foreach(name ${test_rpath_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "}\n")
+
+install(TARGETS rpath_postexcluded DESTINATION lib/rpath_postexcluded)
+install(TARGETS rpath DESTINATION lib/rpath)
+install(TARGETS rpath_origin_postexcluded DESTINATION lib/rpath_origin_postexcluded)
+install(TARGETS rpath_origin DESTINATION lib/rpath_origin)
+install(TARGETS rpath_parent_postexcluded DESTINATION lib/rpath_parent_postexcluded)
+install(TARGETS rpath rpath_origin rpath_parent DESTINATION lib/rpath_parent)
+install(TARGETS rpath_search_postexcluded DESTINATION lib/rpath_search_postexcluded)
+install(TARGETS rpath rpath_origin rpath_parent rpath_search DESTINATION lib/rpath_search)
+install(TARGETS conflict DESTINATION lib/conflict)
+
+target_link_libraries(test_rpath PRIVATE ${test_rpath_names})
+set_property(TARGET test_rpath PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath"
+  "\$ORIGIN/rpath_origin_postexcluded"
+  "\${ORIGIN}/rpath_origin"
+  "${CMAKE_BINARY_DIR}/root-all/lib/conflict"
+  )
+target_link_options(test_rpath PRIVATE -Wl,--disable-new-dtags)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test_runpath.c")
+add_library(test_runpath SHARED "${CMAKE_BINARY_DIR}/test_runpath.c")
+foreach(name ${test_runpath_names} rpath conflict)
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  if(NOT name MATCHES "^(rpath|conflict)$")
+    add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+  endif()
+
+  file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "void test_runpath(void)\n{\n")
+foreach(name ${test_runpath_names} rpath conflict)
+  file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "}\n")
+
+install(TARGETS runpath_postexcluded DESTINATION lib/runpath_postexcluded)
+install(TARGETS runpath DESTINATION lib/runpath)
+install(TARGETS runpath_origin_postexcluded DESTINATION lib/runpath_origin_postexcluded)
+install(TARGETS runpath_origin DESTINATION lib/runpath_origin)
+install(TARGETS runpath_parent_unresolved DESTINATION lib/runpath_parent_unresolved)
+install(TARGETS runpath_search_postexcluded DESTINATION lib/runpath_search_postexcluded)
+install(TARGETS runpath runpath_origin runpath_search DESTINATION lib/runpath_search)
+install(TARGETS conflict DESTINATION lib/conflict2)
+
+target_link_libraries(test_runpath PRIVATE ${test_runpath_names} rpath conflict)
+set_property(TARGET test_runpath PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath/../rpath" # Ensure that files that don't conflict are treated correctly
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath"
+  "\${ORIGIN}/runpath_origin_postexcluded"
+  "\$ORIGIN/runpath_origin"
+  "${CMAKE_BINARY_DIR}/root-all/lib/conflict2"
+  )
+target_link_options(test_runpath PRIVATE -Wl,--enable-new-dtags)
+
+set_property(TARGET test_rpath ${test_rpath_names} test_runpath ${test_runpath_names} PROPERTY LIBRARY_OUTPUT_DIRECTORY lib)
+install(TARGETS test_rpath test_runpath DESTINATION lib)
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c)
+target_link_libraries(topexe PRIVATE test_rpath test_runpath)
+target_link_libraries(toplib PRIVATE test_rpath test_runpath)
+target_link_libraries(topmod PRIVATE test_rpath test_runpath)
+set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent"
+  "${CMAKE_BINARY_DIR}/root-all/lib/runpath_parent_unresolved"
+  )
+target_link_options(topexe PRIVATE -Wl,--disable-new-dtags)
+target_link_options(toplib PRIVATE -Wl,--disable-new-dtags)
+target_link_options(topmod PRIVATE -Wl,--disable-new-dtags)
+
+install(TARGETS topexe toplib RUNTIME DESTINATION bin LIBRARY DESTINATION lib)
+install(TARGETS topmod LIBRARY DESTINATION lib/modules)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES
+        "^lib(test_rpath|rpath_postexcluded|rpath|rpath_parent_postexcluded|rpath_parent|rpath_origin_postexcluded|rpath_origin|rpath_search_postexcluded|rpath_search|rpath_unresolved|test_runpath|runpath_postexcluded|runpath|runpath_origin_postexcluded|runpath_origin|runpath_parent_unresolved|runpath_search_postexcluded|runpath_search|runpath_unresolved|conflict)\\.so$"
+        "^libc\\.so"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES "^.*/(libtest_rpath|rpath/librpath|rpath_parent/librpath_parent|rpath_search/librpath_search|libtest_runpath|runpath/librunpath|runpath_origin_postexcluded|runpath_origin|runpath_search/librunpath_search|conflict2?/libconflict)\\.so$"
+      POST_EXCLUDE_REGEXES ".*"
+      DIRECTORIES
+        "${CMAKE_INSTALL_PREFIX}/lib/rpath_search_postexcluded"
+        "${CMAKE_INSTALL_PREFIX}/lib/rpath_search"
+        "${CMAKE_INSTALL_PREFIX}/lib/runpath_search_postexcluded"
+        "${CMAKE_INSTALL_PREFIX}/lib/runpath_search"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:toplib>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/lib/modules/$<TARGET_FILE_NAME:topmod>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c
new file mode 100644
index 0000000..d196afe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c
@@ -0,0 +1,9 @@
+extern void test_rpath(void);
+extern void test_runpath(void);
+
+int main(void)
+{
+  test_rpath();
+  test_runpath();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c
new file mode 100644
index 0000000..040e591
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c
@@ -0,0 +1,8 @@
+extern void test_rpath(void);
+extern void test_runpath(void);
+
+void toplib(void)
+{
+  test_rpath();
+  test_runpath();
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake
new file mode 100644
index 0000000..4d6dde1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake
@@ -0,0 +1,157 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps2.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_executable_path_postexcluded\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps2.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps3.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_executable_path_postexcluded\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps3.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps4.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps4.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps5.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps5.txt "^${_check}$")
+
+set(_check
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]]
+  [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]]
+  [[/usr/lib/libSystem\.B\.dylib]]
+  )
+check_contents(deps/deps6.txt "^${_check}$")
+
+set(_check
+  [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]]
+  [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]]
+  [[@rpath/librpath_executable_path\.dylib]]
+  [[@rpath/librpath_executable_path_bundle\.dylib]]
+  [[@rpath/librpath_loader_path_unresolved\.dylib]]
+  [[@rpath/librpath_unresolved\.dylib]]
+  )
+check_contents(deps/udeps6.txt "^${_check}$")
+
+set(_check
+  "^libconflict\\.dylib:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict/libconflict\\.dylib;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict2/libconflict\\.dylib\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
+check_contents(deps/cdeps4.txt "${_check}")
+check_contents(deps/cdeps5.txt "${_check}")
+check_contents(deps/cdeps6.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt
new file mode 100644
index 0000000..bc9e97a
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for librpath\.dylib:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath1/librpath\.dylib
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath2/librpath\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake
new file mode 100644
index 0000000..a8446fe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake
@@ -0,0 +1,55 @@
+enable_language(C)
+
+set(test1_names rpath)
+set(test2_names rpath)
+
+file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n")
+add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c")
+set_property(TARGET rpath PROPERTY INSTALL_NAME_DIR @rpath)
+install(TARGETS rpath DESTINATION lib/rpath1)
+install(TARGETS rpath DESTINATION lib/rpath2)
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+set_property(TARGET test1 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath1"
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+set_property(TARGET test2 PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/lib/rpath2"
+  )
+
+install(TARGETS test1 test2 DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^@rpath/librpath\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt
new file mode 100644
index 0000000..73ab9f1
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run otool on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-build/root-all/bin/\.\./lib/libtest\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake
new file mode 100644
index 0000000..3e4c434
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake
@@ -0,0 +1,30 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+set_property(TARGET test PROPERTY INSTALL_NAME_DIR @rpath)
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+set_property(TARGET exe PROPERTY INSTALL_RPATH "@loader_path/../lib")
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^@rpath/libtest\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt
new file mode 100644
index 0000000..01762b4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file @rpath/libunresolved\.dylib$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake
new file mode 100644
index 0000000..c9b6c95
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^@rpath/libunresolved\\.dylib$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake
new file mode 100644
index 0000000..6db05b3
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake
@@ -0,0 +1,216 @@
+enable_language(C)
+
+set(testlib_names
+  preexcluded
+  executable_path
+  executable_path_bundle
+  executable_path_postexcluded
+  loader_path
+  loader_path_unresolved
+  loader_path_postexcluded
+  rpath
+  rpath_unresolved
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_unresolved
+  rpath_loader_path_postexcluded
+  normal
+  normal_unresolved
+  normal_postexcluded
+  conflict
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+foreach(name ${testlib_names})
+  if(name STREQUAL "normal")
+    file(WRITE "${CMAKE_BINARY_DIR}/normal.c" "extern void rpath(void);\nvoid normal(void)\n{\n  rpath();\n}\n")
+  else()
+    file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n")
+  endif()
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "void testlib(void)\n{\n")
+foreach(name ${testlib_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n")
+set_property(TARGET ${testlib_names} PROPERTY BUILD_WITH_INSTALL_NAME_DIR 1)
+target_link_libraries(normal PRIVATE rpath)
+set_property(TARGET normal PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/normal/../rpath"
+  )
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "extern void conflict(void);\nvoid testlib_conflict(void)\n{\n  conflict();\n}\n")
+add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c")
+target_link_libraries(testlib_conflict PRIVATE conflict)
+
+set_property(TARGET testlib PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_unresolved"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_postexcluded"
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict"
+  @executable_path/../lib/rpath_executable_path
+  @executable_path/../lib/rpath_executable_path_unresolved
+  @executable_path/../lib/rpath_executable_path_postexcluded
+  @loader_path/rpath_loader_path
+  @loader_path/rpath_loader_path_unresolved
+  @loader_path/rpath_loader_path_postexcluded
+  )
+set_property(TARGET testlib_conflict PROPERTY INSTALL_RPATH
+  "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict2"
+  )
+
+foreach(t
+  executable_path
+  executable_path_postexcluded
+  loader_path
+  loader_path_postexcluded
+  rpath
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_postexcluded
+  conflict
+  )
+  install(TARGETS ${t} DESTINATION executable/lib/${t})
+endforeach()
+install(TARGETS conflict DESTINATION executable/lib/conflict2)
+
+foreach(t
+  executable_path_bundle
+  executable_path_postexcluded
+  loader_path_postexcluded
+  rpath_postexcluded
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path_postexcluded
+  )
+  install(TARGETS ${t} DESTINATION bundle_executable/lib/${t})
+endforeach()
+
+foreach(t executable_path executable_path_bundle executable_path_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @executable_path/../lib/${t})
+endforeach()
+
+foreach(t loader_path loader_path_unresolved loader_path_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @loader_path/${t})
+endforeach()
+
+foreach(t
+  rpath
+  rpath_unresolved
+  rpath_postexcluded
+  rpath_executable_path
+  rpath_executable_path_bundle
+  rpath_executable_path_postexcluded
+  rpath_loader_path
+  rpath_loader_path_unresolved
+  rpath_loader_path_postexcluded
+  conflict
+  )
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @rpath)
+endforeach()
+
+foreach(t normal normal_unresolved normal_postexcluded)
+  set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR "${CMAKE_BINARY_DIR}/root-all/executable/lib/${t}")
+  if(NOT t STREQUAL "normal_unresolved")
+    install(TARGETS ${t} DESTINATION executable/lib/${t})
+  endif()
+endforeach()
+
+target_link_libraries(testlib PRIVATE ${testlib_names})
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c)
+target_link_libraries(topexe PRIVATE testlib)
+target_link_libraries(toplib PRIVATE testlib)
+target_link_libraries(topmod PRIVATE testlib)
+
+set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH "${CMAKE_BINARY_DIR}/root-all/executable/lib")
+
+install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION executable/bin LIBRARY DESTINATION executable/lib)
+install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION bundle_executable/bin LIBRARY DESTINATION bundle_executable/lib)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|executable_path_postexcluded|loader_path|loader_path_unresolved|loader_path_postexcluded|rpath|rpath_unresolved|rpath_postexcluded|rpath_executable_path|rpath_executable_path_bundle|rpath_executable_path_postexcluded|rpath_loader_path|rpath_loader_path_unresolved|rpath_loader_path_postexcluded|normal|normal_unresolved|normal_postexcluded|conflict|System\\.B)\\.dylib$"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|loader_path|rpath|rpath_executable_path|rpath_executable_path_bundle|rpath_loader_path|normal|conflict|System\\.B)\\.dylib$"
+      POST_EXCLUDE_REGEXES ".*"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps4.txt udeps4.txt cdeps4.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE
+      "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps5.txt udeps5.txt cdeps5.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps6.txt udeps6.txt cdeps6.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>"
+    BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c
new file mode 100644
index 0000000..20c6087
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c
@@ -0,0 +1,7 @@
+extern void testlib(void);
+
+int main(void)
+{
+  testlib();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c
new file mode 100644
index 0000000..cff1bff
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c
@@ -0,0 +1,6 @@
+extern void testlib(void);
+
+void toplib(void)
+{
+  testlib();
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt
new file mode 100644
index 0000000..d506645
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt
@@ -0,0 +1,13 @@
+^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-project\.cmake:[0-9]+ \(file\):
+  You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\.  This is
+  probably not what you intended to do\.  Instead, please consider using it in
+  an install\(CODE\) or install\(SCRIPT\) command\.  For example:
+
+    install\(CODE \[\[
+      file\(GET_RUNTIME_DEPENDENCIES
+        # \.\.\.
+        \)
+      ]]\)
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake
new file mode 100644
index 0000000..842d7ab
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake
@@ -0,0 +1 @@
+file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps)
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt
new file mode 100644
index 0000000..3db835c
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at file-GET_RUNTIME_DEPENDENCIES-unsupported\.cmake:[0-9]+ \(file\):
+  file GET_RUNTIME_DEPENDENCIES is not supported on system "[^
+ ]+"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake
new file mode 100644
index 0000000..b91eefe
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake
@@ -0,0 +1,2 @@
+file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps)
+message(FATAL_ERROR "This message should not be displayed")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake
new file mode 100644
index 0000000..c120ce4
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake
@@ -0,0 +1,38 @@
+function(check_contents filename contents_regex)
+  if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}")
+    file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents)
+    if(NOT contents MATCHES "${contents_regex}")
+      string(APPEND RunCMake_TEST_FAILED "File contents:
+  ${contents}
+do not match what we expected:
+  ${contents_regex}
+in file:
+  ${CMAKE_INSTALL_PREFIX}/${filename}\n")
+      set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(_check
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=]
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.search/(lib)?search\.dll]=]
+  [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?testlib\.dll]=]
+  )
+check_contents(deps/deps1.txt "^${_check}$")
+check_contents(deps/deps2.txt "^${_check}$")
+check_contents(deps/deps3.txt "^${_check}$")
+set(_check
+  [=[(lib)?unresolved\.dll]=]
+  )
+check_contents(deps/udeps1.txt "^${_check}$")
+check_contents(deps/udeps2.txt "^${_check}$")
+check_contents(deps/udeps3.txt "^${_check}$")
+set(_check
+  "^(lib)?conflict\\.dll:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?conflict\\.dll\n$"
+  )
+check_contents(deps/cdeps1.txt "${_check}")
+check_contents(deps/cdeps2.txt "${_check}")
+check_contents(deps/cdeps3.txt "${_check}")
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt
new file mode 100644
index 0000000..66ecb93
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for (lib)?path\.dll:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test1/(lib)?path\.dll
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test2/(lib)?path\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake
new file mode 100644
index 0000000..d413443
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake
@@ -0,0 +1,47 @@
+enable_language(C)
+
+set(test1_names path)
+set(test2_names path)
+
+file(WRITE "${CMAKE_BINARY_DIR}/path.c" "__declspec(dllexport) void path(void) {}\n")
+add_library(path SHARED "${CMAKE_BINARY_DIR}/path.c")
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test1.c")
+add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllexport) void test1(void)\n{\n")
+foreach(name ${test1_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n")
+
+target_link_libraries(test1 PRIVATE ${test1_names})
+
+file(REMOVE "${CMAKE_BINARY_DIR}/test2.c")
+add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllexport) void test2(void)\n{\n")
+foreach(name ${test2_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n")
+
+target_link_libraries(test2 PRIVATE ${test2_names})
+
+install(TARGETS test1 path DESTINATION lib/test1)
+install(TARGETS test2 path DESTINATION lib/test2)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/test1/$<TARGET_FILE_NAME:test1>"
+      "${CMAKE_INSTALL_PREFIX}/lib/test2/$<TARGET_FILE_NAME:test2>"
+    PRE_INCLUDE_REGEXES "^(lib)?path\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt
new file mode 100644
index 0000000..f921409
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Failed to run (dumpbin|objdump) on:
+
+    [^
+]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-build/root-all/bin/(lib)?test\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake
new file mode 100644
index 0000000..6665a3b
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake
@@ -0,0 +1,28 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/test.c" "__declspec(dllexport) void test(void) {}\n")
+file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[__declspec(dllimport) extern void test(void);
+
+int main(void)
+{
+  test();
+  return 0;
+}
+]])
+
+add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c")
+add_executable(exe "${CMAKE_BINARY_DIR}/main.c")
+target_link_libraries(exe PRIVATE test)
+
+install(TARGETS exe DESTINATION bin)
+
+install(CODE [[
+  file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:test>")
+  file(GET_RUNTIME_DEPENDENCIES
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>"
+    PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt
new file mode 100644
index 0000000..a20654c
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve file (lib)?unresolved\.dll$
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake
new file mode 100644
index 0000000..4cc74c7
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void unresolved(void);\n__declspec(dllexport) void testlib(void)\n{\n  unresolved();\n}\n")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "__declspec(dllexport) void unresolved(void) {}\n")
+add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c")
+target_link_libraries(testlib PRIVATE unresolved)
+install(TARGETS testlib DESTINATION lib)
+
+install(CODE [[
+  file(GET_RUNTIME_DEPENDENCIES
+    PRE_INCLUDE_REGEXES "^(lib)?unresolved\\.dll$"
+    PRE_EXCLUDE_REGEXES ".*"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>"
+    )
+  message(FATAL_ERROR "This message should not be displayed")
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake
new file mode 100644
index 0000000..19288d8
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake
@@ -0,0 +1,114 @@
+enable_language(C)
+
+set(testlib_names
+  preexcluded
+  libdir_postexcluded
+  libdir
+  search_postexcluded
+  search
+  unresolved
+  conflict
+  )
+
+file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c")
+add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c")
+foreach(name ${testlib_names})
+  file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "__declspec(dllexport) void ${name}(void) {}\n")
+  add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c")
+
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void ${name}(void);\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllexport) void testlib(void)\n{\n")
+foreach(name ${testlib_names})
+  file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "  ${name}();\n")
+endforeach()
+file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n")
+
+target_link_libraries(testlib PRIVATE ${testlib_names})
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "__declspec(dllimport) extern void conflict(void);\n__declspec(dllexport) void testlib_conflict(void)\n{\n  conflict();\n}\n")
+add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c")
+target_link_libraries(testlib_conflict PRIVATE conflict)
+
+file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) extern void libdir(void);\n__declspec(dllexport) void testlib_noconflict(void)\n{\n  libdir();\n}\n")
+add_library(testlib_noconflict SHARED "${CMAKE_BINARY_DIR}/testlib_noconflict.c")
+target_link_libraries(testlib_noconflict PRIVATE libdir)
+
+install(TARGETS testlib libdir_postexcluded libdir conflict testlib_noconflict DESTINATION bin)
+install(TARGETS libdir search_postexcluded search DESTINATION bin/.search) # Prefixing with "." ensures it is the first item after list(SORT)
+install(TARGETS testlib_conflict conflict DESTINATION bin/.conflict)
+
+add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c)
+add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c)
+add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c)
+target_link_libraries(topexe PRIVATE testlib)
+target_link_libraries(toplib PRIVATE testlib)
+target_link_libraries(topmod PRIVATE testlib)
+
+install(TARGETS topexe toplib topmod DESTINATION bin)
+
+install(CODE [[
+  function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile)
+    file(GET_RUNTIME_DEPENDENCIES
+      RESOLVED_DEPENDENCIES_VAR deps
+      UNRESOLVED_DEPENDENCIES_VAR udeps
+      CONFLICTING_DEPENDENCIES_PREFIX cdeps
+      PRE_INCLUDE_REGEXES
+        "^(lib)?testlib\\.dll$"
+        "^(lib)?libdir_postexcluded\\.dll$"
+        "^(lib)?libdir\\.dll$"
+        "^(lib)?search_postexcluded\\.dll$"
+        "^(lib)?search\\.dll$"
+        "^(lib)?unresolved\\.dll$"
+        "^(lib)?conflict\\.dll$"
+        "^kernel32\\.dll$"
+      PRE_EXCLUDE_REGEXES ".*"
+      POST_INCLUDE_REGEXES
+        "^.*/(lib)?testlib\\.dll$"
+        "^.*/(lib)?libdir\\.dll$"
+        "^.*/(lib)?search\\.dll$"
+        "^.*/(lib)?conflict\\.dll$"
+      POST_EXCLUDE_REGEXES ".*"
+      DIRECTORIES
+        "${CMAKE_INSTALL_PREFIX}/bin/.search"
+      ${ARGN}
+      )
+    list(SORT deps)
+    list(SORT udeps)
+    list(SORT cdeps_FILENAMES)
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}")
+    file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "")
+    foreach(cdep IN LISTS cdeps_FILENAMES)
+      set(cdep_values ${cdeps_${cdep}})
+      list(SORT cdep_values)
+      file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n")
+    endforeach()
+  endfunction()
+
+  exec_get_runtime_dependencies(
+    deps1.txt udeps1.txt cdeps1.txt
+    EXECUTABLES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps2.txt udeps2.txt cdeps2.txt
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:toplib>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+
+  exec_get_runtime_dependencies(
+    deps3.txt udeps3.txt cdeps3.txt
+    MODULES
+      "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topmod>"
+    LIBRARIES
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>"
+      "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>"
+    )
+  ]])
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c
new file mode 100644
index 0000000..713b8eb
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c
@@ -0,0 +1,7 @@
+__declspec(dllimport) extern void testlib(void);
+
+int main(void)
+{
+  testlib();
+  return 0;
+}
diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c
new file mode 100644
index 0000000..6997175
--- /dev/null
+++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c
@@ -0,0 +1,6 @@
+__declspec(dllimport) extern void testlib(void);
+
+__declspec(dllexport) void toplib(void)
+{
+  testlib();
+}
diff --git a/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt b/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt
index 9e73ed5..22226f2 100644
--- a/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt
+++ b/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt
@@ -1,5 +1,5 @@
 ^CMake Error at MATH-InvalidExpression.cmake:1 \(math\):
-  math cannot parse the expression: "INVALID": syntax error, unexpected \$end,
-  expecting exp_PLUS or exp_MINUS or exp_OPENPARENT or exp_NUMBER \(7\).
+  math cannot parse the expression: "INVALID": syntax error, unexpected \$end
+  \(7\).
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/message/RunCMakeTest.cmake b/Tests/RunCMake/message/RunCMakeTest.cmake
index cecfc7f..9198a25 100644
--- a/Tests/RunCMake/message/RunCMakeTest.cmake
+++ b/Tests/RunCMake/message/RunCMakeTest.cmake
@@ -11,44 +11,55 @@
 run_cmake(errormessage_deprecated)
 run_cmake(errormessage_dev)
 
-run_cmake_command(
-    message-loglevel-invalid
-    ${CMAKE_COMMAND} --loglevel=blah -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
+foreach(opt IN ITEMS loglevel log-level)
+  run_cmake_command(
+      message-${opt}-invalid
+      ${CMAKE_COMMAND} --${opt}=blah -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
 
-# Checking various combinations of `message(...)` and log levels `WARNING` to `TRACE`
-# - no CLI option -> `WARNING` to `STATUS` output
+  # Checking various combinations of `message(...)` and log levels `WARNING` to `TRACE`
+  # - no CLI option -> `WARNING` to `STATUS` output
+  run_cmake_command(
+      message-${opt}-default
+      ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - Only `WARNING` output
+  run_cmake_command(
+      message-${opt}-warning
+      ${CMAKE_COMMAND} --${opt}=warning -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - Only `WARNING` and `NOTICE` output
+  run_cmake_command(
+      message-${opt}-notice
+      ${CMAKE_COMMAND} --${opt}=notice -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - `WARNING` to `STATUS` output
+  run_cmake_command(
+      message-${opt}-status
+      ${CMAKE_COMMAND} --${opt}=status -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - `WARNING` to `VERBOSE` output
+  run_cmake_command(
+      message-${opt}-verbose
+      ${CMAKE_COMMAND} --${opt}=verbose -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - `WARNING` to `DEBUG` output
+  run_cmake_command(
+      message-${opt}-debug
+      ${CMAKE_COMMAND} --${opt}=debug -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+  # - `WARNING` to `TRACE` output
+  run_cmake_command(
+      message-${opt}-trace
+      ${CMAKE_COMMAND} --${opt}=trace -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    )
+endforeach()
+
 run_cmake_command(
-    message-loglevel-default
-    ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    message-indent
+    ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-indent.cmake
   )
-# - Only `WARNING` output
 run_cmake_command(
-    message-loglevel-warning
-    ${CMAKE_COMMAND} --loglevel=warning -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
-# - Only `WARNING` and `NOTICE` output
-run_cmake_command(
-    message-loglevel-notice
-    ${CMAKE_COMMAND} --loglevel=notice -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
-# - `WARNING` to `STATUS` output
-run_cmake_command(
-    message-loglevel-status
-    ${CMAKE_COMMAND} --loglevel=status -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
-# - `WARNING` to `VERBOSE` output
-run_cmake_command(
-    message-loglevel-verbose
-    ${CMAKE_COMMAND} --loglevel=verbose -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
-# - `WARNING` to `DEBUG` output
-run_cmake_command(
-    message-loglevel-debug
-    ${CMAKE_COMMAND} --loglevel=debug -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
-  )
-# - `WARNING` to `TRACE` output
-run_cmake_command(
-    message-loglevel-trace
-    ${CMAKE_COMMAND} --loglevel=trace -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
+    message-indent-multiline
+    ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-indent-multiline.cmake
   )
diff --git a/Tests/RunCMake/message/message-indent-multiline-stderr.txt b/Tests/RunCMake/message/message-indent-multiline-stderr.txt
new file mode 100644
index 0000000..5853a31
--- /dev/null
+++ b/Tests/RunCMake/message/message-indent-multiline-stderr.txt
@@ -0,0 +1,3 @@
+ >This is
+ >the multiline
+ >message
diff --git a/Tests/RunCMake/message/message-indent-multiline-stdout.txt b/Tests/RunCMake/message/message-indent-multiline-stdout.txt
new file mode 100644
index 0000000..ae0c72e
--- /dev/null
+++ b/Tests/RunCMake/message/message-indent-multiline-stdout.txt
@@ -0,0 +1,8 @@
+--  >This is
+ >the multiline
+ >message
+ >
+ >
+--  >This is
+ >the multiline
+ >message
diff --git a/Tests/RunCMake/message/message-indent-multiline.cmake b/Tests/RunCMake/message/message-indent-multiline.cmake
new file mode 100644
index 0000000..0f789bf
--- /dev/null
+++ b/Tests/RunCMake/message/message-indent-multiline.cmake
@@ -0,0 +1,13 @@
+# NOTE Use non-space indent string, to check indentation
+# of line endings and "empty" lines.
+# ALERT Do not put any space characters after the non-space!
+list(APPEND CMAKE_MESSAGE_INDENT " >")
+set(msg [[This is
+the multiline
+message]]) # No `\n` at the end!
+# NOTE Two empty lines after the text
+message(STATUS "${msg}\n\n")
+message(STATUS "${msg}")
+# This is just to make sure NOTICE messages are also get indented:
+# https://gitlab.kitware.com/cmake/cmake/issues/19418#note_588011
+message(NOTICE "${msg}")
diff --git a/Tests/RunCMake/message/message-indent-stdout.txt b/Tests/RunCMake/message/message-indent-stdout.txt
new file mode 100644
index 0000000..b2c3c60
--- /dev/null
+++ b/Tests/RunCMake/message/message-indent-stdout.txt
@@ -0,0 +1,13 @@
+-- COUNTING:
+--    COUNTING_ENGLISH:
+--       one
+--       two
+--       three
+--       four
+--       five
+--    COUNTING_BAHASA:
+--       satu
+--       dua
+--       tiga
+--       empat
+--       lima
diff --git a/Tests/RunCMake/message/message-indent.cmake b/Tests/RunCMake/message/message-indent.cmake
new file mode 100644
index 0000000..c07ff45
--- /dev/null
+++ b/Tests/RunCMake/message/message-indent.cmake
@@ -0,0 +1,19 @@
+function(debug_list LIST_VAR)
+  message(STATUS "${LIST_VAR}:")
+  list(APPEND CMAKE_MESSAGE_INDENT "   ")
+  foreach(_item IN LISTS ${LIST_VAR})
+    list(LENGTH ${_item} _item_len)
+    if(_item_len GREATER 1)
+      debug_list(${_item})
+    else()
+      message(STATUS "${_item}")
+    endif()
+  endforeach()
+endfunction()
+
+list(APPEND COUNTING_ENGLISH one two three four five)
+list(APPEND COUNTING_BAHASA satu dua tiga empat lima)
+
+list(APPEND COUNTING COUNTING_ENGLISH COUNTING_BAHASA)
+
+debug_list(COUNTING)
diff --git a/Tests/RunCMake/message/message-log-level-debug-stderr.txt b/Tests/RunCMake/message/message-log-level-debug-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-debug-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-debug-stdout.txt b/Tests/RunCMake/message/message-log-level-debug-stdout.txt
new file mode 100644
index 0000000..1452137
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-debug-stdout.txt
@@ -0,0 +1,3 @@
+-- STATUS message
+-- VERBOSE message
+-- DEBUG message
diff --git a/Tests/RunCMake/message/message-log-level-default-stderr.txt b/Tests/RunCMake/message/message-log-level-default-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-default-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-default-stdout.txt b/Tests/RunCMake/message/message-log-level-default-stdout.txt
new file mode 100644
index 0000000..809f4cc
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-default-stdout.txt
@@ -0,0 +1 @@
+-- STATUS message
diff --git a/Tests/RunCMake/message/message-log-level-invalid-result.txt b/Tests/RunCMake/message/message-log-level-invalid-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-invalid-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/message/message-log-level-invalid-stderr.txt b/Tests/RunCMake/message/message-log-level-invalid-stderr.txt
new file mode 100644
index 0000000..a166bbc
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-invalid-stderr.txt
@@ -0,0 +1 @@
+CMake Error: Invalid level specified for --log-level
diff --git a/Tests/RunCMake/message/message-log-level-notice-stderr.txt b/Tests/RunCMake/message/message-log-level-notice-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-notice-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-status-stderr.txt b/Tests/RunCMake/message/message-log-level-status-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-status-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-status-stdout.txt b/Tests/RunCMake/message/message-log-level-status-stdout.txt
new file mode 100644
index 0000000..809f4cc
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-status-stdout.txt
@@ -0,0 +1 @@
+-- STATUS message
diff --git a/Tests/RunCMake/message/message-log-level-trace-stderr.txt b/Tests/RunCMake/message/message-log-level-trace-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-trace-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-trace-stdout.txt b/Tests/RunCMake/message/message-log-level-trace-stdout.txt
new file mode 100644
index 0000000..1cfce6f
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-trace-stdout.txt
@@ -0,0 +1,4 @@
+-- STATUS message
+-- VERBOSE message
+-- DEBUG message
+-- TRACE message
diff --git a/Tests/RunCMake/message/message-log-level-verbose-stderr.txt b/Tests/RunCMake/message/message-log-level-verbose-stderr.txt
new file mode 100644
index 0000000..efec736
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-verbose-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message
++
+Default NOTICE message
+NOTICE message$
diff --git a/Tests/RunCMake/message/message-log-level-verbose-stdout.txt b/Tests/RunCMake/message/message-log-level-verbose-stdout.txt
new file mode 100644
index 0000000..c15d43f
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-verbose-stdout.txt
@@ -0,0 +1,2 @@
+-- STATUS message
+-- VERBOSE message
diff --git a/Tests/RunCMake/message/message-log-level-warning-stderr.txt b/Tests/RunCMake/message/message-log-level-warning-stderr.txt
new file mode 100644
index 0000000..c721b06
--- /dev/null
+++ b/Tests/RunCMake/message/message-log-level-warning-stderr.txt
@@ -0,0 +1,9 @@
+^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\):
+  Deprecation warning
++
+CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\):
+  Author warning message
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
++
+CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\):
+  Warning message$
diff --git a/Tests/RunCMake/project/CMP0048-NEW.cmake b/Tests/RunCMake/project/CMP0048-NEW.cmake
index 7e16b70..b6e80ac 100644
--- a/Tests/RunCMake/project/CMP0048-NEW.cmake
+++ b/Tests/RunCMake/project/CMP0048-NEW.cmake
@@ -1,9 +1,4 @@
-macro(print_versions name)
-  foreach(v "" _MAJOR _MINOR _PATCH _TWEAK)
-    message(STATUS "PROJECT_VERSION${v}='${PROJECT_VERSION${v}}'")
-    message(STATUS "${name}_VERSION${v}='${${name}_VERSION${v}}'")
-  endforeach()
-endmacro()
+include(PrintVersions.cmake)
 
 cmake_policy(SET CMP0048 NEW)
 
diff --git a/Tests/RunCMake/project/CMP0096-NEW-stdout.txt b/Tests/RunCMake/project/CMP0096-NEW-stdout.txt
new file mode 100644
index 0000000..f6b999a
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-NEW-stdout.txt
@@ -0,0 +1,30 @@
+-- PROJECT_VERSION='2019.07.06'
+-- DateVersion_VERSION='2019.07.06'
+-- PROJECT_VERSION_MAJOR='2019'
+-- DateVersion_VERSION_MAJOR='2019'
+-- PROJECT_VERSION_MINOR='07'
+-- DateVersion_VERSION_MINOR='07'
+-- PROJECT_VERSION_PATCH='06'
+-- DateVersion_VERSION_PATCH='06'
+-- PROJECT_VERSION_TWEAK=''
+-- DateVersion_VERSION_TWEAK=''
+-- PROJECT_VERSION='4294967297'
+-- LongVersion_VERSION='4294967297'
+-- PROJECT_VERSION_MAJOR='4294967297'
+-- LongVersion_VERSION_MAJOR='4294967297'
+-- PROJECT_VERSION_MINOR=''
+-- LongVersion_VERSION_MINOR=''
+-- PROJECT_VERSION_PATCH=''
+-- LongVersion_VERSION_PATCH=''
+-- PROJECT_VERSION_TWEAK=''
+-- LongVersion_VERSION_TWEAK=''
+-- PROJECT_VERSION='0009999999999.0009999999999.0009999999999.0009999999999'
+-- VeryLongVersion_VERSION='0009999999999.0009999999999.0009999999999.0009999999999'
+-- PROJECT_VERSION_MAJOR='0009999999999'
+-- VeryLongVersion_VERSION_MAJOR='0009999999999'
+-- PROJECT_VERSION_MINOR='0009999999999'
+-- VeryLongVersion_VERSION_MINOR='0009999999999'
+-- PROJECT_VERSION_PATCH='0009999999999'
+-- VeryLongVersion_VERSION_PATCH='0009999999999'
+-- PROJECT_VERSION_TWEAK='0009999999999'
+-- VeryLongVersion_VERSION_TWEAK='0009999999999'
diff --git a/Tests/RunCMake/project/CMP0096-NEW.cmake b/Tests/RunCMake/project/CMP0096-NEW.cmake
new file mode 100644
index 0000000..e2cdd20
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-NEW.cmake
@@ -0,0 +1,6 @@
+cmake_policy(SET CMP0048 NEW)
+cmake_policy(SET CMP0096 NEW)
+include(CMP0096-common.cmake)
+
+project(VeryLongVersion VERSION 0009999999999.0009999999999.0009999999999.0009999999999 LANGUAGES NONE)
+print_versions(VeryLongVersion)
diff --git a/Tests/RunCMake/project/CMP0096-OLD-stdout.txt b/Tests/RunCMake/project/CMP0096-OLD-stdout.txt
new file mode 100644
index 0000000..6a945ce
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-OLD-stdout.txt
@@ -0,0 +1,20 @@
+-- PROJECT_VERSION='2019.7.6'
+-- DateVersion_VERSION='2019.7.6'
+-- PROJECT_VERSION_MAJOR='2019'
+-- DateVersion_VERSION_MAJOR='2019'
+-- PROJECT_VERSION_MINOR='7'
+-- DateVersion_VERSION_MINOR='7'
+-- PROJECT_VERSION_PATCH='6'
+-- DateVersion_VERSION_PATCH='6'
+-- PROJECT_VERSION_TWEAK=''
+-- DateVersion_VERSION_TWEAK=''
+-- PROJECT_VERSION='(1|4294967295)'
+-- LongVersion_VERSION='(1|4294967295)'
+-- PROJECT_VERSION_MAJOR='(1|4294967295)'
+-- LongVersion_VERSION_MAJOR='(1|4294967295)'
+-- PROJECT_VERSION_MINOR=''
+-- LongVersion_VERSION_MINOR=''
+-- PROJECT_VERSION_PATCH=''
+-- LongVersion_VERSION_PATCH=''
+-- PROJECT_VERSION_TWEAK=''
+-- LongVersion_VERSION_TWEAK=''
diff --git a/Tests/RunCMake/project/CMP0096-OLD.cmake b/Tests/RunCMake/project/CMP0096-OLD.cmake
new file mode 100644
index 0000000..25a3b19
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-OLD.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0048 NEW)
+cmake_policy(SET CMP0096 OLD)
+include(CMP0096-common.cmake)
diff --git a/Tests/RunCMake/project/CMP0096-WARN-stdout.txt b/Tests/RunCMake/project/CMP0096-WARN-stdout.txt
new file mode 100644
index 0000000..6a945ce
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-WARN-stdout.txt
@@ -0,0 +1,20 @@
+-- PROJECT_VERSION='2019.7.6'
+-- DateVersion_VERSION='2019.7.6'
+-- PROJECT_VERSION_MAJOR='2019'
+-- DateVersion_VERSION_MAJOR='2019'
+-- PROJECT_VERSION_MINOR='7'
+-- DateVersion_VERSION_MINOR='7'
+-- PROJECT_VERSION_PATCH='6'
+-- DateVersion_VERSION_PATCH='6'
+-- PROJECT_VERSION_TWEAK=''
+-- DateVersion_VERSION_TWEAK=''
+-- PROJECT_VERSION='(1|4294967295)'
+-- LongVersion_VERSION='(1|4294967295)'
+-- PROJECT_VERSION_MAJOR='(1|4294967295)'
+-- LongVersion_VERSION_MAJOR='(1|4294967295)'
+-- PROJECT_VERSION_MINOR=''
+-- LongVersion_VERSION_MINOR=''
+-- PROJECT_VERSION_PATCH=''
+-- LongVersion_VERSION_PATCH=''
+-- PROJECT_VERSION_TWEAK=''
+-- LongVersion_VERSION_TWEAK=''
diff --git a/Tests/RunCMake/project/CMP0096-WARN.cmake b/Tests/RunCMake/project/CMP0096-WARN.cmake
new file mode 100644
index 0000000..7fe0861
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-WARN.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0048 NEW)
+
+include(CMP0096-common.cmake)
diff --git a/Tests/RunCMake/project/CMP0096-common.cmake b/Tests/RunCMake/project/CMP0096-common.cmake
new file mode 100644
index 0000000..8d26d30
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0096-common.cmake
@@ -0,0 +1,9 @@
+include(PrintVersions.cmake)
+
+# Test leading zeros motivating this policy.
+project(DateVersion VERSION 2019.07.06 LANGUAGES NONE)
+print_versions(DateVersion)
+
+# Overflow version component in OLD behavior.
+project(LongVersion VERSION 4294967297 #[[ uint32_max + 2 ]] LANGUAGES NONE)
+print_versions(LongVersion)
diff --git a/Tests/RunCMake/project/PrintVersions.cmake b/Tests/RunCMake/project/PrintVersions.cmake
new file mode 100644
index 0000000..ce1b25d
--- /dev/null
+++ b/Tests/RunCMake/project/PrintVersions.cmake
@@ -0,0 +1,6 @@
+macro(print_versions name)
+  foreach(v "" _MAJOR _MINOR _PATCH _TWEAK)
+    message(STATUS "PROJECT_VERSION${v}='${PROJECT_VERSION${v}}'")
+    message(STATUS "${name}_VERSION${v}='${${name}_VERSION${v}}'")
+  endforeach()
+endmacro()
diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake
index 3a8ad4b..8f43a51 100644
--- a/Tests/RunCMake/project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/project/RunCMakeTest.cmake
@@ -27,3 +27,7 @@
 run_cmake(CMP0048-OLD-VERSION)
 run_cmake(CMP0048-WARN)
 run_cmake(CMP0048-NEW)
+
+run_cmake(CMP0096-WARN)
+run_cmake(CMP0096-OLD)
+run_cmake(CMP0096-NEW)
diff --git a/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake b/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake
index b67c598..a419cc9 100644
--- a/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_compile_definitions/RunCMakeTest.cmake
@@ -1,3 +1,4 @@
 include(RunCMake)
 
 run_cmake(empty_keyword_args)
+run_cmake(unknown_imported_target)
diff --git a/Tests/RunCMake/target_compile_definitions/unknown_imported_target.cmake b/Tests/RunCMake/target_compile_definitions/unknown_imported_target.cmake
new file mode 100644
index 0000000..4ae1c0d
--- /dev/null
+++ b/Tests/RunCMake/target_compile_definitions/unknown_imported_target.cmake
@@ -0,0 +1,11 @@
+# Test that target_compile_definitions works on UNKNOWN IMPORTED target
+add_library(imported UNKNOWN IMPORTED)
+target_compile_definitions(imported INTERFACE FOO)
+
+get_target_property(IMPORTED_INTERFACE_CDS imported INTERFACE_COMPILE_DEFINITIONS)
+
+if (NOT FOO IN_LIST IMPORTED_INTERFACE_CDS)
+  message(
+    FATAL_ERROR "FOO should be in INTERFACE_COMPILE_DEFINITIONS.\n"
+    "Actual INTERFACE_COMPILE_DEFINITIONS: " ${IMPORTED_INTERFACE_CDS})
+endif()
diff --git a/Tests/RunCMake/target_link_libraries/StaticPrivateDepNotExported-stderr.txt b/Tests/RunCMake/target_link_libraries/StaticPrivateDepNotExported-stderr.txt
index 6bb44ab..3204225 100644
--- a/Tests/RunCMake/target_link_libraries/StaticPrivateDepNotExported-stderr.txt
+++ b/Tests/RunCMake/target_link_libraries/StaticPrivateDepNotExported-stderr.txt
@@ -1 +1 @@
-CMake Error: install\(EXPORT "Exp" ...\) includes target "foo" which requires target "not_exported" that is not in the export set.
+CMake Error: install\(EXPORT "Exp" ...\) includes target "foo" which requires target "not_exported" that is not in any export set.
diff --git a/Tests/RunCMake/try_compile/CMP0067-stderr.txt b/Tests/RunCMake/try_compile/CMP0067-stderr.txt
index e2677ed..d955dda 100644
--- a/Tests/RunCMake/try_compile/CMP0067-stderr.txt
+++ b/Tests/RunCMake/try_compile/CMP0067-stderr.txt
@@ -19,6 +19,17 @@
 This warning is for project developers.  Use -Wno-dev to suppress it.
 
 after try_compile with CMP0067 WARN-enabled
+CMake Deprecation Warning at CMP0067.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0067 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
 before try_compile with CMP0067 OLD
 after try_compile with CMP0067 OLD
 before try_compile with CMP0067 NEW
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/SourceFileProperty/CMakeLists.txt b/Tests/SourceFileProperty/CMakeLists.txt
index 1b6506d..5e55f7b 100644
--- a/Tests/SourceFileProperty/CMakeLists.txt
+++ b/Tests/SourceFileProperty/CMakeLists.txt
@@ -1,19 +1,27 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.1)
 project(SourceFileProperty C)
 
-set(sources)
-
 if (EXISTS icasetest.c)
   # If a file exists by this name, use it.
   set_source_files_properties(icasetest.c
     PROPERTIES
-      COMPILE_FLAGS -DNEEDED_TO_WORK)
+      COMPILE_DEFINITIONS NEEDED_TO_WORK)
 else ()
   # Work on case-sensitive file systems as well.
   set_source_files_properties(main.c
     PROPERTIES
-      COMPILE_FLAGS -DNO_NEED_TO_CALL)
+      COMPILE_DEFINITIONS NO_NEED_TO_CALL)
 endif ()
-list(APPEND sources ICaseTest.c)
 
-add_executable(SourceFileProperty main.c ${sources})
+add_executable(SourceFileProperty main.c)
+target_sources(SourceFileProperty PRIVATE ICaseTest.c)
+
+get_source_file_property(LANG_MAIN main.c LANGUAGE)
+if(NOT "${LANG_MAIN}" STREQUAL "C")
+  message(FATAL_ERROR "Bad language for file main.c")
+endif()
+
+get_property(LANG_TEST SOURCE ICaseTest.c PROPERTY LANGUAGE)
+if (NOT "${LANG_TEST}" STREQUAL "C")
+  message(FATAL_ERROR "Bad language for file ICaseTest.c")
+endif ()
diff --git a/Tests/SystemInformation/DumpInformation.cxx b/Tests/SystemInformation/DumpInformation.cxx
index e0cc0ce..4328675 100644
--- a/Tests/SystemInformation/DumpInformation.cxx
+++ b/Tests/SystemInformation/DumpInformation.cxx
@@ -1,5 +1,7 @@
 #include "DumpInformation.h"
+
 #include <stdio.h>
+
 #include <sys/stat.h>
 
 void cmDumpInformationPrintFile(const char* name, FILE* fout)
diff --git a/Tests/TestsWorkingDirectory/main.c b/Tests/TestsWorkingDirectory/main.c
index 8755c0e..ca72f21 100644
--- a/Tests/TestsWorkingDirectory/main.c
+++ b/Tests/TestsWorkingDirectory/main.c
@@ -37,9 +37,10 @@
 
 #else
 #  include <fcntl.h>
-#  include <sys/types.h>
 #  include <unistd.h>
 
+#  include <sys/types.h>
+
 static const char* Getcwd(char* buf, unsigned int len)
 {
   const char* ret = getcwd(buf, len);
diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt
index 54e96a2..498e556 100644
--- a/Tests/TryCompile/CMakeLists.txt
+++ b/Tests/TryCompile/CMakeLists.txt
@@ -313,6 +313,60 @@
   TEST_ASSERT(C_STRICT_PROTOTYPES "CHECK_C_COMPILER_FLAG failed -Werror -Wstrict-prototypes")
 endif()
 
+#########################################################################
+#
+# Test that the CHECK_OBJCC_SOURCE_COMPILES, CHECK_OBJCXX_SOURCE_COMPILES
+# CHECK_OBJC_SOURCE_RUNS and CHECK_OBJCXX_SOURCE_RUNS macros work
+
+if (APPLE)
+    enable_language(OBJC)
+    enable_language(OBJCXX)
+
+    include(CheckOBJCSourceCompiles)
+    include(CheckOBJCXXSourceCompiles)
+    include(CheckOBJCSourceRuns)
+    include(CheckOBJCXXSourceRuns)
+
+    CHECK_OBJC_SOURCE_COMPILES("I don't build in Objective-C" OBJC_BUILD_SHOULD_FAIL)
+    CHECK_OBJC_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJC_BUILD_SHOULD_WORK)
+
+    TEST_FAIL(OBJC_BUILD_SHOULD_FAIL "CHECK_OBJC_SOURCE_COMPILES() succeeded, but should have failed")
+    TEST_ASSERT(SIMPLE_OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded")
+
+    set(CMAKE_REQUIRED_LIBRARIES "-framework Foundation")
+
+    CHECK_OBJC_SOURCE_COMPILES("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_BUILD_SHOULD_WORK)
+    CHECK_OBJC_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJC_RUN_SHOULD_FAIL)
+    CHECK_OBJC_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJC_RUN_SHOULD_WORK)
+    CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 2;\n}\n" OBJC_RUN_SHOULD_FAIL)
+    CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_RUN_SHOULD_WORK)
+
+    TEST_ASSERT(OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded")
+    TEST_FAIL(SIMPLE_OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURC_RUNS() succeeds, but should have failed")
+    TEST_ASSERT(SIMPLE_OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded")
+    TEST_FAIL(OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURCE_RUNS() succeeds, but should have failed")
+    TEST_ASSERT(OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded")
+
+
+    CHECK_OBJCXX_SOURCE_COMPILES("I don't build in Objective-C++" OBJCXX_BUILD_SHOULD_FAIL)
+    CHECK_OBJCXX_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJCXX_BUILD_SHOULD_WORK)
+
+    TEST_FAIL(OBJCXX_BUILD_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_COMPILES() succeeded, but should have failed")
+    TEST_ASSERT(SIMPLE_OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded")
+
+    CHECK_OBJCXX_SOURCE_COMPILES("#import <Foundation/Foundation.h>\n#include <iostream>\nint main()\n{\nNSObject *foo;\nstd::cout << \"Hello\" << std::endl;\nreturn 0;\n}\n" OBJCXX_BUILD_SHOULD_WORK)
+    CHECK_OBJCXX_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJCXX_RUN_SHOULD_FAIL)
+    CHECK_OBJCXX_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJCXX_RUN_SHOULD_WORK)
+    CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 2;\n}\n" OBJCXX_RUN_SHOULD_FAIL)
+    CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 0;\n}\n" OBJCXX_RUN_SHOULD_WORK)
+
+    TEST_ASSERT(OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded")
+    TEST_FAIL(SIMPLE_OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURC_RUNS() succeeds, but should have failed")
+    TEST_ASSERT(SIMPLE_OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded")
+    TEST_FAIL(OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_RUNS() succeeds, but should have failed")
+    TEST_ASSERT(OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded")
+endif()
+
 #######################################################################
 #
 # also test that the check_prototype_definition macro works
diff --git a/Tests/Tutorial/Complete/CMakeLists.txt b/Tests/Tutorial/Complete/CMakeLists.txt
deleted file mode 100644
index 9658e65..0000000
--- a/Tests/Tutorial/Complete/CMakeLists.txt
+++ /dev/null
@@ -1,116 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-# 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}")
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-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(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library
-add_subdirectory(MathFunctions)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial MathFunctions)
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
-
-# install the configuration targets
-install(EXPORT MathFunctionsTargets
-  FILE MathFunctionsTargets.cmake
-  DESTINATION lib/cmake/MathFunctions
-)
-
-include(CMakePackageConfigHelpers)
-# generate the config file that is includes the exports
-configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
-  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
-  INSTALL_DESTINATION "lib/cmake/example"
-  NO_SET_AND_CHECK_MACRO
-  NO_CHECK_REQUIRED_COMPONENTS_MACRO
-  )
-# generate the version file for the config file
-write_basic_package_version_file(
-  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
-  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
-  COMPATIBILITY AnyNewerVersion
-)
-
-# install the configuration file
-install(FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
-  DESTINATION lib/cmake/MathFunctions
-  )
-
-# generate the export targets for the build tree
-# needs to be after the install(TARGETS ) command
-export(EXPORT MathFunctionsTargets
-  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
-)
diff --git a/Tests/Tutorial/Complete/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Complete/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 161ad64..0000000
--- a/Tests/Tutorial/Complete/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-
-# add the library that runs
-add_library(MathFunctions MathFunctions.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-target_include_directories(MathFunctions
-                           INTERFACE
-                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-                            $<INSTALL_INTERFACE:include>
-                           )
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-if(USE_MYMATH)
-
-  # does this system provide the log and exp functions?
-  include(CheckSymbolExists)
-  set(CMAKE_REQUIRED_LIBRARIES "m")
-  check_symbol_exists(log "math.h" HAVE_LOG)
-  check_symbol_exists(exp "math.h" HAVE_EXP)
-
-  # first we add the executable that generates the table
-  add_executable(MakeTable MakeTable.cxx)
-
-  # add the command to generate the source code
-  add_custom_command(
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    DEPENDS MakeTable
-    )
-
-  # library that just does sqrt
-  add_library(SqrtLibrary STATIC
-              mysqrt.cxx
-              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-              )
-
-  # state that we depend on our binary dir to find Table.h
-  target_include_directories(SqrtLibrary PRIVATE
-                             ${CMAKE_CURRENT_BINARY_DIR}
-                             )
-
-  set_target_properties(SqrtLibrary PROPERTIES
-                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
-                        )
-
-  target_compile_definitions(SqrtLibrary PRIVATE
-                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
-                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
-                             )
-  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-endif()
-
-target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
-
-# define the symbol stating we are using the declspec(dllexport) when
-# building on windows
-target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-# setup the version numbering
-set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
-set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
-
-install(TARGETS MathFunctions
-        DESTINATION lib
-        EXPORT MathFunctionsTargets)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Complete/MathFunctions/MathFunctions.cxx b/Tests/Tutorial/Complete/MathFunctions/MathFunctions.cxx
deleted file mode 100644
index 5351184..0000000
--- a/Tests/Tutorial/Complete/MathFunctions/MathFunctions.cxx
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#include "MathFunctions.h"
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Tests/Tutorial/Complete/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Complete/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 96d9421..0000000
--- a/Tests/Tutorial/Complete/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-namespace mathfunctions {
-namespace detail {
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
-}
-}
diff --git a/Tests/Tutorial/Complete/TutorialConfig.h.in b/Tests/Tutorial/Complete/TutorialConfig.h.in
deleted file mode 100644
index 8cd2fc9..0000000
--- a/Tests/Tutorial/Complete/TutorialConfig.h.in
+++ /dev/null
@@ -1,3 +0,0 @@
-// the configured version number
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Tests/Tutorial/Complete/tutorial.cxx b/Tests/Tutorial/Complete/tutorial.cxx
deleted file mode 100644
index 443d195..0000000
--- a/Tests/Tutorial/Complete/tutorial.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that computes the square root of a number
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-  const double outputValue = mathfunctions::sqrt(inputValue);
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Consumer/CMakeLists.txt b/Tests/Tutorial/Consumer/CMakeLists.txt
deleted file mode 100644
index 4033b4d..0000000
--- a/Tests/Tutorial/Consumer/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-
-if(NOT DEFINED CMAKE_CXX_STANDARD)
-  set(CMAKE_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED True)
-endif()
-
-
-function(find_external_dependency name)
-  set(${name}_ROOT ""  CACHE PATH "Root directory to find ${name}")
-  mark_as_advanced(${name}_DIR)
-  find_package(${name} PATHS ${${name}_ROOT} REQUIRED)
-endfunction()
-
-
-project(Consumer)
-
-find_external_dependency(MathFunctions)
-
-add_library(consumer consumer.cxx)
-target_link_libraries(consumer PUBLIC MathFunctions)
-
-# install the consumer library
-install(TARGETS consumer DESTINATION bin EXPORT ConsumerTargets)
-
-# install the configuration targets
-install(EXPORT ConsumerTargets
-  FILE ConsumerTargets.cmake
-  DESTINATION lib/cmake/Consumer
-)
-
-include(CMakePackageConfigHelpers)
-# generate the config file that is includes the exports
-configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
-  "${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake"
-  INSTALL_DESTINATION "lib/cmake/example"
-  NO_SET_AND_CHECK_MACRO
-  NO_CHECK_REQUIRED_COMPONENTS_MACRO
-  )
-
-# install the configuration file
-install(FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake
-  DESTINATION lib/cmake/Consumer
-  )
-
-# generate the export targets for the build tree
-# needs to be after the install(TARGETS ) command
-export(EXPORT ConsumerTargets
-  FILE "${CMAKE_CURRENT_BINARY_DIR}/ConsumerTargets.cmake"
-)
diff --git a/Tests/Tutorial/Consumer/directions.txt b/Tests/Tutorial/Consumer/directions.txt
deleted file mode 100644
index 6a70aab..0000000
--- a/Tests/Tutorial/Consumer/directions.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# Import a CMake Project#
-
-This examples shows how a project can find other CMake packages that
-generated Config.cmake files.
-
-It also shows how to state a projects external dependencies when generating a Config.cmake.
diff --git a/Tests/Tutorial/MultiPackage/CMakeLists.txt b/Tests/Tutorial/MultiPackage/CMakeLists.txt
deleted file mode 100644
index 067e807..0000000
--- a/Tests/Tutorial/MultiPackage/CMakeLists.txt
+++ /dev/null
@@ -1,109 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-# control how we mark up Debug libraries compared to Release libraries
-set(CMAKE_DEBUG_POSTFIX "-d")
-
-# 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}")
-
-option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass the version number only
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library
-add_subdirectory(MathFunctions)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial MathFunctions)
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
-
-# install the configuration targets
-install(EXPORT MathFunctionsTargets
-  FILE MathFunctionsTargets.cmake
-  DESTINATION lib/cmake/MathFunctions
-)
-
-include(CMakePackageConfigHelpers)
-# generate the config file that is includes the exports
-configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
-  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
-  INSTALL_DESTINATION "lib/cmake/example"
-  NO_SET_AND_CHECK_MACRO
-  NO_CHECK_REQUIRED_COMPONENTS_MACRO
-  )
-# generate the version file for the config file
-write_basic_package_version_file(
-  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
-  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
-  COMPATIBILITY AnyNewerVersion
-)
-
-# install the configuration file
-install(FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
-  DESTINATION lib/cmake/MathFunctions
-  )
-
-# generate the export targets for the build tree
-# needs to be after the install(TARGETS ) command
-export(EXPORT MathFunctionsTargets
-  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
-)
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/CMakeLists.txt b/Tests/Tutorial/MultiPackage/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 161ad64..0000000
--- a/Tests/Tutorial/MultiPackage/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-
-# add the library that runs
-add_library(MathFunctions MathFunctions.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-target_include_directories(MathFunctions
-                           INTERFACE
-                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-                            $<INSTALL_INTERFACE:include>
-                           )
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-if(USE_MYMATH)
-
-  # does this system provide the log and exp functions?
-  include(CheckSymbolExists)
-  set(CMAKE_REQUIRED_LIBRARIES "m")
-  check_symbol_exists(log "math.h" HAVE_LOG)
-  check_symbol_exists(exp "math.h" HAVE_EXP)
-
-  # first we add the executable that generates the table
-  add_executable(MakeTable MakeTable.cxx)
-
-  # add the command to generate the source code
-  add_custom_command(
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    DEPENDS MakeTable
-    )
-
-  # library that just does sqrt
-  add_library(SqrtLibrary STATIC
-              mysqrt.cxx
-              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-              )
-
-  # state that we depend on our binary dir to find Table.h
-  target_include_directories(SqrtLibrary PRIVATE
-                             ${CMAKE_CURRENT_BINARY_DIR}
-                             )
-
-  set_target_properties(SqrtLibrary PROPERTIES
-                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
-                        )
-
-  target_compile_definitions(SqrtLibrary PRIVATE
-                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
-                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
-                             )
-  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-endif()
-
-target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
-
-# define the symbol stating we are using the declspec(dllexport) when
-# building on windows
-target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-# setup the version numbering
-set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
-set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
-
-install(TARGETS MathFunctions
-        DESTINATION lib
-        EXPORT MathFunctionsTargets)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.cxx b/Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.cxx
deleted file mode 100644
index 5351184..0000000
--- a/Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.cxx
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#include "MathFunctions.h"
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.cxx b/Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 96d9421..0000000
--- a/Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-namespace mathfunctions {
-namespace detail {
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
-}
-}
diff --git a/Tests/Tutorial/MultiPackage/directions.txt b/Tests/Tutorial/MultiPackage/directions.txt
deleted file mode 100644
index c3102bb..0000000
--- a/Tests/Tutorial/MultiPackage/directions.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-# Packaging Debug and Release #
-
-By default CMake is model is that a build directory only contains a single
-configuration, be it Debug, Release, MinSizeRel, or RelWithDebInfo.
-
-But it is possible to setup CPack to bundle multiple build directories at the same
-time to build a package that contains multiple configurations of the same project.
-
-First we need to ahead and construct a directory called 'multi_config' this
-will contain all the builds that we want to package together.
-
-Second create a 'debug' and 'release' directory underneath 'multi_config'. At
-the end you should have a layout that looks like:
-
-─ multi_config
-    ├── debug
-    └── release
-
-Now we need to setup debug and release builds, which would roughly entail
-the following:
-
-  cd debug
-  cmake -DCMAKE_BUILD_TYPE=Debug ../../MultiPackage/
-  cmake --build .
-  cd ../release
-  cmake -DCMAKE_BUILD_TYPE=Release ../../MultiPackage/
-  cmake --build .
-  cd ..
-
-
-Now that both the debug and release builds are complete we can now use
-the custom MultiCPackConfig to package both builds into a single release.
-
-  cpack --config ../../MultiPackage/MultiCPackConfig.cmake
diff --git a/Tests/Tutorial/MultiPackage/tutorial.cxx b/Tests/Tutorial/MultiPackage/tutorial.cxx
deleted file mode 100644
index 443d195..0000000
--- a/Tests/Tutorial/MultiPackage/tutorial.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that computes the square root of a number
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-  const double outputValue = mathfunctions::sqrt(inputValue);
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Readme.txt b/Tests/Tutorial/Readme.txt
deleted file mode 100644
index 74eb01a..0000000
--- a/Tests/Tutorial/Readme.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-
-Step 0: A Starting Point
-Step 1: Configure a File and C++11 Controls
-Step 2: Adding a Library
-Step 3: Usage Requirements for Library
-Step 4: Installing and Testing
-Step 5: System Introspection
-Step 6: Custom Command and Generated File
-Step 7: Building an Installer
-Step 8: CDash submission
-Step 9: Mixing Static and Shared
-Step 10: Generator Expressions
-Step 11: Adding Export Configuration
-Complete: End result of Step 11
-Consumer: Example of Import Packages
-MultiPackage: How to package Debug and Release versions
diff --git a/Tests/Tutorial/Step1/CMakeLists.txt b/Tests/Tutorial/Step1/CMakeLists.txt
deleted file mode 100644
index 141f0c2..0000000
--- a/Tests/Tutorial/Step1/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-project(Tutorial)
-
-add_executable(Tutorial tutorial.cxx)
diff --git a/Tests/Tutorial/Step1/TutorialConfig.h.in b/Tests/Tutorial/Step1/TutorialConfig.h.in
deleted file mode 100644
index 5395a06..0000000
--- a/Tests/Tutorial/Step1/TutorialConfig.h.in
+++ /dev/null
@@ -1,4 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-
diff --git a/Tests/Tutorial/Step1/directions.txt b/Tests/Tutorial/Step1/directions.txt
deleted file mode 100644
index 827d775..0000000
--- a/Tests/Tutorial/Step1/directions.txt
+++ /dev/null
@@ -1,95 +0,0 @@
-# Adding a Version Number and Configured Header File #
-
-The first feature we will add is to provide our executable and project with a
-version number. While we could do this exclusively in the source code, using
-CMakeLists provides more flexibility.
-
-To add a version number we modify the CMakeLists file as follows:
-
-  cmake_minimum_required(VERSION 3.3)
-  project(Tutorial)
-
-  # the version number.
-  set(Tutorial_VERSION_MAJOR 1)
-  set(Tutorial_VERSION_MINOR 0)
-
-  # configure a header file to pass some of the CMake settings
-  # to the source code
-  configure_file(
-    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-    )
-
-  # add the executable
-  add_executable(Tutorial tutorial.cxx)
-
-  # add the binary tree to the search path for include files
-  # so that we will find TutorialConfig.h
-  target_include_directories(Tutorial PUBLIC
-                             "${PROJECT_BINARY_DIR}"
-                             )
-
-
-We then create a TutorialConfig.h.in file in the source tree with the
-following contents:
-
-  // the configured options and settings for Tutorial
-  #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-  #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-
-When CMake configures this header file the values for @Tutorial_VERSION_MAJOR@
-and @Tutorial_VERSION_MINOR@ will be replaced by the values from the CMakeLists
-file. Next we modify tutorial.cxx to include the configured header file and to
-make use of the version numbers. The resulting source code is listed below.
-
-  // A simple program that computes the square root of a number
-  #include <cmath>
-  #include <iostream>
-  #include <string>
-  #include <sstream>
-
-  #include "TutorialConfig.h"
-
-  int main (int argc, char *argv[])
-  {
-    if (argc < 2) {
-      std::cout << argv[0] << " Version "
-                << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR
-                << std::endl;
-      std::cout << "Usage: " << argv[0] << " number" << std::endl;
-      return 1;
-    }
-
-    double inputValue = atof(argv[1]);
-
-    double outputValue = sqrt(inputValue);
-    std::cout << "The square root of "
-              << inputValue << " is " << outputValue << std::endl;
-    return 0;
-  }
-
-# Adding C++11 support #
-
-Let's add some C++11 features to our project. We will need to explicitly state
-in the CMake code that it should use the correct flags. The easiest way to
-enable C++11 support for CMake is by using the CMAKE_CXX_STANDARD
-and CMAKE_CXX_STANDARD_REQUIRED variables.
-
-First, replace `atof` with `std::stod` in tutorial.cxx.
-
-Then, add the CMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED variables to
-the CMakeLists file. The STANADARD value should be set to 11, and REQUIRED
-should be set to True.
-
-
-# Build and Test #
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool
-
-cd to the directory where Tutorial was built (likely the make directory or
-a Debug or Release build configuration subdirectory) and run these commands:
-
-  Tutorial 4294967296
-  Tutorial 10
-  Tutorial
diff --git a/Tests/Tutorial/Step1/tutorial.cxx b/Tests/Tutorial/Step1/tutorial.cxx
deleted file mode 100644
index f8dd0c6..0000000
--- a/Tests/Tutorial/Step1/tutorial.cxx
+++ /dev/null
@@ -1,20 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <cstdlib>
-#include <iostream>
-#include <string>
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = atof(argv[1]);
-
-  double outputValue = sqrt(inputValue);
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step10/CMakeLists.txt b/Tests/Tutorial/Step10/CMakeLists.txt
deleted file mode 100644
index b1d46c4..0000000
--- a/Tests/Tutorial/Step10/CMakeLists.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-# 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}")
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass the version number only
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library
-add_subdirectory(MathFunctions)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial MathFunctions)
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
diff --git a/Tests/Tutorial/Step10/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step10/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 7a23505..0000000
--- a/Tests/Tutorial/Step10/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-
-# add the library that runs
-add_library(MathFunctions MathFunctions.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-target_include_directories(MathFunctions
-                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-                           )
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-if(USE_MYMATH)
-
-  # does this system provide the log and exp functions?
-  include(CheckSymbolExists)
-  set(CMAKE_REQUIRED_LIBRARIES "m")
-  check_symbol_exists(log "math.h" HAVE_LOG)
-  check_symbol_exists(exp "math.h" HAVE_EXP)
-
-  # first we add the executable that generates the table
-  add_executable(MakeTable MakeTable.cxx)
-
-  # add the command to generate the source code
-  add_custom_command(
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    DEPENDS MakeTable
-    )
-
-  # library that just does sqrt
-  add_library(SqrtLibrary STATIC
-              mysqrt.cxx
-              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-              )
-
-  # state that we depend on our binary dir to find Table.h
-  target_include_directories(SqrtLibrary PRIVATE
-                             ${CMAKE_CURRENT_BINARY_DIR}
-                             )
-
-  # state that SqrtLibrary need PIC when the default is shared libraries
-  set_target_properties(SqrtLibrary PROPERTIES
-                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
-                        )
-
-  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
-  if(HAVE_LOG AND HAVE_EXP)
-    target_compile_definitions(SqrtLibrary
-                               PRIVATE "HAVE_LOG" "HAVE_EXP")
-  endif()
-
-  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-endif()
-
-# define the symbol stating we are using the declspec(dllexport) when
-# building on windows
-target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step10/MathFunctions/MathFunctions.cxx b/Tests/Tutorial/Step10/MathFunctions/MathFunctions.cxx
deleted file mode 100644
index 5351184..0000000
--- a/Tests/Tutorial/Step10/MathFunctions/MathFunctions.cxx
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#include "MathFunctions.h"
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Tests/Tutorial/Step10/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step10/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 96d9421..0000000
--- a/Tests/Tutorial/Step10/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-namespace mathfunctions {
-namespace detail {
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
-}
-}
diff --git a/Tests/Tutorial/Step10/TutorialConfig.h.in b/Tests/Tutorial/Step10/TutorialConfig.h.in
deleted file mode 100644
index 8cd2fc9..0000000
--- a/Tests/Tutorial/Step10/TutorialConfig.h.in
+++ /dev/null
@@ -1,3 +0,0 @@
-// the configured version number
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Tests/Tutorial/Step10/directions.txt b/Tests/Tutorial/Step10/directions.txt
deleted file mode 100644
index 5317b54..0000000
--- a/Tests/Tutorial/Step10/directions.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# Adding Generator Expressions #
-
-Generator expressions are evaluated during build system generation to produce
-information specific to each build configuration.
-
-Generator expressions are allowed in the context of many target properties, such
-as LINK_LIBRARIES, INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS and others. They may
-also be used when using commands to populate those properties, such as
-target_link_libraries(), target_include_directories(),
-target_compile_definitions() and others.
-
-Generator expressions may to used to enable conditional linking, conditional
-definitions used when compiling, and conditional include directories and more.
-The conditions may be based on the build configuration, target properties,
-platform information or any other queryable information.
-
-There are different types of generator expressions including Logical,
-Informational, and Output expressions.
-
-Logical expressions are used to create conditional output. The basic expressions
-are the 0 and 1 expressions. A "$<0:...>" results in the empty string, and
-"$<1:...>" results in the content of "...".  They can also be nested.
-For example:
-
-  if(HAVE_LOG AND HAVE_EXP)
-    target_compile_definitions(SqrtLibrary
-                               PRIVATE "HAVE_LOG" "HAVE_EXP")
-  endif()
-
-Can be rewritten with generator expressions:
-
-  target_compile_definitions(SqrtLibrary PRIVATE
-                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
-                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
-                            )
-
-Note that "${HAVE_LOG}" is evaluated at CMake configure time while
-"$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>" is evaluated at build system generation time.
diff --git a/Tests/Tutorial/Step10/tutorial.cxx b/Tests/Tutorial/Step10/tutorial.cxx
deleted file mode 100644
index 443d195..0000000
--- a/Tests/Tutorial/Step10/tutorial.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that computes the square root of a number
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-  const double outputValue = mathfunctions::sqrt(inputValue);
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step11/CMakeLists.txt b/Tests/Tutorial/Step11/CMakeLists.txt
deleted file mode 100644
index b1d46c4..0000000
--- a/Tests/Tutorial/Step11/CMakeLists.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-# 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}")
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass the version number only
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library
-add_subdirectory(MathFunctions)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial MathFunctions)
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
diff --git a/Tests/Tutorial/Step11/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step11/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 760d6a5..0000000
--- a/Tests/Tutorial/Step11/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-
-# add the library that runs
-add_library(MathFunctions MathFunctions.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-target_include_directories(MathFunctions
-                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-                           )
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-if(USE_MYMATH)
-
-  # does this system provide the log and exp functions?
-  include(CheckSymbolExists)
-  set(CMAKE_REQUIRED_LIBRARIES "m")
-  check_symbol_exists(log "math.h" HAVE_LOG)
-  check_symbol_exists(exp "math.h" HAVE_EXP)
-
-  # first we add the executable that generates the table
-  add_executable(MakeTable MakeTable.cxx)
-
-  # add the command to generate the source code
-  add_custom_command(
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    DEPENDS MakeTable
-    )
-
-  # library that just does sqrt
-  add_library(SqrtLibrary STATIC
-              mysqrt.cxx
-              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-              )
-
-  # state that we depend on our binary dir to find Table.h
-  target_include_directories(SqrtLibrary PRIVATE
-                             ${CMAKE_CURRENT_BINARY_DIR}
-                             )
-
-  set_target_properties(SqrtLibrary PROPERTIES
-                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
-                        )
-
-  target_compile_definitions(SqrtLibrary PRIVATE
-                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
-                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
-                             )
-  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-endif()
-
-target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
-
-# define the symbol stating we are using the declspec(dllexport) when
-#building on windows
-target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step11/MathFunctions/MathFunctions.cxx b/Tests/Tutorial/Step11/MathFunctions/MathFunctions.cxx
deleted file mode 100644
index 5351184..0000000
--- a/Tests/Tutorial/Step11/MathFunctions/MathFunctions.cxx
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#include "MathFunctions.h"
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Tests/Tutorial/Step11/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step11/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 96d9421..0000000
--- a/Tests/Tutorial/Step11/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-namespace mathfunctions {
-namespace detail {
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
-}
-}
diff --git a/Tests/Tutorial/Step11/TutorialConfig.h.in b/Tests/Tutorial/Step11/TutorialConfig.h.in
deleted file mode 100644
index 8cd2fc9..0000000
--- a/Tests/Tutorial/Step11/TutorialConfig.h.in
+++ /dev/null
@@ -1,3 +0,0 @@
-// the configured version number
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Tests/Tutorial/Step11/directions.txt b/Tests/Tutorial/Step11/directions.txt
deleted file mode 100644
index ebb5def..0000000
--- a/Tests/Tutorial/Step11/directions.txt
+++ /dev/null
@@ -1,104 +0,0 @@
-# Adding Export Configuration #
-
-During Step 4 of the tutorial we added the ability for CMake to install the
-library and headers of the project. During Step 7 we added the ability
-to package up this information so it could be distributed to other people.
-
-The next step is to add the necessary information so that other CMake projects
-can use our project, be it from a build directory, a local install or when
-packaged.
-
-The first step is to update our install(TARGETS) commands to not only specify
-a DESTINATION but also an EXPORT. The EXPORT keyword generates and installs a
-CMake file containing code to import all targets listed in the install command
-from the installation tree. So let's go ahead and explicitly EXPORT the
-MathFunctions library by updating the install command in
-MathFunctions/CMakeLists.txt to look like:
-
-  install(TARGETS MathFunctions DESTINATION lib EXPORT MathFunctionsTargets)
-
-Now that we have MathFunctions being exported, we also need to explicitly install
-the generated MathFunctionsTargets.cmake file. This is done by adding
-the following to the bottom of the top-level CMakeLists.txt:
-
-  # install the configuration targets
-  install(EXPORT MathFunctionsTargets
-    FILE MathFunctionsTargets.cmake
-    DESTINATION lib/cmake/MathFunctions
-  )
-
-At this point you should try and run CMake. If everything is setup properly
-you will see that CMake will generate an error that looks like:
-
-  Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
-  path:
-
-    "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"
-
-  which is prefixed in the source directory.
-
-What CMake is trying to say is that during generating the export information
-it will export a path that is intrinsically tied to the current machine and
-will not be valid on other machines. The solution to this is to update the
-MathFunctions target_include_directories to understand that it needs different
-INTERFACE locations when being used from within the build directory and from an
-install / package. This means converting the target_include_directories
-call for MathFunctions to look like:
-
-  target_include_directories(MathFunctions
-                             INTERFACE
-                              $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-                              $<INSTALL_INTERFACE:include>
-                             )
-
-Once this has been updated, we can re-run CMake and see verify that it doesn't
-warn anymore.
-
-At this point, we have CMake properly packaging the target information that is
-required but we will still need to generate a MathFunctionsConfig.cmake, so
-that the CMake find_package command can find our project. So let's go ahead and
-add a new file to the top-level of the project called Config.cmake.in with the
-following contents:
-
-  @PACKAGE_INIT@
-
-  include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
-
-Then, to properly configure and install that file, add the following to the
-bottom of the top-level CMakeLists:
-
-  include(CMakePackageConfigHelpers)
-  # generate the config file that is includes the exports
-  configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
-    "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
-    INSTALL_DESTINATION "lib/cmake/example"
-    NO_SET_AND_CHECK_MACRO
-    NO_CHECK_REQUIRED_COMPONENTS_MACRO
-    )
-  # generate the version file for the config file
-  write_basic_package_version_file(
-    "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
-    VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
-    COMPATIBILITY AnyNewerVersion
-  )
-
-  # install the configuration file
-  install(FILES
-    ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
-    DESTINATION lib/cmake/MathFunctions
-    )
-
-At this point, we have generated a relocatable CMake Configuration for our project
-that can be used after the project has been installed or packaged. If we want
-our project to also be used from a build directory we only have to add
-the following to the bottom of the top level CMakeLists:
-
-  # generate the export targets for the build tree
-  # needs to be after the install(TARGETS ) command
-  export(EXPORT MathFunctionsTargets
-    FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
-  )
-
-With this export call we now generate a Targets.cmake, allowing the configured
-MathFunctionsConfig.cmake in the build directory to be used by other projects,
-without needing it to be installed.
diff --git a/Tests/Tutorial/Step11/tutorial.cxx b/Tests/Tutorial/Step11/tutorial.cxx
deleted file mode 100644
index 3768855..0000000
--- a/Tests/Tutorial/Step11/tutorial.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-  const double outputValue = mathfunctions::sqrt(inputValue);
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step2/CMakeLists.txt b/Tests/Tutorial/Step2/CMakeLists.txt
deleted file mode 100644
index 48afaa3..0000000
--- a/Tests/Tutorial/Step2/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
diff --git a/Tests/Tutorial/Step2/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step2/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 8b443a6..0000000
--- a/Tests/Tutorial/Step2/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_library(MathFunctions mysqrt.cxx)
diff --git a/Tests/Tutorial/Step2/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step2/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 7d9379e..0000000
--- a/Tests/Tutorial/Step2/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  double result = x;
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-  return result;
-}
diff --git a/Tests/Tutorial/Step2/TutorialConfig.h.in b/Tests/Tutorial/Step2/TutorialConfig.h.in
deleted file mode 100644
index 5395a06..0000000
--- a/Tests/Tutorial/Step2/TutorialConfig.h.in
+++ /dev/null
@@ -1,4 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-
diff --git a/Tests/Tutorial/Step2/directions.txt b/Tests/Tutorial/Step2/directions.txt
deleted file mode 100644
index bb6662c..0000000
--- a/Tests/Tutorial/Step2/directions.txt
+++ /dev/null
@@ -1,102 +0,0 @@
-# Adding a Library #
-
-Now we will add a library to our project. This library will contain our own
-implementation for computing the square root of a number. The executable can
-then use this library instead of the standard square root function provided by
-the compiler.
-
-For this tutorial we will put the library into a subdirectory
-called MathFunctions. It will have the following one line CMakeLists file:
-
-  add_library(MathFunctions mysqrt.cxx)
-
-The source file mysqrt.cxx has one function called mysqrt that provides similar
-functionality to the compiler’s sqrt function. To make use of the new library
-we add an add_subdirectory call in the top-level CMakeLists file so that the
-library will get built. We add the new library to the executable, and add the
-MathFunctions as an include directory so that mqsqrt.h header file can be
-found. The last few lines of the top-level CMakeLists file now look like:
-
-
-  add_subdirectory(MathFunctions)
-
-  #add the executable
-  add_executable(Tutorial tutorial.cxx)
-
-  target_link_libraries(Tutorial ${EXTRA_LIBS})
-
-
-Now let us make the MathFunctions library optional. While for the tutorial
-there really isn’t any need to do so, but with larger projects this is a common
-occurrence. The first step is to add an option to the top-level CMakeLists file.
-
-  option (USE_MYMATH
-          "Use tutorial provided math implementation" ON)
-
-This will show up in CMake GUI and ccmake with a default value of ON that can
-be changed by the user. This setting will be stored so that the user does not
-need to set the value each time they run CMake on this build directory.
-
-The next change is to make building and linking the MathFunctions library
-conditional. To do this we change the top-level CMakeLists file to look like
-the following:
-
-  cmake_minimum_required(VERSION 3.3)
-  project(Tutorial)
-
-  set(CMAKE_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-  # the version number.
-  set(Tutorial_VERSION_MAJOR 1)
-  set(Tutorial_VERSION_MINOR 0)
-
-  # configure a header file to pass some of the CMake settings
-  # to the source code
-  configure_file(
-    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-    )
-
-  # should we use our own math functions
-  option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-  # add the MathFunctions library?
-  if(USE_MYMATH)
-    add_subdirectory(MathFunctions)
-    list(APPEND EXTRA_LIBS MathFunctions)
-    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
-  endif(USE_MYMATH)
-
-  # add the executable
-  add_executable(Tutorial tutorial.cxx)
-
-  target_link_libraries(Tutorial ${EXTRA_LIBS})
-
-  # add the binary tree to the search path for include files
-  # so that we will find TutorialConfig.h
-  target_include_directories(Tutorial PUBLIC
-                             "${PROJECT_BINARY_DIR}"
-                             ${EXTRA_INCLUDES}
-                             )
-
-Note the use of the variables EXTRA_LIBS, and EXTRA_INCLUDES to collect
-up any optional libraries to later be linked into the executable. This is a
-classic approach when dealing with many optional components, we will cover the
-modern approach in the next step. For now the corresponding changes to the
-source code are fairly straightforward and leave us with:
-
-  #ifdef USE_MYMATH
-    double outputValue = mysqrt(inputValue);
-  #else
-    double outputValue = sqrt(inputValue);
-  #endif
-
-Since the source code now requires USE_MYMATH we can add it to the
-TutorialConfig.h.in. Simply add the following line:
-  #cmakedefine USE_MYMATH
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool and then run the built Tutorial executable.
-
-Which function gives better results, Step1’s sqrt or Step2’s mysqrt?
diff --git a/Tests/Tutorial/Step2/tutorial.cxx b/Tests/Tutorial/Step2/tutorial.cxx
deleted file mode 100644
index 75b7d67..0000000
--- a/Tests/Tutorial/Step2/tutorial.cxx
+++ /dev/null
@@ -1,23 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-  double outputValue = sqrt(inputValue);
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step3/CMakeLists.txt b/Tests/Tutorial/Step3/CMakeLists.txt
deleted file mode 100644
index f904ea7..0000000
--- a/Tests/Tutorial/Step3/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
-endif(USE_MYMATH)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-
-target_link_libraries(Tutorial ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           ${EXTRA_INCLUDES}
-                           )
diff --git a/Tests/Tutorial/Step3/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step3/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 7d9379e..0000000
--- a/Tests/Tutorial/Step3/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  double result = x;
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-  return result;
-}
diff --git a/Tests/Tutorial/Step3/TutorialConfig.h.in b/Tests/Tutorial/Step3/TutorialConfig.h.in
deleted file mode 100644
index 25a0602..0000000
--- a/Tests/Tutorial/Step3/TutorialConfig.h.in
+++ /dev/null
@@ -1,5 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
diff --git a/Tests/Tutorial/Step3/directions.txt b/Tests/Tutorial/Step3/directions.txt
deleted file mode 100644
index 54d0318..0000000
--- a/Tests/Tutorial/Step3/directions.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-# Adding Usage Requirements for Library #
-
-Usage requirements allow for far better control over a library / executable's
-link and include line. While also giving more control over the transitive
-property of targets inside CMake. The primary commands that leverage usage
-requirements are:
-
-  - target_compile_definitions
-  - target_compile_options
-  - target_include_directories
-  - target_link_libraries
-
-First up is MathFunctions. We first state that anybody linking to MathFunctions
-needs to include the current source directory, while MathFunctions itself
-doesn't. So this can become an INTERFACE usage requirement.
-
-Remember INTERFACE means things that consumers require but the producer doesn't.
-
-  target_include_directories(MathFunctions
-            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
-
-Now that we've specified usage requirements for MathFunctions we can safely remove
-our uses of the EXTRA_INCLUDES variable.
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool.
diff --git a/Tests/Tutorial/Step3/tutorial.cxx b/Tests/Tutorial/Step3/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step3/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step4/CMakeLists.txt b/Tests/Tutorial/Step4/CMakeLists.txt
deleted file mode 100644
index 34eab55..0000000
--- a/Tests/Tutorial/Step4/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif(USE_MYMATH)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
diff --git a/Tests/Tutorial/Step4/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step4/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 7d9379e..0000000
--- a/Tests/Tutorial/Step4/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  double result = x;
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-  return result;
-}
diff --git a/Tests/Tutorial/Step4/TutorialConfig.h.in b/Tests/Tutorial/Step4/TutorialConfig.h.in
deleted file mode 100644
index 25a0602..0000000
--- a/Tests/Tutorial/Step4/TutorialConfig.h.in
+++ /dev/null
@@ -1,5 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
diff --git a/Tests/Tutorial/Step4/directions.txt b/Tests/Tutorial/Step4/directions.txt
deleted file mode 100644
index 91e4043..0000000
--- a/Tests/Tutorial/Step4/directions.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-# Installing and Testing #
-
-Now we can start adding testing support and install rules to our project.
-
-The install rules are fairly simple; for MathFunctions we install the library
-and header file, for the application we install the executable and configured
-header.
-
-So to MathFunctions/CMakeLists.txt we add:
-
-  install (TARGETS MathFunctions DESTINATION bin)
-  install (FILES MathFunctions.h DESTINATION include)
-
-And the to top-level CMakeLists.txt we add:
-
-  install(TARGETS Tutorial DESTINATION bin)
-  install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-          DESTINATION include
-          )
-
-That is all that is needed to create a basic local install of the tutorial.
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool. Then build the “install” target by typing 'make install'
-from the command line or build the INSTALL target from an IDE. This will
-install the appropriate header files, libraries, and executables.
-
-Verify that the installed Tutorial runs. Note: The CMake variable
-CMAKE_INSTALL_PREFIX is used to determine the root of where the files will
-be installed.
-
-Next let's test our application. Adding testing is an easy process. At the
-end of the top-level CMakeLists file we can add a number of basic tests to
-verify that the application is working correctly.
-
-  # enable testing
-  enable_testing()
-
-  # does the application run
-  add_test(NAME Runs COMMAND Tutorial 25)
-
-  # does the usage message work?
-  add_test(NAME Usage COMMAND Tutorial)
-  set_tests_properties(Usage
-    PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-    )
-
-  # define a function to simplify adding tests
-  function(do_test target arg result)
-    add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-    set_tests_properties(Comp${arg}
-      PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-      )
-  endfunction(do_test)
-
-  # do a bunch of result based tests
-  do_test(Tutorial 25 "25 is 5")
-  do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-  do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-The first test simply verifies that the application runs, does not segfault or
-otherwise crash, and has a zero return value. This is the basic form of a CTest
-test.
-
-The Usage test uses a regular expression to verify that the usage message
-is printed when an incorrect number of arguments are provided.
-
-Lastly, we have a function called do_test that simplifies running the
-application and verifying that the computed square root is correct for given
-input.
-
-To run tests, cd to the binary directory and run “ctest -N” and “ctest -VV”.
diff --git a/Tests/Tutorial/Step4/tutorial.cxx b/Tests/Tutorial/Step4/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step4/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step5/CMakeLists.txt b/Tests/Tutorial/Step5/CMakeLists.txt
deleted file mode 100644
index 63e5410..0000000
--- a/Tests/Tutorial/Step5/CMakeLists.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif()
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Tests/Tutorial/Step5/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step5/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 11cf412..0000000
--- a/Tests/Tutorial/Step5/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-add_library(MathFunctions mysqrt.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          )
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step5/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step5/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 7d9379e..0000000
--- a/Tests/Tutorial/Step5/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  double result = x;
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-  return result;
-}
diff --git a/Tests/Tutorial/Step5/TutorialConfig.h.in b/Tests/Tutorial/Step5/TutorialConfig.h.in
deleted file mode 100644
index 25a0602..0000000
--- a/Tests/Tutorial/Step5/TutorialConfig.h.in
+++ /dev/null
@@ -1,5 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
diff --git a/Tests/Tutorial/Step5/directions.txt b/Tests/Tutorial/Step5/directions.txt
deleted file mode 100644
index e6f5197..0000000
--- a/Tests/Tutorial/Step5/directions.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-# Adding System Introspection #
-
-Let us consider adding some code to our project that depends on features the
-target platform may not have. For this example, we will add some code that
-depends on whether or not the target platform has the log and exp functions. Of
-course almost every platform has these functions but for this tutorial assume
-that they are not common.
-
-If the platform has log and exp then we will use them to compute the square
-root in the mysqrt function. We first test for the availability of these
-functions using the CheckSymbolExists.cmake macro in the top-level CMakeLists
-file as follows:
-
-  # does this system provide the log and exp functions?
-  include(CheckSymbolExists)
-  set(CMAKE_REQUIRED_LIBRARIES "m")
-  check_symbol_exists(log "math.h" HAVE_LOG)
-  check_symbol_exists(exp "math.h" HAVE_EXP)
-
-Now let's add these defines to TutorialConfig.h.in so that we can use them
-from mysqrt.cxx:
-
-  // does the platform provide exp and log functions?
-  #cmakedefine HAVE_LOG
-  #cmakedefine HAVE_EXP
-
-Modify mysqrt.cxx to include math.h. Next, in the mysqrt function we can
-provide an alternate implementation based on log and exp if they are available
-on the system using the following code:
-
-  // if we have both log and exp then use them
-  #if defined(HAVE_LOG) && defined (HAVE_EXP)
-    double result = exp(log(x)*0.5);
-    std::cout << "Computing sqrt of " << x << " to be " << result << " using log" << std::endl;
-  #else
-    ...
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool.
-
-You will notice that even though HAVE_LOG and HAVE_EXP are both defined mysqrt
-isn't using them. We should realize quickly that we have forgotten to include
-TutorialConfig.h in mysqrt.cxx. We will also need to update
-MathFunctions/CMakeLists.txt with where it is located.
-
-So let's go ahead and update MathFunctions/CMakeLists.txt to look like:
-
-  add_library(MathFunctions mysqrt.cxx)
-
-  target_include_directories(MathFunctions
-            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-            PRIVATE ${Tutorial_BINARY_DIR}
-            )
-
-  install(TARGETS MathFunctions DESTINATION lib)
-  install(FILES MathFunctions.h DESTINATION include)
-
-Now all we need to do is include TutorialConfig.h in mysqrt.cxx
-
-At this point you should go ahead and build the project again.
-
-Run the built Tutorial executable. Which function gives better results now,
-Step1’s sqrt or Step5’s mysqrt?
-
-Exercise: Why is it important that we configure TutorialConfig.h.in after the
-checks for HAVE_LOG and HAVE_EXP? What would happen if we inverted the two?
-
-Exercise: Is there a better place for us to save the HAVE_LOG and HAVE_EXP
-values other than in TutorialConfig.h?
diff --git a/Tests/Tutorial/Step5/tutorial.cxx b/Tests/Tutorial/Step5/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step5/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step6/CMakeLists.txt b/Tests/Tutorial/Step6/CMakeLists.txt
deleted file mode 100644
index 503a312..0000000
--- a/Tests/Tutorial/Step6/CMakeLists.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include(CheckSymbolExists)
-set(CMAKE_REQUIRED_LIBRARIES "m")
-check_symbol_exists(log "math.h" HAVE_LOG)
-check_symbol_exists(exp "math.h" HAVE_EXP)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif()
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Tests/Tutorial/Step6/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step6/MathFunctions/CMakeLists.txt
deleted file mode 100644
index 2946075..0000000
--- a/Tests/Tutorial/Step6/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-add_library(MathFunctions mysqrt.cxx)
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-
-target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE ${Tutorial_BINARY_DIR}
-          )
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step6/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step6/MathFunctions/mysqrt.cxx
deleted file mode 100644
index b9ad20a..0000000
--- a/Tests/Tutorial/Step6/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-#include <iostream>
-
-#include <cmath>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  double result = x;
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
diff --git a/Tests/Tutorial/Step6/TutorialConfig.h.in b/Tests/Tutorial/Step6/TutorialConfig.h.in
deleted file mode 100644
index a091265..0000000
--- a/Tests/Tutorial/Step6/TutorialConfig.h.in
+++ /dev/null
@@ -1,9 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
-// does the platform provide exp and log functions?
-#cmakedefine HAVE_LOG
-#cmakedefine HAVE_EXP
-
diff --git a/Tests/Tutorial/Step6/directions.txt b/Tests/Tutorial/Step6/directions.txt
deleted file mode 100644
index 42b9f06..0000000
--- a/Tests/Tutorial/Step6/directions.txt
+++ /dev/null
@@ -1,104 +0,0 @@
-# Adding a Custom Command and Generated File #
-
-In this section we will show how you can add a generated source file into the
-build process of an application. For this example, we will create a table of
-precomputed square roots as part of the build process, and then compile that
-table into our application.
-
-To accomplish this, we first need a program that will generate the table. In the
-MathFunctions subdirectory a new source file named MakeTable.cxx will do just that.
-
-  // A simple program that builds a sqrt table
-  #include <iostream>
-  #include <fstream>
-  #include <cmath>
-
-  int main (int argc, char *argv[])
-  {
-    // make sure we have enough arguments
-    if (argc < 2) {
-      return 1;
-    }
-
-    std::ofstream fout(argv[1],std::ios_base::out);
-    const bool fileOpen = fout.is_open();
-    if(fileOpen) {
-      fout << "double sqrtTable[] = {" << std::endl;
-      for (int i = 0; i < 10; ++i) {
-        fout << sqrt(static_cast<double>(i)) << "," << std::endl;
-      }
-      // close the table with a zero
-      fout << "0};" << std::endl;
-      fout.close();
-    }
-    return fileOpen ? 0 : 1; // return 0 if wrote the file
-  }
-
-Note that the table is produced as valid C++ code and that the output filename
-is passed in as an argument.
-
-The next step is to add the appropriate commands to MathFunctions’ CMakeLists
-file to build the MakeTable executable and then run it as part of the build
-process. A few commands are needed to accomplish this, as shown below:
-
-  # first we add the executable that generates the table
-  add_executable(MakeTable MakeTable.cxx)
-
-  # add the command to generate the source code
-  add_custom_command(
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-    DEPENDS MakeTable
-    )
-
-  # add the main library
-  add_library(MathFunctions
-              mysqrt.cxx
-              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-              )
-
-  target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PUBLIC ${Tutorial_BINARY_DIR}
-                 # add the binary tree directory to the search path for include files
-                 ${CMAKE_CURRENT_BINARY_DIR}
-          )
-
-  install(TARGETS MathFunctions DESTINATION lib)
-  install(FILES MathFunctions.h DESTINATION include)
-
-First, the executable for MakeTable is added as any other executable would be
-added. Then we add a custom command that specifies how to produce Table.h by
-running MakeTable. Next we have to let CMake know that mysqrt.cxx depends on
-the generated file Table.h. This is done by adding the generated Table.h to the
-list of sources for the library MathFunctions. We also have to add the current
-binary directory to the list of include directories so that Table.h can be
-found and included by mysqrt.cxx.
-
-Now let's use the generated table. First, modify mysqrt.cxx to include Table.h.
-Next, we can rewrite the mysqrt function to use the table:
-
-  if (x <= 0) {
-    return 0;
-  }
-
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result*result);
-    result = result + 0.5*delta/result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-
-Run cmake or cmake-gui to configure the project and then build it with your
-chosen build tool. When this project is built it will first build the MakeTable
-executable. It will then run MakeTable to produce Table.h. Finally, it will
-compile mysqrt.cxx which includes Table.h to produce the MathFunctions library.
diff --git a/Tests/Tutorial/Step6/tutorial.cxx b/Tests/Tutorial/Step6/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step6/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step7/CMakeLists.txt b/Tests/Tutorial/Step7/CMakeLists.txt
deleted file mode 100644
index f2d3839..0000000
--- a/Tests/Tutorial/Step7/CMakeLists.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include(CheckSymbolExists)
-set(CMAKE_REQUIRED_LIBRARIES "m")
-check_symbol_exists(log "math.h" HAVE_LOG)
-check_symbol_exists(exp "math.h" HAVE_EXP)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif(USE_MYMATH)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                            )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Tests/Tutorial/Step7/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step7/MathFunctions/CMakeLists.txt
deleted file mode 100644
index dc3eb98..0000000
--- a/Tests/Tutorial/Step7/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  )
-
-# add the main library
-add_library(MathFunctions
-            mysqrt.cxx
-            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-            )
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-# state that we depend on our binary dir to find Table.h
-target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE ${Tutorial_BINARY_DIR}
-                  ${CMAKE_CURRENT_BINARY_DIR}
-          )
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step7/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step7/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 5272f56..0000000
--- a/Tests/Tutorial/Step7/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-
-  return result;
-}
diff --git a/Tests/Tutorial/Step7/TutorialConfig.h.in b/Tests/Tutorial/Step7/TutorialConfig.h.in
deleted file mode 100644
index a091265..0000000
--- a/Tests/Tutorial/Step7/TutorialConfig.h.in
+++ /dev/null
@@ -1,9 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
-// does the platform provide exp and log functions?
-#cmakedefine HAVE_LOG
-#cmakedefine HAVE_EXP
-
diff --git a/Tests/Tutorial/Step7/build1.cmake b/Tests/Tutorial/Step7/build1.cmake
deleted file mode 100644
index baa475f..0000000
--- a/Tests/Tutorial/Step7/build1.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-set(CTEST_SOURCE_DIRECTORY "$ENV{HOME}/Dashboards/My Tests/CMake/Tests/Tutorial/Step7")
-set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}-build1")
-
-set(CTEST_CMAKE_COMMAND "cmake")
-set(CTEST_COMMAND "ctest -D Experimental")
diff --git a/Tests/Tutorial/Step7/directions.txt b/Tests/Tutorial/Step7/directions.txt
deleted file mode 100644
index 7d7c2ea..0000000
--- a/Tests/Tutorial/Step7/directions.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-# Building an Installer #
-
-Next suppose that we want to distribute our project to other people so that they
-can use it. We want to provide both binary and source distributions on a variety
-of platforms. This is a little different from the install we did previously in
-the Installing and Testing section (Step 4), where we were installing the
-binaries that we had built from the source code. In this example we will be
-building installation packages that support binary installations and package
-management features. To accomplish this we will use CPack to create platform
-specific installers. Specifically we need to add a few lines to the bottom of
-our top-level CMakeLists.txt file.
-
-  include(InstallRequiredSystemLibraries)
-  set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-  set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-  set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-  include(CPack)
-
-That is all there is to it. We start by including InstallRequiredSystemLibraries.
-This module will include any runtime libraries that are needed by the project
-for the current platform. Next we set some CPack variables to where we have
-stored the license and version information for this project. The version
-information makes use of the variables we set earlier in this tutorial. Finally
-we include the CPack module which will use these variables and some other
-properties of the system you are on to setup an installer.
-
-The next step is to build the project in the usual manner and then run CPack
-on it. To build a binary distribution you would run:
-
-  cpack
-
-To create a source distribution you would type:
-
-  cpack -C CPackSourceConfig.cmake
-
-Alternatively, run “make package” or right click the Package target and
-“Build Project” from an IDE.
-
-Run the installer executable found  in the binary directory. Then run the
-installed executable and verify that it works.
diff --git a/Tests/Tutorial/Step7/tutorial.cxx b/Tests/Tutorial/Step7/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step7/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step8/CMakeLists.txt b/Tests/Tutorial/Step8/CMakeLists.txt
deleted file mode 100644
index c66bf96..0000000
--- a/Tests/Tutorial/Step8/CMakeLists.txt
+++ /dev/null
@@ -1,82 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include(CheckSymbolExists)
-set(CMAKE_REQUIRED_LIBRARIES "m")
-check_symbol_exists(log "math.h" HAVE_LOG)
-check_symbol_exists(exp "math.h" HAVE_EXP)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif(USE_MYMATH)
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-enable_testing()
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
diff --git a/Tests/Tutorial/Step8/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step8/MathFunctions/CMakeLists.txt
deleted file mode 100644
index dc3eb98..0000000
--- a/Tests/Tutorial/Step8/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  )
-
-# add the main library
-add_library(MathFunctions
-            mysqrt.cxx
-            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-            )
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-# state that we depend on our binary dir to find Table.h
-target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE ${Tutorial_BINARY_DIR}
-                  ${CMAKE_CURRENT_BINARY_DIR}
-          )
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step8/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step8/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 5b862fb..0000000
--- a/Tests/Tutorial/Step8/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "MathFunctions.h"
-#include "TutorialConfig.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
diff --git a/Tests/Tutorial/Step8/TutorialConfig.h.in b/Tests/Tutorial/Step8/TutorialConfig.h.in
deleted file mode 100644
index e97ce24..0000000
--- a/Tests/Tutorial/Step8/TutorialConfig.h.in
+++ /dev/null
@@ -1,8 +0,0 @@
-// the configured options and settings for Tutorial
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
-
-// does the platform provide exp and log functions?
-#cmakedefine HAVE_LOG
-#cmakedefine HAVE_EXP
diff --git a/Tests/Tutorial/Step8/directions.txt b/Tests/Tutorial/Step8/directions.txt
deleted file mode 100644
index 588d9c6..0000000
--- a/Tests/Tutorial/Step8/directions.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# Adding Support for a Dashboard #
-
-Adding support for submitting our test results to a dashboard is very easy. We
-already defined a number of tests for our project in the earlier steps of this
-tutorial. We just have to run those tests and submit them to a dashboard. To
-include support for dashboards we include the CTest module in our top-level
-CMakeLists.txt.
-
-Replace:
-  # enable testing
-  enable_testing()
-
-With:
-  # enable dashboard scripting
-  include(CTest)
-
-The CTest module will automatically call enable_testing(), so
-we can remove it from our CMake files.
-
-We will also need to create a CTestConfig.cmake file where we can specify the
-name of the project and where to submit the dashboard.
-
-  set(CTEST_PROJECT_NAME "CMakeTutorial")
-  set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
-
-  set(CTEST_DROP_METHOD "http")
-  set(CTEST_DROP_SITE "my.cdash.org/")
-  set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
-  set(CTEST_DROP_SITE_CDASH TRUE)
-
-CTest will read in this file when it runs. To create a simple dashboard you can
-run cmake or cmake-gui to configure the project, but do not build it yet.
-Instead, change directory to the binary tree, and then run:
- 'ctest [-VV] –D Experimental'. On Windows, build the EXPERIMENTAL target.
-
-Ctest will build and test the project and submit results to the Kitware public
-dashboard. The results of your dashboard will be uploaded to Kitware's public
-dashboard here: https://my.cdash.org/index.php?project=CMakeTutorial.
diff --git a/Tests/Tutorial/Step8/tutorial.cxx b/Tests/Tutorial/Step8/tutorial.cxx
deleted file mode 100644
index 1d5742d..0000000
--- a/Tests/Tutorial/Step8/tutorial.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/Tutorial/Step9/CMakeLists.txt b/Tests/Tutorial/Step9/CMakeLists.txt
deleted file mode 100644
index 309d513..0000000
--- a/Tests/Tutorial/Step9/CMakeLists.txt
+++ /dev/null
@@ -1,81 +0,0 @@
-cmake_minimum_required(VERSION 3.3)
-project(Tutorial)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# the version number.
-set(Tutorial_VERSION_MAJOR 1)
-set(Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include(CheckSymbolExists)
-set(CMAKE_REQUIRED_LIBRARIES "m")
-check_symbol_exists(log "math.h" HAVE_LOG)
-check_symbol_exists(exp "math.h" HAVE_EXP)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# configure a header file to pass the version number only
-configure_file(
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the MathFunctions library?
-if(USE_MYMATH)
-  add_subdirectory(MathFunctions)
-  list(APPEND EXTRA_LIBS MathFunctions)
-endif()
-
-# add the executable
-add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-target_include_directories(Tutorial PUBLIC
-                           "${PROJECT_BINARY_DIR}"
-                           )
-
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
-
-# enable testing
-include(CTest)
-
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
-
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction(do_test)
-
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include(CPack)
diff --git a/Tests/Tutorial/Step9/CTestConfig.cmake b/Tests/Tutorial/Step9/CTestConfig.cmake
deleted file mode 100644
index 7a927ac..0000000
--- a/Tests/Tutorial/Step9/CTestConfig.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-## This file should be placed in the root directory of your project.
-## Then modify the CMakeLists.txt file in the root directory of your
-## project to incorporate the testing dashboard.
-##
-## # The following are required to submit to the CDash dashboard:
-##   ENABLE_TESTING()
-##   INCLUDE(CTest)
-
-set(CTEST_PROJECT_NAME "CMakeTutorial")
-set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
-
-set(CTEST_DROP_METHOD "http")
-set(CTEST_DROP_SITE "my.cdash.org")
-set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
-set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Tests/Tutorial/Step9/MathFunctions/CMakeLists.txt b/Tests/Tutorial/Step9/MathFunctions/CMakeLists.txt
deleted file mode 100644
index e651a57..0000000
--- a/Tests/Tutorial/Step9/MathFunctions/CMakeLists.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  )
-
-# add the main library
-add_library(MathFunctions
-            mysqrt.cxx
-            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-            )
-
-# state that anybody linking to us needs to include the current source dir
-# to find MathFunctions.h, while we don't.
-# state that we depend on our binary dir to find Table.h
-target_include_directories(MathFunctions
-          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE   ${CMAKE_CURRENT_BINARY_DIR}
-          )
-
-# use compile definitions to state if we have enabled USE_MYMATH
-# and that anything that links to use will get this define
-target_compile_definitions(MathFunctions INTERFACE "USE_MYMATH")
-
-if(HAVE_LOG AND HAVE_EXP)
-  target_compile_definitions(MathFunctions
-                             PRIVATE "HAVE_LOG" "HAVE_EXP")
-endif()
-
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
diff --git a/Tests/Tutorial/Step9/MathFunctions/MathFunctions.cxx b/Tests/Tutorial/Step9/MathFunctions/MathFunctions.cxx
deleted file mode 100644
index 5351184..0000000
--- a/Tests/Tutorial/Step9/MathFunctions/MathFunctions.cxx
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#include "MathFunctions.h"
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Tests/Tutorial/Step9/MathFunctions/mysqrt.cxx b/Tests/Tutorial/Step9/MathFunctions/mysqrt.cxx
deleted file mode 100644
index 8b82141..0000000
--- a/Tests/Tutorial/Step9/MathFunctions/mysqrt.cxx
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "MathFunctions.h"
-#include <iostream>
-
-// include the generated table
-#include "Table.h"
-
-#include <cmath>
-
-// a hack square root calculation using simple operations
-double mysqrt(double x)
-{
-  if (x <= 0) {
-    return 0;
-  }
-
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = exp(log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
-            << std::endl;
-#else
-  // use the table to help find an initial value
-  double result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
-
-  // if we have both log and exp then use them
-
-  // do ten iterations
-  for (int i = 0; i < 10; ++i) {
-    if (result <= 0) {
-      result = 0.1;
-    }
-    double delta = x - (result * result);
-    result = result + 0.5 * delta / result;
-    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
-  }
-#endif
-  return result;
-}
diff --git a/Tests/Tutorial/Step9/TutorialConfig.h.in b/Tests/Tutorial/Step9/TutorialConfig.h.in
deleted file mode 100644
index 8cd2fc9..0000000
--- a/Tests/Tutorial/Step9/TutorialConfig.h.in
+++ /dev/null
@@ -1,3 +0,0 @@
-// the configured version number
-#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
-#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
diff --git a/Tests/Tutorial/Step9/directions.txt b/Tests/Tutorial/Step9/directions.txt
deleted file mode 100644
index 8771637..0000000
--- a/Tests/Tutorial/Step9/directions.txt
+++ /dev/null
@@ -1,166 +0,0 @@
-# Mixing Static and Shared #
-
-In this section we will show how by using the BUILD_SHARED_LIBS variable we can
-control the default behavior of add_library, and allow control over how
-libraries without an explicit type ( STATIC/SHARED/MODULE/OBJECT ) are built.
-
-To accomplish this we need to add BUILD_SHARED_LIBS to the top level
-CMakeLists.txt. We use the option command as it allows users to optionally
-select if the value should be On or Off.
-
-Next we are going to refactor MathFunctions to become a real library that
-encapsulates using mysqrt or sqrt, instead of requiring the calling code
-to do this logic. This will also mean that USE_MYMATH will not control building
-MathFuctions, but instead will control the behavior of this library.
-
-The first step is to update the starting section of the top level CMakeLists.txt
-to look like:
-
-  cmake_minimum_required(VERSION 3.3)
-  project(Tutorial)
-
-  # 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_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-  option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
-
-  # the version number.
-  set(Tutorial_VERSION_MAJOR 1)
-  set(Tutorial_VERSION_MINOR 0)
-
-  # configure a header file to pass the version number only
-  configure_file(
-    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-    )
-
-  # add the MathFunctions library
-  add_subdirectory(MathFunctions)
-
-  # add the executable
-  add_executable(Tutorial tutorial.cxx)
-  target_link_libraries(Tutorial PUBLIC MathFunctions)
-
-Now that we have made MathFunctions always be used, we will need to update
-the logic of that library. So, in MathFunctions/CMakeLists.txt we need to
-create a SqrtLibrary that will conditionally be built when USE_MYMATH is
-enabled. Now, since this is a tutorial, we are going to explicitly require
-that SqrtLibrary is built statically.
-
-The end result is that MathFunctions/CMakeLists.txt should look like:
-
-  # add the library that runs
-  add_library(MathFunctions MathFunctions.cxx)
-
-  # state that anybody linking to us needs to include the current source dir
-  # to find MathFunctions.h, while we don't.
-  target_include_directories(MathFunctions
-                             INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-                             )
-
-  # should we use our own math functions
-  option(USE_MYMATH "Use tutorial provided math implementation" ON)
-  if(USE_MYMATH)
-
-    # does this system provide the log and exp functions?
-    include(CheckSymbolExists)
-    set(CMAKE_REQUIRED_LIBRARIES "m")
-    check_symbol_exists(log "math.h" HAVE_LOG)
-    check_symbol_exists(exp "math.h" HAVE_EXP)
-
-    # first we add the executable that generates the table
-    add_executable(MakeTable MakeTable.cxx)
-
-    # add the command to generate the source code
-    add_custom_command(
-      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-      COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-      DEPENDS MakeTable
-      )
-
-    # library that just does sqrt
-    add_library(SqrtLibrary STATIC
-                mysqrt.cxx
-                ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-                )
-
-    # state that we depend on our binary dir to find Table.h
-    target_include_directories(SqrtLibrary PRIVATE
-                               ${CMAKE_CURRENT_BINARY_DIR}
-                               )
-
-    target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
-    if(HAVE_LOG AND HAVE_EXP)
-      target_compile_definitions(SqrtLibrary
-                                 PRIVATE "HAVE_LOG" "HAVE_EXP")
-    endif()
-
-    target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-  endif()
-
-  # define the symbol stating we are using the declspec(dllexport) when
-  # building on windows
-  target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-  install(TARGETS MathFunctions DESTINATION lib)
-  install(FILES MathFunctions.h DESTINATION include)
-
-Next, update MathFunctions/mysqrt.cxx to use the mathfunctions and detail namespaces:
-
-  #include <iostream>
-  #include "MathFunctions.h"
-
-  // include the generated table
-  #include "Table.h"
-
-  #include <cmath>
-
-  namespace mathfunctions {
-  namespace detail {
-  // a hack square root calculation using simple operations
-  double mysqrt(double x)
-  {
-    ...
-
-    return result;
-  }
-  }
-  }
-
-We also need to make some changes in tutorial.cxx, so that it no longer uses USE_MYMATH:
-1. Always include MathFunctions.h
-2. Always use mathfunctions::sqrt
-
-Finally, update MathFunctions/MathFunctions.h to use dll export defines:
-
-  #if defined(_WIN32)
-  #if defined(EXPORTING_MYMATH)
-    #define DECLSPEC  __declspec(dllexport)
-  #else
-    #define DECLSPEC  __declspec(dllimport)
-  #endif
-  #else //non windows
-    #define DECLSPEC
-  #endif
-
-  namespace mathfunctions
-  {
-    double DECLSPEC sqrt(double x);
-  }
-
-At this point, if you build everything, you will notice that linking fails
-as we are combining a static library without position enabled code with a
-library that has position enabled code. This solution to this is to explicitly
-set the POSITION_INDEPENDENT_CODE target property of SqrtLibrary to be True no
-matter the build type.
-
-Exercise: We modified MathFunctions.h to use dll export defines. Using CMake
-documentation can you find a helper module to simplify this?
-
-Exercise: Determine what command is enabling PIC for SqrtLibrary.
-What happens if we remove said command?
diff --git a/Tests/Tutorial/Step9/tutorial.cxx b/Tests/Tutorial/Step9/tutorial.cxx
deleted file mode 100644
index 73e67a9..0000000
--- a/Tests/Tutorial/Step9/tutorial.cxx
+++ /dev/null
@@ -1,33 +0,0 @@
-// A simple program that computes the square root of a number
-#include <cmath>
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include "TutorialConfig.h"
-
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
-
-int main(int argc, char* argv[])
-{
-  if (argc < 2) {
-    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
-              << Tutorial_VERSION_MAJOR << std::endl;
-    std::cout << "Usage: " << argv[0] << " number" << std::endl;
-    return 1;
-  }
-
-  double inputValue = std::stod(argv[1]);
-
-#ifdef USE_MYMATH
-  double outputValue = mysqrt(inputValue);
-#else
-  double outputValue = sqrt(inputValue);
-#endif
-
-  std::cout << "The square root of " << inputValue << " is " << outputValue
-            << std::endl;
-  return 0;
-}
diff --git a/Tests/VSExternalInclude/Lib2/lib2.cpp b/Tests/VSExternalInclude/Lib2/lib2.cpp
index adc2d29..3718017 100644
--- a/Tests/VSExternalInclude/Lib2/lib2.cpp
+++ b/Tests/VSExternalInclude/Lib2/lib2.cpp
@@ -1,5 +1,6 @@
 
 #include "lib2.h"
+
 #include "lib1.h"
 
 int add1_and_mult2(int num)
diff --git a/Tests/VSMidl/src/main.cpp b/Tests/VSMidl/src/main.cpp
index 68089ad..ae114fe 100644
--- a/Tests/VSMidl/src/main.cpp
+++ b/Tests/VSMidl/src/main.cpp
@@ -1,6 +1,7 @@
+#include <test_i.c>
+
 #include <stdio.h>
 #include <test.h>
-#include <test_i.c>
 
 int main(int argc, char** argv)
 {
diff --git a/Tests/VSNsightTegra/jni/second.c b/Tests/VSNsightTegra/jni/second.c
index 12fcdb6..30bdc17 100644
--- a/Tests/VSNsightTegra/jni/second.c
+++ b/Tests/VSNsightTegra/jni/second.c
@@ -14,9 +14,10 @@
  * limitations under the License.
  *
  */
-#include "first.h"
 #include <jni.h>
 
+#include "first.h"
+
 jint Java_com_example_twolibs_TwoLibs_add(JNIEnv* env, jobject this, jint x,
                                           jint y)
 {
diff --git a/Tests/VSResource/main.cpp b/Tests/VSResource/main.cpp
index b2b5ac9..6de7adc 100644
--- a/Tests/VSResource/main.cpp
+++ b/Tests/VSResource/main.cpp
@@ -1,6 +1,7 @@
-#include <stdio.h>
 #include <windows.h>
 
+#include <stdio.h>
+
 extern int lib();
 
 struct x
diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt
index efc7760..b8e157d 100644
--- a/Tests/VSWinStorePhone/CMakeLists.txt
+++ b/Tests/VSWinStorePhone/CMakeLists.txt
@@ -9,6 +9,7 @@
 endif()
 
 add_subdirectory(WinRT)
+add_subdirectory(CxxDLL)
 
 set (APP_MANIFEST_NAME Package.appxmanifest)
 if("${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsPhone")
@@ -151,4 +152,4 @@
   set_property(TARGET ${EXE_NAME} PROPERTY VS_SDK_REFERENCES "Microsoft.UniversalCRT.Debug, Version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
 endif()
 
-target_link_libraries(${EXE_NAME} d3d11 JusticeLeagueWinRT)
+target_link_libraries(${EXE_NAME} d3d11 JusticeLeagueWinRT CxxDll)
diff --git a/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt b/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt
new file mode 100644
index 0000000..6bd32a2
--- /dev/null
+++ b/Tests/VSWinStorePhone/CxxDLL/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(CxxDll CXX)
+
+add_library(CxxDll SHARED cxxdll.cpp)
diff --git a/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp b/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp
new file mode 100644
index 0000000..1438e8a
--- /dev/null
+++ b/Tests/VSWinStorePhone/CxxDLL/cxxdll.cpp
@@ -0,0 +1,8 @@
+#include "cxxdll.h"
+
+#include <iostream>
+
+void CxxDllClass::SomeMethod()
+{
+  std::cout << "CxxDllClass::SomeMethod\n";
+}
diff --git a/Tests/VSWinStorePhone/CxxDLL/cxxdll.h b/Tests/VSWinStorePhone/CxxDLL/cxxdll.h
new file mode 100644
index 0000000..86edceb
--- /dev/null
+++ b/Tests/VSWinStorePhone/CxxDLL/cxxdll.h
@@ -0,0 +1,5 @@
+class __declspec(dllexport) CxxDllClass
+{
+public:
+  static void SomeMethod();
+};
diff --git a/Tests/VSWinStorePhone/Direct3DApp1/CubeRenderer.cpp b/Tests/VSWinStorePhone/Direct3DApp1/CubeRenderer.cpp
index 3ba35fa..595f553 100644
--- a/Tests/VSWinStorePhone/Direct3DApp1/CubeRenderer.cpp
+++ b/Tests/VSWinStorePhone/Direct3DApp1/CubeRenderer.cpp
@@ -1,6 +1,8 @@
+// clang-format off
 #include "pch.h"
 
 #include "CubeRenderer.h"
+// clang-format on
 
 using namespace DirectX;
 using namespace Microsoft::WRL;
diff --git a/Tests/VSWinStorePhone/Direct3DApp1/Direct3DApp1.h b/Tests/VSWinStorePhone/Direct3DApp1/Direct3DApp1.h
index c3499c7..79b9070 100644
--- a/Tests/VSWinStorePhone/Direct3DApp1/Direct3DApp1.h
+++ b/Tests/VSWinStorePhone/Direct3DApp1/Direct3DApp1.h
@@ -1,8 +1,10 @@
 #pragma once
 
+// clang-format off
 #include "pch.h"
 
 #include "CubeRenderer.h"
+// clang-format on
 
 ref class Direct3DApp1 sealed
   : public Windows::ApplicationModel::Core::IFrameworkView
diff --git a/Tests/VSWinStorePhone/Direct3DApp1/Direct3DBase.cpp b/Tests/VSWinStorePhone/Direct3DApp1/Direct3DBase.cpp
index 0662fbe..f24ce28 100644
--- a/Tests/VSWinStorePhone/Direct3DApp1/Direct3DBase.cpp
+++ b/Tests/VSWinStorePhone/Direct3DApp1/Direct3DBase.cpp
@@ -1,6 +1,8 @@
+// clang-format off
 #include "pch.h"
 
 #include "Direct3DBase.h"
+// clang-format on
 
 using namespace DirectX;
 using namespace Microsoft::WRL;
diff --git a/Tests/VSWinStorePhone/Direct3DApp1/pch.h b/Tests/VSWinStorePhone/Direct3DApp1/pch.h
index 78ebea3..d80cdb7 100644
--- a/Tests/VSWinStorePhone/Direct3DApp1/pch.h
+++ b/Tests/VSWinStorePhone/Direct3DApp1/pch.h
@@ -1,7 +1,8 @@
 #pragma once
 
+#include <memory>
+
 #include <DirectXMath.h>
 #include <agile.h>
 #include <d3d11_1.h>
-#include <memory>
 #include <wrl/client.h>
diff --git a/Tests/VSWinStorePhone/VerifyAppPackage.cmake b/Tests/VSWinStorePhone/VerifyAppPackage.cmake
new file mode 100644
index 0000000..f9440d7
--- /dev/null
+++ b/Tests/VSWinStorePhone/VerifyAppPackage.cmake
@@ -0,0 +1,34 @@
+set(APP_PKG_NAME Direct3DApp1)
+
+# List of files that are expected to be present in the generated app package
+set(EXPECTED_APP_PKG_CONTENT
+  ${APP_PKG_NAME}.exe
+  CxxDll.dll
+  JusticeLeagueWinRT.winmd
+  JusticeLeagueWinRT.dll
+)
+
+# Windows app package formats can be either msix, appx or xap
+file(GLOB_RECURSE ALL_APP_PKG_FILES ${APP_PACKAGE_DIR} ${APP_PKG_NAME}*.msix ${APP_PKG_NAME}*.appx ${APP_PKG_NAME}*.xap)
+
+# There can be only one generated app package
+list(LENGTH ALL_APP_PKG_FILES APP_PKG_COUNT)
+if(NOT APP_PKG_COUNT EQUAL 1)
+  message(FATAL_ERROR "Expected 1 generated app package, but detected ${APP_PKG_COUNT}: ${ALL_APP_PKG_FILES}")
+endif()
+
+execute_process(COMMAND ${CMAKE_COMMAND} -E tar tf ${ALL_APP_PKG_FILES}
+  OUTPUT_VARIABLE APP_PKG_CONTENT_OUTPUT
+  ERROR_VARIABLE error
+  RESULT_VARIABLE result)
+
+if(NOT result EQUAL 0)
+  message(FATAL_ERROR "Listing app package content failed with: ${error}")
+endif()
+
+foreach(app_pkg_item ${EXPECTED_APP_PKG_CONTENT})
+  string(FIND ${APP_PKG_CONTENT_OUTPUT} ${app_pkg_item} _found)
+  if(_found EQUAL -1)
+    message(FATAL_ERROR "Generated app package is missing an expected item: ${app_pkg_item}")
+  endif()
+endforeach()
diff --git a/Tests/VSXaml/App.xaml.cpp b/Tests/VSXaml/App.xaml.cpp
index 549b7ef..f9ae27e 100644
--- a/Tests/VSXaml/App.xaml.cpp
+++ b/Tests/VSXaml/App.xaml.cpp
@@ -3,9 +3,8 @@
 // Implementation of the App class.
 //
 
-#include "pch.h"
-
 #include "MainPage.xaml.h"
+#include "pch.h"
 
 using namespace VSXaml;
 
diff --git a/Tests/VSXaml/MainPage.xaml.cpp b/Tests/VSXaml/MainPage.xaml.cpp
index 51da237..f372772 100644
--- a/Tests/VSXaml/MainPage.xaml.cpp
+++ b/Tests/VSXaml/MainPage.xaml.cpp
@@ -3,10 +3,10 @@
 // Implementation of the MainPage class.
 //
 
-#include "pch.h"
-
 #include "MainPage.xaml.h"
 
+#include "pch.h"
+
 using namespace VSXaml;
 
 using namespace Platform;
diff --git a/Tests/X11/HelloWorldX11.cxx b/Tests/X11/HelloWorldX11.cxx
index e59248b..f18f8c8 100644
--- a/Tests/X11/HelloWorldX11.cxx
+++ b/Tests/X11/HelloWorldX11.cxx
@@ -14,6 +14,7 @@
 #  define MAIN_H 1
 
 #  include <iostream>
+
 #  include <stdlib.h>
 
 /* include the X library headers */
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index ce4cfaf..5bf13f3 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,11 +3,11 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.1...3.15 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
-  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/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 0393ff1..ef31e8b 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -23,6 +23,7 @@
   # HACK: check whether this can be removed with next iwyu release.
   { include: [ "<bits/shared_ptr.h>", private, "<memory>", public ] },
   { include: [ "<bits/std_function.h>", private, "<functional>", public ] },
+  { include: [ "<bits/refwrap.h>", private, "<functional>", public ] },
   { include: [ "<bits/stdint-intn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/stdint-uintn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/time.h>", private, "<time.h>", public ] },
@@ -42,10 +43,19 @@
   { symbol: [ "std::istringstream", private, "<sstream>", public ] },
   { symbol: [ "std::ostringstream", private, "<sstream>", public ] },
 
-  # HACK: iwyu suggests those two files each time vector[] is used.
+  # HACK: iwyu suggests <ext/alloc_traits.h> and <memory> each time vector[] is used.
   # https://github.com/include-what-you-use/include-what-you-use/issues/166
   { include: [ "<ext/alloc_traits.h>", private, "<vector>", public ] },
-  { include: [ "<memory>", public, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<cmComputeComponentGraph::TarjanEntry> >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<cmFortranFile> >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<cmGraphEdgeList> >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<cmOrderDirectories::ConflictList> >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<cmStateSnapshot> >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<std::basic_string<char> > >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > >::value_type", private, "<vector>", public ] },
+  { symbol: [ "std::allocator_traits<std::allocator<uv_stdio_container_s> >::value_type", private, "<vector>", public ] },
 
   # TODO: enable this block and remove some <utility> includes?
   #{ symbol: [ "std::pair", private, "<utility>", public ] },
@@ -64,6 +74,7 @@
   # Use '-Xiwyu -v7' to see the fully qualified names that need this.
   # TODO: Can this be simplified with an @-expression?
   #{ symbol: [ "@std::__decay_and_strip<.*>::__type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "std::__decay_and_strip<char const (&)[1]>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmCommand *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
@@ -84,7 +95,6 @@
   { include: [ "<inttypes.h>", public, "\"cm_kwiml.h\"", public ] },
 
   # Self-sufficient wrapper for <sys/stat.h>
-  { include: [ "<sys/stat.h>", public, "\"cm_sys_stat.h\"", public ] },
   { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] },
 
   # Wrappers for 3rd-party libraries used from the system.
diff --git a/Utilities/Release/README b/Utilities/Release/README
deleted file mode 100644
index 11de1c3..0000000
--- a/Utilities/Release/README
+++ /dev/null
@@ -1,18 +0,0 @@
-To create a cmake release, make sure the "release" tag is pointing to the
-expected git commit:
-
-https://cmake.org/gitweb?p=cmake.git;a=shortlog;h=refs/heads/release
-
-Then as kitware@hythloth, using an up-to-date CMake:
-
-  cd ~/CMakeReleases/cmake/Utilities/Release
-  mkdir 283rc1
-  cd 283rc1
-  ~/CMakeReleases/build/bin/cmake -DCMAKE_CREATE_VERSION=release -P ../create-cmake-release.cmake
-  ./create-release.sh
-
-
-create-cmake-release.cmake:  script to run to create release sh scripts
-Add or remove machines in create-cmake-release.cmake.
-
-machine_release.cmake : config files for each machine
diff --git a/Utilities/Release/README.rst b/Utilities/Release/README.rst
new file mode 100644
index 0000000..de294d1
--- /dev/null
+++ b/Utilities/Release/README.rst
@@ -0,0 +1,84 @@
+CMake Release Utilities
+***********************
+
+This directory contains scripts used to package CMake itself for distribution
+on ``cmake.org``.  See also the `CMake Source Code Guide`_.
+
+.. _`CMake Source Code Guide`: ../../Help/dev/source.rst
+
+Docker
+------
+
+The ``linux/<arch>/`` directories contain Docker specifications that anyone
+may use to produce Linux binaries for CMake:
+
+* ``linux/<arch>/base/Dockerfile``:
+  Produces a base image with a build environment for portable CMake binaries.
+  This image is published in the `kitware/cmake Docker Hub Repository`_
+  with tag ``build-linux-<arch>-base-<date>``.
+
+* ``linux/<arch>/deps/Dockerfile``:
+  Produces an image with custom-built dependencies for portable CMake binaries.
+  This image is published in the `kitware/cmake Docker Hub Repository`_
+  with tag ``build-linux-<arch>-deps-<date>``.
+
+* ``linux/<arch>/Dockerfile``:
+  Produce an image containing a portable CMake binary package for Linux.
+  Build this image using the CMake source directory as the build context.
+  The resulting image will have an ``/out`` directory containing the package.
+  For example:
+
+  .. code-block:: console
+
+    $ docker build --tag=cmake:build --network none \
+        -f cmake-src/Utilities/Release/linux/$arch/Dockerfile cmake-src
+    $ docker container create --name cmake-build cmake:build
+    $ docker cp cmake-build:/out .
+    $ ls out/cmake-*-Linux-$arch.*
+
+* ``linux/<arch>/test/Dockerfile``:
+  Produces a base image with a test environment for packaged CMake binaries.
+  For example, build the test base image:
+
+  .. code-block:: console
+
+    $ docker build --tag=cmake:test-base \
+        cmake-src/Utilities/Release/linux/$arch/test
+
+  Then create a local ``test/Dockerfile`` to prepare an image with both the
+  CMake source tree and the above-built package::
+
+    FROM cmake:test-base
+    COPY cmake-src /opt/cmake/src/cmake
+    ADD out/cmake-<ver>-Linux-<arch>.tar.gz /opt/
+    ENV PATH=/opt/cmake-<ver>-Linux-<arch>/bin:$PATH
+
+  Build the test image and run it to drive testing:
+
+  .. code-block:: console
+
+    $ docker build --tag cmake:test --network none -f test/Dockerfile .
+    $ docker run --network none cmake:test bash test-make.bash
+    $ docker run --network none cmake:test bash test-ninja.bash
+
+.. _`kitware/cmake Docker Hub Repository`: https://hub.docker.com/r/kitware/cmake
+
+Scripts for Kitware
+-------------------
+
+Kitware uses the following scripts to produce binaries for ``cmake.org``.
+They work only on specific machines Kitware uses for such builds.
+
+* ``create-cmake-release.cmake``:
+  Run ``cmake -DCMAKE_CREATE_VERSION=$ver -P ../create-cmake-release.cmake``
+  to generate ``create-$ver-*.sh`` release scripts.  It also displays
+  instructions to run them.
+
+* ``*_release.cmake``:
+  Platform-specific settings used in corresponding scripts generated above.
+
+* ``release_cmake.cmake``:
+  Code shared by all ``*_release.cmake`` scripts.
+
+* ``release_cmake.sh.in``:
+  Template for script that runs on the actual build machines.
diff --git a/Utilities/Release/WiX/CustomAction/CMakeLists.txt b/Utilities/Release/WiX/CustomAction/CMakeLists.txt
index 7efd01e..9d89dd8 100644
--- a/Utilities/Release/WiX/CustomAction/CMakeLists.txt
+++ b/Utilities/Release/WiX/CustomAction/CMakeLists.txt
@@ -1,9 +1,15 @@
-foreach(CONFIG DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
-  string(REPLACE "/MD" "/MT"
-    "CMAKE_CXX_FLAGS_${CONFIG}"
-    "${CMAKE_CXX_FLAGS_${CONFIG}}"
-  )
-endforeach()
+if(MSVC)
+  if(NOT CMAKE_VERSION VERSION_LESS 3.15)
+    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+  else()
+    foreach(CONFIG DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
+      string(REPLACE "/MD" "/MT"
+        "CMAKE_CXX_FLAGS_${CONFIG}"
+        "${CMAKE_CXX_FLAGS_${CONFIG}}"
+        )
+    endforeach()
+  endif()
+endif()
 
 add_library(CMakeWiXCustomActions MODULE
   detect_nsis_overwrite.cpp
diff --git a/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp b/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp
index 4b17875..4ced987 100644
--- a/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp
+++ b/Utilities/Release/WiX/CustomAction/detect_nsis_overwrite.cpp
@@ -1,16 +1,17 @@
+#include <string>
+#include <vector>
+
 #include <windows.h>
 
 #include <msi.h>
 #include <msiquery.h>
 
-#include <string>
-#include <vector>
-
 std::wstring get_property(MSIHANDLE msi_handle, std::wstring const& name)
 {
   DWORD size = 0;
 
-  UINT status = MsiGetPropertyW(msi_handle, name.c_str(), L"", &size);
+  WCHAR value_buffer[] = L"";
+  UINT status = MsiGetPropertyW(msi_handle, name.c_str(), value_buffer, &size);
 
   if (status == ERROR_MORE_DATA) {
     std::vector<wchar_t> buffer(size + 1);
diff --git a/Utilities/Release/create-cmake-release.cmake b/Utilities/Release/create-cmake-release.cmake
index b3cc352..17a2151 100644
--- a/Utilities/Release/create-cmake-release.cmake
+++ b/Utilities/Release/create-cmake-release.cmake
@@ -5,30 +5,10 @@
 
 file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/logs)
 
-set(RELEASE_SCRIPTS_BATCH_1
-  win32_release.cmake         # Windows x86
-  osx_release.cmake           # OS X x86_64
-  linux64_release.cmake       # Linux x86_64
-)
-
-set(RELEASE_SCRIPTS_BATCH_2
-  win64_release.cmake         # Windows x64
-)
-
-function(write_batch_shell_script filename)
-  set(scripts ${ARGN})
-  set(i 0)
-  file(WRITE ${filename} "#!/bin/bash")
-  foreach(f ${scripts})
-    math(EXPR x "420*(${i}/4)")
-    math(EXPR y "160*(${i}%4)")
-    file(APPEND ${filename}
-    "
-\"${CMAKE_COMMAND}\" -DCMAKE_CREATE_VERSION=${CMAKE_CREATE_VERSION} -DCMAKE_DOC_TARBALL=\"${CMAKE_DOC_TARBALL}\" -P \"${CMAKE_ROOT}/Utilities/Release/${f}\" < /dev/null >& \"${CMAKE_CURRENT_SOURCE_DIR}/logs/${f}-${CMAKE_CREATE_VERSION}.log\" &
-xterm -geometry 64x6+${x}+${y} -sb -sl 2000 -T ${f}-${CMAKE_CREATE_VERSION}.log -e tail -f \"${CMAKE_CURRENT_SOURCE_DIR}/logs/${f}-${CMAKE_CREATE_VERSION}.log\" &
+function(write_rel_shell_script filename script)
+  file(WRITE ${filename} "#!/usr/bin/env bash
+\"${CMAKE_COMMAND}\" -DCMAKE_CREATE_VERSION=${CMAKE_CREATE_VERSION} -DCMAKE_DOC_TARBALL=\"${CMAKE_DOC_TARBALL}\" -P \"${CMAKE_CURRENT_LIST_DIR}/${script}.cmake\" < /dev/null 2>&1 | tee \"${CMAKE_CURRENT_SOURCE_DIR}/logs/${script}-${CMAKE_CREATE_VERSION}.log\"
 ")
-    math(EXPR i "${i}+1")
-  endforeach()
   execute_process(COMMAND chmod a+x ${filename})
 endfunction()
 
@@ -65,12 +45,14 @@
 endfunction()
 
 write_docs_shell_script("create-${CMAKE_CREATE_VERSION}-docs.sh")
-write_batch_shell_script("create-${CMAKE_CREATE_VERSION}-batch1.sh" ${RELEASE_SCRIPTS_BATCH_1})
-write_batch_shell_script("create-${CMAKE_CREATE_VERSION}-batch2.sh" ${RELEASE_SCRIPTS_BATCH_2})
+write_rel_shell_script("create-${CMAKE_CREATE_VERSION}-macos.sh"   osx_release    ) # macOS x86_64
+write_rel_shell_script("create-${CMAKE_CREATE_VERSION}-win64.sh"   win64_release  ) # Windows x64
+write_rel_shell_script("create-${CMAKE_CREATE_VERSION}-win32.sh"   win32_release  ) # Windows x86
 
-message("Run one at a time:
- ./create-${CMAKE_CREATE_VERSION}-docs.sh   &&
- ./create-${CMAKE_CREATE_VERSION}-batch1.sh &&
- ./create-${CMAKE_CREATE_VERSION}-batch2.sh &&
+message("Build docs first and then build for each platform:
+ ./create-${CMAKE_CREATE_VERSION}-docs.sh    &&
+ ./create-${CMAKE_CREATE_VERSION}-macos.sh   &&
+ ./create-${CMAKE_CREATE_VERSION}-win64.sh   &&
+ ./create-${CMAKE_CREATE_VERSION}-win32.sh   &&
  echo done
 ")
diff --git a/Utilities/Release/linux/x86_64/Dockerfile b/Utilities/Release/linux/x86_64/Dockerfile
new file mode 100644
index 0000000..1ba753c
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/Dockerfile
@@ -0,0 +1,36 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Produce an image containing a portable CMake binary package for Linux/x86_64.
+# Build using the CMake source directory as the build context.
+# The resulting image will have an '/out' directory containing the package.
+
+ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-deps-2019-08-09
+ARG FROM_IMAGE_DIGEST=@sha256:630c320b26a67fc584e0bc98314f1fb0cb0abc764348bb2613ef07437f7101f9
+ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
+FROM $FROM_IMAGE
+
+COPY . /opt/cmake/src/cmake
+
+ARG TEST=true
+
+RUN : \
+ && mkdir -p /opt/cmake/src/cmake-build \
+ && cd /opt/cmake/src/cmake-build \
+ && cp ../cmake/Utilities/Release/linux/x86_64/cache.txt CMakeCache.txt \
+ && source /opt/rh/devtoolset-6/enable \
+ && source /opt/rh/rh-python36/enable \
+ && export LANG=en_US.UTF-8 \
+ && set -x \
+ && ../cmake/bootstrap --parallel=$(nproc) --docdir=doc/cmake \
+ && nice make -j $(nproc) \
+ && if $TEST; then \
+        # Run tests that require the full build tree.
+        bin/ctest --output-on-failure -j 8 -R '^(CMake\.|CMakeLib\.|CMakeServerLib\.|RunCMake\.ctest_memcheck)'; \
+    fi \
+ && bin/cpack -G TGZ \
+ && bin/cpack -G STGZ \
+ && set +x \
+ && mkdir /out \
+ && mv cmake-*-Linux-x86_64.* /out \
+ && :
diff --git a/Utilities/Release/linux/x86_64/base/Dockerfile b/Utilities/Release/linux/x86_64/base/Dockerfile
new file mode 100644
index 0000000..dfc7df8
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/base/Dockerfile
@@ -0,0 +1,30 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Produce a base image with a build environment for portable CMake binaries.
+# Build using the directory containing this file as its own build context.
+
+ARG FROM_IMAGE_NAME=centos:6
+ARG FROM_IMAGE_DIGEST=@sha256:dec8f471302de43f4cfcf82f56d99a5227b5ea1aa6d02fa56344986e1f4610e7
+ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
+FROM $FROM_IMAGE
+
+RUN : \
+ && yum install -y centos-release-scl \
+ && yum install -y \
+        ca-certificates \
+        curl \
+        devtoolset-6-gcc \
+        devtoolset-6-gcc-c++ \
+        fontconfig-devel \
+        freetype-devel \
+        git \
+        libX11-devel \
+        libxcb-devel \
+        make \
+        patch \
+        perl \
+        rh-python36-python-pip \
+        xz \
+ && yum clean all \
+ && :
diff --git a/Utilities/Release/linux/x86_64/cache.txt b/Utilities/Release/linux/x86_64/cache.txt
new file mode 100644
index 0000000..a2864e9
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/cache.txt
@@ -0,0 +1,44 @@
+CMAKE_BUILD_TYPE:STRING=Release
+
+CMAKE_C_STANDARD:STRING=11
+CMAKE_CXX_STANDARD:STRING=14
+
+# Require only older APIs where possible.
+CMAKE_C_FLAGS:STRING=-D_POSIX_C_SOURCE=199506L -D_POSIX_SOURCE=1 -D_SVID_SOURCE=1 -D_BSD_SOURCE=1
+
+# Link C++ library statically.
+CMAKE_EXE_LINKER_FLAGS:STRING=-static-libstdc++ -static-libgcc
+
+# Enable ssl support in curl
+CMAKE_USE_OPENSSL:BOOL=ON
+OPENSSL_CRYPTO_LIBRARY:STRING=/opt/openssl/lib/libcrypto.a;-pthread
+OPENSSL_INCLUDE_DIR:PATH=/opt/openssl/include
+OPENSSL_SSL_LIBRARY:FILEPATH=/opt/openssl/lib/libssl.a
+
+# Enable ccmake
+BUILD_CursesDialog:BOOL=ON
+CURSES_FORM_LIBRARY:FILEPATH=/opt/ncurses/lib/libform.a
+CURSES_INCLUDE_PATH:PATH=/opt/ncurses/include
+CURSES_NCURSES_LIBRARY:FILEPATH=/opt/ncurses/lib/libncurses.a
+
+# Enable cmake-gui with static qt plugins
+BUILD_QtDialog:BOOL=TRUE
+CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL:STRING=3
+CMAKE_PREFIX_PATH:STRING=/opt/qt
+CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES:STRING=/opt/qt/plugins/platforms/libqxcb.a;/opt/qt/lib/libQt5XcbQpa.a;/opt/qt/lib/libQt5ServiceSupport.a;/opt/qt/lib/libQt5EdidSupport.a;/opt/qt/lib/libQt5EventDispatcherSupport.a;/opt/qt/lib/libQt5FontDatabaseSupport.a;/opt/qt/lib/libQt5ThemeSupport.a;/opt/qt/lib/libxcb-static.a;-lxcb;-lfontconfig;-lfreetype
+
+# Build documentation.
+SPHINX_EXECUTABLE:FILEPATH=/opt/rh/rh-python36/root/usr/bin/sphinx-build
+SPHINX_HTML:BOOL=ON
+SPHINX_MAN:BOOL=ON
+SPHINX_QTHELP:BOOL=ON
+QCOLLECTIONGENERATOR_EXECUTABLE:PATH=/opt/qt/bin/qhelpgenerator
+
+# We bootstrap as part of the build so skip its test.
+CMAKE_SKIP_BOOTSTRAP_TEST:STRING=TRUE
+
+# Skip Qt5 tests because our Qt is static.
+CMake_TEST_Qt5:BOOL=FALSE
+
+# CPack package file name component for this platform.
+CPACK_SYSTEM_NAME:STRING=Linux-x86_64
diff --git a/Utilities/Release/linux/x86_64/deps/Dockerfile b/Utilities/Release/linux/x86_64/deps/Dockerfile
new file mode 100644
index 0000000..db5551c
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/deps/Dockerfile
@@ -0,0 +1,142 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Produce an image with custom-built dependencies for portable CMake binaries.
+# Build using the directory containing this file as its own build context.
+
+ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-base-2019-08-09
+ARG FROM_IMAGE_DIGEST=@sha256:d2c13617f01181a3143a069e4496d6b78eafffa19d181c42be196d5dfd588151
+ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
+FROM $FROM_IMAGE
+
+# Sphinx
+RUN : \
+ && source /opt/rh/rh-python36/enable \
+ && pip install sphinx==2.1.2 \
+ && :
+
+# Qt
+# Version 5.12.0 was the last to bundle xkbcommon.
+COPY qt-install.patch /opt/qt/src/
+RUN : \
+ && mkdir -p /opt/qt/src/qt-build \
+ && cd /opt/qt/src \
+ && curl -OL https://download.qt.io/archive/qt/5.12/5.12.0/single/qt-everywhere-src-5.12.0.tar.xz \
+ && sha512sum qt-everywhere-src-5.12.0.tar.xz | grep -q 0dd03d2645fb6dac5b58c8caf92b4a0a6900131f1ccfb02443a0df4702b5da0458f4c45e758d1b929ec709b0f4b36900df2fd60a058af9cc8c1a0748b6d57aae \
+ && tar xJf qt-everywhere-src-5.12.0.tar.xz \
+ && cd qt-build \
+ && source /opt/rh/devtoolset-6/enable \
+ && ../qt-everywhere-src-5.12.0/configure \
+      -prefix /opt/qt \
+      -static \
+      -release \
+      -c++std c++11 \
+      -opensource -confirm-license \
+      -gui \
+      -widgets \
+      -xcb \
+      -fontconfig \
+      -sql-sqlite \
+      -qt-doubleconversion \
+      -qt-libjpeg \
+      -qt-libpng \
+      -qt-pcre \
+      -qt-sqlite \
+      -qt-xcb \
+      -qt-xkbcommon \
+      -qt-zlib \
+      -system-freetype \
+      -no-accessibility \
+      -no-compile-examples \
+      -no-cups \
+      -no-dbus \
+      -no-directfb \
+      -no-egl \
+      -no-eglfs \
+      -no-evdev \
+      -no-gbm \
+      -no-gif \
+      -no-glib \
+      -no-gtk \
+      -no-harfbuzz \
+      -no-iconv \
+      -no-icu \
+      -no-journald \
+      -no-kms \
+      -no-libinput \
+      -no-libproxy \
+      -no-linuxfb \
+      -no-ltcg \
+      -no-mirclient \
+      -no-mtdev \
+      -no-opengl \
+      -no-openssl \
+      -no-pch \
+      -no-sql-mysql \
+      -no-sql-psql \
+      -no-sql-sqlite2 \
+      -no-syslog \
+      -no-system-proxies \
+      -no-tslib \
+      -no-use-gold-linker \
+      -skip declarative \
+      -skip multimedia \
+      -skip qtcanvas3d \
+      -skip qtconnectivity \
+      -skip qtdeclarative \
+      -skip qtlocation \
+      -skip qtmultimedia \
+      -skip qtsensors \
+      -skip qtserialport \
+      -skip qtsvg \
+      -skip qtwayland \
+      -skip qtwebchannel \
+      -skip qtwebengine \
+      -skip qtwebsockets \
+      -skip qtwinextras \
+      -skip qtxmlpatterns \
+      -nomake examples \
+      -nomake tests \
+ && make install -j $(nproc) \
+ && cd /opt/qt \
+ && patch -p1 -i src/qt-install.patch \
+ && cd /opt \
+ && rm -rf /opt/qt/src \
+ && :
+
+# Curses
+RUN : \
+ && mkdir -p /opt/ncurses/src/ncurses-build \
+ && cd /opt/ncurses/src \
+ && curl -O https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz \
+ && sha512sum ncurses-6.1.tar.gz | grep -q e308af43f8b7e01e98a55f4f6c4ee4d1c39ce09d95399fa555b3f0cdf5fd0db0f4c4d820b4af78a63f6cf6d8627587114a40af48cfc066134b600520808a77ee \
+ && tar xzf ncurses-6.1.tar.gz \
+ && cd ncurses-build \
+ && source /opt/rh/devtoolset-6/enable \
+ && ../ncurses-6.1/configure \
+      --prefix=/opt/ncurses \
+      --with-terminfo-dirs=/etc/terminfo:/lib/terminfo:/usr/share/terminfo \
+      --with-default-terminfo-dir=/usr/share/terminfo \
+      --without-shared \
+ && make -j $(nproc) \
+ && make install.libs install.includes \
+ && cd /opt \
+ && rm -rf /opt/ncurses/src \
+ && :
+
+# OpenSSL
+COPY openssl-source.patch /opt/openssl/src/
+RUN : \
+ && mkdir -p /opt/openssl/src \
+ && cd /opt/openssl/src \
+ && curl -O https://www.openssl.org/source/openssl-1.1.1c.tar.gz \
+ && sha512sum openssl-1.1.1c.tar.gz | grep -q 8e2c5cc11c120efbb7d7850980cb6eaa782d29b4996b3f3378d37613c1679f852d7cc08a90d62e78fcec3439f06bdbee70064579a8c2adaffd91532a97f646ff \
+ && tar xzf openssl-1.1.1c.tar.gz \
+ && cd openssl-1.1.1c \
+ && patch -p1 -i ../openssl-source.patch \
+ && source /opt/rh/devtoolset-6/enable \
+ && ./Configure --prefix=/opt/openssl linux-elf no-asm no-shared -D_POSIX_C_SOURCE=199506L -D_POSIX_SOURCE=1 -D_SVID_SOURCE=1 -D_BSD_SOURCE=1 \
+ && make install_dev -j $(nproc) \
+ && cd /opt \
+ && rm -rf /opt/openssl/src \
+ && :
diff --git a/Utilities/Release/linux/x86_64/deps/openssl-source.patch b/Utilities/Release/linux/x86_64/deps/openssl-source.patch
new file mode 100644
index 0000000..c81fe2f
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/deps/openssl-source.patch
@@ -0,0 +1,12 @@
+# enable pthread APIs disabled by our _POSIX_SOURCE definitions
+--- openssl-source/crypto/threads_pthread.c.orig
++++ openssl-source/crypto/threads_pthread.c
+@@ -6,6 +6,8 @@
+  * in the file LICENSE in the source distribution or at
+  * https://www.openssl.org/source/license.html
+  */
++#undef _POSIX_C_SOURCE
++#undef _POSIX_SOURCE
+
+ #include <openssl/crypto.h>
+ #include "internal/cryptlib.h"
diff --git a/Utilities/Release/linux/x86_64/deps/qt-install.patch b/Utilities/Release/linux/x86_64/deps/qt-install.patch
new file mode 100644
index 0000000..792aefd
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/deps/qt-install.patch
@@ -0,0 +1,24 @@
+# Add Qt Core dependencies missing from static Qt build.
+--- qt-install/lib/cmake/Qt5Core/Qt5CoreConfig.cmake.orig
++++ qt-install/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
+@@ -111,7 +111,7 @@
+     list(REMOVE_DUPLICATES Qt5Core_COMPILE_DEFINITIONS)
+     list(REMOVE_DUPLICATES Qt5Core_EXECUTABLE_COMPILE_FLAGS)
+
+-    set(_Qt5Core_LIB_DEPENDENCIES "")
++    set(_Qt5Core_LIB_DEPENDENCIES "${_qt5Core_install_prefix}/lib/libqtpcre2.a")
+
+
+     add_library(Qt5::Core STATIC IMPORTED)
+# Add Qt Gui dependencies missing from static Qt build.
+--- qt-install/lib/cmake/Qt5Gui/Qt5GuiConfig.cmake.orig
++++ qt-install/lib/cmake/Qt5Gui/Qt5GuiConfig.cmake
+@@ -111,7 +111,7 @@
+     list(REMOVE_DUPLICATES Qt5Gui_COMPILE_DEFINITIONS)
+     list(REMOVE_DUPLICATES Qt5Gui_EXECUTABLE_COMPILE_FLAGS)
+
+-    set(_Qt5Gui_LIB_DEPENDENCIES "Qt5::Core")
++    set(_Qt5Gui_LIB_DEPENDENCIES "Qt5::Core;${_qt5Gui_install_prefix}/lib/libqtlibpng.a")
+
+
+     add_library(Qt5::Gui STATIC IMPORTED)
diff --git a/Utilities/Release/linux/x86_64/test/Dockerfile b/Utilities/Release/linux/x86_64/test/Dockerfile
new file mode 100644
index 0000000..6629156
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/test/Dockerfile
@@ -0,0 +1,26 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Produce a base image with a test environment for packaged CMake binaries.
+# Build using the directory containing this file as its own build context.
+
+ARG FROM_IMAGE_NAME=debian:9
+ARG FROM_IMAGE_DIGEST=@sha256:397b2157a9ea8d7f16c613aded70284292106e8b813fb1ed5de8a8785310a26a
+ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
+FROM $FROM_IMAGE
+
+RUN : \
+ && apt-get update \
+ && apt-get install -y \
+        dpkg \
+        file \
+        gcc \
+        g++ \
+        gfortran \
+        qt5-default \
+        make \
+        ninja-build \
+ && apt-get clean \
+ && :
+
+COPY test-make.bash test-ninja.bash /
diff --git a/Utilities/Release/linux/x86_64/test/cache-ninja.txt b/Utilities/Release/linux/x86_64/test/cache-ninja.txt
new file mode 100644
index 0000000..b00370e
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/test/cache-ninja.txt
@@ -0,0 +1,4 @@
+CMAKE_Fortran_COMPILER:STRING=
+CMake_TEST_IPO_WORKS_C:BOOL=ON
+CMake_TEST_IPO_WORKS_CXX:BOOL=ON
+CMake_TEST_Qt5:BOOL=ON
diff --git a/Utilities/Release/linux/x86_64/test/test-make.bash b/Utilities/Release/linux/x86_64/test/test-make.bash
new file mode 100644
index 0000000..10d30c3
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/test/test-make.bash
@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+set -e
+set -x
+mkdir -p /opt/cmake/src/cmake-make
+cd /opt/cmake/src/cmake-make
+echo >CMakeCache.txt '
+CMake_TEST_IPO_WORKS_C:BOOL=ON
+CMake_TEST_IPO_WORKS_CXX:BOOL=ON
+CMake_TEST_IPO_WORKS_Fortran:BOOL=ON
+CMake_TEST_NO_NETWORK:BOOL=ON
+CMake_TEST_Qt5:BOOL=ON
+'
+cmake ../cmake -DCMake_TEST_HOST_CMAKE=1 -G "Unix Makefiles"
+make -j $(nproc)
+ctest --output-on-failure -j $(nproc)
diff --git a/Utilities/Release/linux/x86_64/test/test-ninja.bash b/Utilities/Release/linux/x86_64/test/test-ninja.bash
new file mode 100644
index 0000000..fe39e2e
--- /dev/null
+++ b/Utilities/Release/linux/x86_64/test/test-ninja.bash
@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+set -e
+set -x
+mkdir -p /opt/cmake/src/cmake-ninja
+cd /opt/cmake/src/cmake-ninja
+echo >CMakeCache.txt '
+CMAKE_Fortran_COMPILER:STRING=
+CMake_TEST_IPO_WORKS_C:BOOL=ON
+CMake_TEST_IPO_WORKS_CXX:BOOL=ON
+CMake_TEST_NO_NETWORK:BOOL=ON
+CMake_TEST_Qt5:BOOL=ON
+'
+cmake ../cmake -DCMake_TEST_HOST_CMAKE=1 -G "Ninja"
+ninja
+ctest --output-on-failure -j $(nproc)
diff --git a/Utilities/Release/linux64_release.cmake b/Utilities/Release/linux64_release.cmake
deleted file mode 100644
index 958ed25..0000000
--- a/Utilities/Release/linux64_release.cmake
+++ /dev/null
@@ -1,53 +0,0 @@
-set(PROCESSORS 4)
-set(BOOTSTRAP_ARGS "--docdir=doc/cmake")
-set(HOST linux64)
-set(MAKE_PROGRAM "make")
-set(CPACK_BINARY_GENERATORS "STGZ TGZ")
-set(CC /opt/gcc-8.2.0/bin/gcc)
-set(CXX /opt/gcc-8.2.0/bin/g++)
-set(CFLAGS   "")
-set(CXXFLAGS "")
-set(qt_prefix "/home/kitware/qt-5.7.0")
-set(qt_xcb_libs
-  ${qt_prefix}/plugins/platforms/libqxcb.a
-  ${qt_prefix}/lib/libQt5XcbQpa.a
-  ${qt_prefix}/lib/libQt5PlatformSupport.a
-  ${qt_prefix}/lib/libxcb-static.a
-  -lX11-xcb
-  -lX11
-  -lxcb
-  -lfontconfig
-  -lfreetype
-  )
-set(INITIAL_CACHE "
-CMAKE_BUILD_TYPE:STRING=Release
-CMAKE_C_STANDARD:STRING=11
-CMAKE_CXX_STANDARD:STRING=14
-CMAKE_C_FLAGS:STRING=-D_POSIX_C_SOURCE=199506L -D_POSIX_SOURCE=1 -D_SVID_SOURCE=1 -D_BSD_SOURCE=1
-CMAKE_EXE_LINKER_FLAGS:STRING=-static-libstdc++ -static-libgcc
-CURSES_LIBRARY:FILEPATH=/home/kitware/ncurses-5.9/lib/libncurses.a
-CURSES_INCLUDE_PATH:PATH=/home/kitware/ncurses-5.9/include
-FORM_LIBRARY:FILEPATH=/home/kitware/ncurses-5.9/lib/libform.a
-CMAKE_USE_OPENSSL:BOOL=ON
-OPENSSL_CRYPTO_LIBRARY:STRING=/home/kitware/openssl-1.1.1/lib/libcrypto.a;-pthread
-OPENSSL_INCLUDE_DIR:PATH=/home/kitware/openssl-1.1.1/include
-OPENSSL_SSL_LIBRARY:FILEPATH=/home/kitware/openssl-1.1.1/lib/libssl.a
-PYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3
-CPACK_SYSTEM_NAME:STRING=Linux-x86_64
-BUILD_CursesDialog:BOOL=ON
-BUILD_QtDialog:BOOL=TRUE
-CMAKE_SKIP_BOOTSTRAP_TEST:STRING=TRUE
-CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL:STRING=3
-CMAKE_PREFIX_PATH:STRING=${qt_prefix}
-CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES:STRING=${qt_xcb_libs}
-")
-set(ENV [[
-export CMAKE_PREFIX_PATH=/opt/binutils-2.31
-]])
-set(SIGN "")
-
-# Exclude Qt5 tests because our Qt5 is static.
-set(EXTRA_CTEST_ARGS "-E Qt5")
-
-get_filename_component(path "${CMAKE_CURRENT_LIST_FILE}" PATH)
-include(${path}/release_cmake.cmake)
diff --git a/Utilities/Release/osx_release.cmake b/Utilities/Release/osx_release.cmake
index ac35872..5ef3003 100644
--- a/Utilities/Release/osx_release.cmake
+++ b/Utilities/Release/osx_release.cmake
@@ -5,7 +5,7 @@
 set(MAKE_PROGRAM "make")
 set(MAKE "${MAKE_PROGRAM} -j5")
 set(CPACK_BINARY_GENERATORS "DragNDrop TGZ")
-set(CPACK_SOURCE_GENERATORS "TGZ TZ")
+set(CPACK_SOURCE_GENERATORS "")
 set(CPACK_DMG_FORMAT "UDBZ") #build using bzip2 for smaller package size
 set(CC clang)
 set(CXX clang++)
diff --git a/Utilities/Release/push.bash b/Utilities/Release/push.bash
index 1c8efe9..a1c6651 100755
--- a/Utilities/Release/push.bash
+++ b/Utilities/Release/push.bash
@@ -50,6 +50,9 @@
     dir="v${version}"
 fi
 readonly dir
+if ! test -d "${dest}/${dir}"; then
+    mkdir "${dest}/${dir}"
+fi
 
 for f in cmake-${version}*; do
     if ! test -f "${f}"; then
diff --git a/Utilities/Release/win32_release.cmake b/Utilities/Release/win32_release.cmake
index 468e5f4..14e5cba 100644
--- a/Utilities/Release/win32_release.cmake
+++ b/Utilities/Release/win32_release.cmake
@@ -5,7 +5,7 @@
 set(HOST win32)
 set(RUN_LAUNCHER ~/rel/run)
 set(CPACK_BINARY_GENERATORS "WIX ZIP")
-set(CPACK_SOURCE_GENERATORS "ZIP")
+set(CPACK_SOURCE_GENERATORS "")
 set(MAKE_PROGRAM "ninja")
 set(MAKE "${MAKE_PROGRAM} -j16")
 set(qt_prefix "c:/Qt/5.12.1/msvc2017-32-w7-mt")
@@ -28,8 +28,7 @@
 CMAKE_GENERATOR:INTERNAL=Ninja
 BUILD_QtDialog:BOOL=TRUE
 CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL:STRING=3
-CMAKE_C_FLAGS_RELEASE:STRING=-MT -O2 -Ob2 -DNDEBUG
-CMAKE_CXX_FLAGS_RELEASE:STRING=-MT -O2 -Ob2 -DNDEBUG
+CMAKE_MSVC_RUNTIME_LIBRARY:STRING=MultiThreaded$<$<CONFIG:Debug>:Debug>
 CMAKE_EXE_LINKER_FLAGS:STRING=-machine:x86 -subsystem:console,6.01
 CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES:STRING=${qt_win_libs}
 CMAKE_PREFIX_PATH:STRING=${qt_prefix}
diff --git a/Utilities/Release/win64_release.cmake b/Utilities/Release/win64_release.cmake
index 5a93ce6..149d378 100644
--- a/Utilities/Release/win64_release.cmake
+++ b/Utilities/Release/win64_release.cmake
@@ -28,8 +28,7 @@
 CMAKE_GENERATOR:INTERNAL=Ninja
 BUILD_QtDialog:BOOL=TRUE
 CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL:STRING=3
-CMAKE_C_FLAGS_RELEASE:STRING=-MT -O2 -Ob2 -DNDEBUG
-CMAKE_CXX_FLAGS_RELEASE:STRING=-MT -O2 -Ob2 -DNDEBUG
+CMAKE_MSVC_RUNTIME_LIBRARY:STRING=MultiThreaded$<$<CONFIG:Debug>:Debug>
 CMAKE_EXE_LINKER_FLAGS:STRING=-machine:x64 -subsystem:console,6.01
 CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES:STRING=${qt_win_libs}
 CMAKE_PREFIX_PATH:STRING=${qt_prefix}
diff --git a/Utilities/Scripts/regenerate-lexers.bash b/Utilities/Scripts/regenerate-lexers.bash
index b09f25b..2bd58fe 100755
--- a/Utilities/Scripts/regenerate-lexers.bash
+++ b/Utilities/Scripts/regenerate-lexers.bash
@@ -11,6 +11,7 @@
 
 for lexer in            \
     CommandArgument     \
+    CTestProcesses      \
     DependsJava         \
     Expr                \
     Fortran
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index c5b2bfe..17c5018 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,11 +3,11 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.1...3.15 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
-  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)
@@ -105,7 +105,6 @@
     # Workaround sphinx configurability:
     # https://bitbucket.org/birkenfeld/sphinx/issue/1448/make-qthelp-more-configurable
     COMMAND ${CMAKE_COMMAND} "-DQTHELP_DIR=${CMAKE_CURRENT_BINARY_DIR}/qthelp/"
-      "-DCMake_VERSION=${CMake_VERSION_MAJOR}${CMake_VERSION_MINOR}${CMake_VERSION_PATCH}"
       -P "${CMAKE_CURRENT_SOURCE_DIR}/fixup_qthelp_names.cmake"
 
     # Create proper identifiers. Workaround for
@@ -216,7 +215,7 @@
 
 if(SPHINX_QTHELP)
   CMake_OPTIONAL_COMPONENT(sphinx-qthelp)
-  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qthelp/CMake-${CMake_VERSION_MAJOR}${CMake_VERSION_MINOR}${CMake_VERSION_PATCH}.qch
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qthelp/CMake.qch
           DESTINATION ${CMAKE_DOC_DIR} ${COMPONENT}
           )
 endif()
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in
index 70ba080..e50c4f9 100644
--- a/Utilities/Sphinx/conf.py.in
+++ b/Utilities/Sphinx/conf.py.in
@@ -82,4 +82,4 @@
 # Not supported yet by sphinx:
 # https://bitbucket.org/birkenfeld/sphinx/issue/1448/make-qthelp-more-configurable
 # qthelp_namespace = "org.cmake"
-# qthelp_qch_name = "CMake-300.qch"
+# qthelp_qch_name = "CMake.qch"
diff --git a/Utilities/Sphinx/fixup_qthelp_names.cmake b/Utilities/Sphinx/fixup_qthelp_names.cmake
index e35ef25..179e846 100644
--- a/Utilities/Sphinx/fixup_qthelp_names.cmake
+++ b/Utilities/Sphinx/fixup_qthelp_names.cmake
@@ -10,15 +10,6 @@
   QHCP_CONTENT "${QHCP_CONTENT}"
 )
 
-string(REPLACE
-  "<output>CMake.qch" "<output>CMake-${CMake_VERSION}.qch"
-  QHCP_CONTENT "${QHCP_CONTENT}"
-)
-string(REPLACE
-  "<file>CMake.qch" "<file>CMake-${CMake_VERSION}.qch"
-  QHCP_CONTENT "${QHCP_CONTENT}"
-)
-
 file(WRITE "${QTHELP_DIR}/CMake.qhcp" "${QHCP_CONTENT}")
 
 
diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
index 15ba46e..60ee8e6 100644
--- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
+++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
@@ -53,7 +53,7 @@
     message(WARNING "Hiding private symbols regardless CURL_HIDDEN_SYMBOLS being disabled.")
     set(HIDES_CURL_PRIVATE_SYMBOLS TRUE)
   endif()
-elseif()
+else()
   set(HIDES_CURL_PRIVATE_SYMBOLS FALSE)
 endif()
 
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 37522fc..bc8a7dc 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -449,7 +449,12 @@
 endif()
 
 if(CMAKE_USE_OPENSSL)
-  find_package(OpenSSL REQUIRED)
+  find_package(OpenSSL)
+  if(NOT OpenSSL_FOUND)
+    message(FATAL_ERROR
+      "Could not find OpenSSL. Install an OpenSSL development package or "
+      "configure CMake with -DCMAKE_USE_OPENSSL=OFF to build without OpenSSL.")
+  endif()
   set(SSL_ENABLED ON)
   set(USE_OPENSSL ON)
   set(HAVE_LIBCRYPTO ON)
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
index 6e6e57e..fc86505 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
+++ b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
@@ -30,7 +30,7 @@
 #define isfinite finite
 #endif
 #elif defined(__hpux)
-#if !defined(isfinite)
+#if !defined(isfinite) && !defined(__GNUC__)
 #if defined(__ia64) && !defined(finite)
 #define isfinite(x) ((sizeof(x) == sizeof(float) ? \
                      _Isfinitef(x) : _IsFinite(x)))
@@ -86,10 +86,11 @@
 // HP-UX
 #if defined(__hpux)
 # if !defined(isfinite)
-#  if defined(__ia64) && !defined(finite)
+#  if defined(__ia64) && !defined(finite) && !defined(__GNUC__)
 #   define isfinite(x) ((sizeof(x) == sizeof(float) ? \
                         _Isfinitef(x) : _Isfinite(x)))
 #  else
+#   include <math.h>
 #   define isfinite finite
 #  endif
 # endif
diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt
index 2e781f1..fe2ef75 100644
--- a/Utilities/cmlibuv/CMakeLists.txt
+++ b/Utilities/cmlibuv/CMakeLists.txt
@@ -300,6 +300,23 @@
     )
 endif()
 
+if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX")
+  list(APPEND uv_libraries
+    rt
+    )
+  list(APPEND uv_headers
+    include/uv/posix.h
+    )
+  list(APPEND uv_defines
+    _XOPEN_SOURCE_EXTENDED
+    )
+  list(APPEND uv_sources
+    src/unix/hpux.c
+    src/unix/no-fsevents.c
+    src/unix/posix-poll.c
+    )
+endif()
+
 include_directories(
   ${uv_includes}
   ${KWSYS_HEADER_ROOT}
diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h
index e6dc736..eb80bfb 100644
--- a/Utilities/cmlibuv/include/uv.h
+++ b/Utilities/cmlibuv/include/uv.h
@@ -206,6 +206,7 @@
 /* Handle types. */
 typedef struct uv_loop_s uv_loop_t;
 typedef struct uv_handle_s uv_handle_t;
+typedef struct uv_dir_s uv_dir_t;
 typedef struct uv_stream_s uv_stream_t;
 typedef struct uv_tcp_s uv_tcp_t;
 typedef struct uv_udp_s uv_udp_t;
@@ -634,7 +635,11 @@
 UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
                           const struct sockaddr* addr,
                           unsigned int flags);
+UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
 
+UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
 UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
                                  struct sockaddr* name,
                                  int* namelen);
@@ -1112,6 +1117,11 @@
 } uv_timeval_t;
 
 typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
+
+typedef struct {
    uv_timeval_t ru_utime; /* user CPU time used */
    uv_timeval_t ru_stime; /* system CPU time used */
    uint64_t ru_maxrss;    /* maximum resident set size */
@@ -1162,6 +1172,17 @@
 UV_EXTERN int uv_os_setenv(const char* name, const char* value);
 UV_EXTERN int uv_os_unsetenv(const char* name);
 
+#ifdef MAXHOSTNAMELEN
+# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1)
+#else
+  /*
+    Fallback for the maximum hostname size, including the null terminator. The
+    Windows gethostname() documentation states that 256 bytes will always be
+    large enough to hold the null-terminated hostname.
+  */
+# define UV_MAXHOSTNAMESIZE 256
+#endif
+
 UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size);
 
 UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
@@ -1199,9 +1220,19 @@
   UV_FS_FCHOWN,
   UV_FS_REALPATH,
   UV_FS_COPYFILE,
-  UV_FS_LCHOWN
+  UV_FS_LCHOWN,
+  UV_FS_OPENDIR,
+  UV_FS_READDIR,
+  UV_FS_CLOSEDIR
 } uv_fs_type;
 
+struct uv_dir_s {
+  uv_dirent_t* dirents;
+  size_t nentries;
+  void* reserved[4];
+  UV_DIR_PRIVATE_FIELDS
+};
+
 /* uv_fs_t is a subclass of uv_req_t. */
 struct uv_fs_s {
   UV_REQ_FIELDS
@@ -1294,6 +1325,18 @@
                             uv_fs_cb cb);
 UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req,
                                  uv_dirent_t* ent);
+UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            uv_dir_t* dir,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_closedir(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_dir_t* dir,
+                             uv_fs_cb cb);
 UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
                          uv_fs_t* req,
                          const char* path,
@@ -1536,6 +1579,7 @@
 
 UV_EXTERN uint64_t uv_get_free_memory(void);
 UV_EXTERN uint64_t uv_get_total_memory(void);
+UV_EXTERN uint64_t uv_get_constrained_memory(void);
 
 UV_EXTERN uint64_t uv_hrtime(void);
 
@@ -1589,9 +1633,29 @@
 UV_EXTERN void* uv_key_get(uv_key_t* key);
 UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
 
+UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
+
 typedef void (*uv_thread_cb)(void* arg);
 
 UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
+
+typedef enum {
+  UV_THREAD_NO_FLAGS = 0x00,
+  UV_THREAD_HAS_STACK_SIZE = 0x01
+} uv_thread_create_flags;
+
+struct uv_thread_options_s {
+  unsigned int flags;
+  size_t stack_size;
+  /* More fields may be added at any time. */
+};
+
+typedef struct uv_thread_options_s uv_thread_options_t;
+
+UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
+                                  const uv_thread_options_t* params,
+                                  uv_thread_cb entry,
+                                  void* arg);
 UV_EXTERN uv_thread_t uv_thread_self(void);
 UV_EXTERN int uv_thread_join(uv_thread_t *tid);
 UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
diff --git a/Utilities/cmlibuv/include/uv/unix.h b/Utilities/cmlibuv/include/uv/unix.h
index 3c1b363..4e26108 100644
--- a/Utilities/cmlibuv/include/uv/unix.h
+++ b/Utilities/cmlibuv/include/uv/unix.h
@@ -31,13 +31,14 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
-#include <netdb.h>
+#include <netdb.h>  /* MAXHOSTNAMELEN on Solaris */
 
 #include <termios.h>
 #include <pwd.h>
 
 #if !defined(__MVS__)
 #include <semaphore.h>
+#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */
 #endif
 #include <pthread.h>
 #include <signal.h>
@@ -50,12 +51,12 @@
 # include "linux.h"
 #elif defined (__MVS__)
 # include "os390.h"
-#elif defined(__PASE__)
-# include "posix.h"
 #elif defined(_AIX)
 # include "aix.h"
 #elif defined(__sun)
 # include "sunos.h"
+#elif defined(__hpux)
+# include "posix.h"
 #elif defined(__APPLE__)
 # include "darwin.h"
 #elif defined(__DragonFly__)       || \
@@ -64,9 +65,12 @@
       defined(__OpenBSD__)         || \
       defined(__NetBSD__)
 # include "bsd.h"
-#elif defined(__CYGWIN__) || defined(__MSYS__)
+#elif defined(__PASE__)   || \
+      defined(__CYGWIN__) || \
+      defined(__MSYS__)   || \
+      defined(__GNU__)
 # include "posix.h"
-#elif defined(__GNU__)
+#elif defined(__HAIKU__)
 # include "posix.h"
 #endif
 
@@ -149,7 +153,9 @@
 typedef pthread_key_t uv_key_t;
 
 /* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */
-#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
 /* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */
 struct _uv_barrier {
   uv_mutex_t mutex;
@@ -178,6 +184,9 @@
 
 typedef struct dirent uv__dirent_t;
 
+#define UV_DIR_PRIVATE_FIELDS \
+  DIR* dir;
+
 #if defined(DT_UNKNOWN)
 # define HAVE_DIRENT_TYPES
 # if defined(DT_REG)
diff --git a/Utilities/cmlibuv/include/uv/version.h b/Utilities/cmlibuv/include/uv/version.h
index abc140a..97f0bc2 100644
--- a/Utilities/cmlibuv/include/uv/version.h
+++ b/Utilities/cmlibuv/include/uv/version.h
@@ -31,7 +31,7 @@
  */
 
 #define UV_VERSION_MAJOR 1
-#define UV_VERSION_MINOR 24
+#define UV_VERSION_MINOR 29
 #define UV_VERSION_PATCH 2
 #define UV_VERSION_IS_RELEASE 0
 #define UV_VERSION_SUFFIX "dev"
diff --git a/Utilities/cmlibuv/include/uv/win.h b/Utilities/cmlibuv/include/uv/win.h
index f3d3809..7f77cc2 100644
--- a/Utilities/cmlibuv/include/uv/win.h
+++ b/Utilities/cmlibuv/include/uv/win.h
@@ -312,6 +312,11 @@
   char d_name[1];
 } uv__dirent_t;
 
+#define UV_DIR_PRIVATE_FIELDS \
+  HANDLE dir_handle;          \
+  WIN32_FIND_DATAW find_data; \
+  BOOL need_find_call;
+
 #define HAVE_DIRENT_TYPES
 #define UV__DT_DIR     UV_DIRENT_DIR
 #define UV__DT_FILE    UV_DIRENT_FILE
diff --git a/Utilities/cmlibuv/src/fs-poll.c b/Utilities/cmlibuv/src/fs-poll.c
index 6c82dfc..89864e2 100644
--- a/Utilities/cmlibuv/src/fs-poll.c
+++ b/Utilities/cmlibuv/src/fs-poll.c
@@ -22,12 +22,20 @@
 #include "uv.h"
 #include "uv-common.h"
 
+#ifdef _WIN32
+#include "win/internal.h"
+#include "win/handle-inl.h"
+#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h))
+#else
+#include "unix/internal.h"
+#endif
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
 struct poll_ctx {
-  uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
+  uv_fs_poll_t* parent_handle;
   int busy_polling;
   unsigned int interval;
   uint64_t start_time;
@@ -36,6 +44,7 @@
   uv_timer_t timer_handle;
   uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
   uv_stat_t statbuf;
+  struct poll_ctx* previous; /* context from previous start()..stop() period */
   char path[1]; /* variable length */
 };
 
@@ -49,6 +58,7 @@
 
 int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
   uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
+  handle->poll_ctx = NULL;
   return 0;
 }
 
@@ -62,7 +72,7 @@
   size_t len;
   int err;
 
-  if (uv__is_active(handle))
+  if (uv_is_active((uv_handle_t*)handle))
     return 0;
 
   loop = handle->loop;
@@ -90,6 +100,8 @@
   if (err < 0)
     goto error;
 
+  if (handle->poll_ctx != NULL)
+    ctx->previous = handle->poll_ctx;
   handle->poll_ctx = ctx;
   uv__handle_start(handle);
 
@@ -104,19 +116,17 @@
 int uv_fs_poll_stop(uv_fs_poll_t* handle) {
   struct poll_ctx* ctx;
 
-  if (!uv__is_active(handle))
+  if (!uv_is_active((uv_handle_t*)handle))
     return 0;
 
   ctx = handle->poll_ctx;
   assert(ctx != NULL);
-  assert(ctx->parent_handle != NULL);
-  ctx->parent_handle = NULL;
-  handle->poll_ctx = NULL;
+  assert(ctx->parent_handle == handle);
 
   /* Close the timer if it's active. If it's inactive, there's a stat request
    * in progress and poll_cb will take care of the cleanup.
    */
-  if (uv__is_active(&ctx->timer_handle))
+  if (uv_is_active((uv_handle_t*)&ctx->timer_handle))
     uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
 
   uv__handle_stop(handle);
@@ -129,7 +139,7 @@
   struct poll_ctx* ctx;
   size_t required_len;
 
-  if (!uv__is_active(handle)) {
+  if (!uv_is_active((uv_handle_t*)handle)) {
     *size = 0;
     return UV_EINVAL;
   }
@@ -153,6 +163,9 @@
 
 void uv__fs_poll_close(uv_fs_poll_t* handle) {
   uv_fs_poll_stop(handle);
+
+  if (handle->poll_ctx == NULL)
+    uv__make_close_pending((uv_handle_t*)handle);
 }
 
 
@@ -173,14 +186,13 @@
   uv_stat_t* statbuf;
   struct poll_ctx* ctx;
   uint64_t interval;
+  uv_fs_poll_t* handle;
 
   ctx = container_of(req, struct poll_ctx, fs_req);
+  handle = ctx->parent_handle;
 
-  if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
-    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
-    uv_fs_req_cleanup(req);
-    return;
-  }
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle))
+    goto out;
 
   if (req->result != 0) {
     if (ctx->busy_polling != req->result) {
@@ -205,7 +217,7 @@
 out:
   uv_fs_req_cleanup(req);
 
-  if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) {
     uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
     return;
   }
@@ -219,8 +231,27 @@
 }
 
 
-static void timer_close_cb(uv_handle_t* handle) {
-  uv__free(container_of(handle, struct poll_ctx, timer_handle));
+static void timer_close_cb(uv_handle_t* timer) {
+  struct poll_ctx* ctx;
+  struct poll_ctx* it;
+  struct poll_ctx* last;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  handle = ctx->parent_handle;
+  if (ctx == handle->poll_ctx) {
+    handle->poll_ctx = ctx->previous;
+    if (handle->poll_ctx == NULL && uv__is_closing(handle))
+      uv__make_close_pending((uv_handle_t*)handle);
+  } else {
+    for (last = handle->poll_ctx, it = last->previous;
+         it != ctx;
+         last = it, it = it->previous) {
+      assert(last->previous != NULL);
+    }
+    last->previous = ctx->previous;
+  }
+  uv__free(ctx);
 }
 
 
diff --git a/Utilities/cmlibuv/src/threadpool.c b/Utilities/cmlibuv/src/threadpool.c
index 4258933..7aa5755 100644
--- a/Utilities/cmlibuv/src/threadpool.c
+++ b/Utilities/cmlibuv/src/threadpool.c
@@ -27,7 +27,7 @@
 
 #include <stdlib.h>
 
-#define MAX_THREADPOOL_SIZE 128
+#define MAX_THREADPOOL_SIZE 1024
 
 static uv_once_t once = UV_ONCE_INIT;
 static uv_cond_t cond;
diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c
index 337e58e..1f36926 100644
--- a/Utilities/cmlibuv/src/unix/aix.c
+++ b/Utilities/cmlibuv/src/unix/aix.c
@@ -344,6 +344,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   perfstat_cpu_total_t ps_total;
   int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
@@ -1041,6 +1046,7 @@
   struct poll_ctl pc;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct pollfd*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c
index 0b450ae..a5c47bc 100644
--- a/Utilities/cmlibuv/src/unix/async.c
+++ b/Utilities/cmlibuv/src/unix/async.c
@@ -61,14 +61,43 @@
   if (ACCESS_ONCE(int, handle->pending) != 0)
     return 0;
 
-  if (cmpxchgi(&handle->pending, 0, 1) == 0)
-    uv__async_send(handle->loop);
+  /* Tell the other thread we're busy with the handle. */
+  if (cmpxchgi(&handle->pending, 0, 1) != 0)
+    return 0;
+
+  /* Wake up the other thread's event loop. */
+  uv__async_send(handle->loop);
+
+  /* Tell the other thread we're done. */
+  if (cmpxchgi(&handle->pending, 1, 2) != 1)
+    abort();
 
   return 0;
 }
 
 
+/* Only call this from the event loop thread. */
+static int uv__async_spin(uv_async_t* handle) {
+  int rc;
+
+  for (;;) {
+    /* rc=0 -- handle is not pending.
+     * rc=1 -- handle is pending, other thread is still working with it.
+     * rc=2 -- handle is pending, other thread is done.
+     */
+    rc = cmpxchgi(&handle->pending, 2, 0);
+
+    if (rc != 1)
+      return rc;
+
+    /* Other thread is busy with this handle, spin until it's done. */
+    cpu_relax();
+  }
+}
+
+
 void uv__async_close(uv_async_t* handle) {
+  uv__async_spin(handle);
   QUEUE_REMOVE(&handle->queue);
   uv__handle_stop(handle);
 }
@@ -109,8 +138,8 @@
     QUEUE_REMOVE(q);
     QUEUE_INSERT_TAIL(&loop->async_handles, q);
 
-    if (cmpxchgi(&h->pending, 1, 0) == 0)
-      continue;
+    if (0 == uv__async_spin(h))
+      continue;  /* Not pending. */
 
     if (h->async_cb == NULL)
       continue;
diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h
index be741dc..995aca6 100644
--- a/Utilities/cmlibuv/src/unix/atomic-ops.h
+++ b/Utilities/cmlibuv/src/unix/atomic-ops.h
@@ -23,7 +23,6 @@
 #endif
 
 UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
-UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
 UV_UNUSED(static void cpu_relax(void));
 
 /* Prefer hand-rolled assembly over the gcc builtins because the latter also
@@ -49,43 +48,7 @@
   else
     return op4;
 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-  return atomic_cas_uint(ptr, oldval, newval);
-#else
-  return __sync_val_compare_and_swap(ptr, oldval, newval);
-#endif
-}
-
-UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
-#if defined(__i386__) || defined(__x86_64__)
-  long out;
-  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
-                        : "=a" (out), "+m" (*(volatile long*) ptr)
-                        : "r" (newval), "0" (oldval)
-                        : "memory");
-  return out;
-#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__))
-  const long out = (*(volatile int*) ptr);
-# if defined(__64BIT__)
-  __compare_and_swaplp(ptr, &oldval, newval);
-# else
-  __compare_and_swap(ptr, &oldval, newval);
-# endif /* if defined(__64BIT__) */
-  return out;
-#elif defined (__MVS__)
-#ifdef _LP64
-  unsigned long long op4;
-  if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval,
-                  (unsigned long long*) ptr, *ptr, &op4))
-#else
-  unsigned long op4;
-  if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
-                (unsigned int*) ptr, *ptr, &op4))
-#endif
-    return oldval;
-  else
-    return op4;
-#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-  return atomic_cas_ulong(ptr, oldval, newval);
+  return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
 #else
   return __sync_val_compare_and_swap(ptr, oldval, newval);
 #endif
diff --git a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c
index 3c2253f..0d7bbe6 100644
--- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c
+++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c
@@ -31,6 +31,10 @@
 #include <net/if_dl.h>
 #endif
 
+#if defined(__HAIKU__)
+#define IFF_RUNNING IFF_LINK
+#endif
+
 static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
   if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
     return 1;
@@ -45,7 +49,8 @@
   if (exclude_type == UV__EXCLUDE_IFPHYS)
     return (ent->ifa_addr->sa_family != AF_LINK);
 #endif
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \
+    defined(__HAIKU__)
   /*
    * On BSD getifaddrs returns information related to the raw underlying
    * devices.  We're not interested in this information.
@@ -84,7 +89,8 @@
     return 0;
   }
 
-  *addresses = uv__malloc(*count * sizeof(**addresses));
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = uv__calloc(*count, sizeof(**addresses));
 
   if (*addresses == NULL) {
     freeifaddrs(addrs);
@@ -116,6 +122,7 @@
     address++;
   }
 
+#if !(defined(__CYGWIN__) || defined(__MSYS__))
   /* Fill in physical addresses for each interface */
   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
@@ -124,20 +131,15 @@
     address = *addresses;
 
     for (i = 0; i < *count; i++) {
-#if defined(__CYGWIN__) || defined(__MSYS__)
-      memset(address->phys_addr, 0, sizeof(address->phys_addr));
-#else
       if (strcmp(address->name, ent->ifa_name) == 0) {
         struct sockaddr_dl* sa_addr;
         sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
         memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
-      } else {
-        memset(address->phys_addr, 0, sizeof(address->phys_addr));
       }
-#endif
       address++;
     }
   }
+#endif
 
   freeifaddrs(addrs);
 
diff --git a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
index 309ec79..d42ff05 100644
--- a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
+++ b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
@@ -137,4 +137,13 @@
   errno = ENOSYS;
   return -1;
 }
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+  errno = ENOSYS;
+  return -1;
+}
 #endif
diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c
index a8d6adb..cf7dea0 100644
--- a/Utilities/cmlibuv/src/unix/core.c
+++ b/Utilities/cmlibuv/src/unix/core.c
@@ -42,9 +42,9 @@
 #include <pwd.h>
 #include <sched.h>
 #include <sys/utsname.h>
+#include <sys/time.h>
 
 #ifdef __sun
-# include <netdb.h> /* MAXHOSTNAMELEN on Solaris */
 # include <sys/filio.h>
 # include <sys/types.h>
 # include <sys/wait.h>
@@ -91,13 +91,8 @@
 #include <sys/ioctl.h>
 #endif
 
-#if !defined(__MVS__)
-#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */
-#endif
-
-/* Fallback for the maximum hostname length */
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 256
+#if defined(__linux__)
+#include <sys/syscall.h>
 #endif
 
 static int uv__run_pending(uv_loop_t* loop);
@@ -174,7 +169,9 @@
 
   case UV_FS_POLL:
     uv__fs_poll_close((uv_fs_poll_t*)handle);
-    break;
+    /* Poll handles use file system requests, and one of them may still be
+     * running. The poll code will call uv__make_close_pending() for us. */
+    return;
 
   case UV_SIGNAL:
     uv__signal_close((uv_signal_t*) handle);
@@ -520,6 +517,34 @@
 }
 
 
+/* close() on macos has the "interesting" quirk that it fails with EINTR
+ * without closing the file descriptor when a thread is in the cancel state.
+ * That's why libuv calls close$NOCANCEL() instead.
+ *
+ * glibc on linux has a similar issue: close() is a cancellation point and
+ * will unwind the thread when it's in the cancel state. Work around that
+ * by making the system call directly. Musl libc is unaffected.
+ */
+int uv__close_nocancel(int fd) {
+#if defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
+#if defined(__LP64__)
+  extern int close$NOCANCEL(int);
+  return close$NOCANCEL(fd);
+#else
+  extern int close$NOCANCEL$UNIX2003(int);
+  return close$NOCANCEL$UNIX2003(fd);
+#endif
+#pragma GCC diagnostic pop
+#elif defined(__linux__)
+  return syscall(SYS_close, fd);
+#else
+  return close(fd);
+#endif
+}
+
+
 int uv__close_nocheckstdio(int fd) {
   int saved_errno;
   int rc;
@@ -527,7 +552,7 @@
   assert(fd > -1);  /* Catch uninitialized io_watcher.fd bugs. */
 
   saved_errno = errno;
-  rc = close(fd);
+  rc = uv__close_nocancel(fd);
   if (rc == -1) {
     rc = UV__ERR(errno);
     if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS))
@@ -562,7 +587,7 @@
 }
 
 
-#if !defined(__CYGWIN__) && !defined(__MSYS__)
+#if !defined(__hpux) && !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__)
 int uv__cloexec_ioctl(int fd, int set) {
   int r;
 
@@ -895,7 +920,8 @@
   QUEUE_REMOVE(&w->pending_queue);
 
   /* Remove stale events for this file descriptor */
-  uv__platform_invalidate_fd(loop, w->fd);
+  if (w->fd != -1)
+    uv__platform_invalidate_fd(loop, w->fd);
 }
 
 
@@ -929,7 +955,7 @@
   rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec;
   rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec;
 
-#if !defined(__MVS__)
+#if !defined(__MVS__) && !defined(__HAIKU__)
   rusage->ru_maxrss = usage.ru_maxrss;
   rusage->ru_ixrss = usage.ru_ixrss;
   rusage->ru_idrss = usage.ru_idrss;
@@ -1294,7 +1320,7 @@
     instead by creating a large enough buffer and comparing the hostname length
     to the size input.
   */
-  char buf[MAXHOSTNAMELEN + 1];
+  char buf[UV_MAXHOSTNAMESIZE];
   size_t len;
 
   if (buffer == NULL || size == NULL || *size == 0)
@@ -1426,3 +1452,39 @@
   buffer->machine[0] = '\0';
   return r;
 }
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen) {
+  socklen_t socklen;
+  uv_os_fd_t fd;
+  int r;
+
+  r = uv_fileno(handle, &fd);
+  if (r < 0)
+    return r;
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (func(fd, name, &socklen))
+    return UV__ERR(errno);
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  struct timeval time;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  if (gettimeofday(&time, NULL) != 0)
+    return UV__ERR(errno);
+
+  tv->tv_sec = (int64_t) time.tv_sec;
+  tv->tv_usec = (int32_t) time.tv_usec;
+  return 0;
+}
diff --git a/Utilities/cmlibuv/src/unix/darwin.c b/Utilities/cmlibuv/src/unix/darwin.c
index 31ad8a9..e4cd8ff 100644
--- a/Utilities/cmlibuv/src/unix/darwin.c
+++ b/Utilities/cmlibuv/src/unix/darwin.c
@@ -117,6 +117,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c
index 0f729cf..7de88d6 100644
--- a/Utilities/cmlibuv/src/unix/freebsd.c
+++ b/Utilities/cmlibuv/src/unix/freebsd.c
@@ -137,6 +137,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c
index bffc956..5fb34f1b 100644
--- a/Utilities/cmlibuv/src/unix/fs.c
+++ b/Utilities/cmlibuv/src/unix/fs.c
@@ -47,7 +47,7 @@
 
 #if defined(__DragonFly__)        ||                                      \
     defined(__FreeBSD__)          ||                                      \
-    defined(__FreeBSD_kernel_)    ||                                      \
+    defined(__FreeBSD_kernel__)   ||                                      \
     defined(__OpenBSD__)          ||                                      \
     defined(__NetBSD__)
 # define HAVE_PREADV 1
@@ -60,7 +60,6 @@
 #endif
 
 #if defined(__APPLE__)
-# include <copyfile.h>
 # include <sys/sysctl.h>
 #elif defined(__linux__) && !defined(FICLONE)
 # include <sys/ioctl.h>
@@ -143,19 +142,34 @@
   while (0)
 
 
+static int uv__fs_close(int fd) {
+  int rc;
+
+  rc = uv__close_nocancel(fd);
+  if (rc == -1)
+    if (errno == EINTR || errno == EINPROGRESS)
+      rc = 0;  /* The close is in progress, not an error. */
+
+  return rc;
+}
+
+
 static ssize_t uv__fs_fsync(uv_fs_t* req) {
 #if defined(__APPLE__)
   /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache
    * to the drive platters. This is in contrast to Linux's fdatasync and fsync
    * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent
    * for flushing buffered data to permanent storage. If F_FULLFSYNC is not
-   * supported by the file system we should fall back to fsync(). This is the
-   * same approach taken by sqlite.
+   * supported by the file system we fall back to F_BARRIERFSYNC or fsync().
+   * This is the same approach taken by sqlite, except sqlite does not issue
+   * an F_BARRIERFSYNC call.
    */
   int r;
 
   r = fcntl(req->file, F_FULLFSYNC);
   if (r != 0)
+    r = fcntl(req->file, 85 /* F_BARRIERFSYNC */);  /* fsync + barrier */
+  if (r != 0)
     r = fsync(req->file);
   return r;
 #else
@@ -178,7 +192,8 @@
 
 static ssize_t uv__fs_futime(uv_fs_t* req) {
 #if defined(__linux__)                                                        \
-    || defined(_AIX71)
+    || defined(_AIX71)                                                        \
+    || defined(__HAIKU__)
   /* utimesat() has nanosecond resolution but we stick to microseconds
    * for the sake of consistency with other platforms.
    */
@@ -219,7 +234,7 @@
 #endif
 }
 
-#if defined(__sun) && (_XOPEN_SOURCE < 600 || defined(CMAKE_BOOTSTRAP))
+#if (defined(__sun) || defined(__hpux)) && (_XOPEN_SOURCE < 600 || defined(CMAKE_BOOTSTRAP))
 static char* uv__mkdtemp(char *template)
 {
   if (!mktemp(template) || mkdir(template, 0700))
@@ -327,6 +342,18 @@
   req->bufs = NULL;
   req->nbufs = 0;
 
+#ifdef __PASE__
+  /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */
+  if (result == -1 && errno == EOPNOTSUPP) {
+    struct stat buf;
+    ssize_t rc;
+    rc = fstat(req->file, &buf);
+    if (rc == 0 && S_ISDIR(buf.st_mode)) {
+      errno = EISDIR;
+    }
+  }
+#endif
+
   return result;
 }
 
@@ -349,7 +376,7 @@
 
 
 static ssize_t uv__fs_scandir(uv_fs_t* req) {
-  uv__dirent_t **dents;
+  uv__dirent_t** dents;
   int n;
 
   dents = NULL;
@@ -373,6 +400,87 @@
   return n;
 }
 
+static int uv__fs_opendir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = uv__malloc(sizeof(*dir));
+  if (dir == NULL)
+    goto error;
+
+  dir->dir = opendir(req->path);
+  if (dir->dir == NULL)
+    goto error;
+
+  req->ptr = dir;
+  return 0;
+
+error:
+  uv__free(dir);
+  req->ptr = NULL;
+  return -1;
+}
+
+static int uv__fs_readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirent;
+  struct dirent* res;
+  unsigned int dirent_idx;
+  unsigned int i;
+
+  dir = req->ptr;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    /* readdir() returns NULL on end of directory, as well as on error. errno
+       is used to differentiate between the two conditions. */
+    errno = 0;
+    res = readdir(dir->dir);
+
+    if (res == NULL) {
+      if (errno != 0)
+        goto error;
+      break;
+    }
+
+    if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)
+      continue;
+
+    dirent = &dir->dirents[dirent_idx];
+    dirent->name = uv__strdup(res->d_name);
+
+    if (dirent->name == NULL)
+      goto error;
+
+    dirent->type = uv__fs_get_dirent_type(res);
+    ++dirent_idx;
+  }
+
+  return dirent_idx;
+
+error:
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dir->dirents[i].name);
+    dir->dirents[i].name = NULL;
+  }
+
+  return -1;
+}
+
+static int uv__fs_closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = req->ptr;
+
+  if (dir->dir != NULL) {
+    closedir(dir->dir);
+    dir->dir = NULL;
+  }
+
+  uv__free(req->ptr);
+  req->ptr = NULL;
+  return 0;
+}
+
 #if defined(_POSIX_PATH_MAX)
 # define UV__FS_PATH_MAX _POSIX_PATH_MAX
 #elif defined(PATH_MAX)
@@ -702,7 +810,8 @@
 static ssize_t uv__fs_utime(uv_fs_t* req) {
 #if defined(__linux__)                                                         \
     || defined(_AIX71)                                                         \
-    || defined(__sun)
+    || defined(__sun)                                                          \
+    || defined(__HAIKU__)
   /* utimesat() has nanosecond resolution but we stick to microseconds
    * for the sake of consistency with other platforms.
    */
@@ -806,45 +915,6 @@
 }
 
 static ssize_t uv__fs_copyfile(uv_fs_t* req) {
-#if defined(__APPLE__) && !TARGET_OS_IPHONE
-  /* On macOS, use the native copyfile(3). */
-  static int can_clone;
-  copyfile_flags_t flags;
-  char buf[64];
-  size_t len;
-  int major;
-
-  flags = COPYFILE_ALL;
-
-  if (req->flags & UV_FS_COPYFILE_EXCL)
-    flags |= COPYFILE_EXCL;
-
-  /* Check OS version. Cloning is only supported on macOS >= 10.12. */
-  if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
-    if (can_clone == 0) {
-      len = sizeof(buf);
-      if (sysctlbyname("kern.osrelease", buf, &len, NULL, 0))
-        return UV__ERR(errno);
-
-      if (1 != sscanf(buf, "%d", &major))
-        abort();
-
-      can_clone = -1 + 2 * (major >= 16);  /* macOS >= 10.12 */
-    }
-
-    if (can_clone < 0)
-      return UV_ENOSYS;
-  }
-
-  /* copyfile() simply ignores COPYFILE_CLONE if it's not supported. */
-  if (req->flags & UV_FS_COPYFILE_FICLONE)
-    flags |= 1 << 24;  /* COPYFILE_CLONE */
-
-  if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE)
-    flags |= 1 << 25;  /* COPYFILE_CLONE_FORCE */
-
-  return copyfile(req->path, req->new_path, NULL, flags);
-#else
   uv_fs_t fs_req;
   uv_file srcfd;
   uv_file dstfd;
@@ -971,7 +1041,6 @@
 
   errno = UV__ERR(result);
   return -1;
-#endif
 }
 
 static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
@@ -1051,10 +1120,84 @@
 }
 
 
+static int uv__fs_statx(int fd,
+                        const char* path,
+                        int is_fstat,
+                        int is_lstat,
+                        uv_stat_t* buf) {
+  STATIC_ASSERT(UV_ENOSYS != -1);
+#ifdef __linux__
+  static int no_statx;
+  struct uv__statx statxbuf;
+  int dirfd;
+  int flags;
+  int mode;
+  int rc;
+
+  if (no_statx)
+    return UV_ENOSYS;
+
+  dirfd = AT_FDCWD;
+  flags = 0; /* AT_STATX_SYNC_AS_STAT */
+  mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */
+
+  if (is_fstat) {
+    dirfd = fd;
+    flags |= 0x1000; /* AT_EMPTY_PATH */
+  }
+
+  if (is_lstat)
+    flags |= AT_SYMLINK_NOFOLLOW;
+
+  rc = uv__statx(dirfd, path, flags, mode, &statxbuf);
+
+  if (rc == -1) {
+    /* EPERM happens when a seccomp filter rejects the system call.
+     * Has been observed with libseccomp < 2.3.3 and docker < 18.04.
+     */
+    if (errno != EINVAL && errno != EPERM && errno != ENOSYS)
+      return -1;
+
+    no_statx = 1;
+    return UV_ENOSYS;
+  }
+
+  buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor;
+  buf->st_mode = statxbuf.stx_mode;
+  buf->st_nlink = statxbuf.stx_nlink;
+  buf->st_uid = statxbuf.stx_uid;
+  buf->st_gid = statxbuf.stx_gid;
+  buf->st_rdev = statxbuf.stx_rdev_major;
+  buf->st_ino = statxbuf.stx_ino;
+  buf->st_size = statxbuf.stx_size;
+  buf->st_blksize = statxbuf.stx_blksize;
+  buf->st_blocks = statxbuf.stx_blocks;
+  buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
+  buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
+  buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
+  buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
+  buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
+  buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
+  buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;
+  buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;
+  buf->st_flags = 0;
+  buf->st_gen = 0;
+
+  return 0;
+#else
+  return UV_ENOSYS;
+#endif /* __linux__ */
+}
+
+
 static int uv__fs_stat(const char *path, uv_stat_t *buf) {
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = stat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1067,6 +1210,10 @@
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = lstat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1079,6 +1226,10 @@
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = fstat(fd, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1167,7 +1318,7 @@
     X(ACCESS, access(req->path, req->flags));
     X(CHMOD, chmod(req->path, req->mode));
     X(CHOWN, chown(req->path, req->uid, req->gid));
-    X(CLOSE, close(req->file));
+    X(CLOSE, uv__fs_close(req->file));
     X(COPYFILE, uv__fs_copyfile(req));
     X(FCHMOD, fchmod(req->file, req->mode));
     X(FCHOWN, fchown(req->file, req->uid, req->gid));
@@ -1184,6 +1335,9 @@
     X(OPEN, uv__fs_open(req));
     X(READ, uv__fs_read(req));
     X(SCANDIR, uv__fs_scandir(req));
+    X(OPENDIR, uv__fs_opendir(req));
+    X(READDIR, uv__fs_readdir(req));
+    X(CLOSEDIR, uv__fs_closedir(req));
     X(READLINK, uv__fs_readlink(req));
     X(REALPATH, uv__fs_realpath(req));
     X(RENAME, rename(req->path, req->new_path));
@@ -1454,6 +1608,40 @@
   POST;
 }
 
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  INIT(OPENDIR);
+  PATH;
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(READDIR);
+
+  if (dir == NULL || dir->dir == NULL || dir->dirents == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(CLOSEDIR);
+
+  if (dir == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
 
 int uv_fs_readlink(uv_loop_t* loop,
                    uv_fs_t* req,
@@ -1594,6 +1782,9 @@
   req->path = NULL;
   req->new_path = NULL;
 
+  if (req->fs_type == UV_FS_READDIR && req->ptr != NULL)
+    uv__fs_readdir_cleanup(req);
+
   if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
     uv__fs_scandir_cleanup(req);
 
@@ -1601,7 +1792,7 @@
     uv__free(req->bufs);
   req->bufs = NULL;
 
-  if (req->ptr != &req->statbuf)
+  if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf)
     uv__free(req->ptr);
   req->ptr = NULL;
 }
diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c
index c430562..ddacda3 100644
--- a/Utilities/cmlibuv/src/unix/fsevents.c
+++ b/Utilities/cmlibuv/src/unix/fsevents.c
@@ -21,9 +21,10 @@
 #include "uv.h"
 #include "internal.h"
 
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
 
 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
+/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
 
 int uv__fsevents_init(uv_fs_event_t* handle) {
   return 0;
diff --git a/Utilities/cmlibuv/src/unix/getaddrinfo.c b/Utilities/cmlibuv/src/unix/getaddrinfo.c
index 6d23fbe..d7ca7d1 100644
--- a/Utilities/cmlibuv/src/unix/getaddrinfo.c
+++ b/Utilities/cmlibuv/src/unix/getaddrinfo.c
@@ -92,7 +92,9 @@
   }
   assert(!"unknown EAI_* error code");
   abort();
+#ifndef __SUNPRO_C
   return 0;  /* Pacify compiler. */
+#endif
 }
 
 
diff --git a/Utilities/cmlibuv/src/unix/haiku.c b/Utilities/cmlibuv/src/unix/haiku.c
new file mode 100644
index 0000000..7708851
--- /dev/null
+++ b/Utilities/cmlibuv/src/unix/haiku.c
@@ -0,0 +1,176 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <FindDirectory.h> /* find_path() */
+#include <OS.h>
+
+
+void uv_loadavg(double avg[3]) {
+  avg[0] = 0;
+  avg[1] = 0;
+  avg[2] = 0;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  char abspath[B_PATH_NAME_LENGTH];
+  status_t status;
+  ssize_t abspath_len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  status = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, abspath,
+                     sizeof(abspath));
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  abspath_len = uv__strscpy(buffer, abspath, *size);
+  *size -= 1;
+  if (abspath_len >= 0 && *size > (size_t)abspath_len)
+    *size = (size_t)abspath_len;
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  status_t status;
+  system_info sinfo;
+
+  status = get_system_info(&sinfo);
+  if (status != B_OK)
+    return 0;
+
+  return (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  status_t status;
+  system_info sinfo;
+
+  status = get_system_info(&sinfo);
+  if (status != B_OK)
+    return 0;
+
+  return sinfo.max_pages * B_PAGE_SIZE;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  area_info area;
+  ssize_t cookie;
+  status_t status;
+  thread_info thread;
+
+  status = get_thread_info(find_thread(NULL), &thread);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  cookie = 0;
+  *rss = 0;
+  while (get_next_area_info(thread.team, &cookie, &area) == B_OK)
+    *rss += area.ram_size;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  /* system_time() returns time since booting in microseconds */
+  *uptime = (double)system_time() / 1000000;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  cpu_topology_node_info* topology_infos;
+  int i;
+  status_t status;
+  system_info system;
+  uint32_t topology_count;
+  uint64_t cpuspeed;
+  uv_cpu_info_t* cpu_info;
+
+  if (cpu_infos == NULL || count == NULL)
+    return UV_EINVAL;
+
+  status = get_cpu_topology_info(NULL, &topology_count);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  topology_infos = uv__malloc(topology_count * sizeof(*topology_infos));
+  if (topology_infos == NULL)
+    return UV_ENOMEM;
+
+  status = get_cpu_topology_info(topology_infos, &topology_count);
+  if (status != B_OK) {
+    uv__free(topology_infos);
+    return UV__ERR(status);
+  }
+
+  cpuspeed = 0;
+  for (i = 0; i < (int)topology_count; i++) {
+    if (topology_infos[i].type == B_TOPOLOGY_CORE) {
+      cpuspeed = topology_infos[i].data.core.default_frequency;
+      break;
+    }
+  }
+
+  uv__free(topology_infos);
+
+  status = get_system_info(&system);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  *cpu_infos = uv__calloc(system.cpu_count, sizeof(**cpu_infos));
+  if (*cpu_infos == NULL)
+    return UV_ENOMEM;
+
+  /* CPU time and model are not exposed by Haiku. */
+  cpu_info = *cpu_infos;
+  for (i = 0; i < (int)system.cpu_count; i++) {
+    cpu_info->model = uv__strdup("unknown");
+    cpu_info->speed = (int)(cpuspeed / 1000000);
+    cpu_info++;
+  }
+  *count = system.cpu_count;
+
+  return 0;
+}
+
+void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+  int i;
+
+  for (i = 0; i < count; i++)
+    uv__free(cpu_infos[i].model);
+
+  uv__free(cpu_infos);
+}
diff --git a/Utilities/cmlibuv/src/unix/hpux.c b/Utilities/cmlibuv/src/unix/hpux.c
new file mode 100644
index 0000000..4d3f628
--- /dev/null
+++ b/Utilities/cmlibuv/src/unix/hpux.c
@@ -0,0 +1,30 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdint.h>
+#include <time.h>
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  return (uint64_t) gethrtime();
+}
diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c
index 13fed6c..c7e1051 100644
--- a/Utilities/cmlibuv/src/unix/ibmi.c
+++ b/Utilities/cmlibuv/src/unix/ibmi.c
@@ -55,19 +55,155 @@
 #include <strings.h>
 #include <sys/vnode.h>
 
+#include <as400_protos.h>
+
+
+typedef struct {
+  int bytes_available;
+  int bytes_returned;
+  char current_date_and_time[8];
+  char system_name[8];
+  char elapsed_time[6];
+  char restricted_state_flag;
+  char reserved;
+  int percent_processing_unit_used;
+  int jobs_in_system;
+  int percent_permanent_addresses;
+  int percent_temporary_addresses;
+  int system_asp;
+  int percent_system_asp_used;
+  int total_auxiliary_storage;
+  int current_unprotected_storage_used;
+  int maximum_unprotected_storage_used;
+  int percent_db_capability;
+  int main_storage_size;
+  int number_of_partitions;
+  int partition_identifier;
+  int reserved1;
+  int current_processing_capacity;
+  char processor_sharing_attribute;
+  char reserved2[3];
+  int number_of_processors;
+  int active_jobs_in_system;
+  int active_threads_in_system;
+  int maximum_jobs_in_system;
+  int percent_temporary_256mb_segments_used;
+  int percent_temporary_4gb_segments_used;
+  int percent_permanent_256mb_segments_used;
+  int percent_permanent_4gb_segments_used;
+  int percent_current_interactive_performance;
+  int percent_uncapped_cpu_capacity_used;
+  int percent_shared_processor_pool_used;
+  long main_storage_size_long;
+} SSTS0200;
+
+
+static int get_ibmi_system_status(SSTS0200* rcvr) {
+  /* rcvrlen is input parameter 2 to QWCRSSTS */
+  unsigned int rcvrlen = sizeof(*rcvr);
+
+  /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */
+  unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0};
+
+  /* reset_status is input parameter 4 to QWCRSSTS ("*NO       " in EBCDIC) */
+  unsigned char reset_status[] = {
+    0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+  };
+
+  /* errcode is input parameter 5 to QWCRSSTS */
+  struct _errcode {
+    int bytes_provided;
+    int bytes_available;
+    char msgid[7];
+  } errcode;
+
+  /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */
+  ILEpointer __attribute__((aligned(16))) qwcrssts_pointer;
+
+  /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */
+  void* qwcrssts_argv[6];
+
+  /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */
+  int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS");
+
+  if (rc != 0)
+    return rc;
+
+  /* initialize the QWCRSSTS returned info structure */
+  memset(rcvr, 0, sizeof(*rcvr));
+
+  /* initialize the QWCRSSTS error code structure */
+  memset(&errcode, 0, sizeof(errcode));
+  errcode.bytes_provided = sizeof(errcode);
+
+  /* initialize the array of argument pointers for the QWCRSSTS API */
+  qwcrssts_argv[0] = rcvr;
+  qwcrssts_argv[1] = &rcvrlen;
+  qwcrssts_argv[2] = &format;
+  qwcrssts_argv[3] = &reset_status;
+  qwcrssts_argv[4] = &errcode;
+  qwcrssts_argv[5] = NULL;
+
+  /* Call the IBM i QWCRSSTS API from PASE */
+  rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0);
+
+  return rc;
+}
+
+
 uint64_t uv_get_free_memory(void) {
-  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  /* The amount of main storage, in kilobytes, in the system. */
+  uint64_t main_storage_size = rcvr.main_storage_size;
+
+  /* The current amount of storage in use for temporary objects.
+   * in millions (M) of bytes.
+   */
+  uint64_t current_unprotected_storage_used =
+    rcvr.current_unprotected_storage_used * 1024ULL;
+
+  uint64_t free_storage_size =
+    (main_storage_size - current_unprotected_storage_used) * 1024ULL;
+
+  return free_storage_size < 0 ? 0 : free_storage_size;
 }
 
 
 uint64_t uv_get_total_memory(void) {
-  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  return (uint64_t)rcvr.main_storage_size * 1024ULL;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
 }
 
 
 void uv_loadavg(double avg[3]) {
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr)) {
     avg[0] = avg[1] = avg[2] = 0;
     return;
+  }
+
+  /* The average (in tenths) of the elapsed time during which the processing
+   * units were in use. For example, a value of 411 in binary would be 41.1%.
+   * This percentage could be greater than 100% for an uncapped partition.
+   */
+  double processing_unit_used_percent =
+    rcvr.percent_processing_unit_used / 1000.0;
+
+  avg[0] = avg[1] = avg[2] = processing_unit_used_percent;
 }
 
 
@@ -111,3 +247,4 @@
 
   return 0;
 }
+
diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h
index 48fe6e8..b43c0b1 100644
--- a/Utilities/cmlibuv/src/unix/internal.h
+++ b/Utilities/cmlibuv/src/unix/internal.h
@@ -105,8 +105,7 @@
  */
 #if defined(__clang__) ||                                                     \
     defined(__GNUC__) ||                                                      \
-    defined(__INTEL_COMPILER) ||                                              \
-    defined(__SUNPRO_C)
+    defined(__INTEL_COMPILER)
 # define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
 # define UV_UNUSED(declaration)     __attribute__((unused)) declaration
 #else
@@ -194,6 +193,7 @@
 int uv__nonblock_fcntl(int fd, int set);
 int uv__close(int fd); /* preserves errno */
 int uv__close_nocheckstdio(int fd);
+int uv__close_nocancel(int fd);
 int uv__socket(int domain, int type, int protocol);
 ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
 void uv__make_close_pending(uv_handle_t* handle);
@@ -316,4 +316,11 @@
 int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
 #endif
 
+typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen);
+
 #endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c
index c24f96e..c04e7a4 100644
--- a/Utilities/cmlibuv/src/unix/kqueue.c
+++ b/Utilities/cmlibuv/src/unix/kqueue.c
@@ -59,7 +59,7 @@
 }
 
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
 static int uv__has_forked_with_cfrunloop;
 #endif
 
@@ -70,7 +70,7 @@
   if (err)
     return err;
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   if (loop->cf_state != NULL) {
     /* We cannot start another CFRunloop and/or thread in the child
        process; CF aborts if you try or if you try to touch the thread
@@ -86,7 +86,7 @@
     uv__free(loop->cf_state);
     loop->cf_state = NULL;
   }
-#endif
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
   return err;
 }
 
@@ -387,6 +387,7 @@
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct kevent*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -457,7 +458,7 @@
   if (uv__is_active(handle))
     return UV_EINVAL;
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   /* Nullify field to perform checks later */
   handle->cf_cb = NULL;
   handle->realpath = NULL;
@@ -481,7 +482,7 @@
     }
     return r;
   }
-#endif /* defined(__APPLE__) */
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
 
   /* TODO open asynchronously - but how do we report back errors? */
   fd = open(path, O_RDONLY);
@@ -489,8 +490,11 @@
     return UV__ERR(errno);
 
   handle->path = uv__strdup(path);
-  if (handle->path == NULL)
+  if (handle->path == NULL) {
+    uv__close_nocheckstdio(fd);
     return UV_ENOMEM;
+  }
+
   handle->cb = cb;
   uv__handle_start(handle);
   uv__io_init(&handle->event_watcher, uv__fs_event, fd);
@@ -509,7 +513,7 @@
 
   uv__handle_stop(handle);
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   if (!uv__has_forked_with_cfrunloop)
     r = uv__fsevents_close(handle);
 #endif
diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c
index 3341b94..b539beb 100644
--- a/Utilities/cmlibuv/src/unix/linux-core.c
+++ b/Utilities/cmlibuv/src/unix/linux-core.c
@@ -26,6 +26,7 @@
 #include "uv.h"
 #include "internal.h"
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -79,16 +80,20 @@
                       unsigned int numcpus,
                       uv_cpu_info_t* ci);
 static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
-static unsigned long read_cpufreq(unsigned int cpunum);
+static uint64_t read_cpufreq(unsigned int cpunum);
 
 
 int uv__platform_loop_init(uv_loop_t* loop) {
   int fd;
 
-  fd = epoll_create1(EPOLL_CLOEXEC);
+  /* It was reported that EPOLL_CLOEXEC is not defined on Android API < 21,
+   * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all
+   * architectures, we just use that instead.
+   */
+  fd = epoll_create1(O_CLOEXEC);
 
   /* epoll_create1() can fail either because it's not implemented (old kernel)
-   * or because it doesn't understand the EPOLL_CLOEXEC flag.
+   * or because it doesn't understand the O_CLOEXEC flag.
    */
   if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
     fd = epoll_create(256);
@@ -141,6 +146,7 @@
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct epoll_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -714,20 +720,20 @@
 static int read_times(FILE* statfile_fp,
                       unsigned int numcpus,
                       uv_cpu_info_t* ci) {
-  unsigned long clock_ticks;
   struct uv_cpu_times_s ts;
-  unsigned long user;
-  unsigned long nice;
-  unsigned long sys;
-  unsigned long idle;
-  unsigned long dummy;
-  unsigned long irq;
-  unsigned int num;
-  unsigned int len;
+  uint64_t clock_ticks;
+  uint64_t user;
+  uint64_t nice;
+  uint64_t sys;
+  uint64_t idle;
+  uint64_t dummy;
+  uint64_t irq;
+  uint64_t num;
+  uint64_t len;
   char buf[1024];
 
   clock_ticks = sysconf(_SC_CLK_TCK);
-  assert(clock_ticks != (unsigned long) -1);
+  assert(clock_ticks != (uint64_t) -1);
   assert(clock_ticks != 0);
 
   rewind(statfile_fp);
@@ -760,7 +766,8 @@
      * fields, they're not allowed in C89 mode.
      */
     if (6 != sscanf(buf + len,
-                    "%lu %lu %lu %lu %lu %lu",
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64,
                     &user,
                     &nice,
                     &sys,
@@ -782,8 +789,8 @@
 }
 
 
-static unsigned long read_cpufreq(unsigned int cpunum) {
-  unsigned long val;
+static uint64_t read_cpufreq(unsigned int cpunum) {
+  uint64_t val;
   char buf[1024];
   FILE* fp;
 
@@ -796,7 +803,7 @@
   if (fp == NULL)
     return 0;
 
-  if (fscanf(fp, "%lu", &val) != 1)
+  if (fscanf(fp, "%" PRIu64, &val) != 1)
     val = 0;
 
   fclose(fp);
@@ -859,7 +866,8 @@
     return 0;
   }
 
-  *addresses = uv__malloc(*count * sizeof(**addresses));
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = uv__calloc(*count, sizeof(**addresses));
   if (!(*addresses)) {
     freeifaddrs(addrs);
     return UV_ENOMEM;
@@ -898,11 +906,12 @@
     address = *addresses;
 
     for (i = 0; i < (*count); i++) {
-      if (strcmp(address->name, ent->ifa_name) == 0) {
+      size_t namelen = strlen(ent->ifa_name);
+      /* Alias interface share the same physical address */
+      if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
+          (address->name[namelen] == 0 || address->name[namelen] == ':')) {
         sll = (struct sockaddr_ll*)ent->ifa_addr;
         memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
-      } else {
-        memset(address->phys_addr, 0, sizeof(address->phys_addr));
       }
       address++;
     }
@@ -932,3 +941,114 @@
   prctl(PR_SET_NAME, title);  /* Only copies first 16 characters. */
 #endif
 }
+
+
+static uint64_t uv__read_proc_meminfo(const char* what) {
+  uint64_t rc;
+  ssize_t n;
+  char* p;
+  int fd;
+  char buf[4096];  /* Large enough to hold all of /proc/meminfo. */
+
+  rc = 0;
+  fd = uv__open_cloexec("/proc/meminfo", O_RDONLY);
+
+  if (fd == -1)
+    return 0;
+
+  n = read(fd, buf, sizeof(buf) - 1);
+
+  if (n <= 0)
+    goto out;
+
+  buf[n] = '\0';
+  p = strstr(buf, what);
+
+  if (p == NULL)
+    goto out;
+
+  p += strlen(what);
+
+  if (1 != sscanf(p, "%" PRIu64 " kB", &rc))
+    goto out;
+
+  rc *= 1024;
+
+out:
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  return rc;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemFree:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.freeram * info.mem_unit;
+
+  return 0;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemTotal:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.totalram * info.mem_unit;
+
+  return 0;
+}
+
+
+static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {
+  char filename[256];
+  uint64_t rc;
+  int fd;
+  ssize_t n;
+  char buf[32];  /* Large enough to hold an encoded uint64_t. */
+
+  snprintf(filename, 256, "/sys/fs/cgroup/%s/%s", cgroup, param);
+
+  rc = 0;
+  fd = uv__open_cloexec(filename, O_RDONLY);
+
+  if (fd < 0)
+    return 0;
+
+  n = read(fd, buf, sizeof(buf) - 1);
+
+  if (n > 0) {
+    buf[n] = '\0';
+    sscanf(buf, "%" PRIu64, &rc);
+  }
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  return rc;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  /*
+   * This might return 0 if there was a problem getting the memory limit from
+   * cgroups. This is OK because a return value of 0 signifies that the memory
+   * limit is unknown.
+   */
+  return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");
+}
diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.c b/Utilities/cmlibuv/src/unix/linux-syscalls.c
index bfd7544..5637cf9 100644
--- a/Utilities/cmlibuv/src/unix/linux-syscalls.c
+++ b/Utilities/cmlibuv/src/unix/linux-syscalls.c
@@ -187,6 +187,21 @@
 # endif
 #endif /* __NR_pwritev */
 
+#ifndef __NR_statx
+# if defined(__x86_64__)
+#  define __NR_statx 332
+# elif defined(__i386__)
+#  define __NR_statx 383
+# elif defined(__aarch64__)
+#  define __NR_statx 397
+# elif defined(__arm__)
+#  define __NR_statx (UV_SYSCALL_BASE + 397)
+# elif defined(__ppc__)
+#  define __NR_statx 383
+# elif defined(__s390__)
+#  define __NR_statx 379
+# endif
+#endif /* __NR_statx */
 
 int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
 #if defined(__i386__)
@@ -336,3 +351,19 @@
   return errno = ENOSYS, -1;
 #endif
 }
+
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+  /* __NR_statx make Android box killed by SIGSYS.
+   * That looks like a seccomp2 sandbox filter rejecting the system call.
+   */
+#if defined(__NR_statx) && !defined(__ANDROID__)
+  return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.h b/Utilities/cmlibuv/src/unix/linux-syscalls.h
index 3dfd329..7e58bfa 100644
--- a/Utilities/cmlibuv/src/unix/linux-syscalls.h
+++ b/Utilities/cmlibuv/src/unix/linux-syscalls.h
@@ -80,6 +80,36 @@
 #define UV__IN_DELETE_SELF    0x400
 #define UV__IN_MOVE_SELF      0x800
 
+struct uv__statx_timestamp {
+  int64_t tv_sec;
+  uint32_t tv_nsec;
+  int32_t unused0;
+};
+
+struct uv__statx {
+  uint32_t stx_mask;
+  uint32_t stx_blksize;
+  uint64_t stx_attributes;
+  uint32_t stx_nlink;
+  uint32_t stx_uid;
+  uint32_t stx_gid;
+  uint16_t stx_mode;
+  uint16_t unused0;
+  uint64_t stx_ino;
+  uint64_t stx_size;
+  uint64_t stx_blocks;
+  uint64_t stx_attributes_mask;
+  struct uv__statx_timestamp stx_atime;
+  struct uv__statx_timestamp stx_btime;
+  struct uv__statx_timestamp stx_ctime;
+  struct uv__statx_timestamp stx_mtime;
+  uint32_t stx_rdev_major;
+  uint32_t stx_rdev_minor;
+  uint32_t stx_dev_major;
+  uint32_t stx_dev_minor;
+  uint64_t unused1[14];
+};
+
 struct uv__inotify_event {
   int32_t wd;
   uint32_t mask;
@@ -113,5 +143,10 @@
 ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
 ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
 int uv__dup3(int oldfd, int newfd, int flags);
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf);
 
 #endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c
index a2a4e52..c649bb3 100644
--- a/Utilities/cmlibuv/src/unix/netbsd.c
+++ b/Utilities/cmlibuv/src/unix/netbsd.c
@@ -126,6 +126,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   kvm_t *kd = NULL;
   struct kinfo_proc2 *kinfo = NULL;
diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c
index bffb58b..ffae768 100644
--- a/Utilities/cmlibuv/src/unix/openbsd.c
+++ b/Utilities/cmlibuv/src/unix/openbsd.c
@@ -136,6 +136,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   struct kinfo_proc kinfo;
   size_t page_size = getpagesize();
diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c
index dc146e3..273ded7 100644
--- a/Utilities/cmlibuv/src/unix/os390.c
+++ b/Utilities/cmlibuv/src/unix/os390.c
@@ -356,6 +356,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   char* ascb;
   char* rax;
@@ -657,6 +662,7 @@
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct epoll_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c
index 9657bc9..7d97550 100644
--- a/Utilities/cmlibuv/src/unix/pipe.c
+++ b/Utilities/cmlibuv/src/unix/pipe.c
@@ -213,7 +213,7 @@
   }
 
   if (err == 0)
-    uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT);
+    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
 
 out:
   handle->delayed_error = err;
@@ -231,9 +231,6 @@
 }
 
 
-typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
-
-
 static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
                                     uv__peersockfunc func,
                                     char* buffer,
@@ -244,10 +241,13 @@
 
   addrlen = sizeof(sa);
   memset(&sa, 0, addrlen);
-  err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen);
+  err = uv__getsockpeername((const uv_handle_t*) handle,
+                            func,
+                            (struct sockaddr*) &sa,
+                            (int*) &addrlen);
   if (err < 0) {
     *size = 0;
-    return UV__ERR(errno);
+    return err;
   }
 
 #if defined(__linux__)
diff --git a/Utilities/cmlibuv/src/unix/posix-hrtime.c b/Utilities/cmlibuv/src/unix/posix-hrtime.c
index a264250..870b45c 100644
--- a/Utilities/cmlibuv/src/unix/posix-hrtime.c
+++ b/Utilities/cmlibuv/src/unix/posix-hrtime.c
@@ -43,6 +43,20 @@
   return mach_absolute_time() * info.numer / info.denom;
 }
 
+#elif defined(__hpux)
+/* Special case for CMake bootstrap: no CLOCK_MONOTONIC on HP-UX */
+
+#ifndef CMAKE_BOOTSTRAP
+#error "This code path meant only for use during CMake bootstrap."
+#endif
+
+#include <stdint.h>
+#include <time.h>
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  return (uint64_t) gethrtime();
+}
+
 #else
 
 #include <stdint.h>
diff --git a/Utilities/cmlibuv/src/unix/posix-poll.c b/Utilities/cmlibuv/src/unix/posix-poll.c
index f3181f9..a3b9f21 100644
--- a/Utilities/cmlibuv/src/unix/posix-poll.c
+++ b/Utilities/cmlibuv/src/unix/posix-poll.c
@@ -298,6 +298,8 @@
 void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   size_t i;
 
+  assert(fd >= 0);
+
   if (loop->poll_fds_iterating) {
     /* uv__io_poll is currently iterating.  Just invalidate fd.  */
     for (i = 0; i < loop->poll_fds_used; i++)
diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c
index e9579f5..f4826bf 100644
--- a/Utilities/cmlibuv/src/unix/process.c
+++ b/Utilities/cmlibuv/src/unix/process.c
@@ -426,6 +426,11 @@
     if (n == SIGKILL || n == SIGSTOP)
       continue;  /* Can't be changed. */
 
+#if defined(__HAIKU__)
+    if (n == SIGKILLTHR)
+      continue;  /* Can't be changed. */
+#endif
+
     if (SIG_ERR != signal(n, SIG_DFL))
       continue;
 
@@ -486,6 +491,8 @@
                               UV_PROCESS_SETGID |
                               UV_PROCESS_SETUID |
                               UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_HIDE_CONSOLE |
+                              UV_PROCESS_WINDOWS_HIDE_GUI |
                               UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
 
   uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c
index 4b9123f..8121f64 100644
--- a/Utilities/cmlibuv/src/unix/stream.c
+++ b/Utilities/cmlibuv/src/unix/stream.c
@@ -745,13 +745,13 @@
 
   buf = req->bufs + req->write_index;
 
-  while (n > 0) {
+  do {
     len = n < buf->len ? n : buf->len;
     buf->base += len;
     buf->len -= len;
     buf += (buf->len == 0);  /* Advance to next buffer if this one is empty. */
     n -= len;
-  }
+  } while (n > 0);
 
   req->write_index = buf - req->bufs;
 
@@ -897,7 +897,7 @@
     goto error;
   }
 
-  if (n > 0 && uv__write_req_update(stream, req, n)) {
+  if (n >= 0 && uv__write_req_update(stream, req, n)) {
     uv__write_req_finish(req);
     return;  /* TODO(bnoordhuis) Start trying to write the next request. */
   }
@@ -1541,7 +1541,7 @@
   }
 
   if (written == 0 && req_size != 0)
-    return UV_EAGAIN;
+    return req.error < 0 ? req.error : UV_EAGAIN;
   else
     return written;
 }
diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c
index aac6504..0cd25c1 100644
--- a/Utilities/cmlibuv/src/unix/sunos.c
+++ b/Utilities/cmlibuv/src/unix/sunos.c
@@ -121,6 +121,7 @@
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct port_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -138,8 +139,10 @@
   if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
     return UV__ERR(errno);
 
-  if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
+  if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) {
+    perror("(libuv) port_dissociate()");
     abort();
+  }
 
   return 0;
 }
@@ -177,8 +180,14 @@
     w = QUEUE_DATA(q, uv__io_t, watcher_queue);
     assert(w->pevents != 0);
 
-    if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0))
+    if (port_associate(loop->backend_fd,
+                       PORT_SOURCE_FD,
+                       w->fd,
+                       w->pevents,
+                       0)) {
+      perror("(libuv) port_associate()");
       abort();
+    }
 
     w->events = w->pevents;
   }
@@ -222,10 +231,12 @@
       /* Work around another kernel bug: port_getn() may return events even
        * on error.
        */
-      if (errno == EINTR || errno == ETIME)
+      if (errno == EINTR || errno == ETIME) {
         saved_errno = errno;
-      else
+      } else {
+        perror("(libuv) port_getn()");
         abort();
+      }
     }
 
     /* Update loop->time unconditionally. It's tempting to skip the update when
@@ -373,6 +384,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   (void) getloadavg(avg, 3);
 }
diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c
index 2982851..8cedcd6 100644
--- a/Utilities/cmlibuv/src/unix/tcp.c
+++ b/Utilities/cmlibuv/src/unix/tcp.c
@@ -82,7 +82,7 @@
         handle->flags |= flags;
         return 0;
       }
-      
+
       /* Query to see if tcp socket is bound. */
       slen = sizeof(saddr);
       memset(&saddr, 0, sizeof(saddr));
@@ -235,12 +235,16 @@
   if (r == -1 && errno != 0) {
     if (errno == EINPROGRESS)
       ; /* not an error */
-    else if (errno == ECONNREFUSED)
-    /* If we get a ECONNREFUSED wait until the next tick to report the
-     * error. Solaris wants to report immediately--other unixes want to
-     * wait.
+    else if (errno == ECONNREFUSED
+#if defined(__OpenBSD__)
+      || errno == EINVAL
+#endif
+      )
+    /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the
+     * next tick to report the error. Solaris and OpenBSD wants to report
+     * immediately -- other unixes want to wait.
      */
-      handle->delayed_error = UV__ERR(errno);
+      handle->delayed_error = UV__ERR(ECONNREFUSED);
     else
       return UV__ERR(errno);
   }
@@ -279,44 +283,28 @@
 int uv_tcp_getsockname(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
 
   if (handle->delayed_error)
     return handle->delayed_error;
 
-  if (uv__stream_fd(handle) < 0)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
-
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
-
-  if (getsockname(uv__stream_fd(handle), name, &socklen))
-    return UV__ERR(errno);
-
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
 }
 
 
 int uv_tcp_getpeername(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
 
   if (handle->delayed_error)
     return handle->delayed_error;
 
-  if (uv__stream_fd(handle) < 0)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
-
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
-
-  if (getpeername(uv__stream_fd(handle), name, &socklen))
-    return UV__ERR(errno);
-
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
 }
 
 
diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c
index 2900470..0453221 100644
--- a/Utilities/cmlibuv/src/unix/thread.c
+++ b/Utilities/cmlibuv/src/unix/thread.c
@@ -48,8 +48,10 @@
 STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
 #endif
 
-/* Note: guard clauses should match uv_barrier_t's in include/uv/uv-unix.h. */
-#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
 int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
   struct _uv_barrier* b;
   int rc;
@@ -176,8 +178,21 @@
   if (lim.rlim_cur != RLIM_INFINITY) {
     /* pthread_attr_setstacksize() expects page-aligned values. */
     lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
-    if (lim.rlim_cur >= PTHREAD_STACK_MIN)
-      return lim.rlim_cur;
+
+    /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
+     * too small to safely receive signals on.
+     *
+     * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
+     * the largest MINSIGSTKSZ of the architectures that musl supports) so
+     * let's use that as a lower bound.
+     *
+     * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
+     * is between 28 and 133 KB when compiling against glibc, depending
+     * on the architecture.
+     */
+    if (lim.rlim_cur >= 8192)
+      if (lim.rlim_cur >= PTHREAD_STACK_MIN)
+        return lim.rlim_cur;
   }
 #endif
 
@@ -192,13 +207,36 @@
 
 
 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
   int err;
-  size_t stack_size;
   pthread_attr_t* attr;
   pthread_attr_t attr_storage;
+  size_t pagesize;
+  size_t stack_size;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
 
   attr = NULL;
-  stack_size = thread_stack_size();
+  if (stack_size == 0) {
+    stack_size = thread_stack_size();
+  } else {
+    pagesize = (size_t)getpagesize();
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+#ifdef PTHREAD_STACK_MIN
+    if (stack_size < PTHREAD_STACK_MIN)
+      stack_size = PTHREAD_STACK_MIN;
+#endif
+  }
 
   if (stack_size > 0) {
     attr = &attr_storage;
@@ -662,7 +700,7 @@
   if (err)
     return UV__ERR(err);
 
-#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21)
+#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) && !defined(__hpux)
   err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
   if (err)
     goto error2;
@@ -778,7 +816,9 @@
     return UV_ETIMEDOUT;
 
   abort();
+#ifndef __SUNPRO_C
   return UV_EINVAL;  /* Satisfy the compiler. */
+#endif
 }
 
 
diff --git a/Utilities/cmlibuv/src/unix/tty.c b/Utilities/cmlibuv/src/unix/tty.c
index b8bc283..db479d6 100644
--- a/Utilities/cmlibuv/src/unix/tty.c
+++ b/Utilities/cmlibuv/src/unix/tty.c
@@ -200,7 +200,7 @@
 static void uv__tty_make_raw(struct termios* tio) {
   assert(tio != NULL);
 
-#if defined __sun || defined __MVS__
+#if defined __sun || defined __MVS__ || defined __hpux
   /*
    * This implementation of cfmakeraw for Solaris and derivatives is taken from
    * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c
index ec337ec..b578e7b 100644
--- a/Utilities/cmlibuv/src/unix/udp.c
+++ b/Utilities/cmlibuv/src/unix/udp.c
@@ -30,6 +30,7 @@
 #if defined(__MVS__)
 #include <xti.h>
 #endif
+#include <sys/un.h>
 
 #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
 # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
@@ -227,9 +228,22 @@
     assert(req != NULL);
 
     memset(&h, 0, sizeof h);
-    h.msg_name = &req->addr;
-    h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
-      sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+    if (req->addr.ss_family == AF_UNSPEC) {
+      h.msg_name = NULL;
+      h.msg_namelen = 0;
+    } else {
+      h.msg_name = &req->addr;
+      if (req->addr.ss_family == AF_INET6)
+        h.msg_namelen = sizeof(struct sockaddr_in6);
+      else if (req->addr.ss_family == AF_INET)
+        h.msg_namelen = sizeof(struct sockaddr_in);
+      else if (req->addr.ss_family == AF_UNIX)
+        h.msg_namelen = sizeof(struct sockaddr_un);
+      else {
+        assert(0 && "unsupported address family");
+        abort();
+      }
+    }
     h.msg_iov = (struct iovec*) req->bufs;
     h.msg_iovlen = req->nbufs;
 
@@ -263,16 +277,30 @@
  * are different from the BSDs: it _shares_ the port rather than steal it
  * from the current listener.  While useful, it's not something we can emulate
  * on other platforms so we don't enable it.
+ *
+ * zOS does not support getsockname with SO_REUSEPORT option when using
+ * AF_UNIX.
  */
 static int uv__set_reuse(int fd) {
   int yes;
-
-#if defined(SO_REUSEPORT) && !defined(__linux__)
   yes = 1;
+
+#if defined(SO_REUSEPORT) && defined(__MVS__)
+  struct sockaddr_in sockfd;
+  unsigned int sockfd_len = sizeof(sockfd);
+  if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1)
+      return UV__ERR(errno);
+  if (sockfd.sin_family == AF_UNIX) {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
+      return UV__ERR(errno);
+  } else {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+       return UV__ERR(errno);
+  }
+#elif defined(SO_REUSEPORT) && !defined(__linux__)
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #else
-  yes = 1;
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #endif
@@ -383,6 +411,50 @@
 }
 
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  int err;
+
+  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+  if (err)
+    return err;
+
+  do {
+    errno = 0;
+    err = connect(handle->io_watcher.fd, addr, addrlen);
+  } while (err == -1 && errno == EINTR);
+
+  if (err)
+    return UV__ERR(errno);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int r;
+    struct sockaddr addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    addr.sa_family = AF_UNSPEC;
+
+    do {
+      errno = 0;
+      r = connect(handle->io_watcher.fd, &addr, sizeof(addr));
+    } while (r == -1 && errno == EINTR);
+
+    if (r == -1 && errno != EAFNOSUPPORT)
+      return UV__ERR(errno);
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
 int uv__udp_send(uv_udp_send_t* req,
                  uv_udp_t* handle,
                  const uv_buf_t bufs[],
@@ -395,9 +467,11 @@
 
   assert(nbufs > 0);
 
-  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
-  if (err)
-    return err;
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  }
 
   /* It's legal for send_queue_count > 0 even when the write_queue is empty;
    * it means there are error-state requests in the write_completed_queue that
@@ -407,7 +481,10 @@
 
   uv__req_init(handle->loop, req, UV_UDP_SEND);
   assert(addrlen <= sizeof(req->addr));
-  memcpy(&req->addr, addr, addrlen);
+  if (addr == NULL)
+    req->addr.ss_family = AF_UNSPEC;
+  else
+    memcpy(&req->addr, addr, addrlen);
   req->send_cb = send_cb;
   req->handle = handle;
   req->nbufs = nbufs;
@@ -459,9 +536,13 @@
   if (handle->send_queue_count != 0)
     return UV_EAGAIN;
 
-  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
-  if (err)
-    return err;
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  } else {
+    assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
+  }
 
   memset(&h, 0, sizeof h);
   h.msg_name = (struct sockaddr*) addr;
@@ -608,6 +689,7 @@
   uv__io_init(&handle->io_watcher, uv__udp_io, fd);
   QUEUE_INIT(&handle->write_queue);
   QUEUE_INIT(&handle->write_completed_queue);
+
   return 0;
 }
 
@@ -636,6 +718,9 @@
     return err;
 
   handle->io_watcher.fd = sock;
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
   return 0;
 }
 
@@ -743,13 +828,17 @@
                         IPV6_UNICAST_HOPS,
                         &ttl,
                         sizeof(ttl));
-#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
-          defined(__MVS__) */
+
+#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+           defined(__MVS__)) */
 
   return uv__setsockopt_maybe_char(handle,
                                    IP_TTL,
                                    IPV6_UNICAST_HOPS,
                                    ttl);
+
+#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+          defined(__MVS__) */
 }
 
 
@@ -851,23 +940,24 @@
   return 0;
 }
 
+int uv_udp_getpeername(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
+}
 
 int uv_udp_getsockname(const uv_udp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
 
-  if (handle->io_watcher.fd == -1)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
-
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
-
-  if (getsockname(handle->io_watcher.fd, name, &socklen))
-    return UV__ERR(errno);
-
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
 }
 
 
diff --git a/Utilities/cmlibuv/src/uv-common.c b/Utilities/cmlibuv/src/uv-common.c
index 907ebf2..f4853d6 100644
--- a/Utilities/cmlibuv/src/uv-common.c
+++ b/Utilities/cmlibuv/src/uv-common.c
@@ -34,6 +34,7 @@
 # include <malloc.h> /* malloc */
 #else
 # include <net/if.h> /* if_nametoindex */
+# include <sys/un.h> /* AF_UNIX, sockaddr_un */
 #endif
 
 
@@ -223,6 +224,9 @@
   memset(addr, 0, sizeof(*addr));
   addr->sin6_family = AF_INET6;
   addr->sin6_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin6_len = sizeof(*addr);
+#endif
 
   zone_index = strchr(ip, '%');
   if (zone_index != NULL) {
@@ -315,17 +319,20 @@
 }
 
 
-int uv_udp_send(uv_udp_send_t* req,
-                uv_udp_t* handle,
-                const uv_buf_t bufs[],
-                unsigned int nbufs,
-                const struct sockaddr* addr,
-                uv_udp_send_cb send_cb) {
+int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
   unsigned int addrlen;
 
   if (handle->type != UV_UDP)
     return UV_EINVAL;
 
+  /* Disconnect the handle */
+  if (addr == NULL) {
+    if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
+      return UV_ENOTCONN;
+
+    return uv__udp_disconnect(handle);
+  }
+
   if (addr->sa_family == AF_INET)
     addrlen = sizeof(struct sockaddr_in);
   else if (addr->sa_family == AF_INET6)
@@ -333,6 +340,70 @@
   else
     return UV_EINVAL;
 
+  if (handle->flags & UV_HANDLE_UDP_CONNECTED)
+    return UV_EISCONN;
+
+  return uv__udp_connect(handle, addr, addrlen);
+}
+
+
+int uv__udp_is_connected(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+  if (handle->type != UV_UDP)
+    return 0;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EISCONN;
+
+  if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EDESTADDRREQ;
+
+  if (addr != NULL) {
+    if (addr->sa_family == AF_INET)
+      addrlen = sizeof(struct sockaddr_in);
+    else if (addr->sa_family == AF_INET6)
+      addrlen = sizeof(struct sockaddr_in6);
+#if defined(AF_UNIX) && !defined(_WIN32)
+    else if (addr->sa_family == AF_UNIX)
+      addrlen = sizeof(struct sockaddr_un);
+#endif
+    else
+      return UV_EINVAL;
+  } else {
+    addrlen = 0;
+  }
+
+  return addrlen;
+}
+
+
+int uv_udp_send(uv_udp_send_t* req,
+                uv_udp_t* handle,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                const struct sockaddr* addr,
+                uv_udp_send_cb send_cb) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
   return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
 }
 
@@ -341,17 +412,11 @@
                     const uv_buf_t bufs[],
                     unsigned int nbufs,
                     const struct sockaddr* addr) {
-  unsigned int addrlen;
+  int addrlen;
 
-  if (handle->type != UV_UDP)
-    return UV_EINVAL;
-
-  if (addr->sa_family == AF_INET)
-    addrlen = sizeof(struct sockaddr_in);
-  else if (addr->sa_family == AF_INET6)
-    addrlen = sizeof(struct sockaddr_in6);
-  else
-    return UV_EINVAL;
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
 
   return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
 }
@@ -573,37 +638,66 @@
   dent = dents[(*nbufs)++];
 
   ent->name = dent->d_name;
+  ent->type = uv__fs_get_dirent_type(dent);
+
+  return 0;
+}
+
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) {
+  uv_dirent_type_t type;
+
 #ifdef HAVE_DIRENT_TYPES
   switch (dent->d_type) {
     case UV__DT_DIR:
-      ent->type = UV_DIRENT_DIR;
+      type = UV_DIRENT_DIR;
       break;
     case UV__DT_FILE:
-      ent->type = UV_DIRENT_FILE;
+      type = UV_DIRENT_FILE;
       break;
     case UV__DT_LINK:
-      ent->type = UV_DIRENT_LINK;
+      type = UV_DIRENT_LINK;
       break;
     case UV__DT_FIFO:
-      ent->type = UV_DIRENT_FIFO;
+      type = UV_DIRENT_FIFO;
       break;
     case UV__DT_SOCKET:
-      ent->type = UV_DIRENT_SOCKET;
+      type = UV_DIRENT_SOCKET;
       break;
     case UV__DT_CHAR:
-      ent->type = UV_DIRENT_CHAR;
+      type = UV_DIRENT_CHAR;
       break;
     case UV__DT_BLOCK:
-      ent->type = UV_DIRENT_BLOCK;
+      type = UV_DIRENT_BLOCK;
       break;
     default:
-      ent->type = UV_DIRENT_UNKNOWN;
+      type = UV_DIRENT_UNKNOWN;
   }
 #else
-  ent->type = UV_DIRENT_UNKNOWN;
+  type = UV_DIRENT_UNKNOWN;
 #endif
 
-  return 0;
+  return type;
+}
+
+void uv__fs_readdir_cleanup(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  int i;
+
+  if (req->ptr == NULL)
+    return;
+
+  dir = req->ptr;
+  dirents = dir->dirents;
+  req->ptr = NULL;
+
+  if (dirents == NULL)
+    return;
+
+  for (i = 0; i < req->result; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
 }
 
 
diff --git a/Utilities/cmlibuv/src/uv-common.h b/Utilities/cmlibuv/src/uv-common.h
index 15ac4d0..f788161 100644
--- a/Utilities/cmlibuv/src/uv-common.h
+++ b/Utilities/cmlibuv/src/uv-common.h
@@ -103,6 +103,7 @@
 
   /* Only used by uv_udp_t handles. */
   UV_HANDLE_UDP_PROCESSING              = 0x01000000,
+  UV_HANDLE_UDP_CONNECTED               = 0x02000000,
 
   /* Only used by uv_pipe_t handles. */
   UV_HANDLE_NON_OVERLAPPED_PIPE         = 0x01000000,
@@ -142,6 +143,14 @@
                  unsigned int  addrlen,
                  unsigned int flags);
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen);
+
+int uv__udp_disconnect(uv_udp_t* handle);
+
+int uv__udp_is_connected(uv_udp_t* handle);
+
 int uv__udp_send(uv_udp_send_t* req,
                  uv_udp_t* handle,
                  const uv_buf_t bufs[],
@@ -184,6 +193,8 @@
 int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
 
 void uv__fs_scandir_cleanup(uv_fs_t* req);
+void uv__fs_readdir_cleanup(uv_fs_t* req);
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent);
 
 int uv__next_timeout(const uv_loop_t* loop);
 void uv__run_timers(uv_loop_t* loop);
diff --git a/Utilities/cmlibuv/src/uv-data-getter-setters.c b/Utilities/cmlibuv/src/uv-data-getter-setters.c
index b7fcd4a..c302566 100644
--- a/Utilities/cmlibuv/src/uv-data-getter-setters.c
+++ b/Utilities/cmlibuv/src/uv-data-getter-setters.c
@@ -36,7 +36,7 @@
   case UV_REQ_TYPE_MAX:
   case UV_UNKNOWN_REQ:
   default: /* UV_REQ_TYPE_PRIVATE */
-     return NULL;
+    break;
   }
   return NULL;
 }
diff --git a/Utilities/cmlibuv/src/win/core.c b/Utilities/cmlibuv/src/win/core.c
index 58309c6..e9d0a58 100644
--- a/Utilities/cmlibuv/src/win/core.c
+++ b/Utilities/cmlibuv/src/win/core.c
@@ -627,3 +627,26 @@
 int uv_cpumask_size(void) {
   return (int)(sizeof(DWORD_PTR) * 8);
 }
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error) {
+
+  int result;
+  uv_os_fd_t fd;
+
+  result = uv_fileno(handle, &fd);
+  if (result != 0)
+    return result;
+
+  if (delayed_error)
+    return uv_translate_sys_error(delayed_error);
+
+  result = func((SOCKET) fd, name, namelen);
+  if (result != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c
index 65d936b..9e2f084 100644
--- a/Utilities/cmlibuv/src/win/fs.c
+++ b/Utilities/cmlibuv/src/win/fs.c
@@ -1125,6 +1125,137 @@
     uv__free(dirents);
 }
 
+void fs__opendir(uv_fs_t* req) {
+  WCHAR* pathw;
+  size_t len;
+  const WCHAR* fmt;
+  WCHAR* find_path;
+  uv_dir_t* dir;
+
+  pathw = req->file.pathw;
+  dir = NULL;
+  find_path = NULL;
+
+  /* Figure out whether path is a file or a directory. */
+  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+    goto error;
+  }
+
+  dir = uv__malloc(sizeof(*dir));
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  len = wcslen(pathw);
+
+  if (len == 0)
+    fmt = L"./*";
+  else if (IS_SLASH(pathw[len - 1]))
+    fmt = L"%s*";
+  else
+    fmt = L"%s\\*";
+
+  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
+  if (find_path == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  _snwprintf(find_path, len + 3, fmt, pathw);
+  dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
+  uv__free(find_path);
+  find_path = NULL;
+  if (dir->dir_handle == INVALID_HANDLE_VALUE &&
+      GetLastError() != ERROR_FILE_NOT_FOUND) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  dir->need_find_call = FALSE;
+  req->ptr = dir;
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(dir);
+  uv__free(find_path);
+  req->ptr = NULL;
+}
+
+void fs__readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  uv__dirent_t dent;
+  unsigned int dirent_idx;
+  PWIN32_FIND_DATAW find_data;
+  unsigned int i;
+  int r;
+
+  req->flags |= UV_FS_FREE_PTR;
+  dir = req->ptr;
+  dirents = dir->dirents;
+  memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
+  find_data = &dir->find_data;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
+      if (GetLastError() == ERROR_NO_MORE_FILES)
+        break;
+      goto error;
+    }
+
+    /* Skip "." and ".." entries. */
+    if (find_data->cFileName[0] == L'.' &&
+        (find_data->cFileName[1] == L'\0' ||
+        (find_data->cFileName[1] == L'.' &&
+        find_data->cFileName[2] == L'\0'))) {
+      dir->need_find_call = TRUE;
+      continue;
+    }
+
+    r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
+                                  -1,
+                                  (char**) &dirents[dirent_idx].name);
+    if (r != 0)
+      goto error;
+
+    /* Copy file type. */
+    if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+      dent.d_type = UV__DT_DIR;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+      dent.d_type = UV__DT_LINK;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
+      dent.d_type = UV__DT_CHAR;
+    else
+      dent.d_type = UV__DT_FILE;
+
+    dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
+    dir->need_find_call = TRUE;
+    ++dirent_idx;
+  }
+
+  SET_REQ_RESULT(req, dirent_idx);
+  return;
+
+error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+void fs__closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = req->ptr;
+  FindClose(dir->dir_handle);
+  uv__free(req->ptr);
+  SET_REQ_RESULT(req, 0);
+}
 
 INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
     int do_lstat) {
@@ -2039,6 +2170,9 @@
     XX(MKDTEMP, mkdtemp)
     XX(RENAME, rename)
     XX(SCANDIR, scandir)
+    XX(READDIR, readdir)
+    XX(OPENDIR, opendir)
+    XX(CLOSEDIR, closedir)
     XX(LINK, link)
     XX(SYMLINK, symlink)
     XX(READLINK, readlink)
@@ -2080,6 +2214,8 @@
   if (req->flags & UV_FS_FREE_PTR) {
     if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
       uv__fs_scandir_cleanup(req);
+    else if (req->fs_type == UV_FS_READDIR)
+      uv__fs_readdir_cleanup(req);
     else
       uv__free(req->ptr);
   }
@@ -2247,6 +2383,45 @@
   POST;
 }
 
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPENDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err)
+    return uv_translate_sys_error(err);
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(UV_FS_READDIR);
+
+  if (dir == NULL ||
+      dir->dirents == NULL ||
+      dir->dir_handle == INVALID_HANDLE_VALUE) {
+    return UV_EINVAL;
+  }
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(UV_FS_CLOSEDIR);
+  if (dir == NULL)
+    return UV_EINVAL;
+  req->ptr = dir;
+  POST;
+}
 
 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
     const char* new_path, uv_fs_cb cb) {
diff --git a/Utilities/cmlibuv/src/win/handle.c b/Utilities/cmlibuv/src/win/handle.c
index 9d76c3f..61e4df6 100644
--- a/Utilities/cmlibuv/src/win/handle.c
+++ b/Utilities/cmlibuv/src/win/handle.c
@@ -139,7 +139,6 @@
     case UV_FS_POLL:
       uv__fs_poll_close((uv_fs_poll_t*) handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
       return;
 
     default:
diff --git a/Utilities/cmlibuv/src/win/internal.h b/Utilities/cmlibuv/src/win/internal.h
index 206ab5f..f7d8ccf 100644
--- a/Utilities/cmlibuv/src/win/internal.h
+++ b/Utilities/cmlibuv/src/win/internal.h
@@ -276,6 +276,14 @@
 int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
 int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);
 
+typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error);
+
 
 /*
  * Process stdio handles.
diff --git a/Utilities/cmlibuv/src/win/tcp.c b/Utilities/cmlibuv/src/win/tcp.c
index 3ce5548..f2cb527 100644
--- a/Utilities/cmlibuv/src/win/tcp.c
+++ b/Utilities/cmlibuv/src/win/tcp.c
@@ -809,44 +809,24 @@
 int uv_tcp_getsockname(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
 
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
-
-  if (handle->delayed_error) {
-    return uv_translate_sys_error(handle->delayed_error);
-  }
-
-  result = getsockname(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
-
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             handle->delayed_error);
 }
 
 
 int uv_tcp_getpeername(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
 
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
-
-  if (handle->delayed_error) {
-    return uv_translate_sys_error(handle->delayed_error);
-  }
-
-  result = getpeername(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
-
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             handle->delayed_error);
 }
 
 
diff --git a/Utilities/cmlibuv/src/win/thread.c b/Utilities/cmlibuv/src/win/thread.c
index fd4b7c9..89c53ad 100644
--- a/Utilities/cmlibuv/src/win/thread.c
+++ b/Utilities/cmlibuv/src/win/thread.c
@@ -112,9 +112,34 @@
 
 
 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
   struct thread_ctx* ctx;
   int err;
   HANDLE thread;
+  SYSTEM_INFO sysinfo;
+  size_t stack_size;
+  size_t pagesize;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+
+  if (stack_size != 0) {
+    GetNativeSystemInfo(&sysinfo);
+    pagesize = (size_t)sysinfo.dwPageSize;
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+
+    if ((unsigned)stack_size != stack_size)
+      return UV_EINVAL;
+  }
 
   ctx = uv__malloc(sizeof(*ctx));
   if (ctx == NULL)
@@ -126,7 +151,7 @@
   /* Create the thread in suspended state so we have a chance to pass
    * its own creation handle to it */
   thread = (HANDLE) _beginthreadex(NULL,
-                                   0,
+                                   (unsigned)stack_size,
                                    uv__thread_start,
                                    ctx,
                                    CREATE_SUSPENDED,
diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c
index f38e9a8..a98fe26 100644
--- a/Utilities/cmlibuv/src/win/tty.c
+++ b/Utilities/cmlibuv/src/win/tty.c
@@ -736,8 +736,8 @@
       /* Ignore keyup events, unless the left alt key was held and a valid
        * unicode character was emitted. */
       if (!KEV.bKeyDown &&
-          KEV.wVirtualKeyCode != VK_MENU &&
-          KEV.uChar.UnicodeChar != 0) {
+          (KEV.wVirtualKeyCode != VK_MENU ||
+           KEV.uChar.UnicodeChar == 0)) {
         continue;
       }
 
diff --git a/Utilities/cmlibuv/src/win/udp.c b/Utilities/cmlibuv/src/win/udp.c
index 37df849..8aeeab3 100644
--- a/Utilities/cmlibuv/src/win/udp.c
+++ b/Utilities/cmlibuv/src/win/udp.c
@@ -36,22 +36,27 @@
 
 /* A zero-size buffer for use by uv_udp_read */
 static char uv_zero_[] = "";
+int uv_udp_getpeername(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             0);
+}
+
 
 int uv_udp_getsockname(const uv_udp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
 
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
-
-  result = getsockname(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
-
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             0);
 }
 
 
@@ -784,6 +789,18 @@
 }
 
 
+int uv__udp_is_bound(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
 int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
   WSAPROTOCOL_INFOW protocol_info;
   int opt_len;
@@ -803,7 +820,16 @@
                           handle,
                           sock,
                           protocol_info.iAddressFamily);
-  return uv_translate_sys_error(err);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  if (uv__udp_is_bound(handle))
+    handle->flags |= UV_HANDLE_BOUND;
+
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
 }
 
 
@@ -880,6 +906,50 @@
 }
 
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = connect(handle->socket, addr, addrlen);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int err;
+    struct sockaddr addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    err = connect(handle->socket, &addr, sizeof(addr));
+    if (err)
+      return uv_translate_sys_error(err);
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
 /* This function is an egress point, i.e. it returns libuv errors rather than
  * system errors.
  */
@@ -900,6 +970,7 @@
       bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
     else
       return UV_EINVAL;
+
     err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return uv_translate_sys_error(err);
@@ -925,9 +996,11 @@
 
   assert(nbufs > 0);
 
-  err = uv__convert_to_localhost_if_unspecified(addr, &converted);
-  if (err)
-    return err;
+  if (addr != NULL) {
+    err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+    if (err)
+      return err;
+  }
 
   /* Already sending a message.*/
   if (handle->send_queue_count != 0)
diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c
index 9237891..7ca8321 100644
--- a/Utilities/cmlibuv/src/win/util.c
+++ b/Utilities/cmlibuv/src/win/util.c
@@ -59,13 +59,6 @@
 # define UNLEN 256
 #endif
 
-/*
-  Max hostname length. The Windows gethostname() documentation states that 256
-  bytes will always be large enough to hold the null-terminated hostname.
-*/
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 256
-#endif
 
 /* Maximum environment variable size, including the terminating null */
 #define MAX_ENV_VAR_LENGTH 32767
@@ -327,6 +320,11 @@
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 uv_pid_t uv_os_getpid(void) {
   return GetCurrentProcessId();
 }
@@ -684,12 +682,9 @@
                            NULL,
                            (BYTE*)&cpu_brand,
                            &cpu_brand_size);
-    if (err != ERROR_SUCCESS) {
-      RegCloseKey(processor_key);
-      goto error;
-    }
-
     RegCloseKey(processor_key);
+    if (err != ERROR_SUCCESS)
+      goto error;
 
     cpu_info = &cpu_infos[i];
     cpu_info->speed = cpu_speed;
@@ -713,9 +708,11 @@
   return 0;
 
  error:
-  /* This is safe because the cpu_infos array is zeroed on allocation. */
-  for (i = 0; i < cpu_count; i++)
-    uv__free(cpu_infos[i].model);
+  if (cpu_infos != NULL) {
+    /* This is safe because the cpu_infos array is zeroed on allocation. */
+    for (i = 0; i < cpu_count; i++)
+      uv__free(cpu_infos[i].model);
+  }
 
   uv__free(cpu_infos);
   uv__free(sppi);
@@ -1510,7 +1507,7 @@
 
 
 int uv_os_gethostname(char* buffer, size_t* size) {
-  char buf[MAXHOSTNAMELEN + 1];
+  char buf[UV_MAXHOSTNAMESIZE];
   size_t len;
 
   if (buffer == NULL || size == NULL || *size == 0)
@@ -1634,6 +1631,10 @@
      https://github.com/gagern/gnulib/blob/master/lib/uname.c */
   OSVERSIONINFOW os_info;
   SYSTEM_INFO system_info;
+  HKEY registry_key;
+  WCHAR product_name_w[256];
+  DWORD product_name_w_size;
+  int version_size;
   int processor_level;
   int r;
 
@@ -1658,16 +1659,56 @@
   }
 
   /* Populate the version field. */
-  if (WideCharToMultiByte(CP_UTF8,
-                          0,
-                          os_info.szCSDVersion,
-                          -1,
-                          buffer->version,
-                          sizeof(buffer->version),
-                          NULL,
-                          NULL) == 0) {
-    r = uv_translate_sys_error(GetLastError());
-    goto error;
+  version_size = 0;
+  r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                    0,
+                    KEY_QUERY_VALUE,
+                    &registry_key);
+
+  if (r == ERROR_SUCCESS) {
+    product_name_w_size = sizeof(product_name_w);
+    r = RegGetValueW(registry_key,
+                     NULL,
+                     L"ProductName",
+                     RRF_RT_REG_SZ,
+                     NULL,
+                     (PVOID) product_name_w,
+                     &product_name_w_size);
+    RegCloseKey(registry_key);
+
+    if (r == ERROR_SUCCESS) {
+      version_size = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         product_name_w,
+                                         -1,
+                                         buffer->version,
+                                         sizeof(buffer->version),
+                                         NULL,
+                                         NULL);
+      if (version_size == 0) {
+        r = uv_translate_sys_error(GetLastError());
+        goto error;
+      }
+    }
+  }
+
+  /* Append service pack information to the version if present. */
+  if (os_info.szCSDVersion[0] != L'\0') {
+    if (version_size > 0)
+      buffer->version[version_size - 1] = ' ';
+
+    if (WideCharToMultiByte(CP_UTF8,
+                            0,
+                            os_info.szCSDVersion,
+                            -1,
+                            buffer->version + version_size,
+                            sizeof(buffer->version) - version_size,
+                            NULL,
+                            NULL) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
   }
 
   /* Populate the sysname field. */
@@ -1744,3 +1785,20 @@
   buffer->machine[0] = '\0';
   return r;
 }
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
+  const uint64_t epoch = (uint64_t) 116444736000000000ULL;
+  FILETIME file_time;
+  ULARGE_INTEGER ularge;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  GetSystemTimeAsFileTime(&file_time);
+  ularge.LowPart = file_time.dwLowDateTime;
+  ularge.HighPart = file_time.dwHighDateTime;
+  tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
+  tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
+  return 0;
+}
diff --git a/Utilities/cmlibuv/src/win/winsock.c b/Utilities/cmlibuv/src/win/winsock.c
index 5e7da2a..5820ba9 100644
--- a/Utilities/cmlibuv/src/win/winsock.c
+++ b/Utilities/cmlibuv/src/win/winsock.c
@@ -87,12 +87,6 @@
   WSAPROTOCOL_INFOW protocol_info;
   int opt_len;
 
-  /* Initialize winsock */
-  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
-  if (errorno != 0) {
-    uv_fatal_error(errorno, "WSAStartup");
-  }
-
   /* Set implicit binding address used by connectEx */
   if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
     abort();
@@ -102,6 +96,15 @@
     abort();
   }
 
+  /* Skip initialization in safe mode without network support */
+  if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;
+
+  /* Initialize winsock */
+  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+  if (errorno != 0) {
+    uv_fatal_error(errorno, "WSAStartup");
+  }
+
   /* Detect non-IFS LSPs */
   dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 
diff --git a/Utilities/std/.gitattributes b/Utilities/std/.gitattributes
new file mode 100644
index 0000000..cd20549
--- /dev/null
+++ b/Utilities/std/.gitattributes
@@ -0,0 +1 @@
+cm/* our-c-style
diff --git a/Utilities/std/CMakeLists.txt b/Utilities/std/CMakeLists.txt
new file mode 100644
index 0000000..63c0a60
--- /dev/null
+++ b/Utilities/std/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+# source files for CMake std library
+set(SRCS cm/bits/string_view.cxx
+         cm/memory
+         cm/optional
+         cm/shared_mutex
+         cm/string_view
+         cm/utility)
+
+add_library(cmstd STATIC ${SRCS})
diff --git a/Utilities/std/cm/algorithm b/Utilities/std/cm/algorithm
new file mode 100644
index 0000000..8ade99c
--- /dev/null
+++ b/Utilities/std/cm/algorithm
@@ -0,0 +1,38 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_algorithm
+#define cm_algorithm
+
+#include <algorithm> // IWYU pragma: export
+#include <cassert>
+
+namespace cm {
+
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+
+using std::clamp;
+
+#else
+
+template <typename T>
+T const& clamp(T const& v, T const& lo, T const& hi)
+{
+  assert(!(hi < lo));
+  return (v < lo) ? lo : (hi < v) ? hi : v;
+}
+
+template <typename T, typename Comp>
+T const& clamp(T const& v, T const& lo, T const& hi, Comp comp)
+{
+  assert(!comp(hi, lo));
+  return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
+}
+
+#endif
+
+} // namespace cm
+
+#endif
diff --git a/Utilities/std/cm/bits/string_view.cxx b/Utilities/std/cm/bits/string_view.cxx
new file mode 100644
index 0000000..e345fd3
--- /dev/null
+++ b/Utilities/std/cm/bits/string_view.cxx
@@ -0,0 +1,301 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <cm/string_view> // IWYU pragma: associated
+
+#ifndef CMake_HAVE_CXX_STRING_VIEW
+
+#  include <algorithm>
+#  include <ostream>
+#  include <stdexcept>
+
+#  include "cm_kwiml.h"
+
+namespace cm {
+
+string_view::const_reference string_view::at(size_type pos) const
+{
+  if (pos >= size_) {
+    throw std::out_of_range("Index out of range in string_view::at");
+  }
+  return data_[pos];
+}
+
+string_view::size_type string_view::copy(char* dest, size_type count,
+                                         size_type pos) const
+{
+  if (pos > size_) {
+    throw std::out_of_range("Index out of range in string_view::copy");
+  }
+  size_type const rcount = std::min(count, size_ - pos);
+  traits_type::copy(dest, data_ + pos, rcount);
+  return rcount;
+}
+
+string_view string_view::substr(size_type pos, size_type count) const
+{
+  if (pos > size_) {
+    throw std::out_of_range("Index out of range in string_view::substr");
+  }
+  size_type const rcount = std::min(count, size_ - pos);
+  return string_view(data_ + pos, rcount);
+}
+
+int string_view::compare(string_view v) const noexcept
+{
+  size_type const rlen = std::min(size_, v.size_);
+  int c = traits_type::compare(data_, v.data_, rlen);
+  if (c == 0) {
+    if (size_ < v.size_) {
+      c = -1;
+    } else if (size_ > v.size_) {
+      c = 1;
+    }
+  }
+  return c;
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v) const
+{
+  return substr(pos1, count1).compare(v);
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v,
+                         size_type pos2, size_type count2) const
+{
+  return substr(pos1, count1).compare(v.substr(pos2, count2));
+}
+
+int string_view::compare(const char* s) const
+{
+  return compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s) const
+{
+  return substr(pos1, count1).compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s,
+                         size_type count2) const
+{
+  return substr(pos1, count1).compare(string_view(s, count2));
+}
+
+string_view::size_type string_view::find(string_view v, size_type pos) const
+  noexcept
+{
+  for (; pos + v.size_ <= size_; ++pos) {
+    if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == 0) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find(char c, size_type pos) const noexcept
+{
+  return find(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos,
+                                         size_type count) const
+{
+  return find(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos) const
+{
+  return find(string_view(s), pos);
+}
+
+string_view::size_type string_view::rfind(string_view v, size_type pos) const
+  noexcept
+{
+  if (size_ >= v.size_) {
+    for (pos = std::min(pos, size_ - v.size_) + 1; pos > 0;) {
+      --pos;
+      if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) ==
+          0) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::rfind(char c, size_type pos) const noexcept
+{
+  return rfind(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos,
+                                          size_type count) const
+{
+  return rfind(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos) const
+{
+  return rfind(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_of(string_view v,
+                                                  size_type pos) const noexcept
+{
+  for (; pos < size_; ++pos) {
+    if (traits_type::find(v.data_, v.size_, data_[pos])) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_first_of(char c, size_type pos) const
+  noexcept
+{
+  return find_first_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s, size_type pos,
+                                                  size_type count) const
+{
+  return find_first_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s,
+                                                  size_type pos) const
+{
+  return find_first_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_of(string_view v,
+                                                 size_type pos) const noexcept
+{
+  if (size_ > 0) {
+    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+      --pos;
+      if (traits_type::find(v.data_, v.size_, data_[pos])) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_last_of(char c, size_type pos) const
+  noexcept
+{
+  return find_last_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s, size_type pos,
+                                                 size_type count) const
+{
+  return find_last_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s,
+                                                 size_type pos) const
+{
+  return find_last_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(string_view v,
+                                                      size_type pos) const
+  noexcept
+{
+  for (; pos < size_; ++pos) {
+    if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_first_not_of(char c,
+                                                      size_type pos) const
+  noexcept
+{
+  return find_first_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+                                                      size_type pos,
+                                                      size_type count) const
+{
+  return find_first_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+                                                      size_type pos) const
+{
+  return find_first_not_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(string_view v,
+                                                     size_type pos) const
+  noexcept
+{
+  if (size_ > 0) {
+    for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+      --pos;
+      if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+        return pos;
+      }
+    }
+  }
+  return npos;
+}
+
+string_view::size_type string_view::find_last_not_of(char c,
+                                                     size_type pos) const
+  noexcept
+{
+  return find_last_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+                                                     size_type pos,
+                                                     size_type count) const
+{
+  return find_last_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+                                                     size_type pos) const
+{
+  return find_last_not_of(string_view(s), pos);
+}
+
+std::ostream& operator<<(std::ostream& o, string_view v)
+{
+  return o.write(v.data(), v.size());
+}
+
+std::string& operator+=(std::string& s, string_view v)
+{
+  s.append(v.data(), v.size());
+  return s;
+}
+}
+
+std::hash<cm::string_view>::result_type std::hash<cm::string_view>::operator()(
+  argument_type const& s) const noexcept
+{
+  // FNV-1a hash.
+  static KWIML_INT_uint64_t const fnv_offset_basis = 0xcbf29ce484222325;
+  static KWIML_INT_uint64_t const fnv_prime = 0x100000001b3;
+  KWIML_INT_uint64_t h = fnv_offset_basis;
+  for (char const& c : s) {
+    h = h ^ KWIML_INT_uint64_t(KWIML_INT_uint8_t(c));
+    h = h * fnv_prime;
+  }
+  return result_type(h);
+}
+#else
+// Avoid empty translation unit.
+void cm_string_view_cxx()
+{
+}
+#endif
diff --git a/Utilities/std/cm/iterator b/Utilities/std/cm/iterator
new file mode 100644
index 0000000..718f1d6
--- /dev/null
+++ b/Utilities/std/cm/iterator
@@ -0,0 +1,216 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_iterator
+#define cm_iterator
+
+#include <iterator> // IWYU pragma: export
+
+namespace cm {
+
+#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
+using std::make_reverse_iterator;
+
+using std::cbegin;
+using std::cend;
+
+using std::rbegin;
+using std::rend;
+using std::crbegin;
+using std::crend;
+#else
+template <class Iter>
+std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
+{
+  return std::reverse_iterator<Iter>(it);
+}
+
+// std::c{begin,end} backport from C++14
+template <class C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+auto cbegin(C const& c)
+#  else
+constexpr auto cbegin(C const& c) noexcept(noexcept(std::begin(c)))
+#  endif
+  -> decltype(std::begin(c))
+{
+  return std::begin(c);
+}
+
+template <class C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+auto cend(C const& c)
+#  else
+constexpr auto cend(C const& c) noexcept(noexcept(std::end(c)))
+#  endif
+  -> decltype(std::end(c))
+{
+  return std::end(c);
+}
+
+// std::r{begin,end} backport from C++14
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  rbegin(C& c) -> decltype(c.rbegin())
+{
+  return c.rbegin();
+}
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  rbegin(C const& c) -> decltype(c.rbegin())
+{
+  return c.rbegin();
+}
+template <typename T, size_t N>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  std::reverse_iterator<T*>
+    rbegin(T (&arr)[N])
+{
+  return std::reverse_iterator<T*>(arr + N);
+}
+
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  rend(C& c) -> decltype(c.rend())
+{
+  return c.rend();
+}
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  rend(C const& c) -> decltype(c.rend())
+{
+  return c.rend();
+}
+template <typename T, size_t N>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  std::reverse_iterator<T*>
+    rend(T (&arr)[N])
+{
+  return std::reverse_iterator<T*>(arr);
+}
+
+// std::cr{begin,end} backport from C++14
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  crbegin(C const& c) -> decltype(cm::rbegin(c))
+{
+  return cm::rbegin(c);
+}
+
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  crend(C const& c) -> decltype(cm::rend(c))
+{
+  return cm::rend(c);
+}
+#endif
+
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+using std::size;
+
+using std::empty;
+using std::data;
+#else
+
+// std::size backport from C++17.
+template <class C>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  auto
+  size(C const& c) -> decltype(c.size())
+{
+  return c.size();
+}
+
+template <typename T, size_t N>
+#  if !defined(_MSC_VER) || _MSC_VER >= 1900
+constexpr
+#  endif
+  std::size_t
+  size(const T (&)[N]) throw()
+{
+  return N;
+}
+
+// std::empty backport from C++17.
+template <class C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+auto empty(C const& c)
+#  else
+constexpr auto empty(C const& c) noexcept(noexcept(c.empty()))
+#  endif
+  -> decltype(c.empty())
+{
+  return c.empty();
+}
+template <typename T, size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+bool empty(const T (&)[N])
+#  else
+constexpr bool empty(const T (&)[N]) noexcept
+#  endif
+{
+  return false;
+}
+
+// std::data backport from C++17.
+template <class C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+auto data(C const& c)
+#  else
+constexpr auto data(C const& c) noexcept(noexcept(c.data()))
+#  endif
+  -> decltype(c.data())
+{
+  return c.data();
+}
+template <class C>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+auto data(C& c)
+#  else
+constexpr auto data(C& c) noexcept(noexcept(c.data()))
+#  endif
+  -> decltype(c.data())
+{
+  return c.data();
+}
+template <typename T, size_t N>
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+T* data(T (&)[N])
+#  else
+constexpr T* data(T (&arr)[N]) noexcept
+#  endif
+{
+  return arr;
+}
+
+#endif
+
+} // namespace cm
+
+#endif
diff --git a/Utilities/std/cm/memory b/Utilities/std/cm/memory
new file mode 100644
index 0000000..8ebded2
--- /dev/null
+++ b/Utilities/std/cm/memory
@@ -0,0 +1,32 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_memory
+#define cm_memory
+
+#include <memory> // IWYU pragma: export
+#if !defined(CMake_HAVE_CXX_MAKE_UNIQUE)
+#  include <utility>
+#endif
+
+namespace cm {
+
+#if defined(CMake_HAVE_CXX_MAKE_UNIQUE)
+
+using std::make_unique;
+
+#else
+
+template <typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args&&... args)
+{
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+#endif
+
+} // namespace cm
+
+#endif
diff --git a/Utilities/std/cm/optional b/Utilities/std/cm/optional
new file mode 100644
index 0000000..80b0951
--- /dev/null
+++ b/Utilities/std/cm/optional
@@ -0,0 +1,344 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_optional
+#define cm_optional
+
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#  define CMake_HAVE_CXX_OPTIONAL
+#endif
+
+#if defined(CMake_HAVE_CXX_OPTIONAL)
+#  include <optional> // IWYU pragma: export
+#else
+#  include <memory>
+
+#  include <cm/utility>
+#endif
+
+namespace cm {
+
+#if defined(CMake_HAVE_CXX_OPTIONAL)
+
+using std::nullopt_t;
+using std::nullopt;
+using std::optional;
+using std::bad_optional_access;
+using std::make_optional;
+
+#else
+
+class bad_optional_access : public std::exception
+{
+  using std::exception::exception;
+};
+
+struct nullopt_t
+{
+  explicit constexpr nullopt_t(int) {}
+};
+
+constexpr nullopt_t nullopt{ 0 };
+
+template <typename T>
+class optional
+{
+public:
+  using value_type = T;
+
+  optional() noexcept = default;
+  optional(nullopt_t) noexcept;
+  optional(const optional& other);
+  optional(optional&& other) noexcept;
+
+  template <typename... Args>
+  explicit optional(cm::in_place_t, Args&&... args);
+
+  template <
+    typename U = T,
+    typename = typename std::enable_if<
+      std::is_constructible<T, U&&>::value &&
+      !std::is_same<typename std::decay<U>::type, cm::in_place_t>::value &&
+      !std::is_same<typename std::decay<U>::type,
+                    cm::optional<T>>::value>::type>
+  optional(U&& v);
+
+  ~optional();
+
+  optional& operator=(nullopt_t) noexcept;
+  optional& operator=(const optional& other);
+  optional& operator=(optional&& other) noexcept;
+
+  template <
+    typename U = T,
+    typename = typename std::enable_if<
+      !std::is_same<typename std::decay<U>::type, cm::optional<T>>::value &&
+      std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value &&
+      (!std::is_scalar<T>::value ||
+       !std::is_same<typename std::decay<U>::type, T>::value)>::type>
+  optional& operator=(U&& v);
+
+  const T* operator->() const;
+  T* operator->();
+  const T& operator*() const&;
+  T& operator*() &;
+  const T&& operator*() const&&;
+  T&& operator*() &&;
+
+  explicit operator bool() const noexcept;
+  bool has_value() const noexcept;
+
+  T& value() &;
+  const T& value() const&;
+
+  T&& value() &&;
+  const T&& value() const&&;
+
+  template <typename U>
+  T value_or(U&& default_value) const&;
+
+  template <typename U>
+  T value_or(U&& default_value) &&;
+
+  void swap(optional& other) noexcept;
+  void reset() noexcept;
+
+  template <typename... Args>
+  T& emplace(Args&&... args);
+
+private:
+  bool _has_value = false;
+  std::allocator<T> _allocator;
+  union _mem_union
+  {
+    T value;
+
+    // Explicit constructor and destructor is required to make this work
+    _mem_union() noexcept {}
+    ~_mem_union() noexcept {}
+  } _mem;
+};
+
+template <typename T>
+optional<typename std::decay<T>::type> make_optional(T&& value)
+{
+  return optional<typename std::decay<T>::type>(std::forward<T>(value));
+}
+
+template <typename T, class... Args>
+optional<T> make_optional(Args&&... args)
+{
+  return optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+template <typename T>
+optional<T>::optional(nullopt_t) noexcept
+{
+}
+
+template <typename T>
+optional<T>::optional(const optional& other)
+{
+  *this = other;
+}
+
+template <typename T>
+optional<T>::optional(optional&& other) noexcept
+{
+  *this = std::move(other);
+}
+
+template <typename T>
+template <typename... Args>
+optional<T>::optional(cm::in_place_t, Args&&... args)
+{
+  this->emplace(std::forward<Args>(args)...);
+}
+
+template <typename T>
+template <typename U, typename>
+optional<T>::optional(U&& v)
+{
+  this->emplace(std::forward<U>(v));
+}
+
+template <typename T>
+optional<T>::~optional()
+{
+  this->reset();
+}
+
+template <typename T>
+optional<T>& optional<T>::operator=(nullopt_t) noexcept
+{
+  this->reset();
+  return *this;
+}
+
+template <typename T>
+optional<T>& optional<T>::operator=(const optional& other)
+{
+  if (other.has_value()) {
+    if (this->has_value()) {
+      this->value() = *other;
+    } else {
+      this->emplace(*other);
+    }
+  } else {
+    this->reset();
+  }
+  return *this;
+}
+
+template <typename T>
+optional<T>& optional<T>::operator=(optional&& other) noexcept
+{
+  if (other.has_value()) {
+    if (this->has_value()) {
+      this->value() = std::move(*other);
+    } else {
+      this->emplace(std::move(*other));
+    }
+  } else {
+    this->reset();
+  }
+  return *this;
+}
+
+template <typename T>
+template <typename U, typename>
+optional<T>& optional<T>::operator=(U&& v)
+{
+  if (this->has_value()) {
+    this->value() = v;
+  } else {
+    this->emplace(std::forward<U>(v));
+  }
+  return *this;
+}
+
+template <typename T>
+const T* optional<T>::operator->() const
+{
+  return &**this;
+}
+
+template <typename T>
+T* optional<T>::operator->()
+{
+  return &**this;
+}
+
+template <typename T>
+const T& optional<T>::operator*() const&
+{
+  return this->_mem.value;
+}
+
+template <typename T>
+T& optional<T>::operator*() &
+{
+  return this->_mem.value;
+}
+
+template <typename T>
+const T&& optional<T>::operator*() const&&
+{
+  return std::move(**this);
+}
+
+template <typename T>
+T&& optional<T>::operator*() &&
+{
+  return std::move(**this);
+}
+
+template <typename T>
+bool optional<T>::has_value() const noexcept
+{
+  return this->_has_value;
+}
+
+template <typename T>
+optional<T>::operator bool() const noexcept
+{
+  return this->has_value();
+}
+
+template <typename T>
+T& optional<T>::value() &
+{
+  if (!this->has_value()) {
+    throw cm::bad_optional_access{};
+  }
+  return **this;
+}
+
+template <typename T>
+const T& optional<T>::value() const&
+{
+  if (!this->has_value()) {
+    throw cm::bad_optional_access{};
+  }
+  return **this;
+}
+
+template <typename T>
+template <typename U>
+T optional<T>::value_or(U&& default_value) const&
+{
+  return bool(*this) ? **this : static_cast<T>(std::forward<U>(default_value));
+}
+
+template <typename T>
+template <typename U>
+T optional<T>::value_or(U&& default_value) &&
+{
+  return bool(*this) ? std::move(**this)
+                     : static_cast<T>(std::forward<U>(default_value));
+}
+
+template <typename T>
+void optional<T>::swap(optional& other) noexcept
+{
+  if (this->has_value()) {
+    if (other.has_value()) {
+      using std::swap;
+      swap(**this, *other);
+    } else {
+      other.emplace(std::move(**this));
+      this->reset();
+    }
+  } else if (other.has_value()) {
+    this->emplace(std::move(*other));
+    other.reset();
+  }
+}
+
+template <typename T>
+void optional<T>::reset() noexcept
+{
+  if (this->has_value()) {
+    this->_has_value = false;
+    std::allocator_traits<std::allocator<T>>::destroy(this->_allocator,
+                                                      &**this);
+  }
+}
+
+template <typename T>
+template <typename... Args>
+T& optional<T>::emplace(Args&&... args)
+{
+  this->reset();
+  std::allocator_traits<std::allocator<T>>::construct(
+    this->_allocator, &**this, std::forward<Args>(args)...);
+  this->_has_value = true;
+  return this->value();
+}
+
+#endif
+}
+
+#endif
diff --git a/Utilities/std/cm/shared_mutex b/Utilities/std/cm/shared_mutex
new file mode 100644
index 0000000..2ac9447
--- /dev/null
+++ b/Utilities/std/cm/shared_mutex
@@ -0,0 +1,76 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_shared_mutex
+#define cm_shared_mutex
+
+#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
+#  define CMake_HAVE_CXX_SHARED_LOCK
+#endif
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+#  define CMake_HAVE_CXX_SHARED_MUTEX
+#endif
+
+#if defined(CMake_HAVE_CXX_SHARED_LOCK)
+#  include <shared_mutex> // IWYU pragma: export
+#endif
+#if !defined(CMake_HAVE_CXX_SHARED_MUTEX)
+#  include "cm_uv.h"
+#endif
+
+namespace cm {
+#if defined(CMake_HAVE_CXX_SHARED_MUTEX)
+using std::shared_mutex;
+#else
+class shared_mutex
+{
+  uv_rwlock_t _M_;
+
+public:
+  using native_handle_type = uv_rwlock_t*;
+
+  shared_mutex() { uv_rwlock_init(&_M_); }
+  ~shared_mutex() { uv_rwlock_destroy(&_M_); }
+
+  shared_mutex(shared_mutex const&) = delete;
+  shared_mutex& operator=(shared_mutex const&) = delete;
+
+  void lock() { uv_rwlock_wrlock(&_M_); }
+  bool try_lock() { return uv_rwlock_trywrlock(&_M_) == 0; }
+  void unlock() { uv_rwlock_wrunlock(&_M_); }
+
+  void lock_shared() { uv_rwlock_rdlock(&_M_); }
+  void unlock_shared() { uv_rwlock_rdunlock(&_M_); }
+
+  native_handle_type native_handle() { return &_M_; }
+};
+#endif
+
+#if defined(CMake_HAVE_CXX_SHARED_LOCK)
+using std::shared_lock;
+#else
+template <typename T>
+class shared_lock
+{
+  T& _mutex;
+
+public:
+  using mutex_type = T;
+
+  shared_lock(T& m)
+    : _mutex(m)
+  {
+    _mutex.lock_shared();
+  }
+
+  ~shared_lock() { _mutex.unlock_shared(); }
+
+  shared_lock(shared_lock const&) = delete;
+  shared_lock& operator=(shared_lock const&) = delete;
+};
+#endif
+}
+
+#endif
diff --git a/Utilities/std/cm/string_view b/Utilities/std/cm/string_view
new file mode 100644
index 0000000..4d359cb
--- /dev/null
+++ b/Utilities/std/cm/string_view
@@ -0,0 +1,218 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_string_view
+#define cm_string_view
+
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+#  define CMake_HAVE_CXX_STRING_VIEW
+#endif
+
+#ifdef CMake_HAVE_CXX_STRING_VIEW
+#  include <string_view> // IWYU pragma: export
+namespace cm {
+using std::string_view;
+}
+#else
+#  include <cstddef>
+#  include <functional>
+#  include <iosfwd>
+#  include <iterator>
+#  include <string>
+
+namespace cm {
+
+class string_view
+{
+public:
+  using traits_type = std::string::traits_type;
+  using value_type = char;
+  using pointer = char*;
+  using const_pointer = const char*;
+  using reference = char&;
+  using const_reference = char const&;
+  using const_iterator = const char*;
+  using iterator = const_iterator;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+  using reverse_iterator = const_reverse_iterator;
+  using size_type = std::string::size_type;
+  using difference_type = std::string::difference_type;
+
+  static size_type const npos = static_cast<size_type>(-1);
+
+  string_view() noexcept = default;
+  string_view(string_view const&) noexcept = default;
+
+  string_view(const char* s, size_t count) noexcept
+    : data_(s)
+    , size_(count)
+  {
+  }
+
+  string_view(const char* s) noexcept
+    : data_(s)
+    , size_(traits_type::length(s))
+  {
+  }
+
+  // C++17 does not define this constructor.  Instead it defines
+  // a conversion operator on std::string to create a string_view.
+  // Since this implementation is used in C++11, std::string does
+  // not have that conversion.
+  string_view(std::string const& s) noexcept
+    : data_(s.data())
+    , size_(s.size())
+  {
+  }
+
+  // C++17 does not define this conversion.  Instead it defines
+  // a constructor on std::string that can take a string_view.
+  // Since this implementation is used in C++11, std::string does
+  // not have that constructor.
+  explicit operator std::string() const { return std::string(data_, size_); }
+
+  string_view& operator=(string_view const&) = default;
+
+  const_iterator begin() const noexcept { return data_; }
+  const_iterator end() const noexcept { return data_ + size_; }
+  const_iterator cbegin() const noexcept { return begin(); }
+  const_iterator cend() const noexcept { return end(); }
+
+  const_reverse_iterator rbegin() const noexcept
+  {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator rend() const noexcept
+  {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+  const_reverse_iterator crend() const noexcept { return rend(); }
+
+  const_reference operator[](size_type pos) const noexcept
+  {
+    return data_[pos];
+  }
+  const_reference at(size_type pos) const;
+  const_reference front() const noexcept { return data_[0]; }
+  const_reference back() const noexcept { return data_[size_ - 1]; }
+  const_pointer data() const noexcept { return data_; }
+
+  size_type size() const noexcept { return size_; }
+  size_type length() const noexcept { return size_; }
+  size_type max_size() const noexcept { return npos - 1; }
+  bool empty() const noexcept { return size_ == 0; }
+
+  void remove_prefix(size_type n) noexcept
+  {
+    data_ += n;
+    size_ -= n;
+  }
+  void remove_suffix(size_type n) noexcept { size_ -= n; }
+  void swap(string_view& v) noexcept
+  {
+    string_view tmp = v;
+    v = *this;
+    *this = tmp;
+  }
+
+  size_type copy(char* dest, size_type count, size_type pos = 0) const;
+  string_view substr(size_type pos = 0, size_type count = npos) const;
+
+  int compare(string_view v) const noexcept;
+  int compare(size_type pos1, size_type count1, string_view v) const;
+  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
+              size_type count2) const;
+  int compare(const char* s) const;
+  int compare(size_type pos1, size_type count1, const char* s) const;
+  int compare(size_type pos1, size_type count1, const char* s,
+              size_type count2) const;
+
+  size_type find(string_view v, size_type pos = 0) const noexcept;
+  size_type find(char c, size_type pos = 0) const noexcept;
+  size_type find(const char* s, size_type pos, size_type count) const;
+  size_type find(const char* s, size_type pos = 0) const;
+
+  size_type rfind(string_view v, size_type pos = npos) const noexcept;
+  size_type rfind(char c, size_type pos = npos) const noexcept;
+  size_type rfind(const char* s, size_type pos, size_type count) const;
+  size_type rfind(const char* s, size_type pos = npos) const;
+
+  size_type find_first_of(string_view v, size_type pos = 0) const noexcept;
+  size_type find_first_of(char c, size_type pos = 0) const noexcept;
+  size_type find_first_of(const char* s, size_type pos, size_type count) const;
+  size_type find_first_of(const char* s, size_type pos = 0) const;
+
+  size_type find_last_of(string_view v, size_type pos = npos) const noexcept;
+  size_type find_last_of(char c, size_type pos = npos) const noexcept;
+  size_type find_last_of(const char* s, size_type pos, size_type count) const;
+  size_type find_last_of(const char* s, size_type pos = npos) const;
+
+  size_type find_first_not_of(string_view v, size_type pos = 0) const noexcept;
+  size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
+  size_type find_first_not_of(const char* s, size_type pos,
+                              size_type count) const;
+  size_type find_first_not_of(const char* s, size_type pos = 0) const;
+
+  size_type find_last_not_of(string_view v, size_type pos = npos) const
+    noexcept;
+  size_type find_last_not_of(char c, size_type pos = npos) const noexcept;
+  size_type find_last_not_of(const char* s, size_type pos,
+                             size_type count) const;
+  size_type find_last_not_of(const char* s, size_type pos = npos) const;
+
+private:
+  const char* data_ = nullptr;
+  size_type size_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& o, string_view v);
+
+std::string& operator+=(std::string& s, string_view v);
+
+inline bool operator==(string_view l, string_view r) noexcept
+{
+  return l.compare(r) == 0;
+}
+
+inline bool operator!=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) != 0;
+}
+
+inline bool operator<(string_view l, string_view r) noexcept
+{
+  return l.compare(r) < 0;
+}
+
+inline bool operator<=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) <= 0;
+}
+
+inline bool operator>(string_view l, string_view r) noexcept
+{
+  return l.compare(r) > 0;
+}
+
+inline bool operator>=(string_view l, string_view r) noexcept
+{
+  return l.compare(r) >= 0;
+}
+}
+
+namespace std {
+
+template <>
+struct hash<cm::string_view>
+{
+  using argument_type = cm::string_view;
+  using result_type = size_t;
+  result_type operator()(argument_type const& s) const noexcept;
+};
+}
+
+#endif
+#endif
diff --git a/Utilities/std/cm/utility b/Utilities/std/cm/utility
new file mode 100644
index 0000000..3acac4f
--- /dev/null
+++ b/Utilities/std/cm/utility
@@ -0,0 +1,34 @@
+// -*-c++-*-
+// vim: set ft=cpp:
+
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cm_utility
+#define cm_utility
+
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#  define CMake_HAVE_CXX_IN_PLACE
+#endif
+
+#include <utility> // IWYU pragma: export
+
+namespace cm {
+
+#if defined(CMake_HAVE_CXX_IN_PLACE)
+
+using std::in_place_t;
+using std::in_place;
+
+#else
+
+struct in_place_t
+{
+  explicit in_place_t() = default;
+};
+
+constexpr in_place_t in_place{};
+
+#endif
+}
+
+#endif
diff --git a/bootstrap b/bootstrap
index 38fa32b..1f5f066 100755
--- a/bootstrap
+++ b/bootstrap
@@ -261,12 +261,24 @@
   cmAddSubDirectoryCommand \
   cmAddTestCommand \
   cmArgumentParser \
+  cmBinUtilsLinker \
+  cmBinUtilsLinuxELFGetRuntimeDependenciesTool \
+  cmBinUtilsLinuxELFLinker \
+  cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOGetRuntimeDependenciesTool \
+  cmBinUtilsMacOSMachOLinker \
+  cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \
+  cmBinUtilsWindowsPELinker \
+  cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \
   cmBreakCommand \
   cmBuildCommand \
   cmCMakeMinimumRequired \
   cmCMakePolicyCommand \
   cmCPackPropertiesGenerator \
   cmCacheManager \
+  cmCheckCustomOutputs \
   cmCommand \
   cmCommandArgumentParserHelper \
   cmCommands \
@@ -286,7 +298,6 @@
   cmDefinitions \
   cmDepends \
   cmDependsC \
-  cmDisallowedCommand \
   cmDocumentationFormatter \
   cmEnableLanguageCommand \
   cmEnableTestingCommand \
@@ -297,7 +308,6 @@
   cmExportFileGenerator \
   cmExportInstallFileGenerator \
   cmExportSet \
-  cmExportSetMap \
   cmExportTryCompileFileGenerator \
   cmExprParserHelper \
   cmExternalMakefileProjectGenerator \
@@ -315,6 +325,7 @@
   cmFindPathCommand \
   cmFindProgramCommand \
   cmForEachCommand \
+  cmFunctionBlocker \
   cmFunctionCommand \
   cmFSPermissions \
   cmGeneratedFileStream \
@@ -357,6 +368,8 @@
   cmInstallTargetGenerator \
   cmInstallTargetsCommand \
   cmInstalledFile \
+  cmLDConfigLDConfigTool \
+  cmLDConfigTool \
   cmLinkDirectoriesCommand \
   cmLinkItem \
   cmLinkLineComputer \
@@ -388,12 +401,12 @@
   cmPolicies \
   cmProcessOutput \
   cmProjectCommand \
-  cmProperty \
   cmPropertyDefinition \
   cmPropertyDefinitionMap \
   cmPropertyMap \
   cmReturnCommand \
   cmRulePlaceholderExpander \
+  cmRuntimeDependencyArchive \
   cmScriptGenerator \
   cmSearchPath \
   cmSeparateArgumentsCommand \
@@ -409,8 +422,11 @@
   cmState \
   cmStateDirectory \
   cmStateSnapshot \
+  cmString \
+  cmStringAlgorithms \
   cmStringReplaceHelper \
   cmStringCommand \
+  cmSubcommandTable \
   cmSubdirCommand \
   cmSystemTools \
   cmTarget \
@@ -419,6 +435,7 @@
   cmTargetCompileOptionsCommand \
   cmTargetIncludeDirectoriesCommand \
   cmTargetLinkLibrariesCommand \
+  cmTargetPrecompileHeadersCommand \
   cmTargetPropCommandBase \
   cmTargetPropertyComputer \
   cmTargetSourcesCommand \
@@ -427,7 +444,6 @@
   cmTimestamp \
   cmTryCompileCommand \
   cmTryRunCommand \
-  cmUnexpectedCommand \
   cmUnsetCommand \
   cmUVHandlePtr \
   cmUVProcessChain \
@@ -437,15 +453,27 @@
   cmake  \
   cmakemain \
   cmcmd  \
-  cm_string_view \
 "
 
 if ${cmake_system_mingw}; then
   CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\
     cmGlobalMSYSMakefileGenerator \
-    cmGlobalMinGWMakefileGenerator"
+    cmGlobalMinGWMakefileGenerator \
+    cmVSSetupHelper \
+  "
 fi
 
+CMAKE_STD_CXX_HEADERS="\
+  memory \
+  optional \
+  shared_mutex \
+  string_view \
+  utility \
+"
+CMAKE_STD_CXX_SOURCES="\
+  string_view \
+"
+
 LexerParser_CXX_SOURCES="\
   cmCommandArgumentLexer \
   cmCommandArgumentParser \
@@ -1042,6 +1070,10 @@
 #error "On Solaris we need C99."
 #endif
 
+#if defined(__hpux) && !(defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 409)
+#error "On HP-UX we need GCC 4.9 or higher."
+#endif
+
 #include <stdio.h>
 
 int main(int argc, char* argv[])
@@ -1110,6 +1142,43 @@
 #error "SunPro <= 5.13 mode not supported due to bug in move semantics."
 #endif
 
+#if defined(__hpux) && !(defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 409)
+#error "On HP-UX we need GCC 4.9 or higher."
+#endif
+
+#if __cplusplus > 201103L
+#include <iterator>
+int check_cxx14()
+{
+  int a[] = { 0, 1, 2 };
+  auto ai = std::cbegin(a);
+
+  int b[] = { 2, 1, 0 };
+  auto bi = std::cend(b);
+
+  return *ai + *(bi - 1);
+}
+#else
+int check_cxx14()
+{
+  return 0;
+}
+#endif
+
+#if __cplusplus >= 201703L
+#include <optional>
+int check_cxx17()
+{
+  std::optional<int> oi = 0;
+  return oi.value();
+}
+#else
+int check_cxx17()
+{
+  return 0;
+}
+#endif
+
 class Class
 {
 public:
@@ -1120,7 +1189,7 @@
 int main()
 {
   auto const c = std::unique_ptr<Class>(new Class);
-  std::cout << c->Get() << std::endl;
+  std::cout << c->Get() << check_cxx14() << check_cxx17() << std::endl;
   return 0;
 }
 ' > "${TMPFILE}.cxx"
@@ -1295,6 +1364,8 @@
  *
  * Sources:
  * ${CMAKE_CXX_SOURCES} ${CMAKE_C_SOURCES}
+ * STD Sources:
+ * ${CMAKE_STD_CXX_HEADERS} ${CMAKE_STD_CXX_SOURCES}
  * LexerParser Sources:
  * ${LexerParser_CXX_SOURCES} ${LexerParser_C_SOURCES}
  * kwSys Sources:
@@ -1326,9 +1397,14 @@
 cmake_report cmConfigure.h${_tmp} "#define CMake_DEFAULT_RECURSION_LIMIT 400"
 cmake_report cmConfigure.h${_tmp} "#define CMAKE_BIN_DIR \"/bootstrap-not-insalled\""
 cmake_report cmConfigure.h${_tmp} "#define CMAKE_DATA_DIR \"/bootstrap-not-insalled\""
-cmake_report cmConfigure.h${_tmp} "#define CMAKE_BOOTSTRAP"
 cmake_report cmConfigure.h${_tmp} "#define CM_FALLTHROUGH"
 
+if ${cmake_system_mingw}; then
+  cmake_report cmConfigure.h${_tmp} "#if defined(_WIN32) && !defined(NOMINMAX)"
+  cmake_report cmConfigure.h${_tmp} "#  define NOMINMAX"
+  cmake_report cmConfigure.h${_tmp} "#endif"
+fi
+
 # Regenerate configured headers
 for h in Configure VersionConfig; do
   if "${_diff}" cm${h}.h cm${h}.h${_tmp} > /dev/null 2> /dev/null; then
@@ -1356,9 +1432,12 @@
 cmake_generate_file "${cmake_bootstrap_dir}/cmThirdParty.h" ""
 
 # Generate Makefile
-dep="cmConfigure.h cmsys/*.hxx cmsys/*.h `cmake_escape \"${cmake_source_dir}\"`/Source/*.h"
+dep="cmConfigure.h cmsys/*.hxx cmsys/*.h `cmake_escape \"${cmake_source_dir}\"`/Source/*.hxx `cmake_escape \"${cmake_source_dir}\"`/Source/*.h"
+for h in ${CMAKE_STD_CXX_HEADERS}; do
+  dep="${dep} `cmake_escape \"${cmake_source_dir}\"`/Utilities/std/cm/${h}"
+done
 objs=""
-for a in ${CMAKE_CXX_SOURCES} ${CMAKE_C_SOURCES} ${LexerParser_CXX_SOURCES} ${LexerParser_C_SOURCES} ${KWSYS_CXX_SOURCES} ${KWSYS_C_SOURCES}; do
+for a in ${CMAKE_CXX_SOURCES} ${CMAKE_C_SOURCES} ${CMAKE_STD_CXX_SOURCES} ${LexerParser_CXX_SOURCES} ${LexerParser_C_SOURCES} ${KWSYS_CXX_SOURCES} ${KWSYS_C_SOURCES}; do
   objs="${objs} ${a}.o"
 done
 for a in ${LIBUV_C_SOURCES}; do
@@ -1370,9 +1449,8 @@
 uv_c_flags=""
 if ${cmake_system_mingw}; then
   uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600"
-  libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv"
+  libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32"
 else
-  uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP"
   case "${cmake_system}" in
     *AIX*)
       uv_c_flags="${uv_c_flags} -D_ALL_SOURCE -D_XOPEN_SOURCE=500 -D_LINUX_SOURCE_COMPAT"
@@ -1381,6 +1459,9 @@
     *Darwin*)
       uv_c_flags="${uv_c_flags} -D_DARWIN_USE_64_BIT_INODE=1 -D_DARWIN_UNLIMITED_SELECT=1"
       ;;
+    *HP-UX*)
+      uv_c_flags="${uv_c_flags} -D_XOPEN_SOURCE_EXTENDED"
+      ;;
     *Linux*)
       uv_c_flags="${uv_c_flags} -D_GNU_SOURCE"
       libs="${libs} -ldl -lrt"
@@ -1433,14 +1514,17 @@
   -DKWSYS_CXX_HAS_UTIMES=${KWSYS_CXX_HAS_UTIMES}
 "
 cmake_c_flags="${cmake_c_flags} \
+  -DCMAKE_BOOTSTRAP \
   -I`cmake_escape \"${cmake_bootstrap_dir}\"` \
   -I`cmake_escape \"${cmake_source_dir}/Source\"` \
   -I`cmake_escape \"${cmake_source_dir}/Source/LexerParser\"` \
   -I`cmake_escape \"${cmake_source_dir}/Utilities\"`"
 cmake_cxx_flags="${cmake_cxx_flags} \
+  -DCMAKE_BOOTSTRAP \
   -I`cmake_escape \"${cmake_bootstrap_dir}\"` \
   -I`cmake_escape \"${cmake_source_dir}/Source\"` \
   -I`cmake_escape \"${cmake_source_dir}/Source/LexerParser\"` \
+  -I`cmake_escape \"${cmake_source_dir}/Utilities/std\"` \
   -I`cmake_escape \"${cmake_source_dir}/Utilities\"`"
 echo "cmake: ${objs}" > "${cmake_bootstrap_dir}/Makefile"
 echo "	${cmake_cxx_compiler} ${cmake_ld_flags} ${cmake_cxx_flags} ${objs} ${libs} -o cmake" >> "${cmake_bootstrap_dir}/Makefile"
@@ -1455,6 +1539,12 @@
   echo "${a}.o : ${src} ${dep}" >> "${cmake_bootstrap_dir}/Makefile"
   echo "	${cmake_c_compiler} ${cmake_c_flags} -c ${src} -o ${a}.o" >> "${cmake_bootstrap_dir}/Makefile"
 done
+for a in ${CMAKE_STD_CXX_SOURCES}; do
+  src=`cmake_escape "${cmake_source_dir}/Utilities/std/cm/bits/${a}.cxx"`
+  src_flags=`eval echo \\${cmake_cxx_flags_\${a}}`
+  echo "${a}.o : ${src} ${dep}" >> "${cmake_bootstrap_dir}/Makefile"
+  echo "	${cmake_cxx_compiler} ${cmake_cxx_flags} ${src_flags} -c ${src} -o ${a}.o" >> "${cmake_bootstrap_dir}/Makefile"
+done
 for a in ${LexerParser_CXX_SOURCES}; do
   src=`cmake_escape "${cmake_source_dir}/Source/LexerParser/${a}.cxx"`
   src_flags=`eval echo \\${cmake_cxx_flags_\${a}}`