Merge topic 'add_xl_cxx14_support'

458ea9d76c XL: Add C++14 language level flags

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3235
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 893fb43..2e89d7b 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -28,6 +28,7 @@
     string(`SUBSTRING`_ <string> <begin> <length> <out-var>)
     string(`STRIP`_ <string> <out-var>)
     string(`GENEX_STRIP`_ <string> <out-var>)
+    string(`REPEAT`_ <string> <count> <out-var>)
 
   `Comparison`_
     string(`COMPARE`_ <op> <string1> <string2> <out-var>)
@@ -269,6 +270,14 @@
 Strip any :manual:`generator expressions <cmake-generator-expressions(7)>`
 from the ``input string`` and store the result in the ``output variable``.
 
+.. _REPEAT:
+
+.. code-block:: cmake
+
+  string(REPEAT <input string> <count> <output variable>)
+
+Produce the output string as repetion of ``input string`` ``count`` times.
+
 Comparison
 ^^^^^^^^^^
 
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst
index ca8fc77..0bc2ca3 100644
--- a/Help/command/try_compile.rst
+++ b/Help/command/try_compile.rst
@@ -135,6 +135,7 @@
 * :variable:`CMAKE_ENABLE_EXPORTS`
 * :variable:`CMAKE_LINK_SEARCH_START_STATIC`
 * :variable:`CMAKE_LINK_SEARCH_END_STATIC`
+* :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
 * :variable:`CMAKE_POSITION_INDEPENDENT_CODE`
 
 If :policy:`CMP0056` is set to ``NEW``, then
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index e9b3f4c..7f4761f 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -387,14 +387,25 @@
 ``$<TARGET_NAME_IF_EXISTS:tgt>``
   Expands to the ``tgt`` if the given target exists, an empty string
   otherwise.
-``$<TARGET_OUTPUT_NAME:tgt>``
-  Base name of main file where ``tgt`` is the name of a target.
-
-  Note that ``tgt`` is not added as a dependency of the target this
-  expression is evaluated on.
 ``$<TARGET_FILE:tgt>``
   Full path to main file (.exe, .so.1.2, .a) where ``tgt`` is the name of a
   target.
+``$<TARGET_FILE_BASE_NAME:tgt>``
+  Base name of main file where ``tgt`` is the name of a target.
+
+  The base name corresponds to the target file name (see
+  ``$<TARGET_FILE_NAME:tgt>``) without prefix and suffix. For example, if
+  target file name is ``libbase.so``, the base name is ``base``.
+
+  See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+  :prop_tgt:`LIBRARY_OUTPUT_NAME` and :prop_tgt:`RUNTIME_OUTPUT_NAME`
+  target properties and their configuration specific variants
+  :prop_tgt:`OUTPUT_NAME_<CONFIG>`, :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`,
+  :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>` and
+  :prop_tgt:`RUNTIME_OUTPUT_NAME_<CONFIG>`.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
 ``$<TARGET_FILE_PREFIX:tgt>``
   Prefix of main file where ``tgt`` is the name of a target.
 
@@ -409,13 +420,23 @@
   Name of main file (.exe, .so.1.2, .a).
 ``$<TARGET_FILE_DIR:tgt>``
   Directory of main file (.exe, .so.1.2, .a).
-``$<TARGET_LINKER_OUTPUT_NAME:tgt>``
+``$<TARGET_LINKER_FILE:tgt>``
+  File used to link (.a, .lib, .so) where ``tgt`` is the name of a target.
+``$<TARGET_LINKER_FILE_BASE_NAME:tgt>``
   Base name of file used to link where ``tgt`` is the name of a target.
 
+  The base name corresponds to the target linker file name (see
+  ``$<TARGET_LINKER_FILE_NAME:tgt>``) without prefix and suffix. For example,
+  if target file name is ``libbase.a``, the base name is ``base``.
+
+  See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+  and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
+  specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`,
+  :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and
+  :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`.
+
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on.
-``$<TARGET_LINKER_FILE:tgt>``
-  File used to link (.a, .lib, .so) where ``tgt`` is the name of a target.
 ``$<TARGET_LINKER_FILE_PREFIX:tgt>``
   Prefix of file used to link where ``tgt`` is the name of a target.
 
@@ -436,15 +457,6 @@
   Name of file with soname (.so.3).
 ``$<TARGET_SONAME_FILE_DIR:tgt>``
   Directory of with soname (.so.3).
-``$<TARGET_PDB_OUTPUT_NAME:tgt>``
-  Base name of the linker generated program database file (.pdb)
-  where ``tgt`` is the name of a target.
-
-  See also the :prop_tgt:`PDB_NAME` target property and its configuration
-  specific variant :prop_tgt:`PDB_NAME_<CONFIG>`.
-
-  Note that ``tgt`` is not added as a dependency of the target this
-  expression is evaluated on.
 ``$<TARGET_PDB_FILE:tgt>``
   Full path to the linker generated program database file (.pdb)
   where ``tgt`` is the name of a target.
@@ -452,6 +464,19 @@
   See also the :prop_tgt:`PDB_NAME` and :prop_tgt:`PDB_OUTPUT_DIRECTORY`
   target properties and their configuration specific variants
   :prop_tgt:`PDB_NAME_<CONFIG>` and :prop_tgt:`PDB_OUTPUT_DIRECTORY_<CONFIG>`.
+``$<TARGET_PDB_FILE_BASE_NAME:tgt>``
+  Base name of the linker generated program database file (.pdb)
+  where ``tgt`` is the name of a target.
+
+  The base name corresponds to the target PDB file name (see
+  ``$<TARGET_PDB_FILE_NAME:tgt>``) without prefix and suffix. For example,
+  if target file name is ``base.pdb``, the base name is ``base``.
+
+  See also the :prop_tgt:`PDB_NAME` target property and its configuration
+  specific variant :prop_tgt:`PDB_NAME_<CONFIG>`.
+
+  Note that ``tgt`` is not added as a dependency of the target this
+  expression is evaluated on.
 ``$<TARGET_PDB_FILE_NAME:tgt>``
   Name of the linker generated program database file (.pdb).
 ``$<TARGET_PDB_FILE_DIR:tgt>``
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index e89ea3da..043fb5c 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0091: MSVC runtime library flags are selected by an abstraction. </policy/CMP0091>
    CMP0090: export(PACKAGE) does not populate package registry by default. </policy/CMP0090>
    CMP0089: Compiler id for IBM Clang-based XL compilers is now XLClang. </policy/CMP0089>
 
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 4d4b9ff..c11496c 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -280,6 +280,7 @@
    /prop_tgt/MACOSX_RPATH
    /prop_tgt/MANUALLY_ADDED_DEPENDENCIES
    /prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
+   /prop_tgt/MSVC_RUNTIME_LIBRARY
    /prop_tgt/NAME
    /prop_tgt/NO_SONAME
    /prop_tgt/NO_SYSTEM_FROM_IMPORTED
@@ -335,6 +336,7 @@
    /prop_tgt/VS_KEYWORD
    /prop_tgt/VS_MOBILE_EXTENSIONS_VERSION
    /prop_tgt/VS_NO_SOLUTION_DEPLOY
+   /prop_tgt/VS_PROJECT_IMPORT
    /prop_tgt/VS_SCC_AUXPATH
    /prop_tgt/VS_SCC_LOCALPATH
    /prop_tgt/VS_SCC_PROJECTNAME
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 18dd9d7..22e8add 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -386,6 +386,7 @@
    /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_MODULE_LINKER_FLAGS_INIT
    /variable/CMAKE_MSVCIDE_RUN_PATH
+   /variable/CMAKE_MSVC_RUNTIME_LIBRARY
    /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
    /variable/CMAKE_NO_BUILTIN_CHRPATH
    /variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
diff --git a/Help/policy/CMP0091.rst b/Help/policy/CMP0091.rst
new file mode 100644
index 0000000..5b7c4e3
--- /dev/null
+++ b/Help/policy/CMP0091.rst
@@ -0,0 +1,47 @@
+CMP0091
+-------
+
+MSVC runtime library flags are selected by an abstraction.
+
+Compilers targeting the MSVC ABI have flags to select the MSVC runtime library.
+Runtime library selection typically varies with build configuration because
+there is a separate runtime library for Debug builds.
+
+In CMake 3.14 and below, MSVC runtime library selection flags are added to
+the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
+automatically.  This allows users to edit their cache entries to adjust the
+flags.  However, the presence of such default flags is problematic for
+projects that want to choose a different runtime library programmatically.
+In particular, it requires string editing of the
+:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
+CMake builtin defaults so they can be replaced.
+
+CMake 3.15 and above prefer to leave the MSVC runtime library selection flags
+out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
+offer a first-class abstraction.  The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
+variable and :prop_tgt:`MSVC_RUNTIME_LIBRARY` target property may be set to
+select the MSVC runtime library.
+
+This policy provides compatibility with projects that have not been updated
+to be aware of the abstraction.  The policy setting takes effect as of the
+first :command:`project` or :command:`enable_language` command that enables
+a language whose compiler targets the MSVC ABI.
+
+.. note::
+
+  Once the policy has taken effect at the top of a project, that choice
+  must be used throughout the tree.  In projects that have nested projects
+  in subdirectories, be sure to convert everything together.
+
+The ``OLD`` behavior for this policy is to place MSVC runtime library
+flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
+entries and ignore the :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` abstraction.
+The ``NEW`` behavior for this policy is to *not* place MSVC runtime
+library flags in the default cache entries and use the abstraction instead.
+
+This policy was introduced in CMake version 3.15.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
new file mode 100644
index 0000000..2bf71a9
--- /dev/null
+++ b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
@@ -0,0 +1,15 @@
+``MultiThreaded``
+  Compile with ``-MT`` or equivalent flag(s) to use a multi-threaded
+  statically-linked runtime library.
+``MultiThreadedDLL``
+  Compile with ``-MD`` or equivalent flag(s) to use a multi-threaded
+  dynamically-linked runtime library.
+``MultiThreadedDebug``
+  Compile with ``-MTd`` or equivalent flag(s) to use a multi-threaded
+  statically-linked runtime library.
+``MultiThreadedDebugDLL``
+  Compile with ``-MDd`` or equivalent flag(s) to use a multi-threaded
+  dynamically-linked runtime library.
+
+The value is ignored on non-MSVC compilers but an unsupported value will
+be rejected as an error when using a compiler targeting the MSVC ABI.
diff --git a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst
new file mode 100644
index 0000000..1e3f5e9
--- /dev/null
+++ b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst
@@ -0,0 +1,26 @@
+MSVC_RUNTIME_LIBRARY
+--------------------
+
+Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
+
+The allowed values are:
+
+.. include:: MSVC_RUNTIME_LIBRARY-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  add_executable(foo foo.c)
+  set_property(TARGET foo PROPERTY
+    MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
+selects for the target ``foo`` a multi-threaded statically-linked runtime
+library with or without debug information depending on the configuration.
+
+.. note::
+
+  This property has effect only when policy :policy:`CMP0091` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.
diff --git a/Help/prop_tgt/VS_PROJECT_IMPORT.rst b/Help/prop_tgt/VS_PROJECT_IMPORT.rst
new file mode 100644
index 0000000..569c8ea
--- /dev/null
+++ b/Help/prop_tgt/VS_PROJECT_IMPORT.rst
@@ -0,0 +1,8 @@
+VS_PROJECT_IMPORT
+-----------------
+
+Visual Studio managed project imports
+
+Adds to a generated Visual Studio project one or more semicolon-delimited paths
+to .props files needed when building projects from some NuGet packages.
+For example, ``my_packages_path/MyPackage.1.0.0/build/MyPackage.props``.
diff --git a/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst
new file mode 100644
index 0000000..d8b2b21
--- /dev/null
+++ b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst
@@ -0,0 +1,7 @@
+genex-TARGET_FILE_BASE_NAME
+---------------------------
+
+* New ``$<TARGET_FILE_BASE_NAME:...>``, ``$<TARGET_LINKER_FILE_BASE_NAME:...>``
+  and ``$<TARGET_PDB_FILE_BASE_NAME:...>``
+  :manual:`generator expressions <cmake-generator-expressions(7)>` have been
+  added to retrieve the base name of various artifacts.
diff --git a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst b/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst
deleted file mode 100644
index e3ffe57..0000000
--- a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-genex-TARGET_OUTPUT_NAME
-------------------------
-
-* New ``$<TARGET_OUTPUT_NAME:...>``, ``$<TARGET_LINKER_OUTPUT_NAME:...>`` and
-  ``$<TARGET_PDB_OUTPUT_NAME:...>``
-  :manual:`generator expressions <cmake-generator-expressions(7)>` have been
-  added to retrieve the base name of various artifacts.
diff --git a/Help/release/dev/msvc-runtime-library.rst b/Help/release/dev/msvc-runtime-library.rst
new file mode 100644
index 0000000..4dddac2
--- /dev/null
+++ b/Help/release/dev/msvc-runtime-library.rst
@@ -0,0 +1,7 @@
+msvc-runtime-library
+--------------------
+
+* The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` variable and
+  :prop_tgt:`MSVC_RUNTIME_LIBRARY` target property were introduced to
+  select the runtime library used by compilers targeting the MSVC ABI.
+  See policy :policy:`CMP0091`.
diff --git a/Help/release/dev/string-repeat.rst b/Help/release/dev/string-repeat.rst
new file mode 100644
index 0000000..4be0d5c
--- /dev/null
+++ b/Help/release/dev/string-repeat.rst
@@ -0,0 +1,4 @@
+string-repeat
+--------------
+
+* The :command:`string` learned a new sub-command ``REPEAT``.
diff --git a/Help/release/dev/vs-project-import.rst b/Help/release/dev/vs-project-import.rst
new file mode 100644
index 0000000..de6024d
--- /dev/null
+++ b/Help/release/dev/vs-project-import.rst
@@ -0,0 +1,5 @@
+vs-project-import
+-----------------
+
+* The :prop_tgt:`VS_PROJECT_IMPORT` target property was added which allows
+  to import external .props files in managed Visual Studio targets.
diff --git a/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst b/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst
new file mode 100644
index 0000000..6ed68c9
--- /dev/null
+++ b/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst
@@ -0,0 +1,27 @@
+CMAKE_MSVC_RUNTIME_LIBRARY
+--------------------------
+
+Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
+This variable is used to initialize the :prop_tgt:`MSVC_RUNTIME_LIBRARY`
+property on all targets as they are created.  It is also propagated by
+calls to the :command:`try_compile` command into the test project.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
+selects for all following targets a multi-threaded statically-linked runtime
+library with or without debug information depending on the configuration.
+
+.. note::
+
+  This variable has effect only when policy :policy:`CMP0091` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.
diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake
index ff51d30..0448965 100644
--- a/Modules/Compiler/Clang-C.cmake
+++ b/Modules/Compiler/Clang-C.cmake
@@ -23,12 +23,15 @@
     # clang-cl doesn't have any of these
     set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
     set(CMAKE_C90_EXTENSION_COMPILE_OPTION "")
+    set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
 
     set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
     set(CMAKE_C99_EXTENSION_COMPILE_OPTION "")
+    set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
 
     set(CMAKE_C11_STANDARD_COMPILE_OPTION "")
     set(CMAKE_C11_EXTENSION_COMPILE_OPTION "")
+    set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
   endif()
 endif()
 
diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake
index 22c34f8..a722130 100644
--- a/Modules/Compiler/MSVC-C.cmake
+++ b/Modules/Compiler/MSVC-C.cmake
@@ -11,8 +11,8 @@
 # There is no meaningful default for this
 set(CMAKE_C_STANDARD_DEFAULT "")
 
-# There are no C compiler modes so we only need to test features once.
-# Override the default macro for this special case.  Pretend that
+# There are no C compiler modes so we hard-code the known compiler supported
+# features. Override the default macro for this special case.  Pretend that
 # all language standards are available so that at least compilation
 # can be attempted.
 macro(cmake_record_c_compile_features)
@@ -20,6 +20,10 @@
     c_std_90
     c_std_99
     c_std_11
+    c_function_prototypes
+    c_variadic_macros
     )
-  _record_compiler_features(C "" CMAKE_C_COMPILE_FEATURES)
+  list(APPEND CMAKE_C90_COMPILE_FEATURES c_std_90 c_function_prototypes)
+  list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99 c_variadic_macros)
+  list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11)
 endmacro()
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index c30c6c8..7325eca 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -1777,7 +1777,7 @@
     list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
     list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
     # Gentoo
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
+    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}.${COMPONENT_PYTHON_VERSION_MINOR}")
     # RPMs
     list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
   endif()
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake
index 832dca2..00db033 100644
--- a/Modules/FindOpenGL.cmake
+++ b/Modules/FindOpenGL.cmake
@@ -205,11 +205,13 @@
   find_library(OPENGL_glx_LIBRARY
     NAMES GLX
     PATHS ${_OPENGL_LIB_PATH}
+    PATH_SUFFIXES libglvnd
   )
 
   find_library(OPENGL_egl_LIBRARY
     NAMES EGL
     PATHS ${_OPENGL_LIB_PATH}
+    PATH_SUFFIXES libglvnd
   )
 
   find_library(OPENGL_glu_LIBRARY
@@ -264,6 +266,7 @@
             /usr/openwin/lib
             /usr/shlib
             ${_OPENGL_LIB_PATH}
+      PATH_SUFFIXES libglvnd
       )
   endif()
 
diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake
index 2f34a7a..0a98895 100644
--- a/Modules/InstallRequiredSystemLibraries.cmake
+++ b/Modules/InstallRequiredSystemLibraries.cmake
@@ -213,10 +213,13 @@
   elseif(MSVC_VERSION_VERSION GREATER_EQUAL 143)
     message(WARNING "MSVC toolset v${MSVC_VERSION_VERSION} not yet supported.")
   elseif(MSVC_TOOLSET_VERSION EQUAL 142)
-    # FIXME: VS 2019 RC 4 uses VC141 but an update will fix it to be VC142.
-    set(MSVC_REDIST_NAME VC141)
+    set(MSVC_REDIST_NAME VC142)
     set(_MSVC_DLL_VERSION 140)
     set(_MSVC_IDE_VERSION 16)
+    if(MSVC_VERSION EQUAL 1920)
+      # VS2019 named this differently prior to update 1.
+      set(MSVC_REDIST_NAME VC141)
+    endif()
   elseif(MSVC_TOOLSET_VERSION EQUAL 141)
     set(MSVC_REDIST_NAME VC141)
     set(_MSVC_DLL_VERSION 140)
diff --git a/Modules/Platform/Windows-Intel-Fortran.cmake b/Modules/Platform/Windows-Intel-Fortran.cmake
index 3981a09..f00a8e4 100644
--- a/Modules/Platform/Windows-Intel-Fortran.cmake
+++ b/Modules/Platform/Windows-Intel-Fortran.cmake
@@ -4,8 +4,34 @@
 set(CMAKE_Fortran_MODDIR_FLAG "-module:")
 set(CMAKE_Fortran_STANDARD_LIBRARIES_INIT "user32.lib")
 __windows_compiler_intel(Fortran)
-string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp /libs:dll /threads")
-string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full /dbglibs")
+if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+  set(_LIBSDLL "")
+  set(_DBGLIBS "")
+  set(_THREADS "")
+else()
+  set(_LIBSDLL " /libs:dll")
+  set(_DBGLIBS " /dbglibs")
+  set(_THREADS " /threads")
+endif()
+string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp${_LIBSDLL}${_THREADS}")
+string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full${_DBGLIBS}")
 string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " /O1 /DNDEBUG")
 string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " /O2 /DNDEBUG")
 string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " /O2 /debug:full /DNDEBUG")
+unset(_LIBSDLL)
+unset(_DBGLIBS)
+unset(_THREADS)
+
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -threads -libs:static)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll    -dbglibs)
+
+# Intel Fortran for Windows supports single-threaded RTL but it is
+# not implemented by the Visual Studio integration.
+if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreaded                 -libs:static)
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDLL              -libs:dll)
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebug            -libs:static -dbglibs)
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebugDLL         -libs:dll    -dbglibs)
+endif()
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index df3bd1e..4279a74 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -298,6 +298,14 @@
 string(APPEND CMAKE_STATIC_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
 unset(_MACHINE_ARCH_FLAG)
 
+cmake_policy(GET CMP0091 __WINDOWS_MSVC_CMP0091)
+if(__WINDOWS_MSVC_CMP0091 STREQUAL "NEW")
+  set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
+else()
+  set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "")
+endif()
+unset(__WINDOWS_MSVC_CMP0091)
+
 macro(__windows_compiler_msvc lang)
   if(NOT MSVC_VERSION LESS 1400)
     # for 2005 make sure the manifest is put in the dll with mt
@@ -351,21 +359,35 @@
 
   if("x${lang}" STREQUAL "xC" OR
       "x${lang}" STREQUAL "xCXX")
+    if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+      set(_MDd "")
+      set(_MD "")
+    else()
+      set(_MDd " /MDd")
+      set(_MD " /MD")
+    endif()
     if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       # note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
       # that include MS's own headers. CMake itself is affected project too.
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} -fms-extensions -fms-compatibility -D_WINDOWS -Wall${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd -gline-tables-only -fno-inline -O0 ${_RTC1}")
-      string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD -O2 -DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD -gline-tables-only -O2 -fno-inline -DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0 ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} -O2 -DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} -gline-tables-only -O2 -fno-inline -DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
     else()
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS /W3${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd /Zi /Ob0 /Od ${_RTC1}")
-      string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD /O2 /Ob2 /DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD /Zi /O2 /Ob1 /DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD /O1 /Ob1 /DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} /Zi /Ob0 /Od ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} /Zi /O2 /Ob1 /DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
     endif()
+    unset(_MDd)
+    unset(_MD)
+
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -MT)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -MD)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -MTd)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -MDd)
   endif()
   set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
   set(CMAKE_NINJA_DEPTYPE_${lang} msvc)
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 924d997..49f237f 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -350,8 +350,8 @@
   cmQtAutoGenGlobalInitializer.h
   cmQtAutoGenInitializer.cxx
   cmQtAutoGenInitializer.h
-  cmQtAutoGeneratorMocUic.cxx
-  cmQtAutoGeneratorMocUic.h
+  cmQtAutoMocUic.cxx
+  cmQtAutoMocUic.h
   cmQtAutoRcc.cxx
   cmQtAutoRcc.h
   cmRST.cxx
@@ -391,6 +391,8 @@
   cmVariableWatch.h
   cmVersion.cxx
   cmVersion.h
+  cmWorkerPool.cxx
+  cmWorkerPool.h
   cmWorkingDirectory.cxx
   cmWorkingDirectory.h
   cmXMLParser.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 4e7e400..d4d5da5 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 14)
-set(CMake_VERSION_PATCH 20190415)
+set(CMake_VERSION_PATCH 20190418)
 #set(CMake_VERSION_RC 1)
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index dcb1ff5..897f7a8 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -20,6 +20,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmVersion.h"
+#include "cm_static_string_view.hxx"
 #include "cmake.h"
 
 static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN =
@@ -42,6 +43,8 @@
   "CMAKE_LINK_SEARCH_END_STATIC";
 static std::string const kCMAKE_LINK_SEARCH_START_STATIC =
   "CMAKE_LINK_SEARCH_START_STATIC";
+static std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT =
+  "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT";
 static std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES";
 static std::string const kCMAKE_OSX_DEPLOYMENT_TARGET =
   "CMAKE_OSX_DEPLOYMENT_TARGET";
@@ -500,6 +503,13 @@
       fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def);
     }
 
+    /* Set MSVC runtime library policy to match our selection.  */
+    if (const char* msvcRuntimeLibraryDefault =
+          this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
+      fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
+              *msvcRuntimeLibraryDefault ? "NEW" : "OLD");
+    }
+
     std::string projectLangs;
     for (std::string const& li : testLangs) {
       projectLangs += " " + li;
@@ -660,6 +670,7 @@
       vars.insert(kCMAKE_SYSROOT_COMPILE);
       vars.insert(kCMAKE_SYSROOT_LINK);
       vars.insert(kCMAKE_WARN_DEPRECATED);
+      vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
 
       if (const char* varListStr = this->Makefile->GetDefinition(
             kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index af409e4..8b3d9d6 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -2210,8 +2210,8 @@
     // The file used to link to the target (.so, .lib, .a).
     if (!target->IsLinkable()) {
       ::reportError(context, content->GetOriginalExpression(),
-                    "TARGET_LINKER_OUTPUT_NAME is allowed only for libraries "
-                    "and executables with ENABLE_EXPORTS.");
+                    "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
+                    "libraries and executables with ENABLE_EXPORTS.");
       return std::string();
     }
     cmStateEnums::ArtifactType artifact =
@@ -2232,7 +2232,7 @@
     if (target->IsImported()) {
       ::reportError(
         context, content->GetOriginalExpression(),
-        "TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets.");
+        "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
       return std::string();
     }
 
@@ -2243,7 +2243,7 @@
     if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
       ::reportError(
         context, content->GetOriginalExpression(),
-        "TARGET_PDB_OUTPUT_NAME is not supported by the target linker.");
+        "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
       return std::string();
     }
 
@@ -2253,7 +2253,7 @@
         targetType != cmStateEnums::MODULE_LIBRARY &&
         targetType != cmStateEnums::EXECUTABLE) {
       ::reportError(context, content->GetOriginalExpression(),
-                    "TARGET_PDB_OUTPUT_NAME is allowed only for "
+                    "TARGET_PDB_FILE_BASE_NAME is allowed only for "
                     "targets with linker created artifacts.");
       return std::string();
     }
@@ -2263,9 +2263,9 @@
 };
 
 template <typename ArtifactT>
-struct TargetOutputNameArtifact : public TargetArtifactBase
+struct TargetFileBaseNameArtifact : public TargetArtifactBase
 {
-  TargetOutputNameArtifact() {} // NOLINT(modernize-use-equals-default)
+  TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
 
   int NumExpectedParameters() const override { return 1; }
 
@@ -2290,12 +2290,12 @@
   }
 };
 
-static const TargetOutputNameArtifact<ArtifactNameTag> targetOutputNameNode;
-
-static const TargetOutputNameArtifact<ArtifactLinkerTag>
-  targetLinkerOutputNameNode;
-
-static const TargetOutputNameArtifact<ArtifactPdbTag> targetPdbOutputNameNode;
+static const TargetFileBaseNameArtifact<ArtifactNameTag>
+  targetFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
+  targetLinkerFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactPdbTag>
+  targetPdbFileBaseNameNode;
 
 class ArtifactFilePrefixTag;
 class ArtifactLinkerFilePrefixTag;
