Merge topic 'memorize_msvc_like_compilers_C_support'

8e4899fd6c CompileFeatures: Record which C features the MSVC compiler supports

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3226
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/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-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 4d4b9ff..bd19ccf 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -335,6 +335,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/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/ghs-custom-commands.rst b/Help/release/dev/ghs-custom-commands.rst
new file mode 100644
index 0000000..a29ef88
--- /dev/null
+++ b/Help/release/dev/ghs-custom-commands.rst
@@ -0,0 +1,5 @@
+ghs_custom_commands
+-------------------
+
+* The :generator:`Green Hills MULTI` generator now supports
+  :command:`add_custom_command` and :command:`add_custom_target`
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/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake
index b8c8c5d..eabb8b5 100644
--- a/Modules/CMakeDetermineASMCompiler.cmake
+++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -119,35 +119,40 @@
   CMAKE_DETERMINE_COMPILER_ID_VENDOR(ASM${ASM_DIALECT} "${userflags}")
   if("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xIAR")
     # primary necessary to detect architecture, so the right archiver and linker can be picked
-    # eg. IAR Assembler V8.10.1.12857/W32 for ARM
+    # eg. "IAR Assembler V8.10.1.12857/W32 for ARM" or "IAR Assembler V4.11.1.4666 for Renesas RX"
     # Cut out identification first, newline handling is a pain
     string(REGEX MATCH "IAR Assembler[^\r\n]*" _compileid "${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT}")
     if("${_compileid}" MATCHES "V([0-9]+\\.[0-9]+\\.[0-9]+)")
       set(CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION ${CMAKE_MATCH_1})
     endif()
-    if("${_compileid}" MATCHES "for[ ]+([A-Za-z0-9]+)")
-      set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID ${CMAKE_MATCH_1})
+    string(REGEX MATCHALL "([A-Za-z0-9]+)" _all_compileid_matches "${_compileid}")
+    if(_all_compileid_matches)
+      list(GET _all_compileid_matches "-1" CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID)
     endif()
   endif()
   unset(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT)
+  unset(_all_compileid_matches)
   unset(_compileid)
 endif()
 
-
 if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID)
   if(CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION)
     set(_version " ${CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION}")
   else()
     set(_version "")
   endif()
-  message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_version}")
+  if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID)
+    set(_archid " ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}")
+  else()
+    set(_archid "")
+  endif()
+  message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_archid}${_version}")
+  unset(_archid)
   unset(_version)
 else()
   message(STATUS "The ASM${ASM_DIALECT} compiler identification is unknown")
 endif()
 
-
-
 # If we have a gas/as cross compiler, they have usually some prefix, like
 # e.g. powerpc-linux-gas, arm-elf-gas or i586-mingw32msvc-gas , optionally
 # with a 3-component version number at the end
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 594f85b..c1c9982 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -110,8 +110,15 @@
     else()
       set(_version "")
     endif()
+    if(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID)
+      set(_archid " ${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}")
+    else()
+      set(_archid "")
+    endif()
     message(STATUS "The ${lang} compiler identification is "
-      "${CMAKE_${lang}_COMPILER_ID}${_version}")
+      "${CMAKE_${lang}_COMPILER_ID}${_archid}${_version}")
+    unset(_archid)
+    unset(_version)
   else()
     message(STATUS "The ${lang} compiler identification is unknown")
   endif()
diff --git a/Modules/CMakePlatformId.h.in b/Modules/CMakePlatformId.h.in
index 3cb7f24..c88094a 100644
--- a/Modules/CMakePlatformId.h.in
+++ b/Modules/CMakePlatformId.h.in
@@ -156,6 +156,9 @@
 # if defined(__ICCARM__)
 #  define ARCHITECTURE_ID "ARM"
 
+# elif defined(__ICCRX__)
+#  define ARCHITECTURE_ID "RX"
+
 # elif defined(__ICCAVR__)
 #  define ARCHITECTURE_ID "AVR"
 
diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake
index e12bfd1..f9c0ced 100644
--- a/Modules/Compiler/IAR-ASM.cmake
+++ b/Modules/Compiler/IAR-ASM.cmake
@@ -3,21 +3,20 @@
 include(Compiler/IAR)
 
 if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ARM(ASM)
+  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+  __compiler_iar_ilink(ASM)
   set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
 
-  string(APPEND CMAKE_ASM_FLAGS_INIT " ")
-  string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
-  string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
-  string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
-  string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
+  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+  __compiler_iar_ilink(ASM)
+  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
 
 elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
   set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_AVR(ASM)
+  __compiler_iar_xlink(ASM)
   set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;asm;msa)
 
 else()
-  message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\".  This should be automatic.")
+  message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
 endif()
diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake
index b5e61f0..cb10020 100644
--- a/Modules/Compiler/IAR-C.cmake
+++ b/Modules/Compiler/IAR-C.cmake
@@ -3,44 +3,39 @@
 include(Compiler/IAR)
 include(Compiler/CMakeCommonCompilerMacros)
 
-# The toolchains for ARM and AVR are quite different:
-if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  if(NOT CMAKE_C_COMPILER_VERSION)
-    message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected.  This should be automatic.")
-  endif()
+# Common
+if(NOT CMAKE_C_COMPILER_VERSION)
+  message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected.  This should be automatic.")
+endif()
 
-  set(CMAKE_C_EXTENSION_COMPILE_OPTION -e)
+set(CMAKE_C_EXTENSION_COMPILE_OPTION -e)
 
-  set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e)
-
-  if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.10)
-    set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89)
-    set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e)
-    set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
-    set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e)
-  endif()
-  if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.10)
-    set(CMAKE_C11_STANDARD_COMPILE_OPTION "")
-    set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e)
-  endif()
-
-  __compiler_iar_ARM(C)
-  __compiler_check_default_language_standard(C 1.10 90 6.10 99 8.10 11)
-
-elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
-  if(NOT CMAKE_C_COMPILER_VERSION)
-    message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected.  This should be automatic.")
-  endif()
-
-  set(CMAKE_C_EXTENSION_COMPILE_OPTION -e)
-
+if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89)
-  set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e)
+  set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e)
   set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
   set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e)
+elseif()
+  set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
+  set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e)
+endif()
 
-  __compiler_iar_AVR(C)
+if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8)
+  set(CMAKE_C11_STANDARD_COMPILE_OPTION "")
+  set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e)
+endif()
+
+# Architecture specific
+if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
+  __compiler_iar_ilink(C)
+  __compiler_check_default_language_standard(C 1.10 90 6.10 99 8.10 11)
+
+elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
+  __compiler_iar_ilink(C)
+  __compiler_check_default_language_standard(C 1.10 90 2.10 99 4.10 11)
+
+elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
+  __compiler_iar_xlink(C)
   __compiler_check_default_language_standard(C 7.10 99)
   set(CMAKE_C_OUTPUT_EXTENSION ".r90")
 
@@ -48,9 +43,6 @@
     set(CMAKE_C_LINK_FLAGS "-Fmotorola")
   endif()
 
-  set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <OBJECTS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
-  set(CMAKE_C_CREATE_STATIC_LIBRARY "<CMAKE_AR> -o <TARGET> <OBJECTS> ")
-
   # add the target specific include directory:
   get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH)
   get_filename_component(_compilerDir "${_compilerDir}" PATH)
@@ -58,5 +50,5 @@
   include_directories("${_compilerDir}/inc/Atmel" )
 
 else()
-  message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\".  This should be automatic.")
+  message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
 endif()
diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake
index b7076f5..eb27e3c 100644
--- a/Modules/Compiler/IAR-CXX.cmake
+++ b/Modules/Compiler/IAR-CXX.cmake
@@ -3,60 +3,45 @@
 include(Compiler/IAR)
 include(Compiler/CMakeCommonCompilerMacros)
 
-if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  # "(extended) embedded C++" Mode
-  # old version: --ec++ or --eec++
-  # since 8.10:  --c++ --no_exceptions --no_rtti
-  #
-  # --c++ is full C++ and supported since 6.10
-  if(NOT CMAKE_IAR_CXX_FLAG)
-    if(NOT CMAKE_CXX_COMPILER_VERSION)
-      message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected.  This should be automatic.")
-    endif()
-    if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.10)
-      set(CMAKE_IAR_CXX_FLAG --c++)
-    else()
-      set(CMAKE_IAR_CXX_FLAG --eec++)
-    endif()
+# Common
+if(NOT CMAKE_IAR_CXX_FLAG)
+  if(NOT CMAKE_CXX_COMPILER_VERSION)
+    message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.")
   endif()
+  if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8)
+    set(CMAKE_IAR_CXX_FLAG --c++)
+  else()
+    set(CMAKE_IAR_CXX_FLAG --eec++)
+  endif()
+endif()
 
-  set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e)
+set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e)
 
+if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7)
   set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "")
   set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e)
-
-  if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.10)
   set(CMAKE_CXX03_STANDARD_COMPILE_OPTION "")
   set(CMAKE_CXX03_EXTENSION_COMPILE_OPTION -e)
+endif()
+
+if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8)
   set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "")
   set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -e)
   set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "")
   set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -e)
-  endif()
+endif()
 
-  __compiler_iar_ARM(CXX)
+# Architecture specific
+if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
+  __compiler_iar_ilink(CXX)
   __compiler_check_default_language_standard(CXX 6.10 98 8.10 14)
 
+elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
+  __compiler_iar_ilink(CXX)
+  __compiler_check_default_language_standard(CXX 2.10 98 4.10 14)
+
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
-  # "embedded C++" --EC++ is probably closest to CXX98 but with no support for:
-  #  Templates, multiple inheritance, virtual inheritance, exceptions, RTTI, C++ style casts,
-  #  Namespaces, the mutable attribute, no STL, any library features related to the above features.
-  #
-  # "(extended) embedded C++" --EEC++ Mode but DOES NOT support any normal C++ standard
-  # probably closest to CXX98 but with no RTTI and no exceptions, and the library
-  # provided is not in the standard namespace
-  if(NOT CMAKE_IAR_CXX_FLAG)
-    if(NOT CMAKE_CXX_COMPILER_VERSION)
-      message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected.  This should be automatic.")
-    endif()
-    set(CMAKE_IAR_CXX_FLAG --eec++)
-  endif()
-
-  set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e)
-  set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e)
-
-  __compiler_iar_AVR(CXX)
+  __compiler_iar_xlink(CXX)
   __compiler_check_default_language_standard(CXX 7.10 98)
 
   set(CMAKE_CXX_OUTPUT_EXTENSION ".r90")
@@ -64,15 +49,12 @@
     set(CMAKE_CXX_LINK_FLAGS "-Fmotorola")
   endif()
 
-  set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <OBJECTS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
-  set(CMAKE_CXX_CREATE_STATIC_LIBRARY "<CMAKE_AR> -o <TARGET> <OBJECTS> ")
-
   # add the target specific include directory:
   get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH)
   get_filename_component(_compilerDir "${_compilerDir}" PATH)
-  include_directories("${_compilerDir}/inc")
-  include_directories("${_compilerDir}/inc/Atmel")
+  include_directories("${_compilerDir}/inc" )
+  include_directories("${_compilerDir}/inc/Atmel" )
 
 else()
