[Cmake-commits] CMake branch, master, updated. v3.10.1-844-gc1ea96d

Kitware Robot kwrobot at kitware.com
Thu Jan 18 09:55:09 EST 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, master has been updated
       via  c1ea96d04c90ac174989b3771ff90c001189036b (commit)
       via  9cfa213aa62e8a7b73fd0ba63e6e80ba86082e52 (commit)
       via  79908ae4aec7d02d900e53b21ef195b7bfc94725 (commit)
       via  45ee4979fb7c92502f6015ca3d93128f7c921b51 (commit)
       via  6f4b661383fccc5f2f5a936499ba11cf2000b535 (commit)
       via  a008578deebfa71b38786281450e3d9cf84f5847 (commit)
       via  488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4 (commit)
      from  84e2a7e58a897f9003f02afb2b0feb9c5923a27f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c1ea96d04c90ac174989b3771ff90c001189036b
commit c1ea96d04c90ac174989b3771ff90c001189036b
Merge: 84e2a7e 9cfa213
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Jan 18 14:46:20 2018 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Jan 18 09:46:29 2018 -0500

    Merge topic 'autogen-parallel'
    
    9cfa213a Autogen: Rename cmQtAutogeneratorInitializer to cmQtAutoGenInitializer
    79908ae4 Autogen: Add release notes for (CMAKE_)AUTOGEN_PARALLEL
    45ee4979 Autogen: Add documentation for (CMAKE_)AUTOGEN_PARALLEL
    6f4b6613 Autogen: Tests: Add AUTOGEN_PARALLEL tests
    a008578d Autogen: Process files concurrently in AUTOMOC and AUTOUIC
    488baaf0 Autogen: Tests: Fix MocInclude test(s)
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !1632


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=9cfa213aa62e8a7b73fd0ba63e6e80ba86082e52
commit 9cfa213aa62e8a7b73fd0ba63e6e80ba86082e52
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Wed Jan 17 17:21:57 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Rename cmQtAutogeneratorInitializer to cmQtAutoGenInitializer

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 1b46377..cd1287c 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -313,8 +313,8 @@ set(SRCS
   cmQtAutoGen.h
   cmQtAutoGenerator.cxx
   cmQtAutoGenerator.h
-  cmQtAutoGeneratorInitializer.cxx
-  cmQtAutoGeneratorInitializer.h
+  cmQtAutoGenInitializer.cxx
+  cmQtAutoGenInitializer.h
   cmQtAutoGeneratorMocUic.cxx
   cmQtAutoGeneratorMocUic.h
   cmQtAutoGeneratorRcc.cxx
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index fd9b488..47a9390 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -33,7 +33,7 @@
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmQtAutoGeneratorInitializer.h"
+#include "cmQtAutoGenInitializer.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
@@ -1473,10 +1473,10 @@ bool cmGlobalGenerator::ComputeTargetDepends()
   return true;
 }
 
-std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>>
+std::vector<std::unique_ptr<cmQtAutoGenInitializer>>
 cmGlobalGenerator::CreateQtAutoGenInitializers()
 {
-  std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> autogenInits;
+  std::vector<std::unique_ptr<cmQtAutoGenInitializer>> autogenInits;
 
 #ifdef CMAKE_BUILD_WITH_CMAKE
   for (cmLocalGenerator* localGen : this->LocalGenerators) {
@@ -1506,13 +1506,13 @@ cmGlobalGenerator::CreateQtAutoGenInitializers()
       }
 
       std::string qtVersionMajor =
-        cmQtAutoGeneratorInitializer::GetQtMajorVersion(target);
+        cmQtAutoGenInitializer::GetQtMajorVersion(target);
       // don't do anything if there is no Qt4 or Qt5Core (which contains moc)
       if (qtVersionMajor != "4" && qtVersionMajor != "5") {
         continue;
       }
 
-      autogenInits.emplace_back(new cmQtAutoGeneratorInitializer(
+      autogenInits.emplace_back(new cmQtAutoGenInitializer(
         target, mocEnabled, uicEnabled, rccEnabled, qtVersionMajor));
     }
   }
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 92e6a29..3ebff82 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -32,7 +32,7 @@ class cmLinkLineComputer;
 class cmLocalGenerator;
 class cmMakefile;
 class cmOutputConverter;
-class cmQtAutoGeneratorInitializer;
+class cmQtAutoGenInitializer;
 class cmSourceFile;
 class cmStateDirectory;
 class cmake;
@@ -437,7 +437,7 @@ protected:
   virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const;
 
   // Qt auto generators
-  std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>>
+  std::vector<std::unique_ptr<cmQtAutoGenInitializer>>
   CreateQtAutoGenInitializers();
 
   std::string SelectMakeProgram(const std::string& makeProgram,
diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
similarity index 98%
rename from Source/cmQtAutoGeneratorInitializer.cxx
rename to Source/cmQtAutoGenInitializer.cxx
index 29f3653..0b67981 100644
--- a/Source/cmQtAutoGeneratorInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -1,7 +1,7 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGen.h"
-#include "cmQtAutoGeneratorInitializer.h"
+#include "cmQtAutoGenInitializer.h"
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
@@ -226,7 +226,7 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
   return cycle;
 }
 
-cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer(
+cmQtAutoGenInitializer::cmQtAutoGenInitializer(
   cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
   std::string const& qtVersionMajor)
   : Target(target)
@@ -236,11 +236,11 @@ cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer(
   , QtVersionMajor(qtVersionMajor)
   , MultiConfig(MultiConfigT::WRAPPER)
 {
-  this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion(
-    target, this->QtVersionMajor);
+  this->QtVersionMinor =
+    cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
 }
 
-void cmQtAutoGeneratorInitializer::InitCustomTargets()
+void cmQtAutoGenInitializer::InitCustomTargets()
 {
   cmMakefile* makefile = this->Target->Target->GetMakefile();
   cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
@@ -900,7 +900,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
   }
 }
 
-void cmQtAutoGeneratorInitializer::SetupCustomTargets()
+void cmQtAutoGenInitializer::SetupCustomTargets()
 {
   cmMakefile* makefile = this->Target->Target->GetMakefile();
 
@@ -1072,7 +1072,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
   }
 }
 
-void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc()
+void cmQtAutoGenInitializer::SetupCustomTargetsMoc()
 {
   cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
   cmMakefile* makefile = this->Target->Target->GetMakefile();
@@ -1171,7 +1171,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc()
   }
 }
 
-void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic()
+void cmQtAutoGenInitializer::SetupCustomTargetsUic()
 {
   cmMakefile* makefile = this->Target->Target->GetMakefile();
 
@@ -1289,7 +1289,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic()
   }
 }
 
-std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
+std::vector<std::string> cmQtAutoGenInitializer::AddGeneratedSource(
   std::string const& filename, GeneratorT genType)
 {
   std::vector<std::string> genFiles;
@@ -1330,7 +1330,7 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
   return genFiles;
 }
 
-std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion(
+std::string cmQtAutoGenInitializer::GetQtMajorVersion(
   cmGeneratorTarget const* target)
 {
   cmMakefile* makefile = target->Target->GetMakefile();
@@ -1346,7 +1346,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion(
   return qtMajor;
 }
 
-std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion(
+std::string cmQtAutoGenInitializer::GetQtMinorVersion(
   cmGeneratorTarget const* target, std::string const& qtVersionMajor)
 {
   cmMakefile* makefile = target->Target->GetMakefile();
@@ -1366,7 +1366,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion(
   return qtMinor;
 }
 
-bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual(
+bool cmQtAutoGenInitializer::QtVersionGreaterOrEqual(
   unsigned long requestMajor, unsigned long requestMinor) const
 {
   unsigned long majorUL(0);
@@ -1382,9 +1382,9 @@ bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual(
 /// @brief Reads the resource files list from from a .qrc file
 /// @arg fileName Must be the absolute path of the .qrc file
 /// @return True if the rcc file was successfully read
-bool cmQtAutoGeneratorInitializer::RccListInputs(
-  std::string const& fileName, std::vector<std::string>& files,
-  std::string& error)
+bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName,
+                                           std::vector<std::string>& files,
+                                           std::string& error)
 {
   if (!cmSystemTools::FileExists(fileName.c_str())) {
     error = "rcc resource file does not exist:\n  ";
diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGenInitializer.h
similarity index 87%
rename from Source/cmQtAutoGeneratorInitializer.h
rename to Source/cmQtAutoGenInitializer.h
index 0567437..a667017 100644
--- a/Source/cmQtAutoGeneratorInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -1,7 +1,7 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmQtAutoGeneratorInitializer_h
-#define cmQtAutoGeneratorInitializer_h
+#ifndef cmQtAutoGenInitializer_h
+#define cmQtAutoGenInitializer_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
 #include "cmQtAutoGen.h"
@@ -14,7 +14,7 @@
 class cmGeneratorTarget;
 
 /// @brief Initializes the QtAutoGen generators
-class cmQtAutoGeneratorInitializer : public cmQtAutoGen
+class cmQtAutoGenInitializer : public cmQtAutoGen
 {
 public:
   static std::string GetQtMajorVersion(cmGeneratorTarget const* target);
@@ -44,9 +44,9 @@ public:
   };
 
 public:
-  cmQtAutoGeneratorInitializer(cmGeneratorTarget* target, bool mocEnabled,
-                               bool uicEnabled, bool rccEnabled,
-                               std::string const& qtVersionMajor);
+  cmQtAutoGenInitializer(cmGeneratorTarget* target, bool mocEnabled,
+                         bool uicEnabled, bool rccEnabled,
+                         std::string const& qtVersionMajor);
 
   void InitCustomTargets();
   void SetupCustomTargets();

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=79908ae4aec7d02d900e53b21ef195b7bfc94725
commit 79908ae4aec7d02d900e53b21ef195b7bfc94725
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Wed Jan 10 13:15:40 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Add release notes for (CMAKE_)AUTOGEN_PARALLEL

diff --git a/Help/release/dev/autogen-parallel.rst b/Help/release/dev/autogen-parallel.rst
new file mode 100644
index 0000000..50ae9a6
--- /dev/null
+++ b/Help/release/dev/autogen-parallel.rst
@@ -0,0 +1,10 @@
+autogen-parallel
+----------------
+
+* When using :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC`, CMake starts multiple
+  parallel ``moc`` or ``uic`` processes to reduce the build time.
+  The new :variable:`CMAKE_AUTOGEN_PARALLEL` variable and
+  :prop_tgt:`AUTOGEN_PARALLEL` target property allow to modify the number of
+  parallel ``moc`` or ``uic`` processes to start.
+  By default CMake starts a single ``moc`` or ``uic`` process for each physical
+  CPU on the host system.

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=45ee4979fb7c92502f6015ca3d93128f7c921b51
commit 45ee4979fb7c92502f6015ca3d93128f7c921b51
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Wed Jan 10 12:57:43 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Add documentation for (CMAKE_)AUTOGEN_PARALLEL

diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 2c63c4b..88b39d4 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -121,6 +121,7 @@ Properties on Targets
    /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG
    /prop_tgt/ARCHIVE_OUTPUT_NAME
    /prop_tgt/AUTOGEN_BUILD_DIR
+   /prop_tgt/AUTOGEN_PARALLEL
    /prop_tgt/AUTOGEN_TARGET_DEPENDS
    /prop_tgt/AUTOMOC_COMPILER_PREDEFINES
    /prop_tgt/AUTOMOC_DEPEND_FILTERS
diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst
index cafeae1..e5c593f 100644
--- a/Help/manual/cmake-qt.7.rst
+++ b/Help/manual/cmake-qt.7.rst
@@ -96,7 +96,8 @@ following targets by setting the :variable:`CMAKE_AUTOMOC` variable.  The
 options to pass to ``moc``. The :variable:`CMAKE_AUTOMOC_MOC_OPTIONS`
 variable may be populated to pre-set the options for all following targets.
 
-Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`.
+Additional macro names to search for can be added to
+:prop_tgt:`AUTOMOC_MACRO_NAMES`.
 
 Additional ``moc`` dependency file names can be extracted from source code
 by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`.
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 3f57612..c18d8da 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -276,6 +276,7 @@ Variables that Control the Build
    /variable/CMAKE_ANDROID_STL_TYPE
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG
+   /variable/CMAKE_AUTOGEN_PARALLEL
    /variable/CMAKE_AUTOMOC
    /variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES
    /variable/CMAKE_AUTOMOC_DEPEND_FILTERS
diff --git a/Help/prop_tgt/AUTOGEN_PARALLEL.rst b/Help/prop_tgt/AUTOGEN_PARALLEL.rst
new file mode 100644
index 0000000..07fbc5a
--- /dev/null
+++ b/Help/prop_tgt/AUTOGEN_PARALLEL.rst
@@ -0,0 +1,21 @@
+AUTOGEN_PARALLEL
+----------------
+
+Number of parallel ``moc`` or ``uic`` processes to start when using
+:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`.
+
+The custom `<origin>_autogen` target starts a number of threads of which
+each one parses a source file and on demand starts a ``moc`` or ``uic``
+process.  :prop_tgt:`AUTOGEN_PARALLEL` controls how many parallel threads
+(and therefore ``moc`` or ``uic`` processes) are started.
+
+- An empty (or unset) value or the string ``AUTO`` sets the number of
+  threads/processes to the number of physical CPUs on the host system.
+- A positive non zero integer value sets the exact thread/process count.
+- Otherwise a single thread/process is started.
+
+By default :prop_tgt:`AUTOGEN_PARALLEL` is initialized from
+:variable:`CMAKE_AUTOGEN_PARALLEL`.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst
index 9e5261f..3bd693a 100644
--- a/Help/prop_tgt/AUTOMOC.rst
+++ b/Help/prop_tgt/AUTOMOC.rst
@@ -71,7 +71,8 @@ automoc targets together in an IDE, e.g.  in MSVS.
 The global property :prop_gbl:`AUTOGEN_SOURCE_GROUP` can be used to group
 files generated by :prop_tgt:`AUTOMOC` together in an IDE, e.g.  in MSVS.
 
-Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`.
+Additional macro names to search for can be added to
+:prop_tgt:`AUTOMOC_MACRO_NAMES`.
 
 Additional ``moc`` dependency file names can be extracted from source code
 by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`.
@@ -82,5 +83,8 @@ which is controlled by :prop_tgt:`AUTOMOC_COMPILER_PREDEFINES`.
 Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by
 enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`.
 
+The number of parallel ``moc`` processes to start can be modified by
+setting :prop_tgt:`AUTOGEN_PARALLEL`.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst
index 1791384..4fc603f 100644
--- a/Help/prop_tgt/AUTOUIC.rst
+++ b/Help/prop_tgt/AUTOUIC.rst
@@ -33,5 +33,8 @@ autouic targets together in an IDE, e.g. in MSVS.
 Source files can be excluded from :prop_tgt:`AUTOUIC` processing by
 enabling :prop_sf:`SKIP_AUTOUIC` or the broader :prop_sf:`SKIP_AUTOGEN`.
 
+The number of parallel ``uic`` processes to start can be modified by
+setting :prop_tgt:`AUTOGEN_PARALLEL`.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
diff --git a/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst
new file mode 100644
index 0000000..dd9499a
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTOGEN_PARALLEL
+----------------------
+
+Number of parallel ``moc`` or ``uic`` processes to start when using
+:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`.
+
+This variable is used to initialize the :prop_tgt:`AUTOGEN_PARALLEL` property
+on all the targets.  See that target property for additional information.
+
+By default :variable:`CMAKE_AUTOGEN_PARALLEL` is unset.

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6f4b661383fccc5f2f5a936499ba11cf2000b535
commit 6f4b661383fccc5f2f5a936499ba11cf2000b535
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Fri Jan 5 13:28:09 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Tests: Add AUTOGEN_PARALLEL tests

diff --git a/Tests/QtAutogen/CommonTests.cmake b/Tests/QtAutogen/CommonTests.cmake
index c56780e..2c2e6d6 100644
--- a/Tests/QtAutogen/CommonTests.cmake
+++ b/Tests/QtAutogen/CommonTests.cmake
@@ -29,6 +29,12 @@ ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
 if(APPLE AND (NOT QT_TEST_VERSION STREQUAL 4))
   ADD_AUTOGEN_TEST(MacOsFW)
 endif()
+ADD_AUTOGEN_TEST(Parallel parallel)
+ADD_AUTOGEN_TEST(Parallel1 parallel1)
+ADD_AUTOGEN_TEST(Parallel2 parallel2)
+ADD_AUTOGEN_TEST(Parallel3 parallel3)
+ADD_AUTOGEN_TEST(Parallel4 parallel4)
+ADD_AUTOGEN_TEST(ParallelAUTO parallelAUTO)
 ADD_AUTOGEN_TEST(SameName sameName)
 ADD_AUTOGEN_TEST(StaticLibraryCycle slc)
 ADD_AUTOGEN_TEST(Complex QtAutogen)
diff --git a/Tests/QtAutogen/Parallel/CMakeLists.txt b/Tests/QtAutogen/Parallel/CMakeLists.txt
new file mode 100644
index 0000000..9c64804
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(Parallel)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallel ${PARALLEL_SRC})
+set_target_properties(parallel PROPERTIES AUTOGEN_PARALLEL "")
+target_link_libraries(parallel ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc
new file mode 100644
index 0000000..0ea3537
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="aaa/bbb">
+  <file>item.hpp</file>
+  <file>item.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp
new file mode 100644
index 0000000..850206f
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp
@@ -0,0 +1,22 @@
+#include "item.hpp"
+
+namespace aaa {
+namespace bbb {
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  MocLocal obj;
+}
+}
+}
+
+#include "aaa/bbb/item.moc"
diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp
new file mode 100644
index 0000000..0855043
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp
@@ -0,0 +1,18 @@
+#ifndef AAA_BBB_ITEM_HPP
+#define AAA_BBB_ITEM_HPP
+
+#include <QObject>
+
+namespace aaa {
+namespace bbb {
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+}
+}
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/aaa/data.qrc b/Tests/QtAutogen/Parallel/aaa/data.qrc
new file mode 100644
index 0000000..379af60
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/data.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="aaa/">
+  <file>item.hpp</file>
+  <file>item.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/aaa/item.cpp b/Tests/QtAutogen/Parallel/aaa/item.cpp
new file mode 100644
index 0000000..e35d3d1
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/item.cpp
@@ -0,0 +1,22 @@
+#include "item.hpp"
+// Include ui_view.h only in header
+
+namespace aaa {
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  Ui_ViewAAA ui;
+  MocLocal obj;
+}
+}
+
+#include "aaa/item.moc"
diff --git a/Tests/QtAutogen/Parallel/aaa/item.hpp b/Tests/QtAutogen/Parallel/aaa/item.hpp
new file mode 100644
index 0000000..875f72f
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/item.hpp
@@ -0,0 +1,18 @@
+#ifndef AAA_ITEM_HPP
+#define AAA_ITEM_HPP
+
+#include <QObject>
+// Include ui_view.h only in header
+#include <aaa/ui_view.h>
+
+namespace aaa {
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+}
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/aaa/view.ui b/Tests/QtAutogen/Parallel/aaa/view.ui
new file mode 100644
index 0000000..0f09980
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/aaa/view.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ViewAAA</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/Parallel/bbb/aaa/data.qrc b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc
new file mode 100644
index 0000000..da98009
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="bbb/aaa/">
+  <file>item.hpp</file>
+  <file>item.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp
new file mode 100644
index 0000000..7ad01c3
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp
@@ -0,0 +1,22 @@
+#include "item.hpp"
+
+namespace bbb {
+namespace aaa {
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  MocLocal obj;
+}
+}
+}
+
+#include "bbb/aaa/item.moc"
diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp
new file mode 100644
index 0000000..be07ca8
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp
@@ -0,0 +1,18 @@
+#ifndef BBB_AAA_ITEM_HPP
+#define BBB_AAA_ITEM_HPP
+
+#include <QObject>
+
+namespace bbb {
+namespace aaa {
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+}
+}
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/bbb/data.qrc b/Tests/QtAutogen/Parallel/bbb/data.qrc
new file mode 100644
index 0000000..5b080f5
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/data.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="bbb/">
+  <file>item.hpp</file>
+  <file>item.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/bbb/item.cpp b/Tests/QtAutogen/Parallel/bbb/item.cpp
new file mode 100644
index 0000000..9ef128e
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/item.cpp
@@ -0,0 +1,23 @@
+#include "item.hpp"
+// Include ui_view.h only in source
+#include <bbb/ui_view.h>
+
+namespace bbb {
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  Ui_ViewBBB ui;
+  MocLocal obj;
+}
+}
+
+#include "bbb/item.moc"
diff --git a/Tests/QtAutogen/Parallel/bbb/item.hpp b/Tests/QtAutogen/Parallel/bbb/item.hpp
new file mode 100644
index 0000000..d39a9d7
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/item.hpp
@@ -0,0 +1,17 @@
+#ifndef BBB_ITEM_HPP
+#define BBB_ITEM_HPP
+
+#include <QObject>
+// Include ui_view.h only in source
+
+namespace bbb {
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+}
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/bbb/view.ui b/Tests/QtAutogen/Parallel/bbb/view.ui
new file mode 100644
index 0000000..a8f506e
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/bbb/view.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ViewBBB</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/Parallel/ccc/data.qrc b/Tests/QtAutogen/Parallel/ccc/data.qrc
new file mode 100644
index 0000000..f934c39
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/ccc/data.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="ccc/">
+  <file>item.hpp</file>
+  <file>item.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/ccc/item.cpp b/Tests/QtAutogen/Parallel/ccc/item.cpp
new file mode 100644
index 0000000..ab8a281
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/ccc/item.cpp
@@ -0,0 +1,25 @@
+#include "item.hpp"
+// Include ui_view.h in source and header
+#include <ccc/ui_view.h>
+
+namespace ccc {
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  Ui_ViewCCC ui;
+  MocLocal obj;
+}
+}
+
+// Include own moc files
+#include "ccc/item.moc"
+#include "moc_item.cpp"
diff --git a/Tests/QtAutogen/Parallel/ccc/item.hpp b/Tests/QtAutogen/Parallel/ccc/item.hpp
new file mode 100644
index 0000000..20d9dd9
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/ccc/item.hpp
@@ -0,0 +1,18 @@
+#ifndef CCC_ITEM_HPP
+#define CCC_ITEM_HPP
+
+#include <QObject>
+// Include ui_view.h in source and header
+#include <ccc/ui_view.h>
+
+namespace ccc {
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+}
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/ccc/view.ui b/Tests/QtAutogen/Parallel/ccc/view.ui
new file mode 100644
index 0000000..7989c69
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/ccc/view.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ViewCCC</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/Parallel/data.qrc b/Tests/QtAutogen/Parallel/data.qrc
new file mode 100644
index 0000000..4ce0b4e
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/data.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+  <file>main.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/Parallel/item.cpp b/Tests/QtAutogen/Parallel/item.cpp
new file mode 100644
index 0000000..3d1fbe7
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/item.cpp
@@ -0,0 +1,20 @@
+#include "item.hpp"
+// Include ui_view.h in source and header
+#include <ui_view.h>
+
+class MocLocal : public QObject
+{
+  Q_OBJECT;
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
+};
+
+void Item::go()
+{
+  Ui_View ui;
+  MocLocal obj;
+}
+
+#include "item.moc"
diff --git a/Tests/QtAutogen/Parallel/item.hpp b/Tests/QtAutogen/Parallel/item.hpp
new file mode 100644
index 0000000..75e83f4
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/item.hpp
@@ -0,0 +1,15 @@
+#ifndef ITEM_HPP
+#define ITEM_HPP
+
+#include <QObject>
+// Include ui_view.h in source and header
+#include <ui_view.h>
+
+class Item : public QObject
+{
+  Q_OBJECT
+  Q_SLOT
+  void go();
+};
+
+#endif
diff --git a/Tests/QtAutogen/Parallel/main.cpp b/Tests/QtAutogen/Parallel/main.cpp
new file mode 100644
index 0000000..a4ffcb3
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/main.cpp
@@ -0,0 +1,16 @@
+#include "aaa/bbb/item.hpp"
+#include "aaa/item.hpp"
+#include "bbb/aaa/item.hpp"
+#include "bbb/item.hpp"
+#include "ccc/item.hpp"
+
+int main(int argv, char** args)
+{
+  // Object instances
+  ::aaa::Item aaa_item;
+  ::aaa::bbb::Item aaa_bbb_item;
+  ::bbb::Item bbb_item;
+  ::bbb::aaa::Item bbb_aaa_item;
+  ::ccc::Item ccc_item;
+  return 0;
+}
diff --git a/Tests/QtAutogen/Parallel/parallel.cmake b/Tests/QtAutogen/Parallel/parallel.cmake
new file mode 100644
index 0000000..551bcd8
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/parallel.cmake
@@ -0,0 +1,24 @@
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
+
+
+set(PBASE ${CMAKE_CURRENT_LIST_DIR})
+set(PARALLEL_SRC
+  ${PBASE}/aaa/bbb/item.cpp
+  ${PBASE}/aaa/bbb/data.qrc
+  ${PBASE}/aaa/item.cpp
+  ${PBASE}/aaa/data.qrc
+
+  ${PBASE}/bbb/aaa/item.cpp
+  ${PBASE}/bbb/aaa/data.qrc
+  ${PBASE}/bbb/item.cpp
+  ${PBASE}/bbb/data.qrc
+
+  ${PBASE}/ccc/item.cpp
+  ${PBASE}/ccc/data.qrc
+
+  ${PBASE}/item.cpp
+  ${PBASE}/data.qrc
+  ${PBASE}/main.cpp
+)
diff --git a/Tests/QtAutogen/Parallel/view.ui b/Tests/QtAutogen/Parallel/view.ui
new file mode 100644
index 0000000..2ffe734
--- /dev/null
+++ b/Tests/QtAutogen/Parallel/view.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>View</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/Parallel1/CMakeLists.txt b/Tests/QtAutogen/Parallel1/CMakeLists.txt
new file mode 100644
index 0000000..9c0b4e5
--- /dev/null
+++ b/Tests/QtAutogen/Parallel1/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(Parallel1)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallel1 ${PARALLEL_SRC})
+set_target_properties(parallel1 PROPERTIES AUTOGEN_PARALLEL 1)
+target_link_libraries(parallel1 ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/Parallel2/CMakeLists.txt b/Tests/QtAutogen/Parallel2/CMakeLists.txt
new file mode 100644
index 0000000..74c38f1
--- /dev/null
+++ b/Tests/QtAutogen/Parallel2/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(Parallel2)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallel2 ${PARALLEL_SRC})
+set_target_properties(parallel2 PROPERTIES AUTOGEN_PARALLEL 2)
+target_link_libraries(parallel2 ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/Parallel3/CMakeLists.txt b/Tests/QtAutogen/Parallel3/CMakeLists.txt
new file mode 100644
index 0000000..c735531
--- /dev/null
+++ b/Tests/QtAutogen/Parallel3/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(Parallel3)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallel3 ${PARALLEL_SRC})
+set_target_properties(parallel3 PROPERTIES AUTOGEN_PARALLEL 3)
+target_link_libraries(parallel3 ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/Parallel4/CMakeLists.txt b/Tests/QtAutogen/Parallel4/CMakeLists.txt
new file mode 100644
index 0000000..c012ccd
--- /dev/null
+++ b/Tests/QtAutogen/Parallel4/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(Parallel4)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallel4 ${PARALLEL_SRC})
+set_target_properties(parallel4 PROPERTIES AUTOGEN_PARALLEL 4)
+target_link_libraries(parallel4 ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt
new file mode 100644
index 0000000..3fd3ebc
--- /dev/null
+++ b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+project(ParallelAUTO)
+include("../AutogenTest.cmake")
+
+# Test different values for AUTOGEN_PARALLEL
+include("../Parallel/parallel.cmake")
+
+add_executable(parallelAUTO ${PARALLEL_SRC})
+set_target_properties(parallelAUTO PROPERTIES AUTOGEN_PARALLEL "AUTO")
+target_link_libraries(parallelAUTO ${QT_LIBRARIES})

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=a008578deebfa71b38786281450e3d9cf84f5847
commit a008578deebfa71b38786281450e3d9cf84f5847
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Wed Jan 3 16:59:40 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Process files concurrently in AUTOMOC and AUTOUIC
    
    This introduces concurrent thread processing in the `_autogen`
    target wich processes AUTOMOC and AUTOUIC.
    Source file parsing is distributed among the threads by
    using a job queue from which the threads pull new parse jobs.
    Each thread might start an independent ``moc`` or ``uic`` process.
    Altogether this roughly speeds up the AUTOMOC and AUTOUIC build
    process by the number of physical CPUs on the host system.
    
    The exact number of threads to start in  the `_autogen` target
    is controlled by the new AUTOGEN_PARALLEL target property which
    is initialized by the new CMAKE_AUTOGEN_PARALLEL variable.
    If AUTOGEN_PARALLEL is empty or unset (which is the default)
    the thread count is set to the number of physical CPUs on
    the host system.
    
    The AUTOMOC/AUTOUIC generator and the AUTORCC generator are
    refactored to use a libuv loop internally.
    
    Closes #17422.

diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in
index 5457a6f..cbab4a7 100644
--- a/Modules/AutoRccInfo.cmake.in
+++ b/Modules/AutoRccInfo.cmake.in
@@ -1,10 +1,6 @@
 # Meta
 set(ARCC_MULTI_CONFIG @_multi_config@)
 # Directories and files
-set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/")
-set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/")
-set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/")
-set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/")
 set(ARCC_BUILD_DIR @_build_dir@)
 # Qt environment
 set(ARCC_RCC_EXECUTABLE @_qt_rcc_executable@)
diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in
index 9a4a06d..7320c0a 100644
--- a/Modules/AutogenInfo.cmake.in
+++ b/Modules/AutogenInfo.cmake.in
@@ -1,5 +1,6 @@
 # Meta
 set(AM_MULTI_CONFIG @_multi_config@)
+set(AM_PARALLEL @_parallel@)
 # Directories and files
 set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/")
 set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/")
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 255a532..18ecbe7 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -2,16 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGen.h"
 #include "cmAlgorithms.h"
-#include "cmProcessOutput.h"
 #include "cmSystemTools.h"
 
-#include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 #include <algorithm>
 #include <iterator>
 #include <sstream>
-#include <stddef.h>
 
 // - Static variables
 
@@ -21,8 +18,8 @@ std::string const genNameUic = "AutoUic";
 std::string const genNameRcc = "AutoRcc";
 
 std::string const mcNameSingle = "SINGLE";
-std::string const mcNameWrap = "WRAP";
-std::string const mcNameFull = "FULL";
+std::string const mcNameWrapper = "WRAPPER";
+std::string const mcNameMulti = "MULTI";
 
 // - Static functions
 
@@ -80,203 +77,53 @@ void MergeOptions(std::vector<std::string>& baseOpts,
   baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end());
 }
 
-/// @brief Reads the resource files list from from a .qrc file - Qt4 version
-/// @return True if the .qrc file was successfully parsed
-static bool RccListInputsQt4(std::string const& fileName,
-                             std::vector<std::string>& files,
-                             std::string* errorMessage)
-{
-  bool allGood = true;
-  // Read qrc file content into string
-  std::string qrcContents;
-  {
-    cmsys::ifstream ifs(fileName.c_str());
-    if (ifs) {
-      std::ostringstream osst;
-      osst << ifs.rdbuf();
-      qrcContents = osst.str();
-    } else {
-      if (errorMessage != nullptr) {
-        std::string& err = *errorMessage;
-        err = "rcc file not readable:\n  ";
-        err += cmQtAutoGen::Quoted(fileName);
-        err += "\n";
-      }
-      allGood = false;
-    }
-  }
-  if (allGood) {
-    // qrc file directory
-    std::string qrcDir(cmSystemTools::GetFilenamePath(fileName));
-    if (!qrcDir.empty()) {
-      qrcDir += '/';
-    }
-
-    cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
-    cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
-
-    size_t offset = 0;
-    while (fileMatchRegex.find(qrcContents.c_str() + offset)) {
-      std::string qrcEntry = fileMatchRegex.match(1);
-      offset += qrcEntry.size();
-      {
-        fileReplaceRegex.find(qrcEntry);
-        std::string tag = fileReplaceRegex.match(1);
-        qrcEntry = qrcEntry.substr(tag.size());
-      }
-      if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) {
-        qrcEntry = qrcDir + qrcEntry;
-      }
-      files.push_back(qrcEntry);
-    }
-  }
-  return allGood;
-}
-
-/// @brief Reads the resource files list from from a .qrc file - Qt5 version
-/// @return True if the .qrc file was successfully parsed
-static bool RccListInputsQt5(std::string const& rccCommand,
-                             std::vector<std::string> const& rccListOptions,
-                             std::string const& fileName,
-                             std::vector<std::string>& files,
-                             std::string* errorMessage)
-{
-  if (rccCommand.empty()) {
-    cmSystemTools::Error("rcc executable not available");
-    return false;
-  }
-
-  std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
-  std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
-
-  // Run rcc list command
-  bool result = false;
-  int retVal = 0;
-  std::string rccStdOut;
-  std::string rccStdErr;
-  {
-    std::vector<std::string> command;
-    command.push_back(rccCommand);
-    command.insert(command.end(), rccListOptions.begin(),
-                   rccListOptions.end());
-    command.push_back(fileNameName);
-    result = cmSystemTools::RunSingleCommand(
-      command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
-      cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
-  }
-  if (!result || retVal) {
-    if (errorMessage != nullptr) {
-      std::string& err = *errorMessage;
-      err = "rcc list process failed for:\n  ";
-      err += cmQtAutoGen::Quoted(fileName);
-      err += "\n";
-      err += rccStdOut;
-      err += "\n";
-      err += rccStdErr;
-      err += "\n";
-    }
-    return false;
-  }
-
-  // Lambda to strip CR characters
-  auto StripCR = [](std::string& line) {
-    std::string::size_type cr = line.find('\r');
-    if (cr != std::string::npos) {
-      line = line.substr(0, cr);
-    }
-  };
-
-  // Parse rcc std output
-  {
-    std::istringstream ostr(rccStdOut);
-    std::string oline;
-    while (std::getline(ostr, oline)) {
-      StripCR(oline);
-      if (!oline.empty()) {
-        files.push_back(oline);
-      }
-    }
-  }
-  // Parse rcc error output
-  {
-    std::istringstream estr(rccStdErr);
-    std::string eline;
-    while (std::getline(estr, eline)) {
-      StripCR(eline);
-      if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
-        static std::string searchString = "Cannot find file '";
-
-        std::string::size_type pos = eline.find(searchString);
-        if (pos == std::string::npos) {
-          if (errorMessage != nullptr) {
-            std::string& err = *errorMessage;
-            err = "rcc lists unparsable output:\n";
-            err += cmQtAutoGen::Quoted(eline);
-            err += "\n";
-          }
-          return false;
-        }
-        pos += searchString.length();
-        std::string::size_type sz = eline.size() - pos - 1;
-        files.push_back(eline.substr(pos, sz));
-      }
-    }
-  }
-
-  // Convert relative paths to absolute paths
-  for (std::string& resFile : files) {
-    resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile);
-  }
-
-  return true;
-}
-
 // - Class definitions
 