@@ -2474,6 +2474,9 @@
     { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
     { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
+    { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
+    { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
+    { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
@@ -2488,9 +2491,6 @@
     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
     { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
-    { "TARGET_OUTPUT_NAME", &targetOutputNameNode },
-    { "TARGET_LINKER_OUTPUT_NAME", &targetLinkerOutputNameNode },
-    { "TARGET_PDB_OUTPUT_NAME", &targetPdbOutputNameNode },
     { "STREQUAL", &strEqualNode },
     { "EQUAL", &equalNode },
     { "IN_LIST", &inListNode },
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 67763d4..8b51834 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -6,6 +6,7 @@
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -1519,8 +1520,40 @@
   flagsVar += "_FLAGS";
   this->AddConfigVariableFlags(flags, flagsVar, config);
 
-  // Placeholder for possible future per-target flags.
-  static_cast<void>(target);
+  // Add MSVC runtime library flags.  This is activated by the presence
+  // of a default selection whether or not it is overridden by a property.
+  const char* msvcRuntimeLibraryDefault =
+    this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
+  if (msvcRuntimeLibraryDefault && *msvcRuntimeLibraryDefault) {
+    const char* msvcRuntimeLibraryValue =
+      target->GetProperty("MSVC_RUNTIME_LIBRARY");
+    if (!msvcRuntimeLibraryValue) {
+      msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
+    }
+    cmGeneratorExpression ge;
+    std::unique_ptr<cmCompiledGeneratorExpression> cge =
+      ge.Parse(msvcRuntimeLibraryValue);
+    std::string const msvcRuntimeLibrary =
+      cge->Evaluate(this, config, false, target);
+    if (!msvcRuntimeLibrary.empty()) {
+      if (const char* msvcRuntimeLibraryOptions =
+            this->Makefile->GetDefinition(
+              "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
+              msvcRuntimeLibrary)) {
+        this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions);
+      } else if ((this->Makefile->GetSafeDefinition(
+                    "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" ||
+                  this->Makefile->GetSafeDefinition(
+                    "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") &&
+                 !cmSystemTools::GetErrorOccuredFlag()) {
+        // The compiler uses the MSVC ABI so it needs a known runtime library.
+        this->IssueMessage(MessageType::FATAL_ERROR,
+                           "MSVC_RUNTIME_LIBRARY value '" +
+                             msvcRuntimeLibrary + "' not known for this " +
+                             lang + " compiler.");
+      }
+    }
+  }
 }
 
 void cmLocalGenerator::AddLanguageFlagsForLinking(
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 02a6295..113dd35 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -267,7 +267,10 @@
          15, 0, cmPolicies::WARN)                                             \
   SELECT(POLICY, CMP0090,                                                     \
          "export(PACKAGE) does not populate package registry by default.", 3, \
-         15, 0, cmPolicies::WARN)
+         15, 0, cmPolicies::WARN)                                             \
+  SELECT(POLICY, CMP0091,                                                     \
+         "MSVC runtime library flags are selected by an abstraction.", 3, 15, \
+         0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index f115016..6fbea82 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -14,12 +14,6 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-#include <algorithm>
-#include <sstream>
-#include <utility>
-
-// -- Class methods
-
 cmQtAutoGenerator::Logger::Logger()
 {
   // Initialize logger
@@ -431,232 +425,6 @@
   return cmQtAutoGenerator::MakeParentDirectory(filename);
 }
 
-int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
-                                                     ReadOnlyProcessT* process)
-{
-  Process_ = process;
-  Target_ = nullptr;
-  return UVPipe_.init(*uv_loop, 0, this);
-}
-
-int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target)
-{
-  Target_ = target;
-  return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData);
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset()
-{
-  Process_ = nullptr;
-  Target_ = nullptr;
-  UVPipe_.reset();
-  Buffer_.clear();
-  Buffer_.shrink_to_fit();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle,
-                                                         size_t suggestedSize,
-                                                         uv_buf_t* buf)
-{
-  auto& pipe = *reinterpret_cast<PipeT*>(handle->data);
-  pipe.Buffer_.resize(suggestedSize);
-  buf->base = pipe.Buffer_.data();
-  buf->len = pipe.Buffer_.size();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream,
-                                                        ssize_t nread,
-                                                        const uv_buf_t* buf)
-{
-  auto& pipe = *reinterpret_cast<PipeT*>(stream->data);
-  if (nread > 0) {
-    // Append data to merged output
-    if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) {
-      pipe.Target_->append(buf->base, nread);
-    }
-  } else if (nread < 0) {
-    // EOF or error
-    auto* proc = pipe.Process_;
-    // Check it this an unusual error
-    if (nread != UV_EOF) {
-      if (!proc->Result()->error()) {
-        proc->Result()->ErrorMessage =
-          "libuv reading from pipe failed with error code ";
-        proc->Result()->ErrorMessage += std::to_string(nread);
-      }
-    }
-    // Clear libuv pipe handle and try to finish
-    pipe.reset();
-    proc->UVTryFinish();
-  }
-}
-
-void cmQtAutoGenerator::ProcessResultT::reset()
-{
-  ExitStatus = 0;
-  TermSignal = 0;
-  if (!StdOut.empty()) {
-    StdOut.clear();
-    StdOut.shrink_to_fit();
-  }
-  if (!StdErr.empty()) {
-    StdErr.clear();
-    StdErr.shrink_to_fit();
-  }
-  if (!ErrorMessage.empty()) {
-    ErrorMessage.clear();
-    ErrorMessage.shrink_to_fit();
-  }
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::setup(
-  ProcessResultT* result, bool mergedOutput,
-  std::vector<std::string> const& command, std::string const& workingDirectory)
-{
-  Setup_.WorkingDirectory = workingDirectory;
-  Setup_.Command = command;
-  Setup_.Result = result;
-  Setup_.MergedOutput = mergedOutput;
-}
-
-static std::string getUVError(const char* prefixString, int uvErrorCode)
-{
-  std::ostringstream ost;
-  ost << prefixString << ": " << uv_strerror(uvErrorCode);
-  return ost.str();
-}
-
-bool cmQtAutoGenerator::ReadOnlyProcessT::start(
-  uv_loop_t* uv_loop, std::function<void()>&& finishedCallback)
-{
-  if (IsStarted() || (Result() == nullptr)) {
-    return false;
-  }
-
-  // Reset result before the start
-  Result()->reset();
-
-  // Fill command string pointers
-  if (!Setup().Command.empty()) {
-    CommandPtr_.reserve(Setup().Command.size() + 1);
-    for (std::string const& arg : Setup().Command) {
-      CommandPtr_.push_back(arg.c_str());
-    }
-    CommandPtr_.push_back(nullptr);
-  } else {
-    Result()->ErrorMessage = "Empty command";
-  }
-
-  if (!Result()->error()) {
-    if (UVPipeOut_.init(uv_loop, this) != 0) {
-      Result()->ErrorMessage = "libuv stdout pipe initialization failed";
-    }
-  }
-  if (!Result()->error()) {
-    if (UVPipeErr_.init(uv_loop, this) != 0) {
-      Result()->ErrorMessage = "libuv stderr pipe initialization failed";
-    }
-  }
-  if (!Result()->error()) {
-    // -- Setup process stdio options
-    // stdin
-    UVOptionsStdIO_[0].flags = UV_IGNORE;
-    UVOptionsStdIO_[0].data.stream = nullptr;
-    // stdout
-    UVOptionsStdIO_[1].flags =
-      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
-    UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
-    // stderr
-    UVOptionsStdIO_[2].flags =
-      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
-    UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
-
-    // -- Setup process options
-    std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
-    UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit;
-    UVOptions_.file = CommandPtr_[0];
-    UVOptions_.args = const_cast<char**>(CommandPtr_.data());
-    UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
-    UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
-    UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
-    UVOptions_.stdio = UVOptionsStdIO_.data();
-
-    // -- Spawn process
-    int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
-    if (uvErrorCode != 0) {
-      Result()->ErrorMessage =
-        getUVError("libuv process spawn failed ", uvErrorCode);
-    }
-  }
-  // -- Start reading from stdio streams
-  if (!Result()->error()) {
-    if (UVPipeOut_.startRead(&Result()->StdOut) != 0) {
-      Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
-    }
-  }
-  if (!Result()->error()) {
-    if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut
-                                                 : &Result()->StdErr) != 0) {
-      Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
-    }
-  }
-
-  if (!Result()->error()) {
-    IsStarted_ = true;
-    FinishedCallback_ = std::move(finishedCallback);
-  } else {
-    // Clear libuv handles and finish
-    UVProcess_.reset();
-    UVPipeOut_.reset();
-    UVPipeErr_.reset();
-    CommandPtr_.clear();
-  }
-
-  return IsStarted();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle,
-                                                 int64_t exitStatus,
-                                                 int termSignal)
-{
-  auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data);
-  if (proc.IsStarted() && !proc.IsFinished()) {
-    // Set error message on demand
-    proc.Result()->ExitStatus = exitStatus;
-    proc.Result()->TermSignal = termSignal;
-    if (!proc.Result()->error()) {
-      if (termSignal != 0) {
-        proc.Result()->ErrorMessage = "Process was terminated by signal ";
-        proc.Result()->ErrorMessage +=
-          std::to_string(proc.Result()->TermSignal);
-      } else if (exitStatus != 0) {
-        proc.Result()->ErrorMessage = "Process failed with return value ";
-        proc.Result()->ErrorMessage +=
-          std::to_string(proc.Result()->ExitStatus);
-      }
-    }
-
-    // Reset process handle and try to finish
-    proc.UVProcess_.reset();
-    proc.UVTryFinish();
-  }
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
-{
-  // There still might be data in the pipes after the process has finished.
-  // Therefore check if the process is finished AND all pipes are closed
-  // before signaling the worker thread to continue.
-  if (UVProcess_.get() == nullptr) {
-    if (UVPipeOut_.uv_pipe() == nullptr) {
-      if (UVPipeErr_.uv_pipe() == nullptr) {
-        IsFinished_ = true;
-        FinishedCallback_();
-      }
-    }
-  }
-}
-
 cmQtAutoGenerator::cmQtAutoGenerator() = default;
 
 cmQtAutoGenerator::~cmQtAutoGenerator() = default;
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 479d357..437fa20 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -7,14 +7,8 @@
 
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
-#include "cmUVHandlePtr.h"
-#include "cm_uv.h"
 
-#include <array>
-#include <functional>
 #include <mutex>
-#include <stddef.h>
-#include <stdint.h>
 #include <string>
 #include <vector>
 
@@ -137,102 +131,6 @@
     cmFilePathChecksum FilePathChecksum_;
   };
 
-  /// @brief Return value and output of an external process
-  struct ProcessResultT
-  {
-    void reset();
-    bool error() const
-    {
-      return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
-    }
-
-    std::int64_t ExitStatus = 0;
-    int TermSignal = 0;
-    std::string StdOut;
-    std::string StdErr;
-    std::string ErrorMessage;
-  };
-
-  /// @brief External process management class
-  struct ReadOnlyProcessT
-  {
-    // -- Types
-
-    /// @brief libuv pipe buffer class
-    class PipeT
-    {
-    public:
-      int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process);
-      int startRead(std::string* target);
-      void reset();
-
-      // -- Libuv casts
-      uv_pipe_t* uv_pipe() { return UVPipe_.get(); }
-      uv_stream_t* uv_stream()
-      {
-        return reinterpret_cast<uv_stream_t*>(uv_pipe());
-      }
-      uv_handle_t* uv_handle()
-      {
-        return reinterpret_cast<uv_handle_t*>(uv_pipe());
-      }
-
-      // -- Libuv callbacks
-      static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
-                          uv_buf_t* buf);
-      static void UVData(uv_stream_t* stream, ssize_t nread,
-                         const uv_buf_t* buf);
-
-    private:
-      ReadOnlyProcessT* Process_ = nullptr;
-      std::string* Target_ = nullptr;
-      std::vector<char> Buffer_;
-      cm::uv_pipe_ptr UVPipe_;
-    };
-
-    /// @brief Process settings
-    struct SetupT
-    {
-      std::string WorkingDirectory;
-      std::vector<std::string> Command;
-      ProcessResultT* Result = nullptr;
-      bool MergedOutput = false;
-    };
-
-    // -- Const accessors
-    const SetupT& Setup() const { return Setup_; }
-    ProcessResultT* Result() const { return Setup_.Result; }
-    bool IsStarted() const { return IsStarted_; }
-    bool IsFinished() const { return IsFinished_; }
-
-    // -- Runtime
-    void setup(ProcessResultT* result, bool mergedOutput,
-               std::vector<std::string> const& command,
-               std::string const& workingDirectory = std::string());
-    bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback);
-
-  private:
-    // -- Friends
-    friend class PipeT;
-    // -- Libuv callbacks
-    static void UVExit(uv_process_t* handle, int64_t exitStatus,
-                       int termSignal);
-    void UVTryFinish();
-
-    // -- Setup
-    SetupT Setup_;
-    // -- Runtime
-    bool IsStarted_ = false;
-    bool IsFinished_ = false;
-    std::function<void()> FinishedCallback_;
-    std::vector<const char*> CommandPtr_;
-    std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
-    uv_process_options_t UVOptions_;
-    cm::uv_process_ptr UVProcess_;
-    PipeT UVPipeOut_;
-    PipeT UVPipeErr_;
-  };
-
 public:
   // -- Constructors
   cmQtAutoGenerator();
diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx
deleted file mode 100644
index ec1a1aa..0000000
--- a/Source/cmQtAutoGeneratorMocUic.cxx
+++ /dev/null
@@ -1,2052 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmQtAutoGeneratorMocUic.h"
-
-#include <algorithm>
-#include <array>
-#include <cstddef>
-#include <list>
-#include <memory>
-#include <set>
-#include <sstream>
-#include <utility>
-
-#include "cmAlgorithms.h"
-#include "cmCryptoHash.h"
-#include "cmMakefile.h"
-#include "cmQtAutoGen.h"
-#include "cmSystemTools.h"
-#include "cmake.h"
-
-#if defined(__APPLE__)
-#  include <unistd.h>
-#endif
-
-// -- Class methods
-
-std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
-  std::string const& relativePath) const
-{
-  return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
-}
-
-/**
- * @brief Tries to find the header file to the given file base path by
- * appending different header extensions
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader(
-  std::string& header, std::string const& testBasePath) const
-{
-  for (std::string const& ext : HeaderExtensions) {
-    std::string testFilePath(testBasePath);
-    testFilePath.push_back('.');
-    testFilePath += ext;
-    if (FileSys->FileExists(testFilePath)) {
-      header = testFilePath;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
-  std::string const& fileName) const
-{
-  return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
-}
-
-/**
- * @brief Returns the first relevant Qt macro name found in the given C++ code
- * @return The name of the Qt macro or an empty string
- */
-std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro(
-  std::string const& content) const
-{
-  for (KeyExpT const& filter : MacroFilters) {
-    // Run a simple find string operation before the expensive
-    // regular expression check
-    if (content.find(filter.Key) != std::string::npos) {
-      cmsys::RegularExpressionMatch match;
-      if (filter.Exp.find(content.c_str(), match)) {
-        // Return macro name on demand
-        return filter.Key;
-      }
-    }
-  }
-  return std::string();
-}
-
-std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const
-{
-  std::string res;
-  const auto itB = MacroFilters.cbegin();
-  const auto itE = MacroFilters.cend();
-  const auto itL = itE - 1;
-  auto itC = itB;
-  for (; itC != itE; ++itC) {
-    // Separator
-    if (itC != itB) {
-      if (itC != itL) {
-        res += ", ";
-      } else {
-        res += " or ";
-      }
-    }
-    // Key
-    res += itC->Key;
-  }
-  return res;
-}
-
-std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile(
-  std::string const& sourcePath, std::string const& includeString) const
-{
-  // Search in vicinity of the source
-  {
-    std::string testPath = sourcePath;
-    testPath += includeString;
-    if (FileSys->FileExists(testPath)) {
-      return FileSys->GetRealPath(testPath);
-    }
-  }
-  // Search in include directories
-  for (std::string const& path : IncludePaths) {
-    std::string fullPath = path;
-    fullPath.push_back('/');
-    fullPath += includeString;
-    if (FileSys->FileExists(fullPath)) {
-      return FileSys->GetRealPath(fullPath);
-    }
-  }
-  // Return empty string
-  return std::string();
-}
-
-void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
-  std::string const& content, std::set<std::string>& depends) const
-{
-  if (!DependFilters.empty() && !content.empty()) {
-    for (KeyExpT const& filter : DependFilters) {
-      // Run a simple find string check
-      if (content.find(filter.Key) != std::string::npos) {
-        // Run the expensive regular expression check loop
-        const char* contentChars = content.c_str();
-        cmsys::RegularExpressionMatch match;
-        while (filter.Exp.find(contentChars, match)) {
-          {
-            std::string dep = match.match(1);
-            if (!dep.empty()) {
-              depends.emplace(std::move(dep));
-            }
-          }
-          contentChars += match.end();
-        }
-      }
-    }
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped(
-  std::string const& fileName) const
-{
-  return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
-}
-
-void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk)
-{
-  if (AutoMoc && Header) {
-    // Don't parse header for moc if the file is included by a source already
-    if (wrk.Gen().ParallelMocIncluded(FileName)) {
-      AutoMoc = false;
-    }
-  }
-
-  if (AutoMoc || AutoUic) {
-    std::string error;
-    MetaT meta;
-    if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
-      if (!meta.Content.empty()) {
-        meta.FileDir = wrk.FileSys().SubDirPrefix(FileName);
-        meta.FileBase =
-          wrk.FileSys().GetFilenameWithoutLastExtension(FileName);
-
-        bool success = true;
-        if (AutoMoc) {
-          if (Header) {
-            success = ParseMocHeader(wrk, meta);
-          } else {
-            success = ParseMocSource(wrk, meta);
-          }
-        }
-        if (AutoUic && success) {
-          ParseUic(wrk, meta);
-        }
-      } else {
-        wrk.LogFileWarning(GenT::GEN, FileName, "The source file is empty");
-      }
-    } else {
-      wrk.LogFileError(GenT::GEN, FileName,
-                       "Could not read the file: " + error);
-    }
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk,
-                                                        MetaT const& meta)
-{
-  struct JobPre
-  {
-    bool self;       // source file is self
-    bool underscore; // "moc_" style include
-    std::string SourceFile;
-    std::string IncludeString;
-  };
-
-  struct MocInclude
-  {
-    std::string Inc;  // full include string
-    std::string Dir;  // include string directory
-    std::string Base; // include string file base
-  };
-
-  // Check if this source file contains a relevant macro
-  std::string const ownMacro = wrk.Moc().FindMacro(meta.Content);
-
-  // Extract moc includes from file
-  std::deque<MocInclude> mocIncsUsc;
-  std::deque<MocInclude> mocIncsDot;
-  {
-    if (meta.Content.find("moc") != std::string::npos) {
-      const char* contentChars = meta.Content.c_str();
-      cmsys::RegularExpressionMatch match;
-      while (wrk.Moc().RegExpInclude.find(contentChars, match)) {
-        std::string incString = match.match(2);
-        std::string incDir(wrk.FileSys().SubDirPrefix(incString));
-        std::string incBase =
-          wrk.FileSys().GetFilenameWithoutLastExtension(incString);
-        if (cmHasLiteralPrefix(incBase, "moc_")) {
-          // moc_<BASE>.cxx
-          // Remove the moc_ part from the base name
-          mocIncsUsc.emplace_back(MocInclude{
-            std::move(incString), std::move(incDir), incBase.substr(4) });
-        } else {
-          // <BASE>.moc
-          mocIncsDot.emplace_back(MocInclude{
-            std::move(incString), std::move(incDir), std::move(incBase) });
-        }
-        // Forward content pointer
-        contentChars += match.end();
-      }
-    }
-  }
-
-  // Check if there is anything to do
-  if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
-    return true;
-  }
-
-  bool ownDotMocIncluded = false;
-  bool ownMocUscIncluded = false;
-  std::deque<JobPre> jobs;
-
-  // Process moc_<BASE>.cxx includes
-  for (const MocInclude& mocInc : mocIncsUsc) {
-    std::string const header =
-      MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
-    if (!header.empty()) {
-      // Check if header is skipped
-      if (wrk.Moc().skipped(header)) {
-        continue;
-      }
-      // Register moc job
-      const bool ownMoc = (mocInc.Base == meta.FileBase);
-      jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
-      // Store meta information for relaxed mode
-      if (ownMoc) {
-        ownMocUscIncluded = true;
-      }
-    } else {
-      {
-        std::string emsg = "The file includes the moc file ";
-        emsg += Quoted(mocInc.Inc);
-        emsg += ", but the header ";
-        emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
-        emsg += " could not be found.";
-        wrk.LogFileError(GenT::MOC, FileName, emsg);
-      }
-      return false;
-    }
-  }
-
-  // Process <BASE>.moc includes
-  for (const MocInclude& mocInc : mocIncsDot) {
-    const bool ownMoc = (mocInc.Base == meta.FileBase);
-    if (wrk.Moc().RelaxedMode) {
-      // Relaxed mode
-      if (!ownMacro.empty() && ownMoc) {
-        // Add self
-        jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
-        ownDotMocIncluded = true;
-      } else {
-        // In relaxed mode try to find a header instead but issue a warning.
-        // This is for KDE4 compatibility
-        std::string const header =
-          MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
-        if (!header.empty()) {
-          // Check if header is skipped
-          if (wrk.Moc().skipped(header)) {
-            continue;
-          }
-          // Register moc job
-          jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
-          if (ownMacro.empty()) {
-            if (ownMoc) {
-              std::string emsg = "The file includes the moc file ";
-              emsg += Quoted(mocInc.Inc);
-              emsg += ", but does not contain a ";
-              emsg += wrk.Moc().MacrosString();
-              emsg += " macro.\nRunning moc on\n  ";
-              emsg += Quoted(header);
-              emsg += "!\nBetter include ";
-              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += " for a compatibility with strict mode.\n"
-                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-              wrk.LogFileWarning(GenT::MOC, FileName, emsg);
-            } else {
-              std::string emsg = "The file includes the moc file ";
-              emsg += Quoted(mocInc.Inc);
-              emsg += " instead of ";
-              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += ".\nRunning moc on\n  ";
-              emsg += Quoted(header);
-              emsg += "!\nBetter include ";
-              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += " for compatibility with strict mode.\n"
-                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-              wrk.LogFileWarning(GenT::MOC, FileName, emsg);
-            }
-          }
-        } else {
-          {
-            std::string emsg = "The file includes the moc file ";
-            emsg += Quoted(mocInc.Inc);
-            emsg += ", which seems to be the moc file from a different "
-                    "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
-                    "matching header ";
-            emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
-            emsg += " could not be found.";
-            wrk.LogFileError(GenT::MOC, FileName, emsg);
-          }
-          return false;
-        }
-      }
-    } else {
-      // Strict mode
-      if (ownMoc) {
-        // Include self
-        jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
-        ownDotMocIncluded = true;
-        // Accept but issue a warning if moc isn't required
-        if (ownMacro.empty()) {
-          std::string emsg = "The file includes the moc file ";
-          emsg += Quoted(mocInc.Inc);
-          emsg += ", but does not contain a ";
-          emsg += wrk.Moc().MacrosString();
-          emsg += " macro.";
-          wrk.LogFileWarning(GenT::MOC, FileName, emsg);
-        }
-      } else {
-        // Don't allow <BASE>.moc include other than self in strict mode
-        {
-          std::string emsg = "The file includes the moc file ";
-          emsg += Quoted(mocInc.Inc);
-          emsg += ", which seems to be the moc file from a different "
-                  "source file.\nThis is not supported. Include ";
-          emsg += Quoted(meta.FileBase + ".moc");
-          emsg += " to run moc on this source file.";
-          wrk.LogFileError(GenT::MOC, FileName, emsg);
-        }
-        return false;
-      }
-    }
-  }
-
-  if (!ownMacro.empty() && !ownDotMocIncluded) {
-    // In this case, check whether the scanned file itself contains a
-    // Q_OBJECT.
-    // If this is the case, the moc_foo.cpp should probably be generated from
-    // foo.cpp instead of foo.h, because otherwise it won't build.
-    // But warn, since this is not how it is supposed to be used.
-    // This is for KDE4 compatibility.
-    if (wrk.Moc().RelaxedMode && ownMocUscIncluded) {
-      JobPre uscJobPre;
-      // Remove underscore job request
-      {
-        auto itC = jobs.begin();
-        auto itE = jobs.end();
-        for (; itC != itE; ++itC) {
-          JobPre& job(*itC);
-          if (job.self && job.underscore) {
-            uscJobPre = std::move(job);
-            jobs.erase(itC);
-            break;
-          }
-        }
-      }
-      // Issue a warning
-      {
-        std::string emsg = "The file contains a ";
-        emsg += ownMacro;
-        emsg += " macro, but does not include ";
-        emsg += Quoted(meta.FileBase + ".moc");
-        emsg += ". Instead it includes ";
-        emsg += Quoted(uscJobPre.IncludeString);
-        emsg += ".\nRunning moc on\n  ";
-        emsg += Quoted(FileName);
-        emsg += "!\nBetter include ";
-        emsg += Quoted(meta.FileBase + ".moc");
-        emsg += " for compatibility with strict mode.\n"
-                "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
-        wrk.LogFileWarning(GenT::MOC, FileName, emsg);
-      }
-      // Add own source job
-      jobs.emplace_back(
-        JobPre{ true, false, FileName, uscJobPre.IncludeString });
-    } else {
-      // Otherwise always error out since it will not compile.
-      {
-        std::string emsg = "The file contains a ";
-        emsg += ownMacro;
-        emsg += " macro, but does not include ";
-        emsg += Quoted(meta.FileBase + ".moc");
-        emsg += "!\nConsider to\n - add #include \"";
-        emsg += meta.FileBase;
-        emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
-        wrk.LogFileError(GenT::MOC, FileName, emsg);
-      }
-      return false;
-    }
-  }
-
-  // Convert pre jobs to actual jobs
-  for (JobPre& jobPre : jobs) {
-    JobHandleT jobHandle = cm::make_unique<JobMocT>(
-      std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
-    if (jobPre.self) {
-      // Read dependencies from this source
-      static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
-    }
-    if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk,
-                                                        MetaT const& meta)
-{
-  bool success = true;
-  std::string const macroName = wrk.Moc().FindMacro(meta.Content);
-  if (!macroName.empty()) {
-    JobHandleT jobHandle = cm::make_unique<JobMocT>(
-      std::string(FileName), std::string(), std::string());
-    // Read dependencies from this source
-    static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
-    success = wrk.Gen().ParallelJobPushMoc(jobHandle);
-  }
-  return success;
-}
-
-std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders(
-  WorkerT& wrk, std::string const& fileBase) const
-{
-  std::string res = fileBase;
-  res += ".{";
-  res += cmJoin(wrk.Base().HeaderExtensions, ",");
-  res += "}";
-  return res;
-}
-
-std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader(
-  WorkerT& wrk, std::string const& includerDir, std::string const& includeBase)
-{
-  std::string header;
-  // Search in vicinity of the source
-  if (!wrk.Base().FindHeader(header, includerDir + includeBase)) {
-    // Search in include directories
-    for (std::string const& path : wrk.Moc().IncludePaths) {
-      std::string fullPath = path;
-      fullPath.push_back('/');
-      fullPath += includeBase;
-      if (wrk.Base().FindHeader(header, fullPath)) {
-        break;
-      }
-    }
-  }
-  // Sanitize
-  if (!header.empty()) {
-    header = wrk.FileSys().GetRealPath(header);
-  }
-  return header;
-}
-
-bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk,
-                                                  MetaT const& meta)
-{
-  bool success = true;
-  if (meta.Content.find("ui_") != std::string::npos) {
-    const char* contentChars = meta.Content.c_str();
-    cmsys::RegularExpressionMatch match;
-    while (wrk.Uic().RegExpInclude.find(contentChars, match)) {
-      if (!ParseUicInclude(wrk, meta, match.match(2))) {
-        success = false;
-        break;
-      }
-      contentChars += match.end();
-    }
-  }
-  return success;
-}
-
-bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude(
-  WorkerT& wrk, MetaT const& meta, std::string&& includeString)
-{
-  bool success = false;
-  std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString);
-  if (!uiInputFile.empty()) {
-    if (!wrk.Uic().skipped(uiInputFile)) {
-      JobHandleT jobHandle = cm::make_unique<JobUicT>(
-        std::move(uiInputFile), FileName, std::move(includeString));
-      success = wrk.Gen().ParallelJobPushUic(jobHandle);
-    } else {
-      // A skipped file is successful
-      success = true;
-    }
-  }
-  return success;
-}
-
-std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile(
-  WorkerT& wrk, MetaT const& meta, std::string const& includeString)
-{
-  std::string res;
-  std::string searchFile =
-    wrk.FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
-  searchFile += ".ui";
-  // Collect search paths list
-  std::deque<std::string> testFiles;
-  {
-    std::string const searchPath = wrk.FileSys().SubDirPrefix(includeString);
-
-    std::string searchFileFull;
-    if (!searchPath.empty()) {
-      searchFileFull = searchPath;
-      searchFileFull += searchFile;
-    }
-    // Vicinity of the source
-    {
-      std::string const sourcePath = meta.FileDir;
-      testFiles.push_back(sourcePath + searchFile);
-      if (!searchPath.empty()) {
-        testFiles.push_back(sourcePath + searchFileFull);
-      }
-    }
-    // AUTOUIC search paths
-    if (!wrk.Uic().SearchPaths.empty()) {
-      for (std::string const& sPath : wrk.Uic().SearchPaths) {
-        testFiles.push_back((sPath + "/").append(searchFile));
-      }
-      if (!searchPath.empty()) {
-        for (std::string const& sPath : wrk.Uic().SearchPaths) {
-          testFiles.push_back((sPath + "/").append(searchFileFull));
-        }
-      }
-    }
-  }
-
-  // Search for the .ui file!
-  for (std::string const& testFile : testFiles) {
-    if (wrk.FileSys().FileExists(testFile)) {
-      res = wrk.FileSys().GetRealPath(testFile);
-      break;
-    }
-  }
-
-  // Log error
-  if (res.empty()) {
-    std::string emsg = "Could not find ";
-    emsg += Quoted(searchFile);
-    emsg += " in\n";
-    for (std::string const& testFile : testFiles) {
-      emsg += "  ";
-      emsg += Quoted(testFile);
-      emsg += "\n";
-    }
-    wrk.LogFileError(GenT::UIC, FileName, emsg);
-  }
-
-  return res;
-}
-
-void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk)
-{
-  // (Re)generate moc_predefs.h on demand
-  bool generate(false);
-  bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs));
-  if (!fileExists) {
-    if (wrk.Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(wrk.Moc().PredefsFileRel);
-      reason += " because it doesn't exist";
-      wrk.LogInfo(GenT::MOC, reason);
-    }
-    generate = true;
-  } else if (wrk.Moc().SettingsChanged) {
-    if (wrk.Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(wrk.Moc().PredefsFileRel);
-      reason += " because the settings changed.";
-      wrk.LogInfo(GenT::MOC, reason);
-    }
-    generate = true;
-  }
-  if (generate) {
-    ProcessResultT result;
-    {
-      // Compose command
-      std::vector<std::string> cmd = wrk.Moc().PredefsCmd;
-      // Add includes
-      cmd.insert(cmd.end(), wrk.Moc().Includes.begin(),
-                 wrk.Moc().Includes.end());
-      // Add definitions
-      for (std::string const& def : wrk.Moc().Definitions) {
-        cmd.push_back("-D" + def);
-      }
-      // Execute command
-      if (!wrk.RunProcess(GenT::MOC, result, cmd)) {
-        std::string emsg = "The content generation command for ";
-        emsg += Quoted(wrk.Moc().PredefsFileRel);
-        emsg += " failed.\n";
-        emsg += result.ErrorMessage;
-        wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
-      }
-    }
-
-    // (Re)write predefs file only on demand
-    if (!result.error()) {
-      if (!fileExists ||
-          wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
-        std::string error;
-        if (wrk.FileSys().FileWrite(wrk.Moc().PredefsFileAbs, result.StdOut,
-                                    &error)) {
-          // Success
-        } else {
-          std::string emsg = "Writing ";
-          emsg += Quoted(wrk.Moc().PredefsFileRel);
-          emsg += " failed. ";
-          emsg += error;
-          wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg);
-        }
-      } else {
-        // Touch to update the time stamp
-        if (wrk.Log().Verbose()) {
-          std::string msg = "Touching ";
-          msg += Quoted(wrk.Moc().PredefsFileRel);
-          msg += ".";
-          wrk.LogInfo(GenT::MOC, msg);
-        }
-        wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
-      }
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies(
-  WorkerT& wrk, std::string const& content)
-{
-  wrk.Moc().FindDependencies(content, Depends);
-  DependsValid = true;
-}
-
-void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk)
-{
-  // Compute build file name
-  if (!IncludeString.empty()) {
-    BuildFile = wrk.Base().AutogenIncludeDir;
-    BuildFile += '/';
-    BuildFile += IncludeString;
-  } else {
-    // Relative build path
-    std::string relPath = wrk.FileSys().GetFilePathChecksum(SourceFile);
-    relPath += "/moc_";
-    relPath += wrk.FileSys().GetFilenameWithoutLastExtension(SourceFile);
-
-    // Register relative file path with duplication check
-    relPath = wrk.Gen().ParallelMocAutoRegister(relPath);
-
-    // Absolute build path
-    if (wrk.Base().MultiConfig) {
-      BuildFile = wrk.Base().AutogenIncludeDir;
-      BuildFile += '/';
-      BuildFile += relPath;
-    } else {
-      BuildFile = wrk.Base().AbsoluteBuildPath(relPath);
-    }
-  }
-
-  if (UpdateRequired(wrk)) {
-    GenerateMoc(wrk);
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk)
-{
-  bool const verbose = wrk.Gen().Log().Verbose();
-
-  // Test if the build file exists
-  if (!wrk.FileSys().FileExists(BuildFile)) {
-    if (verbose) {
-      std::string reason = "Generating ";
-      reason += Quoted(BuildFile);
-      reason += " from its source file ";
-      reason += Quoted(SourceFile);
-      reason += " because it doesn't exist";
-      wrk.LogInfo(GenT::MOC, reason);
-    }
-    return true;
-  }
-
-  // Test if any setting changed
-  if (wrk.Moc().SettingsChanged) {
-    if (verbose) {
-      std::string reason = "Generating ";
-      reason += Quoted(BuildFile);
-      reason += " from ";
-      reason += Quoted(SourceFile);
-      reason += " because the MOC settings changed";
-      wrk.LogInfo(GenT::MOC, reason);
-    }
-    return true;
-  }
-
-  // Test if the moc_predefs file is newer
-  if (!wrk.Moc().PredefsFileAbs.empty()) {
-    bool isOlder = false;
-    {
-      std::string error;
-      isOlder = wrk.FileSys().FileIsOlderThan(
-        BuildFile, wrk.Moc().PredefsFileAbs, &error);
-      if (!isOlder && !error.empty()) {
-        wrk.LogError(GenT::MOC, error);
-        return false;
-      }
-    }
-    if (isOlder) {
-      if (verbose) {
-        std::string reason = "Generating ";
-        reason += Quoted(BuildFile);
-        reason += " because it's older than: ";
-        reason += Quoted(wrk.Moc().PredefsFileAbs);
-        wrk.LogInfo(GenT::MOC, reason);
-      }
-      return true;
-    }
-  }
-
-  // Test if the source file is newer
-  {
-    bool isOlder = false;
-    {
-      std::string error;
-      isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
-      if (!isOlder && !error.empty()) {
-        wrk.LogError(GenT::MOC, error);
-        return false;
-      }
-    }
-    if (isOlder) {
-      if (verbose) {
-        std::string reason = "Generating ";
-        reason += Quoted(BuildFile);
-        reason += " because it's older than its source file ";
-        reason += Quoted(SourceFile);
-        wrk.LogInfo(GenT::MOC, reason);
-      }
-      return true;
-    }
-  }
-
-  // Test if a dependency file is newer
-  {
-    // Read dependencies on demand
-    if (!DependsValid) {
-      std::string content;
-      {
-        std::string error;
-        if (!wrk.FileSys().FileRead(content, SourceFile, &error)) {
-          std::string emsg = "Could not read file\n  ";
-          emsg += Quoted(SourceFile);
-          emsg += "\nrequired by moc include ";
-          emsg += Quoted(IncludeString);
-          emsg += " in\n  ";
-          emsg += Quoted(IncluderFile);
-          emsg += ".\n";
-          emsg += error;
-          wrk.LogError(GenT::MOC, emsg);
-          return false;
-        }
-      }
-      FindDependencies(wrk, content);
-    }
-    // Check dependency timestamps
-    std::string error;
-    std::string sourceDir = wrk.FileSys().SubDirPrefix(SourceFile);
-    for (std::string const& depFileRel : Depends) {
-      std::string depFileAbs =
-        wrk.Moc().FindIncludedFile(sourceDir, depFileRel);
-      if (!depFileAbs.empty()) {
-        if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
-          if (verbose) {
-            std::string reason = "Generating ";
-            reason += Quoted(BuildFile);
-            reason += " from ";
-            reason += Quoted(SourceFile);
-            reason += " because it is older than it's dependency file ";
-            reason += Quoted(depFileAbs);
-            wrk.LogInfo(GenT::MOC, reason);
-          }
-          return true;
-        }
-        if (!error.empty()) {
-          wrk.LogError(GenT::MOC, error);
-          return false;
-        }
-      } else {
-        std::string message = "Could not find dependency file ";
-        message += Quoted(depFileRel);
-        wrk.LogFileWarning(GenT::MOC, SourceFile, message);
-      }
-    }
-  }
-
-  return false;
-}
-
-void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
-{
-  // Make sure the parent directory exists
-  if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
-    wrk.LogFileError(GenT::MOC, BuildFile,
-                     "Could not create parent directory.");
-    return;
-  }
-  {
-    // Compose moc command
-    std::vector<std::string> cmd;
-    cmd.push_back(wrk.Moc().Executable);
-    // Add options
-    cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(),
-               wrk.Moc().AllOptions.end());
-    // Add predefs include
-    if (!wrk.Moc().PredefsFileAbs.empty()) {
-      cmd.emplace_back("--include");
-      cmd.push_back(wrk.Moc().PredefsFileAbs);
-    }
-    cmd.emplace_back("-o");
-    cmd.push_back(BuildFile);
-    cmd.push_back(SourceFile);
-
-    // Execute moc command
-    ProcessResultT result;
-    if (wrk.RunProcess(GenT::MOC, result, cmd)) {
-      // Moc command success
-      // Print moc output
-      if (!result.StdOut.empty()) {
-        wrk.LogInfo(GenT::MOC, result.StdOut);
-      }
-      // Notify the generator that a not included file changed (on demand)
-      if (IncludeString.empty()) {
-        wrk.Gen().ParallelMocAutoUpdated();
-      }
-    } else {
-      // Moc command failed
-      {
-        std::string emsg = "The moc process failed to compile\n  ";
-        emsg += Quoted(SourceFile);
-        emsg += "\ninto\n  ";
-        emsg += Quoted(BuildFile);
-        emsg += ".\n";
-        emsg += result.ErrorMessage;
-        wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
-      }
-      wrk.FileSys().FileRemove(BuildFile);
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
-{
-  // Compute build file name
-  BuildFile = wrk.Base().AutogenIncludeDir;
-  BuildFile += '/';
-  BuildFile += IncludeString;
-
-  if (UpdateRequired(wrk)) {
-    GenerateUic(wrk);
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk)
-{
-  bool const verbose = wrk.Gen().Log().Verbose();
-
-  // Test if the build file exists
-  if (!wrk.FileSys().FileExists(BuildFile)) {
-    if (verbose) {
-      std::string reason = "Generating ";
-      reason += Quoted(BuildFile);
-      reason += " from its source file ";
-      reason += Quoted(SourceFile);
-      reason += " because it doesn't exist";
-      wrk.LogInfo(GenT::UIC, reason);
-    }
-    return true;
-  }
-
-  // Test if the uic settings changed
-  if (wrk.Uic().SettingsChanged) {
-    if (verbose) {
-      std::string reason = "Generating ";
-      reason += Quoted(BuildFile);
-      reason += " from ";
-      reason += Quoted(SourceFile);
-      reason += " because the UIC settings changed";
-      wrk.LogInfo(GenT::UIC, reason);
-    }
-    return true;
-  }
-
-  // Test if the source file is newer
-  {
-    bool isOlder = false;
-    {
-      std::string error;
-      isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
-      if (!isOlder && !error.empty()) {
-        wrk.LogError(GenT::UIC, error);
-        return false;
-      }
-    }
-    if (isOlder) {
-      if (verbose) {
-        std::string reason = "Generating ";
-        reason += Quoted(BuildFile);
-        reason += " because it's older than its source file ";
-        reason += Quoted(SourceFile);
-        wrk.LogInfo(GenT::UIC, reason);
-      }
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
-{
-  // Make sure the parent directory exists
-  if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
-    wrk.LogFileError(GenT::UIC, BuildFile,
-                     "Could not create parent directory.");
-    return;
-  }
-  {
-    // Compose uic command
-    std::vector<std::string> cmd;
-    cmd.push_back(wrk.Uic().Executable);
-    {
-      std::vector<std::string> allOpts = wrk.Uic().TargetOptions;
-      auto optionIt = wrk.Uic().Options.find(SourceFile);
-      if (optionIt != wrk.Uic().Options.end()) {
-        UicMergeOptions(allOpts, optionIt->second,
-                        (wrk.Base().QtVersionMajor == 5));
-      }
-      cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
-    }
-    cmd.emplace_back("-o");
-    cmd.push_back(BuildFile);
-    cmd.push_back(SourceFile);
-
-    ProcessResultT result;
-    if (wrk.RunProcess(GenT::UIC, result, cmd)) {
-      // Uic command success
-      // Print uic output
-      if (!result.StdOut.empty()) {
-        wrk.LogInfo(GenT::UIC, result.StdOut);
-      }
-    } else {
-      // Uic command failed
-      {
-        std::string emsg = "The uic process failed to compile\n  ";
-        emsg += Quoted(SourceFile);
-        emsg += "\ninto\n  ";
-        emsg += Quoted(BuildFile);
-        emsg += "\nincluded by\n  ";
-        emsg += Quoted(IncluderFile);
-        emsg += ".\n";
-        emsg += result.ErrorMessage;
-        wrk.LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
-      }
-      wrk.FileSys().FileRemove(BuildFile);
-    }
-  }
-}
-
-cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen,
-                                          uv_loop_t* uvLoop)
-  : Gen_(gen)
-{
-  // Initialize uv asynchronous callback for process starting
-  ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this);
-  // Start thread
-  Thread_ = std::thread(&WorkerT::Loop, this);
-}
-
-cmQtAutoGeneratorMocUic::WorkerT::~WorkerT()
-{
-  // Join thread
-  if (Thread_.joinable()) {
-    Thread_.join();
-  }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
-  GenT genType, std::string const& message) const
-{
-  Log().Info(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
-  GenT genType, std::string const& message) const
-{
-  Log().Warning(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
-  GenT genType, std::string const& filename, std::string const& message) const
-{
-  Log().WarningFile(genType, filename, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogError(
-  GenT genType, std::string const& message) const
-{
-  Gen().ParallelRegisterJobError();
-  Log().Error(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
-  GenT genType, std::string const& filename, std::string const& message) const
-{
-  Gen().ParallelRegisterJobError();
-  Log().ErrorFile(genType, filename, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
-  GenT genType, std::string const& message,
-  std::vector<std::string> const& command, std::string const& output) const
-{
-  Gen().ParallelRegisterJobError();
-  Log().ErrorCommand(genType, message, command, output);
-}
-
-bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess(
-  GenT genType, ProcessResultT& result,
-  std::vector<std::string> const& command)
-{
-  if (command.empty()) {
-    return false;
-  }
-
-  // Create process instance
-  {
-    std::lock_guard<std::mutex> lock(ProcessMutex_);
-    Process_ = cm::make_unique<ReadOnlyProcessT>();
-    Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir);
-  }
-
-  // Send asynchronous process start request to libuv loop
-  ProcessRequest_.send();
-
-  // Log command
-  if (this->Log().Verbose()) {
-    std::string msg = "Running command:\n";
-    msg += QuotedCommand(command);
-    msg += '\n';
-    this->LogInfo(genType, msg);
-  }
-
-  // Wait until the process has been finished and destroyed
-  {
-    std::unique_lock<std::mutex> ulock(ProcessMutex_);
-    while (Process_) {
-      ProcessCondition_.wait(ulock);
-    }
-  }
-  return !result.error();
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::Loop()
-{
-  while (true) {
-    Gen().WorkerSwapJob(JobHandle_);
-    if (JobHandle_) {
-      JobHandle_->Process(*this);
-    } else {
-      break;
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
-{
-  auto& wrk = *reinterpret_cast<WorkerT*>(handle->data);
-  {
-    std::lock_guard<std::mutex> lock(wrk.ProcessMutex_);
-    if (wrk.Process_ && !wrk.Process_->IsStarted()) {
-      wrk.Process_->start(handle->loop, [&wrk] { wrk.UVProcessFinished(); });
-    }
-  }
-
-  if (!wrk.Process_->IsStarted()) {
-    wrk.UVProcessFinished();
-  }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
-{
-  {
-    std::lock_guard<std::mutex> lock(ProcessMutex_);
-    if (Process_ && (Process_->IsFinished() || !Process_->IsStarted())) {
-      Process_.reset();
-    }
-  }
-  // Notify idling thread
-  ProcessCondition_.notify_one();
-}
-
-cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
-  : Base_(&FileSys())
-  , Moc_(&FileSys())
-{
-  // Precompile regular expressions
-  Moc_.RegExpInclude.compile(
-    "(^|\n)[ \t]*#[ \t]*include[ \t]+"
-    "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
-  Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
-                             "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
-
-  // Initialize libuv loop
-  uv_disable_stdio_inheritance();
-#ifdef CMAKE_UV_SIGNAL_HACK
-  UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
-#endif
-  UVLoop_ = cm::make_unique<uv_loop_t>();
-  uv_loop_init(UVLoop());
-
-  // Initialize libuv asynchronous iteration request
-  UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
-}
-
-cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
-{
-  // Close libuv loop
-  uv_loop_close(UVLoop());
-}
-
-bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
-{
-  // -- Meta
-  Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
-
-  // Utility lambdas
-  auto InfoGet = [makefile](const char* key) {
-    return makefile->GetSafeDefinition(key);
-  };
-  auto InfoGetBool = [makefile](const char* key) {
-    return makefile->IsOn(key);
-  };
-  auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
-    return list;
-  };
-  auto InfoGetLists =
-    [makefile](const char* key) -> std::vector<std::vector<std::string>> {
-    std::vector<std::vector<std::string>> lists;
-    {
-      std::string const value = makefile->GetSafeDefinition(key);
-      std::string::size_type pos = 0;
-      while (pos < value.size()) {
-        std::string::size_type next = value.find(ListSep, pos);
-        std::string::size_type length =
-          (next != std::string::npos) ? next - pos : value.size() - pos;
-        // Remove enclosing braces
-        if (length >= 2) {
-          std::string::const_iterator itBeg = value.begin() + (pos + 1);
-          std::string::const_iterator itEnd = itBeg + (length - 2);
-          {
-            std::string subValue(itBeg, itEnd);
-            std::vector<std::string> list;
-            cmSystemTools::ExpandListArgument(subValue, list);
-            lists.push_back(std::move(list));
-          }
-        }
-        pos += length;
-        pos += ListSep.size();
-      }
-    }
-    return lists;
-  };
-  auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
-    const char* valueConf = nullptr;
-    {
-      std::string keyConf = key;
-      keyConf += '_';
-      keyConf += InfoConfig();
-      valueConf = makefile->GetDefinition(keyConf);
-    }
-    if (valueConf == nullptr) {
-      return makefile->GetSafeDefinition(key);
-    }
-    return std::string(valueConf);
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](const char* key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
-    return list;
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
-    return false;
-  }
-
-  // -- Meta
-  Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
-  Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
-  {
-    unsigned long num = Base_.NumThreads;
-    if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
-      num = std::max<unsigned long>(num, 1);
-      num = std::min<unsigned long>(num, ParallelMax);
-      Base_.NumThreads = static_cast<unsigned int>(num);
-    }
-  }
-
-  // - Files and directories
-  Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
-  Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
-  Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
-  Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
-  Base_.IncludeProjectDirsBefore =
-    InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
-  Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
-  if (Base_.AutogenBuildDir.empty()) {
-    Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
-    return false;
-  }
-  // include directory
-  Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
-  if (Base_.AutogenIncludeDir.empty()) {
-    Log().ErrorFile(GenT::GEN, InfoFile(),
-                    "Autogen include directory missing");
-    return false;
-  }
-
-  // - Files
-  SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
-  if (SettingsFile_.empty()) {
-    Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
-    return false;
-  }
-
-  // - Qt environment
-  {
-    unsigned long qtv = Base_.QtVersionMajor;
-    if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
-                                     &qtv)) {
-      Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
-    }
-  }
-
-  // - Moc
-  Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
-  Moc_.Enabled = !Moc().Executable.empty();
-  if (Moc().Enabled) {
-    for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
-      Moc_.SkipList.insert(std::move(sfl));
-    }
-    Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
-    Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
-    Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
-    Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
-    for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
-      Moc_.MacroFilters.emplace_back(
-        item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
-    }
-    {
-      auto pushFilter = [this](std::string const& key, std::string const& exp,
-                               std::string& error) {
-        if (!key.empty()) {
-          if (!exp.empty()) {
-            Moc_.DependFilters.emplace_back();
-            KeyExpT& filter(Moc_.DependFilters.back());
-            if (filter.Exp.compile(exp)) {
-              filter.Key = key;
-            } else {
-              error = "Regular expression compiling failed";
-            }
-          } else {
-            error = "Regular expression is empty";
-          }
-        } else {
-          error = "Key is empty";
-        }
-        if (!error.empty()) {
-          error = ("AUTOMOC_DEPEND_FILTERS: " + error);
-          error += "\n";
-          error += "  Key: ";
-          error += Quoted(key);
-          error += "\n";
-          error += "  Exp: ";
-          error += Quoted(exp);
-          error += "\n";
-        }
-      };
-
-      std::string error;
-      // Insert default filter for Q_PLUGIN_METADATA
-      if (Base().QtVersionMajor != 4) {
-        pushFilter("Q_PLUGIN_METADATA",
-                   "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
-                   "[^\\)]*FILE[ \t]*\"([^\"]+)\"",
-                   error);
-      }
-      // Insert user defined dependency filters
-      {
-        std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
-        if ((flts.size() % 2) == 0) {
-          for (std::vector<std::string>::iterator itC = flts.begin(),
-                                                  itE = flts.end();
-               itC != itE; itC += 2) {
-            pushFilter(*itC, *(itC + 1), error);
-            if (!error.empty()) {
-              break;
-            }
-          }
-        } else {
-          Log().ErrorFile(
-            GenT::MOC, InfoFile(),
-            "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
-          return false;
-        }
-      }
-      if (!error.empty()) {
-        Log().ErrorFile(GenT::MOC, InfoFile(), error);
-        return false;
-      }
-    }
-    Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
-    // Install moc predefs job
-    if (!Moc().PredefsCmd.empty()) {
-      JobQueues_.MocPredefs.emplace_back(cm::make_unique<JobMocPredefsT>());
-    }
-  }
-
-  // - Uic
-  Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
-  Uic_.Enabled = !Uic().Executable.empty();
-  if (Uic().Enabled) {
-    for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
-      Uic_.SkipList.insert(std::move(sfl));
-    }
-    Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
-    Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
-    {
-      auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
-      auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
-      // Compare list sizes
-      if (sources.size() != options.size()) {
-        std::ostringstream ost;
-        ost << "files/options lists sizes mismatch (" << sources.size() << "/"
-            << options.size() << ")";
-        Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
-        return false;
-      }
-      auto fitEnd = sources.cend();
-      auto fit = sources.begin();
-      auto oit = options.begin();
-      while (fit != fitEnd) {
-        Uic_.Options[*fit] = std::move(*oit);
-        ++fit;
-        ++oit;
-      }
-    }
-  }
-
-  // - Headers and sources
-  {
-    auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
-      this->JobQueues_.Headers.emplace_back(
-        cm::make_unique<JobParseT>(std::move(hdr), moc, uic, true));
-    };
-    auto addSource = [this](std::string&& src, bool moc, bool uic) {
-      this->JobQueues_.Sources.emplace_back(
-        cm::make_unique<JobParseT>(std::move(src), moc, uic, false));
-    };
-
-    // Add headers
-    for (std::string& hdr : InfoGetList("AM_HEADERS")) {
-      addHeader(std::move(hdr), true, true);
-    }
-    if (Moc().Enabled) {
-      for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
-        addHeader(std::move(hdr), true, false);
-      }
-    }
-    if (Uic().Enabled) {
-      for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
-        addHeader(std::move(hdr), false, true);
-      }
-    }
-
-    // Add sources
-    for (std::string& src : InfoGetList("AM_SOURCES")) {
-      addSource(std::move(src), true, true);
-    }
-    if (Moc().Enabled) {
-      for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
-        addSource(std::move(src), true, false);
-      }
-    }
-    if (Uic().Enabled) {
-      for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
-        addSource(std::move(src), false, true);
-      }
-    }
-  }
-
-  // Init derived information
-  // ------------------------
-
-  // Init file path checksum generator
-  FileSys().setupFilePathChecksum(
-    Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
-    Base().ProjectBinaryDir);
-
-  // Moc variables
-  if (Moc().Enabled) {
-    // Mocs compilation file
-    Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
-
-    // Moc predefs file
-    if (!Moc_.PredefsCmd.empty()) {
-      Moc_.PredefsFileRel = "moc_predefs";
-      if (Base_.MultiConfig) {
-        Moc_.PredefsFileRel += '_';
-        Moc_.PredefsFileRel += InfoConfig();
-      }
-      Moc_.PredefsFileRel += ".h";
-      Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
-    }
-
-    // Sort include directories on demand
-    if (Base().IncludeProjectDirsBefore) {
-      // Move strings to temporary list
-      std::list<std::string> includes;
-      includes.insert(includes.end(), Moc().IncludePaths.begin(),
-                      Moc().IncludePaths.end());
-      Moc_.IncludePaths.clear();
-      Moc_.IncludePaths.reserve(includes.size());
-      // Append project directories only
-      {
-        std::array<std::string const*, 2> const movePaths = {
-          { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
-        };
-        for (std::string const* ppath : movePaths) {
-          std::list<std::string>::iterator it = includes.begin();
-          while (it != includes.end()) {
-            std::string const& path = *it;
-            if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
-              Moc_.IncludePaths.push_back(path);
-              it = includes.erase(it);
-            } else {
-              ++it;
-            }
-          }
-        }
-      }
-      // Append remaining directories
-      Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
-                               includes.end());
-    }
-    // Compose moc includes list
-    {
-      std::set<std::string> frameworkPaths;
-      for (std::string const& path : Moc().IncludePaths) {
-        Moc_.Includes.push_back("-I" + path);
-        // Extract framework path
-        if (cmHasLiteralSuffix(path, ".framework/Headers")) {
-          // Go up twice to get to the framework root
-          std::vector<std::string> pathComponents;
-          FileSys().SplitPath(path, pathComponents);
-          std::string frameworkPath = FileSys().JoinPath(
-            pathComponents.begin(), pathComponents.end() - 2);
-          frameworkPaths.insert(frameworkPath);
-        }
-      }
-      // Append framework includes
-      for (std::string const& path : frameworkPaths) {
-        Moc_.Includes.emplace_back("-F");
-        Moc_.Includes.push_back(path);
-      }
-    }
-    // Setup single list with all options
-    {
-      // Add includes
-      Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
-                             Moc().Includes.end());
-      // Add definitions
-      for (std::string const& def : Moc().Definitions) {
-        Moc_.AllOptions.push_back("-D" + def);
-      }
-      // Add options
-      Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
-                             Moc().Options.end());
-    }
-  }
-
-  return true;
-}
-
-bool cmQtAutoGeneratorMocUic::Process()
-{
-  // Run libuv event loop
-  UVRequest().send();
-  if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
-    if (JobError_) {
-      return false;
-    }
-  } else {
-    return false;
-  }
-  return true;
-}
-
-void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle)
-{
-  reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage();
-}
-
-void cmQtAutoGeneratorMocUic::PollStage()
-{
-  switch (Stage_) {
-    case StageT::SETTINGS_READ:
-      SettingsFileRead();
-      SetStage(StageT::CREATE_DIRECTORIES);
-      break;
-    case StageT::CREATE_DIRECTORIES:
-      CreateDirectories();
-      SetStage(StageT::PARSE_SOURCES);
-      break;
-    case StageT::PARSE_SOURCES:
-      if (ThreadsStartJobs(JobQueues_.Sources)) {
-        SetStage(StageT::PARSE_HEADERS);
-      }
-      break;
-    case StageT::PARSE_HEADERS:
-      if (ThreadsStartJobs(JobQueues_.Headers)) {
-        SetStage(StageT::MOC_PREDEFS);
-      }
-      break;
-    case StageT::MOC_PREDEFS:
-      if (ThreadsStartJobs(JobQueues_.MocPredefs)) {
-        SetStage(StageT::MOC_PROCESS);
-      }
-      break;
-    case StageT::MOC_PROCESS:
-      if (ThreadsStartJobs(JobQueues_.Moc)) {
-        SetStage(StageT::MOCS_COMPILATION);
-      }
-      break;
-    case StageT::MOCS_COMPILATION:
-      if (ThreadsJobsDone()) {
-        MocGenerateCompilation();
-        SetStage(StageT::UIC_PROCESS);
-      }
-      break;
-    case StageT::UIC_PROCESS:
-      if (ThreadsStartJobs(JobQueues_.Uic)) {
-        SetStage(StageT::SETTINGS_WRITE);
-      }
-      break;
-    case StageT::SETTINGS_WRITE:
-      SettingsFileWrite();
-      SetStage(StageT::FINISH);
-      break;
-    case StageT::FINISH:
-      if (ThreadsJobsDone()) {
-        // Clear all libuv handles
-        ThreadsStop();
-        UVRequest().reset();
-        // Set highest END stage manually
-        Stage_ = StageT::END;
-      }
-      break;
-    case StageT::END:
-      break;
-  }
-}
-
-void cmQtAutoGeneratorMocUic::SetStage(StageT stage)
-{
-  if (JobError_) {
-    stage = StageT::FINISH;
-  }
-  // Only allow to increase the stage
-  if (Stage_ < stage) {
-    Stage_ = stage;
-    UVRequest().send();
-  }
-}
-
-void cmQtAutoGeneratorMocUic::SettingsFileRead()
-{
-  // Compose current settings strings
-  {
-    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
-    std::string const sep(" ~~~ ");
-    if (Moc_.Enabled) {
-      std::string str;
-      str += Moc().Executable;
-      str += sep;
-      str += cmJoin(Moc().AllOptions, ";");
-      str += sep;
-      str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
-      str += sep;
-      str += cmJoin(Moc().PredefsCmd, ";");
-      str += sep;
-      SettingsStringMoc_ = crypt.HashString(str);
-    }
-    if (Uic().Enabled) {
-      std::string str;
-      str += Uic().Executable;
-      str += sep;
-      str += cmJoin(Uic().TargetOptions, ";");
-      for (const auto& item : Uic().Options) {
-        str += sep;
-        str += item.first;
-        str += sep;
-        str += cmJoin(item.second, ";");
-      }
-      str += sep;
-      SettingsStringUic_ = crypt.HashString(str);
-    }
-  }
-
-  // Read old settings and compare
-  {
-    std::string content;
-    if (FileSys().FileRead(content, SettingsFile_)) {
-      if (Moc().Enabled) {
-        if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
-          Moc_.SettingsChanged = true;
-        }
-      }
-      if (Uic().Enabled) {
-        if (SettingsStringUic_ != SettingsFind(content, "uic")) {
-          Uic_.SettingsChanged = true;
-        }
-      }
-      // In case any setting changed remove the old settings file.
-      // This triggers a full rebuild on the next run if the current
-      // build is aborted before writing the current settings in the end.
-      if (Moc().SettingsChanged || Uic().SettingsChanged) {
-        FileSys().FileRemove(SettingsFile_);
-      }
-    } else {
-      // Settings file read failed
-      if (Moc().Enabled) {
-        Moc_.SettingsChanged = true;
-      }
-      if (Uic().Enabled) {
-        Uic_.SettingsChanged = true;
-      }
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::SettingsFileWrite()
-{
-  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-  // Only write if any setting changed
-  if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) {
-    if (Log().Verbose()) {
-      Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
-    }
-    // Compose settings file content
-    std::string content;
-    {
-      auto SettingAppend = [&content](const char* key,
-                                      std::string const& value) {
-        if (!value.empty()) {
-          content += key;
-          content += ':';
-          content += value;
-          content += '\n';
-        }
-      };
-      SettingAppend("moc", SettingsStringMoc_);
-      SettingAppend("uic", SettingsStringUic_);
-    }
-    // Write settings file
-    std::string error;
-    if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
-      Log().ErrorFile(GenT::GEN, SettingsFile_,
-                      "Settings file writing failed. " + error);
-      // Remove old settings file to trigger a full rebuild on the next run
-      FileSys().FileRemove(SettingsFile_);
-      RegisterJobError();
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::CreateDirectories()
-{
-  // Create AUTOGEN include directory
-  if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
-    Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
-                    "Could not create directory.");
-    RegisterJobError();
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
-{
-  bool done = false;
-  std::size_t queueSize = queue.size();
-
-  // Change the active queue
-  {
-    std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-    // Check if there are still unfinished jobs from the previous queue
-    if (JobsRemain_ == 0) {
-      if (!JobThreadsAbort_) {
-        JobQueue_.swap(queue);
-        JobsRemain_ = queueSize;
-      } else {
-        // Abort requested
-        queue.clear();
-        queueSize = 0;
-      }
-      done = true;
-    }
-  }
-
-  if (done && (queueSize != 0)) {
-    // Start new threads on demand
-    if (Workers_.empty()) {
-      Workers_.resize(Base().NumThreads);
-      for (auto& item : Workers_) {
-        item = cm::make_unique<WorkerT>(this, UVLoop());
-      }
-    } else {
-      // Notify threads
-      if (queueSize == 1) {
-        JobsConditionRead_.notify_one();
-      } else {
-        JobsConditionRead_.notify_all();
-      }
-    }
-  }
-
-  return done;
-}
-
-void cmQtAutoGeneratorMocUic::ThreadsStop()
-{
-  if (!Workers_.empty()) {
-    // Clear all jobs
-    {
-      std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-      JobThreadsAbort_ = true;
-      JobsRemain_ -= JobQueue_.size();
-      JobQueue_.clear();
-
-      JobQueues_.Sources.clear();
-      JobQueues_.Headers.clear();
-      JobQueues_.MocPredefs.clear();
-      JobQueues_.Moc.clear();
-      JobQueues_.Uic.clear();
-    }
-    // Wake threads
-    JobsConditionRead_.notify_all();
-    // Join and clear threads
-    Workers_.clear();
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
-{
-  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-  return (JobsRemain_ == 0);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
-{
-  bool const jobProcessed(jobHandle);
-  if (jobProcessed) {
-    jobHandle.reset();
-  }
-  {
-    std::unique_lock<std::mutex> jobsLock(JobsMutex_);
-    // Reduce the remaining job count and notify the libuv loop
-    // when all jobs are done
-    if (jobProcessed) {
-      --JobsRemain_;
-      if (JobsRemain_ == 0) {
-        UVRequest().send();
-      }
-    }
-    // Wait for new jobs
-    while (!JobThreadsAbort_ && JobQueue_.empty()) {
-      JobsConditionRead_.wait(jobsLock);
-    }
-    // Try to pick up a new job handle
-    if (!JobThreadsAbort_ && !JobQueue_.empty()) {
-      jobHandle = std::move(JobQueue_.front());
-      JobQueue_.pop_front();
-    }
-  }
-}
-
-void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
-{
-  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-  RegisterJobError();
-}
-
-// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
-// locked
-void cmQtAutoGeneratorMocUic::RegisterJobError()
-{
-  JobError_ = true;
-  if (!JobThreadsAbort_) {
-    JobThreadsAbort_ = true;
-    // Clear remaining jobs
-    if (JobsRemain_ != 0) {
-      JobsRemain_ -= JobQueue_.size();
-      JobQueue_.clear();
-    }
-  }
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
-{
-  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-  if (!JobThreadsAbort_) {
-    bool pushJobHandle = true;
-    // Do additional tests if this is an included moc job
-    const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
-    if (!mocJob.IncludeString.empty()) {
-      // Register included moc file and look for collisions
-      MocIncludedFiles_.emplace(mocJob.SourceFile);
-      if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
-        // Another source file includes the same moc file!
-        for (const JobHandleT& otherHandle : JobQueues_.Moc) {
-          const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
-          if (otherJob.IncludeString == mocJob.IncludeString) {
-            // Check if the same moc file would be generated from different
-            // source files which is an error.
-            if (otherJob.SourceFile != mocJob.SourceFile) {
-              // Include string collision
-              std::string error = "The two source files\n  ";
-              error += Quoted(mocJob.IncluderFile);
-              error += " and\n  ";
-              error += Quoted(otherJob.IncluderFile);
-              error += "\ncontain the same moc include string ";
-              error += Quoted(mocJob.IncludeString);
-              error += "\nbut the moc file would be generated from different "
-                       "source files\n  ";
-              error += Quoted(mocJob.SourceFile);
-              error += " and\n  ";
-              error += Quoted(otherJob.SourceFile);
-              error += ".\nConsider to\n"
-                       "- not include the \"moc_<NAME>.cpp\" file\n"
-                       "- add a directory prefix to a \"<NAME>.moc\" include "
-                       "(e.g \"sub/<NAME>.moc\")\n"
-                       "- rename the source file(s)\n";
-              Log().Error(GenT::MOC, error);
-              RegisterJobError();
-            }
-            // Do not push this job in since the included moc file already
-            // gets generated by an other job.
-            pushJobHandle = false;
-            break;
-          }
-        }
-      }
-    }
-    // Push job on demand
-    if (pushJobHandle) {
-      JobQueues_.Moc.emplace_back(std::move(jobHandle));
-    }
-  }
-  return !JobError_;
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
-{
-  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
-  if (!JobThreadsAbort_) {
-    bool pushJobHandle = true;
-    // Look for include collisions.
-    const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
-    for (const JobHandleT& otherHandle : JobQueues_.Uic) {
-      const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
-      if (otherJob.IncludeString == uicJob.IncludeString) {
-        // Check if the same uic file would be generated from different
-        // source files which would be an error.
-        if (otherJob.SourceFile != uicJob.SourceFile) {
-          // Include string collision
-          std::string error = "The two source files\n  ";
-          error += Quoted(uicJob.IncluderFile);
-          error += " and\n  ";
-          error += Quoted(otherJob.IncluderFile);
-          error += "\ncontain the same uic include string ";
-          error += Quoted(uicJob.IncludeString);
-          error += "\nbut the uic file would be generated from different "
-                   "source files\n  ";
-          error += Quoted(uicJob.SourceFile);
-          error += " and\n  ";
-          error += Quoted(otherJob.SourceFile);
-          error +=
-            ".\nConsider to\n"
-            "- add a directory prefix to a \"ui_<NAME>.h\" include "
-            "(e.g \"sub/ui_<NAME>.h\")\n"
-            "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
-            "include(s)\n";
-          Log().Error(GenT::UIC, error);
-          RegisterJobError();
-        }
-        // Do not push this job in since the uic file already
-        // gets generated by an other job.
-        pushJobHandle = false;
-        break;
-      }
-    }
-    if (pushJobHandle) {
-      JobQueues_.Uic.emplace_back(std::move(jobHandle));
-    }
-  }
-  return !JobError_;
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
-  std::string const& sourceFile)
-{
-  std::lock_guard<std::mutex> mocLock(JobsMutex_);
-  return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
-}
-
-std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
-  std::string const& baseName)
-{
-  std::string res;
-  {
-    std::lock_guard<std::mutex> mocLock(JobsMutex_);
-    res = baseName;
-    res += ".cpp";
-    if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
-      MocAutoFiles_.emplace(res);
-    } else {
-      // Append number suffix to the file name
-      for (unsigned int ii = 2; ii != 1024; ++ii) {
-        res = baseName;
-        res += '_';
-        res += std::to_string(ii);
-        res += ".cpp";
-        if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
-          MocAutoFiles_.emplace(res);
-          break;
-        }
-      }
-    }
-  }
-  return res;
-}
-
-void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
-{
-  std::lock_guard<std::mutex> mocLock(JobsMutex_);
-  MocAutoFileUpdated_ = true;
-}
-
-void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
-{
-  std::lock_guard<std::mutex> mocLock(JobsMutex_);
-  if (!JobError_ && Moc().Enabled) {
-    // Write mocs compilation build file
-    {
-      // Compose mocs compilation file content
-      std::string content =
-        "// This file is autogenerated. Changes will be overwritten.\n";
-      if (MocAutoFiles_.empty()) {
-        // Placeholder content
-        content += "// No files found that require moc or the moc files are "
-                   "included\n";
-        content += "enum some_compilers { need_more_than_nothing };\n";
-      } else {
-        // Valid content
-        char const sbeg = Base().MultiConfig ? '<' : '"';
-        char const send = Base().MultiConfig ? '>' : '"';
-        for (std::string const& mocfile : MocAutoFiles_) {
-          content += "#include ";
-          content += sbeg;
-          content += mocfile;
-          content += send;
-          content += '\n';
-        }
-      }
-
-      std::string const& compAbs = Moc().CompFileAbs;
-      if (FileSys().FileDiffers(compAbs, content)) {
-        // Actually write mocs compilation file
-        if (Log().Verbose()) {
-          Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
-        }
-        std::string error;
-        if (!FileSys().FileWrite(compAbs, content, &error)) {
-          Log().ErrorFile(GenT::MOC, compAbs,
-                          "mocs compilation file writing failed. " + error);
-          RegisterJobError();
-          return;
-        }
-      } else if (MocAutoFileUpdated_) {
-        // Only touch mocs compilation file
-        if (Log().Verbose()) {
-          Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
-        }
-        FileSys().Touch(compAbs);
-      }
-    }
-    // Write mocs compilation wrapper file
-    if (Base().MultiConfig) {
-    }
-  }
-}
diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h
deleted file mode 100644
index 27d73a7..0000000
--- a/Source/cmQtAutoGeneratorMocUic.h
+++ /dev/null
@@ -1,459 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmQtAutoGeneratorMocUic_h
-#define cmQtAutoGeneratorMocUic_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmQtAutoGen.h"
-#include "cmQtAutoGenerator.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
-#include "cm_uv.h"
-#include "cmsys/RegularExpression.hxx"
-
-#include <condition_variable>
-#include <cstddef>
-#include <deque>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <mutex>
-#include <set>
-#include <string>
-#include <thread>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-class cmMakefile;
-
-// @brief AUTOMOC and AUTOUIC generator
-class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator
-{
-public:
-  cmQtAutoGeneratorMocUic();
-  ~cmQtAutoGeneratorMocUic() override;
-
-  cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete;
-  cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete;
-
-public:
-  // -- Types
-  class WorkerT;
-
-  /// @brief Search key plus regular expression pair
-  ///
-  struct KeyExpT
-  {
-    KeyExpT() = default;
-
-    KeyExpT(const char* key, const char* exp)
-      : Key(key)
-      , Exp(exp)
-    {
-    }
-
-    KeyExpT(std::string key, std::string const& exp)
-      : Key(std::move(key))
-      , Exp(exp)
-    {
-    }
-
-    std::string Key;
-    cmsys::RegularExpression Exp;
-  };
-
-  /// @brief Common settings
-  ///
-  class BaseSettingsT
-  {
-  public:
-    // -- Volatile methods
-    BaseSettingsT(FileSystem* fileSystem)
-      : MultiConfig(false)
-      , IncludeProjectDirsBefore(false)
-      , QtVersionMajor(4)
-      , NumThreads(1)
-      , FileSys(fileSystem)
-    {
-    }
-
-    BaseSettingsT(BaseSettingsT const&) = delete;
-    BaseSettingsT& operator=(BaseSettingsT const&) = delete;
-
-    // -- Const methods
-    std::string AbsoluteBuildPath(std::string const& relativePath) const;
-    bool FindHeader(std::string& header,
-                    std::string const& testBasePath) const;
-
-    // -- Attributes
-    // - Config
-    bool MultiConfig;
-    bool IncludeProjectDirsBefore;
-    unsigned int QtVersionMajor;
-    unsigned int NumThreads;
-    // - Directories
-    std::string ProjectSourceDir;
-    std::string ProjectBinaryDir;
-    std::string CurrentSourceDir;
-    std::string CurrentBinaryDir;
-    std::string AutogenBuildDir;
-    std::string AutogenIncludeDir;
-    // - Files
-    std::vector<std::string> HeaderExtensions;
-    // - File system
-    FileSystem* FileSys;
-  };
-
-  /// @brief Moc settings
-  ///
-  class MocSettingsT
-  {
-  public:
-    MocSettingsT(FileSystem* fileSys)
-      : FileSys(fileSys)
-    {
-    }
-
-    MocSettingsT(MocSettingsT const&) = delete;
-    MocSettingsT& operator=(MocSettingsT const&) = delete;
-
-    // -- Const methods
-    bool skipped(std::string const& fileName) const;
-    std::string FindMacro(std::string const& content) const;
-    std::string MacrosString() const;
-    std::string FindIncludedFile(std::string const& sourcePath,
-                                 std::string const& includeString) const;
-    void FindDependencies(std::string const& content,
-                          std::set<std::string>& depends) const;
-
-    // -- Attributes
-    bool Enabled = false;
-    bool SettingsChanged = false;
-    bool RelaxedMode = false;
-    std::string Executable;
-    std::string CompFileAbs;
-    std::string PredefsFileRel;
-    std::string PredefsFileAbs;
-    std::unordered_set<std::string> SkipList;
-    std::vector<std::string> IncludePaths;
-    std::vector<std::string> Includes;
-    std::vector<std::string> Definitions;
-    std::vector<std::string> Options;
-    std::vector<std::string> AllOptions;
-    std::vector<std::string> PredefsCmd;
-    std::vector<KeyExpT> DependFilters;
-    std::vector<KeyExpT> MacroFilters;
-    cmsys::RegularExpression RegExpInclude;
-    // - File system
-    FileSystem* FileSys;
-  };
-
-  /// @brief Uic settings
-  ///
-  class UicSettingsT
-  {
-  public:
-    UicSettingsT() = default;
-
-    UicSettingsT(UicSettingsT const&) = delete;
-    UicSettingsT& operator=(UicSettingsT const&) = delete;
-
-    // -- Const methods
-    bool skipped(std::string const& fileName) const;
-
-    // -- Attributes
-    bool Enabled = false;
-    bool SettingsChanged = false;
-    std::string Executable;
-    std::unordered_set<std::string> SkipList;
-    std::vector<std::string> TargetOptions;
-    std::map<std::string, std::vector<std::string>> Options;
-    std::vector<std::string> SearchPaths;
-    cmsys::RegularExpression RegExpInclude;
-  };
-
-  /// @brief Abstract job class for threaded processing
-  ///
-  class JobT
-  {
-  public:
-    JobT() = default;
-    virtual ~JobT() = default;
-
-    JobT(JobT const&) = delete;
-    JobT& operator=(JobT const&) = delete;
-
-    // -- Abstract processing interface
-    virtual void Process(WorkerT& wrk) = 0;
-  };
-
-  // Job management types
-  typedef std::unique_ptr<JobT> JobHandleT;
-  typedef std::deque<JobHandleT> JobQueueT;
-
-  /// @brief Parse source job
-  ///
-  class JobParseT : public JobT
-  {
-  public:
-    JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false)
-      : FileName(std::move(fileName))
-      , AutoMoc(moc)
-      , AutoUic(uic)
-      , Header(header)
-    {
-    }
-
-  private:
-    struct MetaT
-    {
-      std::string Content;
-      std::string FileDir;
-      std::string FileBase;
-    };
-
-    void Process(WorkerT& wrk) override;
-    bool ParseMocSource(WorkerT& wrk, MetaT const& meta);
-    bool ParseMocHeader(WorkerT& wrk, MetaT const& meta);
-    std::string MocStringHeaders(WorkerT& wrk,
-                                 std::string const& fileBase) const;
-    std::string MocFindIncludedHeader(WorkerT& wrk,
-                                      std::string const& includerDir,
-                                      std::string const& includeBase);
-    bool ParseUic(WorkerT& wrk, MetaT const& meta);
-    bool ParseUicInclude(WorkerT& wrk, MetaT const& meta,
-                         std::string&& includeString);
-    std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta,
-                                    std::string const& includeString);
-
-  private:
-    std::string FileName;
-    bool AutoMoc = false;
-    bool AutoUic = false;
-    bool Header = false;
-  };
-
-  /// @brief Generate moc_predefs
-  ///
-  class JobMocPredefsT : public JobT
-  {
-  private:
-    void Process(WorkerT& wrk) override;
-  };
-
-  /// @brief Moc a file job
-  ///
-  class JobMocT : public JobT
-  {
-  public:
-    JobMocT(std::string&& sourceFile, std::string includerFile,
-            std::string&& includeString)
-      : SourceFile(std::move(sourceFile))
-      , IncluderFile(std::move(includerFile))
-      , IncludeString(std::move(includeString))
-    {
-    }
-
-    void FindDependencies(WorkerT& wrk, std::string const& content);
-
-  private:
-    void Process(WorkerT& wrk) override;
-    bool UpdateRequired(WorkerT& wrk);
-    void GenerateMoc(WorkerT& wrk);
-
-  public:
-    std::string SourceFile;
-    std::string IncluderFile;
-    std::string IncludeString;
-    std::string BuildFile;
-    bool DependsValid = false;
-    std::set<std::string> Depends;
-  };
-
-  /// @brief Uic a file job
-  ///
-  class JobUicT : public JobT
-  {
-  public:
-    JobUicT(std::string&& sourceFile, std::string includerFile,
-            std::string&& includeString)
-      : SourceFile(std::move(sourceFile))
-      , IncluderFile(std::move(includerFile))
-      , IncludeString(std::move(includeString))
-    {
-    }
-
-  private:
-    void Process(WorkerT& wrk) override;
-    bool UpdateRequired(WorkerT& wrk);
-    void GenerateUic(WorkerT& wrk);
-
-  public:
-    std::string SourceFile;
-    std::string IncluderFile;
-    std::string IncludeString;
-    std::string BuildFile;
-  };
-
-  /// @brief Worker Thread
-  ///
-  class WorkerT
-  {
-  public:
-    WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
-    ~WorkerT();
-
-    WorkerT(WorkerT const&) = delete;
-    WorkerT& operator=(WorkerT const&) = delete;
-
-    // -- Const accessors
-    cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; }
-    Logger& Log() const { return Gen_->Log(); }
-    FileSystem& FileSys() const { return Gen_->FileSys(); }
-    const BaseSettingsT& Base() const { return Gen_->Base(); }
-    const MocSettingsT& Moc() const { return Gen_->Moc(); }
-    const UicSettingsT& Uic() const { return Gen_->Uic(); }
-
-    // -- Log info
-    void LogInfo(GenT genType, std::string const& message) const;
-    // -- Log warning
-    void LogWarning(GenT genType, std::string const& message) const;
-    void LogFileWarning(GenT genType, std::string const& filename,
-                        std::string const& message) const;
-    // -- Log error
-    void LogError(GenT genType, std::string const& message) const;
-    void LogFileError(GenT genType, std::string const& filename,
-                      std::string const& message) const;
-    void LogCommandError(GenT genType, std::string const& message,
-                         std::vector<std::string> const& command,
-                         std::string const& output) const;
-
-    // -- External processes
-    /// @brief Verbose logging version
-    bool RunProcess(GenT genType, ProcessResultT& result,
-                    std::vector<std::string> const& command);
-
-  private:
-    /// @brief Thread main loop
-    void Loop();
-
-    // -- Libuv callbacks
-    static void UVProcessStart(uv_async_t* handle);
-    void UVProcessFinished();
-
-  private:
-    // -- Generator
-    cmQtAutoGeneratorMocUic* Gen_;
-    // -- Job handle
-    JobHandleT JobHandle_;
-    // -- Process management
-    std::mutex ProcessMutex_;
-    cm::uv_async_ptr ProcessRequest_;
-    std::condition_variable ProcessCondition_;
-    std::unique_ptr<ReadOnlyProcessT> Process_;
-    // -- System thread
-    std::thread Thread_;
-  };
-
-  /// @brief Processing stage
-  enum class StageT
-  {
-    SETTINGS_READ,
-    CREATE_DIRECTORIES,
-    PARSE_SOURCES,
-    PARSE_HEADERS,
-    MOC_PREDEFS,
-    MOC_PROCESS,
-    MOCS_COMPILATION,
-    UIC_PROCESS,
-    SETTINGS_WRITE,
-    FINISH,
-    END
-  };
-
-  // -- Const settings interface
-  const BaseSettingsT& Base() const { return this->Base_; }
-  const MocSettingsT& Moc() const { return this->Moc_; }
-  const UicSettingsT& Uic() const { return this->Uic_; }
-
-  // -- Worker thread interface
-  void WorkerSwapJob(JobHandleT& jobHandle);
-  // -- Parallel job processing interface
-  void ParallelRegisterJobError();
-  bool ParallelJobPushMoc(JobHandleT& jobHandle);
-  bool ParallelJobPushUic(JobHandleT& jobHandle);
-  bool ParallelMocIncluded(std::string const& sourceFile);
-  std::string ParallelMocAutoRegister(std::string const& baseName);
-  void ParallelMocAutoUpdated();
-
-private:
-  // -- Utility accessors
-  Logger& Log() { return Logger_; }
-  FileSystem& FileSys() { return FileSys_; }
-  // -- libuv loop accessors
-  uv_loop_t* UVLoop() { return UVLoop_.get(); }
-  cm::uv_async_ptr& UVRequest() { return UVRequest_; }
-  // -- Abstract processing interface
-  bool Init(cmMakefile* makefile) override;
-  bool Process() override;
-  // -- Process stage
-  static void UVPollStage(uv_async_t* handle);
-  void PollStage();
-  void SetStage(StageT stage);
-  // -- Settings file
-  void SettingsFileRead();
-  void SettingsFileWrite();
-  // -- Thread processing
-  bool ThreadsStartJobs(JobQueueT& queue);
-  bool ThreadsJobsDone();
-  void ThreadsStop();
-  void RegisterJobError();
-  // -- Generation
-  void CreateDirectories();
-  void MocGenerateCompilation();
-
-private:
-  // -- Utility
-  Logger Logger_;
-  FileSystem FileSys_;
-  // -- Settings
-  BaseSettingsT Base_;
-  MocSettingsT Moc_;
-  UicSettingsT Uic_;
-  // -- libuv loop
-#ifdef CMAKE_UV_SIGNAL_HACK
-  std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
-#endif
-  std::unique_ptr<uv_loop_t> UVLoop_;
-  cm::uv_async_ptr UVRequest_;
-  StageT Stage_ = StageT::SETTINGS_READ;
-  // -- Job queues
-  std::mutex JobsMutex_;
-  struct
-  {
-    JobQueueT Sources;
-    JobQueueT Headers;
-    JobQueueT MocPredefs;
-    JobQueueT Moc;
-    JobQueueT Uic;
-  } JobQueues_;
-  JobQueueT JobQueue_;
-  std::size_t volatile JobsRemain_ = 0;
-  bool volatile JobError_ = false;
-  bool volatile JobThreadsAbort_ = false;
-  std::condition_variable JobsConditionRead_;
-  // -- Moc meta
-  std::set<std::string> MocIncludedStrings_;
-  std::set<std::string> MocIncludedFiles_;
-  std::set<std::string> MocAutoFiles_;
-  bool volatile MocAutoFileUpdated_ = false;
-  // -- Settings file
-  std::string SettingsFile_;
-  std::string SettingsStringMoc_;
-  std::string SettingsStringUic_;
-  // -- Threads and loops
-  std::vector<std::unique_ptr<WorkerT>> Workers_;
-};
-
-#endif
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
new file mode 100644
index 0000000..75c5d8a
--- /dev/null
+++ b/Source/cmQtAutoMocUic.cxx
@@ -0,0 +1,1747 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmQtAutoMocUic.h"
+
+#include <algorithm>
+#include <array>
+#include <deque>
+#include <list>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include "cmAlgorithms.h"
+#include "cmCryptoHash.h"
+#include "cmMakefile.h"
+#include "cmQtAutoGen.h"
+#include "cmSystemTools.h"
+#include "cmake.h"
+
+#if defined(__APPLE__)
+#  include <unistd.h>
+#endif
+
+// -- Class methods
+
+std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath(
+  std::string const& relativePath) const
+{
+  return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
+}
+
+/**
+ * @brief Tries to find the header file to the given file base path by
+ * appending different header extensions
+ * @return True on success
+ */
+bool cmQtAutoMocUic::BaseSettingsT::FindHeader(
+  std::string& header, std::string const& testBasePath) const
+{
+  for (std::string const& ext : HeaderExtensions) {
+    std::string testFilePath(testBasePath);
+    testFilePath.push_back('.');
+    testFilePath += ext;
+    if (FileSys->FileExists(testFilePath)) {
+      header = testFilePath;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+{
+  return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+/**
+ * @brief Returns the first relevant Qt macro name found in the given C++ code
+ * @return The name of the Qt macro or an empty string
+ */
+std::string cmQtAutoMocUic::MocSettingsT::FindMacro(
+  std::string const& content) const
+{
+  for (KeyExpT const& filter : MacroFilters) {
+    // Run a simple find string operation before the expensive
+    // regular expression check
+    if (content.find(filter.Key) != std::string::npos) {
+      cmsys::RegularExpressionMatch match;
+      if (filter.Exp.find(content.c_str(), match)) {
+        // Return macro name on demand
+        return filter.Key;
+      }
+    }
+  }
+  return std::string();
+}
+
+std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
+{
+  std::string res;
+  const auto itB = MacroFilters.cbegin();
+  const auto itE = MacroFilters.cend();
+  const auto itL = itE - 1;
+  auto itC = itB;
+  for (; itC != itE; ++itC) {
+    // Separator
+    if (itC != itB) {
+      if (itC != itL) {
+        res += ", ";
+      } else {
+        res += " or ";
+      }
+    }
+    // Key
+    res += itC->Key;
+  }
+  return res;
+}
+
+std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile(
+  std::string const& sourcePath, std::string const& includeString) const
+{
+  // Search in vicinity of the source
+  {
+    std::string testPath = sourcePath;
+    testPath += includeString;
+    if (FileSys->FileExists(testPath)) {
+      return FileSys->GetRealPath(testPath);
+    }
+  }
+  // Search in include directories
+  for (std::string const& path : IncludePaths) {
+    std::string fullPath = path;
+    fullPath.push_back('/');
+    fullPath += includeString;
+    if (FileSys->FileExists(fullPath)) {
+      return FileSys->GetRealPath(fullPath);
+    }
+  }
+  // Return empty string
+  return std::string();
+}
+
+void cmQtAutoMocUic::MocSettingsT::FindDependencies(
+  std::string const& content, std::set<std::string>& depends) const
+{
+  if (!DependFilters.empty() && !content.empty()) {
+    for (KeyExpT const& filter : DependFilters) {
+      // Run a simple find string check
+      if (content.find(filter.Key) != std::string::npos) {
+        // Run the expensive regular expression check loop
+        const char* contentChars = content.c_str();
+        cmsys::RegularExpressionMatch match;
+        while (filter.Exp.find(contentChars, match)) {
+          {
+            std::string dep = match.match(1);
+            if (!dep.empty()) {
+              depends.emplace(std::move(dep));
+            }
+          }
+          contentChars += match.end();
+        }
+      }
+    }
+  }
+}
+
+bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
+{
+  return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+void cmQtAutoMocUic::JobT::LogError(GenT genType,
+                                    std::string const& message) const
+{
+  Gen()->AbortError();
+  Gen()->Log().Error(genType, message);
+}
+
+void cmQtAutoMocUic::JobT::LogFileError(GenT genType,
+                                        std::string const& filename,
+                                        std::string const& message) const
+{
+  Gen()->AbortError();
+  Gen()->Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoMocUic::JobT::LogCommandError(
+  GenT genType, std::string const& message,
+  std::vector<std::string> const& command, std::string const& output) const
+{
+  Gen()->AbortError();
+  Gen()->Log().ErrorCommand(genType, message, command, output);
+}
+
+bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
+                                      cmWorkerPool::ProcessResultT& result,
+                                      std::vector<std::string> const& command)
+{
+  // Log command
+  if (Log().Verbose()) {
+    std::string msg = "Running command:\n";
+    msg += QuotedCommand(command);
+    msg += '\n';
+    Log().Info(genType, msg);
+  }
+  return cmWorkerPool::JobT::RunProcess(result, command,
+                                        Gen()->Base().AutogenBuildDir);
+}
+
+void cmQtAutoMocUic::JobMocPredefsT::Process()
+{
+  // (Re)generate moc_predefs.h on demand
+  bool generate(false);
+  bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs));
+  if (!fileExists) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(Gen()->Moc().PredefsFileRel);
+      reason += " because it doesn't exist";
+      Log().Info(GenT::MOC, reason);
+    }
+    generate = true;
+  } else if (Gen()->Moc().SettingsChanged) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(Gen()->Moc().PredefsFileRel);
+      reason += " because the settings changed.";
+      Log().Info(GenT::MOC, reason);
+    }
+    generate = true;
+  }
+  if (generate) {
+    cmWorkerPool::ProcessResultT result;
+    {
+      // Compose command
+      std::vector<std::string> cmd = Gen()->Moc().PredefsCmd;
+      // Add includes
+      cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(),
+                 Gen()->Moc().Includes.end());
+      // Add definitions
+      for (std::string const& def : Gen()->Moc().Definitions) {
+        cmd.push_back("-D" + def);
+      }
+      // Execute command
+      if (!RunProcess(GenT::MOC, result, cmd)) {
+        std::string emsg = "The content generation command for ";
+        emsg += Quoted(Gen()->Moc().PredefsFileRel);
+        emsg += " failed.\n";
+        emsg += result.ErrorMessage;
+        LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+      }
+    }
+
+    // (Re)write predefs file only on demand
+    if (!result.error()) {
+      if (!fileExists ||
+          FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+        if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+          // Success
+        } else {
+          std::string emsg = "Writing ";
+          emsg += Quoted(Gen()->Moc().PredefsFileRel);
+          emsg += " failed.";
+          LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg);
+        }
+      } else {
+        // Touch to update the time stamp
+        if (Log().Verbose()) {
+          std::string msg = "Touching ";
+          msg += Quoted(Gen()->Moc().PredefsFileRel);
+          msg += ".";
+          Log().Info(GenT::MOC, msg);
+        }
+        FileSys().Touch(Gen()->Moc().PredefsFileAbs);
+      }
+    }
+  }
+}
+
+void cmQtAutoMocUic::JobParseT::Process()
+{
+  if (AutoMoc && Header) {
+    // Don't parse header for moc if the file is included by a source already
+    if (Gen()->ParallelMocIncluded(FileName)) {
+      AutoMoc = false;
+    }
+  }
+
+  if (AutoMoc || AutoUic) {
+    std::string error;
+    MetaT meta;
+    if (FileSys().FileRead(meta.Content, FileName, &error)) {
+      if (!meta.Content.empty()) {
+        meta.FileDir = FileSys().SubDirPrefix(FileName);
+        meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName);
+
+        bool success = true;
+        if (AutoMoc) {
+          if (Header) {
+            success = ParseMocHeader(meta);
+          } else {
+            success = ParseMocSource(meta);
+          }
+        }
+        if (AutoUic && success) {
+          ParseUic(meta);
+        }
+      } else {
+        Log().WarningFile(GenT::GEN, FileName, "The source file is empty");
+      }
+    } else {
+      LogFileError(GenT::GEN, FileName, "Could not read the file: " + error);
+    }
+  }
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta)
+{
+  struct JobPre
+  {
+    bool self;       // source file is self
+    bool underscore; // "moc_" style include
+    std::string SourceFile;
+    std::string IncludeString;
+  };
+
+  struct MocInclude
+  {
+    std::string Inc;  // full include string
+    std::string Dir;  // include string directory
+    std::string Base; // include string file base
+  };
+
+  // Check if this source file contains a relevant macro
+  std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content);
+
+  // Extract moc includes from file
+  std::deque<MocInclude> mocIncsUsc;
+  std::deque<MocInclude> mocIncsDot;
+  {
+    if (meta.Content.find("moc") != std::string::npos) {
+      const char* contentChars = meta.Content.c_str();
+      cmsys::RegularExpressionMatch match;
+      while (Gen()->Moc().RegExpInclude.find(contentChars, match)) {
+        std::string incString = match.match(2);
+        std::string incDir(FileSys().SubDirPrefix(incString));
+        std::string incBase =
+          FileSys().GetFilenameWithoutLastExtension(incString);
+        if (cmHasLiteralPrefix(incBase, "moc_")) {
+          // moc_<BASE>.cxx
+          // Remove the moc_ part from the base name
+          mocIncsUsc.emplace_back(MocInclude{
+            std::move(incString), std::move(incDir), incBase.substr(4) });
+        } else {
+          // <BASE>.moc
+          mocIncsDot.emplace_back(MocInclude{
+            std::move(incString), std::move(incDir), std::move(incBase) });
+        }
+        // Forward content pointer
+        contentChars += match.end();
+      }
+    }
+  }
+
+  // Check if there is anything to do
+  if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+    return true;
+  }
+
+  bool ownDotMocIncluded = false;
+  bool ownMocUscIncluded = false;
+  std::deque<JobPre> jobs;
+
+  // Process moc_<BASE>.cxx includes
+  for (const MocInclude& mocInc : mocIncsUsc) {
+    std::string const header =
+      MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
+    if (!header.empty()) {
+      // Check if header is skipped
+      if (Gen()->Moc().skipped(header)) {
+        continue;
+      }
+      // Register moc job
+      const bool ownMoc = (mocInc.Base == meta.FileBase);
+      jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
+      // Store meta information for relaxed mode
+      if (ownMoc) {
+        ownMocUscIncluded = true;
+      }
+    } else {
+      {
+        std::string emsg = "The file includes the moc file ";
+        emsg += Quoted(mocInc.Inc);
+        emsg += ", but the header ";
+        emsg += Quoted(MocStringHeaders(mocInc.Base));
+        emsg += " could not be found.";
+        LogFileError(GenT::MOC, FileName, emsg);
+      }
+      return false;
+    }
+  }
+
+  // Process <BASE>.moc includes
+  for (const MocInclude& mocInc : mocIncsDot) {
+    const bool ownMoc = (mocInc.Base == meta.FileBase);
+    if (Gen()->Moc().RelaxedMode) {
+      // Relaxed mode
+      if (!ownMacro.empty() && ownMoc) {
+        // Add self
+        jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+        ownDotMocIncluded = true;
+      } else {
+        // In relaxed mode try to find a header instead but issue a warning.
+        // This is for KDE4 compatibility
+        std::string const header =
+          MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
+        if (!header.empty()) {
+          // Check if header is skipped
+          if (Gen()->Moc().skipped(header)) {
+            continue;
+          }
+          // Register moc job
+          jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
+          if (ownMacro.empty()) {
+            if (ownMoc) {
+              std::string emsg = "The file includes the moc file ";
+              emsg += Quoted(mocInc.Inc);
+              emsg += ", but does not contain a ";
+              emsg += Gen()->Moc().MacrosString();
+              emsg += " macro.\nRunning moc on\n  ";
+              emsg += Quoted(header);
+              emsg += "!\nBetter include ";
+              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+              emsg += " for a compatibility with strict mode.\n"
+                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+              Log().WarningFile(GenT::MOC, FileName, emsg);
+            } else {
+              std::string emsg = "The file includes the moc file ";
+              emsg += Quoted(mocInc.Inc);
+              emsg += " instead of ";
+              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+              emsg += ".\nRunning moc on\n  ";
+              emsg += Quoted(header);
+              emsg += "!\nBetter include ";
+              emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+              emsg += " for compatibility with strict mode.\n"
+                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+              Log().WarningFile(GenT::MOC, FileName, emsg);
+            }
+          }
+        } else {
+          {
+            std::string emsg = "The file includes the moc file ";
+            emsg += Quoted(mocInc.Inc);
+            emsg += ", which seems to be the moc file from a different "
+                    "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
+                    "matching header ";
+            emsg += Quoted(MocStringHeaders(mocInc.Base));
+            emsg += " could not be found.";
+            LogFileError(GenT::MOC, FileName, emsg);
+          }
+          return false;
+        }
+      }
+    } else {
+      // Strict mode
+      if (ownMoc) {
+        // Include self
+        jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+        ownDotMocIncluded = true;
+        // Accept but issue a warning if moc isn't required
+        if (ownMacro.empty()) {
+          std::string emsg = "The file includes the moc file ";
+          emsg += Quoted(mocInc.Inc);
+          emsg += ", but does not contain a ";
+          emsg += Gen()->Moc().MacrosString();
+          emsg += " macro.";
+          Log().WarningFile(GenT::MOC, FileName, emsg);
+        }
+      } else {
+        // Don't allow <BASE>.moc include other than self in strict mode
+        {
+          std::string emsg = "The file includes the moc file ";
+          emsg += Quoted(mocInc.Inc);
+          emsg += ", which seems to be the moc file from a different "
+                  "source file.\nThis is not supported. Include ";
+          emsg += Quoted(meta.FileBase + ".moc");
+          emsg += " to run moc on this source file.";
+          LogFileError(GenT::MOC, FileName, emsg);
+        }
+        return false;
+      }
+    }
+  }
+
+  if (!ownMacro.empty() && !ownDotMocIncluded) {
+    // In this case, check whether the scanned file itself contains a
+    // Q_OBJECT.
+    // If this is the case, the moc_foo.cpp should probably be generated from
+    // foo.cpp instead of foo.h, because otherwise it won't build.
+    // But warn, since this is not how it is supposed to be used.
+    // This is for KDE4 compatibility.
+    if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) {
+      JobPre uscJobPre;
+      // Remove underscore job request
+      {
+        auto itC = jobs.begin();
+        auto itE = jobs.end();
+        for (; itC != itE; ++itC) {
+          JobPre& job(*itC);
+          if (job.self && job.underscore) {
+            uscJobPre = std::move(job);
+            jobs.erase(itC);
+            break;
+          }
+        }
+      }
+      // Issue a warning
+      {
+        std::string emsg = "The file contains a ";
+        emsg += ownMacro;
+        emsg += " macro, but does not include ";
+        emsg += Quoted(meta.FileBase + ".moc");
+        emsg += ". Instead it includes ";
+        emsg += Quoted(uscJobPre.IncludeString);
+        emsg += ".\nRunning moc on\n  ";
+        emsg += Quoted(FileName);
+        emsg += "!\nBetter include ";
+        emsg += Quoted(meta.FileBase + ".moc");
+        emsg += " for compatibility with strict mode.\n"
+                "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
+        Log().WarningFile(GenT::MOC, FileName, emsg);
+      }
+      // Add own source job
+      jobs.emplace_back(
+        JobPre{ true, false, FileName, uscJobPre.IncludeString });
+    } else {
+      // Otherwise always error out since it will not compile.
+      {
+        std::string emsg = "The file contains a ";
+        emsg += ownMacro;
+        emsg += " macro, but does not include ";
+        emsg += Quoted(meta.FileBase + ".moc");
+        emsg += "!\nConsider to\n - add #include \"";
+        emsg += meta.FileBase;
+        emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
+        LogFileError(GenT::MOC, FileName, emsg);
+      }
+      return false;
+    }
+  }
+
+  // Convert pre jobs to actual jobs
+  for (JobPre& jobPre : jobs) {
+    cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+      std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
+    if (jobPre.self) {
+      // Read dependencies from this source
+      JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+      Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+      jobMoc.DependsValid = true;
+    }
+    if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta)
+{
+  bool success = true;
+  std::string const macroName = Gen()->Moc().FindMacro(meta.Content);
+  if (!macroName.empty()) {
+    cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+      std::string(FileName), std::string(), std::string());
+    // Read dependencies from this source
+    {
+      JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+      Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+      jobMoc.DependsValid = true;
+    }
+    success = Gen()->ParallelJobPushMoc(std::move(jobHandle));
+  }
+  return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocStringHeaders(
+  std::string const& fileBase) const
+{
+  std::string res = fileBase;
+  res += ".{";
+  res += cmJoin(Gen()->Base().HeaderExtensions, ",");
+  res += "}";
+  return res;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader(
+  std::string const& includerDir, std::string const& includeBase)
+{
+  std::string header;
+  // Search in vicinity of the source
+  if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) {
+    // Search in include directories
+    for (std::string const& path : Gen()->Moc().IncludePaths) {
+      std::string fullPath = path;
+      fullPath.push_back('/');
+      fullPath += includeBase;
+      if (Gen()->Base().FindHeader(header, fullPath)) {
+        break;
+      }
+    }
+  }
+  // Sanitize
+  if (!header.empty()) {
+    header = FileSys().GetRealPath(header);
+  }
+  return header;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta)
+{
+  bool success = true;
+  if (meta.Content.find("ui_") != std::string::npos) {
+    const char* contentChars = meta.Content.c_str();
+    cmsys::RegularExpressionMatch match;
+    while (Gen()->Uic().RegExpInclude.find(contentChars, match)) {
+      if (!ParseUicInclude(meta, match.match(2))) {
+        success = false;
+        break;
+      }
+      contentChars += match.end();
+    }
+  }
+  return success;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta,
+                                                std::string&& includeString)
+{
+  bool success = false;
+  std::string uiInputFile = UicFindIncludedFile(meta, includeString);
+  if (!uiInputFile.empty()) {
+    if (!Gen()->Uic().skipped(uiInputFile)) {
+      cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>(
+        std::move(uiInputFile), FileName, std::move(includeString));
+      success = Gen()->ParallelJobPushUic(std::move(jobHandle));
+    } else {
+      // A skipped file is successful
+      success = true;
+    }
+  }
+  return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
+  MetaT const& meta, std::string const& includeString)
+{
+  std::string res;
+  std::string searchFile =
+    FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
+  searchFile += ".ui";
+  // Collect search paths list
+  std::deque<std::string> testFiles;
+  {
+    std::string const searchPath = FileSys().SubDirPrefix(includeString);
+
+    std::string searchFileFull;
+    if (!searchPath.empty()) {
+      searchFileFull = searchPath;
+      searchFileFull += searchFile;
+    }
+    // Vicinity of the source
+    {
+      std::string const sourcePath = meta.FileDir;
+      testFiles.push_back(sourcePath + searchFile);
+      if (!searchPath.empty()) {
+        testFiles.push_back(sourcePath + searchFileFull);
+      }
+    }
+    // AUTOUIC search paths
+    if (!Gen()->Uic().SearchPaths.empty()) {
+      for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+        testFiles.push_back((sPath + "/").append(searchFile));
+      }
+      if (!searchPath.empty()) {
+        for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+          testFiles.push_back((sPath + "/").append(searchFileFull));
+        }
+      }
+    }
+  }
+
+  // Search for the .ui file!
+  for (std::string const& testFile : testFiles) {
+    if (FileSys().FileExists(testFile)) {
+      res = FileSys().GetRealPath(testFile);
+      break;
+    }
+  }
+
+  // Log error
+  if (res.empty()) {
+    std::string emsg = "Could not find ";
+    emsg += Quoted(searchFile);
+    emsg += " in\n";
+    for (std::string const& testFile : testFiles) {
+      emsg += "  ";
+      emsg += Quoted(testFile);
+      emsg += "\n";
+    }
+    LogFileError(GenT::UIC, FileName, emsg);
+  }
+
+  return res;
+}
+
+void cmQtAutoMocUic::JobPostParseT::Process()
+{
+  if (Gen()->Moc().Enabled) {
+    // Add mocs compilations fence job
+    Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
+  }
+  // Add finish job
+  Gen()->WorkerPool().EmplaceJob<JobFinishT>();
+}
+
+void cmQtAutoMocUic::JobMocsCompilationT::Process()
+{
+  // Compose mocs compilation file content
+  std::string content =
+    "// This file is autogenerated. Changes will be overwritten.\n";
+  if (Gen()->MocAutoFiles().empty()) {
+    // Placeholder content
+    content += "// No files found that require moc or the moc files are "
+               "included\n";
+    content += "enum some_compilers { need_more_than_nothing };\n";
+  } else {
+    // Valid content
+    char const sbeg = Gen()->Base().MultiConfig ? '<' : '"';
+    char const send = Gen()->Base().MultiConfig ? '>' : '"';
+    for (std::string const& mocfile : Gen()->MocAutoFiles()) {
+      content += "#include ";
+      content += sbeg;
+      content += mocfile;
+      content += send;
+      content += '\n';
+    }
+  }
+
+  std::string const& compAbs = Gen()->Moc().CompFileAbs;
+  if (FileSys().FileDiffers(compAbs, content)) {
+    // Actually write mocs compilation file
+    if (Log().Verbose()) {
+      Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
+    }
+    if (!FileSys().FileWrite(compAbs, content)) {
+      LogFileError(GenT::MOC, compAbs,
+                   "mocs compilation file writing failed.");
+    }
+  } else if (Gen()->MocAutoFileUpdated()) {
+    // Only touch mocs compilation file
+    if (Log().Verbose()) {
+      Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+    }
+    FileSys().Touch(compAbs);
+  }
+}
+
+void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content)
+{
+  Gen()->Moc().FindDependencies(content, Depends);
+  DependsValid = true;
+}
+
+void cmQtAutoMocUic::JobMocT::Process()
+{
+  // Compute build file name
+  if (!IncludeString.empty()) {
+    BuildFile = Gen()->Base().AutogenIncludeDir;
+    BuildFile += '/';
+    BuildFile += IncludeString;
+  } else {
+    // Relative build path
+    std::string relPath = FileSys().GetFilePathChecksum(SourceFile);
+    relPath += "/moc_";
+    relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile);
+
+    // Register relative file path with duplication check
+    relPath = Gen()->ParallelMocAutoRegister(relPath);
+
+    // Absolute build path
+    if (Gen()->Base().MultiConfig) {
+      BuildFile = Gen()->Base().AutogenIncludeDir;
+      BuildFile += '/';
+      BuildFile += relPath;
+    } else {
+      BuildFile = Gen()->Base().AbsoluteBuildPath(relPath);
+    }
+  }
+
+  if (UpdateRequired()) {
+    GenerateMoc();
+  }
+}
+
+bool cmQtAutoMocUic::JobMocT::UpdateRequired()
+{
+  bool const verbose = Log().Verbose();
+
+  // Test if the build file exists
+  if (!FileSys().FileExists(BuildFile)) {
+    if (verbose) {
+      std::string reason = "Generating ";
+      reason += Quoted(BuildFile);
+      reason += " from its source file ";
+      reason += Quoted(SourceFile);
+      reason += " because it doesn't exist";
+      Log().Info(GenT::MOC, reason);
+    }
+    return true;
+  }
+
+  // Test if any setting changed
+  if (Gen()->Moc().SettingsChanged) {
+    if (verbose) {
+      std::string reason = "Generating ";
+      reason += Quoted(BuildFile);
+      reason += " from ";
+      reason += Quoted(SourceFile);
+      reason += " because the MOC settings changed";
+      Log().Info(GenT::MOC, reason);
+    }
+    return true;
+  }
+
+  // Test if the moc_predefs file is newer
+  if (!Gen()->Moc().PredefsFileAbs.empty()) {
+    bool isOlder = false;
+    {
+      std::string error;
+      isOlder = FileSys().FileIsOlderThan(BuildFile,
+                                          Gen()->Moc().PredefsFileAbs, &error);
+      if (!isOlder && !error.empty()) {
+        LogError(GenT::MOC, error);
+        return false;
+      }
+    }
+    if (isOlder) {
+      if (verbose) {
+        std::string reason = "Generating ";
+        reason += Quoted(BuildFile);
+        reason += " because it's older than: ";
+        reason += Quoted(Gen()->Moc().PredefsFileAbs);
+        Log().Info(GenT::MOC, reason);
+      }
+      return true;
+    }
+  }
+
+  // Test if the source file is newer
+  {
+    bool isOlder = false;
+    {
+      std::string error;
+      isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+      if (!isOlder && !error.empty()) {
+        LogError(GenT::MOC, error);
+        return false;
+      }
+    }
+    if (isOlder) {
+      if (verbose) {
+        std::string reason = "Generating ";
+        reason += Quoted(BuildFile);
+        reason += " because it's older than its source file ";
+        reason += Quoted(SourceFile);
+        Log().Info(GenT::MOC, reason);
+      }
+      return true;
+    }
+  }
+
+  // Test if a dependency file is newer
+  {
+    // Read dependencies on demand
+    if (!DependsValid) {
+      std::string content;
+      {
+        std::string error;
+        if (!FileSys().FileRead(content, SourceFile, &error)) {
+          std::string emsg = "Could not read file\n  ";
+          emsg += Quoted(SourceFile);
+          emsg += "\nrequired by moc include ";
+          emsg += Quoted(IncludeString);
+          emsg += " in\n  ";
+          emsg += Quoted(IncluderFile);
+          emsg += ".\n";
+          emsg += error;
+          LogError(GenT::MOC, emsg);
+          return false;
+        }
+      }
+      FindDependencies(content);
+    }
+    // Check dependency timestamps
+    std::string error;
+    std::string sourceDir = FileSys().SubDirPrefix(SourceFile);
+    for (std::string const& depFileRel : Depends) {
+      std::string depFileAbs =
+        Gen()->Moc().FindIncludedFile(sourceDir, depFileRel);
+      if (!depFileAbs.empty()) {
+        if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
+          if (verbose) {
+            std::string reason = "Generating ";
+            reason += Quoted(BuildFile);
+            reason += " from ";
+            reason += Quoted(SourceFile);
+            reason += " because it is older than it's dependency file ";
+            reason += Quoted(depFileAbs);
+            Log().Info(GenT::MOC, reason);
+          }
+          return true;
+        }
+        if (!error.empty()) {
+          LogError(GenT::MOC, error);
+          return false;
+        }
+      } else {
+        std::string message = "Could not find dependency file ";
+        message += Quoted(depFileRel);
+        Log().WarningFile(GenT::MOC, SourceFile, message);
+      }
+    }
+  }
+
+  return false;
+}
+
+void cmQtAutoMocUic::JobMocT::GenerateMoc()
+{
+  // Make sure the parent directory exists
+  if (!FileSys().MakeParentDirectory(BuildFile)) {
+    LogFileError(GenT::MOC, BuildFile, "Could not create parent directory.");
+    return;
+  }
+  {
+    // Compose moc command
+    std::vector<std::string> cmd;
+    cmd.push_back(Gen()->Moc().Executable);
+    // Add options
+    cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(),
+               Gen()->Moc().AllOptions.end());
+    // Add predefs include
+    if (!Gen()->Moc().PredefsFileAbs.empty()) {
+      cmd.emplace_back("--include");
+      cmd.push_back(Gen()->Moc().PredefsFileAbs);
+    }
+    cmd.emplace_back("-o");
+    cmd.push_back(BuildFile);
+    cmd.push_back(SourceFile);
+
+    // Execute moc command
+    cmWorkerPool::ProcessResultT result;
+    if (RunProcess(GenT::MOC, result, cmd)) {
+      // Moc command success
+      // Print moc output
+      if (!result.StdOut.empty()) {
+        Log().Info(GenT::MOC, result.StdOut);
+      }
+      // Notify the generator that a not included file changed (on demand)
+      if (IncludeString.empty()) {
+        Gen()->ParallelMocAutoUpdated();
+      }
+    } else {
+      // Moc command failed
+      {
+        std::string emsg = "The moc process failed to compile\n  ";
+        emsg += Quoted(SourceFile);
+        emsg += "\ninto\n  ";
+        emsg += Quoted(BuildFile);
+        emsg += ".\n";
+        emsg += result.ErrorMessage;
+        LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+      }
+      FileSys().FileRemove(BuildFile);
+    }
+  }
+}
+
+void cmQtAutoMocUic::JobUicT::Process()
+{
+  // Compute build file name
+  BuildFile = Gen()->Base().AutogenIncludeDir;
+  BuildFile += '/';
+  BuildFile += IncludeString;
+
+  if (UpdateRequired()) {
+    GenerateUic();
+  }
+}
+
+bool cmQtAutoMocUic::JobUicT::UpdateRequired()
+{
+  bool const verbose = Log().Verbose();
+
+  // Test if the build file exists
+  if (!FileSys().FileExists(BuildFile)) {
+    if (verbose) {
+      std::string reason = "Generating ";
+      reason += Quoted(BuildFile);
+      reason += " from its source file ";
+      reason += Quoted(SourceFile);
+      reason += " because it doesn't exist";
+      Log().Info(GenT::UIC, reason);
+    }
+    return true;
+  }
+
+  // Test if the uic settings changed
+  if (Gen()->Uic().SettingsChanged) {
+    if (verbose) {
+      std::string reason = "Generating ";
+      reason += Quoted(BuildFile);
+      reason += " from ";
+      reason += Quoted(SourceFile);
+      reason += " because the UIC settings changed";
+      Log().Info(GenT::UIC, reason);
+    }
+    return true;
+  }
+
+  // Test if the source file is newer
+  {
+    bool isOlder = false;
+    {
+      std::string error;
+      isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+      if (!isOlder && !error.empty()) {
+        LogError(GenT::UIC, error);
+        return false;
+      }
+    }
+    if (isOlder) {
+      if (verbose) {
+        std::string reason = "Generating ";
+        reason += Quoted(BuildFile);
+        reason += " because it's older than its source file ";
+        reason += Quoted(SourceFile);
+        Log().Info(GenT::UIC, reason);
+      }
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void cmQtAutoMocUic::JobUicT::GenerateUic()
+{
+  // Make sure the parent directory exists
+  if (!FileSys().MakeParentDirectory(BuildFile)) {
+    LogFileError(GenT::UIC, BuildFile, "Could not create parent directory.");
+    return;
+  }
+  {
+    // Compose uic command
+    std::vector<std::string> cmd;
+    cmd.push_back(Gen()->Uic().Executable);
+    {
+      std::vector<std::string> allOpts = Gen()->Uic().TargetOptions;
+      auto optionIt = Gen()->Uic().Options.find(SourceFile);
+      if (optionIt != Gen()->Uic().Options.end()) {
+        UicMergeOptions(allOpts, optionIt->second,
+                        (Gen()->Base().QtVersionMajor == 5));
+      }
+      cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+    }
+    cmd.emplace_back("-o");
+    cmd.emplace_back(BuildFile);
+    cmd.emplace_back(SourceFile);
+
+    cmWorkerPool::ProcessResultT result;
+    if (RunProcess(GenT::UIC, result, cmd)) {
+      // Uic command success
+      // Print uic output
+      if (!result.StdOut.empty()) {
+        Log().Info(GenT::UIC, result.StdOut);
+      }
+    } else {
+      // Uic command failed
+      {
+        std::string emsg = "The uic process failed to compile\n  ";
+        emsg += Quoted(SourceFile);
+        emsg += "\ninto\n  ";
+        emsg += Quoted(BuildFile);
+        emsg += "\nincluded by\n  ";
+        emsg += Quoted(IncluderFile);
+        emsg += ".\n";
+        emsg += result.ErrorMessage;
+        LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
+      }
+      FileSys().FileRemove(BuildFile);
+    }
+  }
+}
+
+void cmQtAutoMocUic::JobFinishT::Process()
+{
+  Gen()->AbortSuccess();
+}
+
+cmQtAutoMocUic::cmQtAutoMocUic()
+  : Base_(&FileSys())
+  , Moc_(&FileSys())
+{
+  // Precompile regular expressions
+  Moc_.RegExpInclude.compile(
+    "(^|\n)[ \t]*#[ \t]*include[ \t]+"
+    "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+  Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+                             "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+}
+
+cmQtAutoMocUic::~cmQtAutoMocUic() = default;
+
+bool cmQtAutoMocUic::Init(cmMakefile* makefile)
+{
+  // -- Meta
+  Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+
+  // Utility lambdas
+  auto InfoGet = [makefile](const char* key) {
+    return makefile->GetSafeDefinition(key);
+  };
+  auto InfoGetBool = [makefile](const char* key) {
+    return makefile->IsOn(key);
+  };
+  auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
+    std::vector<std::string> list;
+    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
+    return list;
+  };
+  auto InfoGetLists =
+    [makefile](const char* key) -> std::vector<std::vector<std::string>> {
+    std::vector<std::vector<std::string>> lists;
+    {
+      std::string const value = makefile->GetSafeDefinition(key);
+      std::string::size_type pos = 0;
+      while (pos < value.size()) {
+        std::string::size_type next = value.find(ListSep, pos);
+        std::string::size_type length =
+          (next != std::string::npos) ? next - pos : value.size() - pos;
+        // Remove enclosing braces
+        if (length >= 2) {
+          std::string::const_iterator itBeg = value.begin() + (pos + 1);
+          std::string::const_iterator itEnd = itBeg + (length - 2);
+          {
+            std::string subValue(itBeg, itEnd);
+            std::vector<std::string> list;
+            cmSystemTools::ExpandListArgument(subValue, list);
+            lists.push_back(std::move(list));
+          }
+        }
+        pos += length;
+        pos += ListSep.size();
+      }
+    }
+    return lists;
+  };
+  auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
+    const char* valueConf = nullptr;
+    {
+      std::string keyConf = key;
+      keyConf += '_';
+      keyConf += InfoConfig();
+      valueConf = makefile->GetDefinition(keyConf);
+    }
+    if (valueConf == nullptr) {
+      return makefile->GetSafeDefinition(key);
+    }
+    return std::string(valueConf);
+  };
+  auto InfoGetConfigList =
+    [&InfoGetConfig](const char* key) -> std::vector<std::string> {
+    std::vector<std::string> list;
+    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
+    return list;
+  };
+
+  // -- Read info file
+  if (!makefile->ReadListFile(InfoFile())) {
+    Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
+    return false;
+  }
+
+  // -- Meta
+  Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
+  Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
+  {
+    unsigned long num = Base_.NumThreads;
+    if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
+      num = std::max<unsigned long>(num, 1);
+      num = std::min<unsigned long>(num, ParallelMax);
+      Base_.NumThreads = static_cast<unsigned int>(num);
+    }
+  }
+
+  // - Files and directories
+  Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
+  Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
+  Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
+  Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
+  Base_.IncludeProjectDirsBefore =
+    InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
+  Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+  if (Base_.AutogenBuildDir.empty()) {
+    Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
+    return false;
+  }
+  // include directory
+  Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
+  if (Base_.AutogenIncludeDir.empty()) {
+    Log().ErrorFile(GenT::GEN, InfoFile(),
+                    "Autogen include directory missing");
+    return false;
+  }
+
+  // - Files
+  SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+  if (SettingsFile_.empty()) {
+    Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
+    return false;
+  }
+
+  // - Qt environment
+  {
+    unsigned long qtv = Base_.QtVersionMajor;
+    if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
+                                     &qtv)) {
+      Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+    }
+  }
+
+  // - Moc
+  Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+  Moc_.Enabled = !Moc().Executable.empty();
+  if (Moc().Enabled) {
+    for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
+      Moc_.SkipList.insert(std::move(sfl));
+    }
+    Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+    Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
+    Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
+    Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
+    for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+      Moc_.MacroFilters.emplace_back(
+        item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
+    }
+    {
+      auto pushFilter = [this](std::string const& key, std::string const& exp,
+                               std::string& error) {
+        if (!key.empty()) {
+          if (!exp.empty()) {
+            Moc_.DependFilters.emplace_back();
+            KeyExpT& filter(Moc_.DependFilters.back());
+            if (filter.Exp.compile(exp)) {
+              filter.Key = key;
+            } else {
+              error = "Regular expression compiling failed";
+            }
+          } else {
+            error = "Regular expression is empty";
+          }
+        } else {
+          error = "Key is empty";
+        }
+        if (!error.empty()) {
+          error = ("AUTOMOC_DEPEND_FILTERS: " + error);
+          error += "\n";
+          error += "  Key: ";
+          error += Quoted(key);
+          error += "\n";
+          error += "  Exp: ";
+          error += Quoted(exp);
+          error += "\n";
+        }
+      };
+
+      std::string error;
+      // Insert default filter for Q_PLUGIN_METADATA
+      if (Base().QtVersionMajor != 4) {
+        pushFilter("Q_PLUGIN_METADATA",
+                   "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+                   "[^\\)]*FILE[ \t]*\"([^\"]+)\"",
+                   error);
+      }
+      // Insert user defined dependency filters
+      {
+        std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+        if ((flts.size() % 2) == 0) {
+          for (std::vector<std::string>::iterator itC = flts.begin(),
+                                                  itE = flts.end();
+               itC != itE; itC += 2) {
+            pushFilter(*itC, *(itC + 1), error);
+            if (!error.empty()) {
+              break;
+            }
+          }
+        } else {
+          Log().ErrorFile(
+            GenT::MOC, InfoFile(),
+            "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+          return false;
+        }
+      }
+      if (!error.empty()) {
+        Log().ErrorFile(GenT::MOC, InfoFile(), error);
+        return false;
+      }
+    }
+    Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+    // Install moc predefs job
+    if (!Moc().PredefsCmd.empty()) {
+      WorkerPool().EmplaceJob<JobMocPredefsT>();
+    }
+  }
+
+  // - Uic
+  Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+  Uic_.Enabled = !Uic().Executable.empty();
+  if (Uic().Enabled) {
+    for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
+      Uic_.SkipList.insert(std::move(sfl));
+    }
+    Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
+    Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
+    {
+      auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
+      auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
+      // Compare list sizes
+      if (sources.size() != options.size()) {
+        std::ostringstream ost;
+        ost << "files/options lists sizes mismatch (" << sources.size() << "/"
+            << options.size() << ")";
+        Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
+        return false;
+      }
+      auto fitEnd = sources.cend();
+      auto fit = sources.begin();
+      auto oit = options.begin();
+      while (fit != fitEnd) {
+        Uic_.Options[*fit] = std::move(*oit);
+        ++fit;
+        ++oit;
+      }
+    }
+  }
+
+  // - Headers and sources
+  // Add sources
+  {
+    auto addSource = [this](std::string&& src, bool moc, bool uic) {
+      WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false);
+    };
+    for (std::string& src : InfoGetList("AM_SOURCES")) {
+      addSource(std::move(src), true, true);
+    }
+    if (Moc().Enabled) {
+      for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
+        addSource(std::move(src), true, false);
+      }
+    }
+    if (Uic().Enabled) {
+      for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
+        addSource(std::move(src), false, true);
+      }
+    }
+  }
+  // Add Fence job
+  WorkerPool().EmplaceJob<JobFenceT>();
+  // Add headers
+  {
+    auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
+      WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true);
+    };
+    for (std::string& hdr : InfoGetList("AM_HEADERS")) {
+      addHeader(std::move(hdr), true, true);
+    }
+    if (Moc().Enabled) {
+      for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
+        addHeader(std::move(hdr), true, false);
+      }
+    }
+    if (Uic().Enabled) {
+      for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
+        addHeader(std::move(hdr), false, true);
+      }
+    }
+  }
+  // Addpost parse fence job
+  WorkerPool().EmplaceJob<JobPostParseT>();
+
+  // Init derived information
+  // ------------------------
+
+  // Init file path checksum generator
+  FileSys().setupFilePathChecksum(
+    Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+    Base().ProjectBinaryDir);
+
+  // Moc variables
+  if (Moc().Enabled) {
+    // Mocs compilation file
+    Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
+
+    // Moc predefs file
+    if (!Moc_.PredefsCmd.empty()) {
+      Moc_.PredefsFileRel = "moc_predefs";
+      if (Base_.MultiConfig) {
+        Moc_.PredefsFileRel += '_';
+        Moc_.PredefsFileRel += InfoConfig();
+      }
+      Moc_.PredefsFileRel += ".h";
+      Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
+    }
+
+    // Sort include directories on demand
+    if (Base().IncludeProjectDirsBefore) {
+      // Move strings to temporary list
+      std::list<std::string> includes;
+      includes.insert(includes.end(), Moc().IncludePaths.begin(),
+                      Moc().IncludePaths.end());
+      Moc_.IncludePaths.clear();
+      Moc_.IncludePaths.reserve(includes.size());
+      // Append project directories only
+      {
+        std::array<std::string const*, 2> const movePaths = {
+          { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
+        };
+        for (std::string const* ppath : movePaths) {
+          std::list<std::string>::iterator it = includes.begin();
+          while (it != includes.end()) {
+            std::string const& path = *it;
+            if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
+              Moc_.IncludePaths.push_back(path);
+              it = includes.erase(it);
+            } else {
+              ++it;
+            }
+          }
+        }
+      }
+      // Append remaining directories
+      Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
+                               includes.end());
+    }
+    // Compose moc includes list
+    {
+      std::set<std::string> frameworkPaths;
+      for (std::string const& path : Moc().IncludePaths) {
+        Moc_.Includes.push_back("-I" + path);
+        // Extract framework path
+        if (cmHasLiteralSuffix(path, ".framework/Headers")) {
+          // Go up twice to get to the framework root
+          std::vector<std::string> pathComponents;
+          FileSys().SplitPath(path, pathComponents);
+          std::string frameworkPath = FileSys().JoinPath(
+            pathComponents.begin(), pathComponents.end() - 2);
+          frameworkPaths.insert(frameworkPath);
+        }
+      }
+      // Append framework includes
+      for (std::string const& path : frameworkPaths) {
+        Moc_.Includes.emplace_back("-F");
+        Moc_.Includes.push_back(path);
+      }
+    }
+    // Setup single list with all options
+    {
+      // Add includes
+      Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
+                             Moc().Includes.end());
+      // Add definitions
+      for (std::string const& def : Moc().Definitions) {
+        Moc_.AllOptions.push_back("-D" + def);
+      }
+      // Add options
+      Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
+                             Moc().Options.end());
+    }
+  }
+
+  return true;
+}
+
+bool cmQtAutoMocUic::Process()
+{
+  SettingsFileRead();
+  if (!CreateDirectories()) {
+    return false;
+  }
+
+  if (!WorkerPool_.Process(Base().NumThreads, this)) {
+    return false;
+  }
+
+  if (JobError_) {
+    return false;
+  }
+
+  return SettingsFileWrite();
+}
+
+void cmQtAutoMocUic::SettingsFileRead()
+{
+  // Compose current settings strings
+  {
+    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
+    std::string const sep(" ~~~ ");
+    if (Moc_.Enabled) {
+      std::string str;
+      str += Moc().Executable;
+      str += sep;
+      str += cmJoin(Moc().AllOptions, ";");
+      str += sep;
+      str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
+      str += sep;
+      str += cmJoin(Moc().PredefsCmd, ";");
+      str += sep;
+      SettingsStringMoc_ = crypt.HashString(str);
+    }
+    if (Uic().Enabled) {
+      std::string str;
+      str += Uic().Executable;
+      str += sep;
+      str += cmJoin(Uic().TargetOptions, ";");
+      for (const auto& item : Uic().Options) {
+        str += sep;
+        str += item.first;
+        str += sep;
+        str += cmJoin(item.second, ";");
+      }
+      str += sep;
+      SettingsStringUic_ = crypt.HashString(str);
+    }
+  }
+
+  // Read old settings and compare
+  {
+    std::string content;
+    if (FileSys().FileRead(content, SettingsFile_)) {
+      if (Moc().Enabled) {
+        if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
+          Moc_.SettingsChanged = true;
+        }
+      }
+      if (Uic().Enabled) {
+        if (SettingsStringUic_ != SettingsFind(content, "uic")) {
+          Uic_.SettingsChanged = true;
+        }
+      }
+      // In case any setting changed remove the old settings file.
+      // This triggers a full rebuild on the next run if the current
+      // build is aborted before writing the current settings in the end.
+      if (Moc().SettingsChanged || Uic().SettingsChanged) {
+        FileSys().FileRemove(SettingsFile_);
+      }
+    } else {
+      // Settings file read failed
+      if (Moc().Enabled) {
+        Moc_.SettingsChanged = true;
+      }
+      if (Uic().Enabled) {
+        Uic_.SettingsChanged = true;
+      }
+    }
+  }
+}
+
+bool cmQtAutoMocUic::SettingsFileWrite()
+{
+  // Only write if any setting changed
+  if (Moc().SettingsChanged || Uic().SettingsChanged) {
+    if (Log().Verbose()) {
+      Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
+    }
+    // Compose settings file content
+    std::string content;
+    {
+      auto SettingAppend = [&content](const char* key,
+                                      std::string const& value) {
+        if (!value.empty()) {
+          content += key;
+          content += ':';
+          content += value;
+          content += '\n';
+        }
+      };
+      SettingAppend("moc", SettingsStringMoc_);
+      SettingAppend("uic", SettingsStringUic_);
+    }
+    // Write settings file
+    std::string error;
+    if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
+      Log().ErrorFile(GenT::GEN, SettingsFile_,
+                      "Settings file writing failed. " + error);
+      // Remove old settings file to trigger a full rebuild on the next run
+      FileSys().FileRemove(SettingsFile_);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoMocUic::CreateDirectories()
+{
+  // Create AUTOGEN include directory
+  if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
+    Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
+                    "Could not create directory.");
+    return false;
+  }
+  return true;
+}
+
+// Private method that requires cmQtAutoMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoMocUic::Abort(bool error)
+{
+  if (error) {
+    JobError_.store(true);
+  }
+  WorkerPool_.Abort();
+}
+
+bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle)
+{
+  JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle));
+  // Do additional tests if this is an included moc job
+  if (!mocJob.IncludeString.empty()) {
+    std::lock_guard<std::mutex> guard(MocMetaMutex_);
+    // Register included moc file
+    MocIncludedFiles_.emplace(mocJob.SourceFile);
+
+    // Check if the same moc file would be generated from a different
+    // source file.
+    auto const range = MocIncludes_.equal_range(mocJob.IncludeString);
+    for (auto it = range.first; it != range.second; ++it) {
+      if (it->second[0] == mocJob.SourceFile) {
+        // The output file already gets generated
+        return true;
+      }
+      {
+        // The output file already gets generated - from a different source
+        // file!
+        std::string error = "The two source files\n  ";
+        error += Quoted(mocJob.IncluderFile);
+        error += " and\n  ";
+        error += Quoted(it->second[1]);
+        error += "\ncontain the same moc include string ";
+        error += Quoted(mocJob.IncludeString);
+        error += "\nbut the moc file would be generated from different "
+                 "source files\n  ";
+        error += Quoted(mocJob.SourceFile);
+        error += " and\n  ";
+        error += Quoted(it->second[0]);
+        error += ".\nConsider to\n"
+                 "- not include the \"moc_<NAME>.cpp\" file\n"
+                 "- add a directory prefix to a \"<NAME>.moc\" include "
+                 "(e.g \"sub/<NAME>.moc\")\n"
+                 "- rename the source file(s)\n";
+        Log().Error(GenT::MOC, error);
+        AbortError();
+        return false;
+      }
+    }
+
+    // We're still here so register this job
+    MocIncludes_.emplace_hint(range.first, mocJob.IncludeString,
+                              std::array<std::string, 2>{
+                                { mocJob.SourceFile, mocJob.IncluderFile } });
+  }
+  return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle)
+{
+  const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
+  {
+    std::lock_guard<std::mutex> guard(UicMetaMutex_);
+    // Check if the same uic file would be generated from a different
+    // source file.
+    auto const range = UicIncludes_.equal_range(uicJob.IncludeString);
+    for (auto it = range.first; it != range.second; ++it) {
+      if (it->second[0] == uicJob.SourceFile) {
+        // The output file already gets generated
+        return true;
+      }
+      {
+        // The output file already gets generated - from a different .ui
+        // file!
+        std::string error = "The two source files\n  ";
+        error += Quoted(uicJob.IncluderFile);
+        error += " and\n  ";
+        error += Quoted(it->second[1]);
+        error += "\ncontain the same uic include string ";
+        error += Quoted(uicJob.IncludeString);
+        error += "\nbut the uic file would be generated from different "
+                 "source files\n  ";
+        error += Quoted(uicJob.SourceFile);
+        error += " and\n  ";
+        error += Quoted(it->second[0]);
+        error +=
+          ".\nConsider to\n"
+          "- add a directory prefix to a \"ui_<NAME>.h\" include "
+          "(e.g \"sub/ui_<NAME>.h\")\n"
+          "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+          "include(s)\n";
+        Log().Error(GenT::UIC, error);
+        AbortError();
+        return false;
+      }
+    }
+
+    // We're still here so register this job
+    UicIncludes_.emplace_hint(range.first, uicJob.IncludeString,
+                              std::array<std::string, 2>{
+                                { uicJob.SourceFile, uicJob.IncluderFile } });
+  }
+  return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile)
+{
+  std::lock_guard<std::mutex> guard(MocMetaMutex_);
+  return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
+}
+
+std::string cmQtAutoMocUic::ParallelMocAutoRegister(
+  std::string const& baseName)
+{
+  std::string res;
+  {
+    std::lock_guard<std::mutex> mocLock(MocMetaMutex_);
+    res = baseName;
+    res += ".cpp";
+    if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+      MocAutoFiles_.emplace(res);
+    } else {
+      // Append number suffix to the file name
+      for (unsigned int ii = 2; ii != 1024; ++ii) {
+        res = baseName;
+        res += '_';
+        res += std::to_string(ii);
+        res += ".cpp";
+        if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+          MocAutoFiles_.emplace(res);
+          break;
+        }
+      }
+    }
+  }
+  return res;
+}
diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h
new file mode 100644
index 0000000..3902abb
--- /dev/null
+++ b/Source/cmQtAutoMocUic.h
@@ -0,0 +1,413 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmQtAutoMocUic_h
+#define cmQtAutoMocUic_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmQtAutoGen.h"
+#include "cmQtAutoGenerator.h"
+#include "cmWorkerPool.h"
+#include "cmsys/RegularExpression.hxx"
+
+#include <array>
+#include <atomic>
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+class cmMakefile;
+
+// @brief AUTOMOC and AUTOUIC generator
+class cmQtAutoMocUic : public cmQtAutoGenerator
+{
+public:
+  cmQtAutoMocUic();
+  ~cmQtAutoMocUic() override;
+
+  cmQtAutoMocUic(cmQtAutoMocUic const&) = delete;
+  cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete;
+
+public:
+  // -- Types
+  typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap;
+
+  /// @brief Search key plus regular expression pair
+  ///
+  struct KeyExpT
+  {
+    KeyExpT() = default;
+
+    KeyExpT(const char* key, const char* exp)
+      : Key(key)
+      , Exp(exp)
+    {
+    }
+
+    KeyExpT(std::string key, std::string const& exp)
+      : Key(std::move(key))
+      , Exp(exp)
+    {
+    }
+
+    std::string Key;
+    cmsys::RegularExpression Exp;
+  };
+
+  /// @brief Common settings
+  ///
+  class BaseSettingsT
+  {
+  public:
+    // -- Volatile methods
+    BaseSettingsT(FileSystem* fileSystem)
+      : MultiConfig(false)
+      , IncludeProjectDirsBefore(false)
+      , QtVersionMajor(4)
+      , NumThreads(1)
+      , FileSys(fileSystem)
+    {
+    }
+
+    BaseSettingsT(BaseSettingsT const&) = delete;
+    BaseSettingsT& operator=(BaseSettingsT const&) = delete;
+
+    // -- Const methods
+    std::string AbsoluteBuildPath(std::string const& relativePath) const;
+    bool FindHeader(std::string& header,
+                    std::string const& testBasePath) const;
+
+    // -- Attributes
+    // - Config
+    bool MultiConfig;
+    bool IncludeProjectDirsBefore;
+    unsigned int QtVersionMajor;
+    unsigned int NumThreads;
+    // - Directories
+    std::string ProjectSourceDir;
+    std::string ProjectBinaryDir;
+    std::string CurrentSourceDir;
+    std::string CurrentBinaryDir;
+    std::string AutogenBuildDir;
+    std::string AutogenIncludeDir;
+    // - Files
+    std::vector<std::string> HeaderExtensions;
+    // - File system
+    FileSystem* FileSys;
+  };
+
+  /// @brief Moc settings
+  ///
+  class MocSettingsT
+  {
+  public:
+    MocSettingsT(FileSystem* fileSys)
+      : FileSys(fileSys)
+    {
+    }
+
+    MocSettingsT(MocSettingsT const&) = delete;
+    MocSettingsT& operator=(MocSettingsT const&) = delete;
+
+    // -- Const methods
+    bool skipped(std::string const& fileName) const;
+    std::string FindMacro(std::string const& content) const;
+    std::string MacrosString() const;
+    std::string FindIncludedFile(std::string const& sourcePath,
+                                 std::string const& includeString) const;
+    void FindDependencies(std::string const& content,
+                          std::set<std::string>& depends) const;
+
+    // -- Attributes
+    bool Enabled = false;
+    bool SettingsChanged = false;
+    bool RelaxedMode = false;
+    std::string Executable;
+    std::string CompFileAbs;
+    std::string PredefsFileRel;
+    std::string PredefsFileAbs;
+    std::unordered_set<std::string> SkipList;
+    std::vector<std::string> IncludePaths;
+    std::vector<std::string> Includes;
+    std::vector<std::string> Definitions;
+    std::vector<std::string> Options;
+    std::vector<std::string> AllOptions;
+    std::vector<std::string> PredefsCmd;
+    std::vector<KeyExpT> DependFilters;
+    std::vector<KeyExpT> MacroFilters;
+    cmsys::RegularExpression RegExpInclude;
+    // - File system
+    FileSystem* FileSys;
+  };
+
+  /// @brief Uic settings
+  ///
+  class UicSettingsT
+  {
+  public:
+    UicSettingsT() = default;
+
+    UicSettingsT(UicSettingsT const&) = delete;
+    UicSettingsT& operator=(UicSettingsT const&) = delete;
+
+    // -- Const methods
+    bool skipped(std::string const& fileName) const;
+
+    // -- Attributes
+    bool Enabled = false;
+    bool SettingsChanged = false;
+    std::string Executable;
+    std::unordered_set<std::string> SkipList;
+    std::vector<std::string> TargetOptions;
+    std::map<std::string, std::vector<std::string>> Options;
+    std::vector<std::string> SearchPaths;
+    cmsys::RegularExpression RegExpInclude;
+  };
+
+  /// @brief Abstract job class for concurrent job processing
+  ///
+  class JobT : public cmWorkerPool::JobT
+  {
+  protected:
+    /**
+     * @brief Protected default constructor
+     */
+    JobT(bool fence = false)
+      : cmWorkerPool::JobT(fence)
+    {
+    }
+
+    //! Get the generator. Only valid during Process() call!
+    cmQtAutoMocUic* Gen() const
+    {
+      return static_cast<cmQtAutoMocUic*>(UserData());
+    };
+
+    //! Get the file system interface. Only valid during Process() call!
+    FileSystem& FileSys() { return Gen()->FileSys(); }
+    //! Get the logger. Only valid during Process() call!
+    Logger& Log() { return Gen()->Log(); }
+
+    // -- Error logging with automatic abort
+    void LogError(GenT genType, std::string const& message) const;
+    void LogFileError(GenT genType, std::string const& filename,
+                      std::string const& message) const;
+    void LogCommandError(GenT genType, std::string const& message,
+                         std::vector<std::string> const& command,
+                         std::string const& output) const;
+
+    /**
+     * @brief Run an external process. Use only during Process() call!
+     */
+    bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
+                    std::vector<std::string> const& command);
+  };
+
+  /// @brief Fence job utility class
+  ///
+  class JobFenceT : public JobT
+  {
+  public:
+    JobFenceT()
+      : JobT(true)
+    {
+    }
+    void Process() override{};
+  };
+
+  /// @brief Generate moc_predefs.h
+  ///
+  class JobMocPredefsT : public JobT
+  {
+  private:
+    void Process() override;
+  };
+
+  /// @brief Parses a source file
+  ///
+  class JobParseT : public JobT
+  {
+  public:
+    JobParseT(std::string fileName, bool moc, bool uic, bool header = false)
+      : FileName(std::move(fileName))
+      , AutoMoc(moc)
+      , AutoUic(uic)
+      , Header(header)
+    {
+    }
+
+  private:
+    struct MetaT
+    {
+      std::string Content;
+      std::string FileDir;
+      std::string FileBase;
+    };
+
+    void Process() override;
+    bool ParseMocSource(MetaT const& meta);
+    bool ParseMocHeader(MetaT const& meta);
+    std::string MocStringHeaders(std::string const& fileBase) const;
+    std::string MocFindIncludedHeader(std::string const& includerDir,
+                                      std::string const& includeBase);
+    bool ParseUic(MetaT const& meta);
+    bool ParseUicInclude(MetaT const& meta, std::string&& includeString);
+    std::string UicFindIncludedFile(MetaT const& meta,
+                                    std::string const& includeString);
+
+  private:
+    std::string FileName;
+    bool AutoMoc = false;
+    bool AutoUic = false;
+    bool Header = false;
+  };
+
+  /// @brief Generates additional jobs after all files have been parsed
+  ///
+  class JobPostParseT : public JobFenceT
+  {
+  private:
+    void Process() override;
+  };
+
+  /// @brief Generate mocs_compilation.cpp
+  ///
+  class JobMocsCompilationT : public JobFenceT
+  {
+  private:
+    void Process() override;
+  };
+
+  /// @brief Moc a file job
+  ///
+  class JobMocT : public JobT
+  {
+  public:
+    JobMocT(std::string sourceFile, std::string includerFile,
+            std::string includeString)
+      : SourceFile(std::move(sourceFile))
+      , IncluderFile(std::move(includerFile))
+      , IncludeString(std::move(includeString))
+    {
+    }
+
+    void FindDependencies(std::string const& content);
+
+  private:
+    void Process() override;
+    bool UpdateRequired();
+    void GenerateMoc();
+
+  public:
+    std::string SourceFile;
+    std::string IncluderFile;
+    std::string IncludeString;
+    std::string BuildFile;
+    bool DependsValid = false;
+    std::set<std::string> Depends;
+  };
+
+  /// @brief Uic a file job
+  ///
+  class JobUicT : public JobT
+  {
+  public:
+    JobUicT(std::string sourceFile, std::string includerFile,
+            std::string includeString)
+      : SourceFile(std::move(sourceFile))
+      , IncluderFile(std::move(includerFile))
+      , IncludeString(std::move(includeString))
+    {
+    }
+
+  private:
+    void Process() override;
+    bool UpdateRequired();
+    void GenerateUic();
+
+  public:
+    std::string SourceFile;
+    std::string IncluderFile;
+    std::string IncludeString;
+    std::string BuildFile;
+  };
+
+  /// @brief The last job
+  ///
+  class JobFinishT : public JobFenceT
+  {
+  private:
+    void Process() override;
+  };
+
+  // -- Const settings interface
+  const BaseSettingsT& Base() const { return this->Base_; }
+  const MocSettingsT& Moc() const { return this->Moc_; }
+  const UicSettingsT& Uic() const { return this->Uic_; }
+
+  // -- Parallel job processing interface
+  cmWorkerPool& WorkerPool() { return WorkerPool_; }
+  void AbortError() { Abort(true); }
+  void AbortSuccess() { Abort(false); }
+  bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle);
+  bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle);
+
+  // -- Mocs compilation include file updated flag
+  void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); }
+  bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); }
+
+  // -- Mocs compilation file register
+  std::string ParallelMocAutoRegister(std::string const& baseName);
+  bool ParallelMocIncluded(std::string const& sourceFile);
+  std::set<std::string> const& MocAutoFiles() const
+  {
+    return this->MocAutoFiles_;
+  }
+
+private:
+  // -- Utility accessors
+  Logger& Log() { return Logger_; }
+  FileSystem& FileSys() { return FileSys_; }
+  // -- Abstract processing interface
+  bool Init(cmMakefile* makefile) override;
+  bool Process() override;
+  // -- Settings file
+  void SettingsFileRead();
+  bool SettingsFileWrite();
+  // -- Thread processing
+  void Abort(bool error);
+  // -- Generation
+  bool CreateDirectories();
+
+private:
+  // -- Utility
+  Logger Logger_;
+  FileSystem FileSys_;
+  // -- Settings
+  BaseSettingsT Base_;
+  MocSettingsT Moc_;
+  UicSettingsT Uic_;
+  // -- Moc meta
+  std::mutex MocMetaMutex_;
+  std::set<std::string> MocIncludedFiles_;
+  IncludesMap MocIncludes_;
+  std::set<std::string> MocAutoFiles_;
+  std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false);
+  // -- Uic meta
+  std::mutex UicMetaMutex_;
+  IncludesMap UicIncludes_;
+  // -- Settings file
+  std::string SettingsFile_;
+  std::string SettingsStringMoc_;
+  std::string SettingsStringUic_;
+  // -- Thread pool and job queue
+  std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
+  cmWorkerPool WorkerPool_;
+};
+
+#endif
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 252d985..998f904 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -1,9 +1,13 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+#define _SCL_SECURE_NO_WARNINGS
+
 #include "cmStringCommand.h"
 
 #include "cmsys/RegularExpression.hxx"