-  message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\".  This should be automatic." )
+  message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected. This should be automatic." )
 endif()
diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake
index 43477ac..cdfb095 100644
--- a/Modules/Compiler/IAR-DetermineCompiler.cmake
+++ b/Modules/Compiler/IAR-DetermineCompiler.cmake
@@ -31,7 +31,7 @@
 #  define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(((__VER__) / 1000) % 1000)
 #  define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@((__VER__) % 1000)
 #  define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__)
-# elif defined(__VER__) && defined(__ICCAVR__)
+# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__))
 #  define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 100)
 #  define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@((__VER__) - (((__VER__) / 100)*100))
 #  define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__SUBVERSION__)
diff --git a/Modules/Compiler/IAR-FindBinUtils.cmake b/Modules/Compiler/IAR-FindBinUtils.cmake
index 5fecb26..e8f5e6b 100644
--- a/Modules/Compiler/IAR-FindBinUtils.cmake
+++ b/Modules/Compiler/IAR-FindBinUtils.cmake
@@ -10,39 +10,39 @@
 
 set(__iar_hints "${__iar_hint_1}" "${__iar_hint_2}")
 
-if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  # could allow using normal binutils ar, since objects are normal ELF files?
-  find_program(CMAKE_IAR_LINKARM ilinkarm.exe HINTS ${__iar_hints}
-      DOC "The IAR ARM linker")
+if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" OR
+   "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
+   # could allow using normal binutils ar, since objects are normal ELF files?
+  find_program(CMAKE_IAR_LINKER ilink${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}.exe HINTS ${__iar_hints}
+      DOC "The IAR ILINK linker")
   find_program(CMAKE_IAR_ARCHIVE iarchive.exe HINTS ${__iar_hints}
       DOC "The IAR archiver")
 
   # find auxiliary tools
   find_program(CMAKE_IAR_ELFTOOL ielftool.exe HINTS ${__iar_hints}
       DOC "The IAR ELF Tool")
-    find_program(CMAKE_IAR_ELFDUMP ielfdumparm.exe HINTS ${__iar_hints}
+    find_program(CMAKE_IAR_ELFDUMP ielfdump${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}.exe HINTS ${__iar_hints}
       DOC "The IAR ELF Dumper")
   find_program(CMAKE_IAR_OBJMANIP iobjmanip.exe HINTS ${__iar_hints}
       DOC "The IAR ELF Object Tool")
   find_program(CMAKE_IAR_SYMEXPORT isymexport.exe HINTS ${__iar_hints}
       DOC "The IAR Absolute Symbol Exporter")
-  mark_as_advanced(CMAKE_IAR_LINKARM CMAKE_IAR_ARCHIVE CMAKE_IAR_ELFTOOL CMAKE_IAR_ELFDUMP CMAKE_IAR_OBJMANIP CMAKE_IAR_SYMEXPORT)
+  mark_as_advanced(CMAKE_IAR_LINKER CMAKE_IAR_ARCHIVE CMAKE_IAR_ELFTOOL CMAKE_IAR_ELFDUMP CMAKE_IAR_OBJMANIP CMAKE_IAR_SYMEXPORT)
 
   set(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_CUSTOM_CODE
-"set(CMAKE_IAR_LINKARM \"${CMAKE_IAR_LINKARM}\")
+"set(CMAKE_IAR_LINKER \"${CMAKE_IAR_LINKER}\")
 set(CMAKE_IAR_ARCHIVE \"${CMAKE_IAR_ARCHIVE}\")
 set(CMAKE_IAR_ELFTOOL \"${CMAKE_IAR_ELFTOOL}\")
 set(CMAKE_IAR_ELFDUMP \"${CMAKE_IAR_ELFDUMP}\")
 set(CMAKE_IAR_OBJMANIP \"${CMAKE_IAR_OBJMANIP}\")
-set(CMAKE_IAR_LINKARM \"${CMAKE_IAR_LINKARM}\")
+set(CMAKE_IAR_LINKER \"${CMAKE_IAR_LINKER}\")
 ")
 
-
 elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
 
   # For AVR and AVR32, IAR uses the "xlink" linker and the "xar" archiver:
   find_program(CMAKE_IAR_LINKER xlink.exe HINTS ${__iar_hints}
-      DOC "The IAR AVR linker")
+      DOC "The IAR XLINK linker")
   find_program(CMAKE_IAR_AR xar.exe HINTS ${__iar_hints}
       DOC "The IAR archiver")
   mark_as_advanced(CMAKE_IAR_LINKER CMAKE_IAR_AR)
diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake
index bbcdea2..8e75caa 100644
--- a/Modules/Compiler/IAR.cmake
+++ b/Modules/Compiler/IAR.cmake
@@ -2,11 +2,16 @@
 # Documentation can be downloaded here: http://www.iar.com/website1/1.0.1.0/675/1/
 # The initial feature request is here: https://gitlab.kitware.com/cmake/cmake/issues/10176
 # It also contains additional links and information.
-# See USER GUIDES -> C/C++ Development Guide and ReleaseNotes for:
+# See USER GUIDES -> C/C++ Development Guide and ReleaseNotes for EWARM:
 # version 6.30.8: http://supp.iar.com/FilesPublic/UPDINFO/006607/arm/doc/infocenter/index.ENU.html
 # version 7.60.1: http://supp.iar.com/FilesPublic/UPDINFO/011006/arm/doc/infocenter/index.ENU.html
 # version 8.10.1: http://netstorage.iar.com/SuppDB/Public/UPDINFO/011854/arm/doc/infocenter/index.ENU.html
 
+# The IAR internal compiler platform generations (Predefined symbol __IAR_SYSTEMS_ICC__):
+#  9 and higher means C11 and C++14 as language default (EWARM v8.x, EWRX v4.x and higher)
+#  8 means C99 and C++03 as language default (EWARM v6.x, v7.x. EWRX v2.x, 3.x)
+#  7 and lower means C89 and EC++ as language default. (EWARM v5.x and lower)
+
 # C/C++ Standard versions
 #
 # IAR typically only supports one C and C++ Standard version,
@@ -33,14 +38,46 @@
 # code and data size printouts (that can be inspected with common tools).
 
 # This module is shared by multiple languages; use include blocker.
-if(_IARARM_CMAKE_LOADED)
-  return()
-endif()
-set(_IARARM_CMAKE_LOADED 1)
+include_guard()
 
-macro(__compiler_iar_ARM lang)
+macro(__compiler_iar_ilink lang)
   set(CMAKE_EXECUTABLE_SUFFIX ".elf")
   if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
+    set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+    set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
+    set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE     "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy")
+
+    set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
+    set(CMAKE_DEPFILE_FLAGS_${lang} "--dependencies=ns <DEPFILE>")
+
+    string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
+    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -r")
+    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Ohz -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -Oh -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
+  endif()
+
+  if (${lang} STREQUAL "ASM")
+    string(APPEND CMAKE_ASM_FLAGS_INIT " ")
+    string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
+    string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+    string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
+    string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
+  endif()
+
+  set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKER}\" --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
+  set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_CREATE "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --replace <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH "")
+
+  set(CMAKE_LINKER "${CMAKE_IAR_LINKER}" CACHE FILEPATH "The IAR linker" FORCE)
+  set(CMAKE_AR "${CMAKE_IAR_ARCHIVE}" CACHE FILEPATH "The IAR archiver" FORCE)
+endmacro()
+
+macro(__compiler_iar_xlink lang)
+  set(CMAKE_EXECUTABLE_SUFFIX ".bin")
+  if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
 
     set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
     set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
@@ -56,32 +93,12 @@
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
   endif()
 
-  set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKARM}\" --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
-  set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>")
-  set(CMAKE_${lang}_ARCHIVE_CREATE "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>")
-  set(CMAKE_${lang}_ARCHIVE_APPEND "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --replace <LINK_FLAGS> <OBJECTS>")
-  set(CMAKE_${lang}_ARCHIVE_FINISH "")
-
-  set(CMAKE_LINKER "${CMAKE_IAR_LINKARM}" CACHE FILEPATH "The IAR linker" FORCE)
-  set(CMAKE_AR "${CMAKE_IAR_ARCHIVE}" CACHE FILEPATH "The IAR archiver" FORCE)
-endmacro()
-
-macro(__compiler_iar_AVR lang)
-  set(CMAKE_EXECUTABLE_SUFFIX ".bin")
-  if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
-
-    set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-    set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
-    set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE     "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy")
-
-    set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
-    set(CMAKE_DEPFILE_FLAGS_${lang} "--dependencies=ns <DEPFILE>")
-
-    string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
-    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -r")
-    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Ohz -DNDEBUG")
-    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -Oh -DNDEBUG")
-    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
+  if (${lang} STREQUAL "ASM")
+    string(APPEND CMAKE_ASM_FLAGS_INIT " ")
+    string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
+    string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+    string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
+    string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
   endif()
 
   set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKER}\" -S <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 8f1e194..20b37d2 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -422,6 +422,10 @@
       different behavior depending on whether the build starts from a fresh
       build directory or re-uses previous build contents.
 
+      If the CMake generator is the ``Green Hills MULTI`` and not overridden then
+      the orginal projects settings for the GHS toolset and target system
+      customization cache variables are propagated into the external project.
+
     ``SOURCE_SUBDIR <dir>``
       When no ``CONFIGURE_COMMAND`` option is specified, the configure step
       assumes the external project has a ``CMakeLists.txt`` file at the top of
@@ -2850,18 +2854,6 @@
       set(has_cmake_cache_default_args 1)
     endif()
 
-    if(has_cmake_cache_args OR has_cmake_cache_default_args)
-      set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake")
-      if(has_cmake_cache_args)
-        _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1)
-      endif()
-      if(has_cmake_cache_default_args)
-        _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0)
-      endif()
-      _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}")
-      list(APPEND cmd "-C${_ep_cache_args_script}")
-    endif()
-
     get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR)
     get_target_property(cmake_generator_instance ${name} _EP_CMAKE_GENERATOR_INSTANCE)
     get_target_property(cmake_generator_platform ${name} _EP_CMAKE_GENERATOR_PLATFORM)
@@ -2882,6 +2874,16 @@
         list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}")
       else()
         list(APPEND cmd "-G${CMAKE_GENERATOR}")
+        if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI")
+          set(has_cmake_cache_default_args 1)
+          set(cmake_cache_default_args ${cmake_cache_default_args}
+            "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}"
+            "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}"
+            "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}"
+            "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}"
+            "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}"
+            "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}")
+        endif()
       endif()
       if(cmake_generator_platform)
         message(FATAL_ERROR "Option CMAKE_GENERATOR_PLATFORM not allowed without CMAKE_GENERATOR.")
@@ -2903,6 +2905,18 @@
       endif()
     endif()
 
+    if(has_cmake_cache_args OR has_cmake_cache_default_args)
+      set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake")
+      if(has_cmake_cache_args)
+        _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1)
+      endif()
+      if(has_cmake_cache_default_args)
+        _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0)
+      endif()
+      _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}")
+      list(APPEND cmd "-C${_ep_cache_args_script}")
+    endif()
+
     list(APPEND cmd "<SOURCE_DIR><SOURCE_SUBDIR>")
   endif()
 
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index 53be493..7325eca 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -466,8 +466,17 @@
   elseif (GHSMULTI)
     set(_boost_COMPILER "-ghs")
   elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