-std::string const cmQtAutoGen::listSep = "<<<S>>>";
+std::string const cmQtAutoGen::ListSep = "<<<S>>>";
+unsigned int const cmQtAutoGen::ParallelMax = 64;
 
-std::string const& cmQtAutoGen::GeneratorName(Generator type)
+std::string const& cmQtAutoGen::GeneratorName(GeneratorT type)
 {
   switch (type) {
-    case Generator::GEN:
+    case GeneratorT::GEN:
       return genNameGen;
-    case Generator::MOC:
+    case GeneratorT::MOC:
       return genNameMoc;
-    case Generator::UIC:
+    case GeneratorT::UIC:
       return genNameUic;
-    case Generator::RCC:
+    case GeneratorT::RCC:
       return genNameRcc;
   }
   return genNameGen;
 }
 
-std::string cmQtAutoGen::GeneratorNameUpper(Generator genType)
+std::string cmQtAutoGen::GeneratorNameUpper(GeneratorT genType)
 {
   return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType));
 }
 
-std::string const& cmQtAutoGen::MultiConfigName(MultiConfig config)
+std::string const& cmQtAutoGen::MultiConfigName(MultiConfigT config)
 {
   switch (config) {
-    case MultiConfig::SINGLE:
+    case MultiConfigT::SINGLE:
       return mcNameSingle;
-    case MultiConfig::WRAP:
-      return mcNameWrap;
-    case MultiConfig::FULL:
-      return mcNameFull;
+    case MultiConfigT::WRAPPER:
+      return mcNameWrapper;
+    case MultiConfigT::MULTI:
+      return mcNameMulti;
   }
-  return mcNameWrap;
+  return mcNameWrapper;
 }
 
-cmQtAutoGen::MultiConfig cmQtAutoGen::MultiConfigType(std::string const& name)
+cmQtAutoGen::MultiConfigT cmQtAutoGen::MultiConfigType(std::string const& name)
 {
   if (name == mcNameSingle) {
-    return MultiConfig::SINGLE;
+    return MultiConfigT::SINGLE;
   }
-  if (name == mcNameFull) {
-    return MultiConfig::FULL;
+  if (name == mcNameMulti) {
+    return MultiConfigT::MULTI;
   }
-  return MultiConfig::WRAP;
+  return MultiConfigT::WRAPPER;
 }
 
 std::string cmQtAutoGen::Quoted(std::string const& text)
@@ -294,6 +141,33 @@ std::string cmQtAutoGen::Quoted(std::string const& text)
   return res;
 }
 
+std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
+{
+  std::string res;
+  for (std::string const& item : command) {
+    if (!res.empty()) {
+      res.push_back(' ');
+    }
+    std::string const cesc = cmQtAutoGen::Quoted(item);
+    if (item.empty() || (cesc.size() > (item.size() + 2)) ||
+        (cesc.find(' ') != std::string::npos)) {
+      res += cesc;
+    } else {
+      res += item;
+    }
+  }
+  return res;
+}
+
+std::string cmQtAutoGen::SubDirPrefix(std::string const& filename)
+{
+  std::string res(cmSystemTools::GetFilenamePath(filename));
+  if (!res.empty()) {
+    res += '/';
+  }
+  return res;
+}
+
 std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename,
                                               std::string const& suffix)
 {
@@ -333,27 +207,79 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
   MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
 }
 
-bool cmQtAutoGen::RccListInputs(std::string const& rccCommand,
-                                std::vector<std::string> const& rccListOptions,
-                                std::string const& fileName,
-                                std::vector<std::string>& files,
-                                std::string* errorMessage)
+void cmQtAutoGen::RccListParseContent(std::string const& content,
+                                      std::vector<std::string>& files)
 {
-  bool allGood = false;
-  if (cmSystemTools::FileExists(fileName.c_str())) {
-    if (rccListOptions.empty()) {
-      allGood = RccListInputsQt4(fileName, files, errorMessage);
-    } else {
-      allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files,
-                                 errorMessage);
+  cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
+  cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
+
+  const char* contentChars = content.c_str();
+  while (fileMatchRegex.find(contentChars)) {
+    std::string const qrcEntry = fileMatchRegex.match(1);
+    contentChars += qrcEntry.size();
+    {
+      fileReplaceRegex.find(qrcEntry);
+      std::string const tag = fileReplaceRegex.match(1);
+      files.push_back(qrcEntry.substr(tag.size()));
     }
-  } else {
-    if (errorMessage != nullptr) {
-      std::string& err = *errorMessage;
-      err = "rcc resource file does not exist:\n  ";
-      err += cmQtAutoGen::Quoted(fileName);
-      err += "\n";
+  }
+}
+
+bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut,
+                                     std::string const& rccStdErr,
+                                     std::vector<std::string>& files,
+                                     std::string& error)
+{
+  // Lambda to strip CR characters
+  auto StripCR = [](std::string& line) {
+    std::string::size_type cr = line.find('\r');
+    if (cr != std::string::npos) {
+      line = line.substr(0, cr);
     }
+  };
+
+  // Parse rcc std output
+  {
+    std::istringstream ostr(rccStdOut);
+    std::string oline;
+    while (std::getline(ostr, oline)) {
+      StripCR(oline);
+      if (!oline.empty()) {
+        files.push_back(oline);
+      }
+    }
+  }
+  // Parse rcc error output
+  {
+    std::istringstream estr(rccStdErr);
+    std::string eline;
+    while (std::getline(estr, eline)) {
+      StripCR(eline);
+      if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
+        static std::string const searchString = "Cannot find file '";
+
+        std::string::size_type pos = eline.find(searchString);
+        if (pos == std::string::npos) {
+          error = "rcc lists unparsable output:\n";
+          error += cmQtAutoGen::Quoted(eline);
+          error += "\n";
+          return false;
+        }
+        pos += searchString.length();
+        std::string::size_type sz = eline.size() - pos - 1;
+        files.push_back(eline.substr(pos, sz));
+      }
+    }
+  }
+
+  return true;
+}
+
+void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir,
+                                         std::vector<std::string>& files)
+{
+  for (std::string& entry : files) {
+    std::string tmp = cmSystemTools::CollapseCombinedPath(qrcFileDir, entry);
+    entry = std::move(tmp);
   }
-  return allGood;
 }
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index e769e93..30ce0f6 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -9,14 +9,18 @@
 #include <vector>
 
 /** \class cmQtAutoGen
- * \brief Class used as namespace for QtAutogen related types  and functions
+ * \brief Common base class for QtAutoGen classes
  */
 class cmQtAutoGen
 {
 public:
-  static std::string const listSep;
+  /// @brief Nested lists separator
+  static std::string const ListSep;
+  /// @brief Maximum number of parallel threads/processes in a generator
+  static unsigned int const ParallelMax;
 
-  enum Generator
+  /// @brief AutoGen generator type
+  enum class GeneratorT
   {
     GEN, // General
     MOC,
@@ -24,27 +28,33 @@ public:
     RCC
   };
 
-  enum MultiConfig
+  /// @brief Multiconfiguration type
+  enum class MultiConfigT
   {
-    SINGLE, // Single configuration
-    WRAP,   // Multi configuration using wrapper files
-    FULL    // Full multi configuration using per config sources
+    SINGLE,  // Single configuration
+    WRAPPER, // Multi configuration using wrapper files
+    MULTI    // Multi configuration using per config sources
   };
 
 public:
   /// @brief Returns the generator name
-  static std::string const& GeneratorName(Generator genType);
+  static std::string const& GeneratorName(GeneratorT genType);
   /// @brief Returns the generator name in upper case
-  static std::string GeneratorNameUpper(Generator genType);
+  static std::string GeneratorNameUpper(GeneratorT genType);
 
   /// @brief Returns the multi configuration name string
-  static std::string const& MultiConfigName(MultiConfig config);
+  static std::string const& MultiConfigName(MultiConfigT config);
   /// @brief Returns the multi configuration type
-  static MultiConfig MultiConfigType(std::string const& name);
+  static MultiConfigT MultiConfigType(std::string const& name);
 
   /// @brief Returns a the string escaped and enclosed in quotes
   static std::string Quoted(std::string const& text);
 
+  static std::string QuotedCommand(std::vector<std::string> const& command);
+
+  /// @brief Returns the parent directory of the file with a "/" suffix
+  static std::string SubDirPrefix(std::string const& filename);
+
   /// @brief Appends the suffix to the filename before the last dot
   static std::string AppendFilenameSuffix(std::string const& filename,
                                           std::string const& suffix);
@@ -59,14 +69,21 @@ public:
                               std::vector<std::string> const& newOpts,
                               bool isQt5);
 
-  /// @brief Reads the resource files list from from a .qrc file
-  /// @arg fileName Must be the absolute path of the .qrc file
-  /// @return True if the rcc file was successfully read
-  static bool RccListInputs(std::string const& rccCommand,
-                            std::vector<std::string> const& rccListOptions,
-                            std::string const& fileName,
-                            std::vector<std::string>& files,
-                            std::string* errorMessage = nullptr);
+  /// @brief Parses the content of a qrc file
+  ///
+  /// Use when rcc does not support the "--list" option
+  static void RccListParseContent(std::string const& content,
+                                  std::vector<std::string>& files);
+
+  /// @brief Parses the output of the "rcc --list ..." command
+  static bool RccListParseOutput(std::string const& rccStdOut,
+                                 std::string const& rccStdErr,
+                                 std::vector<std::string>& files,
+                                 std::string& error);
+
+  /// @brief Converts relative qrc entry paths to full paths
+  static void RccListConvertFullPath(std::string const& qrcFileDir,
+                                     std::vector<std::string>& files);
 };
 
 #endif
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index ee0ddbc..5b2b6d0 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -4,7 +4,6 @@
 #include "cmQtAutoGenerator.h"
 
 #include "cmsys/FStream.hxx"
-#include "cmsys/Terminal.h"
 
 #include "cmAlgorithms.h"
 #include "cmGlobalGenerator.h"
@@ -14,108 +13,55 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-// -- Static functions
-
-static std::string HeadLine(std::string const& title)
-{
-  std::string head = title;
-  head += '\n';
-  head.append(head.size() - 1, '-');
-  head += '\n';
-  return head;
-}
-
-static std::string QuotedCommand(std::vector<std::string> const& command)
-{
-  std::string res;
-  for (std::string const& item : command) {
-    if (!res.empty()) {
-      res.push_back(' ');
-    }
-    std::string const cesc = cmQtAutoGen::Quoted(item);
-    if (item.empty() || (cesc.size() > (item.size() + 2)) ||
-        (cesc.find(' ') != std::string::npos)) {
-      res += cesc;
-    } else {
-      res += item;
-    }
-  }
-  return res;
-}
+#include <algorithm>
 
 // -- Class methods
 
-cmQtAutoGenerator::cmQtAutoGenerator()
-  : Verbose(cmSystemTools::HasEnv("VERBOSE"))
-  , ColorOutput(true)
+void cmQtAutoGenerator::Logger::SetVerbose(bool value)
 {
-  {
-    std::string colorEnv;
-    cmSystemTools::GetEnv("COLOR", colorEnv);
-    if (!colorEnv.empty()) {
-      this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str());
-    }
-  }
+  Verbose_ = value;
 }
 
-bool cmQtAutoGenerator::Run(std::string const& infoFile,
-                            std::string const& config)
+void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
 {
-  // Info settings
-  this->InfoFile = infoFile;
-  cmSystemTools::ConvertToUnixSlashes(this->InfoFile);
-  this->InfoDir = cmSystemTools::GetFilenamePath(infoFile);
-  this->InfoConfig = config;
-
-  cmake cm(cmake::RoleScript);
-  cm.SetHomeOutputDirectory(this->InfoDir);
-  cm.SetHomeDirectory(this->InfoDir);
-  cm.GetCurrentSnapshot().SetDefaultDefinitions();
-  cmGlobalGenerator gg(&cm);
-
-  cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
-  snapshot.GetDirectory().SetCurrentBinary(this->InfoDir);
-  snapshot.GetDirectory().SetCurrentSource(this->InfoDir);
-
-  auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
-  // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
-  // https://gitlab.kitware.com/cmake/cmake/issues/17570
-  makefile->SetPolicyVersion("3.9");
-  gg.SetCurrentMakefile(makefile.get());
-
-  return this->Process(makefile.get());
+  ColorOutput_ = value;
 }
 
-void cmQtAutoGenerator::LogBold(std::string const& message) const
+std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
 {
-  cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
-                                     cmsysTerminal_Color_ForegroundBold,
-                                   message.c_str(), true, this->ColorOutput);
+  std::string head = title;
+  head += '\n';
+  head.append(head.size() - 1, '-');
+  head += '\n';
+  return head;
 }
 
-void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType,
-                                std::string const& message) const
+void cmQtAutoGenerator::Logger::Info(GeneratorT genType,
+                                     std::string const& message)
 {
-  std::string msg = cmQtAutoGen::GeneratorName(genType);
+  std::string msg = GeneratorName(genType);
   msg += ": ";
   msg += message;
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
-  cmSystemTools::Stdout(msg.c_str(), msg.size());
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    cmSystemTools::Stdout(msg.c_str(), msg.size());
+  }
 }
 
-void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType,
-                                   std::string const& message) const
+void cmQtAutoGenerator::Logger::Warning(GeneratorT genType,
+                                        std::string const& message)
 {
-  std::string msg = cmQtAutoGen::GeneratorName(genType);
-  msg += " warning:";
+  std::string msg;
   if (message.find('\n') == std::string::npos) {
     // Single line message
-    msg.push_back(' ');
+    msg += GeneratorName(genType);
+    msg += " warning: ";
   } else {
     // Multi line message
-    msg.push_back('\n');
+    msg += HeadLine(GeneratorName(genType) + " warning");
   }
   // Message
   msg += message;
@@ -123,55 +69,60 @@ void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType,
     msg.push_back('\n');
   }
   msg.push_back('\n');
-  cmSystemTools::Stdout(msg.c_str(), msg.size());
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    cmSystemTools::Stdout(msg.c_str(), msg.size());
+  }
 }
 
-void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType,
-                                       std::string const& filename,
-                                       std::string const& message) const
+void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType,
+                                            std::string const& filename,
+                                            std::string const& message)
 {
   std::string msg = "  ";
-  msg += cmQtAutoGen::Quoted(filename);
+  msg += Quoted(filename);
   msg.push_back('\n');
   // Message
   msg += message;
-  this->LogWarning(genType, msg);
+  Warning(genType, msg);
 }
 
-void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType,
-                                 std::string const& message) const
+void cmQtAutoGenerator::Logger::Error(GeneratorT genType,
+                                      std::string const& message)
 {
   std::string msg;
-  msg.push_back('\n');
-  msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
+  msg += HeadLine(GeneratorName(genType) + " error");
   // Message
   msg += message;
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
   msg.push_back('\n');
-  cmSystemTools::Stderr(msg.c_str(), msg.size());
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    cmSystemTools::Stderr(msg.c_str(), msg.size());
+  }
 }
 