+#include <algorithm>
 #include <ctype.h>
+#include <iterator>
 #include <memory> // IWYU pragma: keep
 #include <sstream>
 #include <stdio.h>
@@ -13,6 +17,7 @@
 #include "cmCryptoHash.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
@@ -79,6 +84,9 @@
   if (subCommand == "STRIP") {
     return this->HandleStripCommand(args);
   }
+  if (subCommand == "REPEAT") {
+    return this->HandleRepeatCommand(args);
+  }
   if (subCommand == "RANDOM") {
     return this->HandleRandomCommand(args);
   }
@@ -709,6 +717,59 @@
   return true;
 }
 
+bool cmStringCommand::HandleRepeatCommand(std::vector<std::string> const& args)
+{
+  // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)`
+  enum ArgPos : std::size_t
+  {
+    SUB_COMMAND,
+    VALUE,
+    TIMES,
+    OUTPUT_VARIABLE,
+    TOTAL_ARGS
+  };
+
+  if (args.size() != ArgPos::TOTAL_ARGS) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      "sub-command REPEAT requires three arguments.");
+    return true;
+  }
+
+  unsigned long times;
+  if (!cmSystemTools::StringToULong(args[ArgPos::TIMES].c_str(), &times)) {
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 "repeat count is not a positive number.");
+    return true;
+  }
+
+  const auto& stringValue = args[ArgPos::VALUE];
+  const auto& variableName = args[ArgPos::OUTPUT_VARIABLE];
+  const auto inStringLength = stringValue.size();
+
+  std::string result;
+  switch (inStringLength) {
+    case 0u:
+      // Nothing to do for zero length input strings
+      break;
+    case 1u:
+      // NOTE If the string to repeat consists of the only character,
+      // use the appropriate constructor.
+      result = std::string(times, stringValue[0]);
+      break;
+    default:
+      result = std::string(inStringLength * times, char{});
+      for (auto i = 0u; i < times; ++i) {
+        std::copy(cm::cbegin(stringValue), cm::cend(stringValue),
+                  &result[i * inStringLength]);
+      }
+      break;
+  }
+
+  this->Makefile->AddDefinition(variableName, result.c_str());
+  return true;
+}
+
 bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args)
 {
   if (args.size() < 2 || args.size() == 3 || args.size() == 5) {
diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h
index cbff73e..acde605 100644
--- a/Source/cmStringCommand.h
+++ b/Source/cmStringCommand.h
@@ -51,6 +51,7 @@
   bool HandleConcatCommand(std::vector<std::string> const& args);
   bool HandleJoinCommand(std::vector<std::string> const& args);
   bool HandleStripCommand(std::vector<std::string> const& args);
+  bool HandleRepeatCommand(std::vector<std::string> const& args);
   bool HandleRandomCommand(std::vector<std::string> const& args);
   bool HandleFindCommand(std::vector<std::string> const& args);
   bool HandleTimestampCommand(std::vector<std::string> const& args);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index dc9b6d2..9598a3f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -304,6 +304,7 @@
     InitProperty("AUTORCC_OPTIONS", nullptr);
     InitProperty("LINK_DEPENDS_NO_SHARED", nullptr);
     InitProperty("LINK_INTERFACE_LIBRARIES", nullptr);
+    InitProperty("MSVC_RUNTIME_LIBRARY", nullptr);
     InitProperty("WIN32_EXECUTABLE", nullptr);
     InitProperty("MACOSX_BUNDLE", nullptr);
     InitProperty("MACOSX_RPATH", nullptr);
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 0cec2fb..c60706d 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -663,6 +663,7 @@
     this->WriteCustomCommands(e0);
     this->WriteAllSources(e0);
     this->WriteDotNetReferences(e0);
+    this->WriteImports(e0);
     this->WriteEmbeddedResourceGroup(e0);
     this->WriteXamlFilesGroup(e0);
     this->WriteWinRTReferences(e0);
@@ -811,6 +812,24 @@
   this->WriteDotNetReferenceCustomTags(e2, ref);
 }
 
+void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0)
+{
+  const char* imports =
+    this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT");
+  if (imports) {
+    std::vector<std::string> argsSplit;
+    cmSystemTools::ExpandListArgument(std::string(imports), argsSplit, false);
+    for (auto& path : argsSplit) {
+      if (!cmsys::SystemTools::FileIsFullPath(path)) {
+        path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
+      }
+      ConvertToWindowsSlash(path);
+      Elem e1(e0, "Import");
+      e1.Attribute("Project", path);
+    }
+  }
+}
+
 void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags(
   Elem& e2, std::string const& ref)
 {
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 6a1ee1d..1dea8e9 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -76,6 +76,7 @@
   void WriteDotNetReference(Elem& e1, std::string const& ref,
                             std::string const& hint,
                             std::string const& config);
+  void WriteImports(Elem& e0);
   void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref);
   void WriteEmbeddedResourceGroup(Elem& e0);
   void WriteWinRTReferences(Elem& e0);
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index e8b2668..e1b0c70 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -374,19 +374,19 @@
 {
   // Look for Intel Fortran flags that do not map well in the flag table.
   if (this->CurrentTool == FortranCompiler) {
-    if (flag == "/dbglibs") {
+    if (flag == "/dbglibs" || flag == "-dbglibs") {
       this->FortranRuntimeDebug = true;
       return;
     }
-    if (flag == "/threads") {
+    if (flag == "/threads" || flag == "-threads") {
       this->FortranRuntimeMT = true;
       return;
     }
-    if (flag == "/libs:dll") {
+    if (flag == "/libs:dll" || flag == "-libs:dll") {
       this->FortranRuntimeDLL = true;
       return;
     }
-    if (flag == "/libs:static") {
+    if (flag == "/libs:static" || flag == "-libs:static") {
       this->FortranRuntimeDLL = false;
       return;
     }
diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx
new file mode 100644
index 0000000..464182c
--- /dev/null
+++ b/Source/cmWorkerPool.cxx
@@ -0,0 +1,770 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmWorkerPool.h"
+
+#include "cmRange.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
+#include "cm_uv.h"
+
+#include <algorithm>
+#include <array>
+#include <condition_variable>
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <stddef.h>
+#include <thread>
+
+/**
+ * @brief libuv pipe buffer class
+ */
+class cmUVPipeBuffer
+{
+public:
+  typedef cmRange<char const*> DataRange;
+  typedef std::function<void(DataRange)> DataFunction;
+  /// On error the ssize_t argument is a non zero libuv error code
+  typedef std::function<void(ssize_t)> EndFunction;
+
+public:
+  /**
+   * Reset to construction state
+   */
+  void reset();
+
+  /**
+   * Initializes uv_pipe(), uv_stream() and uv_handle()
+   * @return true on success
+   */
+  bool init(uv_loop_t* uv_loop);
+
+  /**
+   * Start reading
+   * @return true on success
+   */
+  bool startRead(DataFunction dataFunction, EndFunction endFunction);
+
+  //! libuv pipe
+  uv_pipe_t* uv_pipe() const { return UVPipe_.get(); }
+  //! uv_pipe() casted to libuv stream
+  uv_stream_t* uv_stream() const { return static_cast<uv_stream_t*>(UVPipe_); }
+  //! uv_pipe() casted to libuv handle
+  uv_handle_t* uv_handle() { return static_cast<uv_handle_t*>(UVPipe_); }
+
+private:
+  // -- Libuv callbacks
+  static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+                      uv_buf_t* buf);
+  static void UVData(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
+
+private:
+  cm::uv_pipe_ptr UVPipe_;
+  std::vector<char> Buffer_;
+  DataFunction DataFunction_;
+  EndFunction EndFunction_;
+};
+
+void cmUVPipeBuffer::reset()
+{
+  if (UVPipe_.get() != nullptr) {
+    EndFunction_ = nullptr;
+    DataFunction_ = nullptr;
+    Buffer_.clear();
+    Buffer_.shrink_to_fit();
+    UVPipe_.reset();
+  }
+}
+
+bool cmUVPipeBuffer::init(uv_loop_t* uv_loop)
+{
+  reset();
+  if (uv_loop == nullptr) {
+    return false;
+  }
+  int ret = UVPipe_.init(*uv_loop, 0, this);
+  return (ret == 0);
+}
+
+bool cmUVPipeBuffer::startRead(DataFunction dataFunction,
+                               EndFunction endFunction)
+{
+  if (UVPipe_.get() == nullptr) {
+    return false;
+  }
+  if (!dataFunction || !endFunction) {
+    return false;
+  }
+  DataFunction_ = std::move(dataFunction);
+  EndFunction_ = std::move(endFunction);
+  int ret = uv_read_start(uv_stream(), &cmUVPipeBuffer::UVAlloc,
+                          &cmUVPipeBuffer::UVData);
+  return (ret == 0);
+}
+
+void cmUVPipeBuffer::UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+                             uv_buf_t* buf)
+{
+  auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(handle->data);
+  pipe.Buffer_.resize(suggestedSize);
+  buf->base = pipe.Buffer_.data();
+  buf->len = static_cast<unsigned long>(pipe.Buffer_.size());
+}
+
+void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread,
+                            const uv_buf_t* buf)
+{
+  auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(stream->data);
+  if (nread > 0) {
+    if (buf->base != nullptr) {
+      // Call data function
+      pipe.DataFunction_(DataRange(buf->base, buf->base + nread));
+    }
+  } else if (nread < 0) {
+    // Save the end function on the stack before resetting the pipe
+    EndFunction efunc;
+    efunc.swap(pipe.EndFunction_);
+    // Reset pipe before calling the end function
+    pipe.reset();
+    // Call end function
+    efunc((nread == UV_EOF) ? 0 : nread);
+  }
+}
+
+/**
+ * @brief External process management class
+ */
+class cmUVReadOnlyProcess
+{
+public:
+  // -- Types
+  //! @brief Process settings
+  struct SetupT
+  {
+    std::string WorkingDirectory;
+    std::vector<std::string> Command;
+    cmWorkerPool::ProcessResultT* Result = nullptr;
+    bool MergedOutput = false;
+  };
+
+public:
+  // -- Const accessors
+  SetupT const& Setup() const { return Setup_; }
+  cmWorkerPool::ProcessResultT* Result() const { return Setup_.Result; }
+  bool IsStarted() const { return IsStarted_; }
+  bool IsFinished() const { return IsFinished_; }
+
+  // -- Runtime
+  void setup(cmWorkerPool::ProcessResultT* result, bool mergedOutput,
+             std::vector<std::string> const& command,
+             std::string const& workingDirectory = std::string());
+  bool start(uv_loop_t* uv_loop, std::function<void()> finishedCallback);
+
+private:
+  // -- Libuv callbacks
+  static void UVExit(uv_process_t* handle, int64_t exitStatus, int termSignal);
+  void UVPipeOutData(cmUVPipeBuffer::DataRange data);
+  void UVPipeOutEnd(ssize_t error);
+  void UVPipeErrData(cmUVPipeBuffer::DataRange data);
+  void UVPipeErrEnd(ssize_t error);
+  void UVTryFinish();
+
+private:
+  // -- Setup
+  SetupT Setup_;
+  // -- Runtime
+  bool IsStarted_ = false;
+  bool IsFinished_ = false;
+  std::function<void()> FinishedCallback_;
+  std::vector<const char*> CommandPtr_;
+  std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
+  uv_process_options_t UVOptions_;
+  cm::uv_process_ptr UVProcess_;
+  cmUVPipeBuffer UVPipeOut_;
+  cmUVPipeBuffer UVPipeErr_;
+};
+
+void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result,
+                                bool mergedOutput,
+                                std::vector<std::string> const& command,
+                                std::string const& workingDirectory)
+{
+  Setup_.WorkingDirectory = workingDirectory;
+  Setup_.Command = command;
+  Setup_.Result = result;
+  Setup_.MergedOutput = mergedOutput;
+}
+
+bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop,
+                                std::function<void()> finishedCallback)
+{
+  if (IsStarted() || (Result() == nullptr)) {
+    return false;
+  }
+
+  // Reset result before the start
+  Result()->reset();
+
+  // Fill command string pointers
+  if (!Setup().Command.empty()) {
+    CommandPtr_.reserve(Setup().Command.size() + 1);
+    for (std::string const& arg : Setup().Command) {
+      CommandPtr_.push_back(arg.c_str());
+    }
+    CommandPtr_.push_back(nullptr);
+  } else {
+    Result()->ErrorMessage = "Empty command";
+  }
+
+  if (!Result()->error()) {
+    if (!UVPipeOut_.init(uv_loop)) {
+      Result()->ErrorMessage = "libuv stdout pipe initialization failed";
+    }
+  }
+  if (!Result()->error()) {
+    if (!UVPipeErr_.init(uv_loop)) {
+      Result()->ErrorMessage = "libuv stderr pipe initialization failed";
+    }
+  }
+  if (!Result()->error()) {
+    // -- Setup process stdio options
+    // stdin
+    UVOptionsStdIO_[0].flags = UV_IGNORE;
+    UVOptionsStdIO_[0].data.stream = nullptr;
+    // stdout
+    UVOptionsStdIO_[1].flags =
+      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+    UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
+    // stderr
+    UVOptionsStdIO_[2].flags =
+      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+    UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
+
+    // -- Setup process options
+    std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
+    UVOptions_.exit_cb = &cmUVReadOnlyProcess::UVExit;
+    UVOptions_.file = CommandPtr_[0];
+    UVOptions_.args = const_cast<char**>(CommandPtr_.data());
+    UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
+    UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
+    UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
+    UVOptions_.stdio = UVOptionsStdIO_.data();
+
+    // -- Spawn process
+    int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
+    if (uvErrorCode != 0) {
+      Result()->ErrorMessage = "libuv process spawn failed";
+      if (const char* uvErr = uv_strerror(uvErrorCode)) {
+        Result()->ErrorMessage += ": ";
+        Result()->ErrorMessage += uvErr;
+      }
+    }
+  }
+  // -- Start reading from stdio streams
+  if (!Result()->error()) {
+    if (!UVPipeOut_.startRead(
+          [this](cmUVPipeBuffer::DataRange range) {
+            this->UVPipeOutData(range);
+          },
+          [this](ssize_t error) { this->UVPipeOutEnd(error); })) {
+      Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
+    }
+  }
+  if (!Result()->error()) {
+    if (!UVPipeErr_.startRead(
+          [this](cmUVPipeBuffer::DataRange range) {
+            this->UVPipeErrData(range);
+          },
+          [this](ssize_t error) { this->UVPipeErrEnd(error); })) {
+      Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
+    }
+  }
+
+  if (!Result()->error()) {
+    IsStarted_ = true;
+    FinishedCallback_ = std::move(finishedCallback);
+  } else {
+    // Clear libuv handles and finish
+    UVProcess_.reset();
+    UVPipeOut_.reset();
+    UVPipeErr_.reset();
+    CommandPtr_.clear();
+  }
+
+  return IsStarted();
+}
+
+void cmUVReadOnlyProcess::UVExit(uv_process_t* handle, int64_t exitStatus,
+                                 int termSignal)
+{
+  auto& proc = *reinterpret_cast<cmUVReadOnlyProcess*>(handle->data);
+  if (proc.IsStarted() && !proc.IsFinished()) {
+    // Set error message on demand
+    proc.Result()->ExitStatus = exitStatus;
+    proc.Result()->TermSignal = termSignal;
+    if (!proc.Result()->error()) {
+      if (termSignal != 0) {
+        proc.Result()->ErrorMessage = "Process was terminated by signal ";
+        proc.Result()->ErrorMessage +=
+          std::to_string(proc.Result()->TermSignal);
+      } else if (exitStatus != 0) {
+        proc.Result()->ErrorMessage = "Process failed with return value ";
+        proc.Result()->ErrorMessage +=
+          std::to_string(proc.Result()->ExitStatus);
+      }
+    }
+
+    // Reset process handle
+    proc.UVProcess_.reset();
+    // Try finish
+    proc.UVTryFinish();
+  }
+}
+
+void cmUVReadOnlyProcess::UVPipeOutData(cmUVPipeBuffer::DataRange data)
+{
+  Result()->StdOut.append(data.begin(), data.end());
+}
+
+void cmUVReadOnlyProcess::UVPipeOutEnd(ssize_t error)
+{
+  // Process pipe error
+  if ((error != 0) && !Result()->error()) {
+    Result()->ErrorMessage =
+      "Reading from stdout pipe failed with libuv error code ";
+    Result()->ErrorMessage += std::to_string(error);
+  }
+  // Try finish
+  UVTryFinish();
+}
+
+void cmUVReadOnlyProcess::UVPipeErrData(cmUVPipeBuffer::DataRange data)
+{
+  std::string* str =
+    Setup_.MergedOutput ? &Result()->StdOut : &Result()->StdErr;
+  str->append(data.begin(), data.end());
+}
+
+void cmUVReadOnlyProcess::UVPipeErrEnd(ssize_t error)
+{
+  // Process pipe error
+  if ((error != 0) && !Result()->error()) {
+    Result()->ErrorMessage =
+      "Reading from stderr pipe failed with libuv error code ";
+    Result()->ErrorMessage += std::to_string(error);
+  }
+  // Try finish
+  UVTryFinish();
+}
+
+void cmUVReadOnlyProcess::UVTryFinish()
+{
+  // There still might be data in the pipes after the process has finished.
+  // Therefore check if the process is finished AND all pipes are closed
+  // before signaling the worker thread to continue.
+  if ((UVProcess_.get() != nullptr) || (UVPipeOut_.uv_pipe() != nullptr) ||
+      (UVPipeErr_.uv_pipe() != nullptr)) {
+    return;
+  }
+  IsFinished_ = true;
+  FinishedCallback_();
+}
+
+/**
+ * @brief Private worker pool internals
+ */
+class cmWorkerPoolInternal
+{
+public:
+  // -- Types
+
+  /**
+   * @brief Worker thread
+   */
+  class WorkerT
+  {
+  public:
+    WorkerT(unsigned int index);
+    ~WorkerT();
+
+    WorkerT(WorkerT const&) = delete;
+    WorkerT& operator=(WorkerT const&) = delete;
+
+    /**
+     * Start the thread
+     */
+    void Start(cmWorkerPoolInternal* internal);
+
+    /**
+     * @brief Run an external process
+     */
+    bool RunProcess(cmWorkerPool::ProcessResultT& result,
+                    std::vector<std::string> const& command,
+                    std::string const& workingDirectory);
+
+    // -- Accessors
+    unsigned int Index() const { return Index_; }
+    cmWorkerPool::JobHandleT& JobHandle() { return JobHandle_; }
+
+  private:
+    // -- Libuv callbacks
+    static void UVProcessStart(uv_async_t* handle);
+    void UVProcessFinished();
+
+  private:
+    //! @brief Job handle
+    cmWorkerPool::JobHandleT JobHandle_;
+    //! @brief Worker index
+    unsigned int Index_;
+    // -- Process management
+    struct
+    {
+      std::mutex Mutex;
+      cm::uv_async_ptr Request;
+      std::condition_variable Condition;
+      std::unique_ptr<cmUVReadOnlyProcess> ROP;
+    } Proc_;
+    // -- System thread
+    std::thread Thread_;
+  };
+
+public:
+  // -- Constructors
+  cmWorkerPoolInternal(cmWorkerPool* pool);
+  ~cmWorkerPoolInternal();
+
+  /**
+   * @brief Runs the libuv loop
+   */
+  bool Process();
+
+  /**
+   * @brief Clear queue and abort threads
+   */
+  void Abort();
+
+  /**
+   * @brief Push a job to the queue and notify a worker
+   */
+  bool PushJob(cmWorkerPool::JobHandleT&& jobHandle);
+
+  /**
+   * @brief Worker thread main loop method
+   */
+  void Work(WorkerT* worker);
+
+  // -- Request slots
+  static void UVSlotBegin(uv_async_t* handle);
+  static void UVSlotEnd(uv_async_t* handle);
+
+public:
+  // -- UV loop
+#ifdef CMAKE_UV_SIGNAL_HACK
+  std::unique_ptr<cmUVSignalHackRAII> UVHackRAII;
+#endif
+  std::unique_ptr<uv_loop_t> UVLoop;
+  cm::uv_async_ptr UVRequestBegin;
+  cm::uv_async_ptr UVRequestEnd;
+
+  // -- Thread pool and job queue
+  std::mutex Mutex;
+  bool Aborting = false;
+  bool FenceProcessing = false;
+  unsigned int WorkersRunning = 0;
+  unsigned int WorkersIdle = 0;
+  unsigned int JobsProcessing = 0;
+  std::deque<cmWorkerPool::JobHandleT> Queue;
+  std::condition_variable Condition;
+  std::vector<std::unique_ptr<WorkerT>> Workers;
+
+  // -- References
+  cmWorkerPool* Pool = nullptr;
+};
+
+cmWorkerPoolInternal::WorkerT::WorkerT(unsigned int index)
+  : Index_(index)
+{
+}
+
+cmWorkerPoolInternal::WorkerT::~WorkerT()
+{
+  if (Thread_.joinable()) {
+    Thread_.join();
+  }
+}
+
+void cmWorkerPoolInternal::WorkerT::Start(cmWorkerPoolInternal* internal)
+{
+  Proc_.Request.init(*(internal->UVLoop), &WorkerT::UVProcessStart, this);
+  Thread_ = std::thread(&cmWorkerPoolInternal::Work, internal, this);
+}
+
+bool cmWorkerPoolInternal::WorkerT::RunProcess(
+  cmWorkerPool::ProcessResultT& result,
+  std::vector<std::string> const& command, std::string const& workingDirectory)
+{
+  if (command.empty()) {
+    return false;
+  }
+  // Create process instance
+  {
+    std::lock_guard<std::mutex> lock(Proc_.Mutex);
+    Proc_.ROP = cm::make_unique<cmUVReadOnlyProcess>();
+    Proc_.ROP->setup(&result, true, command, workingDirectory);
+  }
+  // Send asynchronous process start request to libuv loop
+  Proc_.Request.send();
+  // Wait until the process has been finished and destroyed
+  {
+    std::unique_lock<std::mutex> ulock(Proc_.Mutex);
+    while (Proc_.ROP) {
+      Proc_.Condition.wait(ulock);
+    }
+  }
+  return !result.error();
+}
+
+void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle)
+{
+  auto* wrk = reinterpret_cast<WorkerT*>(handle->data);
+  bool startFailed = false;
+  {
+    auto& Proc = wrk->Proc_;
+    std::lock_guard<std::mutex> lock(Proc.Mutex);
+    if (Proc.ROP && !Proc.ROP->IsStarted()) {
+      startFailed =
+        !Proc.ROP->start(handle->loop, [wrk] { wrk->UVProcessFinished(); });
+    }
+  }
+  // Clean up if starting of the process failed
+  if (startFailed) {
+    wrk->UVProcessFinished();
+  }
+}
+
+void cmWorkerPoolInternal::WorkerT::UVProcessFinished()
+{
+  {
+    std::lock_guard<std::mutex> lock(Proc_.Mutex);
+    if (Proc_.ROP && (Proc_.ROP->IsFinished() || !Proc_.ROP->IsStarted())) {
+      Proc_.ROP.reset();
+    }
+  }
+  // Notify idling thread
+  Proc_.Condition.notify_one();
+}
+
+void cmWorkerPool::ProcessResultT::reset()
+{
+  ExitStatus = 0;
+  TermSignal = 0;
+  if (!StdOut.empty()) {
+    StdOut.clear();
+    StdOut.shrink_to_fit();
+  }
+  if (!StdErr.empty()) {
+    StdErr.clear();
+    StdErr.shrink_to_fit();
+  }
+  if (!ErrorMessage.empty()) {
+    ErrorMessage.clear();
+    ErrorMessage.shrink_to_fit();
+  }
+}
+
+cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool)
+  : Pool(pool)
+{
+  // Initialize libuv loop
+  uv_disable_stdio_inheritance();
+#ifdef CMAKE_UV_SIGNAL_HACK
+  UVHackRAII = cm::make_unique<cmUVSignalHackRAII>();
+#endif
+  UVLoop = cm::make_unique<uv_loop_t>();
+  uv_loop_init(UVLoop.get());
+}
+
+cmWorkerPoolInternal::~cmWorkerPoolInternal()
+{
+  uv_loop_close(UVLoop.get());
+}
+
+bool cmWorkerPoolInternal::Process()
+{
+  // Reset state
+  Aborting = false;
+  // Initialize libuv asynchronous request
+  UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this);
+  UVRequestEnd.init(*UVLoop, &cmWorkerPoolInternal::UVSlotEnd, this);
+  // Send begin request
+  UVRequestBegin.send();
+  // Run libuv loop
+  return (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0);
+}
+
+void cmWorkerPoolInternal::Abort()
+{
+  bool firstCall = false;
+  // Clear all jobs and set abort flag
+  {
+    std::lock_guard<std::mutex> guard(Mutex);
+    if (!Aborting) {
+      // Register abort and clear queue
+      Aborting = true;
+      Queue.clear();
+      firstCall = true;
+    }
+  }
+  if (firstCall) {
+    // Wake threads
+    Condition.notify_all();
+  }
+}
+
+inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle)
+{
+  std::lock_guard<std::mutex> guard(Mutex);
+  if (Aborting) {
+    return false;
+  }
+
+  // Append the job to the queue
+  Queue.emplace_back(std::move(jobHandle));
+
+  // Notify an idle worker if there's one
+  if (WorkersIdle != 0) {
+    Condition.notify_one();
+  }
+
+  return true;
+}
+
+void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle)
+{
+  auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
+  // Create worker threads
+  {
+    unsigned int const num = gint.Pool->ThreadCount();
+    // Create workers
+    gint.Workers.reserve(num);
+    for (unsigned int ii = 0; ii != num; ++ii) {
+      gint.Workers.emplace_back(cm::make_unique<WorkerT>(ii));
+    }
+    // Start workers
+    for (auto& wrk : gint.Workers) {
+      wrk->Start(&gint);
+    }
+  }
+  // Destroy begin request
+  gint.UVRequestBegin.reset();
+}
+
+void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle)
+{
+  auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
+  // Join and destroy worker threads
+  gint.Workers.clear();
+  // Destroy end request
+  gint.UVRequestEnd.reset();
+}
+
+void cmWorkerPoolInternal::Work(WorkerT* worker)
+{
+  std::unique_lock<std::mutex> uLock(Mutex);
+  // Increment running workers count
+  ++WorkersRunning;
+  // Enter worker main loop
+  while (true) {
+    // Abort on request
+    if (Aborting) {
+      break;
+    }
+    // Wait for new jobs
+    if (Queue.empty()) {
+      ++WorkersIdle;
+      Condition.wait(uLock);
+      --WorkersIdle;
+      continue;
+    }
+
+    // Check for fence jobs
+    if (FenceProcessing || Queue.front()->IsFence()) {
+      if (JobsProcessing != 0) {
+        Condition.wait(uLock);
+        continue;
+      }
+      // No jobs get processed. Set the fence job processing flag.
+      FenceProcessing = true;
+    }
+
+    // Pop next job from queue
+    worker->JobHandle() = std::move(Queue.front());
+    Queue.pop_front();
+
+    // Unlocked scope for job processing
+    ++JobsProcessing;
+    {
+      uLock.unlock();
+      worker->JobHandle()->Work(Pool, worker->Index()); // Process job
+      worker->JobHandle().reset();                      // Destroy job
+      uLock.lock();
+    }
+    --JobsProcessing;
+
+    // Was this a fence job?
+    if (FenceProcessing) {
+      FenceProcessing = false;
+      Condition.notify_all();
+    }
+  }
+
+  // Decrement running workers count
+  if (--WorkersRunning == 0) {
+    // Last worker thread about to finish. Send libuv event.
+    UVRequestEnd.send();
+  }
+}
+
+cmWorkerPool::JobT::~JobT() = default;
+
+bool cmWorkerPool::JobT::RunProcess(ProcessResultT& result,
+                                    std::vector<std::string> const& command,
+                                    std::string const& workingDirectory)
+{
+  // Get worker by index
+  auto* wrk = Pool_->Int_->Workers.at(WorkerIndex_).get();
+  return wrk->RunProcess(result, command, workingDirectory);
+}
+
+cmWorkerPool::cmWorkerPool()
+  : Int_(cm::make_unique<cmWorkerPoolInternal>(this))
+{
+}
+
+cmWorkerPool::~cmWorkerPool() = default;
+
+bool cmWorkerPool::Process(unsigned int threadCount, void* userData)
+{
+  // Setup user data
+  UserData_ = userData;
+  ThreadCount_ = (threadCount > 0) ? threadCount : 1u;
+
+  // Run libuv loop
+  bool success = Int_->Process();
+
+  // Clear user data
+  UserData_ = nullptr;
+  ThreadCount_ = 0;
+
+  return success;
+}
+
+bool cmWorkerPool::PushJob(JobHandleT&& jobHandle)
+{
+  return Int_->PushJob(std::move(jobHandle));
+}
+
+void cmWorkerPool::Abort()
+{
+  Int_->Abort();
+}
diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h
new file mode 100644
index 0000000..71c7d84
--- /dev/null
+++ b/Source/cmWorkerPool.h
@@ -0,0 +1,219 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmWorkerPool_h
+#define cmWorkerPool_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmAlgorithms.h" // IWYU pragma: keep
+
+#include <memory> // IWYU pragma: keep
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+// -- Types
+class cmWorkerPoolInternal;
+
+/** @class cmWorkerPool
+ * @brief Thread pool with job queue
+ */
+class cmWorkerPool
+{
+public:
+  /**
+   * Return value and output of an external process.
+   */
+  struct ProcessResultT
+  {
+    void reset();
+    bool error() const
+    {
+      return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
+    }
+
+    std::int64_t ExitStatus = 0;
+    int TermSignal = 0;
+    std::string StdOut;
+    std::string StdErr;
+    std::string ErrorMessage;
+  };
+
+  /**
+   * Abstract job class for concurrent job processing.
+   */
+  class JobT
+  {
+  public:
+    JobT(JobT const&) = delete;
+    JobT& operator=(JobT const&) = delete;
+
+    /**
+     * @brief Virtual destructor.
+     */
+    virtual ~JobT();
+
+    /**
+     * @brief Fence job flag
+     *
+     * Fence jobs require that:
+     * - all jobs before in the queue have been processed
+     * - no jobs later in the queue will be processed before this job was
+     *   processed
+     */
+    bool IsFence() const { return Fence_; }
+
+  protected:
+    /**
+     * @brief Protected default constructor
+     */
+    JobT(bool fence = false)
+      : Fence_(fence)
+    {
+    }
+
+    /**
+     * Abstract processing interface that must be implement in derived classes.
+     */
+    virtual void Process() = 0;
+
+    /**
+     * Get the worker pool.
+     * Only valid during the JobT::Process() call!
+     */
+    cmWorkerPool* Pool() const { return Pool_; }
+
+    /**
+     * Get the user data.
+     * Only valid during the JobT::Process() call!
+     */
+    void* UserData() const { return Pool_->UserData(); };
+
+    /**
+     * Get the worker index.
+     * This is the index of the thread processing this job and is in the range
+     * [0..ThreadCount).
+     * Concurrently processing jobs will never have the same WorkerIndex().
+     * Only valid during the JobT::Process() call!
+     */
+    unsigned int WorkerIndex() const { return WorkerIndex_; }
+
+    /**
+     * Run an external read only process.
+     * Use only during JobT::Process() call!
+     */
+    bool RunProcess(ProcessResultT& result,
+                    std::vector<std::string> const& command,
+                    std::string const& workingDirectory);
+
+  private:
+    //! Needs access to Work()
+    friend class cmWorkerPoolInternal;
+    //! Worker thread entry method.
+    void Work(cmWorkerPool* pool, unsigned int workerIndex)
+    {
+      Pool_ = pool;
+      WorkerIndex_ = workerIndex;
+      this->Process();
+    }
+
+  private:
+    cmWorkerPool* Pool_ = nullptr;
+    unsigned int WorkerIndex_ = 0;
+    bool Fence_ = false;
+  };
+
+  /**
+   * @brief Job handle type
+   */
+  typedef std::unique_ptr<JobT> JobHandleT;
+
+  /**
+   * @brief Fence job base class
+   */
+  class JobFenceT : public JobT
+  {
+  public:
+    JobFenceT()
+      : JobT(true)
+    {
+    }
+    //! Does nothing
+    void Process() override{};
+  };
+
+  /**
+   * @brief Fence job that aborts the worker pool.
+   * This class is useful as the last job in the job queue.
+   */
+  class JobEndT : JobFenceT
+  {
+  public:
+    //! Does nothing
+    void Process() override { Pool()->Abort(); }
+  };
+
+public:
+  // -- Methods
+  cmWorkerPool();
+  ~cmWorkerPool();
+
+  /**
+   * @brief Blocking function that starts threads to process all Jobs in
+   *        the queue.
+   *
+   * This method blocks until a job calls the Abort() method.
+   * @arg threadCount Number of threads to process jobs.
+   * @arg userData Common user data pointer available in all Jobs.
+   */
+  bool Process(unsigned int threadCount, void* userData = nullptr);
+
+  /**
+   * Number of worker threads passed to Process().
+   * Only valid during Process().
+   */
+  unsigned int ThreadCount() const { return ThreadCount_; }
+
+  /**
+   * User data reference passed to Process().
+   * Only valid during Process().
+   */
+  void* UserData() const { return UserData_; }
+
+  // -- Job processing interface
+
+  /**
+   * @brief Clears the job queue and aborts all worker threads.
+   *
+   * This method is thread safe and can be called from inside a job.
+   */
+  void Abort();
+
+  /**
+   * @brief Push job to the queue.
+   *
+   * This method is thread safe and can be called from inside a job or before
+   * Process().
+   */
+  bool PushJob(JobHandleT&& jobHandle);
+
+  /**
+   * @brief Push job to the queue
+   *
+   * This method is thread safe and can be called from inside a job or before
+   * Process().
+   */
+  template <class T, typename... Args>
+  bool EmplaceJob(Args&&... args)
+  {
+    return PushJob(cm::make_unique<T>(std::forward<Args>(args)...));
+  }
+
+private:
+  void* UserData_ = nullptr;
+  unsigned int ThreadCount_ = 0;
+  std::unique_ptr<cmWorkerPoolInternal> Int_;
+};
+
+#endif
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index c18c256..3c75957 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -7,7 +7,7 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmQtAutoGeneratorMocUic.h"
+#include "cmQtAutoMocUic.h"
 #include "cmQtAutoRcc.h"
 #include "cmRange.h"
 #include "cmState.h"