-    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141)
-      set(_boost_COMPILER "-vc141;-vc140")
+    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150)
+      # Not yet known.
+      set(_boost_COMPILER "")
+    elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140)
+      # MSVC toolset 14.x versions are forward compatible.
+      set(_boost_COMPILER "")
+      foreach(v 9 8 7 6 5 4 3 2 1 0)
+        if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v})
+          list(APPEND _boost_COMPILER "-vc14${v}")
+        endif()
+      endforeach()
     elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80)
       set(_boost_COMPILER "-vc${MSVC_TOOLSET_VERSION}")
     elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10)
@@ -1083,9 +1092,15 @@
     else()
       set(_arch_suffix 32)
     endif()
-    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141)
-      list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.1)
-      list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.0)
+    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150)
+      # Not yet known.
+    elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140)
+      # MSVC toolset 14.x versions are forward compatible.
+      foreach(v 9 8 7 6 5 4 3 2 1 0)
+        if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v})
+          list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.${v})
+        endif()
+      endforeach()
     elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80)
       math(EXPR _toolset_major_version "${MSVC_TOOLSET_VERSION} / 10")
       list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-${_toolset_major_version}.0)
@@ -1762,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/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 70c44e1..b7b278b 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 20190412)
+set(CMake_VERSION_PATCH 20190417)
 #set(CMake_VERSION_RC 1)
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index a7d4455..9ad9669 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -126,11 +126,12 @@
     cmSystemTools::SetStdoutCallback([&s](std::string const& m) { s += m; });
     cmSystemTools::SetStderrCallback([&s](std::string const& m) { s += m; });
 
-    this->CM.SetProgressCallback(
-      [&s](const std::string& msg, float /*unused*/) {
+    this->CM.SetProgressCallback([&s](const std::string& msg, float prog) {
+      if (prog < 0) {
         s += msg;
         s += "\n";
-      });
+      }
+    });
   }
 
   ~cmCTestBuildAndTestCaptureRAII()
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/cmGhsMultiGpj.cxx b/Source/cmGhsMultiGpj.cxx
index 8b69b51..da27971 100644
--- a/Source/cmGhsMultiGpj.cxx
+++ b/Source/cmGhsMultiGpj.cxx
@@ -9,7 +9,8 @@
                                  "[Project]",
                                  "[Program]",
                                  "[Reference]",
-                                 "[Subproject]" };
+                                 "[Subproject]",
+                                 "[Custom Target]" };
 
 const char* GhsMultiGpj::GetGpjTag(Types gpjType)
 {
@@ -21,6 +22,7 @@
     case PROGRAM:
     case REFERENCE:
     case SUBPROJECT:
+    case CUSTOM_TARGET:
       tag = GHS_TAG[gpjType];
       break;
     default:
diff --git a/Source/cmGhsMultiGpj.h b/Source/cmGhsMultiGpj.h
index 420eab1..e588150 100644
--- a/Source/cmGhsMultiGpj.h
+++ b/Source/cmGhsMultiGpj.h
@@ -16,7 +16,8 @@
     PROJECT,
     PROGRAM,
     REFERENCE,
-    SUBPROJECT
+    SUBPROJECT,
+    CUSTOM_TARGET
   };
 
   static void WriteGpjTag(Types gpjType, std::ostream& fout);
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 668bcbd..b80da72 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -3,7 +3,7 @@
 #include "cmGhsMultiTargetGenerator.h"
 
 #include "cmCustomCommand.h"
-#include "cmCustomCommandLines.h"
+#include "cmCustomCommandGenerator.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGhsMultiGenerator.h"
@@ -11,17 +11,17 @@
 #include "cmLocalGenerator.h"
 #include "cmLocalGhsMultiGenerator.h"
 #include "cmMakefile.h"
+#include "cmOutputConverter.h"
 #include "cmSourceFile.h"
+#include "cmSourceFileLocation.h"
 #include "cmSourceGroup.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
-#include "cmTargetDepend.h"
 
 #include <algorithm>
-#include <assert.h>
 #include <ostream>
 #include <set>
 #include <utility>
@@ -32,6 +32,11 @@
       static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator()))
   , Makefile(target->Target->GetMakefile())
   , Name(target->GetName())
+#ifdef _WIN32
+  , CmdWindowsShell(true)
+#else
+  , CmdWindowsShell(false)
+#endif
 {
   // Store the configuration name that is being used
   if (const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
@@ -85,10 +90,19 @@
       return;
     }
     case cmStateEnums::UTILITY: {
-      std::string msg = "add_custom_target(<name> ...) not supported: ";
-      msg += this->Name;
-      cmSystemTools::Message(msg);
-      return;
+      this->TargetNameReal = this->GeneratorTarget->GetName();
+      this->TagType = GhsMultiGpj::CUSTOM_TARGET;
+      break;
+    }
+    case cmStateEnums::GLOBAL_TARGET: {
+      this->TargetNameReal = this->GeneratorTarget->GetName();
+      if (this->TargetNameReal ==
+          this->GetGlobalGenerator()->GetInstallTargetName()) {
+        this->TagType = GhsMultiGpj::CUSTOM_TARGET;
+      } else {
+        return;
+      }
+      break;
     }
     default:
       return;
@@ -105,29 +119,29 @@
 
 void cmGhsMultiTargetGenerator::GenerateTarget()
 {
-  // Open the filestream in copy-if-different mode.
-  std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory();
-  fname += "/";
-  fname += this->Name;
-  fname += cmGlobalGhsMultiGenerator::FILE_EXTENSION;
-  cmGeneratedFileStream fout(fname);
+  // Open the target file in copy-if-different mode.
+  std::string fproj = this->LocalGenerator->GetCurrentBinaryDirectory();
+  fproj += "/";
+  fproj += this->Name;
+  fproj += cmGlobalGhsMultiGenerator::FILE_EXTENSION;
+  cmGeneratedFileStream fout(fproj);
   fout.SetCopyIfDifferent(true);
 
   this->GetGlobalGenerator()->WriteFileHeader(fout);
   GhsMultiGpj::WriteGpjTag(this->TagType, fout);
 
-  const std::string language(
-    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName));
-
-  this->WriteTargetSpecifics(fout, this->ConfigName);
-  this->SetCompilerFlags(this->ConfigName, language);
-  this->WriteCompilerFlags(fout, this->ConfigName, language);
-  this->WriteCompilerDefinitions(fout, this->ConfigName, language);
-  this->WriteIncludes(fout, this->ConfigName, language);
-  this->WriteTargetLinkLine(fout, this->ConfigName);
-  this->WriteCustomCommands(fout);
+  if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
+    const std::string language(
+      this->GeneratorTarget->GetLinkerLanguage(this->ConfigName));
+    this->WriteTargetSpecifics(fout, this->ConfigName);
+    this->SetCompilerFlags(this->ConfigName, language);
+    this->WriteCompilerFlags(fout, this->ConfigName, language);
+    this->WriteCompilerDefinitions(fout, this->ConfigName, language);
+    this->WriteIncludes(fout, this->ConfigName, language);
+    this->WriteTargetLinkLine(fout, this->ConfigName);
+    this->WriteBuildEvents(fout);
+  }
   this->WriteSources(fout);
-  this->WriteReferences(fout);
   fout.Close();
 }
 
@@ -304,47 +318,145 @@
   }
 }
 
-void cmGhsMultiTargetGenerator::WriteCustomCommands(std::ostream& fout)
+void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout)
 {
-  WriteCustomCommandsHelper(fout, this->GeneratorTarget->GetPreBuildCommands(),
-                            cmTarget::PRE_BUILD);
-  WriteCustomCommandsHelper(
-    fout, this->GeneratorTarget->GetPostBuildCommands(), cmTarget::POST_BUILD);
+  this->WriteBuildEventsHelper(
+    fout, this->GeneratorTarget->GetPreBuildCommands(),
+    std::string("prebuild"), std::string("preexecShell"));
+
+  if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
+    this->WriteBuildEventsHelper(
+      fout, this->GeneratorTarget->GetPreLinkCommands(),
+      std::string("prelink"), std::string("preexecShell"));
+  }
+
+  this->WriteBuildEventsHelper(
+    fout, this->GeneratorTarget->GetPostBuildCommands(),
+    std::string("postbuild"), std::string("postexecShell"));
+}
+
+void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
+  std::ostream& fout, const std::vector<cmCustomCommand>& ccv,
+  std::string const& name, std::string const& cmd)
+{
+  int cmdcount = 0;
+
+  for (cmCustomCommand const& cc : ccv) {
+    cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator);
+    // Open the filestream for this custom command
+    std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory();
+    fname +=
+      "/" + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
+    fname += "/" + this->Name + "_" + name;
+    fname += std::to_string(cmdcount++);
+    fname += this->CmdWindowsShell ? ".bat" : ".sh";
+    cmGeneratedFileStream f(fname);
+    f.SetCopyIfDifferent(true);
+    this->WriteCustomCommandsHelper(f, ccg);
+    f.Close();
+    if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
+      fout << "    :" << cmd << "=\"" << fname << "\"" << std::endl;
+    } else {
+      fout << fname << std::endl;
+      fout << "    :outputName=\"" << fname << ".rule\"" << std::endl;
+    }
+    for (auto& byp : ccg.GetByproducts()) {
+      fout << "    :extraOutputFile=\"" << byp << "\"" << std::endl;
+    }
+  }
 }
 
 void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper(
-  std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet,
-  cmTarget::CustomCommandType const commandType)
+  std::ostream& fout, cmCustomCommandGenerator const& ccg)
 {
-  for (cmCustomCommand const& customCommand : commandsSet) {
-    cmCustomCommandLines const& commandLines = customCommand.GetCommandLines();
-    for (cmCustomCommandLine const& command : commandLines) {
-      switch (commandType) {
-        case cmTarget::PRE_BUILD:
-          fout << "    :preexecShellSafe=";
-          break;
-        case cmTarget::POST_BUILD:
-          fout << "    :postexecShellSafe=";
-          break;
-        default:
-          assert("Only pre and post are supported");
+  std::vector<std::string> cmdLines;
+
+  // if the command specified a working directory use it.
+  std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory();
+  std::string currentBinDir = dir;
+  std::string workingDir = ccg.GetWorkingDirectory();
+  if (!workingDir.empty()) {
+    dir = workingDir;
+  }
+
+  // Line to check for error between commands.
+#ifdef _WIN32
+  std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%";
+#else
+  std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi";
+#endif
+
+#ifdef _WIN32
+  cmdLines.push_back("@echo off");
+#endif
+  // Echo the custom command's comment text.
+  const char* comment = ccg.GetComment();
+  if (comment && *comment) {
+    std::string echocmd = "echo ";
+    echocmd += comment;
+    cmdLines.push_back(std::move(echocmd));
+  }
+
+  // Switch to working directory
+  std::string cdCmd;
+#ifdef _WIN32
+  std::string cdStr = "cd /D ";
+#else
+  std::string cdStr = "cd ";
+#endif
+  cdCmd = cdStr +
+    this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL);
+  cmdLines.push_back(std::move(cdCmd));
+
+  for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
+    // Build the command line in a single string.
+    std::string cmd = ccg.GetCommand(c);
+    if (!cmd.empty()) {
+      // Use "call " before any invocations of .bat or .cmd files
+      // invoked as custom commands in the WindowsShell.
+      //
+      bool useCall = false;
+
+      if (this->CmdWindowsShell) {
+        std::string suffix;
+        if (cmd.size() > 4) {
+          suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
+          if (suffix == ".bat" || suffix == ".cmd") {
+            useCall = true;
+          }
+        }
       }
 
-      bool firstIteration = true;
-      for (std::string const& commandLine : command) {
-        std::string subCommandE =
-          this->LocalGenerator->EscapeForShell(commandLine, true);
-        fout << (firstIteration ? "'" : " ");
-        // Need to double escape backslashes
-        cmSystemTools::ReplaceString(subCommandE, "\\", "\\\\");
-        fout << subCommandE;
-        firstIteration = false;
+      cmSystemTools::ReplaceString(cmd, "/./", "/");
+      // Convert the command to a relative path only if the current
+      // working directory will be the start-output directory.
+      bool had_slash = cmd.find('/') != std::string::npos;
+      if (workingDir.empty()) {
+        cmd =
+          this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir, cmd);
       }
-      if (!command.empty()) {
-        fout << "'" << std::endl;
+      bool has_slash = cmd.find('/') != std::string::npos;
+      if (had_slash && !has_slash) {
+        // This command was specified as a path to a file in the
+        // current directory.  Add a leading "./" so it can run
+        // without the current directory being in the search path.
+        cmd = "./" + cmd;
       }
+      cmd = this->LocalGenerator->ConvertToOutputFormat(
+        cmd, cmOutputConverter::SHELL);
+      if (useCall) {
+        cmd = "call " + cmd;
+      }
+      ccg.AppendArguments(c, cmd);
+      cmdLines.push_back(std::move(cmd));
     }
   }
+
+  // push back the custom commands
+  for (auto const& c : cmdLines) {
+    fout << c << std::endl;
+    fout << check_error << std::endl;
+  }
 }
 
 void cmGhsMultiTargetGenerator::WriteSourceProperty(
@@ -385,7 +497,7 @@
 
   /* list of known groups and the order they are displayed in a project file */
   const std::vector<std::string> standardGroups = {
-    "Header Files", "Source Files",     "CMake Rules",
+    "CMake Rules",  "Header Files",     "Source Files",
     "Object Files", "Object Libraries", "Resources"
   };
 
@@ -403,6 +515,14 @@
       groupFilesList[i] = *n;
       i += 1;
       groupNames.erase(gn);
+    } else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
+               gn == "CMake Rules") {
+      /* make sure that rules folder always exists in case of custom targets
+       * that have no custom commands except for pre or post build events.
+       */
+      groupFilesList.resize(groupFilesList.size() + 1);
+      groupFilesList[i] = gn;
+      i += 1;
     }
   }
 