-void cmQtAutoGenerator::LogFileError(cmQtAutoGen::Generator genType,
-                                     std::string const& filename,
-                                     std::string const& message) const
+void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType,
+                                          std::string const& filename,
+                                          std::string const& message)
 {
   std::string emsg = "  ";
-  emsg += cmQtAutoGen::Quoted(filename);
+  emsg += Quoted(filename);
   emsg += '\n';
   // Message
   emsg += message;
-  this->LogError(genType, emsg);
+  Error(genType, emsg);
 }
 
-void cmQtAutoGenerator::LogCommandError(
-  cmQtAutoGen::Generator genType, std::string const& message,
-  std::vector<std::string> const& command, std::string const& output) const
+void cmQtAutoGenerator::Logger::ErrorCommand(
+  GeneratorT genType, std::string const& message,
+  std::vector<std::string> const& command, std::string const& output)
 {
   std::string msg;
   msg.push_back('\n');
-  msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error");
+  msg += HeadLine(GeneratorName(genType) + " subprocess error");
   msg += message;
   if (msg.back() != '\n') {
     msg.push_back('\n');
@@ -189,135 +140,495 @@ void cmQtAutoGenerator::LogCommandError(
     msg.push_back('\n');
   }
   msg.push_back('\n');
-  cmSystemTools::Stderr(msg.c_str(), msg.size());
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    cmSystemTools::Stderr(msg.c_str(), msg.size());
+  }
 }
 
-/**
- * @brief Generates the parent directory of the given file on demand
- * @return True on success
- */
-bool cmQtAutoGenerator::MakeParentDirectory(cmQtAutoGen::Generator genType,
-                                            std::string const& filename) const
+std::string cmQtAutoGenerator::FileSystem::RealPath(
+  std::string const& filename)
 {
-  bool success = true;
-  std::string const dirName = cmSystemTools::GetFilenamePath(filename);
-  if (!dirName.empty()) {
-    if (!cmSystemTools::MakeDirectory(dirName)) {
-      this->LogFileError(genType, filename,
-                         "Could not create parent directory");
-      success = false;
-    }
-  }
-  return success;
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmSystemTools::GetRealPath(filename);
+}
+
+bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename)
+{
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmSystemTools::FileExists(filename);
 }
 
-/**
- * @brief Tests if buildFile is older than sourceFile
- * @return True if buildFile  is older than sourceFile.
- *         False may indicate an error.
- */
-bool cmQtAutoGenerator::FileIsOlderThan(std::string const& buildFile,
-                                        std::string const& sourceFile,
-                                        std::string* error)
+bool cmQtAutoGenerator::FileSystem::FileIsOlderThan(
+  std::string const& buildFile, std::string const& sourceFile,
+  std::string* error)
 {
+  bool res(false);
   int result = 0;
-  if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) {
-    return (result < 0);
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
   }
-  if (error != nullptr) {
-    error->append(
-      "File modification time comparison failed for the files\n  ");
-    error->append(cmQtAutoGen::Quoted(buildFile));
-    error->append("\nand\n  ");
-    error->append(cmQtAutoGen::Quoted(sourceFile));
+  if (res) {
+    res = (result < 0);
+  } else {
+    if (error != nullptr) {
+      error->append(
+        "File modification time comparison failed for the files\n  ");
+      error->append(Quoted(buildFile));
+      error->append("\nand\n  ");
+      error->append(Quoted(sourceFile));
+    }
   }
-  return false;
+  return res;
 }
 
-bool cmQtAutoGenerator::FileRead(std::string& content,
-                                 std::string const& filename,
-                                 std::string* error)
+bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content,
+                                             std::string const& filename,
+                                             std::string* error)
 {
   bool success = false;
-  if (cmSystemTools::FileExists(filename)) {
-    std::size_t const length = cmSystemTools::FileLength(filename);
-    cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
-    if (ifs) {
-      content.resize(length);
-      ifs.read(&content.front(), content.size());
+  {
+    std::lock_guard<std::mutex> lock(Mutex_);
+    if (cmSystemTools::FileExists(filename)) {
+      std::size_t const length = cmSystemTools::FileLength(filename);
+      cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
       if (ifs) {
-        success = true;
-      } else {
-        content.clear();
-        if (error != nullptr) {
-          error->append("Reading from the file failed.");
+        content.resize(length);
+        ifs.read(&content.front(), content.size());
+        if (ifs) {
+          success = true;
+        } else {
+          content.clear();
+          if (error != nullptr) {
+            error->append("Reading from the file failed.");
+          }
         }
+      } else if (error != nullptr) {
+        error->append("Opening the file for reading failed.");
       }
     } else if (error != nullptr) {
-      error->append("Opening the file for reading failed.");
+      error->append("The file does not exist.");
     }
-  } else if (error != nullptr) {
-    error->append("The file does not exist.");
   }
   return success;
 }
 
-bool cmQtAutoGenerator::FileWrite(cmQtAutoGen::Generator genType,
-                                  std::string const& filename,
-                                  std::string const& content)
+bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType,
+                                             std::string& content,
+                                             std::string const& filename)
 {
   std::string error;
+  if (!FileRead(content, filename, &error)) {
+    Log()->ErrorFile(genType, filename, error);
+    return false;
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
+                                              std::string const& content,
+                                              std::string* error)
+{
+  bool success = false;
   // Make sure the parent directory exists
-  if (this->MakeParentDirectory(genType, filename)) {
+  if (MakeParentDirectory(filename)) {
+    std::lock_guard<std::mutex> lock(Mutex_);
     cmsys::ofstream outfile;
     outfile.open(filename.c_str(),
                  (std::ios::out | std::ios::binary | std::ios::trunc));
     if (outfile) {
       outfile << content;
       // Check for write errors
-      if (!outfile.good()) {
-        error = "File writing failed";
+      if (outfile.good()) {
+        success = true;
+      } else {
+        if (error != nullptr) {
+          error->assign("File writing failed");
+        }
       }
     } else {
-      error = "Opening file for writing failed";
+      if (error != nullptr) {
+        error->assign("Opening file for writing failed");
+      }
+    }
+  } else {
+    if (error != nullptr) {
+      error->assign("Could not create parent directory");
     }
   }
-  if (!error.empty()) {
-    this->LogFileError(genType, filename, error);
+  return success;
+}
+
+bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType,
+                                              std::string const& filename,
+                                              std::string const& content)
+{
+  std::string error;
+  if (!FileWrite(filename, content, &error)) {
+    Log()->ErrorFile(genType, filename, error);
     return false;
   }
   return true;
 }
 
-bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
-                                    std::string const& content)
+bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
+                                                std::string const& content)
 {
   bool differs = true;
   {
     std::string oldContents;
-    if (this->FileRead(oldContents, filename)) {
+    if (FileRead(oldContents, filename)) {
       differs = (oldContents != content);
     }
   }
   return differs;
 }
 
-/**
- * @brief Runs a command and returns true on success
- * @return True on success
- */
-bool cmQtAutoGenerator::RunCommand(std::vector<std::string> const& command,
-                                   std::string& output) const
-{
-  // Log command
-  if (this->Verbose) {
-    std::string qcmd = QuotedCommand(command);
-    qcmd.push_back('\n');
-    cmSystemTools::Stdout(qcmd.c_str(), qcmd.size());
-  }
-  // Execute command
-  int retVal = 0;
-  bool res = cmSystemTools::RunSingleCommand(
-    command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE);
-  return (res && (retVal == 0));
+bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename)
+{
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmSystemTools::RemoveFile(filename);
+}
+
+bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename)
+{
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmSystemTools::Touch(filename, false);
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname)
+{
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmSystemTools::MakeDirectory(dirname);
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType,
+                                                  std::string const& dirname)
+{
+  if (!MakeDirectory(dirname)) {
+    Log()->ErrorFile(genType, dirname, "Could not create directory");
+    return false;
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
+  std::string const& filename)
+{
+  bool success = true;
+  std::string const dirName = cmSystemTools::GetFilenamePath(filename);
+  if (!dirName.empty()) {
+    success = MakeDirectory(dirName);
+  }
+  return success;
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
+  GeneratorT genType, std::string const& filename)
+{
+  if (!MakeParentDirectory(filename)) {
+    Log()->ErrorFile(genType, filename, "Could not create parent directory");
+    return false;
+  }
+  return true;
+}
+
+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_.front();
+  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;
+}
+
+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_.front());
+    UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
+    UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
+    UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
+    UVOptions_.stdio = &UVOptionsStdIO_.front();
+
+    // -- Spawn process
+    if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) {
+      Result()->ErrorMessage = "libuv process spawn failed";
+    }
+  }
+  // -- 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()
+  : FileSys_(&Logger_)
+{
+  // Initialize logger
+  Logger_.SetVerbose(cmSystemTools::HasEnv("VERBOSE"));
+  {
+    std::string colorEnv;
+    cmSystemTools::GetEnv("COLOR", colorEnv);
+    if (!colorEnv.empty()) {
+      Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv.c_str()));
+    } else {
+      Logger_.SetColorOutput(true);
+    }
+  }
+
+  // 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());
+}
+
+cmQtAutoGenerator::~cmQtAutoGenerator()
+{
+  // Close libuv loop
+  uv_loop_close(UVLoop());
+}
+
+bool cmQtAutoGenerator::Run(std::string const& infoFile,
+                            std::string const& config)
+{
+  // Info settings
+  InfoFile_ = infoFile;
+  cmSystemTools::ConvertToUnixSlashes(InfoFile_);
+  InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
+  InfoConfig_ = config;
+
+  bool success = false;
+  {
+    cmake cm(cmake::RoleScript);
+    cm.SetHomeOutputDirectory(InfoDir());
+    cm.SetHomeDirectory(InfoDir());
+    cm.GetCurrentSnapshot().SetDefaultDefinitions();
+    cmGlobalGenerator gg(&cm);
+
+    cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
+    snapshot.GetDirectory().SetCurrentBinary(InfoDir());
+    snapshot.GetDirectory().SetCurrentSource(InfoDir());
+
+    auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
+    // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
+    // https://gitlab.kitware.com/cmake/cmake/issues/17570
+    makefile->SetPolicyVersion("3.9");
+    gg.SetCurrentMakefile(makefile.get());
+    success = this->Init(makefile.get());
+  }
+  if (success) {
+    success = this->Process();
+  }
+  return success;
+}
+
+std::string cmQtAutoGenerator::SettingsFind(std::string const& content,
+                                            const char* key)
+{
+  std::string prefix(key);
+  prefix += ':';
+  std::string::size_type pos = content.find(prefix);
+  if (pos != std::string::npos) {
+    pos += prefix.size();
+    if (pos < content.size()) {
+      std::string::size_type posE = content.find('\n', pos);
+      if ((posE != std::string::npos) && (posE != pos)) {
+        return content.substr(pos, posE - pos);
+      }
+    }
+  }
+  return std::string();
 }
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 285340d..6b35234 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -6,71 +6,283 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #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>
 
 class cmMakefile;
 
-class cmQtAutoGenerator
+/// @brief Base class for QtAutoGen gernerators
+class cmQtAutoGenerator : public cmQtAutoGen
 {
   CM_DISABLE_COPY(cmQtAutoGenerator)
 public:
+  // -- Types
+
+  /// @brief Thread safe logging
+  class Logger
+  {
+  public:
+    // -- Verbosity
+    bool Verbose() const { return this->Verbose_; }
+    void SetVerbose(bool value);
+    bool ColorOutput() const { return this->ColorOutput_; }
+    void SetColorOutput(bool value);
+    // -- Log info
+    void Info(GeneratorT genType, std::string const& message);
+    // -- Log warning
+    void Warning(GeneratorT genType, std::string const& message);
+    void WarningFile(GeneratorT genType, std::string const& filename,
+                     std::string const& message);
+    // -- Log error
+    void Error(GeneratorT genType, std::string const& message);
+    void ErrorFile(GeneratorT genType, std::string const& filename,
+                   std::string const& message);
+    void ErrorCommand(GeneratorT genType, std::string const& message,
+                      std::vector<std::string> const& command,
+                      std::string const& output);
+
+  private:
+    static std::string HeadLine(std::string const& title);
+
+  private:
+    std::mutex Mutex_;
+    bool volatile Verbose_ = false;
+    bool volatile ColorOutput_ = false;
+  };
+
+  /// @brief Thread safe file system interface
+  class FileSystem
+  {
+  public:
+    FileSystem(Logger* log)
+      : Log_(log)
+    {
+    }
+
+    Logger* Log() const { return Log_; }
+    std::string RealPath(std::string const& filename);
+    bool FileExists(std::string const& filename);
+    bool FileIsOlderThan(std::string const& buildFile,
+                         std::string const& sourceFile,
+                         std::string* error = nullptr);
+
+    bool FileRead(std::string& content, std::string const& filename,
+                  std::string* error = nullptr);
+    /// @brief Error logging version
+    bool FileRead(GeneratorT genType, std::string& content,
+                  std::string const& filename);
+
+    bool FileWrite(std::string const& filename, std::string const& content,
+                   std::string* error = nullptr);
+    /// @brief Error logging version
+    bool FileWrite(GeneratorT genType, std::string const& filename,
+                   std::string const& content);
+
+    bool FileDiffers(std::string const& filename, std::string const& content);
+
+    bool FileRemove(std::string const& filename);
+    bool Touch(std::string const& filename);
+
+    bool MakeDirectory(std::string const& dirname);
+    /// @brief Error logging version
+    bool MakeDirectory(GeneratorT genType, std::string const& dirname);
+
+    bool MakeParentDirectory(std::string const& filename);
+    /// @brief Error logging version
+    bool MakeParentDirectory(GeneratorT genType, std::string const& filename);
+
+  private:
+    std::mutex Mutex_;
+    Logger* Log_;
+  };
+
+  /// @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;
+    };
+
+    // -- Constructor
+    ReadOnlyProcessT() = default;
+
+    // -- 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_;
+  };
+
+#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) &&                    \
+  UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
+#define CMAKE_UV_SIGNAL_HACK
+  /*
+     libuv does not use SA_RESTART on its signal handler, but C++ streams
+     depend on it for reliable i/o operations.  This RAII helper convinces
+     libuv to install its handler, and then revises the handler to add the
+     SA_RESTART flag.  We use a distinct uv loop that never runs to avoid
+     ever really getting a callback.  libuv may fill the hack loop's signal
+     pipe and then stop writing, but that won't break any real loops.
+   */
+  class cmUVSignalHackRAII
+  {
+    uv_loop_t HackLoop;
+    cm::uv_signal_ptr HackSignal;
+    static void HackCB(uv_signal_t*, int) {}
+  public:
+    cmUVSignalHackRAII()
+    {
+      uv_loop_init(&this->HackLoop);
+      this->HackSignal.init(this->HackLoop);
+      this->HackSignal.start(HackCB, SIGCHLD);
+      struct sigaction hack_sa;
+      sigaction(SIGCHLD, NULL, &hack_sa);
+      if (!(hack_sa.sa_flags & SA_RESTART)) {
+        hack_sa.sa_flags |= SA_RESTART;
+        sigaction(SIGCHLD, &hack_sa, NULL);
+      }
+    }
+    ~cmUVSignalHackRAII()
+    {
+      this->HackSignal.stop();
+      uv_loop_close(&this->HackLoop);
+    }
+  };
+#endif
+
+public:
+  // -- Constructors
   cmQtAutoGenerator();
-  virtual ~cmQtAutoGenerator() = default;
+  virtual ~cmQtAutoGenerator();
+
+  // -- Run
   bool Run(std::string const& infoFile, std::string const& config);
 
-  std::string const& GetInfoFile() const { return InfoFile; }
-  std::string const& GetInfoDir() const { return InfoDir; }
-  std::string const& GetInfoConfig() const { return InfoConfig; }
-  bool GetVerbose() const { return Verbose; }
+  // -- Accessors
+  // Logging
+  Logger& Log() { return Logger_; }
+  // File System
+  FileSystem& FileSys() { return FileSys_; }
+  // InfoFile
+  std::string const& InfoFile() const { return InfoFile_; }
+  std::string const& InfoDir() const { return InfoDir_; }
+  std::string const& InfoConfig() const { return InfoConfig_; }
+  // libuv loop
+  uv_loop_t* UVLoop() { return UVLoop_.get(); }
+  cm::uv_async_ptr& UVRequest() { return UVRequest_; }
 
-protected:
-  // -- Central processing
-  virtual bool Process(cmMakefile* makefile) = 0;
-
-  // -- Log info
-  void LogBold(std::string const& message) const;
-  void LogInfo(cmQtAutoGen::Generator genType,
-               std::string const& message) const;
-  // -- Log warning
-  void LogWarning(cmQtAutoGen::Generator genType,
-                  std::string const& message) const;
-  void LogFileWarning(cmQtAutoGen::Generator genType,
-                      std::string const& filename,
-                      std::string const& message) const;
-  // -- Log error
-  void LogError(cmQtAutoGen::Generator genType,
-                std::string const& message) const;
-  void LogFileError(cmQtAutoGen::Generator genType,
-                    std::string const& filename,
-                    std::string const& message) const;
-  void LogCommandError(cmQtAutoGen::Generator genType,
-                       std::string const& message,
-                       std::vector<std::string> const& command,
-                       std::string const& output) const;
   // -- Utility
-  bool MakeParentDirectory(cmQtAutoGen::Generator genType,
-                           std::string const& filename) const;
-  bool FileIsOlderThan(std::string const& buildFile,
-                       std::string const& sourceFile,
-                       std::string* error = nullptr);
-  bool FileRead(std::string& content, std::string const& filename,
-                std::string* error = nullptr);
-  bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename,
-                 std::string const& content);
-  bool FileDiffers(std::string const& filename, std::string const& content);
-  bool RunCommand(std::vector<std::string> const& command,
-                  std::string& output) const;
+  static std::string SettingsFind(std::string const& content, const char* key);
+
+protected:
+  // -- Abstract processing interface
+  virtual bool Init(cmMakefile* makefile) = 0;
+  virtual bool Process() = 0;
 
 private:
+  // -- Logging
+  Logger Logger_;
+  FileSystem FileSys_;
   // -- Info settings
-  std::string InfoFile;
-  std::string InfoDir;
-  std::string InfoConfig;
-  // -- Settings
-  bool Verbose;
-  bool ColorOutput;
+  std::string InfoFile_;
+  std::string InfoDir_;
+  std::string InfoConfig_;
+// -- 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_;
 };
 
 #endif
diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx
index de0ba4f..29f3653 100644
--- a/Source/cmQtAutoGeneratorInitializer.cxx
+++ b/Source/cmQtAutoGeneratorInitializer.cxx
@@ -24,6 +24,7 @@
 #include "cm_sys_stat.h"
 #include "cmake.h"
 #include "cmsys/FStream.hxx"
+#include "cmsys/SystemInformation.hxx"
 
 #include <algorithm>
 #include <array>
@@ -52,6 +53,20 @@ inline static std::string GetSafeProperty(cmSourceFile const* sf,
   return std::string(SafeString(sf->GetProperty(key)));
 }
 
+static std::size_t GetParallelCPUCount()
+{
+  static std::size_t count = 0;
+  // Detect only on the first call
+  if (count == 0) {
+    cmsys::SystemInformation info;
+    info.RunCPUCheck();
+    count = info.GetNumberOfPhysicalCPU();
+    count = std::max<std::size_t>(count, 1);
+    count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
+  }
+  return count;
+}
+
 static void AddDefinitionEscaped(cmMakefile* makefile, const char* key,
                                  std::string const& value)
 {
@@ -85,12 +100,12 @@ static void AddDefinitionEscaped(
     seplist.push_back(std::move(blist));
   }
   makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake(
-                                 cmJoin(seplist, cmQtAutoGen::listSep))
+                                 cmJoin(seplist, cmQtAutoGen::ListSep))
                                  .c_str());
 }
 
 static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
-                             cmQtAutoGen::Generator genType)
+                             cmQtAutoGen::GeneratorT genType)
 {
   cmSourceGroup* sourceGroup = nullptr;
   // Acquire source group
@@ -101,10 +116,10 @@ static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
       std::array<std::string, 2> props;
       // Use generator specific group name
       switch (genType) {
-        case cmQtAutoGen::MOC:
+        case cmQtAutoGen::GeneratorT::MOC:
           props[0] = "AUTOMOC_SOURCE_GROUP";
           break;
-        case cmQtAutoGen::RCC:
+        case cmQtAutoGen::GeneratorT::RCC:
           props[0] = "AUTORCC_SOURCE_GROUP";
           break;
         default:
@@ -219,7 +234,7 @@ cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer(
   , UicEnabled(uicEnabled)
   , RccEnabled(rccEnabled)
   , QtVersionMajor(qtVersionMajor)
-  , MultiConfig(cmQtAutoGen::WRAP)
+  , MultiConfig(MultiConfigT::WRAPPER)
 {
   this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion(
     target, this->QtVersionMajor);
@@ -240,19 +255,19 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
   // Multi configuration
   {
     if (!globalGen->IsMultiConfig()) {
-      this->MultiConfig = cmQtAutoGen::SINGLE;
+      this->MultiConfig = MultiConfigT::SINGLE;
     }
 
     // FIXME: Xcode does not support per-config sources, yet.
     //        (EXCLUDED_SOURCE_FILE_NAMES)
     // if (globalGen->GetName().find("Xcode") != std::string::npos) {
-    //  return cmQtAutoGen::FULL;
+    //  return MultiConfigT::MULTI;
     //}
 
     // FIXME: Visual Studio does not support per-config sources, yet.
     //        (EXCLUDED_SOURCE_FILE_NAMES)
     // if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
-    //  return cmQtAutoGen::FULL;
+    //  return MultiConfigT::MULTI;
     //}
   }
 
@@ -294,7 +309,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
     this->AutogenInfoFile += "/AutogenInfo.cmake";
 
     this->AutogenSettingsFile = this->DirInfo;
-    this->AutogenSettingsFile += "/AutogenOldSettings.cmake";
+    this->AutogenSettingsFile += "/AutogenOldSettings.txt";
   }
 
   // Autogen target FOLDER property
@@ -324,7 +339,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
   {
     std::string base = this->DirInfo;
     base += "/AutogenOldSettings";
-    if (this->MultiConfig == cmQtAutoGen::SINGLE) {
+    if (this->MultiConfig == MultiConfigT::SINGLE) {
       AddCleanFile(makefile, base.append(".cmake"));
     } else {
       for (std::string const& cfg : this->ConfigsList) {
@@ -340,7 +355,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
   // Add moc compilation to generated files list
   if (this->MocEnabled) {
     std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp";
-    auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC);
+    auto files = this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
     for (std::string& file : files) {
       autogenProvides.push_back(std::move(file));
     }
@@ -349,7 +364,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
   // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
   if (this->MocEnabled || this->UicEnabled) {
     std::string includeDir = this->DirBuild + "/include";
-    if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+    if (this->MultiConfig != MultiConfigT::SINGLE) {
       includeDir += "_$<CONFIG>";
     }
     this->Target->AddIncludeDirectory(includeDir, true);
@@ -560,10 +575,10 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
         msg += "For compatibility, CMake is excluding the GENERATED source "
                "file(s):\n";
         for (const std::string& absFile : generatedHeaders) {
-          msg.append("  ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
+          msg.append("  ").append(Quoted(absFile)).append("\n");
         }
         for (const std::string& absFile : generatedSources) {
-          msg.append("  ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
+          msg.append("  ").append(Quoted(absFile)).append("\n");
         }
         msg += "from processing by ";
         msg += tools;
@@ -630,7 +645,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
           qrc.InfoFile = base;
           qrc.InfoFile += "Info.cmake";
           qrc.SettingsFile = base;
-          qrc.SettingsFile += "Settings.cmake";
+          qrc.SettingsFile += "Settings.txt";
         }
       }
     }
@@ -650,16 +665,16 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
         std::vector<std::string> nameOpts;
         nameOpts.emplace_back("-name");
         nameOpts.emplace_back(std::move(name));
-        cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5);
+        RccMergeOptions(opts, nameOpts, QtV5);
       }
       // Merge file option
-      cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5);
+      RccMergeOptions(opts, qrc.Options, QtV5);
       qrc.Options = std::move(opts);
     }
     for (Qrc& qrc : this->Qrcs) {
       // Register file at target
       std::vector<std::string> const ccOutput =
-        this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC);
+        this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
 
       cmCustomCommandLines commandLines;
       {
@@ -686,8 +701,9 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
             ccName += qrc.PathChecksum;
           }
           std::vector<std::string> ccDepends;
-          // Add the .qrc file to the custom target dependencies
+          // Add the .qrc and info file to the custom target dependencies
           ccDepends.push_back(qrc.QrcFile);
+          ccDepends.push_back(qrc.InfoFile);
 
           cmTarget* autoRccTarget = makefile->AddUtilityCommand(
             ccName, cmMakefile::TargetOrigin::Generator, true,
@@ -709,15 +725,14 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
         {
           std::vector<std::string> ccByproducts;
           std::vector<std::string> ccDepends;
-          // Add the .qrc file to the custom command dependencies
+          // Add the .qrc and info file to the custom command dependencies
           ccDepends.push_back(qrc.QrcFile);
+          ccDepends.push_back(qrc.InfoFile);
 
           // Add the resource files to the dependencies
           {
             std::string error;
-            if (cmQtAutoGen::RccListInputs(this->RccExecutable,
-                                           this->RccListOptions, qrc.QrcFile,
-                                           qrc.Resources, &error)) {
+            if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
               for (std::string const& fileName : qrc.Resources) {
                 // Add resource file to the custom command dependencies
                 ccDepends.push_back(fileName);
@@ -903,8 +918,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
 
   // Basic setup
   AddDefinitionEscaped(makefile, "_multi_config",
-                       cmQtAutoGen::MultiConfigName(this->MultiConfig));
+                       MultiConfigName(this->MultiConfig));
   AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild);
+  {
+    std::string parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL");
+    // Autodetect number of CPUs
+    if (parallel.empty() || (parallel == "AUTO")) {
+      parallel = std::to_string(GetParallelCPUCount());
+    }
+    AddDefinitionEscaped(makefile, "_parallel", parallel);
+  }
 
   if (this->MocEnabled || this->UicEnabled) {
     AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor);
@@ -929,7 +952,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
   // Create info directory on demand
   if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
     std::string emsg = ("Could not create directory: ");
-    emsg += cmQtAutoGen::Quoted(this->DirInfo);
+    emsg += Quoted(this->DirInfo);
     cmSystemTools::Error(emsg.c_str());
   }
 
@@ -951,7 +974,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
     if (!ofs) {
       // File open error
       std::string error = "Internal CMake error when trying to open file: ";
-      error += cmQtAutoGen::Quoted(fileName);
+      error += Quoted(fileName);
       error += " for writing.";
       cmSystemTools::Error(error.c_str());
     }
@@ -984,11 +1007,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
       OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes);
       OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions);
       // Settings files (only require for multi configuration generators)
-      if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+      if (this->MultiConfig != MultiConfigT::SINGLE) {
         std::map<std::string, std::string> settingsFiles;
         for (std::string const& cfg : this->ConfigsList) {
-          settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix(
-            this->AutogenSettingsFile, "_" + cfg);
+          settingsFiles[cfg] =
+            AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg);
         }
         OfsWriteMap("AM_SETTINGS_FILE", settingsFiles);
       }
@@ -1033,11 +1056,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
           OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes);
 
           // Settings files (only require for multi configuration generators)
-          if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+          if (this->MultiConfig != MultiConfigT::SINGLE) {
             std::map<std::string, std::string> settingsFiles;
             for (std::string const& cfg : this->ConfigsList) {
               settingsFiles[cfg] =
-                cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
+                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
             }
             OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles);
           }
