Merge topic 'fileapi-toolchains'

6418dabb87 Tests: Add test for toolchains-v1 File API object
1c5bd1bed5 Tests: Add toolchains kind to capabilities test
f72bb2ee0d Help: Add documentation for "toolchains" object kind
bb069c0857 cmFileAPI: Add "toolchains" object kind.

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !5678
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 073630e..680d77b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -284,6 +284,29 @@
     variables:
         RSYNC_DESTINATION: dev
 
+build:macos10.10-package:
+    extends:
+        - .macos10.10_package
+        - .cmake_build_macos_package
+        - .cmake_release_artifacts
+        - .macos_builder_tags_package
+        - .run_only_for_package
+    dependencies:
+        - prep:doc-package
+    needs:
+        - prep:doc-package
+
+upload:macos10.10-package:
+    extends:
+        - .rsync_upload
+        - .run_only_for_package
+    dependencies:
+        - build:macos10.10-package
+    needs:
+        - build:macos10.10-package
+    variables:
+        RSYNC_DESTINATION: dev
+
 # Windows builds
 
 build:windows-vs2019-x64-ninja:
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 76ffd27..87828e1 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -75,7 +75,7 @@
             # Any packages made.
             - build/cmake-*-Linux-x86_64.*
             - build/cmake-*-Linux-aarch64.*
-            - build/cmake-*-macos-universal.*
+            - build/cmake-*-macos*-universal.*
             # Any source packages made.
             - build/cmake-*.tar.gz
             - build/cmake-*.zip
diff --git a/.gitlab/ci/configure_macos10.10_package.cmake b/.gitlab/ci/configure_macos10.10_package.cmake
new file mode 100644
index 0000000..f01e6c8
--- /dev/null
+++ b/.gitlab/ci/configure_macos10.10_package.cmake
@@ -0,0 +1,4 @@
+set(CPACK_SYSTEM_NAME "macos10.10-universal" CACHE STRING "")
+set(CMAKE_OSX_DEPLOYMENT_TARGET "10.10" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_package_common.cmake")
diff --git a/.gitlab/ci/configure_macos_package.cmake b/.gitlab/ci/configure_macos_package.cmake
index a1dbdb9..380e44c 100644
--- a/.gitlab/ci/configure_macos_package.cmake
+++ b/.gitlab/ci/configure_macos_package.cmake
@@ -1,28 +1,4 @@
-set(CMake_DOC_ARTIFACT_PREFIX "$ENV{CI_PROJECT_DIR}/build/install-doc" CACHE PATH "")
-
-# Set up install destinations as expected by the packaging scripts.
-set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "")
-set(CMAKE_DOC_DIR "doc/cmake" CACHE STRING "")
-
-# Settings for CMake packages for macOS.
-set(CPACK_DMG_FORMAT "UDBZ" CACHE STRING "")
-set(CMAKE_CXX_FLAGS "-stdlib=libc++" CACHE STRING "")
-set(CMAKE_C_STANDARD "11" CACHE STRING "")
-set(CMAKE_CXX_STANDARD "14" CACHE STRING "")
-set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
-set(CMAKE_OSX_DEPLOYMENT_TARGET "10.10" CACHE STRING "")
-set(CMAKE_SKIP_BOOTSTRAP_TEST "TRUE" CACHE STRING "")
 set(CPACK_SYSTEM_NAME "macos-universal" CACHE STRING "")
-set(BUILD_CursesDialog "ON" CACHE BOOL "")
-set(BUILD_QtDialog "TRUE" CACHE BOOL "")
-set(CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL "3" CACHE STRING "")
-set(CMake_INSTALL_DEPENDENCIES "ON" CACHE BOOL "")
-set(CMAKE_SKIP_RPATH "TRUE" CACHE BOOL "")
-set(CMake_TEST_NO_FindPackageModeMakefileTest "TRUE" CACHE BOOL "")
+set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "")
 
-# XXX(sccache): restore sccache when it works for multiple architectures:
-# https://github.com/mozilla/sccache/issues/847
-set(configure_no_sccache 1)
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
-include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_package_common.cmake")
diff --git a/.gitlab/ci/configure_macos_package_common.cmake b/.gitlab/ci/configure_macos_package_common.cmake
new file mode 100644
index 0000000..3aa8ae2
--- /dev/null
+++ b/.gitlab/ci/configure_macos_package_common.cmake
@@ -0,0 +1,26 @@
+set(CMake_DOC_ARTIFACT_PREFIX "$ENV{CI_PROJECT_DIR}/build/install-doc" CACHE PATH "")
+
+# Set up install destinations as expected by the packaging scripts.
+set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "")
+set(CMAKE_DOC_DIR "doc/cmake" CACHE STRING "")
+
+# Settings for CMake packages for macOS.
+set(CPACK_DMG_FORMAT "UDBZ" CACHE STRING "")
+set(CMAKE_CXX_FLAGS "-stdlib=libc++" CACHE STRING "")
+set(CMAKE_C_STANDARD "11" CACHE STRING "")
+set(CMAKE_CXX_STANDARD "14" CACHE STRING "")
+set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
+set(CMAKE_SKIP_BOOTSTRAP_TEST "TRUE" CACHE STRING "")
+set(BUILD_CursesDialog "ON" CACHE BOOL "")
+set(BUILD_QtDialog "TRUE" CACHE BOOL "")
+set(CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL "3" CACHE STRING "")
+set(CMake_INSTALL_DEPENDENCIES "ON" CACHE BOOL "")
+set(CMAKE_SKIP_RPATH "TRUE" CACHE BOOL "")
+set(CMake_TEST_NO_FindPackageModeMakefileTest "TRUE" CACHE BOOL "")
+
+# XXX(sccache): restore sccache when it works for multiple architectures:
+# https://github.com/mozilla/sccache/issues/847
+set(configure_no_sccache 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake
index 4a33f12..76c693d 100644
--- a/.gitlab/ci/download_qt.cmake
+++ b/.gitlab/ci/download_qt.cmake
@@ -48,7 +48,11 @@
 
   set(qt_subdir "${qt_version}/msvc${msvc_year}_64")
 elseif (qt_platform STREQUAL "mac_x64")
-  if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "package")
+  if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos_package")
+    list(APPEND qt_files
+      "qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz")
+    set(qt_subdir "qt-5.15.2-macosx10.13-x86_64-arm64")
+  elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos10.10_package")
     list(APPEND qt_files
       "qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz")
     set(qt_subdir "qt-5.9.9-macosx10.10-x86_64-arm64")
diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake
index 832fa98..afbc081 100644
--- a/.gitlab/ci/download_qt_hashes.cmake
+++ b/.gitlab/ci/download_qt_hashes.cmake
@@ -11,3 +11,4 @@
 set("5.15.1-0-202009071110qtbase-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" df2813ce7c6cb4287abd7956cd1cb9d08312e4ac1208b6cb57af4df11b8ebba1)
 
 set("qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz_hash" d4449771afa0bc6a809c14f1e6d939e7732494cf059503ae451e2bfe8fc60cc1)
+set("qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz_hash" 7b9463a01c8beeee5bf8d01c70deff2d08561cd20aaf6f7a2f41cf8b68ce8a6b)
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 7819e3f..450bae7 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -47,6 +47,14 @@
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMake_SKIP_INSTALL: 1
 
+.macos10.10_package:
+    extends: .macos_build
+
+    variables:
+        CMAKE_CONFIGURATION: macos10.10_package
+        CTEST_NO_WARNINGS_ALLOWED: 1
+        CMake_SKIP_INSTALL: 1
+
 ### External testing
 
 .macos_xcode:
diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml
index 6bfa763..8b8daa1 100644
--- a/.gitlab/upload.yml
+++ b/.gitlab/upload.yml
@@ -15,4 +15,4 @@
         - dnf install -y --setopt=install_weak_deps=False rsync openssh-clients
         - chmod 400 $RSYNC_BINARY_KEY
         - ssh-keygen -y -f $RSYNC_BINARY_KEY > $RSYNC_BINARY_KEY.pub
-        - rsync -tv --recursive -e "ssh -i $RSYNC_BINARY_KEY -o StrictHostKeyChecking=no -o LogLevel=ERROR" build/ kitware@public.kitware.com:$RSYNC_DESTINATION/
+        - rsync -tv --recursive -e "ssh -i $RSYNC_BINARY_KEY -o StrictHostKeyChecking=no -o LogLevel=ERROR" build/ kitware@cmake.org:$RSYNC_DESTINATION/
diff --git a/Help/command/if.rst b/Help/command/if.rst
index 72d328d..c51d2bc 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -47,7 +47,8 @@
 and ``MATCHES``.  Then the boolean operators in the order ``NOT``,  ``AND``,
 and finally ``OR``.
 
-Possible conditions are:
+Basic Expressions
+"""""""""""""""""
 
 ``if(<constant>)``
  True if the constant is ``1``, ``ON``, ``YES``, ``TRUE``, ``Y``,
@@ -62,6 +63,9 @@
  True if given a variable that is defined to a value that is not a false
  constant.  False otherwise.  (Note macro arguments are not variables.)
 
+Logic Operators
+"""""""""""""""
+
 ``if(NOT <condition>)``
  True if the condition is not true.
 
@@ -71,6 +75,15 @@
 ``if(<cond1> OR <cond2>)``
  True if either condition would be considered true individually.
 
+``if((condition) AND (condition OR (condition)))``
+ The conditions inside the parenthesis are evaluated first and then
+ the remaining condition is evaluated as in the other examples.
+ Where there are nested parenthesis the innermost are evaluated as part
+ of evaluating the condition that contains them.
+
+Existence Checks
+""""""""""""""""
+
 ``if(COMMAND command-name)``
  True if the given name is a command, macro or function that can be
  invoked.
@@ -89,6 +102,21 @@
   True if the given name is an existing test name created by the
   :command:`add_test` command.
 
+``if(DEFINED <name>|CACHE{<name>}|ENV{<name>})``
+ True if a variable, cache variable or environment variable
+ with given ``<name>`` is defined. The value of the variable
+ does not matter. Note that macro arguments are not variables.
+
+ .. versionadded:: 3.14
+  Added support for ``CACHE{<name>}`` variables.
+
+``if(<variable|string> IN_LIST <variable>)``
+ .. versionadded:: 3.3
+  True if the given element is contained in the named list variable.
+
+File Operations
+"""""""""""""""
+
 ``if(EXISTS path-to-file-or-directory)``
  True if the named file or directory exists.  Behavior is well-defined
  only for full paths. Resolves symbolic links, i.e. if the named file or
@@ -114,6 +142,9 @@
 ``if(IS_ABSOLUTE path)``
  True if the given path is an absolute path.
 