@@ -433,7 +553,7 @@
 
   /* write files into the proper project file
    * -- groups go into main project file
-   *    unless FOLDER property or variable is set.
+   *    unless NO_SOURCE_GROUP_FILE property or variable is set.
    */
   for (auto& sg : groupFilesList) {
     std::ostream* fout;
@@ -472,33 +592,98 @@
       } else {
         *fout << "{comment} " << sg << std::endl;
       }
+    } else if (sg.empty()) {
+      *fout << "{comment} Others" << std::endl;
     }
 
-    /* output rule for each source file */
-    for (const cmSourceFile* si : groupFiles[sg]) {
+    if (sg != "CMake Rules") {
+      /* output rule for each source file */
+      for (const cmSourceFile* si : groupFiles[sg]) {
+        bool compile = true;
+        // Convert filename to native system
+        // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on
+        // windows when opening some files from the search window.
+        std::string fname(si->GetFullPath());
+        cmSystemTools::ConvertToOutputSlashes(fname);
 
-      // Convert filename to native system
-      // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on
-      // windows when opening some files from the search window.
-      std::string fname(si->GetFullPath());
-      cmSystemTools::ConvertToOutputSlashes(fname);
-      *fout << fname << std::endl;
+        /* For custom targets list any associated sources,
+         * comment out source code to prevent it from being
+         * compiled when processing this target.
+         * Otherwise, comment out any custom command (main) dependencies that
+         * are listed as source files to prevent them from being considered
+         * part of the build.
+         */
+        std::string comment;
+        if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
+             !si->GetLanguage().empty()) ||
+            si->GetCustomCommand()) {
+          comment = "{comment} ";
+          compile = false;
+        }
 
-      if ("ld" != si->GetExtension() && "int" != si->GetExtension() &&
-          "bsp" != si->GetExtension()) {
-        WriteObjectLangOverride(*fout, si);
+        *fout << comment << fname << std::endl;
+        if (compile) {
+          if ("ld" != si->GetExtension() && "int" != si->GetExtension() &&
+              "bsp" != si->GetExtension()) {
+            WriteObjectLangOverride(*fout, si);
+          }
+
+          this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I");
+          this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D");
+          this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", "");
+
+          /* to avoid clutter in the GUI only print out the objectName if it
+           * has been renamed */
+          std::string objectName = this->GeneratorTarget->GetObjectName(si);
+          if (!objectName.empty() &&
+              this->GeneratorTarget->HasExplicitObjectName(si)) {
+            *fout << "    -o " << objectName << std::endl;
+          }
+        }
       }
+    } else {
+      std::vector<cmSourceFile const*> customCommands;
+      if (ComputeCustomCommandOrder(customCommands)) {
+        std::string message = "The custom commands for target [" +
+          this->GeneratorTarget->GetName() + "] had a cycle.\n";
+        cmSystemTools::Error(message);
+      } else {
+        /* Custom targets do not have a dependency on SOURCES files.
+         * Therefore the dependency list may include SOURCES files after the
+         * custom target. Because nothing can depend on the custom target just
+         * move it to the last item.
+         */
+        for (auto sf = customCommands.begin(); sf != customCommands.end();
+             ++sf) {
+          if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") {
+            std::rotate(sf, sf + 1, customCommands.end());
+            break;
+          }
+        }
+        int cmdcount = 0;
+        for (auto& sf : customCommands) {
+          const cmCustomCommand* cc = sf->GetCustomCommand();
+          cmCustomCommandGenerator ccg(*cc, this->ConfigName,
+                                       this->LocalGenerator);
 
-      this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I");
-      this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D");
-      this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", "");
-
-      /* to avoid clutter in the gui only print out the objectName if it has
-       * been renamed */
-      std::string objectName = this->GeneratorTarget->GetObjectName(si);
-      if (!objectName.empty() &&
-          this->GeneratorTarget->HasExplicitObjectName(si)) {
-        *fout << "    -o " << objectName << std::endl;
+          // Open the filestream for this custom command
+          std::string fname =
+            this->LocalGenerator->GetCurrentBinaryDirectory();
+          fname += "/" +
+            this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
+          fname += "/" + this->Name + "_cc";
+          fname += std::to_string(cmdcount++) + "_";
+          fname += (sf->GetLocation()).GetName();
+          fname += this->CmdWindowsShell ? ".bat" : ".sh";
+          cmGeneratedFileStream f(fname);
+          f.SetCopyIfDifferent(true);
+          this->WriteCustomCommandsHelper(f, ccg);
+          f.Close();
+          this->WriteCustomCommandLine(*fout, fname, ccg);
+        }
+      }
+      if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) {
+        this->WriteBuildEvents(*fout);
       }
     }
   }
@@ -508,6 +693,33 @@
   }
 }
 
+void cmGhsMultiTargetGenerator::WriteCustomCommandLine(
+  std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg)
+{
+  /* NOTE: Customization Files are not well documented.  Testing showed
+   * that ":outputName=file" can only be used once per script.  The
+   * script will only run if ":outputName=file" is missing or just run
+   * once if ":outputName=file" is not specified.  If there are
+   * multiple outputs then the script needs to be listed multiple times
+   * for each output.  Otherwise it won't rerun the script if one of
+   * the outputs is manually deleted.
+   */
+  bool specifyExtra = true;
+  for (auto& out : ccg.GetOutputs()) {
+    fout << fname << std::endl;
+    fout << "    :outputName=\"" << out << "\"" << std::endl;
+    if (specifyExtra) {
+      for (auto& byp : ccg.GetByproducts()) {
+        fout << "    :extraOutputFile=\"" << byp << "\"" << std::endl;
+      }
+      for (auto& dep : ccg.GetDepends()) {
+        fout << "    :depends=\"" << dep << "\"" << std::endl;
+      }
+      specifyExtra = false;
+    }
+  }
+}
+
 void cmGhsMultiTargetGenerator::WriteObjectLangOverride(
   std::ostream& fout, const cmSourceFile* sourceFile)
 {
@@ -521,35 +733,6 @@
   }
 }
 
-void cmGhsMultiTargetGenerator::WriteReferences(std::ostream& fout)
-{
-  // This only applies to INTEGRITY Applications
-  if (this->TagType != GhsMultiGpj::INTERGRITY_APPLICATION) {
-    return;
-  }
-
-  // Get the targets that this one depends upon
-  cmTargetDependSet unordered =
-    this->GetGlobalGenerator()->GetTargetDirectDepends(this->GeneratorTarget);
-  cmGlobalGhsMultiGenerator::OrderedTargetDependSet ordered(unordered,
-                                                            this->Name);
-  for (auto& t : ordered) {
-    std::string tname = t->GetName();
-    std::string tpath = t->LocalGenerator->GetCurrentBinaryDirectory();
-    std::string rootpath = this->LocalGenerator->GetCurrentBinaryDirectory();
-    std::string outpath =
-      this->LocalGenerator->MaybeConvertToRelativePath(rootpath, tpath) + "/" +
-      tname + "REF" + cmGlobalGhsMultiGenerator::FILE_EXTENSION;
-
-    fout << outpath;
-    fout << "    ";
-    GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fout);
-
-    // Tell the global generator that a refernce project needs to be created
-    t->Target->SetProperty("GHS_REFERENCE_PROJECT", "ON");
-  }
-}
-
 bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()
 {
   const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app");
@@ -566,3 +749,51 @@
   }
   return false;
 }
+
+bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder(
+  std::vector<cmSourceFile const*>& order)
+{
+  std::set<cmSourceFile const*> temp;
+  std::set<cmSourceFile const*> perm;
+
+  // Collect all custom commands for this target
+  std::vector<cmSourceFile const*> customCommands;
+  this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName);
+
+  for (cmSourceFile const* si : customCommands) {
+    bool r = VisitCustomCommand(temp, perm, order, si);
+    if (r) {
+      return r;
+    }
+  }
+  return false;
+}
+
+bool cmGhsMultiTargetGenerator::VisitCustomCommand(
+  std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm,
+  std::vector<cmSourceFile const*>& order, cmSourceFile const* si)
+{
+  /* check if permanent mark is set*/
+  if (perm.find(si) == perm.end()) {
+    /* set temporary mark; check if revisit*/
+    if (temp.insert(si).second) {
+      for (auto& di : si->GetCustomCommand()->GetDepends()) {
+        cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator()
+                                   ->GetMakefile()
+                                   ->GetSourceFileWithOutput(di);
+        /* if sf exists then visit */
+        if (sf && this->VisitCustomCommand(temp, perm, order, sf)) {
+          return true;
+        }
+      }
+      /* mark as complete; insert into beginning of list*/
+      perm.insert(si);
+      order.push_back(si);
+      return false;
+    }
+    /* revisiting item - not a DAG */
+    return true;
+  }
+  /* already complete */
+  return false;
+}
diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h
index a4e23d9..a131567 100644
--- a/Source/cmGhsMultiTargetGenerator.h
+++ b/Source/cmGhsMultiTargetGenerator.h
@@ -5,14 +5,14 @@
 
 #include "cmGhsMultiGpj.h"
 