@@ -1267,16 +1290,15 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic()
 }
 
 std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
-  std::string const& filename, cmQtAutoGen::Generator genType)
+  std::string const& filename, GeneratorT genType)
 {
   std::vector<std::string> genFiles;
   // Register source file in makefile and source group
-  if (this->MultiConfig != cmQtAutoGen::FULL) {
+  if (this->MultiConfig != MultiConfigT::MULTI) {
     genFiles.push_back(filename);
   } else {
     for (std::string const& cfg : this->ConfigsList) {
-      genFiles.push_back(
-        cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg));
+      genFiles.push_back(AppendFilenameSuffix(filename, "_" + cfg));
     }
   }
   {
@@ -1292,14 +1314,14 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
   }
 
   // Add source file to target
-  if (this->MultiConfig != cmQtAutoGen::FULL) {
+  if (this->MultiConfig != MultiConfigT::MULTI) {
     this->Target->AddSource(filename);
   } else {
     for (std::string const& cfg : this->ConfigsList) {
       std::string src = "$<$<CONFIG:";
       src += cfg;
       src += ">:";
-      src += cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg);
+      src += AppendFilenameSuffix(filename, "_" + cfg);
       src += ">";
       this->Target->AddSource(src);
     }
@@ -1356,3 +1378,84 @@ bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual(
   }
   return false;
 }
+
+/// @brief Reads the resource files list from from a .qrc file
+/// @arg fileName Must be the absolute path of the .qrc file
+/// @return True if the rcc file was successfully read
+bool cmQtAutoGeneratorInitializer::RccListInputs(
+  std::string const& fileName, std::vector<std::string>& files,
+  std::string& error)
+{
+  if (!cmSystemTools::FileExists(fileName.c_str())) {
+    error = "rcc resource file does not exist:\n  ";
+    error += Quoted(fileName);
+    error += "\n";
+    return false;
+  }
+  if (!RccListOptions.empty()) {
+    // Use rcc for file listing
+    if (RccExecutable.empty()) {
+      error = "rcc executable not available";
+      return false;
+    }
+
+    // Run rcc list command in the directory of the qrc file with the pathless
+    // qrc file name argument. This way rcc prints relative paths.
+    // This avoids issues on Windows when the qrc file is in a path that
+    // contains non-ASCII characters.
+    std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
+    std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
+
+    bool result = false;
+    int retVal = 0;
+    std::string rccStdOut;
+    std::string rccStdErr;
+    {
+      std::vector<std::string> cmd;
+      cmd.push_back(RccExecutable);
+      cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end());
+      cmd.push_back(fileNameName);
+      result = cmSystemTools::RunSingleCommand(
+        cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
+        cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
+    }
+    if (!result || retVal) {
+      error = "rcc list process failed for:\n  ";
+      error += Quoted(fileName);
+      error += "\n";
+      error += rccStdOut;
+      error += "\n";
+      error += rccStdErr;
+      error += "\n";
+      return false;
+    }
+    if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
+      return false;
+    }
+  } else {
+    // We can't use rcc for the file listing.
+    // Read the qrc file content into string and parse it.
+    {
+      std::string qrcContents;
+      {
+        cmsys::ifstream ifs(fileName.c_str());
+        if (ifs) {
+          std::ostringstream osst;
+          osst << ifs.rdbuf();
+          qrcContents = osst.str();
+        } else {
+          error = "rcc file not readable:\n  ";
+          error += Quoted(fileName);
+          error += "\n";
+          return false;
+        }
+      }
+      // Parse string content
+      RccListParseContent(qrcContents, files);
+    }
+  }
+
+  // Convert relative paths to absolute paths
+  RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files);
+  return true;
+}
diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGeneratorInitializer.h
index e06e1c4..0567437 100644
--- a/Source/cmQtAutoGeneratorInitializer.h
+++ b/Source/cmQtAutoGeneratorInitializer.h
@@ -13,7 +13,8 @@
 
 class cmGeneratorTarget;
 
-class cmQtAutoGeneratorInitializer
+/// @brief Initializes the QtAutoGen generators
+class cmQtAutoGeneratorInitializer : public cmQtAutoGen
 {
 public:
   static std::string GetQtMajorVersion(cmGeneratorTarget const* target);
@@ -55,11 +56,15 @@ private:
   void SetupCustomTargetsUic();
 
   std::vector<std::string> AddGeneratedSource(std::string const& filename,
-                                              cmQtAutoGen::Generator genType);
+                                              GeneratorT genType);
 
   bool QtVersionGreaterOrEqual(unsigned long requestMajor,
                                unsigned long requestMinor) const;
 
+  bool RccListInputs(std::string const& fileName,
+                     std::vector<std::string>& files,
+                     std::string& errorMessage);
+
 private:
   cmGeneratorTarget* Target;
   bool MocEnabled;
@@ -73,7 +78,7 @@ private:
   // Configurations
   std::string ConfigDefault;
   std::vector<std::string> ConfigsList;
-  cmQtAutoGen::MultiConfig MultiConfig;
+  MultiConfigT MultiConfig;
   // Names
   std::string AutogenTargetName;
   std::string AutogenFolder;
diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx
index bce148e..4b02e0b 100644
--- a/Source/cmQtAutoGeneratorMocUic.cxx
+++ b/Source/cmQtAutoGeneratorMocUic.cxx
@@ -5,16 +5,15 @@
 
 #include <algorithm>
 #include <array>
+#include <functional>
 #include <list>
 #include <memory>
 #include <sstream>
-#include <string.h>
 #include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmMakefile.h"
-#include "cmOutputConverter.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
@@ -22,1712 +21,2004 @@
 #include <unistd.h>
 #endif
 
-// -- Static variables
-
-static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH";
-static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH";
+// -- Class methods
 
-// -- Static functions
+std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
+  std::string const& relativePath) const
+{
+  return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath);
+}
 
-static std::string SubDirPrefix(std::string const& fileName)
+/**
+ * @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
 {
-  std::string res(cmSystemTools::GetFilenamePath(fileName));
-  if (!res.empty()) {
-    res += '/';
+  for (std::string const& ext : HeaderExtensions) {
+    std::string testFilePath(testBasePath);
+    testFilePath.push_back('.');
+    testFilePath += ext;
+    if (FileSys->FileExists(testFilePath)) {
+      header = testFilePath;
+      return true;
+    }
   }
-  return res;
+  return false;
 }
 
-static bool ListContains(std::vector<std::string> const& list,
-                         std::string const& entry)
+bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
+  std::string const& fileName) const
 {
-  return (std::find(list.begin(), list.end(), entry) != list.end());
+  return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
 }
 
-// -- Class methods
+/**
+ * @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();
+}
 
-cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
-  : MultiConfig(cmQtAutoGen::WRAP)
-  , IncludeProjectDirsBefore(false)
-  , QtVersionMajor(4)
-  , MocSettingsChanged(false)
-  , MocPredefsChanged(false)
-  , MocRelaxedMode(false)
-  , UicSettingsChanged(false)
+std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const
 {
-  // Precompile regular expressions
-  this->MocRegExpInclude.compile(
-    "[\n][ \t]*#[ \t]*include[ \t]+"
-    "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
-  this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
-                                 "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+  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;
 }
 
-bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
+std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile(
+  std::string const& sourcePath, std::string const& includeString) const
 {
-  // -- Meta
-  this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+  // Search in vicinity of the source
+  {
+    std::string testPath = sourcePath;
+    testPath += includeString;
+    if (FileSys->FileExists(testPath)) {
+      return FileSys->RealPath(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->RealPath(fullPath);
+    }
+  }
+  // Return empty string
+  return std::string();
+}
 
-  // 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(cmQtAutoGen::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);
+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 subValue(itBeg, itEnd);
-            std::vector<std::string> list;
-            cmSystemTools::ExpandListArgument(subValue, list);
-            lists.push_back(std::move(list));
+            std::string dep = match.match(1);
+            if (!dep.empty()) {
+              depends.emplace(std::move(dep));
+            }
           }
+          contentChars += match.end();
         }
-        pos += length;
-        pos += cmQtAutoGen::listSep.size();
       }
     }
-    return lists;
-  };
-  auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
-    const char* valueConf = nullptr;
-    {
-      std::string keyConf = key;
-      keyConf += '_';
-      keyConf += this->GetInfoConfig();
-      valueConf = makefile->GetDefinition(keyConf);
+  }
+}
+
+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 (valueConf == nullptr) {
-      valueConf = makefile->GetSafeDefinition(key);
+  }
+
+  if (AutoMoc || AutoUic) {
+    std::string error;
+    MetaT meta;
+    if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
+      if (!meta.Content.empty()) {
+        meta.FileDir = SubDirPrefix(FileName);
+        meta.FileBase =
+          cmSystemTools::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(GeneratorT::GEN, FileName,
+                           "The source file is empty");
+      }
+    } else {
+      wrk.LogFileError(GeneratorT::GEN, FileName,
+                       "Could not read the file: " + error);
     }
-    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;
+  }
+}
+
+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;
   };
 
-  // -- Read info file
-  if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
-    this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
-                       "File processing failed");
-    return false;
-  }
+  struct MocInclude
+  {
+    std::string Inc;  // full include string
+    std::string Dir;  // include string directory
+    std::string Base; // include string file base
+  };
 
-  // -- Meta
-  this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
-  this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
-  if (this->ConfigSuffix.empty()) {
-    this->ConfigSuffix = "_";
-    this->ConfigSuffix += this->GetInfoConfig();
-  }
+  // Check if this source file contains a relevant macro
+  std::string const ownMacro = wrk.Moc().FindMacro(meta.Content);
 
-  this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE");
-  if (this->SettingsFile.empty()) {
-    this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
-                       "Settings file name missing");
-    return false;
+  // 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(1);
+        std::string incDir(SubDirPrefix(incString));
+        std::string incBase =
+          cmSystemTools::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();
+      }
+    }
   }
 
-  // - Files and directories
-  this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
-  this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
-  this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
-  this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
-  this->IncludeProjectDirsBefore =
-    InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
-  this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
-  if (this->AutogenBuildDir.empty()) {
-    this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
-                       "Autogen build directory missing");
-    return false;
+  // Check if there is anything to do
+  if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+    return true;
   }
 
-  // - Qt environment
-  if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"),
-                                    &this->QtVersionMajor)) {
-    this->QtVersionMajor = 4;
-  }
-  this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE");
-  this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE");
+  bool ownDotMocIncluded = false;
+  bool ownMocUscIncluded = false;
+  std::deque<JobPre> jobs;
 
-  // - Moc
-  if (this->MocEnabled()) {
-    this->MocSkipList = InfoGetList("AM_MOC_SKIP");
-    this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
-#ifdef _WIN32
-    {
-      std::string const win32("WIN32");
-      if (!ListContains(this->MocDefinitions, win32)) {
-        this->MocDefinitions.push_back(win32);
+  // 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;
       }
-    }
-#endif
-    this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
-    this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
-    this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
-    {
-      std::vector<std::string> const MocMacroNames =
-        InfoGetList("AM_MOC_MACRO_NAMES");
-      for (std::string const& item : MocMacroNames) {
-        this->MocMacroFilters.emplace_back(
-          item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
+      // 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;
       }
-    }
-    {
-      std::vector<std::string> const mocDependFilters =
-        InfoGetList("AM_MOC_DEPEND_FILTERS");
-      // Insert Q_PLUGIN_METADATA dependency filter
-      if (this->QtVersionMajor != 4) {
-        this->MocDependFilterPush("Q_PLUGIN_METADATA",
-                                  "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
-                                  "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+    } 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(GeneratorT::MOC, FileName, emsg);
       }
-      // Insert user defined dependency filters
-      if ((mocDependFilters.size() % 2) == 0) {
-        for (std::vector<std::string>::const_iterator
-               dit = mocDependFilters.begin(),
-               ditEnd = mocDependFilters.end();
-             dit != ditEnd; dit += 2) {
-          if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
-            return false;
+      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(GeneratorT::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(GeneratorT::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(GeneratorT::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(GeneratorT::MOC, FileName, emsg);
         }
       } else {
-        this->LogFileError(
-          cmQtAutoGen::MOC, this->GetInfoFile(),
-          "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+        // 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(GeneratorT::MOC, FileName, emsg);
+        }
         return false;
       }
     }
-    this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
   }
 
-  // - Uic
-  if (this->UicEnabled()) {
-    this->UicSkipList = InfoGetList("AM_UIC_SKIP");
-    this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
-    this->UicTargetOptions = 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() << ")";
-        this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str());
-        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;
+          }
+        }
       }
-      auto fitEnd = sources.cend();
-      auto fit = sources.begin();
-      auto oit = options.begin();
-      while (fit != fitEnd) {
-        this->UicOptions[*fit] = std::move(*oit);
-        ++fit;
-        ++oit;
+      // 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(GeneratorT::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(GeneratorT::MOC, FileName, emsg);
+      }
+      return false;
     }
   }
 
-  // Initialize source file jobs
+  // Convert pre jobs to actual jobs
+  for (JobPre& jobPre : jobs) {
+    JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName,
+                                     std::move(jobPre.IncludeString)));
+    if (jobPre.self) {
+      // Read depdendencies 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(
+      new JobMocT(std::string(FileName), std::string(), std::string()));
+    // Read depdendencies 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().RealPath(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(1))) {
+        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(new 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 =
+    cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
+  searchFile += ".ui";
+  // Collect search paths list
+  std::deque<std::string> testFiles;
   {
-    // Utility lambdas
-    auto AddJob = [this](std::map<std::string, SourceJob>& jobs,
-                         std::string&& sourceFile) {
-      const bool moc = !this->MocSkip(sourceFile);
-      const bool uic = !this->UicSkip(sourceFile);
-      if (moc || uic) {
-        SourceJob& job = jobs[std::move(sourceFile)];
-        job.Moc = moc;
-        job.Uic = uic;
-      }
-    };
+    std::string const searchPath = SubDirPrefix(includeString);
 
-    // Add header jobs
-    for (std::string& hdr : InfoGetList("AM_HEADERS")) {
-      AddJob(this->HeaderJobs, std::move(hdr));
+    std::string searchFileFull;
+    if (!searchPath.empty()) {
+      searchFileFull = searchPath;
+      searchFileFull += searchFile;
     }
-    // Add source jobs
+    // Vicinity of the source
     {
-      std::vector<std::string> sources = InfoGetList("AM_SOURCES");
-      // Add header(s) for the source file
-      for (std::string const& src : sources) {
-        const bool srcMoc = !this->MocSkip(src);
-        const bool srcUic = !this->UicSkip(src);
-        if (!srcMoc && !srcUic) {
-          continue;
-        }
-        // Search for the default header file and a private header
-        std::array<std::string, 2> headerBases;
-        headerBases[0] = SubDirPrefix(src);
-        headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
-        headerBases[1] = headerBases[0];
-        headerBases[1] += "_p";
-        for (std::string const& headerBase : headerBases) {
-          std::string header;
-          if (this->FindHeader(header, headerBase)) {
-            const bool moc = srcMoc && !this->MocSkip(header);
-            const bool uic = srcUic && !this->UicSkip(header);
-            if (moc || uic) {
-              SourceJob& job = this->HeaderJobs[std::move(header)];
-              job.Moc = moc;
-              job.Uic = uic;
-            }
-          }
-        }
+      std::string const sourcePath = meta.FileDir;
+      testFiles.push_back(sourcePath + searchFile);
+      if (!searchPath.empty()) {
+        testFiles.push_back(sourcePath + searchFileFull);
       }
-      // Add Source jobs
-      for (std::string& src : sources) {
-        AddJob(this->SourceJobs, std::move(src));
+    }
+    // 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));
+        }
       }
     }
   }
 
-  // Init derived information
-  // ------------------------
-
-  // Init file path checksum generator
-  this->FilePathChecksum.setupParentDirs(
-    this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
-    this->ProjectBinaryDir);
-
-  // include directory
-  this->AutogenIncludeDir = "include";
-  if (this->MultiConfig != cmQtAutoGen::SINGLE) {
-    this->AutogenIncludeDir += this->ConfigSuffix;
+  // Search for the .ui file!
+  for (std::string const& testFile : testFiles) {
+    if (wrk.FileSys().FileExists(testFile)) {
+      res = wrk.FileSys().RealPath(testFile);
+      break;
+    }
   }
-  this->AutogenIncludeDir += "/";
 
-  // Moc variables
-  if (this->MocEnabled()) {
-    // Mocs compilation file
-    this->MocCompFileRel = "mocs_compilation";
-    if (this->MultiConfig == cmQtAutoGen::FULL) {
-      this->MocCompFileRel += this->ConfigSuffix;
+  // 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";
     }
-    this->MocCompFileRel += ".cpp";
-    this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
-      this->AutogenBuildDir, this->MocCompFileRel);
+    wrk.LogFileError(GeneratorT::UIC, FileName, emsg);
+  }
 
-    // Moc predefs file
-    if (!this->MocPredefsCmd.empty()) {
-      this->MocPredefsFileRel = "moc_predefs";
-      if (this->MultiConfig != cmQtAutoGen::SINGLE) {
-        this->MocPredefsFileRel += this->ConfigSuffix;
+  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(GeneratorT::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(GeneratorT::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(GeneratorT::MOC, result, cmd)) {
+        std::string emsg = "The content generation command for ";
+        emsg += Quoted(wrk.Moc().PredefsFileRel);
+        emsg += " failed.\n";
+        emsg += result.ErrorMessage;
+        wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut);
       }
-      this->MocPredefsFileRel += ".h";
-      this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
-        this->AutogenBuildDir, this->MocPredefsFileRel);
     }
 
-    // Sort include directories on demand
-    if (this->IncludeProjectDirsBefore) {
-      // Move strings to temporary list
-      std::list<std::string> includes;
-      includes.insert(includes.end(), this->MocIncludePaths.begin(),
-                      this->MocIncludePaths.end());
-      this->MocIncludePaths.clear();
-      this->MocIncludePaths.reserve(includes.size());
-      // Append project directories only
-      {
-        std::array<std::string const*, 2> const movePaths = {
-          { &this->ProjectBinaryDir, &this->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())) {
-              this->MocIncludePaths.push_back(path);
-              it = includes.erase(it);
-            } else {
-              ++it;
-            }
-          }
+    // (Re)write predefs file only on demand
+    if (!result.error()) {
+      if (!fileExists ||
+          wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
+        if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs,
+                                    result.StdOut)) {
+          // Success
+        } else {
+          std::string emsg = "Writing ";
+          emsg += Quoted(wrk.Moc().PredefsFileRel);
+          emsg += " failed.";
+          wrk.LogFileError(GeneratorT::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(GeneratorT::MOC, msg);
+        }
+        wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
       }
-      // Append remaining directories
-      this->MocIncludePaths.insert(this->MocIncludePaths.end(),
-                                   includes.begin(), includes.end());
     }
-    // Compose moc includes list
+  }
+}
+
+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().AutogenIncludeDirAbs;
+    BuildFile += IncludeString;
+  } else {
+    std::string buildRel = wrk.Base().FilePathChecksum.getPart(SourceFile);
+    buildRel += '/';
+    buildRel += "moc_";
+    buildRel += cmSystemTools::GetFilenameWithoutLastExtension(SourceFile);
+    if (wrk.Base().MultiConfig != MultiConfigT::SINGLE) {
+      buildRel += wrk.Base().ConfigSuffix;
+    }
+    buildRel += ".cpp";
+    wrk.Gen().ParallelMocAutoRegister(buildRel);
+    BuildFile = wrk.Base().AbsoluteBuildPath(buildRel);
+  }
+
+  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(GeneratorT::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(GeneratorT::MOC, reason);
+    }
+    return true;
+  }
+
+  // Test if the moc_predefs file is newer
+  if (!wrk.Moc().PredefsFileAbs.empty()) {
+    bool isOlder = false;
     {
-      std::set<std::string> frameworkPaths;
-      for (std::string const& path : this->MocIncludePaths) {
-        this->MocIncludes.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;
-          cmSystemTools::SplitPath(path, pathComponents);
-          std::string frameworkPath = cmSystemTools::JoinPath(
-            pathComponents.begin(), pathComponents.end() - 2);
-          frameworkPaths.insert(frameworkPath);
-        }
-      }
-      // Append framework includes
-      for (std::string const& path : frameworkPaths) {
-        this->MocIncludes.push_back("-F");
-        this->MocIncludes.push_back(path);
+      std::string error;
+      isOlder = wrk.FileSys().FileIsOlderThan(
+        BuildFile, wrk.Moc().PredefsFileAbs, &error);
+      if (!isOlder && !error.empty()) {
+        wrk.LogError(GeneratorT::MOC, error);
+        return false;
       }
     }
-    // Setup single list with all options
-    {
-      // Add includes
-      this->MocAllOptions.insert(this->MocAllOptions.end(),
-                                 this->MocIncludes.begin(),
-                                 this->MocIncludes.end());
-      // Add definitions
-      for (std::string const& def : this->MocDefinitions) {
-        this->MocAllOptions.push_back("-D" + def);
+    if (isOlder) {
+      if (verbose) {
+        std::string reason = "Generating ";
+        reason += Quoted(BuildFile);
+        reason += " because it's older than: ";
+        reason += Quoted(wrk.Moc().PredefsFileAbs);
+        wrk.LogInfo(GeneratorT::MOC, reason);
       }
-      // Add options
-      this->MocAllOptions.insert(this->MocAllOptions.end(),
-                                 this->MocOptions.begin(),
-                                 this->MocOptions.end());
+      return true;
     }
   }
 
-  return true;
-}
-
-void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile)
-{
-  // Compose current settings strings
+  // Test if the source file is newer
   {
-    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
-    std::string const sep(" ~~~ ");
-    if (this->MocEnabled()) {
-      std::string str;
-      str += this->MocExecutable;
-      str += sep;
-      str += cmJoin(this->MocAllOptions, ";");
-      str += sep;
-      str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
-      str += sep;
-      str += cmJoin(this->MocPredefsCmd, ";");
-      str += sep;
-      this->SettingsStringMoc = crypt.HashString(str);
+    bool isOlder = false;
+    {
+      std::string error;
+      isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+      if (!isOlder && !error.empty()) {
+        wrk.LogError(GeneratorT::MOC, error);
+        return false;
+      }
     }
-    if (this->UicEnabled()) {
-      std::string str;
-      str += this->UicExecutable;
-      str += sep;
-      str += cmJoin(this->UicTargetOptions, ";");
-      for (const auto& item : this->UicOptions) {
-        str += sep;
-        str += item.first;
-        str += sep;
-        str += cmJoin(item.second, ";");
+    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(GeneratorT::MOC, reason);
       }
-      str += sep;
-      this->SettingsStringUic = crypt.HashString(str);
+      return true;
     }
   }
 
-  // Read old settings
-  if (makefile->ReadListFile(this->SettingsFile.c_str())) {
-    {
-      auto SMatch = [makefile](const char* key, std::string const& value) {
-        return (value == makefile->GetSafeDefinition(key));
-      };
-      if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
-        this->MocSettingsChanged = true;
-      }
-      if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) {
-        this->UicSettingsChanged = 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(GeneratorT::MOC, emsg);
+          return false;
+        }
       }
+      FindDependencies(wrk, content);
     }
-    // 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 (this->SettingsChanged()) {
-      cmSystemTools::RemoveFile(this->SettingsFile);
+    // Check dependency timestamps
+    std::string error;
+    std::string sourceDir = 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(GeneratorT::MOC, reason);
+          }
+          return true;
+        }
+        if (!error.empty()) {
+          wrk.LogError(GeneratorT::MOC, error);
+          return false;
+        }
+      } else {
+        std::string message = "Could not find dependency file ";
+        message += Quoted(depFileRel);
+        wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message);
+      }
     }
-  } else {
-    // If the file could not be read re-generate everythiung.
-    this->MocSettingsChanged = true;
-    this->UicSettingsChanged = true;
   }
+
+  return false;
 }
 
-bool cmQtAutoGeneratorMocUic::SettingsFileWrite()
+void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
 {
-  bool success = true;
-  // Only write if any setting changed
-  if (this->SettingsChanged()) {
-    if (this->GetVerbose()) {
-      this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
-                      cmQtAutoGen::Quoted(this->SettingsFile));
-    }
-    // Compose settings file content
-    std::string settings;
-    {
-      auto SettingAppend = [&settings](const char* key,
-                                       std::string const& value) {
-        settings += "set(";
-        settings += key;
-        settings += " ";
-        settings += cmOutputConverter::EscapeForCMake(value);
-        settings += ")\n";
-      };
-      SettingAppend(SettingsKeyMoc, this->SettingsStringMoc);
-      SettingAppend(SettingsKeyUic, this->SettingsStringUic);
-    }
-    // Write settings file
-    if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
-      this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
-                         "Settings file writing failed");
-      // Remove old settings file to trigger a full rebuild on the next run
-      cmSystemTools::RemoveFile(this->SettingsFile);
-      success = false;
+  // Make sure the parent directory exists
+  if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) {
+    // 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.push_back("--include");
+      cmd.push_back(wrk.Moc().PredefsFileAbs);
+    }
+    cmd.push_back("-o");
+    cmd.push_back(BuildFile);
+    cmd.push_back(SourceFile);
+
+    // Execute moc command
+    ProcessResultT result;
+    if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) {
+      // Moc command success
+      if (IncludeString.empty()) {
+        // Notify the generator that a not included file changed
+        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(GeneratorT::MOC, emsg, cmd, result.StdOut);
+      }
+      wrk.FileSys().FileRemove(BuildFile);
     }
   }
-  return success;
 }
 
-bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile)
+void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
 {
-  // the program goes through all .cpp files to see which moc files are
-  // included. It is not really interesting how the moc file is named, but
-  // what file the moc is created from. Once a moc is included the same moc
-  // may not be included in the mocs_compilation.cpp file anymore.
-  // OTOH if there's a header containing Q_OBJECT where no corresponding
-  // moc file is included anywhere a moc_<filename>.cpp file is created and
-  // included in the mocs_compilation.cpp file.
-
-  if (!this->InitInfoFile(makefile)) {
-    return false;
-  }
-  // Read latest settings
-  this->SettingsFileRead(makefile);
+  // Compute build file name
+  BuildFile = wrk.Base().AutogenIncludeDirAbs;
+  BuildFile += IncludeString;
 
-  // Create AUTOGEN include directory
-  {
-    std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
-      this->AutogenBuildDir, this->AutogenIncludeDir);
-    if (!cmSystemTools::MakeDirectory(incDirAbs)) {
-      this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
-                         "Could not create directory");
-      return false;
-    }
+  if (UpdateRequired(wrk)) {
+    GenerateUic(wrk);
   }
+}
 
-  // Parse source files
-  for (const auto& item : this->SourceJobs) {
-    if (!this->ParseSourceFile(item.first, item.second)) {
-      return false;
-    }
-  }
-  // Parse header files
-  for (const auto& item : this->HeaderJobs) {
-    if (!this->ParseHeaderFile(item.first, item.second)) {
-      return false;
+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(GeneratorT::UIC, reason);
     }
-  }
-  // Read missing dependency information
-  if (!this->ParsePostprocess()) {
-    return false;
+    return true;
   }
 
-  // Generate files
-  if (!this->MocGenerateAll()) {
-    return false;
-  }
-  if (!this->UicGenerateAll()) {
-    return false;
+  // 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(GeneratorT::UIC, reason);
+    }
+    return true;
   }
 
-  if (!this->SettingsFileWrite()) {
-    return false;
+  // 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(GeneratorT::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(GeneratorT::UIC, reason);
+      }
+      return true;
+    }
   }
 
-  return true;
+  return false;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename,
-                                              const SourceJob& job)
+void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
 {
-  std::string contentText;
-  std::string error;
-  bool success = this->FileRead(contentText, absFilename, &error);
-  if (success) {
-    if (!contentText.empty()) {
-      if (job.Moc) {
-        success = this->MocParseSourceContent(absFilename, contentText);
-      }
-      if (success && job.Uic) {
-        success = this->UicParseContent(absFilename, contentText);
+  // Make sure the parent directory exists
+  if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) {
+    // 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.push_back("-o");
+    cmd.push_back(BuildFile);
+    cmd.push_back(SourceFile);
+
+    ProcessResultT result;
+    if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) {
+      // Success
     } else {
-      this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
-                           "The source file is empty");
+      // 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(GeneratorT::UIC, emsg, cmd, result.StdOut);
+      }
+      wrk.FileSys().FileRemove(BuildFile);
     }
-  } else {
-    this->LogFileError(cmQtAutoGen::GEN, absFilename,
-                       "Could not read the source file: " + error);
   }
-  return success;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename,
-                                              const SourceJob& job)
+void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job)
 {
-  std::string contentText;
-  std::string error;
-  bool success = this->FileRead(contentText, absFilename, &error);
-  if (success) {
-    if (!contentText.empty()) {
-      if (job.Moc) {
-        this->MocParseHeaderContent(absFilename, contentText);
-      }
-      if (job.Uic) {
-        success = this->UicParseContent(absFilename, contentText);
-      }
-    } else {
-      this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
-                           "The header file is empty");
-    }
-  } else {
-    this->LogFileError(cmQtAutoGen::GEN, absFilename,
-                       "Could not read the header file: " + error);
+  delete job;
+}
+
+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();
   }
-  return success;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParsePostprocess()
+void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
+  GeneratorT genType, std::string const& message) const
 {
-  bool success = true;
-  // Read missing dependencies
-  for (auto& item : this->MocJobsIncluded) {
-    if (!item->DependsValid) {
-      std::string content;
-      std::string error;
-      if (this->FileRead(content, item->SourceFile, &error)) {
-        this->MocFindDepends(item->SourceFile, content, item->Depends);
-        item->DependsValid = true;
-      } else {
-        std::string emsg = "Could not read file\n  ";
-        emsg += item->SourceFile;
-        emsg += "\nrequired by moc include \"";
-        emsg += item->IncludeString;
-        emsg += "\".\n";
-        emsg += error;
-        this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg);
-        success = false;
-        break;
-      }
+  return Log().Info(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
+  GeneratorT genType, std::string const& message) const
+{
+  return Log().Warning(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
+  GeneratorT genType, std::string const& filename,
+  std::string const& message) const
+{
+  return Log().WarningFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogError(
+  GeneratorT genType, std::string const& message) const
+{
+  Gen().ParallelRegisterJobError();
+  Log().Error(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
+  GeneratorT genType, std::string const& filename,
+  std::string const& message) const
+{
+  Gen().ParallelRegisterJobError();
+  Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
+  GeneratorT 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(
+  GeneratorT 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 success;
+  return !result.error();
 }
 
-/**
- * @brief Tests if the file should be ignored for moc scanning
- * @return True if the file should be ignored
- */
-bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const
+void cmQtAutoGeneratorMocUic::WorkerT::Loop()
 {
-  if (this->MocEnabled()) {
-    // Test if the file name is on the skip list
-    if (!ListContains(this->MocSkipList, absFilename)) {
-      return false;
+  while (true) {
+    Gen().WorkerSwapJob(JobHandle_);
+    if (JobHandle_) {
+      JobHandle_->Process(*this);
+    } else {
+      break;
     }
   }
-  return true;
 }
 
-/**
- * @brief Tests if the C++ content requires moc processing
- * @return True if moc is required
- */
-bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText,
-                                          std::string* macroName)
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
 {
-  for (KeyRegExp& filter : this->MocMacroFilters) {
-    // Run a simple find string operation before the expensive
-    // regular expression check
-    if (contentText.find(filter.Key) != std::string::npos) {
-      if (filter.RegExp.find(contentText)) {
-        // Return macro name on demand
-        if (macroName != nullptr) {
-          *macroName = filter.Key;
-        }
-        return true;
-      }
+  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,
+                          std::bind(&WorkerT::UVProcessFinished, &wrk));
     }
   }
-  return false;
 }
 