+Comparisons
+"""""""""""
+
 ``if(<variable|string> MATCHES regex)``
  True if the given string or variable's value matches the given regular
  condition.  See :ref:`Regex Specification` for regex format.
@@ -165,6 +196,9 @@
   True if the given string or variable's value is lexicographically greater
   than or equal to the string or variable on the right.
 
+Version Comparisons
+"""""""""""""""""""
+
 ``if(<variable|string> VERSION_LESS <variable|string>)``
  Component-wise integer version number comparison (version format is
  ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
@@ -197,24 +231,6 @@
   Any non-integer version component or non-integer trailing part of a version
   component effectively truncates the string at that point.
 
-``if(<variable|string> IN_LIST <variable>)``
- .. versionadded:: 3.3
-  True if the given element is contained in the named list variable.
-
-``if(DEFINED <name>|CACHE{<name>}|ENV{<name>})``
- True if a variable, cache variable or environment variable
- with given ``<name>`` is defined. The value of the variable
- does not matter. Note that macro arguments are not variables.
-
- .. versionadded:: 3.14
-  Added support for ``CACHE{<name>}`` variables.
-
-``if((condition) AND (condition OR (condition)))``
- The conditions inside the parenthesis are evaluated first and then
- the remaining condition is evaluated as in the previous examples.
- Where there are nested parenthesis the innermost are evaluated as part
- of evaluating the condition that contains them.
-
 Variable Expansion
 ^^^^^^^^^^^^^^^^^^
 
diff --git a/Help/command/install.rst b/Help/command/install.rst
index bd8da39..35207f4 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -473,6 +473,11 @@
   use "generator expressions" with the syntax ``$<...>``.  See the
   :manual:`cmake-generator-expressions(7)` manual for available expressions.
 
+.. versionadded:: 3.20
+  An install rename given as a ``RENAME`` argument may
+  use "generator expressions" with the syntax ``$<...>``.  See the
+  :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
 Installing Directories
 ^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 4d2b076..d019161 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -7,4 +7,64 @@
 
 .. _`CMake Development`: README.rst
 
-No experimental features are under development in this version of CMake.
+C++20 Module Dependencies
+=========================
+
+The Ninja generator has experimental infrastructure supporting C++20 module
+dependency scanning.  This is similar to the Fortran modules support, but
+relies on external tools to scan C++20 translation units for module
+dependencies.  The approach is described by Kitware's `D1483r1`_ paper.
+
+The ``CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP`` variable can be set to ``1``
+in order to activate this undocumented experimental infrastructure.  This
+is **intended to make the functionality available to compiler writers** so
+they can use it to develop and test their dependency scanning tool.
+The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` variable must also be set
+to tell CMake how to invoke the C++20 module dependency scanning tool.
+
+For example, add code like the following to a test project:
+
+.. code-block:: cmake
+
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+  string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+    "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE>"
+    " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
+    " ${flags_to_scan_deps} -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT>"
+    )
+
+The tool specified by ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` is
+expected to process the translation unit, write preprocessor dependencies
+to the file specified by the ``<DEP_FILE>`` placeholder, and write module
+dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder.
+
+The module dependencies should be written in the format described
+by the `P1689r3`_ paper.
+
+Compiler writers may try out their scanning functionality using
+the `cxx-modules-sandbox`_ test project, modified to set variables
+as above for their compiler.
+
+For compilers that generate module maps, tell CMake as follows:
+
+.. code-block:: cmake
+
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
+    "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>")
+
+Currently, the only supported format is ``gcc``.  The format is described in
+the GCC documentation, but the relevant section for the purposes of CMake is:
+
+    A mapping file consisting of space-separated module-name, filename
+    pairs, one per line.  Only the mappings for the direct imports and any
+    module export name need be provided.  If other mappings are provided,
+    they override those stored in any imported CMI files.  A repository
+    root may be specified in the mapping file by using ``$root`` as the
+    module name in the first active line.
+
+    -- GCC module mapper documentation
+
+.. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
+.. _`P1689r3`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1689r3.html
+.. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox
diff --git a/Help/generator/Ninja Multi-Config.rst b/Help/generator/Ninja Multi-Config.rst
index d1df42b..8901192 100644
--- a/Help/generator/Ninja Multi-Config.rst
+++ b/Help/generator/Ninja Multi-Config.rst
@@ -130,3 +130,34 @@
 the ``generator`` target is called with ``Debug.txt Debug Release`` as
 arguments. The command depends on the ``Release`` builds of ``tgt1`` and
 ``tgt4``, and the ``Debug`` builds of ``tgt2`` and ``tgt3``.
+
+``PRE_BUILD``, ``PRE_LINK``, and ``POST_BUILD`` custom commands for targets
+only get run in their "native" configuration (the ``Release`` configuration in
+the ``build-Release.ninja`` file) unless they have no ``BYPRODUCTS`` or their
+``BYPRODUCTS`` are unique per config. Consider the following example:
+
+.. code-block:: cmake
+
+  add_executable(exe main.c)
+  add_custom_command(
+    TARGET exe
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E echo "Running no-byproduct command"
+    )
+  add_custom_command(
+    TARGET exe
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E echo "Running separate-byproduct command for $<CONFIG>"
+    BYPRODUCTS $<CONFIG>.txt
+    )
+  add_custom_command(
+    TARGET exe
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E echo "Running common-byproduct command for $<CONFIG>"
+    BYPRODUCTS exe.txt
+    )
+
+In this example, if you build ``exe:Debug`` in ``build-Release.ninja``, the
+first and second custom commands get run, since their byproducts are unique
+per-config, but the last custom command does not. However, if you build
+``exe:Release`` in ``build-Release.ninja``, all three custom commands get run.
diff --git a/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt
index 13c82dd..9a9e40e 100644
--- a/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt
+++ b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt
@@ -31,7 +31,7 @@
 install(EXPORT MathFunctionsTargets
         FILE MathFunctionsTargets.cmake
         NAMESPACE MathFunctions::
-        DESTINATION lib/cmake
+        DESTINATION lib/cmake/MathFunctions
 )
 
 # include CMakePackageConfigHelpers macro
@@ -58,14 +58,14 @@
 # create config file
 configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
   "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
-  INSTALL_DESTINATION lib/cmake
+  INSTALL_DESTINATION lib/cmake/MathFunctions
 )
 
 # install config files
 install(FILES
           "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
           "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
-        DESTINATION lib/cmake
+        DESTINATION lib/cmake/MathFunctions
 )
 
 # generate the export targets for the build tree
diff --git a/Help/manual/cmake-developer.7.rst b/Help/manual/cmake-developer.7.rst
index 85ed935..af9a8ab 100644
--- a/Help/manual/cmake-developer.7.rst
+++ b/Help/manual/cmake-developer.7.rst
@@ -23,15 +23,14 @@
 Find Modules
 ============
 
-A "find module" is a ``Find<PackageName>.cmake`` file to be loaded
-by the :command:`find_package` command when invoked for ``<PackageName>``.
+A "find module" is a ``Find<PackageName>.cmake`` file to be loaded by the
+:command:`find_package` command when invoked for ``<PackageName>``.
 
-The primary task of a find module is to determine whether a package
-exists on the system, set the ``<PackageName>_FOUND`` variable to reflect
-this and provide any variables, macros and imported targets required to
-use the package.  A find module is useful in cases where an upstream
-library does not provide a
-:ref:`config file package <Config File Packages>`.
+The primary task of a find module is to determine whether a package is
+available, set the ``<PackageName>_FOUND`` variable to reflect this and
+provide any variables, macros and imported targets required to use the
+package.  A find module is useful in cases where an upstream library does
+not provide a :ref:`config file package <Config File Packages>`.
 
 The traditional approach is to use variables for everything, including
 libraries and executables: see the `Standard Variable Names`_ section
@@ -91,55 +90,92 @@
 For a ``FindXxx.cmake`` module that takes the approach of setting
 variables (either instead of or in addition to creating imported
 targets), the following variable names should be used to keep things
-consistent between find modules.  Note that all variables start with
-``Xxx_`` to make sure they do not interfere with other find modules; the
-same consideration applies to macros, functions and imported targets.
+consistent between Find modules.  Note that all variables start with
+``Xxx_``, which (unless otherwise noted) must match exactly the name
+of the ``FindXxx.cmake`` file, including upper/lowercase.
+This prefix on the variable names ensures that they do not conflict with
+variables of other Find modules.  The same pattern should also be followed
+for any macros, functions and imported targets defined by the Find module.
 
 ``Xxx_INCLUDE_DIRS``
   The final set of include directories listed in one variable for use by
-  client code.  This should not be a cache entry.
+  client code. This should not be a cache entry (note that this also means
+  this variable should not be used as the result variable of a
+  :command:`find_path` command - see ``Xxx_INCLUDE_DIR`` below for that).
 
 ``Xxx_LIBRARIES``
-  The libraries to link against to use Xxx. These should include full
-  paths.  This should not be a cache entry.
+  The libraries to use with the module.  These may be CMake targets, full
+  absolute paths to a library binary or the name of a library that the
+  linker must find in its search path.  This should not be a cache entry
+  (note that this also means this variable should not be used as the
+  result variable of a :command:`find_library` command - see
+  ``Xxx_LIBRARY`` below for that).
 
 ``Xxx_DEFINITIONS``
-  Definitions to use when compiling code that uses Xxx. This really
-  shouldn't include options such as ``-DHAS_JPEG`` that a client
+  The compile definitions to use when compiling code that uses the module.
+  This really shouldn't include options such as ``-DHAS_JPEG`` that a client
   source-code file uses to decide whether to ``#include <jpeg.h>``
 
 ``Xxx_EXECUTABLE``
-  Where to find the Xxx tool.
+  The full absolute path to an executable.  In this case, ``Xxx`` might not
+  be the name of the module, it might be the name of the tool (usually
+  converted to all uppercase), assuming that tool has such a well-known name
+  that it is unlikely that another tool with the same name exists.  It would
+  be appropriate to use this as the result variable of a
+  :command:`find_program` command.
 
-``Xxx_Yyy_EXECUTABLE``
-  Where to find the Yyy tool that comes with Xxx.
+``Xxx_YYY_EXECUTABLE``
+  Similar to ``Xxx_EXECUTABLE`` except here the ``Xxx`` is always the module
+  name and ``YYY`` is the tool name (again, usually fully uppercase).
+  Prefer this form if the tool name is not very widely known or has the
+  potential  to clash with another tool.  For greater consistency, also
+  prefer this form if the module provides more than one executable.
 
 ``Xxx_LIBRARY_DIRS``
   Optionally, the final set of library directories listed in one
-  variable for use by client code.  This should not be a cache entry.
+  variable for use by client code. This should not be a cache entry.
 
 ``Xxx_ROOT_DIR``
-  Where to find the base directory of Xxx.
+  Where to find the base directory of the module.
 
-``Xxx_VERSION_Yy``
-  Expect Version Yy if true. Make sure at most one of these is ever true.
+``Xxx_VERSION_VV``
+  Variables of this form specify whether the ``Xxx`` module being provided
+  is version ``VV`` of the module.  There should not be more than one
+  variable of this form set to true for a given module.  For example, a
+  module ``Barry`` might have evolved over many years and gone through a
+  number of different major versions.  Version 3 of the ``Barry`` module
+  might set the variable ``Barry_VERSION_3`` to true, whereas an older
+  version of the module might set ``Barry_VERSION_2`` to true instead.
+  It would be an error for both ``Barry_VERSION_3`` and ``Barry_VERSION_2``
+  to both be set to true.
 
-``Xxx_WRAP_Yy``
-  If False, do not try to use the relevant CMake wrapping command.
+``Xxx_WRAP_YY``
+  When a variable of this form is set to false, it indicates that the
+  relevant wrapping command should not be used.  The wrapping command
+  depends on the module, it may be implied by the module name or it might
+  be specified by the ``YY`` part of the variable.
 
 ``Xxx_Yy_FOUND``
-  If False, optional Yy part of Xxx system is not available.
+  For variables of this form, ``Yy`` is the name of a component for the
+  module.  It should match exactly one of the valid component names that
+  may be passed to the :command:`find_package` command for the module.
+  If a variable of this form is set to false, it means that the ``Yy``
+  component of module ``Xxx`` was not found or is not available.
+  Variables of this form would typically be used for optional components
+  so that the caller can check whether an optional component is available.
 
 ``Xxx_FOUND``
-  Set to false, or undefined, if we haven't found, or don't want to use
-  Xxx.
+  When the :command:`find_package` command returns to the caller, this
+  variable will be set to true if the module was deemed to have been found
+  successfully.
 
 ``Xxx_NOT_FOUND_MESSAGE``
   Should be set by config-files in the case that it has set
   ``Xxx_FOUND`` to FALSE.  The contained message will be printed by the
   :command:`find_package` command and by
-  ``find_package_handle_standard_args()`` to inform the user about the
-  problem.
+  :command:`find_package_handle_standard_args` to inform the user about the
+  problem.  Use this instead of calling :command:`message` directly to
+  report a reason for failing to find the module or package.
 
 ``Xxx_RUNTIME_LIBRARY_DIRS``
   Optionally, the runtime library search path for use when running an
@@ -160,23 +196,36 @@
 ``Xxx_VERSION_PATCH``
   The patch version of the package found, if any.
 
-The following names should not usually be used in CMakeLists.txt files, but
-are typically cache variables for users to edit and control the
-behaviour of find modules (like entering the path to a library manually)
+The following names should not usually be used in ``CMakeLists.txt`` files.
+They are intended for use by Find modules to specify and cache the locations
+of specific files or directories.  Users are typically able to set and edit
+these variables to control the behavior of Find modules (like entering the
+path to a library manually):
 
 ``Xxx_LIBRARY``
-  The path of the Xxx library (as used with :command:`find_library`, for
-  example).
+  The path of the library.  Use this form only when the module provides a
+  single library.  It is appropriate to use this as the result variable
+  in a :command:`find_library` command.
 
 ``Xxx_Yy_LIBRARY``
-  The path of the Yy library that is part of the Xxx system. It may or
-  may not be required to use Xxx.
+  The path of library ``Yy`` provided by the module ``Xxx``.  Use this form
+  when the module provides more than one library or where other modules may
+  also provide a library of the same name. It is also appropriate to use
+  this form as the result variable in a :command:`find_library` command.
 
 ``Xxx_INCLUDE_DIR``
-  Where to find headers for using the Xxx library.
+  When the module provides only a single library, this variable can be used
+  to specify where to find headers for using the library (or more accurately,
+  the path that consumers of the library should add to their header search
+  path).  It would be appropriate to use this as the result variable in a
+  :command:`find_path` command.
 
 ``Xxx_Yy_INCLUDE_DIR``
-  Where to find headers for using the Yy library of the Xxx system.
+  If the module provides more than one library or where other modules may
+  also provide a library of the same name, this form is recommended for
+  specifying where to find headers for using library ``Yy`` provided by
+  the module.  Again, it would be appropriate to use this as the result
+  variable in a :command:`find_path` command.
 
 To prevent users being overwhelmed with settings to configure, try to
 keep as many options as possible out of the cache, leaving at least one
@@ -185,7 +234,8 @@
 most cache options as advanced.  For packages which provide both debug
 and release binaries, it is common to create cache variables with a
 ``_LIBRARY_<CONFIG>`` suffix, such as ``Foo_LIBRARY_RELEASE`` and
-``Foo_LIBRARY_DEBUG``.
+``Foo_LIBRARY_DEBUG``.  The :module:`SelectLibraryConfigurations` module
+can be helpful for such cases.
 
 While these are the standard variable names, you should provide
 backwards compatibility for any old names that were actually in use.
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 5dbc1a4..af170da 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -196,6 +196,7 @@
    /prop_tgt/EXCLUDE_FROM_ALL
    /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD
    /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG
+   /prop_tgt/EXPORT_COMPILE_COMMANDS
    /prop_tgt/EXPORT_NAME
    /prop_tgt/EXPORT_PROPERTIES
    /prop_tgt/FOLDER
diff --git a/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst b/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst
new file mode 100644
index 0000000..0b1145c
--- /dev/null
+++ b/Help/prop_tgt/EXPORT_COMPILE_COMMANDS.rst
@@ -0,0 +1,9 @@
+EXPORT_COMPILE_COMMANDS
+-----------------------
+
+.. versionadded:: 3.20
+
+Enable/Disable output of compile commands during generation for a target.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_EXPORT_COMPILE_COMMANDS` if it is set when a target is created.
diff --git a/Help/release/3.19.rst b/Help/release/3.19.rst
index eb49c6f..7ad27c8 100644
--- a/Help/release/3.19.rst
+++ b/Help/release/3.19.rst
@@ -390,3 +390,13 @@
 ------
 
 * A precompiled Linux ``aarch64`` binary is now provided on ``cmake.org``.
+
+* Two precompiled macOS binaries are now provided on ``cmake.org``:
+
+  * The naming pattern ``cmake-$ver-macos-universal`` is a universal
+    binary with ``x86_64`` and ``arm64`` architectures.  It requires
+    macOS 10.13 or newer.
+
+  * The naming pattern ``cmake-$ver-macos10.10-universal`` is a universal
+    binary with ``x86_64`` and ``arm64`` architectures.  It requires
+    macOS 10.10 or newer.
diff --git a/Help/release/dev/cuda-nvcc-ccache-symlink.rst b/Help/release/dev/cuda-nvcc-ccache-symlink.rst
new file mode 100644
index 0000000..4d38047
--- /dev/null
+++ b/Help/release/dev/cuda-nvcc-ccache-symlink.rst
@@ -0,0 +1,9 @@
+cuda-nvcc-ccache-symlink
+------------------------
+
+* ``CUDA`` language support now works when ``nvcc`` is a symbolic link,
+  for example due to a ``ccache`` or ``colornvcc`` wrapper script.
+
+* The :module:`FindCUDAToolkit` module gained support for finding CUDA
+  toolkits when ``nvcc`` is a symbolic link,
+  for example due to a ``ccache`` or ``colornvcc`` wrapper script.
diff --git a/Help/release/dev/export-compile-commands-per-target.rst b/Help/release/dev/export-compile-commands-per-target.rst
new file mode 100644
index 0000000..7063547
--- /dev/null
+++ b/Help/release/dev/export-compile-commands-per-target.rst
@@ -0,0 +1,6 @@
+export-compile-commands-per-target
+----------------------------------
+
+* The :prop_tgt:`EXPORT_COMPILE_COMMANDS` target property was added
+  for the associated :variable:`CMAKE_EXPORT_COMPILE_COMMANDS` variable
+  to allow for configuration of exporting compile commands per target.
diff --git a/Help/release/dev/external-project-configure-handled-by-build.rst b/Help/release/dev/external-project-configure-handled-by-build.rst
new file mode 100644
index 0000000..4a1fac8
--- /dev/null
+++ b/Help/release/dev/external-project-configure-handled-by-build.rst
@@ -0,0 +1,8 @@
+external-project-configure-handled-by-build
+-------------------------------------------
+
+* The :module:`ExternalProject` function ``ExternalProject_Add`` learned a new
+  ``CONFIGURE_HANDLED_BY_BUILD`` option to have subsequent runs of the configure
+  step be triggered by the build step when an external project dependency
+  rebuilds instead of always rerunning the configure step when an external
+  project dependency rebuilds.
diff --git a/Help/release/dev/install-files-rename-genex.rst b/Help/release/dev/install-files-rename-genex.rst
new file mode 100644
index 0000000..f735e24
--- /dev/null
+++ b/Help/release/dev/install-files-rename-genex.rst
@@ -0,0 +1,5 @@
+install-files-rename-genex
+--------------------------
+
+* The :command:`install(FILES)` command ``RENAME`` option learned to
+  support :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst b/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst
index 7f7e679..d885516 100644
--- a/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst
+++ b/Help/variable/CMAKE_CUDA_ARCHITECTURES.rst
@@ -18,3 +18,18 @@
 
 This variable is used to initialize the :prop_tgt:`CUDA_ARCHITECTURES` property
 on all targets. See the target property for additional information.
+
+Examples
+^^^^^^^^
+
+.. code-block:: cmake
+
+  cmake_minimum_required(VERSION)
+
+  if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
+    set(CMAKE_CUDA_ARCHITECTURES 75)
+  endif()
+
+  project(example LANGUAGES CUDA)
+
+``CMAKE_CUDA_ARCHITECTURES`` will default to ``75`` unless overridden by the user.
diff --git a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
index 724f309..53a19dc 100644
--- a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
+++ b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
@@ -28,7 +28,8 @@
   ]
 
 This is initialized by the :envvar:`CMAKE_EXPORT_COMPILE_COMMANDS` environment
-variable.
+variable, and initializes the :prop_tgt:`EXPORT_COMPILE_COMMANDS` target
+property for all targets.
 
 .. note::
   This option is implemented only by :ref:`Makefile Generators`
diff --git a/Help/variable/MSVC.rst b/Help/variable/MSVC.rst
index ca8775c..a2dbc2e 100644
--- a/Help/variable/MSVC.rst
+++ b/Help/variable/MSVC.rst
@@ -1,8 +1,7 @@
 MSVC
 ----
 
-Set to ``true`` when the compiler is some version of Microsoft Visual
-C++ or another compiler simulating Visual C++.  Any compiler defining
-``_MSC_VER`` is considered simulating Visual C++.
+Set to ``true`` when the compiler is some version of Microsoft Visual C++
+or another compiler simulating the Visual C++ ``cl`` command-line syntax.
 
 See also the :variable:`MSVC_VERSION` variable.
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index 92ae2ab..45acfe7 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -44,7 +44,7 @@
   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_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP)
 set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
 
 foreach (lang C OBJC OBJCXX)
diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake
index ae3abe9..ab33b40 100644
--- a/Modules/CMakeDetermineCCompiler.cmake
+++ b/Modules/CMakeDetermineCCompiler.cmake
@@ -161,9 +161,10 @@
 
   if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|QCC")
     get_filename_component(COMPILER_BASENAME "${CMAKE_C_COMPILER}" NAME)
-    if (COMPILER_BASENAME MATCHES "^(.+-)(clang|g?cc)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
+    if (COMPILER_BASENAME MATCHES "^(.+-)?(clang|g?cc)(-cl)?(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
       set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
-      set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5})
+      set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_4})
+      set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_6})
     elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
       if(CMAKE_C_COMPILER_TARGET)
         set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_C_COMPILER_TARGET}-)
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake
index 578729c..c77fc3a 100644
--- a/Modules/CMakeDetermineCUDACompiler.cmake
+++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -172,7 +172,20 @@
       endif()
     endif()
 
-    get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY)
+    # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the
+    # real non-scattered toolkit.
+    if(IS_SYMLINK ${_CUDA_NVCC_EXECUTABLE})
+      execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR)
+      if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)")
+        set(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_MATCH_1}")
+      else()
+        message(FATAL_ERROR "Could not execute nvcc with -v.")
+      endif()
+      unset(NVCC_ERR)
+    else()
+      get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY)
+    endif()
+
     set(CMAKE_CUDA_DEVICE_LINKER "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvlink${CMAKE_EXECUTABLE_SUFFIX}")
     set(CMAKE_CUDA_FATBINARY "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/fatbinary${CMAKE_EXECUTABLE_SUFFIX}")
     get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY)
diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake
index 905eb25..7283bc2 100644
--- a/Modules/CMakeDetermineCXXCompiler.cmake
+++ b/Modules/CMakeDetermineCXXCompiler.cmake
@@ -160,8 +160,9 @@
 
   if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|QCC")
     get_filename_component(COMPILER_BASENAME "${CMAKE_CXX_COMPILER}" NAME)
-    if (COMPILER_BASENAME MATCHES "^(.+-)(clan)?[gc]\\+\\+(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
+    if (COMPILER_BASENAME MATCHES "^(.+-)?(clang\\+\\+|g\\+\\+|clang-cl)(-[0-9]+(\\.[0-9]+)*)?(-[^.]+)?(\\.exe)?$")
       set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
+      set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_3})
       set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5})
     elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
       if(CMAKE_CXX_COMPILER_TARGET)
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index d81fd11..ff178f6 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -70,17 +70,18 @@
    OR (CMAKE_GENERATOR MATCHES "Visual Studio"
        AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "Tegra-Android"))
 
+  set(_CMAKE_LINKER_NAMES "link")
+  set(_CMAKE_AR_NAMES "lib")
+  set(_CMAKE_MT_NAMES "mt")
   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")
-    set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-lib")
+    set(_CMAKE_NM_NAMES "llvm-nm" "nm")
+    list(APPEND _CMAKE_AR_NAMES "lib" "llvm-lib")
+    list(APPEND _CMAKE_MT_NAMES "mt" "llvm-mt")
+    list(APPEND _CMAKE_LINKER_NAMES "lld-link")
+    list(APPEND _CMAKE_TOOL_VARS NM)
   endif()
 
-  find_program(CMAKE_LINKER NAMES ${_CMAKE_ADDITIONAL_LINKER_NAMES} link HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_AR     NAMES ${_CMAKE_ADDITIONAL_AR_NAMES}     lib  HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_MT     NAMES mt   HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-
-  list(APPEND _CMAKE_TOOL_VARS LINKER MT)
+  list(APPEND _CMAKE_TOOL_VARS LINKER MT AR)
 
 # in all other cases search for ar, ranlib, etc.
 else()
@@ -92,55 +93,54 @@
   endif()
 
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
-    set(_CMAKE_ADDITIONAL_AR_NAMES "llvm-ar")
-    set(_CMAKE_ADDITIONAL_RANLIB_NAMES "llvm-ranlib")
-    set(_CMAKE_ADDITIONAL_STRIP_NAMES "llvm-strip")
     if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC")
-      set(_CMAKE_ADDITIONAL_LINKER_NAMES "lld-link")
+      set(_CMAKE_LINKER_NAMES "lld-link")
     else()
-      set(_CMAKE_ADDITIONAL_LINKER_NAMES "ld.lld")
+      set(_CMAKE_LINKER_NAMES "ld.lld")
     endif()
-    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")
+    list(APPEND _CMAKE_AR_NAMES "llvm-ar")
+    list(APPEND _CMAKE_NM_NAMES "llvm-nm")
+    list(APPEND _CMAKE_OBJDUMP_NAMES "llvm-objdump")
+    list(APPEND _CMAKE_OBJCOPY_NAMES "llvm-objcopy")
+    list(APPEND _CMAKE_READELF_NAMES "llvm-readelf")
+    list(APPEND _CMAKE_DLLTOOL_NAMES "llvm-dlltool")
+    list(APPEND _CMAKE_ADDR2LINE_NAMES "llvm-addr2line")
   endif()
 
-  if(NOT CMAKE_CROSSCOMPILING AND NOT "${_CMAKE_TOOLCHAIN_PREFIX}" STREQUAL "")
-    list(APPEND _CMAKE_ADDITIONAL_AR_NAMES "ar")
-    list(APPEND _CMAKE_ADDITIONAL_RANLIB_NAMES "ranlib")
-    list(APPEND _CMAKE_ADDITIONAL_STRIP_NAMES "strip")
-    list(APPEND _CMAKE_ADDITIONAL_LINKER_NAMES "ld")
-    list(APPEND _CMAKE_ADDITIONAL_NM_NAMES "nm")
-    list(APPEND _CMAKE_ADDITIONAL_OBJDUMP_NAMES "objdump")
-    list(APPEND _CMAKE_ADDITIONAL_OBJCOPY_NAMES "objcopy")
-    list(APPEND _CMAKE_ADDITIONAL_READELF_NAMES "readelf")
-    list(APPEND _CMAKE_ADDITIONAL_DLLTOOL_NAMES "dlltool")
-    list(APPEND _CMAKE_ADDITIONAL_ADDR2LINE_NAMES "addr2line")
-  endif()
+    list(APPEND _CMAKE_AR_NAMES "ar")
+    list(APPEND _CMAKE_RANLIB_NAMES "ranlib")
+    list(APPEND _CMAKE_STRIP_NAMES "strip")
+    list(APPEND _CMAKE_LINKER_NAMES "ld")
+    list(APPEND _CMAKE_NM_NAMES "nm")
+    list(APPEND _CMAKE_OBJDUMP_NAMES "objdump")
+    list(APPEND _CMAKE_OBJCOPY_NAMES "objcopy")
+    list(APPEND _CMAKE_READELF_NAMES "readelf")
+    list(APPEND _CMAKE_DLLTOOL_NAMES "dlltool")
+    list(APPEND _CMAKE_ADDR2LINE_NAMES "addr2line")
 
-  find_program(CMAKE_AR NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ar${_CMAKE_TOOLCHAIN_SUFFIX} ${_CMAKE_ADDITIONAL_AR_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-
-  find_program(CMAKE_RANLIB NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ranlib ${_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} ${_CMAKE_ADDITIONAL_STRIP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_LINKER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}ld ${_CMAKE_ADDITIONAL_LINKER_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_NM NAMES ${_CMAKE_TOOLCHAIN_PREFIX}nm ${_CMAKE_ADDITIONAL_NM_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_OBJDUMP NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objdump ${_CMAKE_ADDITIONAL_OBJDUMP_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy ${_CMAKE_ADDITIONAL_OBJCOPY_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_READELF NAMES ${_CMAKE_TOOLCHAIN_PREFIX}readelf ${_CMAKE_ADDITIONAL_READELF_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_DLLTOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}dlltool ${_CMAKE_ADDITIONAL_DLLTOOL_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-  find_program(CMAKE_ADDR2LINE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}addr2line ${_CMAKE_ADDITIONAL_ADDR2LINE_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
-
-  list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE)
+    list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE)
 endif()
 
+foreach(TOOL IN LISTS _CMAKE_TOOL_VARS)
+  foreach(NAME IN LISTS _CMAKE_${TOOL}_NAMES)
+    if(NOT _CMAKE_TOOLCHAIN_PREFIX STREQUAL "")
+      if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "")
+        list(PREPEND _CMAKE_${TOOL}_NAMES ${NAME}${_CMAKE_TOOLCHAIN_SUFFIX})
+      endif()
+      list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME})
+    endif()
+    if(NOT _CMAKE_TOOLCHAIN_SUFFIX STREQUAL "")
+      list(PREPEND _CMAKE_${TOOL}_NAMES ${_CMAKE_TOOLCHAIN_PREFIX}${NAME}${_CMAKE_TOOLCHAIN_SUFFIX})
+    endif()
+  endforeach()
+  find_program(CMAKE_${TOOL} NAMES ${_CMAKE_${TOOL}_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+endforeach()
+
+if(NOT CMAKE_RANLIB)
+    set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
+endif()
+
+
 if(CMAKE_PLATFORM_HAS_INSTALLNAME)
   find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
 
@@ -157,7 +157,7 @@
   if(_CMAKE_TOOL_CACHED)
     mark_as_advanced(CMAKE_${var})
   endif()
-  unset(_CMAKE_ADDITIONAL_${var}_NAMES)
+  unset(_CMAKE_${var}_NAMES)
 endforeach()
 unset(_CMAKE_TOOL_VARS)
 unset(_CMAKE_TOOL_CACHED)
diff --git a/Modules/Compiler/Clang-FindBinUtils.cmake b/Modules/Compiler/Clang-FindBinUtils.cmake
index b852660..e6c469a 100644
--- a/Modules/Compiler/Clang-FindBinUtils.cmake
+++ b/Modules/Compiler/Clang-FindBinUtils.cmake
@@ -2,6 +2,12 @@
   message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set")
 endif()
 
+# Ubuntu:
+# * /usr/bin/llvm-ar-9
+# * /usr/bin/llvm-ranlib-9
+string(REGEX MATCH "^([0-9]+)" __version_x
+    "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}")
+
 # Debian:
 # * /usr/bin/llvm-ar-4.0
 # * /usr/bin/llvm-ranlib-4.0
@@ -19,6 +25,7 @@
 # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ar.1.html
 find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
     "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x_y}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x}"
     "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar"
     HINTS ${__clang_hints}
     DOC "LLVM archiver"
@@ -28,6 +35,7 @@
 # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ranlib.1.html
 find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES
     "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x_y}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x}"
     "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib"
     HINTS ${__clang_hints}
     DOC "Generate index for LLVM archive"
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 28d21c1..3307bc6 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -543,6 +543,18 @@
         When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND``
         is used to point to an alternative directory within the source tree.
 
+    ``CONFIGURE_HANDLED_BY_BUILD <bool>``
+      .. versionadded:: 3.20
+
+      Enabling this option relaxes the dependencies of the configure step on
+      other external projects to order-only. This means the configure step will
+      be executed after its external project dependencies are built but it will
+      not be marked dirty when one of its external project dependencies is
+      rebuilt. This option can be enabled when the build step is smart enough
+      to figure out if the configure step needs to be rerun. CMake and Meson are
+      examples of build systems whose build step is smart enough to know if the
+      configure step needs to be rerun.
+
   **Build Step Options:**
     If the configure step assumed the external project uses CMake as its build
     system, the build step will also. Otherwise, the build step will assume a
@@ -3083,6 +3095,23 @@
   )
 endfunction()
 
+function(_ep_get_file_deps var name)
+  set(file_deps)
+
+  get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
+  foreach(dep IN LISTS deps)
+    get_property(dep_type TARGET ${dep} PROPERTY TYPE)
+    if(dep_type STREQUAL "UTILITY")
+      get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT)
+      if(is_ep)
+        _ep_get_step_stampfile(${dep} "done" done_stamp_file)
+        list(APPEND file_deps ${done_stamp_file})
+      endif()
+    endif()
+  endforeach()
+
+  set("${var}" "${file_deps}" PARENT_SCOPE)
+endfunction()
 
 function(_ep_extract_configure_command var name)
   get_property(cmd_set TARGET ${name} PROPERTY _EP_CONFIGURE_COMMAND SET)
@@ -3191,19 +3220,13 @@
 function(_ep_add_configure_command name)
   ExternalProject_Get_Property(${name} binary_dir tmp_dir)
 
-  # Depend on other external projects (file-level).
   set(file_deps)
-  get_property(deps TARGET ${name} PROPERTY _EP_DEPENDS)
-  foreach(dep IN LISTS deps)
-    get_property(dep_type TARGET ${dep} PROPERTY TYPE)
-    if(dep_type STREQUAL "UTILITY")
-      get_property(is_ep TARGET ${dep} PROPERTY _EP_IS_EXTERNAL_PROJECT)
-      if(is_ep)
-        _ep_get_step_stampfile(${dep} "done" done_stamp_file)
-        list(APPEND file_deps ${done_stamp_file})
-      endif()
-    endif()
-  endforeach()
+  get_property(configure_handled_by_build TARGET ${name}
+               PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
+  if(NOT configure_handled_by_build)
+    # Depend on other external projects (file-level)
+    _ep_get_file_deps(file_deps ${name})
+  endif()
 
   _ep_extract_configure_command(cmd ${name})
 
@@ -3254,6 +3277,14 @@
 function(_ep_add_build_command name)
   ExternalProject_Get_Property(${name} binary_dir)
 
+  set(file_deps)
+  get_property(configure_handled_by_build TARGET ${name}
+               PROPERTY _EP_CONFIGURE_HANDLED_BY_BUILD)
+  if(configure_handled_by_build)
+    # Depend on other external projects (file-level)
+    _ep_get_file_deps(file_deps ${name})
+  endif()
+
   get_property(cmd_set TARGET ${name} PROPERTY _EP_BUILD_COMMAND SET)
   if(cmd_set)
     get_property(cmd TARGET ${name} PROPERTY _EP_BUILD_COMMAND)
@@ -3296,6 +3327,7 @@
       BYPRODUCTS \${build_byproducts}
       WORKING_DIRECTORY \${binary_dir}
       DEPENDEES configure
+      DEPENDS \${file_deps}
       ALWAYS \${always}
       ${log}
       ${uses_terminal}
diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake
index 6daf81d..240f0a5 100644
--- a/Modules/FindCUDA.cmake
+++ b/Modules/FindCUDA.cmake
@@ -834,7 +834,20 @@
     )
 
   if (CUDA_TOOLKIT_ROOT_DIR_NVCC)
-    get_filename_component(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CUDA_TOOLKIT_ROOT_DIR_NVCC}" DIRECTORY)
+    # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the
+    # real non-scattered toolkit.
+    if(IS_SYMLINK ${CUDA_TOOLKIT_ROOT_DIR_NVCC})
+      execute_process(COMMAND ${CUDA_TOOLKIT_ROOT_DIR_NVCC} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR)
+      if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)")
+        set(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CMAKE_MATCH_1}")
+      else()
+        message(FATAL_ERROR "Could not execute nvcc with -v.")
+      endif()
+      unset(NVCC_ERR)
+    else()
+      get_filename_component(CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR "${CUDA_TOOLKIT_ROOT_DIR_NVCC}" DIRECTORY)
+    endif()
+
     get_filename_component(CUDA_TOOLKIT_ROOT_DIR "${CUDA_TOOLKIT_ROOT_DIR_NVCC_PAR}" DIRECTORY CACHE)
     string(REGEX REPLACE "[/\\\\]?bin[64]*[/\\\\]?$" "" CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT_DIR})
     # We need to force this back into the cache.
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index 61e264b..0d80c80 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -519,7 +519,19 @@
       endif()
 
       if(CUDAToolkit_NVCC_EXECUTABLE)
-        get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY)
+        # If NVCC is a symlink due to a wrapper script (e.g. ccache or colornvcc), then invoke it to find the
+        # real non-scattered toolkit.
+        if(IS_SYMLINK ${CUDAToolkit_NVCC_EXECUTABLE})
+          execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" ERROR_VARIABLE NVCC_ERR)
+          if(NVCC_ERR MATCHES " _HERE_=([^\r\n]*)")
+            set(CUDAToolkit_BIN_DIR "${CMAKE_MATCH_1}")
+          else()
+            message(FATAL_ERROR "Could not execute nvcc with -v.")
+          endif()
+          unset(NVCC_ERR)
+        else()
+          get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY)
+        endif()
 
         set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
         mark_as_advanced(CUDAToolkit_BIN_DIR)
diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake
index 05696ae..f64f72f 100644
--- a/Modules/FindHDF5.cmake
+++ b/Modules/FindHDF5.cmake
@@ -116,15 +116,22 @@
 
 With all components enabled, the following targets will be defined:
 
-::
-
-  ``hdf5::hdf5``
-  ``hdf5::hdf5_hl_cpp``
-  ``hdf5::hdf5_fortran``
-  ``hdf5::hdf5_hl``
-  ``hdf5::hdf5_hl_cpp``
-  ``hdf5::hdf5_hl_fortran``
-  ``hdf5::h5diff``
+``HDF5::HDF5``
+  All detected ``HDF5_LIBRARIES``.
+``hdf5::hdf5``
+  C library.
+``hdf5::hdf5_cpp``
+  C++ library.
+``hdf5::hdf5_fortran``
+  Fortran library.
+``hdf5::hdf5_hl``
+  High-level C library.
+``hdf5::hdf5_hl_cpp``
+  High-level C++ library.
+``hdf5::hdf5_hl_fortran``
+  High-level Fortran library.
+``hdf5::h5diff``
+  ``h5diff`` executable.
 
 Hints
 ^^^^^
@@ -1147,6 +1154,21 @@
     message(STATUS "HDF5_${_lang}_HL_LIBRARY: ${HDF5_${_lang}_HL_LIBRARY}")
     message(STATUS "HDF5_${_lang}_HL_LIBRARIES: ${HDF5_${_lang}_HL_LIBRARIES}")
   endforeach()
+  message(STATUS "Defined targets (if any):")
+  foreach(_lang IN  ITEMS "" "_cpp" "_fortran")
+    foreach(_hl IN  ITEMS "" "_hl")
+      foreach(_prefix IN ITEMS "hdf5::" "")
+        foreach(_suffix IN ITEMS "-static" "-shared" "")
+          set (_target ${_prefix}hdf5${_hl}${_lang}${_suffix})
+          if (TARGET  ${_target})
+            message(STATUS "... ${_target}")
+          else()
+            #message(STATUS "... ${_target} does not exist")
+          endif()
+        endforeach()
+      endforeach()
+    endforeach()
+  endforeach()
 endif()
 unset(_lang)
 unset(_HDF5_NEED_TO_SEARCH)
diff --git a/Modules/FindJPEG.cmake b/Modules/FindJPEG.cmake
index 3f243de..add2486 100644
--- a/Modules/FindJPEG.cmake
+++ b/Modules/FindJPEG.cmake
@@ -58,7 +58,7 @@
 
 find_path(JPEG_INCLUDE_DIR jpeglib.h)
 
-set(jpeg_names ${JPEG_NAMES} jpeg jpeg-static libjpeg libjpeg-static)
+set(jpeg_names ${JPEG_NAMES} jpeg jpeg-static libjpeg libjpeg-static turbojpeg turbojpeg-static)
 foreach(name ${jpeg_names})
   list(APPEND jpeg_names_debug "${name}d")
 endforeach()
diff --git a/Modules/FindPostgreSQL.cmake b/Modules/FindPostgreSQL.cmake
index cd50d8c..911210f 100644
--- a/Modules/FindPostgreSQL.cmake
+++ b/Modules/FindPostgreSQL.cmake
@@ -247,7 +247,7 @@
 # Did we find anything?
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 find_package_handle_standard_args(PostgreSQL
-                                  REQUIRED_VARS PostgreSQL_LIBRARY PostgreSQL_INCLUDE_DIR PostgreSQL_TYPE_INCLUDE_DIR
+                                  REQUIRED_VARS PostgreSQL_LIBRARY PostgreSQL_INCLUDE_DIR
                                   VERSION_VAR PostgreSQL_VERSION_STRING)
 set(PostgreSQL_FOUND  ${POSTGRESQL_FOUND})
 
@@ -271,16 +271,19 @@
 
 # Now try to get the include and library path.
 if(PostgreSQL_FOUND)
+  set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR})
+  if(PostgreSQL_TYPE_INCLUDE_DIR)
+    list(APPEND PostgreSQL_INCLUDE_DIRS ${PostgreSQL_TYPE_INCLUDE_DIR})
+  endif()
+  set(PostgreSQL_LIBRARY_DIRS ${PostgreSQL_LIBRARY_DIR} )
   if (NOT TARGET PostgreSQL::PostgreSQL)
     add_library(PostgreSQL::PostgreSQL UNKNOWN IMPORTED)
     set_target_properties(PostgreSQL::PostgreSQL PROPERTIES
-      INTERFACE_INCLUDE_DIRECTORIES "${PostgreSQL_INCLUDE_DIR};${PostgreSQL_TYPE_INCLUDE_DIR}")
+      INTERFACE_INCLUDE_DIRECTORIES "${PostgreSQL_INCLUDE_DIRS}")
     __postgresql_import_library(PostgreSQL::PostgreSQL PostgreSQL_LIBRARY "")
     __postgresql_import_library(PostgreSQL::PostgreSQL PostgreSQL_LIBRARY "RELEASE")
     __postgresql_import_library(PostgreSQL::PostgreSQL PostgreSQL_LIBRARY "DEBUG")
   endif ()
-  set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR} ${PostgreSQL_TYPE_INCLUDE_DIR} )
-  set(PostgreSQL_LIBRARY_DIRS ${PostgreSQL_LIBRARY_DIR} )
 endif()
 
 mark_as_advanced(PostgreSQL_INCLUDE_DIR PostgreSQL_TYPE_INCLUDE_DIR)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 4598fe4..f60b098 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 19)
-set(CMake_VERSION_PATCH 20210106)
+set(CMake_VERSION_PATCH 20210114)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 4ba0ae1..5a6c775 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -139,7 +139,7 @@
         s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
       }
       this->TestResult.CompletionStatus = s.str();
-      cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped ");
+      outputStream << "***Skipped ";
       skipped = true;
     } else if (success != this->TestProperties->WillFail) {
       this->TestResult.Status = cmCTestTestHandler::COMPLETED;
@@ -195,8 +195,9 @@
   sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime().count());
   outputStream << buf << "\n";
 
+  bool passedOrSkipped = passed || skipped;
   if (this->CTest->GetTestProgressOutput()) {
-    if (!passed) {
+    if (!passedOrSkipped) {
       // If the test did not pass, reprint test name and error
       std::string output = this->GetTestPrefix(completed, total);
       std::string testName = this->TestProperties->Name;
@@ -216,7 +217,7 @@
       cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
     }
   }
-  if (!this->CTest->GetTestProgressOutput() || !passed) {
+  if (!this->CTest->GetTestProgressOutput() || !passedOrSkipped) {
     cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
   }
 
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
index 452a303..0c263bb 100644
--- a/Source/QtDialog/CMakeLists.txt
+++ b/Source/QtDialog/CMakeLists.txt
@@ -3,20 +3,52 @@
 
 project(QtDialog)
 CMake_OPTIONAL_COMPONENT(cmake-gui)
-find_package(Qt5Widgets REQUIRED)
+set (QT_COMPONENTS
+  Core
+  Widgets
+  Gui
+)
+
+set(CMake_QT_MAJOR_VERSION "A" CACHE
+  STRING "Expected Qt major version. Valid values are A (auto-select), 5, 6.")
+set(SUPPORTED_QT_VERSIONS "A" 5 6)
+set_property(CACHE CMake_QT_MAJOR_VERSION PROPERTY STRINGS ${SUPPORTED_QT_VERSIONS})
+if(NOT CMake_QT_MAJOR_VERSION STREQUAL "A")
+  if(NOT CMake_QT_MAJOR_VERSION IN_LIST SUPPORTED_QT_VERSIONS)
+    message(FATAL_ERROR "Supported Qt versions are \"${SUPPORTED_QT_VERSIONS}\"."
+            " But CMake_QT_MAJOR_VERSION is set to ${CMake_QT_MAJOR_VERSION}.")
+  endif()
+  set(INSTALLED_QT_VERSION ${CMake_QT_MAJOR_VERSION})
+else()
+  find_package(Qt6Widgets QUIET)
+  set(INSTALLED_QT_VERSION 6)
+  if(NOT Qt6Widgets_FOUND)
+    find_package(Qt5Widgets QUIET)
+    if(NOT Qt5Widgets_FOUND)
+      message(FATAL_ERROR "Could not find a valid Qt installation.")
+    endif()
+    set(INSTALLED_QT_VERSION 5)
+  endif()
+endif()
+
+find_package(Qt${INSTALLED_QT_VERSION}
+    COMPONENTS ${QT_COMPONENTS}
+    REQUIRED QUIET
+)
 
 set(CMake_QT_EXTRA_LIBRARIES)
 
 # Try to find the package WinExtras for the task bar progress
 if(WIN32)
-  find_package(Qt5WinExtras QUIET)
-  if (Qt5WinExtras_FOUND)
+  find_package(Qt${INSTALLED_QT_VERSION}WinExtras QUIET)
+  if (Qt${INSTALLED_QT_VERSION}WinExtras_FOUND)
     add_definitions(-DQT_WINEXTRAS)
-    list(APPEND CMake_QT_EXTRA_LIBRARIES Qt5::WinExtras)
+    list(APPEND CMake_QT_EXTRA_LIBRARIES Qt${INSTALLED_QT_VERSION}::WinExtras)
+    list(APPEND QT_COMPONENTS WinExtras)
   endif()
 endif()
 
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt${INSTALLED_QT_VERSION}Widgets_EXECUTABLE_COMPILE_FLAGS}")
 
 if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES)
   list(APPEND CMake_QT_EXTRA_LIBRARIES ${CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES})
@@ -31,11 +63,25 @@
 endif()
 
 # We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows.
-# FIXME: This should be part of Qt5 CMake scripts, but unfortunately
-# Qt5 support is missing there.
 if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32))
-  macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var)
-    get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION)
+  function(_qt_get_plugin_name_with_version target out_var)
+      string(REGEX REPLACE "^Qt::(.+)" "Qt${INSTALLED_QT_VERSION}::\\1"
+             qt_plugin_with_version "${target}")
+      if(TARGET "${qt_plugin_with_version}")
+          set("${out_var}" "${qt_plugin_with_version}" PARENT_SCOPE)
+      else()
+          set("${out_var}" "" PARENT_SCOPE)
+      endif()
+  endfunction()
+  macro(install_qt_plugin _qt_plugin_name _qt_plugins_var)
+    if(TARGET "${_qt_plugin_name}")
+      get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION)
+    else()
+      _qt_get_plugin_name_with_version("Qt::${_qt_plugin_name}" _qt_plugin_with_version_name)
+      if(TARGET "${_qt_plugin_with_version_name}")
+        get_target_property(_qt_plugin_path "${_qt_plugin_with_version_name}" LOCATION)
+      endif()
+    endif()
     if(EXISTS "${_qt_plugin_path}")
       get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME)
       get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH)
@@ -51,19 +97,47 @@
         ${COMPONENT})
       set(${_qt_plugins_var}
         "${${_qt_plugins_var}};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}")
-    else()
-      message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found")
     endif()
   endmacro()
+  macro(install_qt_plugins _comps _plugins_var)
+    foreach(_qt_comp ${${_comps}})
+      if (INSTALLED_QT_VERSION VERSION_LESS 6)
+        set(_qt_module_plugins ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_PLUGINS})
+      else()
+        get_target_property(_qt_module_plugins Qt${INSTALLED_QT_VERSION}::${_qt_comp} QT_PLUGINS)
+      endif()
+      foreach(_qt_plugin ${_qt_module_plugins})
+        if (INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6)
+          # Qt6 provides the plugins as individual packages that need to be found.
+          find_package(Qt${INSTALLED_QT_VERSION}${_qt_plugin} QUIET
+            PATHS ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_DIR})
+        endif()
+        install_qt_plugin("${_qt_plugin}" "${_plugins_var}")
+      endforeach()
+    endforeach()
+  endmacro()
   if(APPLE)
-    install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS)
+    if (INSTALLED_QT_VERSION VERSION_EQUAL 5)
+      install_qt_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS)
+      if(TARGET Qt5::QMacStylePlugin)
+        install_qt_plugin("Qt5::QMacStylePlugin" QT_PLUGINS)
+      endif()
+    else()
+      # FIXME: Minimize plugins for Qt6.
+      install_qt_plugins(QT_COMPONENTS QT_PLUGINS)
+    endif()
     file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
       "[Paths]\nPlugins = ${_qt_plugin_dir}\n")
     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
       DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources"
       ${COMPONENT})
   elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES)
-    install_qt5_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS)
+    if (INSTALLED_QT_VERSION VERSION_EQUAL 5)
+      install_qt_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS)
+    else()
+      # FIXME: Minimize plugins for Qt6.
+      install_qt_plugins(QT_COMPONENTS QT_PLUGINS)
+    endif()
     file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
       "[Paths]\nPlugins = ../${_qt_plugin_dir}\n")
     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
@@ -72,8 +146,8 @@
   endif()
 endif()
 
-get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION)
-get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH)
+get_property(_Qt_Core_LOCATION TARGET Qt${INSTALLED_QT_VERSION}::Core PROPERTY LOCATION)
+get_filename_component(Qt_BIN_DIR "${_Qt_Core_LOCATION}" PATH)
 if(APPLE)
   get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH)
 endif()
@@ -105,7 +179,7 @@
   WarningMessagesDialog.cxx
   WarningMessagesDialog.h
   )
-qt5_wrap_ui(UI_SRCS
+set(UI_SRCS
   CMakeSetupDialog.ui
   Compilers.ui
   CrossCompiler.ui
@@ -114,7 +188,7 @@
   RegexExplorer.ui
   WarningMessagesDialog.ui
   )
-qt5_wrap_cpp(MOC_SRCS
+set(MOC_SRCS
   AddCacheEntry.h
   Compilers.h
   CMakeSetupDialog.h
@@ -128,7 +202,18 @@
   RegexExplorer.h
   WarningMessagesDialog.h
   )
-qt5_add_resources(RC_SRCS CMakeSetup.qrc)
+set(QRC_SRCS CMakeSetup.qrc)
+
+if (INSTALLED_QT_VERSION VERSION_LESS 6)
+  qt5_wrap_ui(UI_BUILT_SRCS ${UI_SRCS})
+  qt5_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS})
+  qt5_add_resources(QRC_BUILT_SRCS ${QRC_SRCS})
+else()
+  qt_wrap_ui(UI_BUILT_SRCS ${UI_SRCS})
+  qt_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS})
+  qt_add_resources(QRC_BUILT_SRCS ${QRC_SRCS})
+endif()
+add_library(CMakeGUIQRCLib OBJECT ${QRC_BUILT_SRCS})
 
 if (FALSE) # CMake's bootstrap binary does not support automoc
   set(CMAKE_AUTOMOC 1)
@@ -136,9 +221,8 @@
   set(CMAKE_AUTOUIC 1)
 else ()
   list(APPEND SRCS
-    ${UI_SRCS}
-    ${MOC_SRCS}
-    ${RC_SRCS})
+    ${UI_BUILT_SRCS}
+    ${MOC_BUILT_SRCS})
 endif ()
 
 if(USE_LGPL)
@@ -153,14 +237,16 @@
 
 add_library(CMakeGUILib STATIC ${SRCS})
 # CMake_QT_EXTRA_LIBRARIES have to come before the main libraries on the link line
-target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES} Qt5::Core Qt5::Widgets)
+target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES}
+  Qt${INSTALLED_QT_VERSION}::Core Qt${INSTALLED_QT_VERSION}::Widgets)
 
 add_library(CMakeGUIMainLib STATIC CMakeSetup.cxx)
 target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib)
 
 add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE})
-target_link_libraries(cmake-gui CMakeGUIMainLib Qt5::Core)
+target_link_libraries(cmake-gui CMakeGUIMainLib Qt${INSTALLED_QT_VERSION}::Core)
 
+target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeGUIQRCLib>)
 if(WIN32)
   target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc)
 endif()
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index 861c6e3..5debdb8 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -7,7 +7,6 @@
 #include <QDir>
 #include <QLocale>
 #include <QString>
-#include <QTextCodec>
 #include <QTranslator>
 #include <QtPlugin>
 
@@ -122,9 +121,6 @@
 
   setlocale(LC_NUMERIC, "C");
 
-  QTextCodec* utf8_codec = QTextCodec::codecForName("UTF-8");
-  QTextCodec::setCodecForLocale(utf8_codec);
-
   // tell the cmake library where cmake is
   QDir cmExecDir(QApplication::applicationDirPath());
 #if defined(Q_OS_MAC)
diff --git a/Source/QtDialog/EnvironmentDialog.cxx b/Source/QtDialog/EnvironmentDialog.cxx
index 846456c..d4f978c 100644
--- a/Source/QtDialog/EnvironmentDialog.cxx
+++ b/Source/QtDialog/EnvironmentDialog.cxx
@@ -81,7 +81,11 @@
   auto* model = this->sourceModel();
   auto key =
     model->data(model->index(row, 0, parent), Qt::DisplayRole).toString();
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+  return key.contains(this->filterRegularExpression());
+#else
   return key.contains(this->filterRegExp());
+#endif
 }
 
 EnvironmentDialog::EnvironmentDialog(const QProcessEnvironment& environment,
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx
index 22f5be1..7c9032e 100644
--- a/Source/QtDialog/QCMakeCacheView.cxx
+++ b/Source/QtDialog/QCMakeCacheView.cxx
@@ -47,7 +47,11 @@
 
     // check all strings for a match
     foreach (QString const& str, strs) {
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
+      if (str.contains(this->filterRegularExpression())) {
+#else
       if (str.contains(this->filterRegExp())) {
+#endif
         return true;
       }
     }
diff --git a/Source/QtDialog/QCMakeWidgets.cxx b/Source/QtDialog/QCMakeWidgets.cxx
index e68faba..ca65d13 100644
--- a/Source/QtDialog/QCMakeWidgets.cxx
+++ b/Source/QtDialog/QCMakeWidgets.cxx
@@ -1,20 +1,23 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-// FIXME: Port to QFileSystemModel from the deprecated QDirModel.
-// Be sure completion works when incrementally editing existing paths.
 #define QT_DEPRECATED_WARNINGS_SINCE QT_VERSION_CHECK(5, 14, 0)
 
 #include "QCMakeWidgets.h"
 
 #include <utility>
 
-#include <QDirModel>
 #include <QFileDialog>
 #include <QFileInfo>
 #include <QResizeEvent>
 #include <QToolButton>
 
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#  include <QFileSystemModel>
+#else
+#  include <QDirModel>
+#endif
+
 QCMakeFileEditor::QCMakeFileEditor(QWidget* p, QString var)
   : QLineEdit(p)
   , Variable(std::move(var))
@@ -93,8 +96,30 @@
   }
 }
 
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+// use same QFileSystemModel for all completers
+static QFileSystemModel* fileDirModel()
+
+{
+  static QFileSystemModel* m = nullptr;
+  if (!m) {
+    m = new QFileSystemModel();
+  }
+  return m;
+}
+static QFileSystemModel* pathDirModel()
+{
+  static QFileSystemModel* m = nullptr;
+  if (!m) {
+    m = new QFileSystemModel();
+    m->setFilter(QDir::AllDirs | QDir::Drives | QDir::NoDotAndDotDot);
+  }
+  return m;
+}
+#else
 // use same QDirModel for all completers
 static QDirModel* fileDirModel()
+
 {
   static QDirModel* m = nullptr;
   if (!m) {
@@ -111,12 +136,19 @@
   }
   return m;
 }
+#endif
 
 QCMakeFileCompleter::QCMakeFileCompleter(QObject* o, bool dirs)
   : QCompleter(o)
 {
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+  QFileSystemModel* m = dirs ? pathDirModel() : fileDirModel();
+  this->setModel(m);
+  m->setRootPath(QString());
+#else
   QDirModel* m = dirs ? pathDirModel() : fileDirModel();
   this->setModel(m);
+#endif
 }
 
 QString QCMakeFileCompleter::pathFromIndex(const QModelIndex& idx) const
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 6507f38..6c1071d 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -2901,7 +2901,7 @@
 
 bool cmCTest::GetTestProgressOutput() const
 {
-  return this->Impl->TestProgressOutput;
+  return this->Impl->TestProgressOutput && !GetExtraVerbose();
 }
 
 bool cmCTest::GetVerbose() const
diff --git a/Source/cmCommandLineArgument.h b/Source/cmCommandLineArgument.h
new file mode 100644
index 0000000..cbedf0a
--- /dev/null
+++ b/Source/cmCommandLineArgument.h
@@ -0,0 +1,159 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+template <typename FunctionSignature>
+struct cmCommandLineArgument
+{
+  enum class Values
+  {
+    Zero,
+    One,
+    Two,
+    ZeroOrOne,
+    OneOrMore
+  };
+
+  std::string InvalidSyntaxMessage;
+  std::string InvalidValueMessage;
+  std::string Name;
+  Values Type;
+  std::function<FunctionSignature> StoreCall;
+
+  template <typename FunctionType>
+  cmCommandLineArgument(std::string n, Values t, FunctionType&& func)
+    : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
+    , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
+    , Name(std::move(n))
+    , Type(t)
+    , StoreCall(std::forward<FunctionType>(func))
+  {
+  }
+
+  template <typename FunctionType>
+  cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
+                        FunctionType&& func)
+    : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
+    , InvalidValueMessage(std::move(failedMsg))
+    , Name(std::move(n))
+    , Type(t)
+    , StoreCall(std::forward<FunctionType>(func))
+  {
+  }
+
+  bool matches(std::string const& input) const
+  {
+    return (this->Type == Values::Zero) ? (input == this->Name)
+                                        : cmHasPrefix(input, this->Name);
+  }
+
+  template <typename T, typename... CallState>
+  bool parse(std::string const& input, T& index,
+             std::vector<std::string> const& allArgs,
+             CallState&&... state) const
+  {
+    enum class ParseMode
+    {
+      Valid,
+      Invalid,
+      SyntaxError,
+      ValueError
+    };
+    ParseMode parseState = ParseMode::Valid;
+
+    if (this->Type == Values::Zero) {
+      if (input.size() == this->Name.size()) {
+        parseState =
+          this->StoreCall(std::string{}, std::forward<CallState>(state)...)
+          ? ParseMode::Valid
+          : ParseMode::Invalid;
+      } else {
+        parseState = ParseMode::SyntaxError;
+      }
+
+    } else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) {
+      if (input.size() == this->Name.size()) {
+        ++index;
+        if (index >= allArgs.size() || allArgs[index][0] == '-') {
+          if (this->Type == Values::ZeroOrOne) {
+            parseState =
+              this->StoreCall(std::string{}, std::forward<CallState>(state)...)
+              ? ParseMode::Valid
+              : ParseMode::Invalid;
+          } else {
+            parseState = ParseMode::ValueError;
+          }
+        } else {
+          parseState =
+            this->StoreCall(allArgs[index], std::forward<CallState>(state)...)
+            ? ParseMode::Valid
+            : ParseMode::Invalid;
+        }
+      } else {
+        // parse the string to get the value
+        auto possible_value = cm::string_view(input).substr(this->Name.size());
+        if (possible_value.empty()) {
+          parseState = ParseMode::SyntaxError;
+          parseState = ParseMode::ValueError;
+        } else if (possible_value[0] == '=') {
+          possible_value.remove_prefix(1);
+          if (possible_value.empty()) {
+            parseState = ParseMode::ValueError;
+          } else {
+            parseState = this->StoreCall(std::string(possible_value),
+                                         std::forward<CallState>(state)...)
+              ? ParseMode::Valid
+              : ParseMode::Invalid;
+          }
+        }
+        if (parseState == ParseMode::Valid) {
+          parseState = this->StoreCall(std::string(possible_value),
+                                       std::forward<CallState>(state)...)
+            ? ParseMode::Valid
+            : ParseMode::Invalid;
+        }
+      }
+    } else if (this->Type == Values::Two) {
+      if (input.size() == this->Name.size()) {
+        if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
+            allArgs[index + 2][0] == '-') {
+          parseState = ParseMode::ValueError;
+        } else {
+          index += 2;
+          parseState =
+            this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
+                            std::forward<CallState>(state)...)
+            ? ParseMode::Valid
+            : ParseMode::Invalid;
+        }
+      }
+    } else if (this->Type == Values::OneOrMore) {
+      if (input.size() == this->Name.size()) {
+        auto nextValueIndex = index + 1;
+        if (nextValueIndex >= allArgs.size() || allArgs[index + 1][0] == '-') {
+          parseState = ParseMode::ValueError;
+        } else {
+          std::string buffer = allArgs[nextValueIndex++];
+          while (nextValueIndex < allArgs.size() &&
+                 allArgs[nextValueIndex][0] != '-') {
+            buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]);
+          }
+          parseState =
+            this->StoreCall(buffer, std::forward<CallState>(state)...)
+            ? ParseMode::Valid
+            : ParseMode::Invalid;
+        }
+      }
+    }
+
+    if (parseState == ParseMode::SyntaxError) {
+      cmSystemTools::Error(this->InvalidSyntaxMessage);
+    } else if (parseState == ParseMode::ValueError) {
+      cmSystemTools::Error(this->InvalidValueMessage);
+    }
+    return (parseState == ParseMode::Valid);
+  }
+};
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index a853bb1..113751a 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -707,7 +707,7 @@
       break;
     }
     input.replace(pos, endPos - pos + 1, targetName);
-    lastPos = endPos;
+    lastPos = pos + targetName.size();
   }
 
   pos = 0;
diff --git a/Source/cmGccDepfileReader.cxx b/Source/cmGccDepfileReader.cxx
index 96a562e..8253375 100644
--- a/Source/cmGccDepfileReader.cxx
+++ b/Source/cmGccDepfileReader.cxx
@@ -12,40 +12,33 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath)
-{
-  cmGccDepfileLexerHelper helper;
-  if (helper.readFile(filePath)) {
-    return cm::make_optional(std::move(helper).extractContent());
-  }
-  return cm::nullopt;
-}
-
 cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
                                                    const std::string& prefix)
 {
-  auto deps = cmReadGccDepfile(filePath);
-
-  if (prefix.empty() || !deps) {
-    return deps;
+  cmGccDepfileLexerHelper helper;
+  if (!helper.readFile(filePath)) {
+    return cm::nullopt;
   }
+  auto deps = cm::make_optional(std::move(helper).extractContent());
 
   for (auto& dep : *deps) {
     for (auto& rule : dep.rules) {
-      if (!cmSystemTools::FileIsFullPath(rule)) {
+      if (!prefix.empty() && !cmSystemTools::FileIsFullPath(rule)) {
         rule = cmStrCat(prefix, rule);
       }
       if (cmSystemTools::FileIsFullPath(rule)) {
         rule = cmSystemTools::CollapseFullPath(rule);
       }
+      cmSystemTools::ConvertToLongPath(rule);
     }
     for (auto& path : dep.paths) {
-      if (!cmSystemTools::FileIsFullPath(path)) {
+      if (!prefix.empty() && !cmSystemTools::FileIsFullPath(path)) {
         path = cmStrCat(prefix, path);
       }
       if (cmSystemTools::FileIsFullPath(path)) {
         path = cmSystemTools::CollapseFullPath(path);
       }
+      cmSystemTools::ConvertToLongPath(path);
     }
   }
 
diff --git a/Source/cmGccDepfileReader.h b/Source/cmGccDepfileReader.h
index 66ff75d..c8a3748 100644
--- a/Source/cmGccDepfileReader.h
+++ b/Source/cmGccDepfileReader.h
@@ -8,10 +8,8 @@
 
 #include "cmGccDepfileReaderTypes.h"
 
-cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath);
-
 /*
  * Read dependencies file and append prefix to all relative paths
  */
-cm::optional<cmGccDepfileContent> cmReadGccDepfile(const char* filePath,
-                                                   const std::string& prefix);
+cm::optional<cmGccDepfileContent> cmReadGccDepfile(
+  const char* filePath, const std::string& prefix = {});
diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx
index c4bec23..36f583f 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.cxx
+++ b/Source/cmGlobalNMakeMakefileGenerator.cxx
@@ -21,6 +21,8 @@
   this->PassMakeflags = true;
   this->UnixCD = false;
   this->MakeSilentFlag = "/nologo";
+  // nmake breaks on '!' in long-line dependencies
+  this->ToolSupportsLongLineDependencies = false;
 }
 
 void cmGlobalNMakeMakefileGenerator::EnableLanguage(
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index a95ab25..4f17408 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -555,6 +555,7 @@
   this->TargetAll = this->NinjaOutputPath("all");
   this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
   this->DisableCleandead = false;
+  this->DiagnosedCxxModuleSupport = false;
 
   this->PolicyCMP0058 =
     this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
@@ -755,6 +756,37 @@
   return true;
 }
 
+bool cmGlobalNinjaGenerator::CheckCxxModuleSupport()
+{
+  bool const diagnose = !this->DiagnosedCxxModuleSupport &&
+    !this->CMakeInstance->GetIsInTryCompile();
+  if (diagnose) {
+    this->DiagnosedCxxModuleSupport = true;
+    this->GetCMakeInstance()->IssueMessage(
+      MessageType::AUTHOR_WARNING,
+      "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP "
+      "is experimental.  It is meant only for compiler developers to try.");
+  }
+  if (this->NinjaSupportsDyndeps) {
+    return true;
+  }
+  if (diagnose) {
+    std::ostringstream e;
+    /* clang-format off */
+    e <<
+      "The Ninja generator does not support C++20 modules "
+      "using Ninja version \n"
+      "  " << this->NinjaVersion << "\n"
+      "due to lack of required features.  "
+      "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
+      ;
+    /* clang-format on */
+    this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    cmSystemTools::SetFatalErrorOccured();
+  }
+  return false;
+}
+
 bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const
 {
   if (this->NinjaSupportsDyndeps) {
@@ -766,7 +798,8 @@
   e <<
     "The Ninja generator does not support Fortran using Ninja version\n"
     "  " << this->NinjaVersion << "\n"
-    "due to lack of required features.  Ninja 1.10 or higher is required."
+    "due to lack of required features.  "
+    "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
     ;
   /* clang-format on */
   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -785,7 +818,9 @@
   e <<
     "The Ninja generator does not support ISPC using Ninja version\n"
     "  " << this->NinjaVersion << "\n"
-    "due to lack of required features.  Ninja 1.10 or higher is required."
+    "due to lack of required features.  "
+    "Ninja " << RequiredNinjaVersionForMultipleOutputs() <<
+    " or higher is required."
     ;
   /* clang-format on */
   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -2336,7 +2371,7 @@
   std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
   std::string const& module_dir,
   std::vector<std::string> const& linked_target_dirs,
-  std::string const& arg_lang)
+  std::string const& arg_lang, std::string const& arg_modmapfmt)
 {
   // Setup path conversions.
   {
@@ -2423,6 +2458,48 @@
         build.Variables.emplace("restat", "1");
       }
 
+      if (arg_modmapfmt.empty()) {
+        // nothing to do.
+      } else {
+        std::stringstream mm;
+        if (arg_modmapfmt == "gcc") {
+          // Documented in GCC's documentation. The format is a series of lines
+          // with a module name and the associated filename separated by
+          // spaces. The first line may use `$root` as the module name to
+          // specify a "repository root". That is used to anchor any relative
+          // paths present in the file (CMake should never generate any).
+
+          // Write the root directory to use for module paths.
+          mm << "$root .\n";
+
+          for (auto const& l : object.Provides) {
+            auto m = mod_files.find(l.LogicalName);
+            if (m != mod_files.end()) {
+              mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second)
+                 << "\n";
+            }
+          }
+          for (auto const& r : object.Requires) {
+            auto m = mod_files.find(r.LogicalName);
+            if (m != mod_files.end()) {
+              mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second)
+                 << "\n";
+            }
+          }
+        } else {
+          cmSystemTools::Error(
+            cmStrCat("-E cmake_ninja_dyndep does not understand the ",
+                     arg_modmapfmt, " module map format"));
+          return false;
+        }
+
+        // XXX(modmap): If changing this path construction, change
+        // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the
+        // corresponding file path.
+        cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
+        mmf << mm.str();
+      }
+
       this->WriteBuild(ddf, build);
     }
   }
@@ -2446,6 +2523,7 @@
   std::string arg_dd;
   std::string arg_lang;
   std::string arg_tdi;
+  std::string arg_modmapfmt;
   std::vector<std::string> arg_ddis;
   for (std::string const& arg : arg_full) {
     if (cmHasLiteralPrefix(arg, "--tdi=")) {
@@ -2454,6 +2532,8 @@
       arg_lang = arg.substr(7);
     } else if (cmHasLiteralPrefix(arg, "--dd=")) {
       arg_dd = arg.substr(5);
+    } else if (cmHasLiteralPrefix(arg, "--modmapfmt=")) {
+      arg_modmapfmt = arg.substr(12);
     } else if (!cmHasLiteralPrefix(arg, "--") &&
                cmHasLiteralSuffix(arg, ".ddi")) {
       arg_ddis.push_back(arg);
@@ -2512,7 +2592,7 @@
   if (!ggd ||
       !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile(
         dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis,
-        module_dir, linked_target_dirs, arg_lang)) {
+        module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) {
     return 1;
   }
   return 0;
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 0e94678..fd8542f 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -396,15 +396,13 @@
   bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
   void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
 
-  bool WriteDyndepFile(std::string const& dir_top_src,
-                       std::string const& dir_top_bld,
-                       std::string const& dir_cur_src,
-                       std::string const& dir_cur_bld,
-                       std::string const& arg_dd,
-                       std::vector<std::string> const& arg_ddis,
-                       std::string const& module_dir,
-                       std::vector<std::string> const& linked_target_dirs,
-                       std::string const& arg_lang);
+  bool WriteDyndepFile(
+    std::string const& dir_top_src, std::string const& dir_top_bld,
+    std::string const& dir_cur_src, std::string const& dir_cur_bld,
+    std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
+    std::string const& module_dir,
+    std::vector<std::string> const& linked_target_dirs,
+    std::string const& arg_lang, std::string const& arg_modmapfmt);
 
   virtual std::string BuildAlias(const std::string& alias,
                                  const std::string& /*config*/) const
@@ -448,6 +446,8 @@
 
   bool IsSingleConfigUtility(cmGeneratorTarget const* target) const;
 
+  bool CheckCxxModuleSupport();
+
 protected:
   void Generate() override;
 
@@ -568,6 +568,8 @@
   bool NinjaSupportsMultipleOutputs = false;
   bool NinjaSupportsMetadataOnRegeneration = false;
 
+  bool DiagnosedCxxModuleSupport = false;
+
 private:
   void InitOutputPathPrefix();
 
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index d59d382..8a1ccd1 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -18,6 +18,7 @@
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmComputeLinkInformation.h"
+#include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
@@ -798,9 +799,10 @@
 }
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(
-  cmXCodeObject::PBXType ptype)
+  cmXCodeObject::PBXType ptype, cm::string_view key)
 {
-  auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT);
+  auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT,
+                                              this->GetObjectId(ptype, key));
   auto ptr = obj.get();
   this->addObject(std::move(obj));
   return ptr;
@@ -808,7 +810,9 @@
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
 {
-  auto obj = cm::make_unique<cmXCodeObject>(cmXCodeObject::None, type);
+  auto obj = cm::make_unique<cmXCodeObject>(
+    cmXCodeObject::None, type,
+    "Temporary cmake object, should not be referred to in Xcode file");
   auto ptr = obj.get();
   this->addObject(std::move(obj));
   return ptr;
@@ -1739,13 +1743,13 @@
   if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
     // create prebuild phase
     preBuildPhase =
-      this->CreateRunScriptBuildPhase("CMake PreBuild Rules", prebuild);
+      this->CreateRunScriptBuildPhase("CMake PreBuild Rules", gtgt, prebuild);
     // create prelink phase
     preLinkPhase =
-      this->CreateRunScriptBuildPhase("CMake PreLink Rules", prelink);
+      this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink);
     // create postbuild phase
-    postBuildPhase =
-      this->CreateRunScriptBuildPhase("CMake PostBuild Rules", postbuild);
+    postBuildPhase = this->CreateRunScriptBuildPhase("CMake PostBuild Rules",
+                                                     gtgt, postbuild);
   } else {
     std::vector<cmSourceFile*> classes;
     if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) {
@@ -1877,7 +1881,8 @@
   }
 
   cmXCodeObject* buildPhase =
-    this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
+    this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
+                       cmStrCat(gt->GetName(), ':', sf->GetFullPath()));
   buildPhase->AddAttribute("buildActionMask",
                            this->CreateString("2147483647"));
   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
@@ -1936,7 +1941,8 @@
 }
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
-  std::string const& name, std::vector<cmCustomCommand> const& commands)
+  std::string const& name, cmGeneratorTarget const* gt,
+  std::vector<cmCustomCommand> const& commands)
 {
   if (commands.empty()) {
     return nullptr;
@@ -1959,7 +1965,8 @@
   }
 
   cmXCodeObject* buildPhase =
-    this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
+    this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
+                       cmStrCat(gt->GetName(), ':', name));
   buildPhase->AddAttribute("buildActionMask",
                            this->CreateString("2147483647"));
   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
@@ -2927,8 +2934,8 @@
 cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(
   cmGeneratorTarget* gtgt)
 {
-  cmXCodeObject* shellBuildPhase =
-    this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
+  cmXCodeObject* shellBuildPhase = this->CreateObject(
+    cmXCodeObject::PBXShellScriptBuildPhase, gtgt->GetName());
   shellBuildPhase->AddAttribute("buildActionMask",
                                 this->CreateString("2147483647"));
   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
@@ -3156,6 +3163,32 @@
   return i->second;
 }
 
+std::string cmGlobalXCodeGenerator::GetObjectId(cmXCodeObject::PBXType ptype,
+                                                cm::string_view key)
+{
+  std::string objectId;
+  if (!key.empty()) {
+    cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
+    hash.Initialize();
+    hash.Append(&ptype, sizeof(ptype));
+    hash.Append(key);
+    objectId = cmSystemTools::UpperCase(hash.FinalizeHex().substr(0, 24));
+  } else {
+    char cUuid[40] = { 0 };
+    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+    CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid);
+    CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8);
+    objectId = cUuid;
+    CFRelease(s);
+    CFRelease(uuid);
+    cmSystemTools::ReplaceString(objectId, "-", "");
+    if (objectId.size() > 24) {
+      objectId = objectId.substr(0, 24);
+    }
+  }
+  return objectId;
+}
+
 std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name,
                                                   const std::string& id)
 {
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 14db1dc..1ab56e2 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -11,6 +11,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmGlobalGenerator.h"
 #include "cmXCodeObject.h"
 
@@ -162,11 +164,13 @@
                                  const std::string& configName);
 
   cmXCodeObject* FindXCodeTarget(const cmGeneratorTarget*);
+  std::string GetObjectId(cmXCodeObject::PBXType ptype, cm::string_view key);
   std::string GetOrCreateId(const std::string& name, const std::string& id);
 
   // create cmXCodeObject from these functions so that memory can be managed
   // correctly.  All objects created are stored in this->XCodeObjects.
-  cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype);
+  cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype,
+                              cm::string_view key = {});
   cmXCodeObject* CreateObject(cmXCodeObject::Type type);
   cmXCodeObject* CreateString(const std::string& s);
   cmXCodeObject* CreateObjectReference(cmXCodeObject*);
@@ -256,7 +260,8 @@
                                            cmGeneratorTarget const* gt,
                                            cmCustomCommand const& cc);
   cmXCodeObject* CreateRunScriptBuildPhase(
-    std::string const& name, std::vector<cmCustomCommand> const& commands);
+    std::string const& name, cmGeneratorTarget const* gt,
+    std::vector<cmCustomCommand> const& commands);
   std::string ConstructScript(cmCustomCommandGenerator const& ccg);
   void CreateReRunCMakeFile(cmLocalGenerator* root,
                             std::vector<cmLocalGenerator*> const& gens);
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx
index c45000d..0a353e4 100644
--- a/Source/cmInstallFilesGenerator.cxx
+++ b/Source/cmInstallFilesGenerator.cxx
@@ -25,10 +25,14 @@
   , Programs(programs)
   , Optional(optional)
 {
-  // We need per-config actions if the destination has generator expressions.
+  // We need per-config actions if the destination and rename have generator
+  // expressions.
   if (cmGeneratorExpression::Find(this->Destination) != std::string::npos) {
     this->ActionsPerConfig = true;
   }
+  if (cmGeneratorExpression::Find(this->Rename) != std::string::npos) {
+    this->ActionsPerConfig = true;
+  }
 
   // We need per-config actions if any directories have generator expressions.
   if (!this->ActionsPerConfig) {
@@ -56,6 +60,12 @@
                                          this->LocalGenerator, config);
 }
 
+std::string cmInstallFilesGenerator::GetRename(std::string const& config) const
+{
+  return cmGeneratorExpression::Evaluate(this->Rename, this->LocalGenerator,
+                                         config);
+}
+
 void cmInstallFilesGenerator::AddFilesInstallRule(
   std::ostream& os, std::string const& config, Indent indent,
   std::vector<std::string> const& files)
@@ -66,7 +76,7 @@
     os, this->GetDestination(config),
     (this->Programs ? cmInstallType_PROGRAMS : cmInstallType_FILES), files,
     this->Optional, this->FilePermissions.c_str(), no_dir_permissions,
-    this->Rename.c_str(), nullptr, indent);
+    this->GetRename(config).c_str(), nullptr, indent);
 }
 
 void cmInstallFilesGenerator::GenerateScriptActions(std::ostream& os,
diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h
index b5a1ef4..66145f4 100644
--- a/Source/cmInstallFilesGenerator.h
+++ b/Source/cmInstallFilesGenerator.h
@@ -31,6 +31,7 @@
   bool Compute(cmLocalGenerator* lg) override;
 
   std::string GetDestination(std::string const& config) const;
+  std::string GetRename(std::string const& config) const;
 
 protected:
   void GenerateScriptActions(std::ostream& os, Indent indent) override;
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 53c871b..fdddb45 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -422,9 +422,10 @@
 bool HandleRemoveItemCommand(std::vector<std::string> const& args,
                              cmExecutionStatus& status)
 {
-  if (args.size() < 3) {
-    status.SetError("sub-command REMOVE_ITEM requires two or more arguments.");
-    return false;
+  assert(args.size() >= 2);
+
+  if (args.size() == 2) {
+    return true;
   }
 
   const std::string& listName = args[1];
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 5a747e5..7229101 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -693,13 +693,11 @@
   }
 }
 
-namespace {
-bool HasUniqueByproducts(cmLocalGenerator& lg,
-                         std::vector<std::string> const& byproducts,
-                         cmListFileBacktrace const& bt)
+bool cmLocalNinjaGenerator::HasUniqueByproducts(
+  std::vector<std::string> const& byproducts, cmListFileBacktrace const& bt)
 {
   std::vector<std::string> configs =
-    lg.GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+    this->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
   cmGeneratorExpression ge(bt);
   for (std::string const& p : byproducts) {
     if (cmGeneratorExpression::Find(p) == std::string::npos) {
@@ -709,7 +707,7 @@
     std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p);
     for (std::string const& config : configs) {
       for (std::string const& b :
-           lg.ExpandCustomCommandOutputPaths(*cge, config)) {
+           this->ExpandCustomCommandOutputPaths(*cge, config)) {
         if (!seen.insert(b).second) {
           return false;
         }
@@ -719,6 +717,7 @@
   return true;
 }
 
+namespace {
 bool HasUniqueOutputs(std::vector<cmCustomCommandGenerator> const& ccgs)
 {
   std::set<std::string> allOutputs;
@@ -746,7 +745,7 @@
   // In Ninja Multi-Config, we can only produce cross-config utility
   // commands if all byproducts are per-config.
   if (!this->GetGlobalGenerator()->IsMultiConfig() ||
-      !HasUniqueByproducts(*this, byproducts, bt)) {
+      !this->HasUniqueByproducts(byproducts, bt)) {
     return this->cmLocalGenerator::CreateUtilityOutput(targetName, byproducts,
                                                        bt);
   }
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
index 87d5e53..5b850f3 100644
--- a/Source/cmLocalNinjaGenerator.h
+++ b/Source/cmLocalNinjaGenerator.h
@@ -86,6 +86,9 @@
                                cmNinjaDeps& ninjaDeps,
                                const std::string& config);
 
+  bool HasUniqueByproducts(std::vector<std::string> const& byproducts,
+                           cmListFileBacktrace const& bt);
+
 protected:
   std::string ConvertToIncludeReference(
     std::string const& path,
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 0faef15..cea20ad 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -303,7 +303,7 @@
 
   args.reserve(lff.Arguments().size());
   for (cmListFileArgument const& arg : lff.Arguments()) {
-    if (expand) {
+    if (expand && arg.Delim != cmListFileArgument::Bracket) {
       temp = arg.Value;
       this->ExpandVariablesInString(temp);
       args.push_back(temp);
@@ -1216,9 +1216,10 @@
   };
 
   // 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)$");
+  cmsys::RegularExpression sourceFiles(
+    "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|cu|m|mm|"
+    "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
+    "hm|hpp|hxx|in|txx|inl)$");
 
   // Choose whether to use a main dependency.
   if (sourceFiles.find(source)) {
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 0f91d6d..adf40b0 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -345,27 +345,51 @@
              this->LocalGenerator->GetBinaryDirectory(), compilerDependFile))
       << "\n\n";
 
-    if (!cmSystemTools::FileExists(compilerDependFile)) {
-      // Write an empty dependency file.
-      cmGeneratedFileStream depFileStream(
-        compilerDependFile, false,
-        this->GlobalGenerator->GetMakefileEncoding());
-      depFileStream << "# Empty compiler generated dependencies file for "
-                    << this->GeneratorTarget->GetName() << ".\n"
-                    << "# This may be replaced when dependencies are built.\n";
-    }
+    // Write an empty dependency file.
+    cmGeneratedFileStream depFileStream(
+      compilerDependFile, false, this->GlobalGenerator->GetMakefileEncoding());
+    depFileStream << "# Empty compiler generated dependencies file for "
+                  << this->GeneratorTarget->GetName() << ".\n"
+                  << "# This may be replaced when dependencies are built.\n";
+    // remove internal dependency file
+    cmSystemTools::RemoveFile(
+      cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.internal"));
 
     std::string compilerDependTimestamp =
       cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts");
     if (!cmSystemTools::FileExists(compilerDependTimestamp)) {
       // Write a dependency timestamp file.
-      cmGeneratedFileStream depFileStream(
+      cmGeneratedFileStream timestampFileStream(
         compilerDependTimestamp, false,
         this->GlobalGenerator->GetMakefileEncoding());
-      depFileStream << "# CMAKE generated file: DO NOT EDIT!\n"
-                    << "# Timestamp file for compiler generated dependencies "
-                       "management for "
-                    << this->GeneratorTarget->GetName() << ".\n";
+      timestampFileStream
+        << "# CMAKE generated file: DO NOT EDIT!\n"
+        << "# Timestamp file for compiler generated dependencies "
+           "management for "
+        << this->GeneratorTarget->GetName() << ".\n";
+    }
+
+    // deactivate no longer needed legacy dependency files
+    // Write an empty dependency file.
+    cmGeneratedFileStream legacyDepFileStream(
+      dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
+    legacyDepFileStream
+      << "# Empty dependencies file for " << this->GeneratorTarget->GetName()
+      << ".\n"
+      << "# This may be replaced when dependencies are built.\n";
+    // remove internal dependency file
+    cmSystemTools::RemoveFile(
+      cmStrCat(this->TargetBuildDirectoryFull, "/depend.internal"));
+  } else {
+    // make sure the depend file exists
+    if (!cmSystemTools::FileExists(dependFileNameFull)) {
+      // Write an empty dependency file.
+      cmGeneratedFileStream depFileStream(
+        dependFileNameFull, false,
+        this->GlobalGenerator->GetMakefileEncoding());
+      depFileStream << "# Empty dependencies file for "
+                    << this->GeneratorTarget->GetName() << ".\n"
+                    << "# This may be replaced when dependencies are built.\n";
     }
   }
 
@@ -381,16 +405,6 @@
       << "\n\n";
   }
 
-  // make sure the depend file exists
-  if (!cmSystemTools::FileExists(dependFileNameFull)) {
-    // Write an empty dependency file.
-    cmGeneratedFileStream depFileStream(
-      dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
-    depFileStream << "# Empty dependencies file for "
-                  << this->GeneratorTarget->GetName() << ".\n"
-                  << "# This may be replaced when dependencies are built.\n";
-  }
-
   // Open the flags file.  This should be copy-if-different because the
   // rules may depend on this file itself.
   this->FlagFileNameFull =
@@ -855,6 +869,7 @@
     shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat(
       depFile, cmOutputConverter::SHELL);
     vars.DependencyFile = shellDependencyFile.c_str();
+    this->CleanFiles.insert(depFile);
 
     dependencyTimestamp = this->LocalGenerator->MaybeConvertToRelativePath(
       this->LocalGenerator->GetBinaryDirectory(),
@@ -898,7 +913,7 @@
       cmExpandList(compileRule, compileCommands);
     }
 
-    if (this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS") &&
+    if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") &&
         lang_can_export_cmds && compileCommands.size() == 1) {
       std::string compileCommand = compileCommands[0];
 
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 620b8ff..49e5e4c 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/vector>
 
 #include "cmComputeLinkInformation.h"
@@ -1238,14 +1239,17 @@
   std::vector<std::string> preLinkCmdLines;
   std::vector<std::string> postBuildCmdLines;
 
-  if (config == fileConfig) {
-    std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
-                                                  &preLinkCmdLines,
-                                                  &postBuildCmdLines };
+  std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
+                                                &preLinkCmdLines,
+                                                &postBuildCmdLines };
 
-    for (unsigned i = 0; i != 3; ++i) {
-      for (cmCustomCommand const& cc : *cmdLists[i]) {
-        cmCustomCommandGenerator ccg(cc, config, this->GetLocalGenerator());
+  for (unsigned i = 0; i != 3; ++i) {
+    for (cmCustomCommand const& cc : *cmdLists[i]) {
+      if (config == fileConfig ||
+          this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(),
+                                                         cc.GetBacktrace())) {
+        cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(),
+                                     true, config);
         localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
         std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
         std::transform(ccByproducts.begin(), ccByproducts.end(),
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 18c9d82..672b579 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -35,6 +35,7 @@
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
+#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -146,9 +147,26 @@
     '_', config);
 }
 
-bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang) const
+bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
+  std::string const& lang, std::string const& config) const
 {
-  return lang == "Fortran";
+  if (lang != "CXX") {
+    return false;
+  }
+  if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
+    return false;
+  }
+  cmGeneratorTarget const* tgt = this->GetGeneratorTarget();
+  cmStandardLevelResolver standardResolver(this->Makefile);
+  bool const uses_cxx20 =
+    standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20");
+  return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport();
+}
+
+bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
+                                        std::string const& config) const
+{
+  return lang == "Fortran" || this->NeedCxxModuleSupport(lang, config);
 }
 
 std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
@@ -530,8 +548,9 @@
   scanVars.CMTargetName = vars.CMTargetName;
   scanVars.CMTargetType = vars.CMTargetType;
   scanVars.Language = vars.Language;
-  scanVars.Object = "$out"; // for RULE_LAUNCH_COMPILE
+  scanVars.Object = "$OBJ_FILE";
   scanVars.PreprocessedSource = "$out";
+  scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE";
   scanVars.DependencyFile = rule.DepFile.c_str();
   scanVars.DependencyTarget = "$out";
 
@@ -586,7 +605,7 @@
   cmMakefile* mf = this->GetMakefile();
 
   // For some cases we scan to dynamically discover dependencies.
-  bool const needDyndep = this->NeedDyndep(lang);
+  bool const needDyndep = this->NeedDyndep(lang, config);
   bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang);
 
   std::string flags = "$FLAGS";
@@ -601,6 +620,10 @@
       responseFlag = "@";
     }
   }
+  std::string const modmapFormatVar =
+    cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FORMAT");
+  std::string const modmapFormat =
+    this->Makefile->GetSafeDefinition(modmapFormatVar);
 
   std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
     this->GetLocalGenerator()->CreateRulePlaceholderExpander());
@@ -624,16 +647,26 @@
     // Rule to scan dependencies of sources that need preprocessing.
     {
       std::vector<std::string> scanCommands;
-      std::string const& scanRuleName =
-        this->LanguagePreprocessAndScanRule(lang, config);
-      std::string const& ppCommmand = mf->GetRequiredDefinition(
-        cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
-      cmExpandList(ppCommmand, scanCommands);
-      for (std::string& i : scanCommands) {
-        i = cmStrCat(launcher, i);
+      std::string scanRuleName;
+      if (compilationPreprocesses) {
+        scanRuleName = this->LanguageScanRule(lang, config);
+        std::string const& scanCommand = mf->GetRequiredDefinition(
+          cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_SOURCE"));
+        cmExpandList(scanCommand, scanCommands);
+        for (std::string& i : scanCommands) {
+          i = cmStrCat(launcher, i);
+        }
+      } else {
+        scanRuleName = this->LanguagePreprocessAndScanRule(lang, config);
+        std::string const& ppCommmand = mf->GetRequiredDefinition(
+          cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
+        cmExpandList(ppCommmand, scanCommands);
+        for (std::string& i : scanCommands) {
+          i = cmStrCat(launcher, i);
+        }
+        scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
+                                                 "$DYNDEP_INTERMEDIATE_FILE"));
       }
-      scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
-                                               "$DYNDEP_INTERMEDIATE_FILE"));
 
       auto scanRule = GetScanRule(
         scanRuleName, vars, responseFlag, flags, rulePlaceholderExpander.get(),
@@ -641,12 +674,18 @@
 
       scanRule.Comment =
         cmStrCat("Rule for generating ", lang, " dependencies.");
-      scanRule.Description = cmStrCat("Building ", lang, " preprocessed $out");
+      if (compilationPreprocesses) {
+        scanRule.Description =
+          cmStrCat("Scanning $in for ", lang, " dependencies");
+      } else {
+        scanRule.Description =
+          cmStrCat("Building ", lang, " preprocessed $out");
+      }
 
       this->GetGlobalGenerator()->AddRule(scanRule);
     }
 
-    {
+    if (!compilationPreprocesses) {
       // Compilation will not preprocess, so it does not need the defines
       // unless the compiler wants them for some other purpose.
       if (!this->CompileWithDefines(lang)) {
@@ -681,12 +720,16 @@
 
     // Run CMake dependency scanner on the source file (using the preprocessed
     // source if that was performed).
+    std::string ddModmapArg;
+    if (!modmapFormat.empty()) {
+      ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat);
+    }
     {
       std::vector<std::string> ddCmds;
       {
-        std::string ccmd =
-          cmStrCat(cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi,
-                   " --lang=", lang, " --dd=$out @", rule.RspFile);
+        std::string ccmd = cmStrCat(
+          cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang,
+          ddModmapArg, " --dd=$out @", rule.RspFile);
         ddCmds.emplace_back(std::move(ccmd));
       }
       rule.Command = this->GetLocalGenerator()->BuildCommandLine(ddCmds);
@@ -748,6 +791,14 @@
     }
   }
 
+  if (needDyndep && !modmapFormat.empty()) {
+    std::string modmapFlags = mf->GetRequiredDefinition(
+      cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG"));
+    cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
+                                 "$DYNDEP_MODULE_MAP_FILE");
+    flags += cmStrCat(' ', modmapFlags);
+  }
+
   vars.Flags = flags.c_str();
   vars.DependencyFile = rule.DepFile.c_str();
 
@@ -1053,6 +1104,7 @@
                                    const std::string& ppFileName,
                                    bool compilePP, bool compilePPWithDefines,
                                    cmNinjaBuild& objBuild, cmNinjaVars& vars,
+                                   std::string const& modmapFormat,
                                    const std::string& objectFileName,
                                    cmLocalGenerator* lg)
 {
@@ -1123,6 +1175,15 @@
     vars.erase("DEP_FILE");
   }
 
+  if (!modmapFormat.empty()) {
+    // XXX(modmap): If changing this path construction, change
+    // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding
+    // file path.
+    std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
+    scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
+    scanBuild.ImplicitOuts.push_back(ddModmapFile);
+  }
+
   return scanBuild;
 }
 }
@@ -1262,10 +1323,17 @@
   }
 
   // For some cases we scan to dynamically discover dependencies.
-  bool const needDyndep = this->NeedDyndep(language);
+  bool const needDyndep = this->NeedDyndep(language, config);
   bool const compilationPreprocesses =
     !this->NeedExplicitPreprocessing(language);
 
+  std::string modmapFormat;
+  if (needDyndep) {
+    std::string const modmapFormatVar =
+      cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT");
+    modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
+  }
+
   if (needDyndep) {
     // If source/target has preprocessing turned off, we still need to
     // generate an explicit dependency step
@@ -1295,7 +1363,7 @@
 
     cmNinjaBuild ppBuild = GetScanBuildStatement(
       scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild,
-      vars, objectFileName, this->LocalGenerator);
+      vars, modmapFormat, objectFileName, this->LocalGenerator);
 
     if (compilePP) {
       // In case compilation requires flags that are incompatible with
@@ -1331,6 +1399,12 @@
     std::string const dyndep = this->GetDyndepFilePath(language, config);
     objBuild.OrderOnlyDeps.push_back(dyndep);
     vars["dyndep"] = dyndep;
+
+    if (!modmapFormat.empty()) {
+      std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
+      vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
+      objBuild.OrderOnlyDeps.push_back(ddModmapFile);
+    }
   }
 
   this->EnsureParentDirectoryExists(objectFileName);
@@ -1444,17 +1518,26 @@
   tdi["compiler-id"] = this->Makefile->GetSafeDefinition(
     cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
 
+  std::string mod_dir;
   if (lang == "Fortran") {
-    std::string mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
+    mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
       this->Makefile->GetHomeOutputDirectory());
-    if (mod_dir.empty()) {
-      mod_dir = this->Makefile->GetCurrentBinaryDirectory();
-    }
-    tdi["module-dir"] = mod_dir;
+  } else if (lang == "CXX") {
+    mod_dir =
+      cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory);
+  }
+  if (mod_dir.empty()) {
+    mod_dir = this->Makefile->GetCurrentBinaryDirectory();
+  }
+  tdi["module-dir"] = mod_dir;
+
+  if (lang == "Fortran") {
     tdi["submodule-sep"] =
       this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
     tdi["submodule-ext"] =
       this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
+  } else if (lang == "CXX") {
+    // No extra information necessary.
   }
 
   tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory();
@@ -1536,7 +1619,7 @@
   std::string const& objectFileDir, std::string const& flags,
   std::string const& defines, std::string const& includes)
 {
-  if (!this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS")) {
+  if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
     return;
   }
 
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 83a4342..79dc622 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -71,9 +71,11 @@
                                const std::string& config) const;
   std::string LanguageDyndepRule(std::string const& lang,
                                  const std::string& config) const;
-  bool NeedDyndep(std::string const& lang) const;
+  bool NeedDyndep(std::string const& lang, std::string const& config) const;
   bool NeedExplicitPreprocessing(std::string const& lang) const;
   bool CompileWithDefines(std::string const& lang) const;
+  bool NeedCxxModuleSupport(std::string const& lang,
+                            std::string const& config) const;
 
   std::string OrderDependsTargetForTarget(const std::string& config);
 
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 7365fdb..3556051 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -522,6 +522,7 @@
   class JobDepFilesMergeT : public JobFenceT
   {
   private:
+    std::vector<std::string> initialDependencies() const;
     void Process() override;
   };
 
@@ -2198,6 +2199,29 @@
   return escapedPath;
 }
 
+/*
+ * Return the initial dependencies of the merged depfile.
+ * Those are dependencies from the project files, not from moc runs.
+ */
+std::vector<std::string>
+cmQtAutoMocUicT::JobDepFilesMergeT::initialDependencies() const
+{
+  std::vector<std::string> dependencies;
+  dependencies.reserve(this->BaseConst().ListFiles.size() +
+                       this->BaseEval().Headers.size() +
+                       this->BaseEval().Sources.size());
+  cm::append(dependencies, this->BaseConst().ListFiles);
+  auto append_file_path =
+    [&dependencies](const SourceFileMapT::value_type& p) {
+      dependencies.push_back(p.first);
+    };
+  std::for_each(this->BaseEval().Headers.begin(),
+                this->BaseEval().Headers.end(), append_file_path);
+  std::for_each(this->BaseEval().Sources.begin(),
+                this->BaseEval().Sources.end(), append_file_path);
+  return dependencies;
+}
+
 void cmQtAutoMocUicT::JobDepFilesMergeT::Process()
 {
   if (this->Log().Verbose()) {
@@ -2215,7 +2239,7 @@
     return dependenciesFromDepFile(f.c_str());
   };
 
-  std::vector<std::string> dependencies = this->BaseConst().ListFiles;
+  std::vector<std::string> dependencies = this->initialDependencies();
   ParseCacheT& parseCache = this->BaseEval().ParseCache;
   auto processMappingEntry = [&](const MappingMapT::value_type& m) {
     auto cacheEntry = parseCache.GetOrInsert(m.first);
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 5363fef..d00bbdf 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -44,6 +44,11 @@
       return replaceValues.Source;
     }
   }
+  if (replaceValues.DynDepFile) {
+    if (variable == "DYNDEP_FILE") {
+      return replaceValues.DynDepFile;
+    }
+  }
   if (replaceValues.PreprocessedSource) {
     if (variable == "PREPROCESSED_SOURCE") {
       return replaceValues.PreprocessedSource;
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 710f8a6..f8dc368 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -41,6 +41,7 @@
     const char* Source = nullptr;
     const char* AssemblySource = nullptr;
     const char* PreprocessedSource = nullptr;
+    const char* DynDepFile = nullptr;
     const char* Output = nullptr;
     const char* Object = nullptr;
     const char* ObjectDir = nullptr;
diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx
index 40bf4c9..e046069 100644
--- a/Source/cmScanDepFormat.cxx
+++ b/Source/cmScanDepFormat.cxx
@@ -55,9 +55,8 @@
 #define PARSE_BLOB(val, res)                                                  \
   do {                                                                        \
     if (!ParseFilename(val, res)) {                                           \
-      cmSystemTools::Error(                                                   \
-        cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,           \
-                 ": invalid blob"));                                          \
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \
+                                    arg_pp, ": invalid blob"));               \
       return false;                                                           \
     }                                                                         \
   } while (0)
@@ -65,9 +64,8 @@
 #define PARSE_FILENAME(val, res)                                              \
   do {                                                                        \
     if (!ParseFilename(val, res)) {                                           \
-      cmSystemTools::Error(                                                   \
-        cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,           \
-                 ": invalid filename"));                                      \
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \
+                                    arg_pp, ": invalid filename"));           \
       return false;                                                           \
     }                                                                         \
                                                                               \
@@ -84,7 +82,7 @@
   {
     Json::Reader reader;
     if (!reader.parse(ppf, ppio, false)) {
-      cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
                                     arg_pp,
                                     reader.getFormattedErrorMessages()));
       return false;
@@ -93,7 +91,7 @@
 
   Json::Value const& version = ppi["version"];
   if (version.asUInt() != 0) {
-    cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+    cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
                                   arg_pp, ": version ", version.asString()));
     return false;
   }
@@ -101,7 +99,7 @@
   Json::Value const& rules = ppi["rules"];
   if (rules.isArray()) {
     if (rules.size() != 1) {
-      cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+      cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
                                     arg_pp, ": expected 1 source entry"));
       return false;
     }
@@ -109,9 +107,9 @@
     for (auto const& rule : rules) {
       Json::Value const& workdir = rule["work-directory"];
       if (!workdir.isString()) {
-        cmSystemTools::Error(
-          cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,
-                   ": work-directory is not a string"));
+        cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
+                                      arg_pp,
+                                      ": work-directory is not a string"));
         return false;
       }
       std::string work_directory;
@@ -134,7 +132,7 @@
           if (outputs.isArray()) {
             if (outputs.empty()) {
               cmSystemTools::Error(
-                cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,
+                cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp,
                          ": expected at least one 1 output"));
               return false;
             }
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index 94b5cc8..76a5ded 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -175,7 +175,7 @@
 #define CM_HEADER_REGEX "\\.(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$"
 
 #define CM_SOURCE_REGEX                                                       \
-  "\\.(C|F|M|c|c\\+\\+|cc|cpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|"                 \
+  "\\.(C|F|M|c|c\\+\\+|cc|cpp|mpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|"             \
   "rc|def|r|odl|idl|hpj|bat)$"
 
 #define CM_PCH_REGEX "cmake_pch(_[^.]+)?\\.(h|hxx)$"
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx
index 5d8ccf1..bf6925e 100644
--- a/Source/cmStandardLevelResolver.cxx
+++ b/Source/cmStandardLevelResolver.cxx
@@ -44,6 +44,16 @@
   int value;
 };
 
+int ParseStd(std::string const& level)
+{
+  try {
+    return std::stoi(level);
+  } catch (std::invalid_argument&) {
+    // Fall through to use an invalid value.
+  }
+  return -1;
+}
+
 struct StanardLevelComputer
 {
   explicit StanardLevelComputer(std::string lang, std::vector<int> levels,
@@ -113,17 +123,8 @@
       standardStr = "03";
     }
 
-    int standardValue = -1;
-    int defaultValue = -1;
-    try {
-      standardValue = std::stoi(standardStr);
-      defaultValue = std::stoi(*defaultStd);
-    } catch (std::invalid_argument&) {
-      // fall through as we want an error
-      // when we can't find the bad value in the `stds` vector
-    }
-
-    auto stdIt = std::find(cm::cbegin(stds), cm::cend(stds), standardValue);
+    auto stdIt =
+      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
     if (stdIt == cm::cend(stds)) {
       std::string e =
         cmStrCat(this->Language, "_STANDARD is set to invalid value '",
@@ -134,7 +135,7 @@
     }
 
     auto defaultStdIt =
-      std::find(cm::cbegin(stds), cm::cend(stds), defaultValue);
+      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
     if (defaultStdIt == cm::cend(stds)) {
       std::string e = cmStrCat("CMAKE_", this->Language,
                                "_STANDARD_DEFAULT is set to invalid value '",
@@ -195,7 +196,7 @@
     if (existingStandard) {
       existingLevelIter =
         std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
-                  std::stoi(*existingStandard));
+                  ParseStd(*existingStandard));
       if (existingLevelIter == cm::cend(this->Levels)) {
         const std::string e =
           cmStrCat("The ", this->Language, "_STANDARD property on target \"",
@@ -240,7 +241,7 @@
     }
     // convert defaultStandard to an integer
     if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
-                  std::stoi(*defaultStandard)) == cm::cend(this->Levels)) {
+                  ParseStd(*defaultStandard)) == cm::cend(this->Levels)) {
       const std::string e = cmStrCat("The CMAKE_", this->Language,
                                      "_STANDARD_DEFAULT variable contains an "
                                      "invalid value: \"",
@@ -257,7 +258,7 @@
 
     auto existingLevelIter =
       std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
-                std::stoi(*existingStandard));
+                ParseStd(*existingStandard));
     if (existingLevelIter == cm::cend(this->Levels)) {
       const std::string e =
         cmStrCat("The ", this->Language, "_STANDARD property on target \"",
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 6a705f4..024356f 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -1255,6 +1255,30 @@
 #endif
 }
 
+void cmSystemTools::ConvertToLongPath(std::string& path)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  // Try to convert path to a long path only if the path contains character '~'
+  if (path.find('~') == std::string::npos) {
+    return;
+  }
+
+  std::wstring wPath = cmsys::Encoding::ToWide(path);
+  DWORD ret = GetLongPathNameW(wPath.c_str(), nullptr, 0);
+  std::vector<wchar_t> buffer(ret);
+  if (ret != 0) {
+    ret = GetLongPathNameW(wPath.c_str(), buffer.data(),
+                           static_cast<DWORD>(buffer.size()));
+  }
+
+  if (ret != 0) {
+    path = cmsys::Encoding::ToNarrow(buffer.data());
+  }
+#else
+  static_cast<void>(path);
+#endif
+}
+
 std::string cmSystemTools::ConvertToRunCommandPath(const std::string& path)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 1100f05..5bbbb0c 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -287,6 +287,12 @@
   // running cmake needs paths to be in its format
   static std::string ConvertToRunCommandPath(const std::string& path);
 
+  /**
+   * For windows computes the long path for the given path,
+   * For Unix, it is a noop
+   */
+  static void ConvertToLongPath(std::string& path);
+
   /** compute the relative path from local to remote.  local must
       be a directory.  remote can be a file or a directory.
       Both remote and local must be full paths.  Basically, if
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 2fd6063..1fd2355 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -383,6 +383,7 @@
     initProp("UNITY_BUILD");
     initProp("UNITY_BUILD_UNIQUE_ID");
     initProp("OPTIMIZE_DEPENDENCIES");
+    initProp("EXPORT_COMPILE_COMMANDS");
     initPropValue("UNITY_BUILD_BATCH_SIZE", "8");
     initPropValue("UNITY_BUILD_MODE", "BATCH");
     initPropValue("PCH_WARN_INVALID", "ON");
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 9c41504..a93a78a 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -1897,8 +1897,15 @@
     }
     // Figure out if there's any additional flags to use
     if (cmProp saf = sf->GetProperty("VS_SHADER_FLAGS")) {
+      cmGeneratorExpression ge;
+      std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*saf);
+
       for (const std::string& config : this->Configurations) {
-        toolSettings[config]["AdditionalOptions"] = *saf;
+        std::string evaluated = cge->Evaluate(this->LocalGenerator, config);
+
+        if (!evaluated.empty()) {
+          toolSettings[config]["AdditionalOptions"] = evaluated;
+        }
       }
     }
     // Figure out if debug information should be generated
diff --git a/Source/cmXCode21Object.cxx b/Source/cmXCode21Object.cxx
index 1cf9a95..9b0dc58 100644
--- a/Source/cmXCode21Object.cxx
+++ b/Source/cmXCode21Object.cxx
@@ -4,11 +4,12 @@
 
 #include <ostream>
 #include <string>
+#include <utility>
 
 #include "cmSystemTools.h"
 
-cmXCode21Object::cmXCode21Object(PBXType ptype, Type type)
-  : cmXCodeObject(ptype, type)
+cmXCode21Object::cmXCode21Object(PBXType ptype, Type type, std::string id)
+  : cmXCodeObject(ptype, type, std::move(id))
 {
   this->Version = 21;
 }
diff --git a/Source/cmXCode21Object.h b/Source/cmXCode21Object.h
index eb017447..f3fc438 100644
--- a/Source/cmXCode21Object.h
+++ b/Source/cmXCode21Object.h
@@ -13,7 +13,7 @@
 class cmXCode21Object : public cmXCodeObject
 {
 public:
-  cmXCode21Object(PBXType ptype, Type type);
+  cmXCode21Object(PBXType ptype, Type type, std::string id);
   void PrintComment(std::ostream&) override;
   static void PrintList(std::vector<std::unique_ptr<cmXCodeObject>> const&,
                         std::ostream& out, PBXType t);
diff --git a/Source/cmXCodeObject.cxx b/Source/cmXCodeObject.cxx
index b301ab1..d5c5275 100644
--- a/Source/cmXCodeObject.cxx
+++ b/Source/cmXCodeObject.cxx
@@ -40,7 +40,7 @@
   this->Version = 15;
 }
 
-cmXCodeObject::cmXCodeObject(PBXType ptype, Type type)
+cmXCodeObject::cmXCodeObject(PBXType ptype, Type type, std::string id)
 {
   this->Version = 15;
   this->Target = nullptr;
@@ -48,27 +48,7 @@
 
   this->IsA = ptype;
 
-  if (type == OBJECT) {
-    // Set the Id of an Xcode object to a unique string for each instance.
-    // However the Xcode user file references certain Ids: for those cases,
-    // override the generated Id using SetId().
-    //
-    char cUuid[40] = { 0 };
-    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
-    CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid);
-    CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8);
-    this->Id = cUuid;
-    CFRelease(s);
-    CFRelease(uuid);
-  } else {
-    this->Id =
-      "Temporary cmake object, should not be referred to in Xcode file";
-  }
-
-  cmSystemTools::ReplaceString(this->Id, "-", "");
-  if (this->Id.size() > 24) {
-    this->Id = this->Id.substr(0, 24);
-  }
+  this->Id = std::move(id);
 
   this->TypeValue = type;
   if (this->TypeValue == OBJECT) {
diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h
index ab7f99e..dd5e86e 100644
--- a/Source/cmXCodeObject.h
+++ b/Source/cmXCodeObject.h
@@ -57,7 +57,7 @@
   };
   static const char* PBXTypeNames[];
   virtual ~cmXCodeObject();
-  cmXCodeObject(PBXType ptype, Type type);
+  cmXCodeObject(PBXType ptype, Type type, std::string id);
   Type GetType() const { return this->TypeValue; }
   PBXType GetIsA() const { return this->IsA; }
 
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 1691037..48848a7 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -29,6 +29,7 @@
 #include "cm_sys_stat.h"
 
 #include "cmCMakePresetsFile.h"
+#include "cmCommandLineArgument.h"
 #include "cmCommands.h"
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
@@ -132,131 +133,13 @@
 using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
 #endif
 
-struct CommandArgument
-{
-  enum struct Values
-  {
-    Zero,
-    One,
-    Two,
-  };
-
-  std::string InvalidSyntaxMessage;
-  std::string InvalidValueMessage;
-  std::string Name;
-  CommandArgument::Values Type;
-  std::function<bool(std::string const& value, cmake* state)> StoreCall;
-
-  template <typename FunctionType>
-  CommandArgument(std::string n, CommandArgument::Values t,
-                  FunctionType&& func)
-    : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
-    , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
-    , Name(std::move(n))
-    , Type(t)
-    , StoreCall(std::forward<FunctionType>(func))
-  {
-  }
-
-  template <typename FunctionType>
-  CommandArgument(std::string n, std::string failedMsg,
-                  CommandArgument::Values t, FunctionType&& func)
-    : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
-    , InvalidValueMessage(std::move(failedMsg))
-    , Name(std::move(n))
-    , Type(t)
-    , StoreCall(std::forward<FunctionType>(func))
-  {
-  }
-
-  bool matches(std::string const& input) const
-  {
-    return cmHasPrefix(input, this->Name);
-  }
-
-  template <typename T>
-  bool parse(std::string const& input, T& index,
-             std::vector<std::string> const& allArgs, cmake* state) const
-  {
-    enum struct ParseMode
-    {
-      Valid,
-      Invalid,
-      SyntaxError,
-      ValueError
-    };
-    ParseMode parseState = ParseMode::Valid;
-
-    // argument is the next parameter
-    if (this->Type == CommandArgument::Values::Zero) {
-      if (input.size() == this->Name.size()) {
-        parseState = this->StoreCall(input, state) ? ParseMode::Valid
-                                                   : ParseMode::Invalid;
-      } else {
-        parseState = ParseMode::SyntaxError;
-      }
-
-    } else if (this->Type == CommandArgument::Values::One) {
-      if (input.size() == this->Name.size()) {
-        ++index;
-        if (index >= allArgs.size() || allArgs[index][0] == '-') {
-          parseState = ParseMode::ValueError;
-        } else {
-          parseState = this->StoreCall(allArgs[index], state)
-            ? ParseMode::Valid
-            : ParseMode::Invalid;
-        }
-      } else {
-        // parse the string to get the value
-        auto possible_value = cm::string_view(input).substr(this->Name.size());
-        if (possible_value.empty()) {
-          parseState = ParseMode::SyntaxError;
-          parseState = ParseMode::ValueError;
-        } else if (possible_value[0] == '=') {
-          possible_value.remove_prefix(1);
-          if (possible_value.empty()) {
-            parseState = ParseMode::ValueError;
-          } else {
-            parseState = this->StoreCall(std::string(possible_value), state)
-              ? ParseMode::Valid
-              : ParseMode::Invalid;
-          }
-        }
-        if (parseState == ParseMode::Valid) {
-          parseState = this->StoreCall(std::string(possible_value), state)
-            ? ParseMode::Valid
-            : ParseMode::Invalid;
-        }
-      }
-    } else if (this->Type == CommandArgument::Values::Two) {
-      if (input.size() == this->Name.size()) {
-        if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
-            allArgs[index + 2][0] == '-') {
-          parseState = ParseMode::ValueError;
-        } else {
-          index += 2;
-          parseState =
-            this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
-                            state)
-            ? ParseMode::Valid
-            : ParseMode::Invalid;
-        }
-      }
-    }
-
-    if (parseState == ParseMode::SyntaxError) {
-      cmSystemTools::Error(this->InvalidSyntaxMessage);
-    } else if (parseState == ParseMode::ValueError) {
-      cmSystemTools::Error(this->InvalidValueMessage);
-    }
-    return (parseState == ParseMode::Valid);
-  }
-};
-
 auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool {
   return true;
 };
 
+using CommandArgument =
+  cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
+
 } // namespace
 
 static bool cmakeCheckStampFile(const std::string& stampName);
@@ -325,8 +208,9 @@
     };
 
     // The "c" extension MUST precede the "C" extension.
-    setupExts(this->CLikeSourceFileExtensions,
-              { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" });
+    setupExts(
+      this->CLikeSourceFileExtensions,
+      { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M", "mm" });
     setupExts(this->HeaderFileExtensions,
               { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
     setupExts(this->CudaFileExtensions, { "cu" });
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index f7734a6..ba471b7 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -5,7 +5,6 @@
 
 #include <algorithm>
 #include <cassert>
-#include <cctype>
 #include <climits>
 #include <cstring>
 #include <iostream>
@@ -19,6 +18,7 @@
 
 #include <cm3p/uv.h>
 
+#include "cmCommandLineArgument.h"
 #include "cmConsoleBuf.h"
 #include "cmDocumentationEntry.h" // IWYU pragma: keep
 #include "cmGlobalGenerator.h"
@@ -213,61 +213,114 @@
   }
 #endif
 
+  bool wizard_mode = false;
   bool sysinfo = false;
   bool list_cached = false;
   bool list_all_cached = false;
   bool list_help = false;
   bool view_only = false;
   cmake::WorkingMode workingMode = cmake::NORMAL_MODE;
-  std::vector<std::string> args;
-  for (int i = 0; i < ac; ++i) {
-    if (strcmp(av[i], "-i") == 0) {
-      /* clang-format off */
-      std::cerr <<
-        "The \"cmake -i\" wizard mode is no longer supported.\n"
-        "Use the -D option to set cache values on the command line.\n"
-        "Use cmake-gui or ccmake for an interactive dialog.\n";
-      /* clang-format on */
-      return 1;
-    }
-    if (strcmp(av[i], "--system-information") == 0) {
-      sysinfo = true;
-    } else if (strcmp(av[i], "-N") == 0) {
-      view_only = true;
-    } else if (strcmp(av[i], "-L") == 0) {
-      list_cached = true;
-    } else if (strcmp(av[i], "-LA") == 0) {
-      list_all_cached = true;
-    } else if (strcmp(av[i], "-LH") == 0) {
-      list_cached = true;
-      list_help = true;
-    } else if (strcmp(av[i], "-LAH") == 0) {
-      list_all_cached = true;
-      list_help = true;
-    } else if (cmHasLiteralPrefix(av[i], "-P")) {
-      if (i == ac - 1) {
-        cmSystemTools::Error("No script specified for argument -P");
-        return 1;
+  std::vector<std::string> parsedArgs;
+
+  using CommandArgument =
+    cmCommandLineArgument<bool(std::string const& value)>;
+  std::vector<CommandArgument> arguments = {
+    CommandArgument{
+      "-i", CommandArgument::Values::Zero,
+      [&wizard_mode](std::string const&) -> bool {
+        /* clang-format off */
+        std::cerr <<
+          "The \"cmake -i\" wizard mode is no longer supported.\n"
+          "Use the -D option to set cache values on the command line.\n"
+          "Use cmake-gui or ccmake for an interactive dialog.\n";
+        /* clang-format on */
+        wizard_mode = true;
+        return true;
+      } },
+    CommandArgument{ "--system-information", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       sysinfo = true;
+                       return true;
+                     } },
+    CommandArgument{ "-N", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       view_only = true;
+                       return true;
+                     } },
+    CommandArgument{ "-LAH", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       list_all_cached = true;
+                       list_help = true;
+                       return true;
+                     } },
+    CommandArgument{ "-LA", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       list_all_cached = true;
+                       return true;
+                     } },
+    CommandArgument{ "-LH", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       list_cached = true;
+                       list_help = true;
+                       return true;
+                     } },
+    CommandArgument{ "-L", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       list_cached = true;
+                       return true;
+                     } },
+    CommandArgument{ "-P", "No script specified for argument -P",
+                     CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       workingMode = cmake::SCRIPT_MODE;
+                       parsedArgs.emplace_back("-P");
+                       parsedArgs.push_back(value);
+                       return true;
+                     } },
+    CommandArgument{ "--find-package", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       workingMode = cmake::FIND_PACKAGE_MODE;
+                       parsedArgs.emplace_back("--find-package");
+                       return true;
+                     } },
+    CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       workingMode = cmake::HELP_MODE;
+                       parsedArgs.emplace_back("--list-presets");
+                       return true;
+                     } },
+  };
+
+  std::vector<std::string> inputArgs;
+  inputArgs.reserve(ac);
+  cm::append(inputArgs, av, av + ac);
+
+  for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
+    std::string const& arg = inputArgs[i];
+    bool matched = false;
+    for (auto const& m : arguments) {
+      if (m.matches(arg)) {
+        matched = true;
+        if (m.parse(arg, i, inputArgs)) {
+          break;
+        }
+        return 1; // failed to parse
       }
-      workingMode = cmake::SCRIPT_MODE;
-      args.emplace_back(av[i]);
-      i++;
-      args.emplace_back(av[i]);
-    } else if (cmHasLiteralPrefix(av[i], "--find-package")) {
-      workingMode = cmake::FIND_PACKAGE_MODE;
-      args.emplace_back(av[i]);
-    } else if (strcmp(av[i], "--list-presets") == 0) {
-      workingMode = cmake::HELP_MODE;
-      args.emplace_back(av[i]);
-    } else {
-      args.emplace_back(av[i]);
+    }
+    if (!matched) {
+      parsedArgs.emplace_back(av[i]);
     }
   }
+
+  if (wizard_mode) {
+    return 1;
+  }
+
   if (sysinfo) {
     cmake cm(cmake::RoleProject, cmState::Project);
     cm.SetHomeDirectory("");
     cm.SetHomeOutputDirectory("");
-    int ret = cm.GetSystemInformation(args);
+    int ret = cm.GetSystemInformation(parsedArgs);
     return ret;
   }
   cmake::Role const role =
@@ -297,7 +350,7 @@
   });
   cm.SetWorkingMode(workingMode);
 
-  int res = cm.Run(args, view_only);
+  int res = cm.Run(parsedArgs, view_only);
   if (list_cached || list_all_cached) {
     std::cout << "-- Cache values" << std::endl;
     std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys();
@@ -332,16 +385,9 @@
 }
 
 #ifndef CMAKE_BOOTSTRAP
-int extract_job_number(int& index, char const* current, char const* next,
-                       int len_of_flag)
+int extract_job_number(std::string const& command,
+                       std::string const& jobString)
 {
-  std::string command(current);
-  std::string jobString = command.substr(len_of_flag);
-  if (jobString.empty() && next && isdigit(next[0])) {
-    ++index; // skip parsing the job number
-    jobString = std::string(next);
-  }
-
   int jobs = -1;
   unsigned long numJobs = 0;
   if (jobString.empty()) {
@@ -356,8 +402,8 @@
       jobs = int(numJobs);
     }
   } else {
-    std::cerr << "'" << command.substr(0, len_of_flag) << "' invalid number '"
-              << jobString << "' given.\n\n";
+    std::cerr << "'" << command << "' invalid number '" << jobString
+              << "' given.\n\n";
   }
   return jobs;
 }
@@ -374,88 +420,107 @@
   std::string config;
   std::string dir;
   std::vector<std::string> nativeOptions;
+  bool nativeOptionsPassed = false;
   bool cleanFirst = false;
   bool foundClean = false;
   bool foundNonClean = false;
   bool verbose = cmSystemTools::HasEnv("VERBOSE");
 
-  enum Doing
-  {
-    DoingNone,
-    DoingDir,
-    DoingTarget,
-    DoingConfig,
-    DoingNative
+  auto jLambda = [&](std::string const& value) -> bool {
+    jobs = extract_job_number("-j", value);
+    if (jobs < 0) {
+      dir.clear();
+    }
+    return true;
   };
-  Doing doing = DoingDir;
-  for (int i = 2; i < ac; ++i) {
-    if (doing == DoingNative) {
-      nativeOptions.emplace_back(av[i]);
-    } else if (cmHasLiteralPrefix(av[i], "-j")) {
-      const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr);
-      jobs = extract_job_number(i, av[i], nextArg, sizeof("-j") - 1);
-      if (jobs < 0) {
-        dir.clear();
+  auto parallelLambda = [&](std::string const& value) -> bool {
+    jobs = extract_job_number("--parallel", value);
+    if (jobs < 0) {
+      dir.clear();
+    }
+    return true;
+  };
+  auto targetLambda = [&](std::string const& value) -> bool {
+    if (!value.empty()) {
+      std::vector<std::string> values = cmExpandedList(value);
+      for (auto const& v : values) {
+        targets.emplace_back(v);
+        if (v == "clean") {
+          foundClean = true;
+        } else {
+          foundNonClean = true;
+        }
       }
-      doing = DoingNone;
-    } else if (cmHasLiteralPrefix(av[i], "--parallel")) {
-      const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr);
-      jobs = extract_job_number(i, av[i], nextArg, sizeof("--parallel") - 1);
-      if (jobs < 0) {
-        dir.clear();
-      }
-      doing = DoingNone;
-    } else if ((strcmp(av[i], "--target") == 0) ||
-               (strcmp(av[i], "-t") == 0)) {
-      doing = DoingTarget;
-    } else if (strcmp(av[i], "--config") == 0) {
-      doing = DoingConfig;
-    } else if (strcmp(av[i], "--clean-first") == 0) {
-      cleanFirst = true;
-      doing = DoingNone;
-    } else if ((strcmp(av[i], "--verbose") == 0) ||
-               (strcmp(av[i], "-v") == 0)) {
-      verbose = true;
-      doing = DoingNone;
-    } else if (strcmp(av[i], "--use-stderr") == 0) {
-      /* tolerate legacy option */
-    } else if (strcmp(av[i], "--") == 0) {
-      doing = DoingNative;
-    } else {
-      switch (doing) {
-        case DoingDir:
-          dir = cmSystemTools::CollapseFullPath(av[i]);
-          doing = DoingNone;
+      return true;
+    }
+    return false;
+  };
+  auto verboseLambda = [&](std::string const&) -> bool {
+    verbose = true;
+    return true;
+  };
+
+  using CommandArgument =
+    cmCommandLineArgument<bool(std::string const& value)>;
+
+  std::vector<CommandArgument> arguments = {
+    CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne, jLambda },
+    CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne,
+                     parallelLambda },
+    CommandArgument{ "-t", CommandArgument::Values::OneOrMore, targetLambda },
+    CommandArgument{ "--target", CommandArgument::Values::OneOrMore,
+                     targetLambda },
+    CommandArgument{ "--config", CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       config = value;
+                       return true;
+                     } },
+    CommandArgument{ "--clean-first", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       cleanFirst = true;
+                       return true;
+                     } },
+    CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
+    CommandArgument{ "--verbose", CommandArgument::Values::Zero,
+                     verboseLambda },
+    /* legacy option no-op*/
+    CommandArgument{ "--use-stderr", CommandArgument::Values::Zero,
+                     [](std::string const&) -> bool { return true; } },
+    CommandArgument{ "--", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       nativeOptionsPassed = true;
+                       return true;
+                     } },
+  };
+
+  if (ac >= 3) {
+    dir = cmSystemTools::CollapseFullPath(av[2]);
+
+    std::vector<std::string> inputArgs;
+    inputArgs.reserve(ac - 3);
+    cm::append(inputArgs, av + 3, av + ac);
+
+    decltype(inputArgs.size()) i = 0;
+    for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) {
+
+      std::string const& arg = inputArgs[i];
+      for (auto const& m : arguments) {
+        if (m.matches(arg) && m.parse(arg, i, inputArgs)) {
           break;
-        case DoingTarget:
-          if (strlen(av[i]) == 0) {
-            std::cerr << "Warning: Argument number " << i
-                      << " after --target option is empty." << std::endl;
-          } else {
-            targets.emplace_back(av[i]);
-            if (strcmp(av[i], "clean") == 0) {
-              foundClean = true;
-            } else {
-              foundNonClean = true;
-            }
-          }
-          if (foundClean && foundNonClean) {
-            std::cerr << "Error: Building 'clean' and other targets together "
-                         "is not supported."
-                      << std::endl;
-            dir.clear();
-          }
-          break;
-        case DoingConfig:
-          config = av[i];
-          doing = DoingNone;
-          break;
-        default:
-          std::cerr << "Unknown argument " << av[i] << std::endl;
-          dir.clear();
-          break;
+        }
       }
     }
+
+    if (nativeOptionsPassed) {
+      cm::append(nativeOptions, inputArgs.begin() + i, inputArgs.end());
+    }
+  }
+
+  if (foundClean && foundNonClean) {
+    std::cerr << "Error: Building 'clean' and other targets together "
+                 "is not supported."
+              << std::endl;
+    dir.clear();
   }
 
   if (jobs == cmake::NO_BUILD_PARALLEL_LEVEL) {
@@ -658,60 +723,59 @@
   bool strip = false;
   bool verbose = cmSystemTools::HasEnv("VERBOSE");
 
-  enum Doing
-  {
-    DoingNone,
-    DoingDir,
-    DoingConfig,
-    DoingComponent,
-    DoingPrefix,
-    DoingDefaultDirectoryPermissions,
+  auto verboseLambda = [&](std::string const&) -> bool {
+    verbose = true;
+    return true;
   };
 
-  Doing doing = DoingDir;
+  using CommandArgument =
+    cmCommandLineArgument<bool(std::string const& value)>;
 
-  for (int i = 2; i < ac; ++i) {
-    if (strcmp(av[i], "--config") == 0) {
-      doing = DoingConfig;
-    } else if (strcmp(av[i], "--component") == 0) {
-      doing = DoingComponent;
-    } else if (strcmp(av[i], "--prefix") == 0) {
-      doing = DoingPrefix;
-    } else if (strcmp(av[i], "--strip") == 0) {
-      strip = true;
-      doing = DoingNone;
-    } else if ((strcmp(av[i], "--verbose") == 0) ||
-               (strcmp(av[i], "-v") == 0)) {
-      verbose = true;
-      doing = DoingNone;
-    } else if (strcmp(av[i], "--default-directory-permissions") == 0) {
-      doing = DoingDefaultDirectoryPermissions;
-    } else {
-      switch (doing) {
-        case DoingDir:
-          dir = cmSystemTools::CollapseFullPath(av[i]);
-          doing = DoingNone;
+  std::vector<CommandArgument> arguments = {
+    CommandArgument{ "--config", CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       config = value;
+                       return true;
+                     } },
+    CommandArgument{ "--component", CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       component = value;
+                       return true;
+                     } },
+    CommandArgument{ "--default-directory-permissions",
+                     CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       defaultDirectoryPermissions = value;
+                       return true;
+                     } },
+    CommandArgument{ "--prefix", CommandArgument::Values::One,
+                     [&](std::string const& value) -> bool {
+                       prefix = value;
+                       return true;
+                     } },
+    CommandArgument{ "--strip", CommandArgument::Values::Zero,
+                     [&](std::string const&) -> bool {
+                       strip = true;
+                       return true;
+                     } },
+    CommandArgument{ "-v", CommandArgument::Values::Zero, verboseLambda },
+    CommandArgument{ "--verbose", CommandArgument::Values::Zero,
+                     verboseLambda }
+  };
+
+  if (ac >= 3) {
+    dir = cmSystemTools::CollapseFullPath(av[2]);
+
+    std::vector<std::string> inputArgs;
+    inputArgs.reserve(ac - 3);
+    cm::append(inputArgs, av + 3, av + ac);
+    for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
+
+      std::string const& arg = inputArgs[i];
+      for (auto const& m : arguments) {
+        if (m.matches(arg) && m.parse(arg, i, inputArgs)) {
           break;
-        case DoingConfig:
-          config = av[i];
-          doing = DoingNone;
-          break;
-        case DoingComponent:
-          component = av[i];
-          doing = DoingNone;
-          break;
-        case DoingPrefix:
-          prefix = av[i];
-          doing = DoingNone;
-          break;
-        case DoingDefaultDirectoryPermissions:
-          defaultDirectoryPermissions = av[i];
-          doing = DoingNone;
-          break;
-        default:
-          std::cerr << "Unknown argument " << av[i] << std::endl;
-          dir.clear();
-          break;
+        }
       }
     }
   }
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 4c0fbeb..851205e 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -231,11 +231,10 @@
   bool ProcessLine() override
   {
     if (cmHasPrefix(this->Line, this->IncludePrefix)) {
-      this->DepFile << cmCMakePath(
-                         cmTrimWhitespace(this->Line.c_str() +
-                                          this->IncludePrefix.size()))
-                         .GenericString()
-                    << std::endl;
+      auto path =
+        cmTrimWhitespace(this->Line.c_str() + this->IncludePrefix.size());
+      cmSystemTools::ConvertToLongPath(path);
+      this->DepFile << cmCMakePath(path).GenericString() << std::endl;
     } else {
       this->Output << this->Line << std::endl << std::flush;
     }
diff --git a/Tests/CMakeTests/ListTest.cmake.in b/Tests/CMakeTests/ListTest.cmake.in
index 785f41d..76737e5 100644
--- a/Tests/CMakeTests/ListTest.cmake.in
+++ b/Tests/CMakeTests/ListTest.cmake.in
@@ -142,9 +142,8 @@
 set(Insert-List-Only-STDERR "at least three")
 set(Length-List-Only-STDERR "two")
 set(Remove_At-List-Only-STDERR "at least two")
-set(Remove_Item-List-Only-STDERR "two or more")
 
-foreach(cmd IN ITEMS Find Get Insert Length Remove_At Remove_Item)
+foreach(cmd IN ITEMS Find Get Insert Length Remove_At)
   string(TOUPPER ${cmd} cmd_upper)
   set(${cmd}-List-Only-RESULT 1)
   set(${cmd}-List-Only-STDERR ".*CMake Error at List-${cmd}-List-Only.cmake:1 \\(list\\):.*list sub-command ${cmd_upper} requires ${${cmd}-List-Only-STDERR} arguments.*")
diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt
index ab0b5d8..a3d98f6 100644
--- a/Tests/ConfigSources/CMakeLists.txt
+++ b/Tests/ConfigSources/CMakeLists.txt
@@ -5,6 +5,11 @@
 endif()
 project(ConfigSources CXX)
 
+if("${CMAKE_CXX_COMPILER_ID};${CMAKE_CXX_SIMULATE_ID}" STREQUAL "Intel;MSVC")
+  string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Z7")
+  string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -Z7")
+endif()
+
 # Source file(s) named with the configuration(s).
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/config_$<CONFIG>.cpp"
diff --git a/Tests/Fuzzing/README.rst b/Tests/Fuzzing/README.rst
new file mode 100644
index 0000000..a869f9c
--- /dev/null
+++ b/Tests/Fuzzing/README.rst
@@ -0,0 +1,8 @@
+The fuzzers in this directory are run continuously through OSS-fuzz.
+All fuzzers are implemented by way of the `libFuzzer engine`_.
+
+The link to the OSS-fuzz integration can be found here: (pending)
+All email addresses in the `project.yaml` file on OSS-fuzz will have access
+to detailed bug reports and will be notified via email if/when bugs are found.
+
+.. _`libFuzzer Engine`: https://llvm.org/docs/LibFuzzer.html
diff --git a/Tests/Fuzzing/xml_parser_fuzzer.cc b/Tests/Fuzzing/xml_parser_fuzzer.cc
new file mode 100644
index 0000000..1faa918
--- /dev/null
+++ b/Tests/Fuzzing/xml_parser_fuzzer.cc
@@ -0,0 +1,27 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cmXMLParser.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{
+  char test_file[] = "libfuzzer.xml";
+
+  FILE* fp = fopen(test_file, "wb");
+  if (!fp)
+    return 0;
+  fwrite(data, size, 1, fp);
+  fclose(fp);
+
+  cmXMLParser parser;
+  if (!parser.ParseFile(test_file)) {
+    return 1;
+  }
+
+  remove(test_file);
+  return 0;
+}
diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt
index d980a52..d4c19c7 100644
--- a/Tests/IncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/CMakeLists.txt
@@ -67,13 +67,7 @@
 endif()
 
 # Test escaping of special characters in include directory paths.
-set(special_chars "~@&{}()'")
-if(NOT (CMAKE_GENERATOR STREQUAL "NMake Makefiles" AND
-        "x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC" AND
-        "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 13.0))
-  # NMake from VS 6 mistakes '!' in a path after a line continuation for a directive.
-  string(APPEND special_chars "!")
-endif()
+set(special_chars "~@&{}()!'")
 if(NOT CMAKE_GENERATOR MATCHES "(Unix|MinGW|MSYS) Makefiles")
   # when compiler is used for dependencies, special characters for make are not escaped
   string(APPEND special_chars "%")
diff --git a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
index 9b32e59..c53e857 100644
--- a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
@@ -47,19 +47,38 @@
 endmacro()
 
 
-# Initial build
+# Configure the test project
 configure_file("${mocBasicSrcDir}/test1a.h.in" "${mocBasicBinDir}/test1.h" COPYONLY)
-try_compile(MOC_RERUN
-  "${mocBasicBinDir}"
-  "${mocBasicSrcDir}"
-  MocBasic
-  CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
-              "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
-              "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+configure_file("${mocBasicSrcDir}/myobject3a.h.in" "${mocBasicBinDir}/myobject3.h" @ONLY)
+if(CMAKE_GENERATOR_INSTANCE)
+    set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}")
+else()
+    set(_D_CMAKE_GENERATOR_INSTANCE "")
+endif()
+execute_process(
+  COMMAND "${CMAKE_COMMAND}" -B "${mocBasicBinDir}" -S "${mocBasicSrcDir}"
+          -G "${CMAKE_GENERATOR}"
+          -A "${CMAKE_GENERATOR_PLATFORM}"
+          -T "${CMAKE_GENERATOR_TOOLSET}"
+          ${_D_CMAKE_GENERATOR_INSTANCE}
+          "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
+          "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+          "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+  RESULT_VARIABLE exit_code
   OUTPUT_VARIABLE output
 )
-if (NOT MOC_RERUN)
-  message(FATAL_ERROR "Initial build of mocBasic failed. Output: ${output}")
+if(NOT exit_code EQUAL 0)
+  message(FATAL_ERROR "Initial configuration of mocBasic failed. Output: ${output}")
+endif()
+
+# Initial build
+execute_process(
+    COMMAND "${CMAKE_COMMAND}" --build "${mocBasicBinDir}"
+    RESULT_VARIABLE exit_code
+    OUTPUT_VARIABLE output
+)
+if(NOT exit_code EQUAL 0)
+    message(FATAL_ERROR "Initial build of mocBasic failed. Output: ${output}")
 endif()
 
 # Get name of the output binary
@@ -100,3 +119,43 @@
 rebuild(3)
 acquire_timestamp(After)
 require_change_not()
+
+
+# - Ensure that the timestamp will change
+# - Remove Q_OBJECT from header
+# - Rebuild
+acquire_timestamp(Before)
+sleep()
+message(STATUS "Remove Q_OBJECT from header file for a MOC re-run")
+configure_file("${mocBasicSrcDir}/test1c.h.in" "${mocBasicBinDir}/test1.h" COPYONLY)
+sleep()
+rebuild(4)
+acquire_timestamp(After)
+require_change()
+
+
+# - Ensure that the timestamp will change
+# - Add Q_OBJECT to header again
+# - Rebuild
+acquire_timestamp(Before)
+sleep()
+message(STATUS "Add Q_OBJECT to test1.h for a MOC re-run")
+configure_file("${mocBasicSrcDir}/test1a.h.in" "${mocBasicBinDir}/test1.h" COPYONLY)
+sleep()
+rebuild(5)
+acquire_timestamp(After)
+require_change()
+
+
+# - Ensure that the timestamp will change
+# - Add Q_OBJECT to MyObject3
+# - Rebuild
+acquire_timestamp(Before)
+sleep()
+message(STATUS "Add Q_OBJECT to myobject3.h file for a MOC re-run")
+set(CLASS_CONTENT "Q_OBJECT")
+configure_file("${mocBasicSrcDir}/myobject3a.h.in" "${mocBasicBinDir}/myobject3.h" @ONLY)
+sleep()
+rebuild(6)
+acquire_timestamp(After)
+require_change()
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
index 6a9f550..42f2f57 100644
--- a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
@@ -13,10 +13,15 @@
 
 add_executable(mocBasic
   ${CMAKE_CURRENT_BINARY_DIR}/test1.h
+  ${CMAKE_CURRENT_BINARY_DIR}/myobject3.h
   ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+  plainobject.cpp
   res1.qrc
 )
-target_include_directories(mocBasic PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_include_directories(mocBasic PRIVATE
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}
+)
 target_link_libraries(mocBasic ${QT_QTCORE_TARGET})
 # Write target name to text file
 add_custom_command(TARGET mocBasic POST_BUILD COMMAND
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in
index 9d7ea37..5accfd6 100644
--- a/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/main.cpp.in
@@ -1,4 +1,5 @@
 #include "test1.h"
+#include "plainobject.h"
 
 extern int qInitResources_res1();
 
@@ -16,6 +17,7 @@
 
   Test1 test1;
   Test2 test2;
+  PlainObject plainObject;
 
   return 0;
 }
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in
new file mode 100644
index 0000000..d62c314
--- /dev/null
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/myobject3a.h.in
@@ -0,0 +1,13 @@
+#ifndef MYOBJECT3_H
+#define MYOBJECT3_H
+
+#include <qobject.h>
+
+class MyObject3 : public QObject
+{
+    @CLASS_CONTENT@
+public:
+    MyObject3() {}
+};
+
+#endif
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp
new file mode 100644
index 0000000..0ca785e
--- /dev/null
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.cpp
@@ -0,0 +1,12 @@
+#include "plainobject.h"
+
+#include "myobject3.h"
+
+PlainObject::PlainObject()
+{
+}
+
+void PlainObject::doSomething()
+{
+  MyObject3 obj3;
+}
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h
new file mode 100644
index 0000000..8037858
--- /dev/null
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/plainobject.h
@@ -0,0 +1,12 @@
+#ifndef PLAINOBJECT_H
+#define PLAINOBJECT_H
+
+// Class that is plain C++, no Qt involved.
+class PlainObject
+{
+public:
+  PlainObject();
+  void doSomething();
+};
+
+#endif
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in b/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in
new file mode 100644
index 0000000..d0b9868
--- /dev/null
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/test1c.h.in
@@ -0,0 +1,6 @@
+#include <QObject>
+class Test1
+{
+public:
+  void onTst1() {}
+};
diff --git a/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake
new file mode 100644
index 0000000..3e953b3
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom-Script.cmake
@@ -0,0 +1,4 @@
+if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/exists-for-build2")
+  message(FATAL_ERROR "Custom command incorrectly re-ran after CMake re-ran!")
+endif()
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/out.txt")
diff --git a/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake
new file mode 100644
index 0000000..697e485
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/RepeatCMake-Custom.cmake
@@ -0,0 +1,5 @@
+add_custom_command(OUTPUT out.txt
+  COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/RepeatCMake-Custom-Script.cmake
+  DEPENDS ${CMAKE_CURRENT_LIST_DIR}/RepeatCMake-Custom-Script.cmake
+  )
+add_custom_target(drive ALL DEPENDS out.txt)
diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
index 67da6ae..6232634 100644
--- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
+++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
@@ -79,6 +79,23 @@
   endif()
 endif()
 
+function(run_RepeatCMake CASE)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build)
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+  else()
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  run_cmake(${CASE})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${CASE}-build1 ${CMAKE_COMMAND} --build . --config Debug)
+  run_cmake_command(${CASE}-rerun1 ${CMAKE_COMMAND} .)
+  file(WRITE ${RunCMake_TEST_BINARY_DIR}/exists-for-build2 "")
+  run_cmake_command(${CASE}-build2 ${CMAKE_COMMAND} --build . --config Debug)
+endfunction()
+
+run_RepeatCMake(RepeatCMake-Custom)
+
 function(run_ReGeneration)
   # test re-generation of project even if CMakeLists.txt files disappeared
 
diff --git a/Tests/RunCMake/CommandLine/trace-expand.cmake b/Tests/RunCMake/CommandLine/trace-expand.cmake
index e69de29..24da02a 100644
--- a/Tests/RunCMake/CommandLine/trace-expand.cmake
+++ b/Tests/RunCMake/CommandLine/trace-expand.cmake
@@ -0,0 +1 @@
+set(a [[\B]])
diff --git a/Tests/RunCMake/ExportCompileCommands/Properties.cmake b/Tests/RunCMake/ExportCompileCommands/Properties.cmake
new file mode 100644
index 0000000..c7a38b7
--- /dev/null
+++ b/Tests/RunCMake/ExportCompileCommands/Properties.cmake
@@ -0,0 +1,22 @@
+enable_language(C)
+
+add_library(Unset STATIC empty.c)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+add_library(SetOn STATIC empty.c)
+set(CMAKE_EXPORT_COMPILE_COMMANDS OFF)
+add_library(SetOff STATIC empty.c)
+
+get_property(_set TARGET Unset PROPERTY EXPORT_COMPILE_COMMANDS)
+if(_set)
+  message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be unset for Unset target (got \"${_set}\")")
+endif()
+
+get_property(_on TARGET SetOn PROPERTY EXPORT_COMPILE_COMMANDS)
+if(NOT _on STREQUAL "ON")
+  message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be \"ON\" for SetOn target (got \"${_on}\")")
+endif()
+
+get_property(_off TARGET SetOff PROPERTY EXPORT_COMPILE_COMMANDS)
+if(NOT _off STREQUAL "OFF")
+  message(SEND_ERROR "EXPORT_COMPILE_COMMANDS property should be \"OFF\" for SetOff target (got \"${_off}\")")
+endif()
diff --git a/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake
new file mode 100644
index 0000000..d698742
--- /dev/null
+++ b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand-check.cmake
@@ -0,0 +1,32 @@
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/compile_commands.json")
+  set(RunCMake_TEST_FAILED "compile_commands.json not generated")
+  return()
+endif()
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" compile_commands)
+
+macro(check_error)
+  if(error)
+    message(SEND_ERROR "Unexpected error \"${error}\"\nFor: ${compile_commands}")
+  endif()
+endmacro()
+
+string(JSON num_commands ERROR_VARIABLE error LENGTH "${compile_commands}")
+check_error()
+
+# Only one of the targets has the EXPORT_COMPILE_COMMANDS property enabled.
+if(NOT num_commands STREQUAL 1)
+  message(SEND_ERROR "Expected 1 compile command, got ${num_commands} for ${compile_commands}")
+endif()
+
+# Get the compile command generated.
+string(JSON result ERROR_VARIABLE error GET "${compile_commands}" 0)
+check_error()
+string(JSON result ERROR_VARIABLE error GET "${result}" file)
+check_error()
+
+# And ensure the correct target is in that compile command.
+cmake_path(COMPARE "${result}" EQUAL "${RunCMake_TEST_SOURCE_DIR}/expected_file.c" good)
+if(NOT good)
+  message(SEND_ERROR "Expected \"${result}\" in \"${RunCMake_TEST_SOURCE_DIR}/expected_file.c\"")
+endif()
diff --git a/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake
new file mode 100644
index 0000000..46c8845
--- /dev/null
+++ b/Tests/RunCMake/ExportCompileCommands/PropertiesGenerateCommand.cmake
@@ -0,0 +1,7 @@
+enable_language(C)
+
+add_library(Unset STATIC empty.c)
+add_library(ToBeSet STATIC expected_file.c)
+
+# Only one target with EXPORT_COMPILE_COMMANDS property.
+set_property(TARGET ToBeSet PROPERTY EXPORT_COMPILE_COMMANDS TRUE)
diff --git a/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake b/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake
index 9e7e732..b691637 100644
--- a/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExportCompileCommands/RunCMakeTest.cmake
@@ -1,4 +1,12 @@
 include(RunCMake)
 
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+else()
+  set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+endif()
+
 run_cmake_with_options(BeforeProject -DCMAKE_PROJECT_INCLUDE_BEFORE=BeforeProjectBEFORE.cmake)
 run_cmake(CustomCompileRule)
+run_cmake(Properties)
+run_cmake(PropertiesGenerateCommand)
diff --git a/Tests/RunCMake/ExportCompileCommands/expected_file.c b/Tests/RunCMake/ExportCompileCommands/expected_file.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/ExportCompileCommands/expected_file.c
diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake
new file mode 100644
index 0000000..887da0f
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake
@@ -0,0 +1,19 @@
+file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_AFTER "%s")
+# When BUILD_ALWAYS is set, the build stamp is never created.
+file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_AFTER "%s")
+file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_AFTER "%s")
+
+if(NOT PROJ1_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ1_CONFIGURE_TIMESTAMP_AFTER)
+  set(RunCMake_TEST_FAILED "Unexpected rebuild of proj1 configure step (${PROJ1_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ1_CONFIGURE_TIMESTAMP_AFTER})")
+  return()
+endif()
+
+if(NOT PROJ2_CONFIGURE_TIMESTAMP_BEFORE EQUAL PROJ2_CONFIGURE_TIMESTAMP_AFTER)
+  set(RunCMake_TEST_FAILED "Unexpected rebuild of proj2 configure step (${PROJ2_CONFIGURE_TIMESTAMP_BEFORE} != ${PROJ2_CONFIGURE_TIMESTAMP_AFTER})")
+  return()
+endif()
+
+if(PROJ2_BUILD_TIMESTAMP_BEFORE EQUAL PROJ2_BUILD_TIMESTAMP_AFTER)
+  set(RunCMake_TEST_FAILED "proj2 build step did not rebuild (${PROJ2_BUILD_TIMESTAMP_BEFORE} != ${PROJ2_BUILD_TIMESTAMP_AFTER})")
+  return()
+endif()
diff --git a/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake
new file mode 100644
index 0000000..c86a60e
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/CONFIGURE_HANDLED_BY_BUILD.cmake
@@ -0,0 +1,28 @@
+include(ExternalProject)
+
+# Given this setup, on the first build, both configure steps and both build
+# steps will run. On a noop rebuild, only the build steps will run. Without
+# CONFIGURE_HANDLED_BY_BUILD, the configure step of proj2 would also run on a
+# noop rebuild.
+
+ExternalProject_Add(proj1
+  DOWNLOAD_COMMAND ""
+  SOURCE_DIR ""
+  CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something"
+  # file(TIMESTAMP) gives back the timestamp in seconds so we sleep a second to
+  # make sure we get a different timestamp on the stamp file
+  BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1
+  INSTALL_COMMAND ""
+  BUILD_ALWAYS ON
+  STAMP_DIR "stamp"
+)
+ExternalProject_Add(proj2
+  DOWNLOAD_COMMAND ""
+  SOURCE_DIR ""
+  CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Doing something"
+  BUILD_COMMAND ${CMAKE_COMMAND} -E sleep 1
+  INSTALL_COMMAND ""
+  CONFIGURE_HANDLED_BY_BUILD ON
+  DEPENDS proj1
+  STAMP_DIR "stamp"
+)
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
index 598671f..22b8d24 100644
--- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -151,3 +151,33 @@
 if(doSubstitutionTest)
     __ep_test_with_build(Substitutions)
 endif()
+
+function(__ep_test_CONFIGURE_HANDLED_BY_BUILD)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CONFIGURE_HANDLED_BY_BUILD-build)
+  run_cmake(CONFIGURE_HANDLED_BY_BUILD)
+
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(BUILD_CONFIG --config Debug)
+    set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp/Debug")
+  else()
+    set(BUILD_CONFIG "")
+    set(STAMP_DIR "${RunCMake_TEST_BINARY_DIR}/stamp")
+  endif()
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-build ${CMAKE_COMMAND} --build . ${BUILD_CONFIG})
+
+  # Calculate timestamps before rebuilding so we can compare before and after in
+  # CONFIGURE_HANDLED_BY_BUILD-rebuild-check.cmake
+
+  file(TIMESTAMP "${STAMP_DIR}/proj1-configure" PROJ1_CONFIGURE_TIMESTAMP_BEFORE "%s")
+  # When BUILD_ALWAYS is set, the build stamp is never created.
+  file(TIMESTAMP "${STAMP_DIR}/proj2-configure" PROJ2_CONFIGURE_TIMESTAMP_BEFORE "%s")
+  file(TIMESTAMP "${STAMP_DIR}/proj2-build" PROJ2_BUILD_TIMESTAMP_BEFORE "%s")
+
+  run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG})
+endfunction()
+
+if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ")
+  __ep_test_CONFIGURE_HANDLED_BY_BUILD()
+endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt
new file mode 100644
index 0000000..fad923a
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild-debug-in-release-graph-build-stdout.txt
@@ -0,0 +1,2 @@
+Running post-build command with Debug
+Generating Debug\.txt
diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt
new file mode 100644
index 0000000..485a52c
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild-release-build-stdout.txt
@@ -0,0 +1,3 @@
+Running post-build command with Release
+Generating out\.txt with Release
+Generating Release\.txt
diff --git a/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake b/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake
new file mode 100644
index 0000000..5fcff74
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/PostBuild.cmake
@@ -0,0 +1,6 @@
+enable_language(C)
+
+add_executable(Exe main.c)
+add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Running post-build command with $<CONFIG>")
+add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Generating out.txt with $<CONFIG>" BYPRODUCTS out.txt)
+add_custom_command(TARGET Exe POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Generating $<CONFIG>.txt" BYPRODUCTS $<CONFIG>.txt)
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index dc2db57..480d628 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -187,6 +187,12 @@
 run_cmake_build(SimpleCrossConfigs all-all-in-release-graph Release all:all)
 run_cmake_build(SimpleCrossConfigs all-relwithdebinfo-in-release-graph Release all:RelWithDebInfo)
 
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PostBuild-build)
+set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all")
+run_cmake_configure(PostBuild)
+run_cmake_build(PostBuild release Release Exe)
+run_cmake_build(PostBuild debug-in-release-graph Release Exe:Debug)
+
 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Framework-build)
 set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all")
 run_cmake_configure(Framework)
diff --git a/Tests/RunCMake/install/FILES-RENAME-all-check.cmake b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake
new file mode 100644
index 0000000..7e9b103
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-RENAME-all-check.cmake
@@ -0,0 +1 @@
+check_installed([[^src;src/script_Debug\.ps]])
diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-result.txt b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-RENAME-bad-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt
new file mode 100644
index 0000000..9844158
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-RENAME-bad-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error:
+  Error evaluating generator expression:
+
+    \$<NOTAGENEX>
+
+  Expression did not evaluate to a known generator expression
diff --git a/Tests/RunCMake/install/FILES-RENAME-bad.cmake b/Tests/RunCMake/install/FILES-RENAME-bad.cmake
new file mode 100644
index 0000000..5be0bb2
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-RENAME-bad.cmake
@@ -0,0 +1,4 @@
+install(FILES empty.c
+  DESTINATION mybin
+  RENAME $<NOTAGENEX>
+  )
diff --git a/Tests/RunCMake/install/FILES-RENAME.cmake b/Tests/RunCMake/install/FILES-RENAME.cmake
new file mode 100644
index 0000000..5896e64
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-RENAME.cmake
@@ -0,0 +1,4 @@
+install(FILES script.bat
+  DESTINATION src
+  RENAME script_$<CONFIG>.ps
+  )
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index d64d88b..b067b3a 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -74,6 +74,7 @@
 run_cmake(DIRECTORY-DIRECTORY-bad)
 run_cmake(DIRECTORY-DESTINATION-bad)
 run_cmake(FILES-DESTINATION-bad)
+run_cmake(FILES-RENAME-bad)
 run_cmake(TARGETS-DESTINATION-bad)
 run_cmake(EXPORT-OldIFace)
 run_cmake(EXPORT-UnknownExport)
@@ -91,6 +92,10 @@
 run_cmake(FILES-DESTINATION-TYPE)
 run_cmake(DIRECTORY-DESTINATION-TYPE)
 
+set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug")
+run_install_test(FILES-RENAME)
+unset(RunCMake_TEST_OPTIONS)
+
 if(APPLE)
   run_cmake(TARGETS-Apple-Defaults)
 endif()
diff --git a/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake
new file mode 100644
index 0000000..f69c024
--- /dev/null
+++ b/Tests/RunCMake/list/REMOVE_ITEM-NoItemArg.cmake
@@ -0,0 +1,5 @@
+set(ls "a" "b" "c")
+list(REMOVE_ITEM ls alpha)
+if (NOT ls STREQUAL "a;b;c")
+  message(FATAL_ERROR "list(REMOVE_ITEM) modified for empty item")
+endif ()
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake
index b4a91bc..c11891c 100644
--- a/Tests/RunCMake/list/RunCMakeTest.cmake
+++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -30,6 +30,7 @@
 run_cmake(REMOVE_AT-NotList)
 run_cmake(REMOVE_DUPLICATES-NotList)
 run_cmake(REMOVE_ITEM-NotList)
+run_cmake(REMOVE_ITEM-NoItemArg)
 run_cmake(REVERSE-NotList)
 run_cmake(SORT-NotList)
 
diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt
index 56e4c1d..edd4330 100644
--- a/Tests/VSWinStorePhone/CMakeLists.txt
+++ b/Tests/VSWinStorePhone/CMakeLists.txt
@@ -119,13 +119,13 @@
 set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_TYPE Pixel)
 set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_ENTRYPOINT mainPS)
 set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_MODEL 4.0_level_9_3)
-set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_FLAGS "/DFLAGS_ADDED")
+set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_FLAGS $<1:/DFLAGS_ADDED>)
 set_property(SOURCE ${PIXELSHADER_FILES} PROPERTY VS_SHADER_OUTPUT_HEADER_FILE "$(OutDir)%(Filename).h")
 
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_TYPE Vertex)
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_ENTRYPOINT mainVS)
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_MODEL 4.0_level_9_3)
-set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_FLAGS "/DFLAGS_ADDED")
+set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_FLAGS $<1:/DFLAGS_ADDED>)
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_OUTPUT_HEADER_FILE "$(OutDir)%(Filename).h")
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SETTINGS "$<$<CONFIG:DEBUG>:SourceProperty1=SourceProperty1Value>")
 
diff --git a/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash b/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash
new file mode 100755
index 0000000..bf92e62
--- /dev/null
+++ b/Utilities/Release/macos/qt-5.15.2-macosx10.13-x86_64-arm64.bash
@@ -0,0 +1,125 @@
+#!/usr/bin/env bash
+
+# Run this script on a macOS x86_64 host to generate Qt universal binaries.
+#
+# This script requires the 'makeuniversal' tool from:
+#
+#   https://github.com/fizzyade/makeuniversal
+#
+# Build it with an existing local Qt installation first.
+#
+# Set the PATH environment variable to contain the location of 'makeuniversal'.
+
+set -e
+set -x
+
+umask 022
+
+# Verify that 'makeuniversal' is available in the PATH.
+type -p makeuniversal >/dev/null
+
+# Download, verify, and extract sources.
+curl -OL https://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz
+shasum -a 256 qt-everywhere-src-5.15.2.tar.xz | grep -q 3a530d1b243b5dec00bc54937455471aaa3e56849d2593edb8ded07228202240
+tar xjf qt-everywhere-src-5.15.2.tar.xz
+
+# Build the x86_64 variant.
+mkdir qt-5.15.2-x86_64
+cd qt-5.15.2-x86_64
+../qt-everywhere-src-5.15.2/configure \
+  --prefix=/ \
+  -platform macx-clang \
+  -device-option QMAKE_APPLE_DEVICE_ARCHS=x86_64 \
+  -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=10.13 \
+  -release \
+  -opensource -confirm-license \
+  -gui \
+  -widgets \
+  -no-gif \
+  -no-icu \
+  -no-pch \
+  -no-angle \
+  -no-opengl \
+  -no-dbus \
+  -no-harfbuzz \
+  -skip declarative \
+  -skip multimedia \
+  -skip qtcanvas3d \
+  -skip qtcharts \
+  -skip qtconnectivity \
+  -skip qtdeclarative \
+  -skip qtgamepad \
+  -skip qtlocation \
+  -skip qtmultimedia \
+  -skip qtnetworkauth \
+  -skip qtpurchasing \
+  -skip qtremoteobjects \
+  -skip qtscript \
+  -skip qtsensors \
+  -skip qtserialbus \
+  -skip qtserialport \
+  -skip qtsvg \
+  -skip qtwebchannel \
+  -skip qtwebengine \
+  -skip qtwebsockets \
+  -skip qtxmlpatterns \
+  -nomake examples \
+  -nomake tests \
+  -nomake tools
+make -j 8
+cd ..
+
+# Build the arm64 variant.
+mkdir qt-5.15.2-arm64
+cd qt-5.15.2-arm64
+../qt-everywhere-src-5.15.2/configure \
+  --prefix=/ \
+  -platform macx-clang \
+  -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64 \
+  -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=10.13 \
+  -release \
+  -opensource -confirm-license \
+  -gui \
+  -widgets \
+  -no-gif \
+  -no-icu \
+  -no-pch \
+  -no-angle \
+  -no-opengl \
+  -no-dbus \
+  -no-harfbuzz \
+  -skip declarative \
+  -skip multimedia \
+  -skip qtcanvas3d \
+  -skip qtcharts \
+  -skip qtconnectivity \
+  -skip qtdeclarative \
+  -skip qtgamepad \
+  -skip qtlocation \
+  -skip qtmultimedia \
+  -skip qtnetworkauth \
+  -skip qtpurchasing \
+  -skip qtremoteobjects \
+  -skip qtscript \
+  -skip qtsensors \
+  -skip qtserialbus \
+  -skip qtserialport \
+  -skip qtsvg \
+  -skip qtwebchannel \
+  -skip qtwebengine \
+  -skip qtwebsockets \
+  -skip qtxmlpatterns \
+  -nomake examples \
+  -nomake tests \
+  -nomake tools
+make -j 8 -k
+cd ..
+
+# Combine the two builds into universal binaries.
+makeuniversal qt-5.15.2-univ qt-5.15.2-x86_64 qt-5.15.2-arm64
+cd qt-5.15.2-univ
+make install -j 8 INSTALL_ROOT=/tmp/qt-5.15.2-macosx10.13-x86_64-arm64
+cd ..
+
+# Create the final tarball containing universal binaries.
+tar cjf qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz -C /tmp qt-5.15.2-macosx10.13-x86_64-arm64
diff --git a/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash b/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash
index a61e114..79931ec 100755
--- a/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash
+++ b/Utilities/Release/macos/qt-5.9.9-macosx10.10-x86_64-arm64.bash
@@ -13,6 +13,8 @@
 set -e
 set -x
 
+umask 022
+
 # Verify that 'makeuniversal' is available in the PATH.
 type -p makeuniversal >/dev/null