-#include "cmTarget.h"
-
 #include <iosfwd>
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
 
 class cmCustomCommand;
+class cmCustomCommandGenerator;
 class cmGeneratorTarget;
 class cmGlobalGhsMultiGenerator;
 class cmLocalGhsMultiGenerator;
@@ -49,15 +49,23 @@
   void WriteIncludes(std::ostream& fout, const std::string& config,
                      const std::string& language);
   void WriteTargetLinkLine(std::ostream& fout, std::string const& config);
-  void WriteCustomCommands(std::ostream& fout);
-  void WriteCustomCommandsHelper(
-    std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet,
-    cmTarget::CustomCommandType commandType);
+  void WriteBuildEvents(std::ostream& fout);
+  void WriteBuildEventsHelper(std::ostream& fout,
+                              const std::vector<cmCustomCommand>& ccv,
+                              std::string const& name, std::string const& cmd);
+  void WriteCustomCommandsHelper(std::ostream& fout,
+                                 cmCustomCommandGenerator const& ccg);
+  void WriteCustomCommandLine(std::ostream& fout, std::string& fname,
+                              cmCustomCommandGenerator const& ccg);
+  bool ComputeCustomCommandOrder(std::vector<cmSourceFile const*>& order);
+  bool VisitCustomCommand(std::set<cmSourceFile const*>& temp,
+                          std::set<cmSourceFile const*>& perm,
+                          std::vector<cmSourceFile const*>& order,
+                          cmSourceFile const* sf);
   void WriteSources(std::ostream& fout_proj);
   void WriteSourceProperty(std::ostream& fout, const cmSourceFile* sf,
                            std::string const& propName,
                            std::string const& propFlag);
-  void WriteReferences(std::ostream& fout);
   static void WriteObjectLangOverride(std::ostream& fout,
                                       const cmSourceFile* sourceFile);
 
@@ -71,7 +79,8 @@
   std::string TargetNameReal;
   GhsMultiGpj::Types TagType;
   std::string const Name;
-  std::string ConfigName; /* CMAKE_BUILD_TYPE */
+  std::string ConfigName;     /* CMAKE_BUILD_TYPE */
+  bool const CmdWindowsShell; /* custom commands run in cmd.exe or /bin/sh */
 };
 
 #endif // ! cmGhsMultiTargetGenerator_h
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 9f361f6..b69dea0 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -265,14 +265,68 @@
        << std::endl;
 }
 
-void cmGlobalGhsMultiGenerator::WriteTopLevelProject(
-  std::ostream& fout, cmLocalGenerator* root,
-  std::vector<cmLocalGenerator*>& generators)
+void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout)
 {
-  WriteFileHeader(fout);
+  fout << "Commands {\n"
+          "  Custom_Rule_Command {\n"
+          "    name = \"Custom Rule Command\"\n"
+          "    exec = \"";
+#ifdef _WIN32
+  fout << "cmd.exe";
+#else
+  fout << "/bin/sh";
+#endif
+  fout << "\"\n"
+          "    options = {\"SpecialOptions\"}\n"
+          "  }\n"
+          "}\n";
 
-  this->WriteMacros(fout);
-  this->WriteHighLevelDirectives(fout);
+  fout << "\n\n";
+  fout << "FileTypes {\n"
+          "  CmakeRule {\n"
+          "    name = \"Custom Rule\"\n"
+          "    action = \"&Run\"\n"
+          "    extensions = {\"";
+#ifdef _WIN32
+  fout << "bat";
+#else
+  fout << "sh";
+#endif
+  fout << "\"}\n"
+          "    grepable = false\n"
+          "    command = \"Custom Rule Command\"\n"
+          "    commandLine = \"$COMMAND ";
+#ifdef _WIN32
+  fout << "/c";
+#endif
+  fout << " $INPUTFILE\"\n"
+          "    progress = \"Processing Custom Rule\"\n"
+          "    promoteToFirstPass = true\n"
+          "    outputType = \"None\"\n"
+          "    color = \"#800080\"\n"
+          "  }\n"
+          "}\n";
+}
+
+void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout)
+{
+  fout << "FileTypes {\n"
+          "  CmakeTarget {\n"
+          "    name = \"Custom Target\"\n"
+          "    action = \"&Execute\"\n"
+          "    grepable = false\n"
+          "    outputType = \"None\"\n"
+          "    color = \"#800080\"\n"
+          "  }\n"
+          "}\n";
+}
+
+void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
+                                                     cmLocalGenerator* root)
+{
+  this->WriteFileHeader(fout);
+  this->WriteMacros(fout, root);
+  this->WriteHighLevelDirectives(root, fout);
   GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
 
   fout << "# Top Level Project File" << std::endl;
@@ -298,71 +352,152 @@
     }
     fout << "\"" << this->OsDir << "\"" << std::endl;
   }
-
-  WriteSubProjects(fout, root, generators);
 }
 
-void cmGlobalGhsMultiGenerator::WriteSubProjects(
-  std::ostream& fout, cmLocalGenerator* root,
-  std::vector<cmLocalGenerator*>& generators)
+void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
+                                                 std::string& all_target)
 {
+  fout << "CMakeFiles/" << all_target << " [Project]" << std::endl;
+  // All known targets
+  for (cmGeneratorTarget const* target : this->ProjectTargets) {
+    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
+        target->GetType() == cmStateEnums::MODULE_LIBRARY ||
+        target->GetType() == cmStateEnums::SHARED_LIBRARY ||
+        (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
+         target->GetName() != GetInstallTargetName())) {
+      continue;
+    }
+    fout << "CMakeFiles/" << target->GetName() + ".tgt" + FILE_EXTENSION
+         << " [Project]" << std::endl;
+  }
+}
+
+void cmGlobalGhsMultiGenerator::WriteProjectLine(
+  std::ostream& fout, cmGeneratorTarget const* target, cmLocalGenerator* root,
+  std::string& rootBinaryDir)
+{
+  const char* projName = target->GetProperty("GENERATOR_FILE_NAME");
+  const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
+  if (projName && projType) {
+    cmLocalGenerator* lg = target->GetLocalGenerator();
+    std::string dir = lg->GetCurrentBinaryDirectory();
+    dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir);
+    if (dir == ".") {
+      dir.clear();
+    } else {
+      if (dir.back() != '/') {
+        dir += "/";
+      }
+    }
+
+    std::string projFile = dir + projName + FILE_EXTENSION;
+    fout << projFile;
+    fout << " " << projType << std::endl;
+  } else {
+    /* Should never happen */
+    std::string message =
+      "The project file for target [" + target->GetName() + "] is missing.\n";
+    cmSystemTools::Error(message);
+    fout << "{comment} " << target->GetName() << " [missing project file]\n";
+  }
+}
+
+void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
+{
+  std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
+  rootBinaryDir += "/CMakeFiles";
+
+  // All known targets
+  for (cmGeneratorTarget const* target : this->ProjectTargets) {
+    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
+        target->GetType() == cmStateEnums::MODULE_LIBRARY ||
+        target->GetType() == cmStateEnums::SHARED_LIBRARY ||
+        (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
+         target->GetName() != GetInstallTargetName())) {
+      continue;
+    }
+
+    // create target build file
+    std::string name = target->GetName() + ".tgt" + FILE_EXTENSION;
+    std::string fname = rootBinaryDir + "/" + name;
+    cmGeneratedFileStream fbld(fname);
+    fbld.SetCopyIfDifferent(true);
+    this->WriteFileHeader(fbld);
+    GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
+    std::vector<cmGeneratorTarget const*> build;
+    if (ComputeTargetBuildOrder(target, build)) {
+      std::string message = "The inter-target dependency graph for target [" +
+        target->GetName() + "] had a cycle.\n";
+      cmSystemTools::Error(message);
+    } else {
+      for (auto& tgt : build) {
+        WriteProjectLine(fbld, tgt, root, rootBinaryDir);
+      }
+    }
+    fbld.Close();
+  }
+}
+
+void cmGlobalGhsMultiGenerator::WriteAllTarget(
+  cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators,
+  std::string& all_target)
+{
+  this->ProjectTargets.clear();
+
+  // create target build file
+  all_target = root->GetProjectName() + "." + this->GetAllTargetName() +
+    ".tgt" + FILE_EXTENSION;
+  std::string fname =
+    root->GetCurrentBinaryDirectory() + "/CMakeFiles/" + all_target;
+  cmGeneratedFileStream fbld(fname);
+  fbld.SetCopyIfDifferent(true);
+  this->WriteFileHeader(fbld);
+  GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
+
   // Collect all targets under this root generator and the transitive
   // closure of their dependencies.
   TargetDependSet projectTargets;
   TargetDependSet originalTargets;
   this->GetTargetSets(projectTargets, originalTargets, root, generators);
-  OrderedTargetDependSet orderedProjectTargets(projectTargets, "");
-
-  // write out all the sub-projects
-  std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
-  for (cmGeneratorTarget const* target : orderedProjectTargets) {
-    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+  OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
+  std::vector<cmGeneratorTarget const*> defaultTargets;
+  for (cmGeneratorTarget const* t : sortedProjectTargets) {
+    /* save list of all targets in sorted order */
+    this->ProjectTargets.push_back(t);
+  }
+  for (cmGeneratorTarget const* t : sortedProjectTargets) {
+    if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       continue;
     }
-
-    const char* projName = target->GetProperty("GENERATOR_FILE_NAME");
-    const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
-    if (projName && projType) {
-      cmLocalGenerator* lg = target->GetLocalGenerator();
-      std::string dir = lg->GetCurrentBinaryDirectory();
-      dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir);
-      if (dir == ".") {
-        dir.clear();
-      } else {
-        if (dir.back() != '/') {
-          dir += "/";
-        }
-      }
-
-      if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) {
-        fout << "{comment} ";
-      }
-      std::string projFile = dir + projName + FILE_EXTENSION;
-      fout << projFile;
-      fout << " " << projType << std::endl;
-
-      if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) {
-        // create reference project
-        std::string fname = dir;
-        fname += target->GetName();
-        fname += "REF";
-        fname += FILE_EXTENSION;
-
-        cmGeneratedFileStream fref(fname);
-        fref.SetCopyIfDifferent(true);
-
-        this->WriteFileHeader(fref);
-        GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref);
-        fref << "    :reference=" << projFile << std::endl;
-
-        fref.Close();
-      }
+    if (!cmSystemTools::IsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) {
+      defaultTargets.push_back(t);
     }
   }