@@ -1018,7 +1018,7 @@
 
 #ifdef CMAKE_BUILD_WITH_CMAKE
     if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
-      cmQtAutoGeneratorMocUic autoGen;
+      cmQtAutoMocUic autoGen;
       std::string const& infoDir = args[2];
       std::string const& config = args[3];
       return autoGen.Run(infoDir, config) ? 0 : 1;
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index db4ef90..1302c64 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -465,6 +465,14 @@
 ENDIF()
 
 IF(KWSYS_USE_SystemTools)
+  if (NOT DEFINED KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP)
+    set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1)
+  endif ()
+  if (KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP)
+    set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1)
+  else ()
+    set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 0)
+  endif ()
   KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV
     "Checking whether CXX compiler has setenv" DIRECT)
   KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UNSETENV
diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in
index d1e7464..92ffea3 100644
--- a/Source/kwsys/Configure.hxx.in
+++ b/Source/kwsys/Configure.hxx.in
@@ -11,6 +11,9 @@
 /* Whether <ext/stdio_filebuf.h> is available. */
 #define @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H                         \
   @KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@
+/* Whether the translation map is available or not. */
+#define @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP                     \
+  @KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@
 
 #if defined(__SUNPRO_CC) && __SUNPRO_CC > 0x5130 && defined(__has_attribute)
 #  define @KWSYS_NAMESPACE@__has_cpp_attribute(x) __has_attribute(x)