-std::string cmQtAutoGeneratorMocUic::MocStringMacros() const
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
 {
-  std::string res;
-  const auto itB = this->MocMacroFilters.cbegin();
-  const auto itE = this->MocMacroFilters.cend();
-  const auto itL = itE - 1;
-  auto itC = itB;
-  for (; itC != itE; ++itC) {
-    // Separator
-    if (itC != itB) {
-      if (itC != itL) {
-        res += ", ";
-      } else {
-        res += " or ";
-      }
+  {
+    std::lock_guard<std::mutex> lock(ProcessMutex_);
+    if (Process_ && Process_->IsFinished()) {
+      Process_.reset();
     }
-    // Key
-    res += itC->Key;
   }
-  return res;
+  // Notify idling thread
+  ProcessCondition_.notify_one();
 }
 
-std::string cmQtAutoGeneratorMocUic::MocStringHeaders(
-  std::string const& fileBase) const
+cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
+  : Base_(&FileSys())
+  , Moc_(&FileSys())
+  , Stage_(StageT::SETTINGS_READ)
+  , JobsRemain_(0)
+  , JobError_(false)
+  , JobThreadsAbort_(false)
+  , MocAutoFileUpdated_(false)
 {
-  std::string res = fileBase;
-  res += ".{";
-  res += cmJoin(this->HeaderExtensions, ",");
-  res += "}";
-  return res;
+  // 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 asynchronous iteration request
+  UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
 }
 
-std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader(
-  std::string const& sourcePath, std::string const& includeBase) const
+cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
 {
-  std::string header;
-  // Search in vicinity of the source
-  if (!this->FindHeader(header, sourcePath + includeBase)) {
-    // Search in include directories
-    for (std::string const& path : this->MocIncludePaths) {
-      std::string fullPath = path;
-      fullPath.push_back('/');
-      fullPath += includeBase;
-      if (this->FindHeader(header, fullPath)) {
-        break;
-      }
-    }
-  }
-  // Sanitize
-  if (!header.empty()) {
-    header = cmSystemTools::GetRealPath(header);
-  }
-  return header;
 }
 
-bool cmQtAutoGeneratorMocUic::MocFindIncludedFile(
-  std::string& absFile, std::string const& sourcePath,
-  std::string const& includeString) const
+bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
 {
-  bool success = false;
-  // Search in vicinity of the source
-  {
-    std::string testPath = sourcePath;
-    testPath += includeString;
-    if (cmSystemTools::FileExists(testPath.c_str())) {
-      absFile = cmSystemTools::GetRealPath(testPath);
-      success = true;
-    }
-  }
-  // Search in include directories
-  if (!success) {
-    for (std::string const& path : this->MocIncludePaths) {
-      std::string fullPath = path;
-      fullPath.push_back('/');
-      fullPath += includeString;
-      if (cmSystemTools::FileExists(fullPath.c_str())) {
-        absFile = cmSystemTools::GetRealPath(fullPath);
-        success = true;
-        break;
+  // -- 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 success;
-}
-
-bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key,
-                                                  std::string const& regExp)
-{
-  std::string error;
-  if (!key.empty()) {
-    if (!regExp.empty()) {
-      KeyRegExp filter;
-      filter.Key = key;
-      if (filter.RegExp.compile(regExp)) {
-        this->MocDependFilters.push_back(std::move(filter));
-      } else {
-        error = "Regular expression compiling failed";
-      }
-    } else {
-      error = "Regular expression is empty";
+    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);
     }
-  } else {
-    error = "Key is empty";
-  }
-  if (!error.empty()) {
-    std::string emsg = "AUTOMOC_DEPEND_FILTERS: ";
-    emsg += error;
-    emsg += "\n";
-    emsg += "  Key:    ";
-    emsg += cmQtAutoGen::Quoted(key);
-    emsg += "\n";
-    emsg += "  RegExp: ";
-    emsg += cmQtAutoGen::Quoted(regExp);
-    emsg += "\n";
-    this->LogError(cmQtAutoGen::MOC, emsg);
+    if (valueConf == nullptr) {
+      valueConf = 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().c_str())) {
+    Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed");
     return false;
   }
-  return true;
-}
 
-void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename,
-                                             std::string const& contentText,
-                                             std::set<std::string>& depends)
-{
-  if (this->MocDependFilters.empty() && contentText.empty()) {
-    return;
-  }
-
-  std::vector<std::string> matches;
-  for (KeyRegExp& filter : this->MocDependFilters) {
-    // Run a simple find string check
-    if (contentText.find(filter.Key) != std::string::npos) {
-      // Run the expensive regular expression check loop
-      const char* contentChars = contentText.c_str();
-      while (filter.RegExp.find(contentChars)) {
-        std::string match = filter.RegExp.match(1);
-        if (!match.empty()) {
-          matches.emplace_back(std::move(match));
-        }
-        contentChars += filter.RegExp.end();
-      }
-    }
-  }
+  // -- Meta
+  Base_.MultiConfig = MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
 
-  if (!matches.empty()) {
-    std::string const sourcePath = SubDirPrefix(absFilename);
-    for (std::string const& match : matches) {
-      // Find the dependency file
-      std::string incFile;
-      if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
-        depends.insert(incFile);
-        if (this->GetVerbose()) {
-          this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n  " +
-                          cmQtAutoGen::Quoted(absFilename) + "\n  " +
-                          cmQtAutoGen::Quoted(incFile));
-        }
-      } else {
-        this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
-                             "Could not find dependency file " +
-                               cmQtAutoGen::Quoted(match));
-      }
-    }
+  Base_.ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
+  if (Base_.ConfigSuffix.empty()) {
+    Base_.ConfigSuffix = "_";
+    Base_.ConfigSuffix += InfoConfig();
   }
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocParseSourceContent(
-  std::string const& absFilename, std::string const& contentText)
-{
-  if (this->GetVerbose()) {
-    this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
-  }
-
-  auto AddJob = [this, &absFilename](std::string const& sourceFile,
-                                     std::string const& includeString,
-                                     std::string const* content) {
-    auto job = cm::make_unique<MocJobIncluded>();
-    job->SourceFile = sourceFile;
-    job->BuildFileRel = this->AutogenIncludeDir;
-    job->BuildFileRel += includeString;
-    job->Includer = absFilename;
-    job->IncludeString = includeString;
-    job->DependsValid = (content != nullptr);
-    if (job->DependsValid) {
-      this->MocFindDepends(sourceFile, *content, job->Depends);
-    }
-    this->MocJobsIncluded.push_back(std::move(job));
-  };
 
-  struct MocInc
-  {
-    std::string Inc;  // full include string
-    std::string Dir;  // include string directory
-    std::string Base; // include string file base
-  };
+  SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+  if (SettingsFile_.empty()) {
+    Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing");
+    return false;
+  }
 
-  // Extract moc includes from file
-  std::vector<MocInc> mocIncsUsc;
-  std::vector<MocInc> mocIncsDot;
   {
-    const char* contentChars = contentText.c_str();
-    if (strstr(contentChars, "moc") != nullptr) {
-      while (this->MocRegExpInclude.find(contentChars)) {
-        std::string incString = this->MocRegExpInclude.match(1);
-        std::string incDir(SubDirPrefix(incString));
-        std::string incBase =
-          cmSystemTools::GetFilenameWithoutLastExtension(incString);
-        if (cmHasLiteralPrefix(incBase, "moc_")) {
-          // moc_<BASE>.cxx
-          // Remove the moc_ part from the base name
-          mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir),
-                                       incBase.substr(4) });
-        } else {
-          // <BASE>.moc
-          mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir),
-                                       std::move(incBase) });
-        }
-        // Forward content pointer
-        contentChars += this->MocRegExpInclude.end();
-      }
+    unsigned long num = Base_.NumThreads;
+    if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL"), &num)) {
+      num = std::max<unsigned long>(num, 1);
+      num = std::min<unsigned long>(num, ParallelMax);
+      Base_.NumThreads = static_cast<unsigned int>(num);
     }
   }
 
-  std::string selfMacroName;
-  const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
-
-  // Check if there is anything to do
-  if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) {
-    return true;
+  // - 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(GeneratorT::GEN, InfoFile(),
+                    "Autogen build directory missing");
+    return false;
   }
 
-  // Scan file variables
-  std::string const scanFileDir = SubDirPrefix(absFilename);
-  std::string const scanFileBase =
-    cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
-  // Relaxed mode variables
-  bool ownDotMocIncluded = false;
-  std::string ownMocUscInclude;
-  std::string ownMocUscHeader;
-
-  // Process moc_<BASE>.cxx includes
-  for (const MocInc& mocInc : mocIncsUsc) {
-    std::string const header =
-      this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
-    if (!header.empty()) {
-      // Check if header is skipped
-      if (this->MocSkip(header)) {
-        continue;
-      }
-      // Register moc job
-      AddJob(header, mocInc.Inc, nullptr);
-      // Store meta information for relaxed mode
-      if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) {
-        ownMocUscInclude = mocInc.Inc;
-        ownMocUscHeader = header;
-      }
-    } else {
-      std::string emsg = "The file includes the moc file ";
-      emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-      emsg += ", but could not find the header ";
-      emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base));
-      this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
-      return false;
+  // - Qt environment
+  {
+    unsigned long qtv = Base_.QtVersionMajor;
+    if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) {
+      Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
     }
   }
 