+  std::vector<cmGeneratorTarget const*> build;
+  if (ComputeTargetBuildOrder(defaultTargets, build)) {
+    std::string message = "The inter-target dependency graph for project [" +
+      root->GetProjectName() + "] had a cycle.\n";
+    cmSystemTools::Error(message);
+  } else {
+    // determine the targets for ALL target
+    std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
+    rootBinaryDir += "/CMakeFiles";
+    for (cmGeneratorTarget const* target : build) {
+      if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
+          target->GetType() == cmStateEnums::MODULE_LIBRARY ||
+          target->GetType() == cmStateEnums::SHARED_LIBRARY) {
+        continue;
+      }
+      this->WriteProjectLine(fbld, target, root, rootBinaryDir);
+    }
+  }
+  fbld.Close();
 }
 
 void cmGlobalGhsMultiGenerator::Generate()
 {
+  std::string fname;
+
   // first do the superclass method
   this->cmGlobalGenerator::Generate();
 
@@ -370,11 +505,32 @@
   for (auto& it : this->ProjectMap) {
     this->OutputTopLevelProject(it.second[0], it.second);
   }
+
+  // create custom rule BOD file
+  fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
+    "/CMakeFiles/custom_rule.bod";
+  cmGeneratedFileStream frule(fname);
+  frule.SetCopyIfDifferent(true);
+  this->WriteFileHeader(frule);
+  this->WriteCustomRuleBOD(frule);
+  frule.Close();
+
+  // create custom target BOD file
+  fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
+    "/CMakeFiles/custom_target.bod";
+  cmGeneratedFileStream ftarget(fname);
+  ftarget.SetCopyIfDifferent(true);
+  this->WriteFileHeader(ftarget);
+  this->WriteCustomTargetBOD(ftarget);
+  ftarget.Close();
 }
 
 void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
 {
+  std::string fname;
+  std::string all_target;
+
   if (generators.empty()) {
     return;
   }
@@ -383,18 +539,21 @@
    * with target projects.  This avoid the issue where the project has
    * the same name as the executable target.
    */
-  std::string fname = root->GetCurrentBinaryDirectory();
+  fname = root->GetCurrentBinaryDirectory();
   fname += "/";
   fname += root->GetProjectName();
   fname += ".top";
   fname += FILE_EXTENSION;
 
-  cmGeneratedFileStream fout(fname);
-  fout.SetCopyIfDifferent(true);
+  cmGeneratedFileStream top(fname);
+  top.SetCopyIfDifferent(true);
+  this->WriteTopLevelProject(top, root);
 
-  this->WriteTopLevelProject(fout, root, generators);
+  this->WriteAllTarget(root, generators, all_target);
+  this->WriteTargets(root);
 
-  fout.Close();
+  this->WriteSubProjects(top, all_target);
+  top.Close();
 }
 
 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
@@ -426,6 +585,9 @@
   std::vector<std::string> files;
   cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
   if (!files.empty()) {
+    /* if multiple top-projects are found in build directory
+     * then prefer projectName top-project.
+     */
     auto p = std::find(files.begin(), files.end(), proj);
     if (p == files.end()) {
       proj = files.at(0);
@@ -440,20 +602,24 @@
     } else {
       for (const auto& tname : targetNames) {
         if (!tname.empty()) {
-          if (tname.compare(tname.size() - 4, 4, ".gpj") == 0) {
-            makeCommand.Add(tname);
-          } else {
-            makeCommand.Add(tname + ".gpj");
-          }
+          makeCommand.Add(tname + ".tgt.gpj");
         }
       }
     }
+  } else {
+    /* transform name to default build */;
+    std::string all = proj;
+    all.replace(all.end() - 7, all.end(),
+                std::string(this->GetAllTargetName()) + ".tgt.gpj");
+    makeCommand.Add(all);
   }
   return { makeCommand };
 }
 
-void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout)
+void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
+                                            cmLocalGenerator* root)
 {
+  fout << "macro PROJ_NAME=" << root->GetProjectName() << std::endl;
   char const* ghsGpjMacros =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
   if (nullptr != ghsGpjMacros) {
@@ -465,13 +631,14 @@
   }
 }
 
-void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout)
+void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(
+  cmLocalGenerator* root, std::ostream& fout)
 {
   /* set primary target */
   std::string tgt;
   const char* t =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET");
-  if (t) {
+  if (t && *t != '\0') {
     tgt = t;
     this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET");
   } else {
@@ -486,16 +653,20 @@
   }
 
   fout << "primaryTarget=" << tgt << std::endl;
+  fout << "customization=" << root->GetBinaryDirectory()
+       << "/CMakeFiles/custom_rule.bod" << std::endl;
+  fout << "customization=" << root->GetBinaryDirectory()
+       << "/CMakeFiles/custom_target.bod" << std::endl;
 
   char const* const customization =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION");
   if (nullptr != customization && strlen(customization) > 0) {
-    fout << "customization=" << trimQuotes(customization) << std::endl;
+    fout << "customization=" << this->TrimQuotes(customization) << std::endl;
     this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
   }
 }
 
-std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str)
+std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string const& str)
 {
   std::string result;
   result.reserve(str.size());
@@ -528,3 +699,56 @@
 {
   this->insert(targets.begin(), targets.end());
 }
+
+bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
+  cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build)
+{
+  std::vector<cmGeneratorTarget const*> t{ tgt };
+  return ComputeTargetBuildOrder(t, build);
+}
+
+bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
+  std::vector<cmGeneratorTarget const*>& tgt,
+  std::vector<cmGeneratorTarget const*>& build)
+{
+  std::set<cmGeneratorTarget const*> temp;
+  std::set<cmGeneratorTarget const*> perm;
+
+  for (auto ti : tgt) {
+    bool r = VisitTarget(temp, perm, build, ti);
+    if (r) {
+      return r;
+    }
+  }
+  return false;
+}
+
+bool cmGlobalGhsMultiGenerator::VisitTarget(
+  std::set<cmGeneratorTarget const*>& temp,
+  std::set<cmGeneratorTarget const*>& perm,
+  std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti)
+{
+  /* check if permanent mark is set*/
+  if (perm.find(ti) == perm.end()) {
+    /* set temporary mark; check if revisit*/
+    if (temp.insert(ti).second) {
+      /* sort targets lexicographically to ensure that nodes are always visited
+       * in the same order */
+      OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti),
+                                           "");
+      for (auto& di : sortedTargets) {
+        if (this->VisitTarget(temp, perm, order, di)) {
+          return true;
+        }
+      }
+      /* mark as complete; insert into beginning of list*/
+      perm.insert(ti);
+      order.push_back(ti);
+      return false;
+    }
+    /* revisiting item - not a DAG */
+    return true;
+  }
+  /* already complete */
+  return false;
+}
diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h
index 1aeb1dc..98358c7 100644
--- a/Source/cmGlobalGhsMultiGenerator.h
+++ b/Source/cmGlobalGhsMultiGenerator.h
@@ -78,23 +78,7 @@
   // Write the common disclaimer text at the top of each build file.
   void WriteFileHeader(std::ostream& fout);
 
-  // Target dependency sorting
-  class TargetSet : public std::set<cmGeneratorTarget const*>
-  {
-  };
-  class TargetCompare
-  {
-    std::string First;
-
-  public:
-    TargetCompare(std::string first)
-      : First(std::move(first))
-    {
-    }
-    bool operator()(cmGeneratorTarget const* l,
-                    cmGeneratorTarget const* r) const;
-  };
-  class OrderedTargetDependSet;
+  const char* GetInstallTargetName() const override { return "install"; }
 
 protected:
   void Generate() override;
@@ -111,18 +95,53 @@
   /* top-level project */
   void OutputTopLevelProject(cmLocalGenerator* root,
                              std::vector<cmLocalGenerator*>& generators);
-  void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root,
-                            std::vector<cmLocalGenerator*>& generators);
-  void WriteMacros(std::ostream& fout);
-  void WriteHighLevelDirectives(std::ostream& fout);
-  void WriteSubProjects(std::ostream& fout, cmLocalGenerator* root,
-                        std::vector<cmLocalGenerator*>& generators);
+  void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root);
+  void WriteMacros(std::ostream& fout, cmLocalGenerator* root);
+  void WriteHighLevelDirectives(cmLocalGenerator* root, std::ostream& fout);
+  void WriteSubProjects(std::ostream& fout, std::string& all_target);
+  void WriteTargets(cmLocalGenerator* root);
+  void WriteProjectLine(std::ostream& fout, cmGeneratorTarget const* target,
+                        cmLocalGenerator* root, std::string& rootBinaryDir);
+  void WriteCustomRuleBOD(std::ostream& fout);
+  void WriteCustomTargetBOD(std::ostream& fout);
+  void WriteAllTarget(cmLocalGenerator* root,
+                      std::vector<cmLocalGenerator*>& generators,
+                      std::string& all_target);
 
-  std::string trimQuotes(std::string const& str);
+  std::string TrimQuotes(std::string const& str);
 
   std::string OsDir;
   static const char* DEFAULT_BUILD_PROGRAM;
   static const char* DEFAULT_TOOLSET_ROOT;