@@ -56,6 +59,8 @@
     @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H
 #  define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH
 #  define KWSYS_NULLPTR @KWSYS_NAMESPACE@_NULLPTR
+#  define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP                               \
+    @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP
 #endif
 
 #endif
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 87da80e..e756cdc 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -453,11 +453,13 @@
 {
 public:
   typedef std::map<std::string, std::string> StringMap;
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
   /**
    * Path translation table from dir to refdir
    * Each time 'dir' will be found it will be replace by 'refdir'
    */
   StringMap TranslationMap;
+#endif
 #ifdef _WIN32
   static std::string GetCasePathName(std::string const& pathIn);
   static std::string GetActualCaseForPathCached(std::string const& path);
@@ -623,7 +625,9 @@
   std::string env;
   if (SystemTools::GetEnv(key, env)) {
     std::string& menv = SystemTools::Statics->EnvMap[key];
-    menv = std::move(env);
+    if (menv != env) {
+      menv = std::move(env);
+    }
     return menv.c_str();
   }
   return KWSYS_NULLPTR;
@@ -2810,27 +2814,15 @@
     SystemTools::GetPath(path);
   }
   // now add the additional paths
-  {
-    for (std::vector<std::string>::const_iterator i = userPaths.begin();
-         i != userPaths.end(); ++i) {
-      path.push_back(*i);
-    }
-  }
-  // Add a trailing slash to all paths to aid the search process.
-  {
-    for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
-         ++i) {
-      std::string& p = *i;
-      if (p.empty() || p.back() != '/') {
-        p += "/";
-      }
-    }
-  }
+  path.reserve(path.size() + userPaths.size());
+  path.insert(path.end(), userPaths.begin(), userPaths.end());
   // now look for the file
   std::string tryPath;