-  // Process <BASE>.moc includes
-  for (const MocInc& mocInc : mocIncsDot) {
-    const bool ownMoc = (mocInc.Base == scanFileBase);
-    if (this->MocRelaxedMode) {
-      // Relaxed mode
-      if (selfRequiresMoc && ownMoc) {
-        // Add self
-        AddJob(absFilename, mocInc.Inc, &contentText);
-        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 =
-          this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
-        if (!header.empty()) {
-          // Check if header is skipped
-          if (this->MocSkip(header)) {
-            continue;
-          }
-          // Register moc job
-          AddJob(header, mocInc.Inc, nullptr);
-          if (!selfRequiresMoc) {
-            if (ownMoc) {
-              std::string emsg = "The file includes the moc file ";
-              emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-              emsg += ", but does not contain a ";
-              emsg += this->MocStringMacros();
-              emsg += " macro.\nRunning moc on\n  ";
-              emsg += cmQtAutoGen::Quoted(header);
-              emsg += "!\nBetter include ";
-              emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += " for a compatibility with strict mode.\n"
-                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-              this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
+  // - Moc
+  Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+  Moc_.Enabled = !Moc().Executable.empty();
+  if (Moc().Enabled) {
+    {
+      auto lst = InfoGetList("AM_MOC_SKIP");
+      Moc_.SkipList.insert(lst.begin(), lst.end());
+    }
+    Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+#ifdef _WIN32
+    {
+      std::string win32("WIN32");
+      auto itB = Moc().Definitions.cbegin();
+      auto itE = Moc().Definitions.cend();
+      if (std::find(itB, itE, win32) == itE) {
+        Moc_.Definitions.emplace_back(std::move(win32));
+      }
+    }
+#endif
+    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.push_back(KeyExpT());
+            KeyExpT& filter(Moc_.DependFilters.back());
+            if (filter.Exp.compile(exp)) {
+              filter.Key = key;
             } else {
-              std::string emsg = "The file includes the moc file ";
-              emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-              emsg += " instead of ";
-              emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += ".\nRunning moc on\n  ";
-              emsg += cmQtAutoGen::Quoted(header);
-              emsg += "!\nBetter include ";
-              emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
-              emsg += " for compatibility with strict mode.\n"
-                      "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-              this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
+              error = "Regular expression compiling failed";
             }
+          } else {
+            error = "Regular expression is empty";
           }
         } else {
-          std::string emsg = "The file includes the moc file ";
-          emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-          emsg += ", which seems to be the moc file from a different "
-                  "source file. CMake also could not find a matching "
-                  "header.";
-          this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
-          return false;
+          error = "Key is empty";
         }
-      }
-    } else {
-      // Strict mode
-      if (ownMoc) {
-        // Include self
-        AddJob(absFilename, mocInc.Inc, &contentText);
-        ownDotMocIncluded = true;
-        // Accept but issue a warning if moc isn't required
-        if (!selfRequiresMoc) {
-          std::string emsg = "The file includes the moc file ";
-          emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-          emsg += ", but does not contain a ";
-          emsg += this->MocStringMacros();
-          emsg += " macro.";
-          this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
+        if (!error.empty()) {
+          error = ("AUTOMOC_DEPEND_FILTERS: " + error);
+          error += "\n";
+          error += "  Key: ";
+          error += Quoted(key);
+          error += "\n";
+          error += "  Exp: ";
+          error += Quoted(exp);
+          error += "\n";
         }
-      } else {
-        // Don't allow <BASE>.moc include other than self in strict mode
-        std::string emsg = "The file includes the moc file ";
-        emsg += cmQtAutoGen::Quoted(mocInc.Inc);
-        emsg += ", which seems to be the moc file from a different "
-                "source file.\nThis is not supported. Include ";
-        emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
-        emsg += " to run moc on this source file.";
-        this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
-        return false;
-      }
-    }
-  }
+      };
 
-  if (selfRequiresMoc && !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.
-    if (this->MocRelaxedMode && !ownMocUscInclude.empty()) {
-      // This is for KDE4 compatibility:
-      std::string emsg = "The file contains a ";
-      emsg += selfMacroName;
-      emsg += " macro, but does not include ";
-      emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
-      emsg += ". Instead it includes ";
-      emsg += cmQtAutoGen::Quoted(ownMocUscInclude);
-      emsg += ".\nRunning moc on\n  ";
-      emsg += cmQtAutoGen::Quoted(absFilename);
-      emsg += "!\nBetter include ";
-      emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
-      emsg += " for compatibility with strict mode.\n"
-              "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
-      this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
-
-      // Remove own header job
+      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
       {
-        auto itC = this->MocJobsIncluded.begin();
-        auto itE = this->MocJobsIncluded.end();
-        for (; itC != itE; ++itC) {
-          if ((*itC)->SourceFile == ownMocUscHeader) {
-            if ((*itC)->IncludeString == ownMocUscInclude) {
-              this->MocJobsIncluded.erase(itC);
+        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(
+            GeneratorT::MOC, InfoFile(),
+            "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+          return false;
         }
       }
-      // Add own source job
-      AddJob(absFilename, ownMocUscInclude, &contentText);
-    } else {
-      // Otherwise always error out since it will not compile:
-      std::string emsg = "The file contains a ";
-      emsg += selfMacroName;
-      emsg += " macro, but does not include ";
-      emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
-      emsg += "!\nConsider to\n - add #include \"";
-      emsg += scanFileBase;
-      emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
-      this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
-      return false;
+      if (!error.empty()) {
+        Log().ErrorFile(GeneratorT::MOC, InfoFile(), error);
+        return false;
+      }
+    }
+    Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+    // Install moc predefs job
+    if (!Moc().PredefsCmd.empty()) {
+      JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT());
     }
   }
-  return true;
-}
 
-void cmQtAutoGeneratorMocUic::MocParseHeaderContent(
-  std::string const& absFilename, std::string const& contentText)
-{
-  if (this->GetVerbose()) {
-    this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
-  }
-
-  auto const fit =
-    std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(),
-                 [&absFilename](std::unique_ptr<MocJobIncluded> const& job) {
-                   return job->SourceFile == absFilename;
-                 });
-  if (fit == this->MocJobsIncluded.cend()) {
-    if (this->MocRequired(contentText)) {
-      auto job = cm::make_unique<MocJobAuto>();
-      job->SourceFile = absFilename;
-      {
-        std::string& bld = job->BuildFileRel;
-        bld = this->FilePathChecksum.getPart(absFilename);
-        bld += '/';
-        bld += "moc_";
-        bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
-        if (this->MultiConfig != cmQtAutoGen::SINGLE) {
-          bld += this->ConfigSuffix;
-        }
-        bld += ".cpp";
+  // - Uic
+  Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+  Uic_.Enabled = !Uic().Executable.empty();
+  if (Uic().Enabled) {
+    {
+      auto lst = InfoGetList("AM_UIC_SKIP");
+      Uic_.SkipList.insert(lst.begin(), lst.end());
+    }
+    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 missmatch (" << sources.size() << "/"
+            << options.size() << ")";
+        Log().ErrorFile(GeneratorT::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;
       }
-      this->MocFindDepends(absFilename, contentText, job->Depends);
-      this->MocJobsAuto.push_back(std::move(job));
     }
   }
-}
-
-bool cmQtAutoGeneratorMocUic::MocGenerateAll()
-{
-  if (!this->MocEnabled()) {
-    return true;
-  }
 
-  // Look for name collisions in included moc files
+  // Initialize source file jobs
   {
-    bool collision = false;
-    std::map<std::string, std::vector<MocJobIncluded const*>> collisions;
-    for (auto const& job : this->MocJobsIncluded) {
-      auto& list = collisions[job->IncludeString];
-      if (!list.empty()) {
-        collision = true;
-      }
-      list.push_back(job.get());
-    }
-    if (collision) {
-      std::string emsg =
-        "Included moc files with the same name will be "
-        "generated from different sources.\n"
-        "Consider 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"
-        "Include conflicts\n"
-        "-----------------\n";
-      const auto& colls = collisions;
-      for (auto const& coll : colls) {
-        if (coll.second.size() > 1) {
-          emsg += cmQtAutoGen::Quoted(coll.first);
-          emsg += " included in\n";
-          for (const MocJobIncluded* job : coll.second) {
-            emsg += " - ";
-            emsg += cmQtAutoGen::Quoted(job->Includer);
-            emsg += "\n";
-          }
-          emsg += "would be generated from\n";
-          for (const MocJobIncluded* job : coll.second) {
-            emsg += " - ";
-            emsg += cmQtAutoGen::Quoted(job->SourceFile);
-            emsg += "\n";
+    std::hash<std::string> stringHash;
+    std::set<std::size_t> uniqueHeaders;
+
+    // Add header jobs
+    for (std::string& hdr : InfoGetList("AM_HEADERS")) {
+      const bool moc = !Moc().skipped(hdr);
+      const bool uic = !Uic().skipped(hdr);
+      if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) {
+        JobQueues_.Headers.emplace_back(
+          new JobParseT(std::move(hdr), moc, uic, true));
+      }
+    }
+    // Add source jobs
+    {
+      std::vector<std::string> sources = InfoGetList("AM_SOURCES");
+      // Add header(s) for the source file
+      for (std::string& src : sources) {
+        const bool srcMoc = !Moc().skipped(src);
+        const bool srcUic = !Uic().skipped(src);
+        if (!srcMoc && !srcUic) {
+          continue;
+        }
+        // Search for the default header file and a private header
+        {
+          std::array<std::string, 2> bases;
+          bases[0] = SubDirPrefix(src);
+          bases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
+          bases[1] = bases[0];
+          bases[1] += "_p";
+          for (std::string const& headerBase : bases) {
+            std::string header;
+            if (Base().FindHeader(header, headerBase)) {
+              const bool moc = srcMoc && !Moc().skipped(header);
+              const bool uic = srcUic && !Uic().skipped(header);
+              if ((moc || uic) &&
+                  uniqueHeaders.emplace(stringHash(header)).second) {
+                JobQueues_.Headers.emplace_back(
+                  new JobParseT(std::move(header), moc, uic, true));
+              }
+            }
           }
         }
+        // Add source job
+        JobQueues_.Sources.emplace_back(
+          new JobParseT(std::move(src), srcMoc, srcUic));
       }
-      this->LogError(cmQtAutoGen::MOC, emsg);
-      return false;
     }
   }
 
-  // (Re)generate moc_predefs.h on demand
-  if (!this->MocPredefsCmd.empty()) {
-    if (this->MocSettingsChanged ||
-        !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
-      if (this->GetVerbose()) {
-        this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
+  // Init derived information
+  // ------------------------
+
+  // Init file path checksum generator
+  Base_.FilePathChecksum.setupParentDirs(
+    Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+    Base().ProjectBinaryDir);
+
+  // include directory
+  Base_.AutogenIncludeDirRel = "include";
+  if (Base().MultiConfig != MultiConfigT::SINGLE) {
+    Base_.AutogenIncludeDirRel += Base().ConfigSuffix;
+  }
+  Base_.AutogenIncludeDirRel += "/";
+  Base_.AutogenIncludeDirAbs =
+    Base_.AbsoluteBuildPath(Base().AutogenIncludeDirRel);
+
+  // Moc variables
+  if (Moc().Enabled) {
+    // Mocs compilation file
+    Moc_.CompFileRel = "mocs_compilation";
+    if (Base_.MultiConfig == MultiConfigT::MULTI) {
+      Moc_.CompFileRel += Base().ConfigSuffix;
+    }
+    Moc_.CompFileRel += ".cpp";
+    Moc_.CompFileAbs = Base_.AbsoluteBuildPath(Moc().CompFileRel);
+
+    // Moc predefs file
+    if (!Moc_.PredefsCmd.empty()) {
+      Moc_.PredefsFileRel = "moc_predefs";
+      if (Base_.MultiConfig != MultiConfigT::SINGLE) {
+        Moc_.PredefsFileRel += Base().ConfigSuffix;
       }
+      Moc_.PredefsFileRel += ".h";
+      Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
+    }
 
-      std::string output;
+    // 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
       {
-        // Compose command
-        std::vector<std::string> cmd = this->MocPredefsCmd;
-        // Add includes
-        cmd.insert(cmd.end(), this->MocIncludes.begin(),
-                   this->MocIncludes.end());
-        // Add definitions
-        for (std::string const& def : this->MocDefinitions) {
-          cmd.push_back("-D" + def);
-        }
-        // Execute command
-        if (!this->RunCommand(cmd, output)) {
-          this->LogCommandError(cmQtAutoGen::MOC,
-                                "moc_predefs generation failed", cmd, output);
-          return false;
+        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;
+            }
+          }
         }
       }
-
-      // (Re)write predefs file only on demand
-      if (this->FileDiffers(this->MocPredefsFileAbs, output)) {
-        if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
-                            output)) {
-          this->MocPredefsChanged = true;
-        } else {
-          this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
-                             "moc_predefs file writing failed");
-          return false;
-        }
-      } else {
-        // Touch to update the time stamp
-        if (this->GetVerbose()) {
-          this->LogInfo(cmQtAutoGen::MOC,
-                        "Touching moc_predefs " + this->MocPredefsFileRel);
+      // 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;
+          cmSystemTools::SplitPath(path, pathComponents);
+          std::string frameworkPath = cmSystemTools::JoinPath(
+            pathComponents.begin(), pathComponents.end() - 2);
+          frameworkPaths.insert(frameworkPath);
         }
-        cmSystemTools::Touch(this->MocPredefsFileAbs, false);
+      }
+      // Append framework includes
+      for (std::string const& path : frameworkPaths) {
+        Moc_.Includes.push_back("-F");
+        Moc_.Includes.push_back(path);
       }
     }
-
-    // Add moc_predefs.h to moc file dependencies
-    for (auto const& item : this->MocJobsIncluded) {
-      item->Depends.insert(this->MocPredefsFileAbs);
-    }
-    for (auto const& item : this->MocJobsAuto) {
-      item->Depends.insert(this->MocPredefsFileAbs);
+    // 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());
     }
   }
 
-  // Generate moc files that are included by source files.
-  for (auto const& item : this->MocJobsIncluded) {
-    if (!this->MocGenerateFile(*item)) {
-      return false;
-    }
-  }
-  // Generate moc files that are _not_ included by source files.
-  bool autoNameGenerated = false;
-  for (auto const& item : this->MocJobsAuto) {
-    if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
+  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;
+}
 
-  // Compose mocs compilation file content
-  {
-    std::string mocs =
-      "// This file is autogenerated. Changes will be overwritten.\n";
-    if (this->MocJobsAuto.empty()) {
-      // Placeholder content
-      mocs +=
-        "// No files found that require moc or the moc files are included\n";
-      mocs += "enum some_compilers { need_more_than_nothing };\n";
-    } else {
-      // Valid content
-      for (const auto& item : this->MocJobsAuto) {
-        mocs += "#include \"";
-        mocs += item->BuildFileRel;
-        mocs += "\"\n";
-      }
-    }
+void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle)
+{
+  reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage();
+}
 
-    if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
-      // Actually write mocs compilation file
-      if (this->GetVerbose()) {
-        this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
+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);
       }
-      if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
-        this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
-                           "mocs compilation file writing failed");
-        return false;
+      break;
+    case StageT::PARSE_HEADERS:
+      if (ThreadsStartJobs(JobQueues_.Headers)) {
+        SetStage(StageT::MOC_PREDEFS);
       }
-    } else if (autoNameGenerated) {
-      // Only touch mocs compilation file
-      if (this->GetVerbose()) {
-        this->LogInfo(cmQtAutoGen::MOC,
-                      "Touching mocs compilation " + this->MocCompFileRel);
+      break;
+    case StageT::MOC_PREDEFS:
+      if (ThreadsStartJobs(JobQueues_.MocPredefs)) {
+        SetStage(StageT::MOC_PROCESS);
       }
-      cmSystemTools::Touch(this->MocCompFileAbs, false);
-    }
+      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;
   }
-
-  return true;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob,
-                                              bool* generated)
+void cmQtAutoGeneratorMocUic::SetStage(StageT stage)
 {
-  bool success = true;
-
-  std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
-    this->AutogenBuildDir, mocJob.BuildFileRel);
-
-  bool generate = false;
-  std::string generateReason;
-  if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(mocFileAbs);
-      generateReason += " from its source file ";
-      generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
-      generateReason += " because it doesn't exist";
-    }
-    generate = true;
+  if (JobError_) {
+    stage = StageT::FINISH;
   }
-  if (!generate && this->MocSettingsChanged) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(mocFileAbs);
-      generateReason += " from ";
-      generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
-      generateReason += " because the MOC settings changed";
-    }
-    generate = true;
+  // Only allow to increase the stage
+  if (Stage_ < stage) {
+    Stage_ = stage;
+    UVRequest().send();
   }
-  if (!generate && this->MocPredefsChanged) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(mocFileAbs);
-      generateReason += " from ";
-      generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
-      generateReason += " because moc_predefs.h changed";
+}
+
+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);
     }
-    generate = true;
-  }
-  if (!generate) {
-    std::string error;
-    if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
-      if (this->GetVerbose()) {
-        generateReason = "Generating ";
-        generateReason += cmQtAutoGen::Quoted(mocFileAbs);
-        generateReason += " because it's older than its source file ";
-        generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
-      }
-      generate = true;
-    } else {
-      if (!error.empty()) {
-        this->LogError(cmQtAutoGen::MOC, error);
-        success = false;
+    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);
     }
   }
-  if (success && !generate) {
-    // Test if a dependency file is newer
-    std::string error;
-    for (std::string const& depFile : mocJob.Depends) {
-      if (FileIsOlderThan(mocFileAbs, depFile, &error)) {
-        if (this->GetVerbose()) {
-          generateReason = "Generating ";
-          generateReason += cmQtAutoGen::Quoted(mocFileAbs);
-          generateReason += " from ";
-          generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
-          generateReason += " because it is older than ";
-          generateReason += cmQtAutoGen::Quoted(depFile);
+
+  // Read old settings and compare
+  {
+    std::string content;
+    if (FileSys().FileRead(content, SettingsFile_)) {
+      if (Moc().Enabled) {
+        if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
+          Moc_.SettingsChanged = true;
         }
-        generate = true;
-        break;
       }
-      if (!error.empty()) {
-        this->LogError(cmQtAutoGen::MOC, error);
-        success = false;
-        break;
+      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;
       }
     }
   }
+}
 
-  if (generate) {
-    // Log
-    if (this->GetVerbose()) {
-      this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
-      this->LogInfo(cmQtAutoGen::MOC, generateReason);
+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(GeneratorT::GEN,
+                 "Writing settings file " + Quoted(SettingsFile_));
     }
-
-    // Make sure the parent directory exists
-    if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) {
-      // Compose moc command
-      std::vector<std::string> cmd;
-      cmd.push_back(this->MocExecutable);
-      // Add options
-      cmd.insert(cmd.end(), this->MocAllOptions.begin(),
-                 this->MocAllOptions.end());
-      // Add predefs include
-      if (!this->MocPredefsFileAbs.empty()) {
-        cmd.push_back("--include");
-        cmd.push_back(this->MocPredefsFileAbs);
-      }
-      cmd.push_back("-o");
-      cmd.push_back(mocFileAbs);
-      cmd.push_back(mocJob.SourceFile);
-
-      // Execute moc command
-      std::string output;
-      if (this->RunCommand(cmd, output)) {
-        // Success
-        if (generated != nullptr) {
-          *generated = true;
-        }
-      } else {
-        // Moc command failed
-        {
-          std::string emsg = "moc failed for\n  ";
-          emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
-          this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
+    // 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';
         }
-        cmSystemTools::RemoveFile(mocFileAbs);
-        success = false;
-      }
-    } else {
-      // Parent directory creation failed
-      success = false;
+      };
+      SettingAppend("moc", SettingsStringMoc_);
+      SettingAppend("uic", SettingsStringUic_);
+    }
+    // Write settings file
+    if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) {
+      Log().ErrorFile(GeneratorT::GEN, SettingsFile_,
+                      "Settings file writing failed");
+      // Remove old settings file to trigger a full rebuild on the next run
+      FileSys().FileRemove(SettingsFile_);
+      RegisterJobError();
     }
   }
-  return success;
 }
 
-/**
- * @brief Tests if the file name is in the skip list
- */
-bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const
+void cmQtAutoGeneratorMocUic::CreateDirectories()
 {
-  if (this->UicEnabled()) {
-    // Test if the file name is on the skip list
-    if (!ListContains(this->UicSkipList, absFilename)) {
-      return false;
-    }
+  // Create AUTOGEN include directory
+  if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) {
+    RegisterJobError();
   }
-  return true;
 }
 
-bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename,
-                                              std::string const& contentText)
+bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
 {
-  if (this->GetVerbose()) {
-    this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
-  }
+  bool done = false;
+  std::size_t queueSize = queue.size();
 
-  std::vector<std::string> includes;
-  // Extracte includes
+  // Change the active queue
   {
-    const char* contentChars = contentText.c_str();
-    if (strstr(contentChars, "ui_") != nullptr) {
-      while (this->UicRegExpInclude.find(contentChars)) {
-        includes.push_back(this->UicRegExpInclude.match(1));
-        contentChars += this->UicRegExpInclude.end();
+    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;
     }
   }
 
-  for (std::string const& includeString : includes) {
-    std::string uiInputFile;
-    if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) {
-      return false;
-    }
-    // Check if this file should be skipped
-    if (this->UicSkip(uiInputFile)) {
-      continue;
-    }
-    // Check if the job already exists
-    bool jobExists = false;
-    for (const auto& job : this->UicJobs) {
-      if ((job->SourceFile == uiInputFile) &&
-          (job->IncludeString == includeString)) {
-        jobExists = true;
-        break;
+  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();
       }
-    }
-    if (!jobExists) {
-      auto job = cm::make_unique<UicJob>();
-      job->SourceFile = uiInputFile;
-      job->BuildFileRel = this->AutogenIncludeDir;
-      job->BuildFileRel += includeString;
-      job->Includer = absFilename;
-      job->IncludeString = includeString;
-      this->UicJobs.push_back(std::move(job));
     }
   }
 
-  return true;
+  return done;
 }
 
-bool cmQtAutoGeneratorMocUic::UicFindIncludedFile(
-  std::string& absFile, std::string const& sourceFile,
-  std::string const& includeString)
+void cmQtAutoGeneratorMocUic::ThreadsStop()
 {
-  bool success = false;
-  std::string searchFile =
-    cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
-  searchFile += ".ui";
-  // Collect search paths list
-  std::vector<std::string> testFiles;
-  {
-    std::string const searchPath = SubDirPrefix(includeString);
-
-    std::string searchFileFull;
-    if (!searchPath.empty()) {
-      searchFileFull = searchPath;
-      searchFileFull += searchFile;
-    }
-    // Vicinity of the source
+  if (!Workers_.empty()) {
+    // Clear all jobs
     {
-      std::string const sourcePath = SubDirPrefix(sourceFile);
-      testFiles.push_back(sourcePath + searchFile);
-      if (!searchPath.empty()) {
-        testFiles.push_back(sourcePath + searchFileFull);
-      }
-    }
-    // AUTOUIC search paths
-    if (!this->UicSearchPaths.empty()) {
-      for (std::string const& sPath : this->UicSearchPaths) {
-        testFiles.push_back((sPath + "/").append(searchFile));
-      }
-      if (!searchPath.empty()) {
-        for (std::string const& sPath : this->UicSearchPaths) {
-          testFiles.push_back((sPath + "/").append(searchFileFull));
-        }
-      }
-    }
+      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();
   }
+}
 
-  // Search for the .ui file!
-  for (std::string const& testFile : testFiles) {
-    if (cmSystemTools::FileExists(testFile.c_str())) {
-      absFile = cmSystemTools::GetRealPath(testFile);
-      success = true;
-      break;
-    }
-  }
+bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
+{
+  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+  return (JobsRemain_ == 0);
+}
 
-  // Log error
-  if (!success) {
-    std::string emsg = "Could not find ";
-    emsg += cmQtAutoGen::Quoted(searchFile);
-    emsg += " in\n";
-    for (std::string const& testFile : testFiles) {
-      emsg += "  ";
-      emsg += cmQtAutoGen::Quoted(testFile);
-      emsg += "\n";
+void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
+{
+  bool const jobProcessed(jobHandle);
+  if (jobProcessed) {
+    jobHandle.reset(nullptr);
+  }
+  {
+    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();
     }
-    this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
   }
+}
 
-  return success;
+void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
+{
+  std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+  RegisterJobError();
 }
 
-bool cmQtAutoGeneratorMocUic::UicGenerateAll()
+// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoGeneratorMocUic::RegisterJobError()
 {
-  if (!this->UicEnabled()) {
-    return true;
+  JobError_ = true;
+  if (!JobThreadsAbort_) {
+    JobThreadsAbort_ = true;
+    // Clear remaining jobs
+    if (JobsRemain_ != 0) {
+      JobsRemain_ -= JobQueue_.size();
+      JobQueue_.clear();
+    }
   }
+}
 
-  // Look for name collisions in included uic files
-  {
-    bool collision = false;
-    std::map<std::string, std::vector<UicJob const*>> collisions;
-    for (auto const& job : this->UicJobs) {
-      auto& list = collisions[job->IncludeString];
-      if (!list.empty()) {
-        collision = true;
-      }
-      list.push_back(job.get());
-    }
-    if (collision) {
-      std::string emsg =
-        "Included uic files with the same name will be "
-        "generated from different sources.\n"
-        "Consider 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"
-        "Include conflicts\n"
-        "-----------------\n";
-      const auto& colls = collisions;
-      for (auto const& coll : colls) {
-        if (coll.second.size() > 1) {
-          emsg += cmQtAutoGen::Quoted(coll.first);
-          emsg += " included in\n";
-          for (const UicJob* job : coll.second) {
-            emsg += " - ";
-            emsg += cmQtAutoGen::Quoted(job->Includer);
-            emsg += "\n";
-          }
-          emsg += "would be generated from\n";
-          for (const UicJob* job : coll.second) {
-            emsg += " - ";
-            emsg += cmQtAutoGen::Quoted(job->SourceFile);
-            emsg += "\n";
+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 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(GeneratorT::MOC, error);
+              RegisterJobError();
+            }
+            // Do not push this job in since the included moc file already
+            // gets generated by an other job.
+            pushJobHandle = false;
+            break;
           }
         }
       }
-      this->LogError(cmQtAutoGen::UIC, emsg);
-      return false;
+    }
+    // Push job on demand
+    if (pushJobHandle) {
+      JobQueues_.Moc.emplace_back(std::move(jobHandle));
     }
   }
+  return !JobError_;
+}
 
-  // Generate ui header files
-  for (const auto& item : this->UicJobs) {
-    if (!this->UicGenerateFile(*item)) {
-      return false;
+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 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(GeneratorT::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 true;
+  return !JobError_;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob)
+bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
+  std::string const& sourceFile)
 {
-  bool success = true;
+  std::lock_guard<std::mutex> mocLock(JobsMutex_);
+  return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
+}
 
-  std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
-    this->AutogenBuildDir, uicJob.BuildFileRel);
+void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
+  std::string const& mocFile)
+{
+  std::lock_guard<std::mutex> mocLock(JobsMutex_);
+  MocAutoFiles_.emplace(mocFile);
+}
 