+
+  bool ComputeTargetBuildOrder(cmGeneratorTarget const* tgt,
+                               std::vector<cmGeneratorTarget const*>& build);
+  bool ComputeTargetBuildOrder(std::vector<cmGeneratorTarget const*>& tgt,
+                               std::vector<cmGeneratorTarget const*>& build);
+  bool VisitTarget(std::set<cmGeneratorTarget const*>& temp,
+                   std::set<cmGeneratorTarget const*>& perm,
+                   std::vector<cmGeneratorTarget const*>& order,
+                   cmGeneratorTarget const* ti);
+
+  std::vector<cmGeneratorTarget const*> ProjectTargets;
+
+  // Target sorting
+  class TargetSet : public std::set<cmGeneratorTarget const*>
+  {
+  };
+  class TargetCompare
+  {
+    std::string First;
+
+  public:
+    TargetCompare(std::string first)
+      : First(std::move(first))
+    {
+    }
+    bool operator()(cmGeneratorTarget const* l,
+                    cmGeneratorTarget const* r) const;
+  };
+  class OrderedTargetDependSet;
 };
 
 class cmGlobalGhsMultiGenerator::OrderedTargetDependSet
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/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 5195957..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)
 {
@@ -1430,10 +1449,10 @@
 static void ConvertToWindowsSlash(std::string& s)
 {
   // first convert all of the slashes
-  std::string::size_type pos = 0;
-  while ((pos = s.find('/', pos)) != std::string::npos) {
-    s[pos] = '\\';
-    pos++;
+  for (auto& ch : s) {
+    if (ch == '/') {
+      ch = '\\';
+    }
   }
 }
 
@@ -1449,7 +1468,7 @@
   std::vector<cmGeneratorTarget::AllConfigSource> const& sources =
     this->GeneratorTarget->GetAllConfigSources();
 
-  std::set<cmSourceGroup*> groupsUsed;
+  std::set<cmSourceGroup const*> groupsUsed;
   for (cmGeneratorTarget::AllConfigSource const& si : sources) {
     std::string const& source = si.Source->GetFullPath();
     cmSourceGroup* sourceGroup =
@@ -1534,13 +1553,13 @@
     {
       Elem e1(e0, "ItemGroup");
       e1.SetHasElements();
-      std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(),
-                                            groupsUsed.end());
+      std::vector<cmSourceGroup const*> groupsVec(groupsUsed.begin(),
+                                                  groupsUsed.end());
       std::sort(groupsVec.begin(), groupsVec.end(),
-                [](cmSourceGroup* l, cmSourceGroup* r) {
+                [](cmSourceGroup const* l, cmSourceGroup const* r) {
                   return l->GetFullName() < r->GetFullName();
                 });
-      for (cmSourceGroup* sg : groupsVec) {
+      for (cmSourceGroup const* sg : groupsVec) {
         std::string const& name = sg->GetFullName();
         if (!name.empty()) {
           std::string guidName = "SG_Filter_" + name;
@@ -1572,7 +1591,7 @@
 
 // Add to groupsUsed empty source groups that have non-empty children.
 void cmVisualStudio10TargetGenerator::AddMissingSourceGroups(
-  std::set<cmSourceGroup*>& groupsUsed,
+  std::set<cmSourceGroup const*>& groupsUsed,
   const std::vector<cmSourceGroup>& allGroups)
 {
   for (cmSourceGroup const& current : allGroups) {
@@ -1583,17 +1602,15 @@
 
     this->AddMissingSourceGroups(groupsUsed, children);
 
-    cmSourceGroup* current_ptr = const_cast<cmSourceGroup*>(&current);
-    if (groupsUsed.find(current_ptr) != groupsUsed.end()) {
+    if (groupsUsed.count(&current) > 0) {
       continue; // group has already been added to set
     }
 
     // check if it least one of the group's descendants is not empty
     // (at least one child must already have been added)
-    std::vector<cmSourceGroup>::const_iterator child_it = children.begin();
+    auto child_it = children.begin();
     while (child_it != children.end()) {
-      cmSourceGroup* child_ptr = const_cast<cmSourceGroup*>(&(*child_it));
-      if (groupsUsed.find(child_ptr) != groupsUsed.end()) {
+      if (groupsUsed.count(&(*child_it)) > 0) {
         break; // found a child that was already added => add current group too
       }
       child_it++;
@@ -1603,7 +1620,7 @@
       continue; // no descendants have source files => ignore this group
     }
 
-    groupsUsed.insert(current_ptr);
+    groupsUsed.insert(&current);
   }
 }
 
@@ -2530,10 +2547,9 @@
   } else {
     std::set<std::string> languages;
     this->GeneratorTarget->GetLanguages(languages, configName);
-    for (const char* const* l = cm::cbegin(clLangs); l != cm::cend(clLangs);
-         ++l) {
-      if (languages.find(*l) != languages.end()) {
-        langForClCompile = *l;
+    for (const char* l : clLangs) {
+      if (languages.count(l)) {
+        langForClCompile = l;
         break;
       }
     }
@@ -4058,10 +4074,7 @@
 {
   std::set<std::string> expectedResxHeaders;
   this->GeneratorTarget->GetExpectedResxHeaders(expectedResxHeaders, "");
-
-  std::set<std::string>::const_iterator it =
-    expectedResxHeaders.find(headerFile);
-  return it != expectedResxHeaders.end();
+  return expectedResxHeaders.count(headerFile) > 0;
 }
 
 bool cmVisualStudio10TargetGenerator::IsXamlHeader(
@@ -4069,10 +4082,7 @@
 {
   std::set<std::string> expectedXamlHeaders;
   this->GeneratorTarget->GetExpectedXamlHeaders(expectedXamlHeaders, "");
-
-  std::set<std::string>::const_iterator it =
-    expectedXamlHeaders.find(headerFile);
-  return it != expectedXamlHeaders.end();
+  return expectedXamlHeaders.count(headerFile) > 0;
 }
 
 bool cmVisualStudio10TargetGenerator::IsXamlSource(
@@ -4080,10 +4090,7 @@
 {
   std::set<std::string> expectedXamlSources;
   this->GeneratorTarget->GetExpectedXamlSources(expectedXamlSources, "");
-
-  std::set<std::string>::const_iterator it =
-    expectedXamlSources.find(sourceFile);
-  return it != expectedXamlSources.end();
+  return expectedXamlSources.count(sourceFile) > 0;
 }
 
 void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1)
@@ -4654,10 +4661,8 @@
 void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties(
   Elem& e2, const std::map<std::string, std::string>& tags)
 {
-  if (!tags.empty()) {
-    for (const auto& i : tags) {
-      e2.Element(i.first.c_str(), i.second);
-    }
+  for (const auto& i : tags) {
+    e2.Element(i.first.c_str(), i.second);
   }
 }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 5901004..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);
@@ -165,7 +166,7 @@
   void WriteGroupSources(Elem& e0, std::string const& name,
                          ToolSources const& sources,
                          std::vector<cmSourceGroup>&);
-  void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed,
+  void AddMissingSourceGroups(std::set<cmSourceGroup const*>& groupsUsed,
                               const std::vector<cmSourceGroup>& allGroups);
   bool IsResxHeader(const std::string& headerFile);
   bool IsXamlHeader(const std::string& headerFile);
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/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 3746965..2e0902c 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -2336,7 +2336,7 @@
     endmacro()
     macro(add_test_GhsMulti_rename_install test_name)
       add_test_GhsMulti( ${test_name} GhsMultiRenameInstall ${test_name}
-        "-DCMAKE_INSTALL_PREFIX=. -DRUN_TEST=${test_name}" ${CMAKE_CMAKE_COMMAND} -P ./cmake_install.cmake)
+        "-DCMAKE_INSTALL_PREFIX=. -DRUN_TEST=${test_name}" ${CMAKE_CMAKE_COMMAND} --build . --target install)
     endmacro()
     #unset ghs config variables
     unset(ghs_config_name)
@@ -2391,6 +2391,9 @@
       add_test_GhsMulti(compiler_options_kernel GhsMultiCompilerOptions Kernel "-DRUN_TEST=KERNEL_FLAGS -DRUN_TEST_BUILD_TYPE=DEBUG")
       add_test_GhsMulti(try_compile_copy GhsMultiCopyFile "" "")
       add_test_GhsMulti(ghs_platform GhsMultiPlatform "" "")
+      add_test_GhsMulti(custom_target GhsMultiCustomTarget "" "")
+      add_test_GhsMulti(dep_order GhsMultiDepOrder "" "")
+      add_test_GhsMulti(external_project GhsMultiExternalProject "" "")
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/GhsMulti/${ghs_config_name}")
       #unset ghs config variables
       unset(ghs_config_name)
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/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt
new file mode 100644
index 0000000..93d668b
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt
@@ -0,0 +1,110 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(test C)
+
+# Tests assume no previous builds in the build directory
+file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/build)
+
+macro (test_output)
+  if (BUILD_OUTPUT STREQUAL EXPECTED_LINES )
+    message("Build OK")
+  else()
+    message("BUILD_OUTPUT")
+    foreach(Line IN LISTS BUILD_OUTPUT)
+      message("${Line}")
+    endforeach()
+    message("EXPECTED_LINES")
+    foreach(Line IN LISTS EXPECTED_LINES)
+      message("${Line}")
+    endforeach()
+    message(SEND_ERROR "Build KO")
+  endif()
+endmacro()
+
+message("Copy project")
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt.in
+  ${CMAKE_CURRENT_BINARY_DIR}/src/CMakeLists.txt COPYONLY)
+
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/exe1.c
+          ${CMAKE_CURRENT_SOURCE_DIR}/lib1.c
+  DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/src
+)
+
+message("Building ALL target")
+try_compile(RESULT
+  ${CMAKE_CURRENT_BINARY_DIR}/build
+  ${CMAKE_CURRENT_BINARY_DIR}/src
+  test
+  CMAKE_FLAGS
+    -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}
+  OUTPUT_VARIABLE BUILD_OUTPUT)
+
+message("Output from build:\n${BUILD_OUTPUT}")
+
+#filter outputs
+string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}")
+string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}")
+list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:")
+
+unset(EXPECTED_LINES)
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild")
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild")
+list(APPEND EXPECTED_LINES "CT: Processing target_cmd")
+list(APPEND EXPECTED_LINES "CT: Processing target_cmd_prebuild")
+list(APPEND EXPECTED_LINES "CT: Processing target_cmd_postbuild")
+
+test_output()
+
+message("Building target_update_files target")
+try_compile(RESULT
+  ${CMAKE_CURRENT_BINARY_DIR}/build
+  ${CMAKE_CURRENT_BINARY_DIR}/src
+  test target_update_files
+  CMAKE_FLAGS
+    -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}
+  OUTPUT_VARIABLE BUILD_OUTPUT)
+
+message("Output from build:\n${BUILD_OUTPUT}")
+
+#filter outputs
+string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}")
+string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}")
+list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:")
+
+unset(EXPECTED_LINES)
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild")
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild")
+list(APPEND EXPECTED_LINES "CT: generate C file another_file")
+list(APPEND EXPECTED_LINES "CT: generate text file dependsA")
+list(APPEND EXPECTED_LINES "CT: generate text file out_of_order_dep")
+list(APPEND EXPECTED_LINES "CT: generate text files A, B, and C")
+list(APPEND EXPECTED_LINES "CT: Processing target_update_files")
+
+test_output()
+
+message("Rerun target_update_files target")
+try_compile(RESULT
+  ${CMAKE_CURRENT_BINARY_DIR}/build
+  ${CMAKE_CURRENT_BINARY_DIR}/src
+  test target_update_files
+  CMAKE_FLAGS
+    -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}
+  OUTPUT_VARIABLE BUILD_OUTPUT)
+
+message("Output from build:\n${BUILD_OUTPUT}")
+
+#filter outputs
+string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}")
+string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}")
+list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:")
+
+unset(EXPECTED_LINES)
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild")
+list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild")
+list(APPEND EXPECTED_LINES "CT: generate text files A, B, and C")
+list(APPEND EXPECTED_LINES "CT: Processing target_update_files")
+
+test_output()
diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in
new file mode 100644
index 0000000..fed946c
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in
@@ -0,0 +1,108 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(test C)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GHS")
+  add_link_options("-non_shared")
+endif()
+
+add_library(lib1 lib1.c)
+
+set(TEST_MISSING_TARGET_SRC 0)
+set(TEST_MISSING_TARGET_DEP 0)
+set(TEST_MISSING_DEP 0)
+set(TEST_DEP_CYCLE 0)
+
+add_executable(exe1 exe1.c)
+target_link_libraries(exe1 lib1)
+
+add_custom_target(target_cmd ALL
+  COMMAND ${CMAKE_COMMAND} -E echo "target_cmd" > target_cmd
+  COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_extra" > target_cmd_extra.txt
+  BYPRODUCTS target_cmd target_cmd_extra.txt
+  COMMENT "CT: Processing target_cmd")
+
+add_custom_command(TARGET target_cmd PRE_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_prebuild" > target_cmd_prebuild.txt
+  BYPRODUCTS target_cmd_prebuild.txt
+  COMMENT "CT: Processing target_cmd_prebuild")
+#event does not run for custom targets
+add_custom_command(TARGET target_cmd PRE_LINK
+  COMMAND ${CMAKE_COMMAND} -E echo "executing target_cmd_prelink commands"
+  COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_prelink" > target_cmd_prelink.txt
+  BYPRODUCTS target_cmd_prelink.txt
+  COMMENT "CT: Processing target_cmd_prelink")
+add_custom_command(TARGET target_cmd POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "executing target_cmd_postbuild commands"
+  COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_postbuild" > target_cmd_postbuild.txt
+  BYPRODUCTS target_cmd_postbuild.txt
+  COMMENT "CT: Processing target_cmd_postbuild")
+
+add_custom_target(target_empty ALL
+  COMMENT "CT: Processing target_empty")
+
+add_custom_command(TARGET target_empty PRE_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "target_empty_prebuild" > target_empty_prebuild.txt
+  BYPRODUCTS target_empty_prebuild.txt
+  COMMENT "CT: Processing target_empty_prebuild")
+add_custom_command(TARGET target_empty POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "target_empty_postbuild" > target_empty_postbuild.txt
+  BYPRODUCTS target_empty_postbuild.txt
+  COMMENT "CT: Processing target_empty_postbuild")
+
+add_dependencies(target_cmd target_empty)
+
+add_custom_command(
+  OUTPUT out_of_order_dep.txt
+  COMMAND ${CMAKE_COMMAND} -E echo "out_of_order_dep" > out_of_order_dep.txt
+  COMMENT "CT: generate text file out_of_order_dep"
+  DEPENDS dependsA.txt
+)
+
+if(TEST_MISSING_TARGET_SRC)
+  set(SRC_FILE does_not_exist)
+endif()
+if(TEST_MISSING_TARGET_DEP)
+  set(DEP_FILE does_not_exist)
+endif()
+
+add_custom_target(target_update_files
+  DEPENDS genc_do_not_list.txt ${DEP_FILE}
+  SOURCES gena.txt genb.txt another_file.c ${SRC_FILE}
+  BYPRODUCTS junkit.txt
+  COMMAND ${CMAKE_COMMAND} -E copy another_file.c junkit.txt
+  COMMENT "CT: Processing target_update_files")
+
+add_custom_command(
+  OUTPUT force_rebuild gena.txt genb.txt genc_do_not_list.txt
+  COMMAND ${CMAKE_COMMAND} -E copy dependsA.txt gena.txt
+  COMMAND ${CMAKE_COMMAND} -E echo "genb" > genb.txt
+  COMMAND ${CMAKE_COMMAND} -E echo "genc" > genc_do_not_list.txt
+  DEPENDS out_of_order_dep.txt dependsA.txt
+  COMMENT "CT: generate text files A, B, and C"
+)
+
+if(TEST_MISSING_DEP)
+  set(MISSING_DEP MISSING_DEP)
+endif()
+if(TEST_DEP_CYCLE)
+  set(DEP_CYCLE out_of_order_dep.txt)
+endif()
+
+add_custom_command(
+  OUTPUT dependsA.txt
+  COMMAND ${CMAKE_COMMAND} -E echo "dependsA" > dependsA.txt
+  DEPENDS ${MISSING_DEP} ${DEP_CYCLE} another_file.c
+  COMMENT "CT: generate text file dependsA"
+)
+
+add_custom_command(
+  OUTPUT another_file.c
+  COMMAND ${CMAKE_COMMAND} -E echo "//auto-gen file" > another_file.c
+  COMMENT "CT: generate C file another_file"
+)
+
+add_dependencies(target_update_files target_empty)
diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c b/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c
new file mode 100644
index 0000000..29ad70a
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c
@@ -0,0 +1,5 @@
+extern int func(void);
+int main(void)
+{
+  return func();
+}
diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c b/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c
new file mode 100644
index 0000000..b35e9cc
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c
@@ -0,0 +1,4 @@
+int func(void)
+{
+  return 2;
+}
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt
new file mode 100644
index 0000000..2e2871b
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(test C)
+
+#set_property( GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1)
+add_subdirectory(exec)
+add_subdirectory(lib)
+add_subdirectory(protolib)
+add_dependencies(lib1 proto)
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt
new file mode 100644
index 0000000..85ee805
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+add_executable(exe1 exe1.c)
+target_link_libraries(exe1 lib1)
+target_include_directories(exe1 PRIVATE "${test_BINARY_DIR}")
+if(CMAKE_C_COMPILER_ID STREQUAL "GHS")
+  target_link_options(exe1 PRIVATE "-non_shared")
+endif()
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c b/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c
new file mode 100644
index 0000000..fbf4ed4
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c
@@ -0,0 +1,8 @@
+#include "lib1.h"
+#include "p.h"
+
+int main(void)
+{
+  return func1() + func2() + func3() + func1p() + func2p() + func3p() +
+    PROTO1 + PROTO2 + PROTO3;
+}
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt
new file mode 100644
index 0000000..ae30fa2
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+add_library(lib1 STATIC
+  func1.c lib1.h
+  "${test_BINARY_DIR}/protolib/proto1.c"
+  "${test_BINARY_DIR}/protolib/proto1.h")
+set_source_files_properties(
+  "${test_BINARY_DIR}/protolib/proto1.c"
+  "${test_BINARY_DIR}/protolib/proto1.h"
+  PROPERTIES GENERATED 1)
+target_include_directories(lib1 PRIVATE "${test_BINARY_DIR}/protolib"
+  PUBLIC .)
+add_custom_command( TARGET lib1 POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -E copy "${test_BINARY_DIR}/protolib/proto1.h" "${test_BINARY_DIR}/p.h"
+  COMMENT "Copy ${test_BINARY_DIR}/protolib/proto1.h ${test_BINARY_DIR}/p.h"
+  BYPRODUCTS "${test_BINARY_DIR}/p.h")
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c b/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c
new file mode 100644
index 0000000..53334fe
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c
@@ -0,0 +1,17 @@
+#include "lib1.h"
+#include "proto1.h"
+
+int func1(void)
+{
+  return 1 + PROTO1;
+}
+
+int func2(void)
+{
+  return 2 + PROTO2;
+}
+
+int func3(void)
+{
+  return 3 + PROTO3;
+}
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h b/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h
new file mode 100644
index 0000000..5e99f02
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h
@@ -0,0 +1,3 @@
+extern int func1(void);
+extern int func2(void);
+extern int func3(void);
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt
new file mode 100644
index 0000000..8cb6869
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+add_custom_target(proto ALL
+  DEPENDS proto1.c
+          proto1.h
+  SOURCES
+          ${test_SOURCE_DIR}/protolib/proto1.c.in
+          ${test_SOURCE_DIR}/protolib/proto1.h.in
+  COMMENT "Creating proto files")
+
+add_custom_command(
+  OUTPUT proto1.c
+  COMMAND ${CMAKE_COMMAND} -E copy
+    ${test_SOURCE_DIR}/protolib/proto1.c.in proto1.c
+  DEPENDS ${test_SOURCE_DIR}/protolib/proto1.c.in
+  COMMENT "generate proto C files"
+)
+
+add_custom_command(
+  OUTPUT proto1.h
+  COMMAND ${CMAKE_COMMAND} -E copy
+    ${test_SOURCE_DIR}/protolib/proto1.h.in proto1.h
+  DEPENDS ${test_SOURCE_DIR}/protolib/proto1.h.in
+  COMMENT "generate proto H files"
+)
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in
new file mode 100644
index 0000000..0efb1bd
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in
@@ -0,0 +1,16 @@
+#include "proto1.h"
+
+int func1p(void)
+{
+  return 1;
+}
+
+int func2p(void)
+{
+  return 2;
+}
+
+int func3p(void)
+{
+  return 3;
+}
diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in
new file mode 100644
index 0000000..f2f93af
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in
@@ -0,0 +1,7 @@
+extern int func1p(void);
+extern int func2p(void);
+extern int func3p(void);
+
+#define PROTO1 0x1
+#define PROTO2 0x2
+#define PROTO3 0x3
diff --git a/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt b/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt
new file mode 100644
index 0000000..24126c8
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(test C)
+include(ExternalProject)
+
+ExternalProject_Add(another_project
+  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/empty
+  BINARY_DIR empty_build
+  INSTALL_COMMAND ""
+  TEST_COMMAND ""
+  )
diff --git a/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt b/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt
new file mode 100644
index 0000000..6846a98
--- /dev/null
+++ b/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(empty NONE)
+
+message("EMPTY PROJECT")
diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt
index e431217..3837b5a 100644
--- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt
+++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt
@@ -1,4 +1,3 @@
 add_executable(App Main.c)