-  for (std::vector<std::string>::const_iterator p = path.begin();
-       p != path.end(); ++p) {
-    tryPath = *p;
+  for (std::string const& p : path) {
+    tryPath = p;
+    if (tryPath.empty() || tryPath.back() != '/') {
+      tryPath += '/';
+    }
     tryPath += name;
     if (SystemTools::FileExists(tryPath)) {
       return tryPath;
@@ -2904,14 +2896,13 @@
   // the end of it
   // on windows try .com then .exe
   if (name.size() <= 3 || name[name.size() - 4] != '.') {
-    extensions.push_back(".com");
-    extensions.push_back(".exe");
+    extensions.emplace_back(".com");
+    extensions.emplace_back(".exe");
 
     // first try with extensions if the os supports them
-    for (std::vector<std::string>::iterator i = extensions.begin();
-         i != extensions.end(); ++i) {
+    for (std::string const& ext : extensions) {
       tryPath = name;
-      tryPath += *i;
+      tryPath += ext;
       if (SystemTools::FileExists(tryPath, true)) {
         return SystemTools::CollapseFullPath(tryPath);
       }
@@ -2930,43 +2921,33 @@
     SystemTools::GetPath(path);
   }
   // now add the additional paths
-  {
-    for (std::vector<std::string>::const_iterator i = userPaths.begin();
-         i != userPaths.end(); ++i) {
-      path.push_back(*i);
-    }
-  }
+  path.reserve(path.size() + userPaths.size());
+  path.insert(path.end(), userPaths.begin(), userPaths.end());
   // Add a trailing slash to all paths to aid the search process.
-  {
-    for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
-         ++i) {
-      std::string& p = *i;
-      if (p.empty() || p.back() != '/') {
-        p += "/";
-      }
+  for (std::string& p : path) {
+    if (p.empty() || p.back() != '/') {
+      p += '/';
     }
   }
   // Try each path
-  for (std::vector<std::string>::iterator p = path.begin(); p != path.end();
-       ++p) {
+  for (std::string& p : path) {
 #ifdef _WIN32
     // Remove double quotes from the path on windows
-    SystemTools::ReplaceString(*p, "\"", "");
+    SystemTools::ReplaceString(p, "\"", "");
 #endif
 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
     // first try with extensions
-    for (std::vector<std::string>::iterator ext = extensions.begin();
-         ext != extensions.end(); ++ext) {
-      tryPath = *p;
+    for (std::string const& ext : extensions) {
+      tryPath = p;
       tryPath += name;
-      tryPath += *ext;
+      tryPath += ext;
       if (SystemTools::FileExists(tryPath, true)) {
         return SystemTools::CollapseFullPath(tryPath);
       }
     }
 #endif
     // now try it without them
-    tryPath = *p;
+    tryPath = p;
     tryPath += name;
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
@@ -2980,10 +2961,9 @@
                                      const std::vector<std::string>& path,
                                      bool noSystemPath)
 {
-  for (std::vector<std::string>::const_iterator it = names.begin();
-       it != names.end(); ++it) {
+  for (std::string const& name : names) {
     // Try to find the program.
-    std::string result = SystemTools::FindProgram(*it, path, noSystemPath);
+    std::string result = SystemTools::FindProgram(name, path, noSystemPath);
     if (!result.empty()) {
       return result;
     }
@@ -3008,27 +2988,18 @@
   std::vector<std::string> path;
   SystemTools::GetPath(path);
   // now add the additional paths
-  {
-    for (std::vector<std::string>::const_iterator i = userPaths.begin();
-         i != userPaths.end(); ++i) {
-      path.push_back(*i);
-    }
-  }
+  path.reserve(path.size() + userPaths.size());
+  path.insert(path.end(), userPaths.begin(), userPaths.end());
   // Add a trailing slash to all paths to aid the search process.
-  {
-    for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
-         ++i) {
-      std::string& p = *i;
-      if (p.empty() || p.back() != '/') {
-        p += "/";
-      }
+  for (std::string& p : path) {
+    if (p.empty() || p.back() != '/') {
+      p += '/';
     }
   }
   std::string tryPath;
-  for (std::vector<std::string>::const_iterator p = path.begin();
-       p != path.end(); ++p) {
+  for (std::string const& p : path) {
 #if defined(__APPLE__)
-    tryPath = *p;
+    tryPath = p;
     tryPath += name;
     tryPath += ".framework";
     if (SystemTools::FileIsDirectory(tryPath)) {
@@ -3036,42 +3007,42 @@
     }
 #endif
 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
-    tryPath = *p;
+    tryPath = p;
     tryPath += name;
     tryPath += ".lib";
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
     }
 #else
-    tryPath = *p;
+    tryPath = p;
     tryPath += "lib";
     tryPath += name;
     tryPath += ".so";
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
     }
-    tryPath = *p;
+    tryPath = p;
     tryPath += "lib";
     tryPath += name;
     tryPath += ".a";
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
     }
-    tryPath = *p;
+    tryPath = p;
     tryPath += "lib";
     tryPath += name;
     tryPath += ".sl";
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
     }
-    tryPath = *p;
+    tryPath = p;
     tryPath += "lib";
     tryPath += name;
     tryPath += ".dylib";
     if (SystemTools::FileExists(tryPath, true)) {
       return SystemTools::CollapseFullPath(tryPath);
     }
-    tryPath = *p;
+    tryPath = p;
     tryPath += "lib";
     tryPath += name;
     tryPath += ".dll";
@@ -3333,9 +3304,8 @@
       msg << "  argv[0] = \"" << argv0 << "\"\n";
     }
     msg << "  Attempted paths:\n";
-    std::vector<std::string>::iterator i;
-    for (i = failures.begin(); i != failures.end(); ++i) {
-      msg << "    \"" << *i << "\"\n";
+    for (std::string const& ff : failures) {
+      msg << "    \"" << ff << "\"\n";
     }
     errorMsg = msg.str();
     return false;
@@ -3349,6 +3319,7 @@
   return SystemTools::CollapseFullPath(in_relative, KWSYS_NULLPTR);
 }
 
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
 void SystemTools::AddTranslationPath(const std::string& a,
                                      const std::string& b)
 {
@@ -3412,6 +3383,7 @@
   // Remove the trailing slash we added before.
   path.pop_back();
 }
+#endif
 
 static void SystemToolsAppendComponents(
   std::vector<std::string>& out_components,
@@ -3482,6 +3454,7 @@
   // Transform the path back to a string.
   std::string newPath = SystemTools::JoinPath(out_components);
 
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
   // Update the translation table with this potentially new path.  I am not
   // sure why this line is here, it seems really questionable, but yet I
   // would put good money that if I remove it something will break, basically
@@ -3497,6 +3470,7 @@
   // SystemTools::AddTranslationPath(newPath, in_path);
 
   SystemTools::CheckTranslationPath(newPath);
+#endif
 #ifdef _WIN32
   newPath = SystemTools::Statics->GetActualCaseForPathCached(newPath);
   SystemTools::ConvertToUnixSlashes(newPath);
@@ -3558,28 +3532,26 @@
   // for each entry that is not common in the local path
   // add a ../ to the finalpath array, this gets us out of the local
   // path into the remote dir
-  for (unsigned int i = 0; i < localSplit.size(); ++i) {
-    if (!localSplit[i].empty()) {
-      finalPath.push_back("../");
+  for (std::string const& lp : localSplit) {
+    if (!lp.empty()) {
+      finalPath.emplace_back("../");
     }
   }
   // for each entry that is not common in the remote path add it
   // to the final path.
-  for (std::vector<std::string>::iterator vit = remoteSplit.begin();
-       vit != remoteSplit.end(); ++vit) {
-    if (!vit->empty()) {
-      finalPath.push_back(*vit);
+  for (std::string const& rp : remoteSplit) {
+    if (!rp.empty()) {
+      finalPath.push_back(rp);
     }
   }
   std::string relativePath; // result string
   // now turn the array of directories into a unix path by puttint /
   // between each entry that does not already have one
-  for (std::vector<std::string>::iterator vit1 = finalPath.begin();
-       vit1 != finalPath.end(); ++vit1) {
+  for (std::string const& fp : finalPath) {
     if (!relativePath.empty() && relativePath.back() != '/') {
-      relativePath += "/";
+      relativePath += '/';
     }
-    relativePath += *vit1;
+    relativePath += fp;
   }
   return relativePath;
 }
@@ -3727,8 +3699,7 @@
   // Construct result in a single string.
   std::string result;
   size_t len = 0;
-  std::vector<std::string>::const_iterator i;
-  for (i = first; i != last; ++i) {
+  for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
     len += 1 + i->size();
   }
   result.reserve(len);
@@ -4737,10 +4708,11 @@
   // Create statics singleton instance
   SystemTools::Statics = new SystemToolsStatic;
 
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
 // Add some special translation paths for unix.  These are not added
 // for windows because drive letters need to be maintained.  Also,
 // there are not sym-links and mount points on windows anyway.
-#if !defined(_WIN32) || defined(__CYGWIN__)
+#  if !defined(_WIN32) || defined(__CYGWIN__)
   // The tmp path is frequently a logical path so always keep it:
   SystemTools::AddKeepPath("/tmp/");
 
@@ -4778,6 +4750,7 @@
       }
     }
   }
+#  endif
 #endif
 }
 
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in
index cdc9483..dd1266b 100644
--- a/Source/kwsys/SystemTools.hxx.in
+++ b/Source/kwsys/SystemTools.hxx.in
@@ -891,6 +891,7 @@
    */
   static int GetTerminalWidth();
 
+#if @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP
   /**
    * Add an entry in the path translation table.
    */
@@ -907,6 +908,7 @@
    * Update path by going through the Path Translation table;
    */
   static void CheckTranslationPath(std::string& path);
+#endif
 
   /**
    * Delay the execution for a specified amount of time specified
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 2e0902c..bc4812b 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1995,6 +1995,10 @@
     if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
       ADD_TEST_MACRO(PrecompiledHeader foo)
     endif()
+    ADD_TEST_MACRO(MSVCRuntimeLibrary)
+    if(CMAKE_Fortran_COMPILER)
+      ADD_TEST_MACRO(MSVCRuntimeLibrary.Fortran)
+    endif()
   endif()
   if(MSVC OR
       "${CMAKE_GENERATOR}" MATCHES "(MSYS|MinGW) Makefiles")
diff --git a/Tests/FindBoost/CMakeLists.txt b/Tests/FindBoost/CMakeLists.txt
index 17a8ec7..58d795b 100644
--- a/Tests/FindBoost/CMakeLists.txt
+++ b/Tests/FindBoost/CMakeLists.txt
@@ -33,3 +33,16 @@
   --build-options ${build_options}
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
+
+if (CMake_TEST_FindBoost_Python)
+  add_test(NAME FindBoost.TestPython COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindBoost/TestPython"
+    "${CMake_BINARY_DIR}/Tests/FindBoost/TestPython"
+    ${build_generator_args}
+    --build-project TestFindBoostPython
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+endif ()
diff --git a/Tests/FindBoost/TestPython/CMakeLists.txt b/Tests/FindBoost/TestPython/CMakeLists.txt
new file mode 100644
index 0000000..4d137ca
--- /dev/null
+++ b/Tests/FindBoost/TestPython/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.14)
+project(TestFindBoostPython CXX)
+include(CTest)
+
+find_package(Boost OPTIONAL_COMPONENTS python27 python34 python35 python36 python37)
+
+set(FAILTEST TRUE)
+foreach (v IN ITEMS 27 34 35 36 37)
+  if (Boost_PYTHON${v}_FOUND)
+    set(FAILTEST FALSE)
+    break()
+  endif ()
+endforeach ()
+
+if (FAILTEST)
+  message(FATAL_ERROR "No Boost Python module found")
+endif ()
diff --git a/Tests/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/CMakeLists.txt
new file mode 100644
index 0000000..b7a6e86
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/CMakeLists.txt
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 3.14)
+cmake_policy(SET CMP0091 NEW)
+project(MSVCRuntimeLibrary)
+
+function(verify_combinations threads lang src)
+  set(verify_tc_config_ Release)
+  set(verify_tc_config_Debug Debug)
+  set(verify_def_MultiThreaded -DVERIFY_MT)
+  set(verify_def_Debug -DVERIFY_DEBUG)
+  set(verify_def_DLL -DVERIFY_DLL)
+  foreach(dbg "" Debug)
+    foreach(dll "" DLL)
+      # Construct the name of this runtime library combination.
+      set(rtl "${threads}${dbg}${dll}")
+
+      # Test that try_compile builds with this RTL.
+      set(CMAKE_MSVC_RUNTIME_LIBRARY "${rtl}")
+      set(CMAKE_TRY_COMPILE_CONFIGURATION "${verify_tc_config_${dbg}}")
+      set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+      try_compile(${rtl}_COMPILES
+        ${CMAKE_CURRENT_BINARY_DIR}/try_compile/${rtl}
+        ${CMAKE_CURRENT_SOURCE_DIR}/${src}
+        COMPILE_DEFINITIONS ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}}
+        OUTPUT_VARIABLE ${rtl}_OUTPUT
+        )
+      if(${rtl}_COMPILES)
+        message(STATUS "try_compile with ${rtl} worked")
+      else()
+        string(REPLACE "\n" "\n  " ${rtl}_OUTPUT "  ${${rtl}_OUTPUT}")
+        message(SEND_ERROR "try_compile with ${rtl} failed:\n${${rtl}_OUTPUT}")
+      endif()
+
+      # Test that targets build with this RTL.
+      set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+      add_library(${rtl}-${lang} ${src})
+      set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
+      target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
+    endforeach()
+  endforeach()
+endfunction()
+
+function(verify lang src)
+  add_library(default-${lang} ${src})
+  target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
+  verify_combinations(MultiThreaded ${lang} ${src})
+endfunction()
+
+verify(C verify.c)
+verify(CXX verify.cxx)
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
new file mode 100644
index 0000000..169ba07
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.14)
+cmake_policy(SET CMP0091 NEW)
+project(MSVCRuntimeLibraryFortran Fortran)
+
+foreach(t MultiThreaded SingleThreaded)
+  foreach(dbg "" Debug)
+    foreach(dll "" DLL)
+      set(var "CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_${t}${dbg}${dll}")
+      # ifort does not actually define these, so inject them
+      string(REPLACE "-threads" "-threads;-D_MT" "${var}" "${${var}}")
+      string(REPLACE "-dbglibs" "-dbglibs;-D_DEBUG" "${var}" "${${var}}")
+    endforeach()
+  endforeach()
+endforeach()
+string(APPEND CMAKE_Fortran_FLAGS " -w")
+
+function(verify_combinations threads lang src)
+  set(verify_tc_config_ Release)
+  set(verify_tc_config_Debug Debug)
+  set(verify_def_MultiThreaded -DVERIFY_MT)
+  set(verify_def_Debug -DVERIFY_DEBUG)
+  set(verify_def_DLL -DVERIFY_DLL)
+  foreach(dbg "" Debug)
+    foreach(dll "" DLL)
+      # Construct the name of this runtime library combination.
+      set(rtl "${threads}${dbg}${dll}")
+
+      # Test that targets build with this RTL.
+      set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+      add_library(${rtl}-${lang} ${src})
+      set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
+      target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
+    endforeach()
+  endforeach()
+endfunction()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+function(verify lang src)
+  add_library(default-${lang} ${src})
+  target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
+  verify_combinations(MultiThreaded ${lang} ${src})
+endfunction()
+
+verify(Fortran verify.F90)
+# Intel Fortran for Windows supports single-threaded RTL but it is
+# not implemented by the Visual Studio integration.
+if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+  verify_combinations(SingleThreaded Fortran verify.F90)
+endif()
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/verify.F90 b/Tests/MSVCRuntimeLibrary/Fortran/verify.F90
new file mode 100644
index 0000000..6fe5e05
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/Fortran/verify.F90
@@ -0,0 +1 @@
+#include "../verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.c b/Tests/MSVCRuntimeLibrary/verify.c
new file mode 100644
index 0000000..741bca6
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.c
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.cxx b/Tests/MSVCRuntimeLibrary/verify.cxx
new file mode 100644
index 0000000..741bca6
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.cxx
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.h b/Tests/MSVCRuntimeLibrary/verify.h
new file mode 100644
index 0000000..58d65fe
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.h
@@ -0,0 +1,29 @@
+#ifdef VERIFY_DEBUG
+#  ifndef _DEBUG
+#    error "_DEBUG not defined by debug runtime library selection"
+#  endif
+#else
+#  ifdef _DEBUG
+#    error "_DEBUG defined by non-debug runtime library selection"
+#  endif
+#endif
+
+#ifdef VERIFY_DLL
+#  ifndef _DLL
+#    error "_DLL not defined by DLL runtime library selection"
+#  endif
+#else
+#  ifdef _DLL
+#    error "_DLL defined by non-DLL runtime library selection"
+#  endif
+#endif
+
+#ifdef VERIFY_MT
+#  ifndef _MT
+#    error "_MT not defined by multi-threaded runtime library selection"
+#  endif
+#else
+#  ifdef _MT
+#    error "_MT defined by single-threaded runtime library selection"
+#  endif
+#endif
diff --git a/Tests/QtAutogen/ManySources/CMakeLists.txt b/Tests/QtAutogen/ManySources/CMakeLists.txt
new file mode 100644
index 0000000..df8a2a6
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.10)
+project(ManySources)
+include("../AutogenGuiTest.cmake")
+
+# Test AUTOMOC and AUTOUIC on many source files to stress the concurrent
+# parsing and processing framework.
+
+set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
+set(CBD ${CMAKE_CURRENT_BINARY_DIR})
+
+set(SRCS main.cpp)
+set(MAIN_INCLUDES "\n// Item includes\n")
+set(MAIN_ITEMS "\n// Items\n")
+
+set(NUM 24)
+foreach(III RANGE 1 ${NUM})
+  configure_file(${CSD}/object.h.in ${CBD}/object_${III}.h)
+  configure_file(${CSD}/item.h.in ${CBD}/item_${III}.h)
+  configure_file(${CSD}/item.cpp.in ${CBD}/item_${III}.cpp)
+  configure_file(${CSD}/view.ui.in ${CBD}/view_${III}.ui)
+  configure_file(${CSD}/data.qrc.in ${CBD}/data_${III}.qrc)
+
+  list(APPEND SRCS ${CBD}/item_${III}.cpp)
+  list(APPEND SRCS ${CBD}/data_${III}.qrc)
+
+  string(APPEND MAIN_INCLUDES "#include \"item_${III}.h\"\n")
+  string(APPEND MAIN_ITEMS "Item_${III} item_${III};\n")
+  string(APPEND MAIN_ITEMS "item_${III}.TheSlot();\n")
+endforeach()
+
+configure_file(${CSD}/main.cpp.in ${CBD}/main.cpp)
+
+add_executable(manySources ${SRCS} ${CBD}/main.cpp)
+target_link_libraries(manySources ${QT_LIBRARIES})
+set_target_properties(manySources PROPERTIES AUTOMOC 1 AUTOUIC 1 AUTORCC 1)
diff --git a/Tests/QtAutogen/ManySources/data.qrc.in b/Tests/QtAutogen/ManySources/data.qrc.in
new file mode 100644
index 0000000..870d486
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/data.qrc.in
@@ -0,0 +1,7 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+  <file>object_@III@.h</file>
+  <file>item_@III@.h</file>
+  <file>item_@III@.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/ManySources/item.cpp.in b/Tests/QtAutogen/ManySources/item.cpp.in
new file mode 100644
index 0000000..c34ad16
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/item.cpp.in
@@ -0,0 +1,27 @@
+#include "item_@III@.h"
+#include "object_@III@.h"
+// AUTOUIC include
+#include <ui_view_@III@.h>
+
+class LocalObject_@III@ : public QObject
+{
+  Q_OBJECT;
+
+public:
+  LocalObject_@III@() = default;
+  ~LocalObject_@III@() = default;
+};
+
+void Item_@III@ ::TheSlot()
+{
+  LocalObject_@III@ localObject;
+  Object_@III@ obj;
+  obj.ObjectSlot();
+
+  Ui_View_@III@ ui_view;
+}
+
+// AUTOMOC includes
+#include "item_@III@.moc"
+#include "moc_item_@III@.cpp"
+#include "moc_object_@III@.cpp"
diff --git a/Tests/QtAutogen/ManySources/item.h.in b/Tests/QtAutogen/ManySources/item.h.in
new file mode 100644
index 0000000..67ad794
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/item.h.in
@@ -0,0 +1,15 @@
+#ifndef ITEM_@III@HPP
+#define ITEM_@III@HPP
+
+#include <QObject>
+
+class Item_@III@ : public QObject
+{
+  Q_OBJECT
+
+public:
+  Q_SLOT
+  void TheSlot();
+};
+
+#endif
diff --git a/Tests/QtAutogen/ManySources/main.cpp.in b/Tests/QtAutogen/ManySources/main.cpp.in
new file mode 100644
index 0000000..e1dda40
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/main.cpp.in
@@ -0,0 +1,7 @@
+@MAIN_INCLUDES@
+
+int main(int argv, char** args)
+{
+  @MAIN_ITEMS@
+  return 0;
+}
diff --git a/Tests/QtAutogen/ManySources/object.h.in b/Tests/QtAutogen/ManySources/object.h.in
new file mode 100644
index 0000000..a747cbc
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/object.h.in
@@ -0,0 +1,15 @@
+#ifndef OBJECT_@III@H
+#define OBJECT_@III@H
+
+#include <QObject>
+
+class Object_@III@ : public QObject
+{
+  Q_OBJECT
+
+public:
+  Q_SLOT
+  void ObjectSlot(){};
+};
+
+#endif
diff --git a/Tests/QtAutogen/ManySources/view.ui.in b/Tests/QtAutogen/ManySources/view.ui.in
new file mode 100644
index 0000000..6901fe3
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/view.ui.in
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>View_@III@</class>
+ <widget class="QWidget" name="Base">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake
index 096d5e3..6771828 100644
--- a/Tests/QtAutogen/Tests.cmake
+++ b/Tests/QtAutogen/Tests.cmake
@@ -5,6 +5,7 @@
 ADD_AUTOGEN_TEST(Complex QtAutogen)
 ADD_AUTOGEN_TEST(GlobalAutogenTarget)
 ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
+ADD_AUTOGEN_TEST(ManySources manySources)
 ADD_AUTOGEN_TEST(MocOnly mocOnly)
 ADD_AUTOGEN_TEST(MocOptions mocOptions)
 ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 68411d6..a8dcb4b 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -195,6 +195,9 @@
 if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|XL|XLClang)$")
   add_RunCMake_test(MetaCompileFeatures)
 endif()
+if(MSVC)
+  add_RunCMake_test(MSVCRuntimeLibrary)
+endif()
 add_RunCMake_test(ObjectLibrary)
 add_RunCMake_test(ParseImplicitIncludeInfo)
 if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
rename to Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..3b2a814
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake:2 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+  TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake
new file mode 100644
index 0000000..489d8e6
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -0,0 +1,2 @@
+add_library(empty UNKNOWN IMPORTED)
+add_custom_target(custom COMMAND echo $<TARGET_PDB_FILE_BASE_NAME:empty>)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 783bfb3..0000000
--- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CMake Error at ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake:2 \(add_custom_target\):
-  Error evaluating generator expression:
-
-    \$<TARGET_PDB_OUTPUT_NAME:empty>
-
-  TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake
deleted file mode 100644
index 010b38e..0000000
--- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-add_library(empty UNKNOWN IMPORTED)
-add_custom_target(custom COMMAND echo $<TARGET_PDB_OUTPUT_NAME:empty>)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..b061ce3
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+  TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 70%
rename from Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
rename to Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
index 07951de..811c3f7 100644
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -5,5 +5,5 @@
 
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
+  CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]"
 )
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 00ec496..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CMake Error at NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\):
-  Error evaluating generator expression:
-
-    \$<TARGET_PDB_OUTPUT_NAME:empty>
-
-  TARGET_PDB_OUTPUT_NAME is not supported by the target linker.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..c7d245c
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+  TARGET_PDB_FILE_BASE_NAME is allowed only for targets with linker created
+  artifacts.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 70%
copy from Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
index 07951de..811c3f7 100644
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -5,5 +5,5 @@
 
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
+  CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]"
 )
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 8ac349e..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CMake Error at NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\):
-  Error evaluating generator expression:
-
-    \$<TARGET_PDB_OUTPUT_NAME:empty>
-
-  TARGET_PDB_OUTPUT_NAME is allowed only for targets with linker created
-  artifacts.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
deleted file mode 100644
index 07951de..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-
-enable_language(C)
-
-add_library(empty STATIC empty.c)
-
-file(GENERATE
-  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
-)
diff --git a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
index 775f68a..006b0da 100644
--- a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
+++ b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
@@ -3,4 +3,4 @@
 set_property(TARGET empty1 PROPERTY OUTPUT_NAME $<TARGET_FILE_NAME:empty1>)
 
 add_executable(empty2 empty.c)
-set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_OUTPUT_NAME:empty2>)
+set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_FILE_BASE_NAME:empty2>)
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
index 0b0fb78..477b593 100644
--- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -41,10 +41,10 @@
 run_cmake(TARGET_FILE_SUFFIX-imported-target)
 run_cmake(TARGET_FILE_SUFFIX-non-valid-target)
 run_cmake(TARGET_LINKER_FILE_SUFFIX-non-valid-target)
-run_cmake(TARGET_OUTPUT_NAME)
-run_cmake(TARGET_OUTPUT_NAME-imported-target)
-run_cmake(TARGET_OUTPUT_NAME-non-valid-target)
-run_cmake(TARGET_LINKER_OUTPUT_NAME-non-valid-target)
+run_cmake(TARGET_FILE_BASE_NAME)
+run_cmake(TARGET_FILE_BASE_NAME-imported-target)
+run_cmake(TARGET_FILE_BASE_NAME-non-valid-target)
+run_cmake(TARGET_LINKER_FILE_BASE_NAME-non-valid-target)
 run_cmake(TARGET_PROPERTY-LOCATION)
 run_cmake(TARGET_PROPERTY-SOURCES)
 run_cmake(LINK_ONLY-not-linking)
@@ -78,15 +78,15 @@
 run_cmake(ImportedTarget-TARGET_BUNDLE_DIR)
 run_cmake(ImportedTarget-TARGET_BUNDLE_CONTENT_DIR)
 run_cmake(ImportedTarget-TARGET_PDB_FILE)
-run_cmake(ImportedTarget-TARGET_PDB_OUTPUT_NAME)
+run_cmake(ImportedTarget-TARGET_PDB_FILE_BASE_NAME)
 if(LINKER_SUPPORTS_PDB)
   run_cmake(NonValidTarget-TARGET_PDB_FILE)
   run_cmake(ValidTarget-TARGET_PDB_FILE)
-  run_cmake(NonValidTarget-TARGET_PDB_OUTPUT_NAME)
-  run_cmake(ValidTarget-TARGET_PDB_OUTPUT_NAME)
+  run_cmake(NonValidTarget-TARGET_PDB_FILE_BASE_NAME)
+  run_cmake(ValidTarget-TARGET_PDB_FILE_BASE_NAME)
 else()
   run_cmake(NonValidCompiler-TARGET_PDB_FILE)
-  run_cmake(NonValidCompiler-TARGET_PDB_OUTPUT_NAME)
+  run_cmake(NonValidCompiler-TARGET_PDB_FILE_BASE_NAME)
 endif()
 
 set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=OLD)
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake
new file mode 100644
index 0000000..793edb1
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake
@@ -0,0 +1,2 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake
new file mode 100644
index 0000000..793edb1
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake
@@ -0,0 +1,2 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake
new file mode 100644
index 0000000..aa54b31
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake
@@ -0,0 +1,79 @@
+
+cmake_minimum_required(VERSION 3.14)
+
+enable_language (C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+  if (NOT "${value}" STREQUAL "${expected}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+  endif()
+endmacro()
+]])
+
+add_executable(exec1 IMPORTED)
+add_library (shared1 SHARED IMPORTED)
+add_library (static1 STATIC IMPORTED)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1")
+]])
+
+
+add_executable (exec2 IMPORTED)
+set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
+add_library (shared2 SHARED IMPORTED)
+set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
+add_library (static2 STATIC IMPORTED)
+set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom")
+]])
+
+
+add_executable (exec3 IMPORTED)
+set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
+set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
+set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
+set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
+add_library (shared3 SHARED IMPORTED)
+set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
+set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
+set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
+set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
+add_library (static3 STATIC IMPORTED)
+set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
+set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
+set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
+set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime")
+check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
+check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive")
+]])
+
+
+unset(GENERATE_CONDITION)
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+  list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
+  set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
+endif()
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt
rename to Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt
new file mode 100644
index 0000000..ecb9e5d
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at TARGET_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_FILE_BASE_NAME:empty>
+
+  Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
similarity index 67%
rename from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake
rename to Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
index 5248dfa..8622b7d 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
@@ -3,5 +3,5 @@
 
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "[$<TARGET_OUTPUT_NAME:empty>]"
+  CONTENT "[$<TARGET_FILE_BASE_NAME:empty>]"
 )
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake
new file mode 100644
index 0000000..5ea53a0
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake
@@ -0,0 +1,96 @@
+
+cmake_minimum_required(VERSION 3.14)
+
+enable_language (C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+  if (NOT "${value}" STREQUAL "${expected}")
+    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+  endif()
+endmacro()
+]])
+
+add_executable (exec1 empty.c)
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+  string(APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB default" "$<TARGET_PDB_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB default" "$<TARGET_PDB_FILE_BASE_NAME:shared1>" "shared1")
+]])
+endif()
+
+
+add_executable (exec2 empty.c)
+set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
+add_library (shared2 SHARED empty.c)
+set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
+add_library (static2 STATIC empty.c)
+set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+  string (APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:shared2>" "shared2_custom")
+  ]])
+endif()
+
+add_executable (exec3 empty.c)
+set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
+set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
+set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
+set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
+add_library (shared3 SHARED empty.c)
+set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
+set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
+set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
+set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
+add_library (static3 STATIC empty.c)
+set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
+set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
+set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
+set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime")
+check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
+check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+  string (APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:exec3>" "exec3_pdb")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:shared3>" "shared3_pdb")
+]])
+endif()
+
+
+unset(GENERATE_CONDITION)
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+  list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
+  set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
+endif()
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake"
+  CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt
rename to Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt
new file mode 100644
index 0000000..1ae2f2c
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\):
+  Error evaluating generator expression:
+
+    \$<TARGET_LINKER_FILE_BASE_NAME:empty>
+
+  Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
similarity index 63%
rename from Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake
rename to Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
index c439535..776fb4b 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
@@ -3,5 +3,5 @@
 
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "[$<TARGET_LINKER_OUTPUT_NAME:empty>]"
+  CONTENT "[$<TARGET_LINKER_FILE_BASE_NAME:empty>]"
 )
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt
deleted file mode 100644
index 0e09469..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\):
-  Error evaluating generator expression:
-
-    \$<TARGET_LINKER_OUTPUT_NAME:empty>
-
-  Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake
deleted file mode 100644
index fa4f2b9..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-
-include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake
deleted file mode 100644
index fa4f2b9..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-
-include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake
deleted file mode 100644
index 548a2d7..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake
+++ /dev/null
@@ -1,79 +0,0 @@
-
-cmake_minimum_required(VERSION 3.14)
-
-enable_language (C)
-
-set (GENERATE_CONTENT [[
-macro (CHECK_VALUE test_msg value expected)
-  if (NOT "${value}" STREQUAL "${expected}")
-    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
-  endif()
-endmacro()
-]])
-
-add_executable(exec1 IMPORTED)
-add_library (shared1 SHARED IMPORTED)
-add_library (static1 STATIC IMPORTED)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1")
-]])
-
-
-add_executable (exec2 IMPORTED)
-set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
-add_library (shared2 SHARED IMPORTED)
-set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
-add_library (static2 STATIC IMPORTED)
-set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom")
-]])
-
-
-add_executable (exec3 IMPORTED)
-set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
-set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
-set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
-set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
-add_library (shared3 SHARED IMPORTED)
-set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
-set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
-set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
-set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
-add_library (static3 STATIC IMPORTED)
-set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
-set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
-set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
-set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime")
-check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
-check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive")
-]])
-
-
-unset(GENERATE_CONDITION)
-get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(_isMultiConfig)
-  list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
-  set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
-endif()
-
-file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake"
-  CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt
deleted file mode 100644
index 9672a99..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at TARGET_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\):
-  Error evaluating generator expression:
-
-    \$<TARGET_OUTPUT_NAME:empty>
-
-  Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake
deleted file mode 100644
index b7bae15..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,96 +0,0 @@
-
-cmake_minimum_required(VERSION 3.14)
-
-enable_language (C)
-
-set (GENERATE_CONTENT [[
-macro (CHECK_VALUE test_msg value expected)
-  if (NOT "${value}" STREQUAL "${expected}")
-    string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
-  endif()
-endmacro()
-]])
-
-add_executable (exec1 empty.c)
-add_library (shared1 SHARED empty.c)
-add_library (static1 STATIC empty.c)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
-  string(APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB default" "$<TARGET_PDB_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB default" "$<TARGET_PDB_OUTPUT_NAME:shared1>" "shared1")
-]])
-endif()
-
-
-add_executable (exec2 empty.c)
-set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
-add_library (shared2 SHARED empty.c)
-set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
-add_library (static2 STATIC empty.c)
-set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
-  string (APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB custom" "$<TARGET_PDB_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB custom" "$<TARGET_PDB_OUTPUT_NAME:shared2>" "shared2_custom")
-  ]])
-endif()
-
-add_executable (exec3 empty.c)
-set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
-set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
-set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
-set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
-add_library (shared3 SHARED empty.c)
-set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
-set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
-set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
-set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
-add_library (static3 STATIC empty.c)
-set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
-set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
-set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
-set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime")
-check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
-check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
-  string (APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:exec3>" "exec3_pdb")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:shared3>" "shared3_pdb")
-]])
-endif()
-
-
-unset(GENERATE_CONDITION)
-get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(_isMultiConfig)
-  list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
-  set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
-endif()
-
-file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake"
-  CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake
new file mode 100644
index 0000000..996d2d4
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake
@@ -0,0 +1,7 @@
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8)
+
+list(GET TEST_TXT 0 PDB_FILE_BASE_NAME)
+
+if(NOT PDB_FILE_BASE_NAME MATCHES "empty")
+  set(RunCMake_TEST_FAILED "unexpected PDB_FILE_BASE_NAME [${PDB_FILE_BASE_NAME}]")
+endif()
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 88%
rename from Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
rename to Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
index ba70b43..cc53bdf 100644
--- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -11,6 +11,6 @@
 
 file(GENERATE
   OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
-  CONTENT "$<TARGET_PDB_OUTPUT_NAME:empty>"
+  CONTENT "$<TARGET_PDB_FILE_BASE_NAME:empty>"
   ${GENERATE_CONDITION}
 )
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake
deleted file mode 100644
index 8d1103e..0000000
--- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8)
-
-list(GET TEST_TXT 0 PDB_OUTPUT_NAME)
-
-if(NOT PDB_OUTPUT_NAME MATCHES "empty")
-  set(RunCMake_TEST_FAILED "unexpected PDB_OUTPUT_NAME [${PDB_OUTPUT_NAME}]")
-endif()
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-result.txt
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt
new file mode 100644
index 0000000..803058d
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error in CMakeLists.txt:
+  MSVC_RUNTIME_LIBRARY value 'BogusValue' not known for this C compiler.$
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake
new file mode 100644
index 0000000..c3ea2fd
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0091 NEW)
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake
new file mode 100644
index 0000000..734cc9f
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0091 OLD)
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake
new file mode 100644
index 0000000..26f86a0
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake
@@ -0,0 +1,2 @@
+
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake
new file mode 100644
index 0000000..7827d2a
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake
@@ -0,0 +1,37 @@
+enable_language(C)
+
+cmake_policy(GET CMP0091 cmp0091)
+if(cmp0091 STREQUAL "NEW")
+  if(NOT CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT not set under NEW behavior")
+  endif()
+else()
+  if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT is set under OLD behavior")
+  endif()
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+  if(CMAKE_C_FLAGS_DEBUG MATCHES "[/-]MDd( |$)")
+    set(have_MDd 1)
+  else()
+    set(have_MDd 0)
+  endif()
+  if(CMAKE_C_FLAGS_RELEASE MATCHES "[/-]MD( |$)")
+    set(have_MD 1)
+  else()
+    set(have_MD 0)
+  endif()
+  if(cmp0091 STREQUAL "NEW")
+    if(have_MDd OR have_MD)
+      message(SEND_ERROR "Have a -MD* flag under NEW behavior.")
+    endif()
+  else()
+    if(NOT (have_MDd AND have_MD))
+      message(SEND_ERROR "Do not have -MD* flags under OLD behavior.")
+    endif()
+  endif()
+endif()
+
+set(CMAKE_MSVC_RUNTIME_LIBRARY BogusValue)
+add_library(foo empty.c)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt
new file mode 100644
index 0000000..3e470a2
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake
new file mode 100644
index 0000000..fad18da
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0091-WARN)
+run_cmake(CMP0091-OLD)
+run_cmake(CMP0091-NEW)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/empty.c b/Tests/RunCMake/MSVCRuntimeLibrary/empty.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/empty.c
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index df253a9..0ac589d 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -2,6 +2,7 @@
 
 run_cmake(VsCSharpCompilerOpts)
 run_cmake(ExplicitCMakeLists)
+run_cmake(RuntimeLibrary)
 run_cmake(SourceGroupCMakeLists)
 
 run_cmake(VsConfigurationType)
@@ -18,3 +19,4 @@
 run_cmake(VSCSharpDefines)
 run_cmake(VsSdkDirectories)
 run_cmake(VsGlobals)
+run_cmake(VsProjectImport)
diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake
new file mode 100644
index 0000000..6b43d47
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake
@@ -0,0 +1,34 @@
+macro(RuntimeLibrary_check tgt rtl_expect)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
+    return()
+  endif()
+
+  set(HAVE_Runtimelibrary 0)
+
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<RuntimeLibrary>([^<>]+)</RuntimeLibrary>")
+      set(rtl_actual "${CMAKE_MATCH_1}")
+      if(NOT "${rtl_actual}" STREQUAL "${rtl_expect}")
+        set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has RuntimeLibrary '${rtl_actual}', not '${rtl_expect}'.")
+        return()
+      endif()
+      set(HAVE_Runtimelibrary 1)
+      break()
+    endif()
+  endforeach()
+
+  if(NOT HAVE_Runtimelibrary)
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a RuntimeLibrary field.")
+    return()
+  endif()
+endmacro()
+
+RuntimeLibrary_check(default-C MultiThreadedDebugDLL)
+RuntimeLibrary_check(default-CXX MultiThreadedDebugDLL)
+RuntimeLibrary_check(MTd-C MultiThreadedDebug)
+RuntimeLibrary_check(MTd-CXX MultiThreadedDebug)
+RuntimeLibrary_check(MT-C MultiThreaded)
+RuntimeLibrary_check(MT-CXX MultiThreaded)
diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake
new file mode 100644
index 0000000..6c77a25
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake
@@ -0,0 +1,16 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+cmake_policy(SET CMP0091 NEW)
+enable_language(C)
+enable_language(CXX)
+
+add_library(default-C empty.c)
+add_library(default-CXX empty.cxx)
+
+set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
+add_library(MTd-C empty.c)
+add_library(MTd-CXX empty.cxx)
+
+add_library(MT-C empty.c)
+set_property(TARGET MT-C PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
+add_library(MT-CXX empty.cxx)
+set_property(TARGET MT-CXX PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
diff --git a/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake
new file mode 100644
index 0000000..e438bf4
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake
@@ -0,0 +1,28 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(test1Import "path\\\\to\\\\nuget_packages\\\\Foo.1.0.0\\\\build\\\\Foo.props")
+set(test2Import "path\\\\to\\\\nuget_packages\\\\Bar.1.0.0\\\\build\\\\Bar.props")
+
+set(import1Found FALSE)
+set(import2Found FALSE)
+
+file(STRINGS "${vcProjectFile}" lines)
+
+foreach(i 1 2)
+  set(testImport "${test${i}Import}")
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<Import Project=\".*${test1Import}\" />$")
+      message(STATUS "foo.vcxproj is using project import ${testImport}")
+      set(import${i}Found TRUE)
+    endif()
+  endforeach()
+endforeach()
+
+if(NOT import1Found OR NOT import2Found)
+  set(RunCMake_TEST_FAILED "Imported project not found.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsProjectImport.cmake b/Tests/RunCMake/VS10Project/VsProjectImport.cmake
new file mode 100644
index 0000000..70bdded
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsProjectImport.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+add_library(foo foo.cpp)
+
+set(test1Import "path/to/nuget_packages/Foo.1.0.0/build/Foo.props")
+set(test2Import "path/to/nuget_packages/Bar.1.0.0/build/Bar.props")
+
+set_property(TARGET foo PROPERTY
+  VS_PROJECT_IMPORT
+    ${test1Import}
+    ${test2Import}
+  )
diff --git a/Tests/RunCMake/VS10Project/empty.c b/Tests/RunCMake/VS10Project/empty.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/empty.c
diff --git a/Tests/RunCMake/VS10Project/empty.cxx b/Tests/RunCMake/VS10Project/empty.cxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/empty.cxx
diff --git a/Tests/RunCMake/string/Repeat.cmake b/Tests/RunCMake/string/Repeat.cmake
new file mode 100644
index 0000000..fc390aa
--- /dev/null
+++ b/Tests/RunCMake/string/Repeat.cmake
@@ -0,0 +1,45 @@
+string(REPEAT "q" 4 q_out)
+
+if(NOT DEFINED q_out)
+  message(FATAL_ERROR "q_out is not defined")
+endif()
+
+if(NOT q_out STREQUAL "qqqq")
+  message(FATAL_ERROR "unexpected result")
+endif()
+
+string(REPEAT "1234" 0 zero_out)
+
+if(NOT DEFINED zero_out)
+  message(FATAL_ERROR "zero_out is not defined")
+endif()
+
+if(NOT zero_out STREQUAL "")
+  message(FATAL_ERROR "unexpected result")
+endif()
+
+unset(zero_out)
+
+string(REPEAT "" 100 zero_out)
+
+if(NOT DEFINED zero_out)
+  message(FATAL_ERROR "zero_out is not defined")
+endif()
+
+if(NOT zero_out STREQUAL "")
+  message(FATAL_ERROR "unexpected result")
+endif()
+
+string(REPEAT "1" 1 one_out)
+
+if(NOT one_out STREQUAL "1")
+  message(FATAL_ERROR "unexpected result")
+endif()
+
+unset(one_out)
+
+string(REPEAT "one" 1 one_out)
+
+if(NOT one_out STREQUAL "one")
+  message(FATAL_ERROR "unexpected result")
+endif()
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/string/RepeatNegativeCount-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt
copy to Tests/RunCMake/string/RepeatNegativeCount-result.txt
diff --git a/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt
new file mode 100644
index 0000000..bbd498e
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at RepeatNegativeCount.cmake:[0-9]+ \(string\):
+  repeat count is not a positive number.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/string/RepeatNegativeCount.cmake b/Tests/RunCMake/string/RepeatNegativeCount.cmake
new file mode 100644
index 0000000..769e7c0
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNegativeCount.cmake
@@ -0,0 +1 @@
+string(REPEAT "blah" -1 out)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/string/RepeatNoArgs-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/string/RepeatNoArgs-result.txt
diff --git a/Tests/RunCMake/string/RepeatNoArgs-stderr.txt b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt
new file mode 100644
index 0000000..5abcb3b
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at RepeatNoArgs.cmake:[0-9]+ \(string\):
+  sub-command REPEAT requires three arguments.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/string/RepeatNoArgs.cmake b/Tests/RunCMake/string/RepeatNoArgs.cmake
new file mode 100644
index 0000000..e327e99
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNoArgs.cmake
@@ -0,0 +1 @@
+string(REPEAT)
diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake
index 211337a..c432b4e 100644
--- a/Tests/RunCMake/string/RunCMakeTest.cmake
+++ b/Tests/RunCMake/string/RunCMakeTest.cmake
@@ -33,3 +33,7 @@
 run_cmake(UTF-16LE)
 run_cmake(UTF-32BE)
 run_cmake(UTF-32LE)
+
+run_cmake(Repeat)
+run_cmake(RepeatNoArgs)
+run_cmake(RepeatNegativeCount)
diff --git a/bootstrap b/bootstrap
index d46b8a8..8b9c404 100755
--- a/bootstrap
+++ b/bootstrap
@@ -685,6 +685,7 @@
               s/@KWSYS_NAME_IS_KWSYS@/${KWSYS_NAME_IS_KWSYS}/g;
               s/@KWSYS_STL_HAS_WSTRING@/${KWSYS_STL_HAS_WSTRING}/g;
               s/@KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@/${KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H}/g;
+              s/@KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@/${KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP}/g;
              }" "${INFILE}" >> "${OUTFILE}${_tmp}"
     if [ -f "${OUTFILE}${_tmp}" ]; then
       if "${_diff}" "${OUTFILE}" "${OUTFILE}${_tmp}" > /dev/null 2> /dev/null ; then
@@ -1217,6 +1218,7 @@
 KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H=0
 KWSYS_CXX_HAS_UTIMENSAT=0
 KWSYS_CXX_HAS_UTIMES=0
+KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP=1
 
 if cmake_try_run "${cmake_cxx_compiler}" \
   "${cmake_cxx_flags} -DTEST_KWSYS_CXX_HAS_SETENV" \