-  bool generate = false;
-  std::string generateReason;
-  if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(uicFileAbs);
-      generateReason += " from its source file ";
-      generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
-      generateReason += " because it doesn't exist";
-    }
-    generate = true;
-  }
-  if (!generate && this->UicSettingsChanged) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(uicFileAbs);
-      generateReason += " from ";
-      generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
-      generateReason += " because the UIC settings changed";
-    }
-    generate = true;
-  }
-  if (!generate) {
-    std::string error;
-    if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) {
-      if (this->GetVerbose()) {
-        generateReason = "Generating ";
-        generateReason += cmQtAutoGen::Quoted(uicFileAbs);
-        generateReason += " because it's older than its source file ";
-        generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
-      }
-      generate = true;
-    } else {
-      if (!error.empty()) {
-        this->LogError(cmQtAutoGen::UIC, error);
-        success = false;
-      }
-    }
-  }
-  if (generate) {
-    // Log
-    if (this->GetVerbose()) {
-      this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
-      this->LogInfo(cmQtAutoGen::UIC, generateReason);
-    }
+void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
+{
+  std::lock_guard<std::mutex> mocLock(JobsMutex_);
+  MocAutoFileUpdated_ = true;
+}
 
-    // Make sure the parent directory exists
-    if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) {
-      // Compose uic command
-      std::vector<std::string> cmd;
-      cmd.push_back(this->UicExecutable);
-      {
-        std::vector<std::string> allOpts = this->UicTargetOptions;
-        auto optionIt = this->UicOptions.find(uicJob.SourceFile);
-        if (optionIt != this->UicOptions.end()) {
-          cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
-                                       (this->QtVersionMajor == 5));
+void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
+{
+  std::lock_guard<std::mutex> mocLock(JobsMutex_);
+  if (!JobThreadsAbort_ && Moc().Enabled) {
+    // 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
+        for (std::string const& mocfile : MocAutoFiles_) {
+          content += "#include \"";
+          content += mocfile;
+          content += "\"\n";
         }
-        cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
       }
-      cmd.push_back("-o");
-      cmd.push_back(uicFileAbs);
-      cmd.push_back(uicJob.SourceFile);
 
-      std::string output;
-      if (this->RunCommand(cmd, output)) {
-        // Success
-      } else {
-        // Command failed
-        {
-          std::string emsg = "uic failed for\n  ";
-          emsg += cmQtAutoGen::Quoted(uicJob.SourceFile);
-          emsg += "\nincluded by\n  ";
-          emsg += cmQtAutoGen::Quoted(uicJob.Includer);
-          this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output);
+      std::string const& compRel = Moc().CompFileRel;
+      std::string const& compAbs = Moc().CompFileAbs;
+      if (FileSys().FileDiffers(compAbs, content)) {
+        // Actually write mocs compilation file
+        if (Log().Verbose()) {
+          Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compRel);
         }
-        cmSystemTools::RemoveFile(uicFileAbs);
-        success = false;
+        if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) {
+          Log().ErrorFile(GeneratorT::MOC, compAbs,
+                          "mocs compilation file writing failed");
+          RegisterJobError();
+          return;
+        }
+      } else if (MocAutoFileUpdated_) {
+        // Only touch mocs compilation file
+        if (Log().Verbose()) {
+          Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel);
+        }
+        FileSys().Touch(compAbs);
       }
-    } else {
-      // Parent directory creation failed
-      success = false;
-    }
-  }
-  return success;
-}
-
-/**
- * @brief Tries to find the header file to the given file base path by
- * appending different header extensions
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header,
-                                         std::string const& testBasePath) const
-{
-  for (std::string const& ext : this->HeaderExtensions) {
-    std::string testFilePath(testBasePath);
-    testFilePath.push_back('.');
-    testFilePath += ext;
-    if (cmSystemTools::FileExists(testFilePath.c_str())) {
-      header = testFilePath;
-      return true;
     }
   }
-  return false;
 }
diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h
index d510939..215e25a 100644
--- a/Source/cmQtAutoGeneratorMocUic.h
+++ b/Source/cmQtAutoGeneratorMocUic.h
@@ -8,188 +8,434 @@
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
+#include "cmUVHandlePtr.h"
+#include "cm_uv.h"
 #include "cmsys/RegularExpression.hxx"
 
+#include <algorithm>
+#include <condition_variable>
+#include <cstddef>
+#include <deque>
 #include <map>
 #include <memory> // IWYU pragma: keep
+#include <mutex>
 #include <set>
 #include <string>
+#include <thread>
 #include <vector>
 
 class cmMakefile;
 
+// @brief AUTOMOC and AUTOUIC generator
 class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator
 {
   CM_DISABLE_COPY(cmQtAutoGeneratorMocUic)
 public:
   cmQtAutoGeneratorMocUic();
+  ~cmQtAutoGeneratorMocUic() override;
 
-private:
+public:
   // -- Types
+  class WorkerT;
 
   /// @brief Search key plus regular expression pair
-  struct KeyRegExp
+  ///
+  struct KeyExpT
   {
-    KeyRegExp() = default;
+    KeyExpT() = default;
 
-    KeyRegExp(const char* key, const char* regExp)
+    KeyExpT(const char* key, const char* exp)
       : Key(key)
-      , RegExp(regExp)
+      , Exp(exp)
     {
     }
 
-    KeyRegExp(std::string const& key, std::string const& regExp)
+    KeyExpT(std::string const& key, std::string const& exp)
       : Key(key)
-      , RegExp(regExp)
+      , Exp(exp)
     {
     }
 
     std::string Key;
-    cmsys::RegularExpression RegExp;
+    cmsys::RegularExpression Exp;
   };
 
-  /// @brief Source file job
-  struct SourceJob
+  /// @brief Common settings
+  ///
+  class BaseSettingsT
   {
-    bool Moc = false;
-    bool Uic = false;
+    CM_DISABLE_COPY(BaseSettingsT)
+  public:
+    // -- Volatile methods
+    BaseSettingsT(FileSystem* fileSystem)
+      : MultiConfig(MultiConfigT::WRAPPER)
+      , IncludeProjectDirsBefore(false)
+      , QtVersionMajor(4)
+      , NumThreads(1)
+      , FileSys(fileSystem)
+    {
+    }
+
+    // -- Const methods
+    std::string AbsoluteBuildPath(std::string const& relativePath) const;
+    bool FindHeader(std::string& header,
+                    std::string const& testBasePath) const;
+
+    // -- Attributes
+    // - Config
+    std::string ConfigSuffix;
+    MultiConfigT 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 AutogenIncludeDirRel;
+    std::string AutogenIncludeDirAbs;
+    // - Files
+    cmFilePathChecksum FilePathChecksum;
+    std::vector<std::string> HeaderExtensions;
+    // - File system
+    FileSystem* FileSys;
   };
 
-  /// @brief MOC job
-  struct MocJobAuto
+  /// @brief Moc settings
+  ///
+  class MocSettingsT
   {
-    std::string SourceFile;
-    std::string BuildFileRel;
-    std::set<std::string> Depends;
+    CM_DISABLE_COPY(MocSettingsT)
+  public:
+    MocSettingsT(FileSystem* fileSys)
+      : FileSys(fileSys)
+    {
+    }
+
+    // -- 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 CompFileRel;
+    std::string CompFileAbs;
+    std::string PredefsFileRel;
+    std::string PredefsFileAbs;
+    std::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 MOC job
-  struct MocJobIncluded : MocJobAuto
+  /// @brief Uic settings
+  ///
+  class UicSettingsT
   {
-    bool DependsValid = false;
-    std::string Includer;
+    CM_DISABLE_COPY(UicSettingsT)
+  public:
+    UicSettingsT() = default;
+    // -- Const methods
+    bool skipped(std::string const& fileName) const;
+
+    // -- Attributes
+    bool Enabled = false;
+    bool SettingsChanged = false;
+    std::string Executable;
+    std::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
+  {
+    CM_DISABLE_COPY(JobT)
+  public:
+    JobT() = default;
+    virtual ~JobT() = default;
+    // -- Abstract processing interface
+    virtual void Process(WorkerT& wrk) = 0;
+  };
+
+  /// @brief Deleter for classes derived from Job
+  ///
+  struct JobDeleterT
+  {
+    void operator()(JobT* job);
+  };
+
+  // Job management types
+  typedef std::unique_ptr<JobT, JobDeleterT> 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 const& includerFile,
+            std::string&& includeString)
+      : SourceFile(std::move(sourceFile))
+      , IncluderFile(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 job
-  struct UicJob
+  /// @brief Uic a file job
+  ///
+  class JobUicT : public JobT
   {
+  public:
+    JobUicT(std::string&& sourceFile, std::string const& includerFile,
+            std::string&& includeString)
+      : SourceFile(std::move(sourceFile))
+      , IncluderFile(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 BuildFileRel;
-    std::string Includer;
+    std::string IncluderFile;
     std::string IncludeString;
+    std::string BuildFile;
   };
 
-  // -- Initialization
-  bool InitInfoFile(cmMakefile* makefile);
+  /// @brief Worker Thread
+  ///
+  class WorkerT
+  {
+    CM_DISABLE_COPY(WorkerT)
+  public:
+    WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
+    ~WorkerT();
+
+    // -- 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(); }
 
-  // -- Settings file
-  void SettingsFileRead(cmMakefile* makefile);
-  bool SettingsFileWrite();
-  bool SettingsChanged() const
+    // -- Log info
+    void LogInfo(GeneratorT genType, std::string const& message) const;
+    // -- Log warning
+    void LogWarning(GeneratorT genType, std::string const& message) const;
+    void LogFileWarning(GeneratorT genType, std::string const& filename,
+                        std::string const& message) const;
+    // -- Log error
+    void LogError(GeneratorT genType, std::string const& message) const;
+    void LogFileError(GeneratorT genType, std::string const& filename,
+                      std::string const& message) const;
+    void LogCommandError(GeneratorT genType, std::string const& message,
+                         std::vector<std::string> const& command,
+                         std::string const& output) const;
+
+    // -- External processes
+    /// @brief Verbose logging version
+    bool RunProcess(GeneratorT 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
   {
-    return (this->MocSettingsChanged || this->UicSettingsChanged);
-  }
-
-  // -- Central processing
-  bool Process(cmMakefile* makefile) override;
-
-  // -- Source parsing
-  bool ParseSourceFile(std::string const& absFilename, const SourceJob& job);
-  bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job);
-  bool ParsePostprocess();
-
-  // -- Moc
-  bool MocEnabled() const { return !this->MocExecutable.empty(); }
-  bool MocSkip(std::string const& absFilename) const;
-  bool MocRequired(std::string const& contentText,
-                   std::string* macroName = nullptr);
-  // Moc strings
-  std::string MocStringMacros() const;
-  std::string MocStringHeaders(std::string const& fileBase) const;
-  std::string MocFindIncludedHeader(std::string const& sourcePath,
-                                    std::string const& includeBase) const;
-  bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile,
-                           std::string const& includeString) const;
-  // Moc depends
-  bool MocDependFilterPush(std::string const& key, std::string const& regExp);
-  void MocFindDepends(std::string const& absFilename,
-                      std::string const& contentText,
-                      std::set<std::string>& depends);
-  // Moc
-  bool MocParseSourceContent(std::string const& absFilename,
-                             std::string const& contentText);
-  void MocParseHeaderContent(std::string const& absFilename,
-                             std::string const& contentText);
-
-  bool MocGenerateAll();
-  bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr);
-
-  // -- Uic
-  bool UicEnabled() const { return !this->UicExecutable.empty(); }
-  bool UicSkip(std::string const& absFilename) const;
-  bool UicParseContent(std::string const& fileName,
-                       std::string const& contentText);
-  bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile,
-                           std::string const& includeString);
-  bool UicGenerateAll();
-  bool UicGenerateFile(const UicJob& uicJob);
-
-  // -- Utility
-  bool FindHeader(std::string& header, std::string const& testBasePath) const;
-
-  // -- Meta
-  std::string ConfigSuffix;
-  cmQtAutoGen::MultiConfig MultiConfig;
+    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);
+  void ParallelMocAutoRegister(std::string const& mocFile);
+  void ParallelMocAutoUpdated();
+
+private:
+  // -- 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:
   // -- Settings
-  bool IncludeProjectDirsBefore;
-  std::string SettingsFile;
-  std::string SettingsStringMoc;
-  std::string SettingsStringUic;
-  // -- Directories
-  std::string ProjectSourceDir;
-  std::string ProjectBinaryDir;
-  std::string CurrentSourceDir;
-  std::string CurrentBinaryDir;
-  std::string AutogenBuildDir;
-  std::string AutogenIncludeDir;
-  // -- Qt environment
-  unsigned long QtVersionMajor;
-  std::string MocExecutable;
-  std::string UicExecutable;
-  // -- File lists
-  std::map<std::string, SourceJob> HeaderJobs;
-  std::map<std::string, SourceJob> SourceJobs;
-  std::vector<std::string> HeaderExtensions;
-  cmFilePathChecksum FilePathChecksum;
-  // -- Moc
-  bool MocSettingsChanged;
-  bool MocPredefsChanged;
-  bool MocRelaxedMode;
-  std::string MocCompFileRel;
-  std::string MocCompFileAbs;
-  std::string MocPredefsFileRel;
-  std::string MocPredefsFileAbs;
-  std::vector<std::string> MocSkipList;
-  std::vector<std::string> MocIncludePaths;
-  std::vector<std::string> MocIncludes;
-  std::vector<std::string> MocDefinitions;
-  std::vector<std::string> MocOptions;
-  std::vector<std::string> MocAllOptions;
-  std::vector<std::string> MocPredefsCmd;
-  std::vector<KeyRegExp> MocDependFilters;
-  std::vector<KeyRegExp> MocMacroFilters;
-  cmsys::RegularExpression MocRegExpInclude;
-  std::vector<std::unique_ptr<MocJobIncluded>> MocJobsIncluded;
-  std::vector<std::unique_ptr<MocJobAuto>> MocJobsAuto;
-  // -- Uic
-  bool UicSettingsChanged;
-  std::vector<std::string> UicSkipList;
-  std::vector<std::string> UicTargetOptions;
-  std::map<std::string, std::vector<std::string>> UicOptions;
-  std::vector<std::string> UicSearchPaths;
-  cmsys::RegularExpression UicRegExpInclude;
-  std::vector<std::unique_ptr<UicJob>> UicJobs;
+  BaseSettingsT Base_;
+  MocSettingsT Moc_;
+  UicSettingsT Uic_;
+  // -- Progress
+  StageT Stage_;
+  // -- Job queues
+  std::mutex JobsMutex_;
+  struct
+  {
+    JobQueueT Sources;
+    JobQueueT Headers;
+    JobQueueT MocPredefs;
+    JobQueueT Moc;
+    JobQueueT Uic;
+  } JobQueues_;
+  JobQueueT JobQueue_;
+  std::size_t volatile JobsRemain_;
+  bool volatile JobError_;
+  bool volatile JobThreadsAbort_;
+  std::condition_variable JobsConditionRead_;
+  // -- Moc meta
+  std::set<std::string> MocIncludedStrings_;
+  std::set<std::string> MocIncludedFiles_;
+  std::set<std::string> MocAutoFiles_;
+  bool volatile MocAutoFileUpdated_;
+  // -- 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/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx
index 3c9f1a8..e8ff75a 100644
--- a/Source/cmQtAutoGeneratorRcc.cxx
+++ b/Source/cmQtAutoGeneratorRcc.cxx
@@ -6,22 +6,30 @@
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmMakefile.h"
-#include "cmOutputConverter.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
 
-// -- Static variables
-
-static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH";
+#include <functional>
 
 // -- Class methods
 
 cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc()
-  : MultiConfig(cmQtAutoGen::WRAP)
-  , SettingsChanged(false)
+  : SettingsChanged_(false)
+  , MultiConfig_(MultiConfigT::WRAPPER)
+  , Stage_(StageT::SETTINGS_READ)
+  , Error_(false)
+  , Generate_(false)
+  , BuildFileChanged_(false)
 {
+  // Initialize libuv asynchronous iteration request
+  UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this);
 }
 
-bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
+cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc()
+{
+}
+
+bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile)
 {
   // Utility lambdas
   auto InfoGet = [makefile](const char* key) {
@@ -37,7 +45,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
     {
       std::string keyConf = key;
       keyConf += '_';
-      keyConf += this->GetInfoConfig();
+      keyConf += InfoConfig();
       valueConf = makefile->GetDefinition(keyConf);
     }
     if (valueConf == nullptr) {
@@ -53,79 +61,180 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
   };
 
   // -- Read info file
-  if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "File processing failed");
+  if (!makefile->ReadListFile(InfoFile().c_str())) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed");
     return false;
   }
 
   // -- Meta
-  this->MultiConfig =
-    cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG"));
-  this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX");
-  if (this->ConfigSuffix.empty()) {
-    this->ConfigSuffix = "_";
-    this->ConfigSuffix += this->GetInfoConfig();
+  MultiConfig_ = MultiConfigType(InfoGet("ARCC_MULTI_CONFIG"));
+  ConfigSuffix_ = InfoGetConfig("ARCC_CONFIG_SUFFIX");
+  if (ConfigSuffix_.empty()) {
+    ConfigSuffix_ = "_";
+    ConfigSuffix_ += InfoConfig();
   }
 
-  this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE");
+  SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE");
 
   // - Files and directories
-  this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR");
-  this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR");
-  this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR");
-  this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR");
-  this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR");
+  AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
 
   // - Qt environment
-  this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE");
-  this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS");
+  RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
+  RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
 
   // - Job
-  this->QrcFile = InfoGet("ARCC_SOURCE");
-  this->RccFile = InfoGet("ARCC_OUTPUT");
-  this->Options = InfoGetConfigList("ARCC_OPTIONS");
-  this->Inputs = InfoGetList("ARCC_INPUTS");
+  QrcFile_ = InfoGet("ARCC_SOURCE");
+  QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_);
+  QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_);
+  RccFile_ = InfoGet("ARCC_OUTPUT");
+  Options_ = InfoGetConfigList("ARCC_OPTIONS");
+  Inputs_ = InfoGetList("ARCC_INPUTS");
 
   // - Validity checks
-  if (this->SettingsFile.empty()) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "Settings file name missing");
+  if (SettingsFile_.empty()) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing");
     return false;
   }
-  if (this->AutogenBuildDir.empty()) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "Autogen build directory missing");
+  if (AutogenBuildDir_.empty()) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(),
+                    "Autogen build directory missing");
     return false;
   }
-  if (this->RccExecutable.empty()) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "rcc executable missing");
+  if (RccExecutable_.empty()) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing");
     return false;
   }
-  if (this->QrcFile.empty()) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "rcc input file missing");
+  if (QrcFile_.empty()) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing");
     return false;
   }
-  if (this->RccFile.empty()) {
-    this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
-                       "rcc output file missing");
+  if (RccFile_.empty()) {
+    Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing");
     return false;
   }
 
   // Init derived information
   // ------------------------
 
-  // Init file path checksum generator
-  this->FilePathChecksum.setupParentDirs(
-    this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
-    this->ProjectBinaryDir);
+  // Compute rcc output file name
+  {
+    std::string suffix;
+    switch (MultiConfig_) {
+      case MultiConfigT::SINGLE:
+        break;
+      case MultiConfigT::WRAPPER:
+        suffix = "_CMAKE";
+        suffix += ConfigSuffix_;
+        suffix += "_";
+        break;
+      case MultiConfigT::MULTI:
+        suffix = ConfigSuffix_;
+        break;
+    }
+    RccFileBuild_ = AppendFilenameSuffix(RccFile_, suffix);
+  }
+
+  return true;
+}
 
+bool cmQtAutoGeneratorRcc::Process()
+{
+  // Run libuv event loop
+  UVRequest().send();
+  if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
+    if (Error_) {
+      return false;
+    }
+  } else {
+    return false;
+  }
   return true;
 }
 
-void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
+void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle)
+{
+  reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage();
+}
+
+void cmQtAutoGeneratorRcc::PollStage()
+{
+  switch (Stage_) {
+    // -- Initialize
+    case StageT::SETTINGS_READ:
+      SettingsFileRead();
+      SetStage(StageT::TEST_QRC_RCC_FILES);
+      break;
+
+    // -- Change detection
+    case StageT::TEST_QRC_RCC_FILES:
+      if (TestQrcRccFiles()) {
+        SetStage(StageT::GENERATE);
+      } else {
+        SetStage(StageT::TEST_RESOURCES_READ);
+      }
+      break;
+    case StageT::TEST_RESOURCES_READ:
+      if (TestResourcesRead()) {
+        SetStage(StageT::TEST_RESOURCES);
+      }
+      break;
+    case StageT::TEST_RESOURCES:
+      if (TestResources()) {
+        SetStage(StageT::GENERATE);
+      } else {
+        SetStage(StageT::TEST_INFO_FILE);
+      }
+      break;
+    case StageT::TEST_INFO_FILE:
+      TestInfoFile();
+      SetStage(StageT::GENERATE_WRAPPER);
+      break;
+
+    // -- Generation
+    case StageT::GENERATE:
+      GenerateParentDir();
+      SetStage(StageT::GENERATE_RCC);
+      break;
+    case StageT::GENERATE_RCC:
+      if (GenerateRcc()) {
+        SetStage(StageT::GENERATE_WRAPPER);
+      }
+      break;
+    case StageT::GENERATE_WRAPPER:
+      GenerateWrapper();
+      SetStage(StageT::SETTINGS_WRITE);
+      break;
+
+    // -- Finalize
+    case StageT::SETTINGS_WRITE:
+      SettingsFileWrite();
+      SetStage(StageT::FINISH);
+      break;
+    case StageT::FINISH:
+      // Clear all libuv handles
+      UVRequest().reset();
+      // Set highest END stage manually
+      Stage_ = StageT::END;
+      break;
+    case StageT::END:
+      break;
+  }
+}
+
+void cmQtAutoGeneratorRcc::SetStage(StageT stage)
+{
+  if (Error_) {
+    stage = StageT::FINISH;
+  }
+  // Only allow to increase the stage
+  if (Stage_ < stage) {
+    Stage_ = stage;
+    UVRequest().send();
+  }
+}
+
+void cmQtAutoGeneratorRcc::SettingsFileRead()
 {
   // Compose current settings strings
   {
@@ -133,293 +242,375 @@ void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
     std::string const sep(" ~~~ ");
     {
       std::string str;
-      str += this->RccExecutable;
+      str += RccExecutable_;
       str += sep;
-      str += cmJoin(this->RccListOptions, ";");
+      str += cmJoin(RccListOptions_, ";");
       str += sep;
-      str += this->QrcFile;
+      str += QrcFile_;
       str += sep;
-      str += this->RccFile;
+      str += RccFile_;
       str += sep;
-      str += cmJoin(this->Options, ";");
+      str += cmJoin(Options_, ";");
       str += sep;
-      str += cmJoin(this->Inputs, ";");
+      str += cmJoin(Inputs_, ";");
       str += sep;
-      this->SettingsString = crypt.HashString(str);
+      SettingsString_ = crypt.HashString(str);
     }
   }
 
   // Read old settings
-  if (makefile->ReadListFile(this->SettingsFile.c_str())) {
-    {
-      auto SMatch = [makefile](const char* key, std::string const& value) {
-        return (value == makefile->GetSafeDefinition(key));
-      };
-      if (!SMatch(SettingsKeyRcc, this->SettingsString)) {
-        this->SettingsChanged = true;
+  {
+    std::string content;
+    if (FileSys().FileRead(content, SettingsFile_)) {
+      SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc"));
+      // 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 (SettingsChanged_) {
+        FileSys().FileRemove(SettingsFile_);
       }
+    } else {
+      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 (this->SettingsChanged) {
-      cmSystemTools::RemoveFile(this->SettingsFile);
-    }
-  } else {
-    // If the file could not be read re-generate everythiung.
-    this->SettingsChanged = true;
   }
 }
 
-bool cmQtAutoGeneratorRcc::SettingsFileWrite()
+void cmQtAutoGeneratorRcc::SettingsFileWrite()
 {
-  bool success = true;
   // Only write if any setting changed
-  if (this->SettingsChanged) {
-    if (this->GetVerbose()) {
-      this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " +
-                      cmQtAutoGen::Quoted(this->SettingsFile));
-    }
-    // Compose settings file content
-    std::string settings;
-    {
-      auto SettingAppend = [&settings](const char* key,
-                                       std::string const& value) {
-        settings += "set(";
-        settings += key;
-        settings += " ";
-        settings += cmOutputConverter::EscapeForCMake(value);
-        settings += ")\n";
-      };
-      SettingAppend(SettingsKeyRcc, this->SettingsString);
+  if (SettingsChanged_) {
+    if (Log().Verbose()) {
+      Log().Info(GeneratorT::RCC,
+                 "Writing settings file " + Quoted(SettingsFile_));
     }
     // Write settings file
-    if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) {
-      this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile,
-                         "Settings file writing failed");
+    std::string content = "rcc:";
+    content += SettingsString_;
+    content += '\n';
+    if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) {
+      Log().ErrorFile(GeneratorT::RCC, SettingsFile_,
+                      "Settings file writing failed");
       // Remove old settings file to trigger a full rebuild on the next run
-      cmSystemTools::RemoveFile(this->SettingsFile);
-      success = false;
+      FileSys().FileRemove(SettingsFile_);
+      Error_ = true;
     }
   }
-  return success;
 }
 
-bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile)
+bool cmQtAutoGeneratorRcc::TestQrcRccFiles()
 {
-  // Read info file
-  if (!this->InfoFileRead(makefile)) {
-    return false;
+  // Do basic checks if rcc generation is required
+
+  // Test if the rcc output file exists
+  if (!FileSys().FileExists(RccFileBuild_)) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(RccFileBuild_);
+      reason += " from its source file ";
+      reason += Quoted(QrcFile_);
+      reason += " because it doesn't exist";
+      Log().Info(GeneratorT::RCC, reason);
+    }
+    Generate_ = true;
+    return Generate_;
   }
-  // Read latest settings
-  this->SettingsFileRead(makefile);
-  // Generate rcc file
-  if (!this->RccGenerate()) {
-    return false;
+
+  // Test if the settings changed
+  if (SettingsChanged_) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(RccFileBuild_);
+      reason += " from ";
+      reason += Quoted(QrcFile_);
+      reason += " because the RCC settings changed";
+      Log().Info(GeneratorT::RCC, reason);
+    }
+    Generate_ = true;
+    return Generate_;
   }
-  // Write latest settings
-  if (!this->SettingsFileWrite()) {
-    return false;
+
+  // Test if the rcc output file is older than the .qrc file
+  {
+    bool isOlder = false;
+    {
+      std::string error;
+      isOlder = FileSys().FileIsOlderThan(RccFileBuild_, QrcFile_, &error);
+      if (!error.empty()) {
+        Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+        Error_ = true;
+      }
+    }
+    if (isOlder) {
+      if (Log().Verbose()) {
+        std::string reason = "Generating ";
+        reason += Quoted(RccFileBuild_);
+        reason += " because it is older than ";
+        reason += Quoted(QrcFile_);
+        Log().Info(GeneratorT::RCC, reason);
+      }
+      Generate_ = true;
+    }
   }
-  return true;
+
+  return Generate_;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorRcc::RccGenerate()
+bool cmQtAutoGeneratorRcc::TestResourcesRead()
 {
-  bool success = true;
-  bool rccGenerated = false;
-
-  std::string rccFileAbs;
-  {
-    std::string suffix;
-    switch (this->MultiConfig) {
-      case cmQtAutoGen::SINGLE:
-        break;
-      case cmQtAutoGen::WRAP:
-        suffix = "_CMAKE";
-        suffix += this->ConfigSuffix;
-        suffix += "_";
-        break;
-      case cmQtAutoGen::FULL:
-        suffix = this->ConfigSuffix;
-        break;
-    }
-    rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix);
+  if (!Inputs_.empty()) {
+    // Inputs are known already
+    return true;
   }
-  std::string const rccFileRel = cmSystemTools::RelativePath(
-    this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
 
-  // Check if regeneration is required
-  bool generate = false;
-  std::string generateReason;
-  if (!cmSystemTools::FileExists(this->QrcFile)) {
-    {
-      std::string error = "Could not find the file\n  ";
-      error += cmQtAutoGen::Quoted(this->QrcFile);
-      this->LogError(cmQtAutoGen::RCC, error);
+  if (!RccListOptions_.empty()) {
+    // Start a rcc list process and parse the output
+    if (Process_) {
+      // Process is running already
+      if (Process_->IsFinished()) {
+        // Process is finished
+        if (!ProcessResult_.error()) {
+          // Process success
+          std::string parseError;
+          if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr,
+                                  Inputs_, parseError)) {
+            Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError);
+            Error_ = true;
+          }
+        } else {
+          Log().ErrorFile(GeneratorT::RCC, QrcFile_,
+                          ProcessResult_.ErrorMessage);
+          Error_ = true;
+        }
+        // Clean up
+        Process_.reset();
+        ProcessResult_.reset();
+      } else {
+        // Process is not finished, yet.
+        return false;
+      }
+    } else {
+      // Start a new process
+      // rcc prints relative entry paths when started in the directory of the
+      // qrc file with a pathless qrc file name argument.
+      // This is important because on Windows absolute paths returned by rcc
+      // might contain bad multibyte characters when the qrc file path
+      // contains non-ASCII pcharacters.
+      std::vector<std::string> cmd;
+      cmd.push_back(RccExecutable_);
+      cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end());
+      cmd.push_back(QrcFileName_);
+      // We're done here if the process fails to start
+      return !StartProcess(QrcFileDir_, cmd, false);
     }
-    success = false;
-  }
-  if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(rccFileAbs);
-      generateReason += " from its source file ";
-      generateReason += cmQtAutoGen::Quoted(this->QrcFile);
-      generateReason += " because it doesn't exist";
+  } else {
+    // rcc does not support the --list command.
+    // Read the qrc file content and parse it.
+    std::string qrcContent;
+    if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) {
+      RccListParseContent(qrcContent, Inputs_);
     }
-    generate = true;
   }
-  if (success && !generate && this->SettingsChanged) {
-    if (this->GetVerbose()) {
-      generateReason = "Generating ";
-      generateReason += cmQtAutoGen::Quoted(rccFileAbs);
-      generateReason += " from ";
-      generateReason += cmQtAutoGen::Quoted(this->QrcFile);
-      generateReason += " because the RCC settings changed";
-    }
-    generate = true;
+
+  if (!Inputs_.empty()) {
+    // Convert relative paths to absolute paths
+    RccListConvertFullPath(QrcFileDir_, Inputs_);
+  }
+
+  return true;
+}
+
+bool cmQtAutoGeneratorRcc::TestResources()
+{
+  if (Inputs_.empty()) {
+    return true;
   }
-  if (success && !generate) {
+  {
     std::string error;
-    if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) {
-      if (this->GetVerbose()) {
-        generateReason = "Generating ";
-        generateReason += cmQtAutoGen::Quoted(rccFileAbs);
-        generateReason += " because it is older than ";
-        generateReason += cmQtAutoGen::Quoted(this->QrcFile);
+    for (std::string const& resFile : Inputs_) {
+      // Check if the resource file exists
+      if (!FileSys().FileExists(resFile)) {
+        error = "Could not find the resource file\n  ";
+        error += Quoted(resFile);
+        error += '\n';
+        Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+        Error_ = true;
+        break;
       }
-      generate = true;
-    } else {
+      // Check if the resource file is newer than the build file
+      if (FileSys().FileIsOlderThan(RccFileBuild_, resFile, &error)) {
+        if (Log().Verbose()) {
+          std::string reason = "Generating ";
+          reason += Quoted(RccFileBuild_);
+          reason += " from ";
+          reason += Quoted(QrcFile_);
+          reason += " because it is older than ";
+          reason += Quoted(resFile);
+          Log().Info(GeneratorT::RCC, reason);
+        }
+        Generate_ = true;
+        break;
+      }
+      // Print error and break on demand
       if (!error.empty()) {
-        this->LogError(cmQtAutoGen::RCC, error);
-        success = false;
+        Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+        Error_ = true;
+        break;
       }
     }
   }
-  if (success && !generate) {
-    // Acquire input file list
-    std::vector<std::string> readFiles;
-    std::vector<std::string> const* files = nullptr;
-    if (!this->Inputs.empty()) {
-      files = &this->Inputs;
-    } else {
-      // Read input file list from qrc file
+
+  return Generate_;
+}
+
+void cmQtAutoGeneratorRcc::TestInfoFile()
+{
+  // Test if the rcc output file is older than the info file
+  {
+    bool isOlder = false;
+    {
       std::string error;
-      if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions,
-                                     this->QrcFile, readFiles, &error)) {
-        files = &readFiles;
-      } else {
-        this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error);
-        success = false;
+      isOlder = FileSys().FileIsOlderThan(RccFileBuild_, InfoFile(), &error);
+      if (!error.empty()) {
+        Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+        Error_ = true;
       }
     }
-    // Test if any input file is newer than the build file
-    if (files != nullptr) {
-      std::string error;
-      for (std::string const& resFile : *files) {
-        if (!cmSystemTools::FileExists(resFile.c_str())) {
-          error = "Could not find the file\n  ";
-          error += cmQtAutoGen::Quoted(resFile);
-          error += "\nwhich is listed in\n  ";
-          error += cmQtAutoGen::Quoted(this->QrcFile);
-          break;
-        }
-        if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
-          if (this->GetVerbose()) {
-            generateReason = "Generating ";
-            generateReason += cmQtAutoGen::Quoted(rccFileAbs);
-            generateReason += " from ";
-            generateReason += cmQtAutoGen::Quoted(this->QrcFile);
-            generateReason += " because it is older than ";
-            generateReason += cmQtAutoGen::Quoted(resFile);
-          }
-          generate = true;
-          break;
-        }
-        if (!error.empty()) {
-          break;
-        }
-      }
-      // Print error
-      if (!error.empty()) {
-        this->LogError(cmQtAutoGen::RCC, error);
-        success = false;
+    if (isOlder) {
+      if (Log().Verbose()) {
+        std::string reason = "Touching ";
+        reason += Quoted(RccFileBuild_);
+        reason += " because it is older than ";
+        reason += Quoted(InfoFile());
+        Log().Info(GeneratorT::RCC, reason);
       }
+      // Touch build file
+      FileSys().Touch(RccFileBuild_);
+      BuildFileChanged_ = true;
     }
   }
-  // Regenerate on demand
-  if (generate) {
-    // Log
-    if (this->GetVerbose()) {
-      this->LogBold("Generating RCC source " + rccFileRel);
-      this->LogInfo(cmQtAutoGen::RCC, generateReason);
-    }
+}
 
-    // Make sure the parent directory exists
-    if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) {
-      // Compose rcc command
-      std::vector<std::string> cmd;
-      cmd.push_back(this->RccExecutable);
-      cmd.insert(cmd.end(), this->Options.begin(), this->Options.end());
-      cmd.push_back("-o");
-      cmd.push_back(rccFileAbs);
-      cmd.push_back(this->QrcFile);
-
-      std::string output;
-      if (this->RunCommand(cmd, output)) {
-        // Success
-        rccGenerated = true;
+void cmQtAutoGeneratorRcc::GenerateParentDir()
+{
+  // Make sure the parent directory exists
+  if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileBuild_)) {
+    Error_ = true;
+  }
+}
+
+/**
+ * @return True when finished
+ */
+bool cmQtAutoGeneratorRcc::GenerateRcc()
+{
+  if (!Generate_) {
+    // Nothing to do
+    return true;
+  }
+
+  if (Process_) {
+    // Process is running already
+    if (Process_->IsFinished()) {
+      // Process is finished
+      if (!ProcessResult_.error()) {
+        // Process success
+        BuildFileChanged_ = true;
       } else {
+        // Process failed
         {
-          std::string emsg = "rcc failed for\n  ";
-          emsg += cmQtAutoGen::Quoted(this->QrcFile);
-          this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
+          std::string emsg = "The rcc process failed to compile\n  ";
+          emsg += Quoted(QrcFile_);
+          emsg += "\ninto\n  ";
+          emsg += Quoted(RccFileBuild_);
+          if (ProcessResult_.error()) {
+            emsg += "\n";
+            emsg += ProcessResult_.ErrorMessage;
+          }
+          Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command,
+                             ProcessResult_.StdOut);
         }
-        cmSystemTools::RemoveFile(rccFileAbs);
-        success = false;
+        FileSys().FileRemove(RccFileBuild_);
+        Error_ = true;
       }
+      // Clean up
+      Process_.reset();
+      ProcessResult_.reset();
     } else {
-      // Parent directory creation failed
-      success = false;
+      // Process is not finished, yet.
+      return false;
     }
+  } else {
+    // Start a rcc process
+    std::vector<std::string> cmd;
+    cmd.push_back(RccExecutable_);
+    cmd.insert(cmd.end(), Options_.begin(), Options_.end());
+    cmd.push_back("-o");
+    cmd.push_back(RccFileBuild_);
+    cmd.push_back(QrcFile_);
+    // We're done here if the process fails to start
+    return !StartProcess(AutogenBuildDir_, cmd, true);
   }
 