-target_include_directories(App PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../Lib)
 target_link_libraries(App Lib)
 target_compile_options(App PUBLIC "-non_shared")
diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt
index 00e0f59..bb9849a 100644
--- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt
+++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt
@@ -1 +1,2 @@
 add_library(Lib HelperFun.c HelperFun.h)
+target_include_directories(Lib PUBLIC .)
diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt
index c5db155..3f2f0eb 100644
--- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt
+++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt
@@ -18,3 +18,4 @@
 
 # create monolith INTEGRITY application
 add_executable(monolith test.int)
+add_dependencies(monolith vas)
diff --git a/Tests/GhsMulti/GhsMultiPlatform/file1.c b/Tests/GhsMulti/GhsMultiPlatform/file1.c
deleted file mode 100644
index 4132aa4..0000000
--- a/Tests/GhsMulti/GhsMultiPlatform/file1.c
+++ /dev/null
@@ -1,4 +0,0 @@
-int main(void)
-{
-  return -42;
-}
diff --git a/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt b/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt
index f5792b4..b2540d9 100644
--- a/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt
+++ b/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt
@@ -13,7 +13,7 @@
 
 if(RUN_TEST STREQUAL "SINGLE_EXEC")
   add_executable(exe1 exe.c)
-  set(targets_to_install ${targets_to_install} exe1)
+  set(targets_to_install exe1)
 endif()
 
 if(RUN_TEST STREQUAL "SINGLE_EXEC_RENAMED")
@@ -22,7 +22,7 @@
   set_property(TARGET exe1 PROPERTY RUNTIME_OUTPUT_DIRECTORY ${name}_bin_$<CONFIG>)
   set_property(TARGET exe1 PROPERTY OUTPUT_NAME ${name}_$<CONFIG>)
   set_property(TARGET exe1 PROPERTY SUFFIX .bin)
-  set(targets_to_install ${targets_to_install} exe1)
+  set(targets_to_install exe1)
 endif()
 
 if(RUN_TEST STREQUAL "EXEC_AND_LIB")
@@ -33,7 +33,7 @@
 
  add_executable(exe1 exe1.c)
  target_link_libraries(exe1 lib1)
- set(targets_to_install ${targets_to_install} exe1 lib1)
+ set(targets_to_install exe1 lib1)
 endif()
 
 install(TARGETS ${targets_to_install}
diff --git a/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt b/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt
index ed3094b..f5f3c55 100644
--- a/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt
+++ b/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt
@@ -5,8 +5,6 @@
 
 project(test C)
 
-add_custom_target(testTarget ALL echo this is a test)
-
 add_library(sharedLib SHARED file.c)
 
 add_library(moduleLib MODULE file.c)
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/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/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index df253a9..219e529 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -18,3 +18,4 @@
 run_cmake(VSCSharpDefines)
 run_cmake(VsSdkDirectories)
 run_cmake(VsGlobals)
+run_cmake(VsProjectImport)
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/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)