+  return true;
+}
+
+void cmQtAutoGeneratorRcc::GenerateWrapper()
+{
   // Generate a wrapper source file on demand
-  if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) {
+  if (MultiConfig_ == MultiConfigT::WRAPPER) {
     // Wrapper file name
-    std::string const& wrapperFileAbs = this->RccFile;
-    std::string const wrapperFileRel = cmSystemTools::RelativePath(
-      this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
+    std::string const& wrapperAbs = RccFile_;
     // Wrapper file content
     std::string content = "// This is an autogenerated configuration "
                           "wrapper file. Changes will be overwritten.\n"
                           "#include \"";
-    content += cmSystemTools::GetFilenameName(rccFileRel);
+    content += cmSystemTools::GetFilenameName(RccFileBuild_);
     content += "\"\n";
     // Write content to file
-    if (this->FileDiffers(wrapperFileAbs, content)) {
+    if (FileSys().FileDiffers(wrapperAbs, content)) {
       // Write new wrapper file
-      if (this->GetVerbose()) {
-        this->LogBold("Generating RCC wrapper " + wrapperFileRel);
+      if (Log().Verbose()) {
+        Log().Info(GeneratorT::RCC, "Generating RCC wrapper " + wrapperAbs);
       }
-      if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
-        this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
-                           "rcc wrapper file writing failed");
-        success = false;
+      if (!FileSys().FileWrite(GeneratorT::RCC, wrapperAbs, content)) {
+        Log().ErrorFile(GeneratorT::RCC, wrapperAbs,
+                        "RCC wrapper file writing failed");
+        Error_ = true;
       }
-    } else if (rccGenerated) {
+    } else if (BuildFileChanged_) {
       // Just touch the wrapper file
-      if (this->GetVerbose()) {
-        this->LogInfo(cmQtAutoGen::RCC,
-                      "Touching RCC wrapper " + wrapperFileRel);
+      if (Log().Verbose()) {
+        Log().Info(GeneratorT::RCC, "Touching RCC wrapper " + wrapperAbs);
       }
-      cmSystemTools::Touch(wrapperFileAbs, false);
+      FileSys().Touch(wrapperAbs);
     }
   }
+}
+
+bool cmQtAutoGeneratorRcc::StartProcess(
+  std::string const& workingDirectory, std::vector<std::string> const& command,
+  bool mergedOutput)
+{
+  // Log command
+  if (Log().Verbose()) {
+    std::string msg = "Running command:\n";
+    msg += QuotedCommand(command);
+    msg += '\n';
+    Log().Info(GeneratorT::RCC, msg);
+  }
 
-  return success;
+  // Create process handler
+  Process_ = cm::make_unique<ReadOnlyProcessT>();
+  Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory);
+  // Start process
+  if (!Process_->start(UVLoop(),
+                       std::bind(&cm::uv_async_ptr::send, &UVRequest()))) {
+    Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage);
+    Error_ = true;
+    // Clean up
+    Process_.reset();
+    ProcessResult_.reset();
+    return false;
+  }
+  return true;
 }
diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h
index 0e3f690..8a69c6c 100644
--- a/Source/cmQtAutoGeneratorRcc.h
+++ b/Source/cmQtAutoGeneratorRcc.h
@@ -5,52 +5,97 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
+#include "cm_uv.h"
 
 #include <string>
 #include <vector>
 
 class cmMakefile;
 
+// @brief AUTORCC generator
 class cmQtAutoGeneratorRcc : public cmQtAutoGenerator
 {
   CM_DISABLE_COPY(cmQtAutoGeneratorRcc)
 public:
   cmQtAutoGeneratorRcc();
+  ~cmQtAutoGeneratorRcc() override;
 
 private:
-  // -- Initialization & settings
-  bool InfoFileRead(cmMakefile* makefile);
-  void SettingsFileRead(cmMakefile* makefile);
-  bool SettingsFileWrite();
-  // -- Central processing
-  bool Process(cmMakefile* makefile) override;
-  bool RccGenerate();
+  // -- Types
 
+  /// @brief Processing stage
+  enum class StageT
+  {
+    SETTINGS_READ,
+    TEST_QRC_RCC_FILES,
+    TEST_RESOURCES_READ,
+    TEST_RESOURCES,
+    TEST_INFO_FILE,
+    GENERATE,
+    GENERATE_RCC,
+    GENERATE_WRAPPER,
+    SETTINGS_WRITE,
+    FINISH,
+    END
+  };
+
+  // -- 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();
+  // -- Tests
+  bool TestQrcRccFiles();
+  bool TestResourcesRead();
+  bool TestResources();
+  void TestInfoFile();
+  // -- Generation
+  void GenerateParentDir();
+  bool GenerateRcc();
+  void GenerateWrapper();
+
+  // -- Utility
+  bool StartProcess(std::string const& workingDirectory,
+                    std::vector<std::string> const& command,
+                    bool mergedOutput);
+
+private:
   // -- Config settings
-  std::string ConfigSuffix;
-  cmQtAutoGen::MultiConfig MultiConfig;
-  // -- Settings
-  bool SettingsChanged;
-  std::string SettingsFile;
-  std::string SettingsString;
+  bool SettingsChanged_;
+  std::string ConfigSuffix_;
+  MultiConfigT MultiConfig_;
   // -- Directories
-  std::string ProjectSourceDir;
-  std::string ProjectBinaryDir;
-  std::string CurrentSourceDir;
-  std::string CurrentBinaryDir;
-  std::string AutogenBuildDir;
-  cmFilePathChecksum FilePathChecksum;
+  std::string AutogenBuildDir_;
   // -- Qt environment
-  std::string RccExecutable;
-  std::vector<std::string> RccListOptions;
+  std::string RccExecutable_;
+  std::vector<std::string> RccListOptions_;
   // -- Job
-  std::string QrcFile;
-  std::string RccFile;
-  std::vector<std::string> Options;
-  std::vector<std::string> Inputs;
+  std::string QrcFile_;
+  std::string QrcFileName_;
+  std::string QrcFileDir_;
+  std::string RccFile_;
+  std::string RccFileWrapper_;
+  std::string RccFileBuild_;
+  std::vector<std::string> Options_;
+  std::vector<std::string> Inputs_;
+  // -- Subprocess
+  ProcessResultT ProcessResult_;
+  std::unique_ptr<ReadOnlyProcessT> Process_;
+  // -- Settings file
+  std::string SettingsFile_;
+  std::string SettingsString_;
+  // -- libuv loop
+  StageT Stage_;
+  bool Error_;
+  bool Generate_;
+  bool BuildFileChanged_;
 };
 
 #endif
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 1974be3..663a4c9 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -247,6 +247,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     this->SetPropertyDefault("AUTOMOC", nullptr);
     this->SetPropertyDefault("AUTOUIC", nullptr);
     this->SetPropertyDefault("AUTORCC", nullptr);
+    this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr);
     this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr);
     this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr);
     this->SetPropertyDefault("AUTOMOC_MACRO_NAMES", nullptr);

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4
commit 488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4
Author:     Sebastian Holtermann <sebholt at xwmw.org>
AuthorDate: Wed Jan 3 16:59:02 2018 +0100
Commit:     Sebastian Holtermann <sebholt at xwmw.org>
CommitDate: Wed Jan 17 17:23:49 2018 +0100

    Autogen: Tests: Fix MocInclude test(s)

diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp
index dea6cb5..d8bf284 100644
--- a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp
+++ b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp
@@ -1,6 +1,8 @@
 #ifndef EOBJAEXTRA_P_HPP
 #define EOBJAEXTRA_P_HPP
 
+#include <QObject>
+
 class EObjAExtraPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/EObjA_p.hpp b/Tests/QtAutogen/MocInclude/EObjA_p.hpp
index 1e0d7e1..9ef5624 100644
--- a/Tests/QtAutogen/MocInclude/EObjA_p.hpp
+++ b/Tests/QtAutogen/MocInclude/EObjA_p.hpp
@@ -1,6 +1,8 @@
 #ifndef EOBJA_P_HPP
 #define EOBJA_P_HPP
 
+#include <QObject>
+
 class EObjAPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/EObjB_p.hpp b/Tests/QtAutogen/MocInclude/EObjB_p.hpp
index 2905f28..84b1ea2 100644
--- a/Tests/QtAutogen/MocInclude/EObjB_p.hpp
+++ b/Tests/QtAutogen/MocInclude/EObjB_p.hpp
@@ -1,6 +1,8 @@
 #ifndef EOBJB_P_HPP
 #define EOBJB_P_HPP
 
+#include <QObject>
+
 class EObjBPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/LObjA_p.h b/Tests/QtAutogen/MocInclude/LObjA_p.h
index ebe8395..97113d6 100644
--- a/Tests/QtAutogen/MocInclude/LObjA_p.h
+++ b/Tests/QtAutogen/MocInclude/LObjA_p.h
@@ -1,6 +1,8 @@
 #ifndef LOBJA_P_HPP
 #define LOBJA_P_HPP
 
+#include <QObject>
+
 class LObjAPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/LObjB_p.h b/Tests/QtAutogen/MocInclude/LObjB_p.h
index b871f2d..b88f40e 100644
--- a/Tests/QtAutogen/MocInclude/LObjB_p.h
+++ b/Tests/QtAutogen/MocInclude/LObjB_p.h
@@ -1,6 +1,8 @@
 #ifndef LOBJB_P_HPP
 #define LOBJB_P_HPP
 
+#include <QObject>
+
 class LObjBPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/ObjA_p.h b/Tests/QtAutogen/MocInclude/ObjA_p.h
index eb60c98..d944bc6 100644
--- a/Tests/QtAutogen/MocInclude/ObjA_p.h
+++ b/Tests/QtAutogen/MocInclude/ObjA_p.h
@@ -1,6 +1,8 @@
 #ifndef OBJA_P_HPP
 #define OBJA_P_HPP
 
+#include <QObject>
+
 class ObjAPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/ObjB_p.h b/Tests/QtAutogen/MocInclude/ObjB_p.h
index 418da65..61ba604 100644
--- a/Tests/QtAutogen/MocInclude/ObjB_p.h
+++ b/Tests/QtAutogen/MocInclude/ObjB_p.h
@@ -1,6 +1,8 @@
 #ifndef OBJB_P_HPP
 #define OBJB_P_HPP
 
+#include <QObject>
+
 class ObjBPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp
index db8a096..3231fac 100644
--- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp
+++ b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp
@@ -1,6 +1,8 @@
 #ifndef EOBJBEXTRA_P_HPP
 #define EOBJBEXTRA_P_HPP
 
+#include <QObject>
+
 class EObjBExtraPrivate : public QObject
 {
   Q_OBJECT
diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp
index 7b37dfd..4a43755 100644
--- a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp
+++ b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp
@@ -1,6 +1,8 @@
 #ifndef GOBJ_P_HPP
 #define GOBJ_P_HPP
 
+#include <QObject>
+
 namespace subGlobal {
 
 class GObjPrivate : public QObject

-----------------------------------------------------------------------

Summary of changes:
 Help/manual/cmake-properties.7.rst                 |    1 +
 Help/manual/cmake-qt.7.rst                         |    3 +-
 Help/manual/cmake-variables.7.rst                  |    1 +
 Help/prop_tgt/AUTOGEN_PARALLEL.rst                 |   21 +
 Help/prop_tgt/AUTOMOC.rst                          |    6 +-
 Help/prop_tgt/AUTOUIC.rst                          |    3 +
 Help/release/dev/autogen-parallel.rst              |   10 +
 Help/variable/CMAKE_AUTOGEN_PARALLEL.rst           |   10 +
 Modules/AutoRccInfo.cmake.in                       |    4 -
 Modules/AutogenInfo.cmake.in                       |    1 +
 Source/CMakeLists.txt                              |    4 +-
 Source/cmGlobalGenerator.cxx                       |   10 +-
 Source/cmGlobalGenerator.h                         |    4 +-
 Source/cmQtAutoGen.cxx                             |  314 +-
 Source/cmQtAutoGen.h                               |   55 +-
 ...rInitializer.cxx => cmQtAutoGenInitializer.cxx} |  201 +-
 ...ratorInitializer.h => cmQtAutoGenInitializer.h} |   21 +-
 Source/cmQtAutoGenerator.cxx                       |  669 +++--
 Source/cmQtAutoGenerator.h                         |  308 +-
 Source/cmQtAutoGeneratorMocUic.cxx                 | 3153 +++++++++++---------
 Source/cmQtAutoGeneratorMocUic.h                   |  510 +++-
 Source/cmQtAutoGeneratorRcc.cxx                    |  719 +++--
 Source/cmQtAutoGeneratorRcc.h                      |   97 +-
 Source/cmTarget.cxx                                |    1 +
 Tests/QtAutogen/CommonTests.cmake                  |    6 +
 Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp        |    2 +
 Tests/QtAutogen/MocInclude/EObjA_p.hpp             |    2 +
 Tests/QtAutogen/MocInclude/EObjB_p.hpp             |    2 +
 Tests/QtAutogen/MocInclude/LObjA_p.h               |    2 +
 Tests/QtAutogen/MocInclude/LObjB_p.h               |    2 +
 Tests/QtAutogen/MocInclude/ObjA_p.h                |    2 +
 Tests/QtAutogen/MocInclude/ObjB_p.h                |    2 +
 .../QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp |    2 +
 Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp    |    2 +
 Tests/QtAutogen/Parallel/CMakeLists.txt            |   10 +
 .../{SameName => Parallel}/aaa/bbb/data.qrc        |    0
 .../{SameName => Parallel}/aaa/bbb/item.cpp        |    0
 .../{SameName => Parallel}/aaa/bbb/item.hpp        |    0
 .../QtAutogen/{SameName => Parallel}/aaa/data.qrc  |    0
 .../QtAutogen/{SameName => Parallel}/aaa/item.cpp  |    0
 .../QtAutogen/{SameName => Parallel}/aaa/item.hpp  |    0
 Tests/QtAutogen/{SameName => Parallel}/aaa/view.ui |    0
 .../{SameName => Parallel}/bbb/aaa/data.qrc        |    0
 .../{SameName => Parallel}/bbb/aaa/item.cpp        |    0
 .../{SameName => Parallel}/bbb/aaa/item.hpp        |    0
 .../QtAutogen/{SameName => Parallel}/bbb/data.qrc  |    0
 .../QtAutogen/{SameName => Parallel}/bbb/item.cpp  |    0
 .../QtAutogen/{SameName => Parallel}/bbb/item.hpp  |    0
 Tests/QtAutogen/{SameName => Parallel}/bbb/view.ui |    0
 .../QtAutogen/{SameName => Parallel}/ccc/data.qrc  |    0
 .../QtAutogen/{SameName => Parallel}/ccc/item.cpp  |    0
 .../QtAutogen/{SameName => Parallel}/ccc/item.hpp  |    0
 Tests/QtAutogen/{SameName => Parallel}/ccc/view.ui |    0
 Tests/QtAutogen/{SameName => Parallel}/data.qrc    |    0
 Tests/QtAutogen/{SameName => Parallel}/item.cpp    |    0
 Tests/QtAutogen/{SameName => Parallel}/item.hpp    |    0
 Tests/QtAutogen/{SameName => Parallel}/main.cpp    |    0
 Tests/QtAutogen/Parallel/parallel.cmake            |   24 +
 Tests/QtAutogen/{SameName => Parallel}/view.ui     |    0
 Tests/QtAutogen/Parallel1/CMakeLists.txt           |   10 +
 Tests/QtAutogen/Parallel2/CMakeLists.txt           |   10 +
 Tests/QtAutogen/Parallel3/CMakeLists.txt           |   10 +
 Tests/QtAutogen/Parallel4/CMakeLists.txt           |   10 +
 Tests/QtAutogen/ParallelAUTO/CMakeLists.txt        |   10 +
 64 files changed, 3869 insertions(+), 2365 deletions(-)
 create mode 100644 Help/prop_tgt/AUTOGEN_PARALLEL.rst
 create mode 100644 Help/release/dev/autogen-parallel.rst
 create mode 100644 Help/variable/CMAKE_AUTOGEN_PARALLEL.rst
 rename Source/{cmQtAutoGeneratorInitializer.cxx => cmQtAutoGenInitializer.cxx} (88%)
 rename Source/{cmQtAutoGeneratorInitializer.h => cmQtAutoGenInitializer.h} (78%)
 create mode 100644 Tests/QtAutogen/Parallel/CMakeLists.txt
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/bbb/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/bbb/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/bbb/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/aaa/view.ui (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/aaa/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/aaa/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/aaa/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/bbb/view.ui (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/ccc/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/ccc/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/ccc/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/ccc/view.ui (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/data.qrc (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/item.cpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/item.hpp (100%)
 copy Tests/QtAutogen/{SameName => Parallel}/main.cpp (100%)
 create mode 100644 Tests/QtAutogen/Parallel/parallel.cmake
 copy Tests/QtAutogen/{SameName => Parallel}/view.ui (100%)
 create mode 100644 Tests/QtAutogen/Parallel1/CMakeLists.txt
 create mode 100644 Tests/QtAutogen/Parallel2/CMakeLists.txt
 create mode 100644 Tests/QtAutogen/Parallel3/CMakeLists.txt
 create mode 100644 Tests/QtAutogen/Parallel4/CMakeLists.txt
 create mode 100644 Tests/QtAutogen/ParallelAUTO/CMakeLists.txt


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list