Merge topic 'autogen-generators-refactor'

51fd7b71 Autogen: Tests: Add a change-not test to the mocRerun test
c8f92db7 Autogen: Tests: Disable an AUTOMOC_DEPENDS_FILTER test for Ninja
3c77515e Autogen: Tests: Refactor the QtAutogenRebuild rccDepends test
04a0daee Autogen: Tests: Move each rerun test script to a NAME.cmake file
e5c6610a Autogen: Tests: Extend CMAKE_AUTOMOC_DEPEND_FILTERS rebuild test
93265652 Autogen: Tests: Add <SUBDIR>/ui_view.h AUTOUIC includes to sameName test
4eb7d817 Autogen: Tests: Add <SUBDIR>/item.moc includes to sameName test
3f223743 Autogen: Read relative paths from rcc output
...

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !1244
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 95cd122..d9b1fd3 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -2,6 +2,7 @@
    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"
@@ -158,15 +159,18 @@
     std::string rccStdOut;
     std::string rccStdErr;
     int retVal = 0;
-    bool result =
-      cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
-                                      nullptr, cmSystemTools::OUTPUT_NONE);
+    bool result = cmSystemTools::RunSingleCommand(
+      command, &rccStdOut, &rccStdErr, &retVal, nullptr,
+      cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
     if (result && retVal == 0 &&
         rccStdOut.find("--list") != std::string::npos) {
       hasDashDashList = true;
     }
   }
 
+  std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
+  std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
+
   // Run rcc list command
   bool result = false;
   int retVal = 0;
@@ -176,16 +180,16 @@
     std::vector<std::string> command;
     command.push_back(rccCommand);
     command.push_back(hasDashDashList ? "--list" : "-list");
-    command.push_back(fileName);
-    result =
-      cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
-                                      nullptr, cmSystemTools::OUTPUT_NONE);
+    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::ostringstream ost;
-      ost << "rcc list process for " << cmQtAutoGen::Quoted(fileName)
-          << " failed:\n"
+      ost << "rcc list process failed for\n  " << cmQtAutoGen::Quoted(fileName)
+          << "\n"
           << rccStdOut << "\n"
           << rccStdErr << "\n";
       *errorMessage = ost.str();
@@ -230,6 +234,11 @@
     }
   }
 
+  // Convert relative paths to absolute paths
+  for (std::string& resFile : files) {
+    resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile);
+  }
+
   return true;
 }
 
diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx
index 8c03045..e8a9006 100644
--- a/Source/cmQtAutoGenerators.cxx
+++ b/Source/cmQtAutoGenerators.cxx
@@ -8,6 +8,7 @@
 #include <algorithm>
 #include <array>
 #include <list>
+#include <memory>
 #include <sstream>
 #include <string.h>
 #include <utility>
@@ -35,14 +36,23 @@
 
 // -- Static functions
 
-static std::string QuotedCommand(const std::vector<std::string>& command)
+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(' ');
     }
-    const std::string cesc = cmQtAutoGen::Quoted(item);
+    std::string const cesc = cmQtAutoGen::Quoted(item);
     if (item.empty() || (cesc.size() > (item.size() + 2)) ||
         (cesc.find(' ') != std::string::npos)) {
       res += cesc;
@@ -53,7 +63,7 @@
   return res;
 }
 
-static std::string SubDirPrefix(const std::string& fileName)
+static std::string SubDirPrefix(std::string const& fileName)
 {
   std::string res(cmSystemTools::GetFilenamePath(fileName));
   if (!res.empty()) {
@@ -62,36 +72,58 @@
   return res;
 }
 
-static bool ReadAll(std::string& content, const std::string& filename)
+static bool ReadFile(std::string& content, std::string const& filename,
+                     std::string* error = nullptr)
 {
   bool success = false;
-  {
-    cmsys::ifstream ifs(filename.c_str());
+  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) {
-      std::ostringstream osst;
-      osst << ifs.rdbuf();
-      content = osst.str();
-      success = true;
+      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("The file does not exist.");
   }
   return success;
 }
 
 /**
- * @brief Tests if buildFile doesn't exist or is older than sourceFile
- * @return True if buildFile doesn't exist or is older than sourceFile
+ * @brief Tests if buildFile is older than sourceFile
+ * @return True if buildFile  is older than sourceFile.
+ *         False may indicate an error.
  */
-static bool FileAbsentOrOlder(const std::string& buildFile,
-                              const std::string& sourceFile)
+static bool FileIsOlderThan(std::string const& buildFile,
+                            std::string const& sourceFile,
+                            std::string* error = nullptr)
 {
   int result = 0;
-  bool success =
-    cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
-  return (!success || (result <= 0));
+  if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) {
+    return (result < 0);
+  }
+  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));
+  }
+  return false;
 }
 
-static bool ListContains(const std::vector<std::string>& list,
-                         const std::string& entry)
+static bool ListContains(std::vector<std::string> const& list,
+                         std::string const& entry)
 {
   return (std::find(list.begin(), list.end(), entry) != list.end());
 }
@@ -99,24 +131,20 @@
 // -- Class methods
 
 cmQtAutoGenerators::cmQtAutoGenerators()
-  : Verbose(cmSystemTools::HasEnv("VERBOSE"))
+  : IncludeProjectDirsBefore(false)
+  , Verbose(cmSystemTools::HasEnv("VERBOSE"))
   , ColorOutput(true)
   , MocSettingsChanged(false)
   , MocPredefsChanged(false)
-  , MocRunFailed(false)
+  , MocRelaxedMode(false)
   , UicSettingsChanged(false)
-  , UicRunFailed(false)
   , RccSettingsChanged(false)
-  , RccRunFailed(false)
 {
-
-  std::string colorEnv;
-  cmSystemTools::GetEnv("COLOR", colorEnv);
-  if (!colorEnv.empty()) {
-    if (cmSystemTools::IsOn(colorEnv.c_str())) {
-      this->ColorOutput = true;
-    } else {
-      this->ColorOutput = false;
+  {
+    std::string colorEnv;
+    cmSystemTools::GetEnv("COLOR", colorEnv);
+    if (!colorEnv.empty()) {
+      this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str());
     }
   }
 
@@ -134,8 +162,8 @@
                                  "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
 }
 
-bool cmQtAutoGenerators::Run(const std::string& targetDirectory,
-                             const std::string& config)
+bool cmQtAutoGenerators::Run(std::string const& targetDirectory,
+                             std::string const& config)
 {
   cmake cm(cmake::RoleScript);
   cm.SetHomeOutputDirectory(targetDirectory);
@@ -147,16 +175,14 @@
   snapshot.GetDirectory().SetCurrentBinary(targetDirectory);
   snapshot.GetDirectory().SetCurrentSource(targetDirectory);
 
-  std::unique_ptr<cmMakefile> makefile(new cmMakefile(&gg, snapshot));
+  auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
   gg.SetCurrentMakefile(makefile.get());
 
   bool success = false;
-  if (this->ReadAutogenInfoFile(makefile.get(), targetDirectory, config)) {
-    // Read old settings
+  if (this->InitInfoFile(makefile.get(), targetDirectory, config)) {
+    // Read latest settings
     this->SettingsFileRead(makefile.get());
-    // Init and run
-    this->Init(makefile.get());
-    if (this->RunAutogen()) {
+    if (this->Process()) {
       // Write current settings
       if (this->SettingsFileWrite()) {
         success = true;
@@ -166,46 +192,11 @@
   return success;
 }
 
-bool cmQtAutoGenerators::MocDependFilterPush(const std::string& key,
-                                             const std::string& regExp)
+bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile,
+                                      std::string const& targetDirectory,
+                                      std::string const& config)
 {
-  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";
-    }
-  } 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);
-    return false;
-  }
-  return true;
-}
-
-bool cmQtAutoGenerators::ReadAutogenInfoFile(
-  cmMakefile* makefile, const std::string& targetDirectory,
-  const std::string& config)
-{
-  // Lambdas
+  // Utility lambdas
   auto InfoGet = [makefile](const char* key) {
     return makefile->GetSafeDefinition(key);
   };
@@ -221,7 +212,7 @@
     [makefile](const char* key) -> std::vector<std::vector<std::string>> {
     std::vector<std::vector<std::string>> lists;
     {
-      const std::string value = makefile->GetSafeDefinition(key);
+      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);
@@ -266,16 +257,18 @@
     return list;
   };
 
-  std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
-  cmSystemTools::ConvertToUnixSlashes(filename);
-  filename += "/AutogenInfo.cmake";
+  this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory);
+  cmSystemTools::ConvertToUnixSlashes(this->InfoFile);
+  this->InfoFile += "/AutogenInfo.cmake";
 
-  if (!makefile->ReadListFile(filename.c_str())) {
-    this->LogFileError(cmQtAutoGen::GEN, filename, "File processing failed");
+  if (!makefile->ReadListFile(this->InfoFile.c_str())) {
+    this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
+                       "File processing failed");
     return false;
   }
 
   // -- Meta
+  this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
   this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
 
   // - Old settings file
@@ -296,12 +289,10 @@
     InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
   this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
   if (this->AutogenBuildDir.empty()) {
-    this->LogFileError(cmQtAutoGen::GEN, filename,
+    this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
                        "Autogen build directory missing");
     return false;
   }
-  this->Sources = InfoGetList("AM_SOURCES");
-  this->Headers = InfoGetList("AM_HEADERS");
 
   // - Qt environment
   this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR");
@@ -312,7 +303,8 @@
 
   // Check Qt version
   if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) {
-    this->LogFileError(cmQtAutoGen::GEN, filename, "Unsupported Qt version: " +
+    this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
+                       "Unsupported Qt version: " +
                          cmQtAutoGen::Quoted(this->QtMajorVersion));
     return false;
   }
@@ -323,7 +315,7 @@
     this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
 #ifdef _WIN32
     {
-      const std::string win32("WIN32");
+      std::string const win32("WIN32");
       if (!ListContains(this->MocDefinitions, win32)) {
         this->MocDefinitions.push_back(win32);
       }
@@ -333,7 +325,7 @@
     this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
     this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
     {
-      const std::vector<std::string> MocMacroNames =
+      std::vector<std::string> const MocMacroNames =
         InfoGetList("AM_MOC_MACRO_NAMES");
       for (std::string const& item : MocMacroNames) {
         this->MocMacroFilters.emplace_back(
@@ -341,7 +333,7 @@
       }
     }
     {
-      const std::vector<std::string> mocDependFilters =
+      std::vector<std::string> const mocDependFilters =
         InfoGetList("AM_MOC_DEPEND_FILTERS");
       // Insert Q_PLUGIN_METADATA dependency filter
       if (this->QtMajorVersion != "4") {
@@ -361,7 +353,7 @@
         }
       } else {
         this->LogFileError(
-          cmQtAutoGen::MOC, filename,
+          cmQtAutoGen::MOC, this->InfoFile,
           "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
         return false;
       }
@@ -378,22 +370,21 @@
       auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
       auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
       // Compare list sizes
-      if (sources.size() == options.size()) {
-        auto fitEnd = sources.cend();
-        auto fit = sources.begin();
-        auto oit = options.begin();
-        while (fit != fitEnd) {
-          this->UicOptions[*fit] = std::move(*oit);
-          ++fit;
-          ++oit;
-        }
-      } else {
+      if (sources.size() != options.size()) {
         std::ostringstream ost;
         ost << "files/options lists sizes missmatch (" << sources.size() << "/"
             << options.size() << ")";
-        this->LogFileError(cmQtAutoGen::UIC, filename, ost.str());
+        this->LogFileError(cmQtAutoGen::UIC, this->InfoFile, ost.str());
         return false;
       }
+      auto fitEnd = sources.cend();
+      auto fit = sources.begin();
+      auto oit = options.begin();
+      while (fit != fitEnd) {
+        this->UicOptions[*fit] = std::move(*oit);
+        ++fit;
+        ++oit;
+      }
     }
   }
 
@@ -409,24 +400,23 @@
       std::ostringstream ost;
       ost << "sources, builds lists sizes missmatch (" << sources.size() << "/"
           << builds.size() << ")";
-      this->LogFileError(cmQtAutoGen::RCC, filename, ost.str());
+      this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
       return false;
     }
     if (sources.size() != options.size()) {
       std::ostringstream ost;
       ost << "sources, options lists sizes missmatch (" << sources.size()
           << "/" << options.size() << ")";
-      this->LogFileError(cmQtAutoGen::RCC, filename, ost.str());
+      this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
       return false;
     }
     if (sources.size() != inputs.size()) {
       std::ostringstream ost;
       ost << "sources, inputs lists sizes missmatch (" << sources.size() << "/"
           << inputs.size() << ")";
-      this->LogFileError(cmQtAutoGen::RCC, filename, ost.str());
+      this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
       return false;
     }
-
     {
       auto srcItEnd = sources.end();
       auto srcIt = sources.begin();
@@ -445,6 +435,156 @@
     }
   }
 
+  // Initialize source file jobs
+  {
+    // 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;
+      }
+    };
+
+    // Add header jobs
+    for (std::string& hdr : InfoGetList("AM_HEADERS")) {
+      AddJob(this->HeaderJobs, std::move(hdr));
+    }
+    // Add source jobs
+    {
+      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;
+            }
+          }
+        }
+      }
+      // Add Source jobs
+      for (std::string& src : sources) {
+        AddJob(this->SourceJobs, std::move(src));
+      }
+    }
+  }
+
+  // Init derived information
+  // ------------------------
+
+  // Init file path checksum generator
+  this->FilePathChecksum.setupParentDirs(
+    this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
+    this->ProjectBinaryDir);
+
+  // include directory
+  this->AutogenIncludeDir = "include";
+  this->AutogenIncludeDir += this->ConfigSuffix;
+  this->AutogenIncludeDir += "/";
+
+  if (this->MocEnabled()) {
+    // Mocs compilation file
+    this->MocCompFileRel = "mocs_compilation.cpp";
+    this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
+      this->AutogenBuildDir, this->MocCompFileRel);
+
+    // Moc predefs file
+    if (!this->MocPredefsCmd.empty()) {
+      this->MocPredefsFileRel = "moc_predefs";
+      this->MocPredefsFileRel += this->ConfigSuffix;
+      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;
+            }
+          }
+        }
+      }
+      // Append remaining directories
+      this->MocIncludePaths.insert(this->MocIncludePaths.end(),
+                                   includes.begin(), includes.end());
+    }
+    // Compose moc includes list
+    {
+      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);
+      }
+    }
+    // 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);
+      }
+      // Add options
+      this->MocAllOptions.insert(this->MocAllOptions.end(),
+                                 this->MocOptions.begin(),
+                                 this->MocOptions.end());
+    }
+  }
+
   return true;
 }
 
@@ -453,16 +593,12 @@
   // Compose current settings strings
   {
     cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
-    const std::string sep(" ~~~ ");
+    std::string const sep(" ~~~ ");
     if (this->MocEnabled()) {
       std::string str;
       str += this->MocExecutable;
       str += sep;
-      str += cmJoin(this->MocDefinitions, ";");
-      str += sep;
-      str += cmJoin(this->MocIncludePaths, ";");
-      str += sep;
-      str += cmJoin(this->MocOptions, ";");
+      str += cmJoin(this->MocAllOptions, ";");
       str += sep;
       str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
       str += sep;
@@ -503,7 +639,7 @@
   // Read old settings
   if (makefile->ReadListFile(this->SettingsFile.c_str())) {
     {
-      auto SMatch = [makefile](const char* key, const std::string& value) {
+      auto SMatch = [makefile](const char* key, std::string const& value) {
         return (value == makefile->GetSafeDefinition(key));
       };
       if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
@@ -519,7 +655,7 @@
     // 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->AnySettingsChanged()) {
+    if (this->SettingsChanged()) {
       cmSystemTools::RemoveFile(this->SettingsFile);
     }
   } else {
@@ -534,7 +670,7 @@
 {
   bool success = true;
   // Only write if any setting changed
-  if (this->AnySettingsChanged()) {
+  if (this->SettingsChanged()) {
     if (this->Verbose) {
       this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
                       cmQtAutoGen::Quoted(this->SettingsFile));
@@ -543,7 +679,7 @@
     std::string settings;
     {
       auto SettingAppend = [&settings](const char* key,
-                                       const std::string& value) {
+                                       std::string const& value) {
         settings += "set(";
         settings += key;
         settings += " ";
@@ -557,7 +693,7 @@
     // Write settings file
     if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
       this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
-                         "File writing failed");
+                         "Settings file writing failed");
       // Remove old settings file to trigger a full rebuild on the next run
       cmSystemTools::RemoveFile(this->SettingsFile);
       success = false;
@@ -566,89 +702,7 @@
   return success;
 }
 
-void cmQtAutoGenerators::Init(cmMakefile* makefile)
-{
-  // Mocs compilation file
-  this->MocCompFileRel = "mocs_compilation.cpp";
-  this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
-    this->AutogenBuildDir, this->MocCompFileRel);
-
-  // Mocs include directory
-  this->AutogenIncludeDir = "include";
-  this->AutogenIncludeDir += this->ConfigSuffix;
-  this->AutogenIncludeDir += "/";
-
-  // Moc predefs file
-  if (!this->MocPredefsCmd.empty()) {
-    this->MocPredefsFileRel = "moc_predefs";
-    this->MocPredefsFileRel += this->ConfigSuffix;
-    this->MocPredefsFileRel += ".h";
-    this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
-      this->AutogenBuildDir, this->MocPredefsFileRel);
-  }
-
-  // Init file path checksum generator
-  FPathChecksum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir,
-                                this->ProjectSourceDir,
-                                this->ProjectBinaryDir);
-
-  // Acquire header extensions
-  this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
-
-  // 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
-    {
-      const std::array<const std::string*, 2> movePaths = {
-        { &this->ProjectBinaryDir, &this->ProjectSourceDir }
-      };
-      for (const std::string* ppath : movePaths) {
-        std::list<std::string>::iterator it = includes.begin();
-        while (it != includes.end()) {
-          const std::string& path = *it;
-          if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
-            this->MocIncludePaths.push_back(path);
-            it = includes.erase(it);
-          } else {
-            ++it;
-          }
-        }
-      }
-    }
-    // Append remaining directories
-    this->MocIncludePaths.insert(this->MocIncludePaths.end(), includes.begin(),
-                                 includes.end());
-  }
-  // Compose moc includes list
-  {
-    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);
-    }
-  }
-}
-
-bool cmQtAutoGenerators::RunAutogen()
+bool cmQtAutoGenerators::Process()
 {
   // 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
@@ -660,7 +714,7 @@
 
   // Create AUTOGEN include directory
   {
-    const std::string incDirAbs = cmSystemTools::CollapseCombinedPath(
+    std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
       this->AutogenBuildDir, this->AutogenIncludeDir);
     if (!cmSystemTools::MakeDirectory(incDirAbs)) {
       this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
@@ -669,45 +723,28 @@
     }
   }
 
-  // key = moc source filepath, value = moc output filepath
-  std::map<std::string, std::string> mocsIncluded;
-  std::map<std::string, std::string> mocsNotIncluded;
-  std::map<std::string, std::set<std::string>> mocDepends;
-  std::map<std::string, std::vector<std::string>> uisIncluded;
-  // collects all headers which may need to be mocced
-  std::set<std::string> mocHeaderFiles;
-  std::set<std::string> uicHeaderFiles;
-
-  // Parse sources
-  for (std::string const& src : this->Sources) {
-    // Parse source file for MOC/UIC
-    if (!this->ParseSourceFile(src, mocsIncluded, mocDepends, uisIncluded,
-                               this->MocRelaxedMode)) {
+  // Parse source files
+  for (const auto& item : this->SourceJobs) {
+    if (!this->ParseSourceFile(item.first, item.second)) {
       return false;
     }
-    // Find additional headers
-    this->SearchHeadersForSourceFile(src, mocHeaderFiles, uicHeaderFiles);
   }
-
-  // Parse headers
-  for (std::string const& hdr : this->Headers) {
-    if (!this->MocSkip(hdr)) {
-      mocHeaderFiles.insert(hdr);
-    }
-    if (!this->UicSkip(hdr)) {
-      uicHeaderFiles.insert(hdr);
+  // Parse header files
+  for (const auto& item : this->HeaderJobs) {
+    if (!this->ParseHeaderFile(item.first, item.second)) {
+      return false;
     }
   }
-  if (!this->ParseHeaders(mocHeaderFiles, uicHeaderFiles, mocsIncluded,
-                          mocsNotIncluded, mocDepends, uisIncluded)) {
+  // Read missing dependency information
+  if (!this->ParsePostprocess()) {
     return false;
-  };
+  }
 
   // Generate files
-  if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded, mocDepends)) {
+  if (!this->MocGenerateAll()) {
     return false;
   }
-  if (!this->UicGenerateAll(uisIncluded)) {
+  if (!this->UicGenerateAll()) {
     return false;
   }
   if (!this->RccGenerateAll()) {
@@ -718,10 +755,111 @@
 }
 
 /**
+ * @return True on success
+ */
+bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename,
+                                         const SourceJob& job)
+{
+  std::string contentText;
+  std::string error;
+  bool success = ReadFile(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);
+      }
+    } else {
+      this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
+                           "The source file is empty");
+    }
+  } else {
+    this->LogFileError(cmQtAutoGen::GEN, absFilename,
+                       "Could not read the source file: " + error);
+  }
+  return success;
+}
+
+/**
+ * @return True on success
+ */
+bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename,
+                                         const SourceJob& job)
+{
+  std::string contentText;
+  std::string error;
+  bool success = ReadFile(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);
+  }
+  return success;
+}
+
+/**
+ * @return True on success
+ */
+bool cmQtAutoGenerators::ParsePostprocess()
+{
+  bool success = true;
+  // Read missin dependecies
+  for (auto& item : this->MocJobsIncluded) {
+    if (!item->DependsValid) {
+      std::string content;
+      std::string error;
+      if (ReadFile(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 success;
+}
+
+/**
+ * @brief Tests if the file should be ignored for moc scanning
+ * @return True if the file should be ignored
+ */
+bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const
+{
+  if (this->MocEnabled()) {
+    // Test if the file name is on the skip list
+    if (!ListContains(this->MocSkipList, absFilename)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
  * @brief Tests if the C++ content requires moc processing
  * @return True if moc is required
  */
-bool cmQtAutoGenerators::MocRequired(const std::string& contentText,
+bool cmQtAutoGenerators::MocRequired(std::string const& contentText,
                                      std::string* macroName)
 {
   for (KeyRegExp& filter : this->MocMacroFilters) {
@@ -740,120 +878,7 @@
   return false;
 }
 
-void cmQtAutoGenerators::MocFindDepends(
-  const std::string& absFilename, const std::string& contentText,
-  std::map<std::string, std::set<std::string>>& mocDepends)
-{
-  for (KeyRegExp& filter : this->MocDependFilters) {
-    // Run a simple find string operation before the expensive
-    // regular expression check
-    if (contentText.find(filter.Key) != std::string::npos) {
-      // Run regular expression check loop
-      const std::string sourcePath = SubDirPrefix(absFilename);
-      const char* contentChars = contentText.c_str();
-      while (filter.RegExp.find(contentChars)) {
-        // Evaluate match
-        const std::string match = filter.RegExp.match(1);
-        if (!match.empty()) {
-          // Find the dependency file
-          std::string incFile;
-          if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
-            mocDepends[absFilename].insert(incFile);
-            if (this->Verbose) {
-              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));
-          }
-        }
-        contentChars += filter.RegExp.end();
-      }
-    }
-  }
-}
-
-/**
- * @brief Tests if the file should be ignored for moc scanning
- * @return True if the file should be ignored
- */
-bool cmQtAutoGenerators::MocSkip(const std::string& absFilename) const
-{
-  if (this->MocEnabled()) {
-    // Test if the file name is on the skip list
-    if (!ListContains(this->MocSkipList, absFilename)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-/**
- * @brief Tests if the file name is in the skip list
- */
-bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const
-{
-  if (this->UicEnabled()) {
-    // Test if the file name is on the skip list
-    if (!ListContains(this->UicSkipList, absFilename)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGenerators::ParseSourceFile(
-  const std::string& absFilename,
-  std::map<std::string, std::string>& mocsIncluded,
-  std::map<std::string, std::set<std::string>>& mocDepends,
-  std::map<std::string, std::vector<std::string>>& uisIncluded, bool relaxed)
-{
-  std::string contentText;
-  bool success = ReadAll(contentText, absFilename);
-  if (success) {
-    if (!contentText.empty()) {
-      // Parse source contents for MOC
-      if (success && !this->MocSkip(absFilename)) {
-        success = this->MocParseSourceContent(
-          absFilename, contentText, mocsIncluded, mocDepends, relaxed);
-      }
-      // Parse source contents for UIC
-      if (success && !this->UicSkip(absFilename)) {
-        this->UicParseContent(absFilename, contentText, uisIncluded);
-      }
-    } else {
-      this->LogFileWarning(cmQtAutoGen::GEN, absFilename, "The file is empty");
-    }
-  } else {
-    this->LogFileError(cmQtAutoGen::GEN, absFilename, "Could not read file");
-  }
-  return success;
-}
-
-void cmQtAutoGenerators::UicParseContent(
-  const std::string& absFilename, const std::string& contentText,
-  std::map<std::string, std::vector<std::string>>& uisIncluded)
-{
-  if (this->Verbose) {
-    this->LogInfo(cmQtAutoGen::UIC, "Checking " + absFilename);
-  }
-
-  const char* contentChars = contentText.c_str();
-  if (strstr(contentChars, "ui_") != nullptr) {
-    while (this->UicRegExpInclude.find(contentChars)) {
-      uisIncluded[absFilename].push_back(this->UicRegExpInclude.match(1));
-      contentChars += this->UicRegExpInclude.end();
-    }
-  }
-}
-
-std::string cmQtAutoGenerators::MocMacroNamesString() const
+std::string cmQtAutoGenerators::MocStringMacros() const
 {
   std::string res;
   const auto itB = this->MocMacroFilters.cbegin();
@@ -875,339 +900,479 @@
   return res;
 }
 
-std::string cmQtAutoGenerators::MocHeaderSuffixesString() const
+std::string cmQtAutoGenerators::MocStringHeaders(
+  std::string const& fileBase) const
 {
-  std::string res = ".{";
+  std::string res = fileBase;
+  res += ".{";
   res += cmJoin(this->HeaderExtensions, ",");
   res += "}";
   return res;
 }
 
-/**
- * @return True on success
- */
-bool cmQtAutoGenerators::MocParseSourceContent(
-  const std::string& absFilename, const std::string& contentText,
-  std::map<std::string, std::string>& mocsIncluded,
-  std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed)
+std::string cmQtAutoGenerators::MocFindIncludedHeader(
+  std::string const& sourcePath, std::string const& includeBase) const
 {
-  if (this->Verbose) {
-    this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename);
-  }
-
-  const std::string scanFileDir = SubDirPrefix(absFilename);
-  const std::string scanFileBase =
-    cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
-
-  std::string selfMacroName;
-  const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
-  bool ownDotMocIncluded = false;
-  std::string ownMocUnderscoreInclude;
-  std::string ownMocUnderscoreHeader;
-
-  // first a simple string check for "moc" is *much* faster than the regexp,
-  // and if the string search already fails, we don't have to try the
-  // expensive regexp
-  const char* contentChars = contentText.c_str();
-  if (strstr(contentChars, "moc") != nullptr) {
-    // Iterate over all included moc files
-    while (this->MocRegExpInclude.find(contentChars)) {
-      const std::string incString = this->MocRegExpInclude.match(1);
-      // Basename of the moc include
-      const std::string incDir(SubDirPrefix(incString));
-      const std::string incBase =
-        cmSystemTools::GetFilenameWithoutLastExtension(incString);
-
-      // If the moc include is of the moc_foo.cpp style we expect
-      // the Q_OBJECT class declaration in a header file.
-      // If the moc include is of the foo.moc style we need to look for
-      // a Q_OBJECT macro in the current source file, if it contains the
-      // macro we generate the moc file from the source file.
-      if (cmHasLiteralPrefix(incBase, "moc_")) {
-        // Include: moc_FOO.cxx
-        // Remove the moc_ part
-        const std::string incRealBase = incBase.substr(4);
-        const std::string headerToMoc =
-          this->MocFindHeader(scanFileDir, incDir + incRealBase);
-        if (!headerToMoc.empty()) {
-          if (!this->MocSkip(headerToMoc)) {
-            // Register moc job
-            mocsIncluded[headerToMoc] = incString;
-            this->MocFindDepends(headerToMoc, contentText, mocDepends);
-            // Store meta information for relaxed mode
-            if (relaxed && (incRealBase == scanFileBase)) {
-              ownMocUnderscoreInclude = incString;
-              ownMocUnderscoreHeader = headerToMoc;
-            }
-          }
-        } else {
-          std::ostringstream ost;
-          ost << "The file includes the moc file "
-              << cmQtAutoGen::Quoted(incString)
-              << ", but could not find header ";
-          ost << cmQtAutoGen::Quoted(incRealBase +
-                                     this->MocHeaderSuffixesString());
-          this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str());
-          return false;
-        }
-      } else {
-        // Include: FOO.moc
-        bool ownDotMoc = (incBase == scanFileBase);
-        std::string fileToMoc;
-        if (relaxed) {
-          // Mode: Relaxed
-          if (selfRequiresMoc && ownDotMoc) {
-            // Include self
-            fileToMoc = absFilename;
-            ownDotMocIncluded = true;
-          } else {
-            // In relaxed mode try to find a header instead but issue a warning
-            const std::string headerToMoc =
-              this->MocFindHeader(scanFileDir, incDir + incBase);
-            if (!headerToMoc.empty()) {
-              if (!this->MocSkip(headerToMoc)) {
-                // This is for KDE4 compatibility:
-                fileToMoc = headerToMoc;
-
-                auto quoted_inc = cmQtAutoGen::Quoted(incString);
-                auto quoted_header = cmQtAutoGen::Quoted(headerToMoc);
-                auto quoted_base =
-                  cmQtAutoGen::Quoted("moc_" + incBase + ".cpp");
-                if (!selfRequiresMoc) {
-                  if (ownDotMoc) {
-                    std::ostringstream ost;
-                    ost << "The file includes the moc file " << quoted_inc
-                        << ", but does not contain a "
-                        << this->MocMacroNamesString() << " macro.\n"
-                        << "Running moc on\n"
-                        << "  " << quoted_header << "!\n"
-                        << "Better include " << quoted_base
-                        << " for a compatibility with strict mode.\n"
-                           "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-                    this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
-                                         ost.str());
-                  } else if (!ownDotMoc) {
-                    std::ostringstream ost;
-                    ost << "The file includes the moc file " << quoted_inc
-                        << " instead of " << quoted_base << ".\n";
-                    ost << "Running moc on\n"
-                        << "  " << quoted_header << "!\n"
-                        << "Better include " << quoted_base
-                        << " for compatibility with strict mode.\n"
-                           "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
-                    this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
-                                         ost.str());
-                  }
-                } else {
-                  if (!ownDotMoc) {
-                    // Handled further down
-                  }
-                }
-              }
-            } else {
-              std::ostringstream ost;
-              ost << "The file includes the moc file "
-                  << cmQtAutoGen::Quoted(incString)
-                  << ", 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, ost.str());
-              return false;
-            }
-          }
-        } else {
-          // Mode: Strict
-          if (ownDotMoc) {
-            // Include self
-            fileToMoc = absFilename;
-            ownDotMocIncluded = true;
-            // Accept but issue a warning if moc isn't required
-            if (!selfRequiresMoc) {
-              std::ostringstream ost;
-              ost << "The file includes the moc file "
-                  << cmQtAutoGen::Quoted(incString)
-                  << ", but does not contain a " << this->MocMacroNamesString()
-                  << " macro.";
-              this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str());
-            }
-          } else {
-            // Don't allow FOO.moc include other than self in strict mode
-            std::ostringstream ost;
-            ost << "The file includes the moc file "
-                << cmQtAutoGen::Quoted(incString)
-                << ", which seems to be the moc file from a different "
-                   "source file. This is not supported. Include "
-                << cmQtAutoGen::Quoted(scanFileBase + ".moc")
-                << " to run moc on this source file.";
-            this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str());
-            return false;
-          }
-        }
-        if (!fileToMoc.empty()) {
-          mocsIncluded[fileToMoc] = incString;
-          this->MocFindDepends(fileToMoc, contentText, mocDepends);
-        }
+  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;
       }
-      // Forward content pointer
-      contentChars += this->MocRegExpInclude.end();
     }
   }
-
-  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 (relaxed && !ownMocUnderscoreInclude.empty()) {
-      // This is for KDE4 compatibility:
-      std::ostringstream ost;
-      ost << "The file contains a " << selfMacroName
-          << " macro, but does not include "
-          << cmQtAutoGen::Quoted(scanFileBase + ".moc")
-          << ". Instead it includes "
-          << cmQtAutoGen::Quoted(ownMocUnderscoreInclude) << ".\n"
-          << "Running moc on\n"
-          << "  " << cmQtAutoGen::Quoted(absFilename) << "!\n"
-          << "Better include " << cmQtAutoGen::Quoted(scanFileBase + ".moc")
-          << " for compatibility with strict mode.\n"
-             "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
-      this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str());
-
-      // Use scanned source file instead of scanned header file as moc source
-      mocsIncluded[absFilename] = ownMocUnderscoreInclude;
-      this->MocFindDepends(absFilename, contentText, mocDepends);
-      // Remove
-      mocsIncluded.erase(ownMocUnderscoreHeader);
-    } else {
-      // Otherwise always error out since it will not compile:
-      std::ostringstream ost;
-      ost << "The file contains a " << selfMacroName
-          << " macro, but does not include "
-          << cmQtAutoGen::Quoted(scanFileBase + ".moc") << "!\n"
-          << "Consider adding the include or enabling SKIP_AUTOMOC for this "
-             "file.";
-      this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str());
-      return false;
-    }
+  // Sanitize
+  if (!header.empty()) {
+    header = cmSystemTools::GetRealPath(header);
   }
-
-  return true;
+  return header;
 }
 
-void cmQtAutoGenerators::MocParseHeaderContent(
-  const std::string& absFilename, const std::string& contentText,
-  std::map<std::string, std::string>& mocsNotIncluded,
-  std::map<std::string, std::set<std::string>>& mocDepends)
+bool cmQtAutoGenerators::MocFindIncludedFile(
+  std::string& absFile, std::string const& sourcePath,
+  std::string const& includeString) const
 {
-  // Log
-  if (this->Verbose) {
-    this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename);
-  }
-  if (this->MocRequired(contentText)) {
-    // Register moc job
-    mocsNotIncluded[absFilename] =
-      this->ChecksumedPath(absFilename, "moc_", this->ConfigSuffix + ".cpp");
-    this->MocFindDepends(absFilename, contentText, mocDepends);
-  }
-}
-
-void cmQtAutoGenerators::SearchHeadersForSourceFile(
-  const std::string& absFilename, std::set<std::string>& mocHeaderFiles,
-  std::set<std::string>& uicHeaderFiles) const
-{
-  std::array<std::string, 2> basepaths;
+  bool success = false;
+  // Search in vicinity of the source
   {
-    std::string bpath = SubDirPrefix(absFilename);
-    bpath += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
-    // search for default header files and private header files
-    basepaths[0] = bpath;
-    basepaths[1] = bpath;
-    basepaths[1] += "_p";
-  }
-
-  for (const std::string& bPath : basepaths) {
-    std::string headerName;
-    if (this->FindHeader(headerName, bPath)) {
-      // Moc headers
-      if (!this->MocSkip(absFilename) && !this->MocSkip(headerName)) {
-        mocHeaderFiles.insert(headerName);
-      }
-      // Uic headers
-      if (!this->UicSkip(absFilename) && !this->UicSkip(headerName)) {
-        uicHeaderFiles.insert(headerName);
-      }
+    std::string testPath = sourcePath;
+    testPath += includeString;
+    if (cmSystemTools::FileExists(testPath.c_str())) {
+      absFile = cmSystemTools::GetRealPath(testPath);
+      success = true;
     }
   }
-}
-
-bool cmQtAutoGenerators::ParseHeaders(
-  const std::set<std::string>& mocHeaderFiles,
-  const std::set<std::string>& uicHeaderFiles,
-  const std::map<std::string, std::string>& mocsIncluded,
-  std::map<std::string, std::string>& mocsNotIncluded,
-  std::map<std::string, std::set<std::string>>& mocDepends,
-  std::map<std::string, std::vector<std::string>>& uisIncluded)
-{
-  bool success = true;
-  // Merged header files list to read files only once
-  std::set<std::string> headerFiles;
-  headerFiles.insert(mocHeaderFiles.begin(), mocHeaderFiles.end());
-  headerFiles.insert(uicHeaderFiles.begin(), uicHeaderFiles.end());
-
-  for (std::string const& headerName : headerFiles) {
-    std::string contentText;
-    if (ReadAll(contentText, headerName)) {
-      // Parse header content for MOC
-      if ((mocHeaderFiles.find(headerName) != mocHeaderFiles.end()) &&
-          (mocsIncluded.find(headerName) == mocsIncluded.end())) {
-        this->MocParseHeaderContent(headerName, contentText, mocsNotIncluded,
-                                    mocDepends);
+  // 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;
       }
-      // Parse header content for UIC
-      if (uicHeaderFiles.find(headerName) != uicHeaderFiles.end()) {
-        this->UicParseContent(headerName, contentText, uisIncluded);
-      }
-    } else {
-      this->LogFileError(cmQtAutoGen::GEN, headerName,
-                         "Could not read header file");
-      success = false;
-      break;
     }
   }
   return success;
 }
 
-bool cmQtAutoGenerators::MocGenerateAll(
-  const std::map<std::string, std::string>& mocsIncluded,
-  const std::map<std::string, std::string>& mocsNotIncluded,
-  const std::map<std::string, std::set<std::string>>& mocDepends)
+bool cmQtAutoGenerators::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";
+    }
+  } 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);
+    return false;
+  }
+  return true;
+}
+
+void cmQtAutoGenerators::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();
+      }
+    }
+  }
+
+  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->Verbose) {
+          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));
+      }
+    }
+  }
+}
+
+/**
+ * @return True on success
+ */
+bool cmQtAutoGenerators::MocParseSourceContent(std::string const& absFilename,
+                                               std::string const& contentText)
+{
+  if (this->Verbose) {
+    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
+  };
+
+  // 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();
+      }
+    }
+  }
+
+  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;
+  }
+
+  // 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;
+    }
+  }
+
+  // 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);
+            } 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);
+            }
+          }
+        } 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;
+        }
+      }
+    } 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);
+        }
+      } 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
+      {
+        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);
+              break;
+            }
+          }
+        }
+      }
+      // 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;
+    }
+  }
+  return true;
+}
+
+void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename,
+                                               std::string const& contentText)
+{
+  if (this->Verbose) {
+    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)) {
+      static std::string const prefix = "moc_";
+      static std::string const suffix = this->ConfigSuffix + ".cpp";
+
+      auto job = cm::make_unique<MocJobAuto>();
+      job->SourceFile = absFilename;
+      {
+        std::string& bld = job->BuildFileRel;
+        bld = this->FilePathChecksum.getPart(absFilename);
+        bld += "/";
+        bld += prefix;
+        bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
+        bld += suffix;
+      }
+      this->MocFindDepends(absFilename, contentText, job->Depends);
+      this->MocJobsAuto.push_back(std::move(job));
+    }
+  }
+}
+
+bool cmQtAutoGenerators::MocGenerateAll()
 {
   if (!this->MocEnabled()) {
     return true;
   }
 
-  // Look for name collisions
+  // Look for name collisions in included moc files
   {
-    std::multimap<std::string, std::string> collisions;
-    // Test merged map of included and notIncluded
-    std::map<std::string, std::string> mergedMocs(mocsIncluded);
-    mergedMocs.insert(mocsNotIncluded.begin(), mocsNotIncluded.end());
-    if (this->NameCollisionTest(mergedMocs, collisions)) {
-      std::string msg = "The same moc file will be generated "
-                        "from different sources.\n"
-                        "To avoid this error either\n"
-                        " - rename the source files or\n"
-                        " - do not include the (moc_NAME.cpp|NAME.moc) file";
-      this->LogNameCollisionError(cmQtAutoGen::MOC, msg, collisions);
+    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";
+          }
+        }
+      }
+      this->LogError(cmQtAutoGen::MOC, emsg);
       return false;
     }
   }
 
-  // Generate moc_predefs
+  // (Re)generate moc_predefs.h on demand
   if (!this->MocPredefsCmd.empty()) {
     if (this->MocSettingsChanged ||
-        FileAbsentOrOlder(this->MocPredefsFileAbs, this->SettingsFile)) {
+        !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
       if (this->Verbose) {
         this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
       }
@@ -1216,16 +1381,9 @@
       {
         // 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);
-        }
         // Add options
-        cmd.insert(cmd.end(), this->MocOptions.begin(),
-                   this->MocOptions.end());
+        cmd.insert(cmd.end(), this->MocAllOptions.begin(),
+                   this->MocAllOptions.end());
         // Execute command
         if (!this->RunCommand(cmd, output)) {
           this->LogCommandError(cmQtAutoGen::MOC,
@@ -1240,6 +1398,8 @@
                             output)) {
           this->MocPredefsChanged = true;
         } else {
+          this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
+                             "moc_predefs file writing failed");
           return false;
         }
       } else {
@@ -1251,104 +1411,157 @@
         cmSystemTools::Touch(this->MocPredefsFileAbs, false);
       }
     }
-  }
 
-  // Generate moc files that are included by source files.
-  for (auto const& it : mocsIncluded) {
-    if (!this->MocGenerateFile(it.first, it.second, mocDepends, true)) {
-      if (this->MocRunFailed) {
-        return false;
-      }
+    // Add moc_predefs.h to moc file dependecies
+    for (auto const& item : this->MocJobsIncluded) {
+      item->Depends.insert(this->MocPredefsFileAbs);
+    }
+    for (auto const& item : this->MocJobsAuto) {
+      item->Depends.insert(this->MocPredefsFileAbs);
     }
   }
 
+  // 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 mocCompFileGenerated = false;
-  for (auto const& it : mocsNotIncluded) {
-    if (this->MocGenerateFile(it.first, it.second, mocDepends, false)) {
-      mocCompFileGenerated = true;
-    } else {
-      if (this->MocRunFailed) {
-        return false;
-      }
+  bool autoNameGenerated = false;
+  for (auto const& item : this->MocJobsAuto) {
+    if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
+      return false;
     }
   }
 
   // Compose mocs compilation file content
-  std::string automocSource;
   {
-    std::ostringstream ost;
-    ost << "/* This file is autogenerated, do not edit*/\n";
-    if (mocsNotIncluded.empty()) {
+    std::string mocs = "/* This file is autogenerated, do not edit*/\n";
+    if (this->MocJobsAuto.empty()) {
       // Dummy content
-      ost << "enum some_compilers { need_more_than_nothing };\n";
+      mocs += "enum some_compilers { need_more_than_nothing };\n";
     } else {
       // Valid content
-      for (auto const& it : mocsNotIncluded) {
-        ost << "#include \"" << it.second << "\"\n";
+      for (const auto& item : this->MocJobsAuto) {
+        mocs += "#include \"";
+        mocs += item->BuildFileRel;
+        mocs += "\"\n";
       }
     }
-    automocSource = ost.str();
-  }
 
-  if (this->FileDiffers(this->MocCompFileAbs, automocSource)) {
-    // Actually write mocs compilation file
-    if (this->Verbose) {
-      this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
+    if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
+      // Actually write mocs compilation file
+      if (this->Verbose) {
+        this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
+      }
+      if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
+        this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
+                           "mocs compilation file writing failed");
+        return false;
+      }
+    } else if (autoNameGenerated) {
+      // Only touch mocs compilation file
+      if (this->Verbose) {
+        this->LogInfo(cmQtAutoGen::MOC,
+                      "Touching mocs compilation " + this->MocCompFileRel);
+      }
+      cmSystemTools::Touch(this->MocCompFileAbs, false);
     }
-    if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs,
-                         automocSource)) {
-      return false;
-    }
-  } else if (mocCompFileGenerated) {
-    // Only touch mocs compilation file
-    if (this->Verbose) {
-      this->LogInfo(cmQtAutoGen::MOC,
-                    "Touching mocs compilation " + this->MocCompFileRel);
-    }
-    cmSystemTools::Touch(this->MocCompFileAbs, false);
   }
 
   return true;
 }
 
 /**
- * @return True if a moc file was created. False may indicate an error.
+ * @return True on success
  */
-bool cmQtAutoGenerators::MocGenerateFile(
-  const std::string& sourceFile, const std::string& mocFileName,
-  const std::map<std::string, std::set<std::string>>& mocDepends,
-  bool included)
+bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& mocJob,
+                                         bool* generated)
 {
-  bool mocGenerated = false;
-  bool generateMoc = this->MocSettingsChanged || this->MocPredefsChanged;
+  bool success = true;
 
-  const std::string mocFileRel =
-    included ? (this->AutogenIncludeDir + mocFileName) : mocFileName;
-  const std::string mocFileAbs =
-    cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, mocFileRel);
+  std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
+    this->AutogenBuildDir, mocJob.BuildFileRel);
 
-  if (!generateMoc) {
-    // Test if the source file is newer that the build file
-    generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile);
-    if (!generateMoc) {
-      // Test if a dependency file changed
-      std::map<std::string, std::set<std::string>>::const_iterator dit =
-        mocDepends.find(sourceFile);
-      if (dit != mocDepends.end()) {
-        for (std::string const& fit : dit->second) {
-          if (FileAbsentOrOlder(mocFileAbs, fit)) {
-            generateMoc = true;
-            break;
-          }
-        }
+  bool generate = false;
+  std::string generateReason;
+  if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
+    if (this->Verbose) {
+      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 (!generate && this->MocSettingsChanged) {
+    if (this->Verbose) {
+      generateReason = "Generating ";
+      generateReason += cmQtAutoGen::Quoted(mocFileAbs);
+      generateReason += " from ";
+      generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
+      generateReason += " because the MOC settings changed";
+    }
+    generate = true;
+  }
+  if (!generate && this->MocPredefsChanged) {
+    if (this->Verbose) {
+      generateReason = "Generating ";
+      generateReason += cmQtAutoGen::Quoted(mocFileAbs);
+      generateReason += " from ";
+      generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
+      generateReason += " because moc_predefs.h changed";
+    }
+    generate = true;
+  }
+  if (!generate) {
+    std::string error;
+    if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
+      if (this->Verbose) {
+        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 (generateMoc) {
+  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->Verbose) {
+          generateReason = "Generating ";
+          generateReason += cmQtAutoGen::Quoted(mocFileAbs);
+          generateReason += " from ";
+          generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
+          generateReason += " because it is older than ";
+          generateReason += cmQtAutoGen::Quoted(depFile);
+        }
+        generate = true;
+        break;
+      }
+      if (!error.empty()) {
+        this->LogError(cmQtAutoGen::MOC, error);
+        success = false;
+        break;
+      }
+    }
+  }
+
+  if (generate) {
     // Log
     if (this->Verbose) {
-      this->LogBold("Generating MOC source " + mocFileRel);
+      this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
+      this->LogInfo(cmQtAutoGen::MOC, generateReason);
     }
 
     // Make sure the parent directory exists
@@ -1356,15 +1569,9 @@
       // Compose moc command
       std::vector<std::string> cmd;
       cmd.push_back(this->MocExecutable);
-      // 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);
-      }
       // Add options
-      cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end());
+      cmd.insert(cmd.end(), this->MocAllOptions.begin(),
+                 this->MocAllOptions.end());
       // Add predefs include
       if (!this->MocPredefsFileAbs.empty()) {
         cmd.push_back("--include");
@@ -1372,37 +1579,111 @@
       }
       cmd.push_back("-o");
       cmd.push_back(mocFileAbs);
-      cmd.push_back(sourceFile);
+      cmd.push_back(mocJob.SourceFile);
 
       // Execute moc command
       std::string output;
       if (this->RunCommand(cmd, output)) {
         // Success
-        mocGenerated = true;
+        if (generated != nullptr) {
+          *generated = true;
+        }
       } else {
-        this->LogCommandError(cmQtAutoGen::MOC, "moc failed for\n  " +
-                                cmQtAutoGen::Quoted(sourceFile),
-                              cmd, output);
+        // Moc command failed
+        {
+          std::string emsg = "moc failed for\n  ";
+          emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
+          this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
+        }
         cmSystemTools::RemoveFile(mocFileAbs);
-        this->MocRunFailed = true;
+        success = false;
       }
     } else {
       // Parent directory creation failed
-      this->MocRunFailed = true;
+      success = false;
     }
   }
-  return mocGenerated;
+  return success;
+}
+
+/**
+ * @brief Tests if the file name is in the skip list
+ */
+bool cmQtAutoGenerators::UicSkip(std::string const& absFilename) const
+{
+  if (this->UicEnabled()) {
+    // Test if the file name is on the skip list
+    if (!ListContains(this->UicSkipList, absFilename)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerators::UicParseContent(std::string const& absFilename,
+                                         std::string const& contentText)
+{
+  if (this->Verbose) {
+    this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
+  }
+
+  std::vector<std::string> includes;
+  // Extracte includes
+  {
+    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();
+      }
+    }
+  }
+
+  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 (!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;
 }
 
 bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile,
-                                             const std::string& sourceFile,
-                                             const std::string& searchPath,
-                                             const std::string& searchFile)
+                                             std::string const& sourceFile,
+                                             std::string const& includeString)
 {
   bool success = false;
-  std::vector<std::string> testFiles;
+  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;
@@ -1410,7 +1691,7 @@
     }
     // Vicinity of the source
     {
-      const std::string sourcePath = SubDirPrefix(sourceFile);
+      std::string const sourcePath = SubDirPrefix(sourceFile);
       testFiles.push_back(sourcePath + searchFile);
       if (!searchPath.empty()) {
         testFiles.push_back(sourcePath + searchFileFull);
@@ -1440,76 +1721,75 @@
 
   // Log error
   if (!success) {
-    std::ostringstream ost;
-    ost << "Could not find " << cmQtAutoGen::Quoted(searchFile) << " in\n";
+    std::string emsg = "Could not find ";
+    emsg += cmQtAutoGen::Quoted(searchFile);
+    emsg += " in\n";
     for (std::string const& testFile : testFiles) {
-      ost << "  " << cmQtAutoGen::Quoted(testFile) << "\n";
+      emsg += "  ";
+      emsg += cmQtAutoGen::Quoted(testFile);
+      emsg += "\n";
     }
-    this->LogFileError(cmQtAutoGen::UIC, sourceFile, ost.str());
+    this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
   }
 
   return success;
 }
 
-bool cmQtAutoGenerators::UicGenerateAll(
-  const std::map<std::string, std::vector<std::string>>& uisIncluded)
+bool cmQtAutoGenerators::UicGenerateAll()
 {
   if (!this->UicEnabled()) {
     return true;
   }
 
-  // single map with input / output names
-  std::map<std::string, std::map<std::string, std::string>> sourceGenMap;
+  // Look for name collisions in included uic files
   {
-    // Collision lookup map
-    std::map<std::string, std::string> testMap;
-    // Compile maps
-    for (auto const& sit : uisIncluded) {
-      const std::string& source(sit.first);
-      const std::vector<std::string>& sourceIncs(sit.second);
-      // insert new source/destination map
-      std::map<std::string, std::string>& uiGenMap = sourceGenMap[source];
-      for (std::string const& inc : sourceIncs) {
-        // Remove ui_ from the begin filename by substr()
-        const std::string uiBasePath = SubDirPrefix(inc);
-        const std::string uiBaseName =
-          cmSystemTools::GetFilenameWithoutLastExtension(inc).substr(3);
-        const std::string uiFileName = uiBaseName + ".ui";
-        std::string uiInputFile;
-        if (UicFindIncludedFile(uiInputFile, source, uiBasePath, uiFileName)) {
-          std::string uiOutputFile = uiBasePath;
-          uiOutputFile += "ui_";
-          uiOutputFile += uiBaseName;
-          uiOutputFile += ".h";
-          cmSystemTools::ReplaceString(uiOutputFile, "..", "__");
-          uiGenMap[uiInputFile] = uiOutputFile;
-          testMap[uiInputFile] = uiOutputFile;
-        } else {
-          return false;
+    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";
+          }
         }
       }
-    }
-    // look for name collisions
-    {
-      std::multimap<std::string, std::string> collisions;
-      if (this->NameCollisionTest(testMap, collisions)) {
-        std::string msg = "The same ui_NAME.h file will be generated "
-                          "from different sources.\n"
-                          "To avoid this error rename the source files.\n";
-        this->LogNameCollisionError(cmQtAutoGen::UIC, msg, collisions);
-        return false;
-      }
+      this->LogError(cmQtAutoGen::UIC, emsg);
+      return false;
     }
   }
 
-  // generate ui files
-  for (auto const& srcItem : sourceGenMap) {
-    for (auto const& item : srcItem.second) {
-      if (!this->UicGenerateFile(srcItem.first, item.first, item.second)) {
-        if (this->UicRunFailed) {
-          return false;
-        }
-      }
+  // Generate ui header files
+  for (const auto& item : this->UicJobs) {
+    if (!this->UicGenerateFile(*item)) {
+      return false;
     }
   }
 
@@ -1517,27 +1797,59 @@
 }
 
 /**
- * @return True if a uic file was created. False may indicate an error.
+ * @return True on success
  */
-bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName,
-                                         const std::string& uiInputFile,
-                                         const std::string& uiOutputFile)
+bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob)
 {
-  bool uicGenerated = false;
-  bool generateUic = this->UicSettingsChanged;
+  bool success = true;
 
-  const std::string uicFileRel = this->AutogenIncludeDir + uiOutputFile;
-  const std::string uicFileAbs =
-    cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, uicFileRel);
+  std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
+    this->AutogenBuildDir, uicJob.BuildFileRel);
 
-  if (!generateUic) {
-    // Test if the source file is newer that the build file
-    generateUic = FileAbsentOrOlder(uicFileAbs, uiInputFile);
+  bool generate = false;
+  std::string generateReason;
+  if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
+    if (this->Verbose) {
+      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 (generateUic) {
+  if (!generate && this->UicSettingsChanged) {
+    if (this->Verbose) {
+      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->Verbose) {
+        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->Verbose) {
-      this->LogBold("Generating UIC header " + uicFileRel);
+      this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
+      this->LogInfo(cmQtAutoGen::UIC, generateReason);
     }
 
     // Make sure the parent directory exists
@@ -1547,7 +1859,7 @@
       cmd.push_back(this->UicExecutable);
       {
         std::vector<std::string> allOpts = this->UicTargetOptions;
-        auto optionIt = this->UicOptions.find(uiInputFile);
+        auto optionIt = this->UicOptions.find(uicJob.SourceFile);
         if (optionIt != this->UicOptions.end()) {
           cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
                                        (this->QtMajorVersion == "5"));
@@ -1556,31 +1868,29 @@
       }
       cmd.push_back("-o");
       cmd.push_back(uicFileAbs);
-      cmd.push_back(uiInputFile);
+      cmd.push_back(uicJob.SourceFile);
 
       std::string output;
       if (this->RunCommand(cmd, output)) {
         // Success
-        uicGenerated = true;
       } else {
         // Command failed
         {
-          std::ostringstream ost;
-          ost << "uic failed for\n"
-              << "  " << cmQtAutoGen::Quoted(uiInputFile) << "\n"
-              << "needed by\n"
-              << "  " << cmQtAutoGen::Quoted(realName);
-          this->LogCommandError(cmQtAutoGen::UIC, ost.str(), cmd, output);
+          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);
         }
         cmSystemTools::RemoveFile(uicFileAbs);
-        this->UicRunFailed = true;
+        success = false;
       }
     } else {
       // Parent directory creation failed
-      this->UicRunFailed = true;
+      success = false;
     }
   }
-  return uicGenerated;
+  return success;
 }
 
 bool cmQtAutoGenerators::RccGenerateAll()
@@ -1589,24 +1899,22 @@
     return true;
   }
 
-  // Generate qrc files
-  for (RccJob const& rccJob : this->RccJobs) {
+  // Generate rcc files
+  for (const RccJob& rccJob : this->RccJobs) {
     if (!this->RccGenerateFile(rccJob)) {
-      if (this->RccRunFailed) {
-        return false;
-      }
+      return false;
     }
   }
   return true;
 }
 
 /**
- * @return True if a rcc file was created. False may indicate an error.
+ * @return True on success
  */
 bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob)
 {
+  bool success = true;
   bool rccGenerated = false;
-  bool generateRcc = this->RccSettingsChanged;
 
   std::string rccFileAbs;
   if (this->ConfigSuffix.empty()) {
@@ -1618,46 +1926,114 @@
     rccFileAbs += this->ConfigSuffix;
     rccFileAbs += cmSystemTools::GetFilenameLastExtension(rccJob.RccFile);
   }
-  const std::string rccFileRel = cmSystemTools::RelativePath(
+  std::string const rccFileRel = cmSystemTools::RelativePath(
     this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
 
   // Check if regeneration is required
-  if (!generateRcc) {
-    // Test if the resources list file is newer than build file
-    generateRcc = FileAbsentOrOlder(rccFileAbs, rccJob.QrcFile);
-    if (!generateRcc) {
-      // Acquire input file list
-      std::vector<std::string> readFiles;
-      const std::vector<std::string>* files = &rccJob.Inputs;
-      if (files->empty()) {
-        // Read input file list from qrc file
-        std::string error;
-        if (cmQtAutoGen::RccListInputs(this->QtMajorVersion,
-                                       this->RccExecutable, rccJob.QrcFile,
-                                       readFiles, &error)) {
-          files = &readFiles;
-        } else {
-          files = nullptr;
-          this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error);
-          this->RccRunFailed = true;
+  bool generate = false;
+  std::string generateReason;
+  if (!cmSystemTools::FileExists(rccJob.QrcFile)) {
+    {
+      std::string error = "Could not find the file\n  ";
+      error += cmQtAutoGen::Quoted(rccJob.QrcFile);
+      this->LogError(cmQtAutoGen::RCC, error);
+    }
+    success = false;
+  }
+  if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
+    if (this->Verbose) {
+      generateReason = "Generating ";
+      generateReason += cmQtAutoGen::Quoted(rccFileAbs);
+      generateReason += " from its source file ";
+      generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
+      generateReason += " because it doesn't exist";
+    }
+    generate = true;
+  }
+  if (success && !generate && this->RccSettingsChanged) {
+    if (this->Verbose) {
+      generateReason = "Generating ";
+      generateReason += cmQtAutoGen::Quoted(rccFileAbs);
+      generateReason += " from ";
+      generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
+      generateReason += " because the RCC settings changed";
+    }
+    generate = true;
+  }
+  if (success && !generate) {
+    std::string error;
+    if (FileIsOlderThan(rccFileAbs, rccJob.QrcFile, &error)) {
+      if (this->Verbose) {
+        generateReason = "Generating ";
+        generateReason += cmQtAutoGen::Quoted(rccFileAbs);
+        generateReason += " because it is older than ";
+        generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
+      }
+      generate = true;
+    } else {
+      if (!error.empty()) {
+        this->LogError(cmQtAutoGen::RCC, error);
+        success = false;
+      }
+    }
+  }
+  if (success && !generate) {
+    // Acquire input file list
+    std::vector<std::string> readFiles;
+    std::vector<std::string> const* files = nullptr;
+    if (!rccJob.Inputs.empty()) {
+      files = &rccJob.Inputs;
+    } else {
+      // Read input file list from qrc file
+      std::string error;
+      if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable,
+                                     rccJob.QrcFile, readFiles, &error)) {
+        files = &readFiles;
+      } else {
+        this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error);
+        success = false;
+      }
+    }
+    // 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(rccJob.QrcFile);
+          break;
+        }
+        if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
+          if (this->Verbose) {
+            generateReason = "Generating ";
+            generateReason += cmQtAutoGen::Quoted(rccFileAbs);
+            generateReason += " from ";
+            generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
+            generateReason += " because it is older than ";
+            generateReason += cmQtAutoGen::Quoted(resFile);
+          }
+          generate = true;
+          break;
+        }
+        if (!error.empty()) {
+          break;
         }
       }
-      // Test if any input file is newer than the build file
-      if (files != nullptr) {
-        for (std::string const& file : *files) {
-          if (FileAbsentOrOlder(rccFileAbs, file)) {
-            generateRcc = true;
-            break;
-          }
-        }
+      // Print error
+      if (!error.empty()) {
+        this->LogError(cmQtAutoGen::RCC, error);
+        success = false;
       }
     }
   }
   // Regenerate on demand
-  if (generateRcc) {
+  if (generate) {
     // Log
     if (this->Verbose) {
       this->LogBold("Generating RCC source " + rccFileRel);
+      this->LogInfo(cmQtAutoGen::RCC, generateReason);
     }
 
     // Make sure the parent directory exists
@@ -1675,22 +2051,24 @@
         // Success
         rccGenerated = true;
       } else {
-        this->LogCommandError(cmQtAutoGen::RCC, "rcc failed for\n  " +
-                                cmQtAutoGen::Quoted(rccJob.QrcFile),
-                              cmd, output);
+        {
+          std::string emsg = "rcc failed for\n  ";
+          emsg += cmQtAutoGen::Quoted(rccJob.QrcFile);
+          this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
+        }
         cmSystemTools::RemoveFile(rccFileAbs);
-        this->RccRunFailed = true;
+        success = false;
       }
     } else {
       // Parent directory creation failed
-      this->RccRunFailed = true;
+      success = false;
     }
   }
   // For a multi configuration generator generate a wrapper file
-  if (!this->ConfigSuffix.empty() && !this->RccRunFailed) {
+  if (success && !this->ConfigSuffix.empty()) {
     // Wrapper file name
-    const std::string& wrapperFileAbs = rccJob.RccFile;
-    const std::string wrapperFileRel = cmSystemTools::RelativePath(
+    std::string const& wrapperFileAbs = rccJob.RccFile;
+    std::string const wrapperFileRel = cmSystemTools::RelativePath(
       this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
     // Wrapper file content
     std::string content = "// This is an autogenerated configuration "
@@ -1700,17 +2078,17 @@
     content += "\"\n";
     // Write content to file
     if (this->FileDiffers(wrapperFileAbs, content)) {
-      // Write new wrapper file if the content differs
+      // Write new wrapper file
       if (this->Verbose) {
         this->LogBold("Generating RCC wrapper " + wrapperFileRel);
       }
       if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
-        // Error
-        rccGenerated = false;
-        this->RccRunFailed = true;
+        this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
+                           "rcc wrapper file writing failed");
+        success = false;
       }
     } else if (rccGenerated) {
-      // Only touch wrapper file if the content matches
+      // Just touch the wrapper file
       if (this->Verbose) {
         this->LogInfo(cmQtAutoGen::RCC,
                       "Touching RCC wrapper " + wrapperFileRel);
@@ -1719,10 +2097,10 @@
     }
   }
 
-  return rccGenerated;
+  return success;
 }
 
-void cmQtAutoGenerators::LogBold(const std::string& message) const
+void cmQtAutoGenerators::LogBold(std::string const& message) const
 {
   cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
                                      cmsysTerminal_Color_ForegroundBold,
@@ -1730,7 +2108,7 @@
 }
 
 void cmQtAutoGenerators::LogInfo(cmQtAutoGen::GeneratorType genType,
-                                 const std::string& message) const
+                                 std::string const& message) const
 {
   std::string msg = cmQtAutoGen::GeneratorName(genType);
   msg += ": ";
@@ -1742,7 +2120,7 @@
 }
 
 void cmQtAutoGenerators::LogWarning(cmQtAutoGen::GeneratorType genType,
-                                    const std::string& message) const
+                                    std::string const& message) const
 {
   std::string msg = cmQtAutoGen::GeneratorName(genType);
   msg += " warning:";
@@ -1758,182 +2136,102 @@
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
-  msg += "\n";
+  msg.push_back('\n');
   cmSystemTools::Stdout(msg.c_str(), msg.size());
 }
 
 void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::GeneratorType genType,
-                                        const std::string& filename,
-                                        const std::string& message) const
+                                        std::string const& filename,
+                                        std::string const& message) const
 {
-  std::string emsg = "  ";
-  emsg += cmQtAutoGen::Quoted(filename);
-  emsg += "\n";
+  std::string msg = "  ";
+  msg += cmQtAutoGen::Quoted(filename);
+  msg.push_back('\n');
   // Message
-  emsg += message;
-  this->LogWarning(genType, emsg);
+  msg += message;
+  this->LogWarning(genType, msg);
 }
 
 void cmQtAutoGenerators::LogError(cmQtAutoGen::GeneratorType genType,
-                                  const std::string& message) const
+                                  std::string const& message) const
 {
   std::string msg;
   msg.push_back('\n');
-  msg = cmQtAutoGen::GeneratorName(genType);
-  msg += " error:";
-  if (message.find('\n') == std::string::npos) {
-    // Single line message
-    msg.push_back(' ');
-  } else {
-    // Multi line message
-    msg.push_back('\n');
-  }
+  msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
   // Message
   msg += message;
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
-  msg += "\n";
+  msg.push_back('\n');
   cmSystemTools::Stderr(msg.c_str(), msg.size());
 }
 
 void cmQtAutoGenerators::LogFileError(cmQtAutoGen::GeneratorType genType,
-                                      const std::string& filename,
-                                      const std::string& message) const
+                                      std::string const& filename,
+                                      std::string const& message) const
 {
   std::string emsg = "  ";
   emsg += cmQtAutoGen::Quoted(filename);
-  emsg += "\n";
+  emsg += '\n';
   // Message
   emsg += message;
   this->LogError(genType, emsg);
 }
 
-void cmQtAutoGenerators::LogNameCollisionError(
-  cmQtAutoGen::GeneratorType genType, const std::string& message,
-  const std::multimap<std::string, std::string>& collisions) const
-{
-  std::string emsg;
-  // Add message
-  if (!message.empty()) {
-    emsg += message;
-    if (emsg.back() != '\n') {
-      emsg.push_back('\n');
-    }
-  }
-  // Append collision list
-  for (auto const& item : collisions) {
-    emsg += "  ";
-    emsg += item.first;
-    emsg += " -> ";
-    emsg += item.second;
-    emsg += "\n";
-  }
-  this->LogError(genType, emsg);
-}
-
 void cmQtAutoGenerators::LogCommandError(
-  cmQtAutoGen::GeneratorType genType, const std::string& message,
-  const std::vector<std::string>& command, const std::string& output) const
+  cmQtAutoGen::GeneratorType genType, std::string const& message,
+  std::vector<std::string> const& command, std::string const& output) const
 {
   std::string msg;
   msg.push_back('\n');
-  msg += cmQtAutoGen::GeneratorName(genType);
-  msg += " subprocess error: ";
+  msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error");
   msg += message;
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
   msg.push_back('\n');
-  msg += "Command\n";
-  msg += "-------\n";
+  msg += HeadLine("Command");
   msg += QuotedCommand(command);
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
   msg.push_back('\n');
-  msg += "Output\n";
-  msg += "------\n";
+  msg += HeadLine("Output");
   msg += output;
   if (msg.back() != '\n') {
     msg.push_back('\n');
   }
-  msg += "\n";
+  msg.push_back('\n');
   cmSystemTools::Stderr(msg.c_str(), msg.size());
 }
 
 /**
- * @brief Collects name collisions as output/input pairs
- * @return True if there were collisions
- */
-bool cmQtAutoGenerators::NameCollisionTest(
-  const std::map<std::string, std::string>& genFiles,
-  std::multimap<std::string, std::string>& collisions) const
-{
-  typedef std::map<std::string, std::string>::const_iterator Iter;
-  typedef std::map<std::string, std::string>::value_type VType;
-  for (Iter ait = genFiles.begin(); ait != genFiles.end(); ++ait) {
-    bool first_match(true);
-    for (Iter bit = (++Iter(ait)); bit != genFiles.end(); ++bit) {
-      if (ait->second == bit->second) {
-        if (first_match) {
-          if (collisions.find(ait->second) != collisions.end()) {
-            // We already know of this collision from before
-            break;
-          }
-          collisions.insert(VType(ait->second, ait->first));
-          first_match = false;
-        }
-        collisions.insert(VType(bit->second, bit->first));
-      }
-    }
-  }
-
-  return !collisions.empty();
-}
-
-/**
- * @brief Generates a file path based on the checksum of the source file path
- * @return The path
- */
-std::string cmQtAutoGenerators::ChecksumedPath(
-  const std::string& sourceFile, const std::string& basePrefix,
-  const std::string& baseSuffix) const
-{
-  std::string res = FPathChecksum.getPart(sourceFile);
-  res += "/";
-  res += basePrefix;
-  res += cmSystemTools::GetFilenameWithoutLastExtension(sourceFile);
-  res += baseSuffix;
-  return res;
-}
-
-/**
  * @brief Generates the parent directory of the given file on demand
  * @return True on success
  */
 bool cmQtAutoGenerators::MakeParentDirectory(
-  cmQtAutoGen::GeneratorType genType, const std::string& filename) const
+  cmQtAutoGen::GeneratorType genType, std::string const& filename) const
 {
   bool success = true;
-  const std::string dirName = cmSystemTools::GetFilenamePath(filename);
+  std::string const dirName = cmSystemTools::GetFilenamePath(filename);
   if (!dirName.empty()) {
-    success = cmSystemTools::MakeDirectory(dirName);
-    if (!success) {
+    if (!cmSystemTools::MakeDirectory(dirName)) {
       this->LogFileError(genType, filename,
                          "Could not create parent directory");
+      success = false;
     }
   }
   return success;
 }
 
-bool cmQtAutoGenerators::FileDiffers(const std::string& filename,
-                                     const std::string& content)
+bool cmQtAutoGenerators::FileDiffers(std::string const& filename,
+                                     std::string const& content)
 {
   bool differs = true;
   {
     std::string oldContents;
-    if (ReadAll(oldContents, filename)) {
+    if (ReadFile(oldContents, filename)) {
       differs = (oldContents != content);
     }
   }
@@ -1941,14 +2239,15 @@
 }
 
 bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::GeneratorType genType,
-                                   const std::string& filename,
-                                   const std::string& content)
+                                   std::string const& filename,
+                                   std::string const& content)
 {
   std::string error;
   // Make sure the parent directory exists
   if (this->MakeParentDirectory(genType, filename)) {
     cmsys::ofstream outfile;
-    outfile.open(filename.c_str(), std::ios::trunc);
+    outfile.open(filename.c_str(),
+                 (std::ios::out | std::ios::binary | std::ios::trunc));
     if (outfile) {
       outfile << content;
       // Check for write errors
@@ -1970,7 +2269,7 @@
  * @brief Runs a command and returns true on success
  * @return True on success
  */
-bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command,
+bool cmQtAutoGenerators::RunCommand(std::vector<std::string> const& command,
                                     std::string& output) const
 {
   // Log command
@@ -1992,7 +2291,7 @@
  * @return True on success
  */
 bool cmQtAutoGenerators::FindHeader(std::string& header,
-                                    const std::string& testBasePath) const
+                                    std::string const& testBasePath) const
 {
   for (std::string const& ext : this->HeaderExtensions) {
     std::string testFilePath(testBasePath);
@@ -2005,56 +2304,3 @@
   }
   return false;
 }
-
-std::string cmQtAutoGenerators::MocFindHeader(
-  const std::string& sourcePath, const std::string& includeBase) const
-{
-  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 (FindHeader(header, fullPath)) {
-        break;
-      }
-    }
-  }
-  // Sanitize
-  if (!header.empty()) {
-    header = cmSystemTools::GetRealPath(header);
-  }
-  return header;
-}
-
-bool cmQtAutoGenerators::MocFindIncludedFile(
-  std::string& absFile, const std::string& sourcePath,
-  const std::string& includeString) const
-{
-  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;
-      }
-    }
-  }
-  return success;
-}
diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h
index 5d3ad74..0b502de 100644
--- a/Source/cmQtAutoGenerators.h
+++ b/Source/cmQtAutoGenerators.h
@@ -10,6 +10,7 @@
 #include "cmsys/RegularExpression.hxx"
 
 #include <map>
+#include <memory> // IWYU pragma: keep
 #include <set>
 #include <string>
 #include <vector>
@@ -21,7 +22,7 @@
   CM_DISABLE_COPY(cmQtAutoGenerators)
 public:
   cmQtAutoGenerators();
-  bool Run(const std::string& targetDirectory, const std::string& config);
+  bool Run(std::string const& targetDirectory, std::string const& config);
 
 private:
   // -- Types
@@ -37,7 +38,7 @@
     {
     }
 
-    KeyRegExp(const std::string& key, const std::string& regExp)
+    KeyRegExp(std::string const& key, std::string const& regExp)
       : Key(key)
       , RegExp(regExp)
     {
@@ -47,6 +48,38 @@
     cmsys::RegularExpression RegExp;
   };
 
+  /// @brief Source file job
+  struct SourceJob
+  {
+    bool Moc = false;
+    bool Uic = false;
+  };
+
+  /// @brief MOC job
+  struct MocJobAuto
+  {
+    std::string SourceFile;
+    std::string BuildFileRel;
+    std::set<std::string> Depends;
+  };
+
+  /// @brief MOC job
+  struct MocJobIncluded : MocJobAuto
+  {
+    bool DependsValid = false;
+    std::string Includer;
+    std::string IncludeString;
+  };
+
+  /// @brief UIC job
+  struct UicJob
+  {
+    std::string SourceFile;
+    std::string BuildFileRel;
+    std::string Includer;
+    std::string IncludeString;
+  };
+
   /// @brief RCC job
   struct RccJob
   {
@@ -56,149 +89,110 @@
     std::vector<std::string> Inputs;
   };
 
-  // -- Configuration
-  bool MocDependFilterPush(const std::string& key, const std::string& regExp);
-  bool ReadAutogenInfoFile(cmMakefile* makefile,
-                           const std::string& targetDirectory,
-                           const std::string& config);
-
-  bool MocEnabled() const { return !this->MocExecutable.empty(); }
-  bool UicEnabled() const { return !this->UicExecutable.empty(); }
-  bool RccEnabled() const { return !this->RccExecutable.empty(); }
+  // -- Initialization
+  bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory,
+                    std::string const& config);
 
   // -- Settings file
   void SettingsFileRead(cmMakefile* makefile);
   bool SettingsFileWrite();
-
-  bool AnySettingsChanged() const
+  bool SettingsChanged() const
   {
     return (this->MocSettingsChanged || this->RccSettingsChanged ||
             this->UicSettingsChanged);
   }
 
-  // -- Init and run
-  void Init(cmMakefile* makefile);
-  bool RunAutogen();
+  // -- Central processing
+  bool Process();
 
-  // -- Content analysis
-  bool MocRequired(const std::string& contentText,
+  // -- 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);
-  void MocFindDepends(
-    const std::string& absFilename, const std::string& contentText,
-    std::map<std::string, std::set<std::string>>& mocDepends);
+  // 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 MocSkip(const std::string& absFilename) const;
-  bool UicSkip(const std::string& absFilename) const;
+  bool MocGenerateAll();
+  bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr);
 
-  bool ParseSourceFile(
-    const std::string& absFilename,
-    std::map<std::string, std::string>& mocsIncluded,
-    std::map<std::string, std::set<std::string>>& mocDepends,
-    std::map<std::string, std::vector<std::string>>& includedUis,
-    bool relaxed);
+  // -- 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);
 
-  void SearchHeadersForSourceFile(const std::string& absFilename,
-                                  std::set<std::string>& mocHeaderFiles,
-                                  std::set<std::string>& uicHeaderFiles) const;
-
-  bool ParseHeaders(
-    const std::set<std::string>& mocHeaderFiles,
-    const std::set<std::string>& uicHeaderFiles,
-    const std::map<std::string, std::string>& mocsIncluded,
-    std::map<std::string, std::string>& mocsNotIncluded,
-    std::map<std::string, std::set<std::string>>& mocDepends,
-    std::map<std::string, std::vector<std::string>>& includedUis);
-
-  void UicParseContent(
-    const std::string& fileName, const std::string& contentText,
-    std::map<std::string, std::vector<std::string>>& includedUis);
-
-  std::string MocMacroNamesString() const;
-  std::string MocHeaderSuffixesString() const;
-
-  bool MocParseSourceContent(
-    const std::string& absFilename, const std::string& contentText,
-    std::map<std::string, std::string>& mocsIncluded,
-    std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed);
-
-  void MocParseHeaderContent(
-    const std::string& absFilename, const std::string& contentText,
-    std::map<std::string, std::string>& mocsNotIncluded,
-    std::map<std::string, std::set<std::string>>& mocDepends);
-
-  // -- Moc file generation
-  bool MocGenerateAll(
-    const std::map<std::string, std::string>& mocsIncluded,
-    const std::map<std::string, std::string>& mocsNotIncluded,
-    const std::map<std::string, std::set<std::string>>& mocDepends);
-  bool MocGenerateFile(
-    const std::string& sourceFile, const std::string& mocFileName,
-    const std::map<std::string, std::set<std::string>>& mocDepends,
-    bool included);
-
-  // -- Uic file generation
-  bool UicFindIncludedFile(std::string& absFile, const std::string& sourceFile,
-                           const std::string& searchPath,
-                           const std::string& searchFile);
-  bool UicGenerateAll(
-    const std::map<std::string, std::vector<std::string>>& includedUis);
-  bool UicGenerateFile(const std::string& realName,
-                       const std::string& uiInputFile,
-                       const std::string& uiOutputFile);
-
-  // -- Rcc file generation
+  // -- Rcc
+  bool RccEnabled() const { return !this->RccExecutable.empty(); }
   bool RccGenerateAll();
   bool RccGenerateFile(const RccJob& rccJob);
 
   // -- Log info
-  void LogBold(const std::string& message) const;
+  void LogBold(std::string const& message) const;
   void LogInfo(cmQtAutoGen::GeneratorType genType,
-               const std::string& message) const;
+               std::string const& message) const;
   // -- Log warning
   void LogWarning(cmQtAutoGen::GeneratorType genType,
-                  const std::string& message) const;
+                  std::string const& message) const;
   void LogFileWarning(cmQtAutoGen::GeneratorType genType,
-                      const std::string& filename,
-                      const std::string& message) const;
+                      std::string const& filename,
+                      std::string const& message) const;
   // -- Log error
   void LogError(cmQtAutoGen::GeneratorType genType,
-                const std::string& message) const;
+                std::string const& message) const;
   void LogFileError(cmQtAutoGen::GeneratorType genType,
-                    const std::string& filename,
-                    const std::string& message) const;
+                    std::string const& filename,
+                    std::string const& message) const;
   void LogCommandError(cmQtAutoGen::GeneratorType genType,
-                       const std::string& message,
-                       const std::vector<std::string>& command,
-                       const std::string& output) const;
-  void LogNameCollisionError(
-    cmQtAutoGen::GeneratorType genType, const std::string& message,
-    const std::multimap<std::string, std::string>& collisions) const;
+                       std::string const& message,
+                       std::vector<std::string> const& command,
+                       std::string const& output) const;
 
   // -- Utility
-  bool NameCollisionTest(
-    const std::map<std::string, std::string>& genFiles,
-    std::multimap<std::string, std::string>& collisions) const;
-  std::string ChecksumedPath(const std::string& sourceFile,
-                             const std::string& basePrefix,
-                             const std::string& baseSuffix) const;
   bool MakeParentDirectory(cmQtAutoGen::GeneratorType genType,
-                           const std::string& filename) const;
-  bool FileDiffers(const std::string& filename, const std::string& content);
+                           std::string const& filename) const;
+  bool FileDiffers(std::string const& filename, std::string const& content);
   bool FileWrite(cmQtAutoGen::GeneratorType genType,
-                 const std::string& filename, const std::string& content);
-
-  bool RunCommand(const std::vector<std::string>& command,
+                 std::string const& filename, std::string const& content);
+  bool FindHeader(std::string& header, std::string const& testBasePath) const;
+  bool RunCommand(std::vector<std::string> const& command,
                   std::string& output) const;
 
-  bool FindHeader(std::string& header, const std::string& testBasePath) const;
-
-  std::string MocFindHeader(const std::string& sourcePath,
-                            const std::string& includeBase) const;
-  bool MocFindIncludedFile(std::string& absFile, const std::string& sourceFile,
-                           const std::string& includeString) const;
-
   // -- Meta
   std::string ConfigSuffix;
+  std::string InfoFile;
+  // -- Settings
+  bool IncludeProjectDirsBefore;
+  bool Verbose;
+  bool ColorOutput;
+  std::string SettingsFile;
+  std::string SettingsStringMoc;
+  std::string SettingsStringUic;
+  std::string SettingsStringRcc;
   // -- Directories
   std::string ProjectSourceDir;
   std::string ProjectBinaryDir;
@@ -213,23 +207,14 @@
   std::string UicExecutable;
   std::string RccExecutable;
   // -- File lists
-  std::vector<std::string> Sources;
-  std::vector<std::string> Headers;
+  std::map<std::string, SourceJob> HeaderJobs;
+  std::map<std::string, SourceJob> SourceJobs;
   std::vector<std::string> HeaderExtensions;
-  cmFilePathChecksum FPathChecksum;
-  // -- Settings
-  bool IncludeProjectDirsBefore;
-  bool Verbose;
-  bool ColorOutput;
-  std::string SettingsFile;
-  std::string SettingsStringMoc;
-  std::string SettingsStringUic;
-  std::string SettingsStringRcc;
+  cmFilePathChecksum FilePathChecksum;
   // -- Moc
   bool MocSettingsChanged;
   bool MocPredefsChanged;
   bool MocRelaxedMode;
-  bool MocRunFailed;
   std::string MocCompFileRel;
   std::string MocCompFileAbs;
   std::string MocPredefsFileRel;
@@ -239,21 +224,23 @@
   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;
-  bool UicRunFailed;
   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;
   // -- Rcc
   bool RccSettingsChanged;
-  bool RccRunFailed;
   std::vector<RccJob> RccJobs;
 };
 
diff --git a/Tests/QtAutogen/sameName/CMakeLists.txt b/Tests/QtAutogen/sameName/CMakeLists.txt
index 4d2dcd9..f695875 100644
--- a/Tests/QtAutogen/sameName/CMakeLists.txt
+++ b/Tests/QtAutogen/sameName/CMakeLists.txt
@@ -17,7 +17,10 @@
   main.cpp
 )
 target_link_libraries(sameName ${QT_LIBRARIES})
-set_target_properties(sameName PROPERTIES AUTOMOC TRUE AUTORCC TRUE)
+set_target_properties(sameName PROPERTIES
+  AUTOMOC TRUE
+  AUTOUIC TRUE
+  AUTORCC TRUE)
 
 # Set different compression levels
 if (QT_TEST_VERSION STREQUAL 4)
diff --git a/Tests/QtAutogen/sameName/aaa/bbb/item.cpp b/Tests/QtAutogen/sameName/aaa/bbb/item.cpp
index 20d0044..850206f 100644
--- a/Tests/QtAutogen/sameName/aaa/bbb/item.cpp
+++ b/Tests/QtAutogen/sameName/aaa/bbb/item.cpp
@@ -3,8 +3,20 @@
 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/sameName/aaa/item.cpp b/Tests/QtAutogen/sameName/aaa/item.cpp
index 95dd3b6..e35d3d1 100644
--- a/Tests/QtAutogen/sameName/aaa/item.cpp
+++ b/Tests/QtAutogen/sameName/aaa/item.cpp
@@ -1,8 +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/sameName/aaa/item.hpp b/Tests/QtAutogen/sameName/aaa/item.hpp
index b63466f..875f72f 100644
--- a/Tests/QtAutogen/sameName/aaa/item.hpp
+++ b/Tests/QtAutogen/sameName/aaa/item.hpp
@@ -2,6 +2,8 @@
 #define AAA_ITEM_HPP
 
 #include <QObject>
+// Include ui_view.h only in header
+#include <aaa/ui_view.h>
 
 namespace aaa {
 
diff --git a/Tests/QtAutogen/sameName/aaa/view.ui b/Tests/QtAutogen/sameName/aaa/view.ui
new file mode 100644
index 0000000..0f09980
--- /dev/null
+++ b/Tests/QtAutogen/sameName/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/sameName/bbb/aaa/item.cpp b/Tests/QtAutogen/sameName/bbb/aaa/item.cpp
index ac4b2c2..7ad01c3 100644
--- a/Tests/QtAutogen/sameName/bbb/aaa/item.cpp
+++ b/Tests/QtAutogen/sameName/bbb/aaa/item.cpp
@@ -3,8 +3,20 @@
 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/sameName/bbb/item.cpp b/Tests/QtAutogen/sameName/bbb/item.cpp
index f97a143..9ef128e 100644
--- a/Tests/QtAutogen/sameName/bbb/item.cpp
+++ b/Tests/QtAutogen/sameName/bbb/item.cpp
@@ -1,8 +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/sameName/bbb/item.hpp b/Tests/QtAutogen/sameName/bbb/item.hpp
index 5b7f985..d39a9d7 100644
--- a/Tests/QtAutogen/sameName/bbb/item.hpp
+++ b/Tests/QtAutogen/sameName/bbb/item.hpp
@@ -2,6 +2,7 @@
 #define BBB_ITEM_HPP
 
 #include <QObject>
+// Include ui_view.h only in source
 
 namespace bbb {
 
diff --git a/Tests/QtAutogen/sameName/bbb/view.ui b/Tests/QtAutogen/sameName/bbb/view.ui
new file mode 100644
index 0000000..a8f506e
--- /dev/null
+++ b/Tests/QtAutogen/sameName/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/sameName/ccc/item.cpp b/Tests/QtAutogen/sameName/ccc/item.cpp
index d90b2b8..ab8a281 100644
--- a/Tests/QtAutogen/sameName/ccc/item.cpp
+++ b/Tests/QtAutogen/sameName/ccc/item.cpp
@@ -1,23 +1,25 @@
 #include "item.hpp"
+// Include ui_view.h in source and header
+#include <ccc/ui_view.h>
 
 namespace ccc {
 
-void Item::go()
-{
-}
-
-class MocTest : public QObject
+class MocLocal : public QObject
 {
   Q_OBJECT;
-  Q_SLOT
-  void go();
+
+public:
+  MocLocal() = default;
+  ~MocLocal() = default;
 };
 
-void MocTest::go()
+void Item::go()
 {
+  Ui_ViewCCC ui;
+  MocLocal obj;
 }
 }
 
 // Include own moc files
-#include "item.moc"
+#include "ccc/item.moc"
 #include "moc_item.cpp"
diff --git a/Tests/QtAutogen/sameName/ccc/item.hpp b/Tests/QtAutogen/sameName/ccc/item.hpp
index 96fcc24..20d9dd9 100644
--- a/Tests/QtAutogen/sameName/ccc/item.hpp
+++ b/Tests/QtAutogen/sameName/ccc/item.hpp
@@ -2,6 +2,8 @@
 #define CCC_ITEM_HPP
 
 #include <QObject>
+// Include ui_view.h in source and header
+#include <ccc/ui_view.h>
 
 namespace ccc {
 
diff --git a/Tests/QtAutogen/sameName/ccc/view.ui b/Tests/QtAutogen/sameName/ccc/view.ui
new file mode 100644
index 0000000..7989c69
--- /dev/null
+++ b/Tests/QtAutogen/sameName/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/sameName/item.cpp b/Tests/QtAutogen/sameName/item.cpp
index e013cf3..3d1fbe7 100644
--- a/Tests/QtAutogen/sameName/item.cpp
+++ b/Tests/QtAutogen/sameName/item.cpp
@@ -1,5 +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/sameName/item.hpp b/Tests/QtAutogen/sameName/item.hpp
index 91bba3b..75e83f4 100644
--- a/Tests/QtAutogen/sameName/item.hpp
+++ b/Tests/QtAutogen/sameName/item.hpp
@@ -2,6 +2,8 @@
 #define ITEM_HPP
 
 #include <QObject>
+// Include ui_view.h in source and header
+#include <ui_view.h>
 
 class Item : public QObject
 {
diff --git a/Tests/QtAutogen/sameName/view.ui b/Tests/QtAutogen/sameName/view.ui
new file mode 100644
index 0000000..2ffe734
--- /dev/null
+++ b/Tests/QtAutogen/sameName/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/QtAutogenRerun/CMakeLists.txt b/Tests/QtAutogenRerun/CMakeLists.txt
index 088025f..e72c191 100644
--- a/Tests/QtAutogenRerun/CMakeLists.txt
+++ b/Tests/QtAutogenRerun/CMakeLists.txt
@@ -36,204 +36,17 @@
 
 endif()
 
-# -- Test
-# Dummy test to generate clean target
+# Dummy executable to generate clean target
 add_executable(dummy dummy.cpp)
 
 # -- Test
-# When a file listed in a .qrc file changes the target must be rebuilt
-set(timeformat "%Y%j%H%M%S")
-set(RCC_DEPENDS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/rccDepends")
-set(RCC_DEPENDS_BIN "${CMAKE_CURRENT_BINARY_DIR}/rccDepends")
-configure_file(${RCC_DEPENDS_SRC}/res1a.qrc.in ${RCC_DEPENDS_BIN}/res1.qrc COPYONLY)
-configure_file(${RCC_DEPENDS_SRC}/res2a.qrc.in ${RCC_DEPENDS_BIN}/res2.qrc.in COPYONLY)
-try_compile(RCC_DEPENDS
-  "${RCC_DEPENDS_BIN}"
-  "${RCC_DEPENDS_SRC}"
-  rccDepends
-  CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
-              "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
-              "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
-  OUTPUT_VARIABLE output
-)
-if (NOT RCC_DEPENDS)
-  message(SEND_ERROR "Initial build of rccDepends failed. Output: ${output}")
-endif()
-# Get name and timestamp of the output binary
-file(STRINGS "${RCC_DEPENDS_BIN}/target.txt" targetList ENCODING UTF-8)
-list(GET targetList 0 rccDependsBin)
-file(TIMESTAMP "${rccDependsBin}" timeBegin "${timeformat}")
-# Sleep, touch regular qrc input file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res1/input.txt")
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Second build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep1 "${timeformat}")
-if (NOT timeStep1 GREATER timeBegin)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the first step!")
-endif()
-# Sleep, update regular qrc file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-configure_file(${RCC_DEPENDS_SRC}/res1b.qrc.in ${RCC_DEPENDS_BIN}/res1.qrc COPYONLY)
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Third build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep2 "${timeformat}")
-if (NOT timeStep2 GREATER timeStep1)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the second step!")
-endif()
-# Sleep, touch regular qrc newly added input file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res1/inputAdded.txt")
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Fourth build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep3 "${timeformat}")
-if (NOT timeStep3 GREATER timeStep2)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the third step!")
-endif()
-# Sleep, touch generated qrc input file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res2/input.txt")
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Fifth build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep4 "${timeformat}")
-if (NOT timeStep4 GREATER timeStep3)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the fourth step!")
-endif()
-# Sleep, update generated qrc file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-configure_file(${RCC_DEPENDS_SRC}/res2b.qrc.in ${RCC_DEPENDS_BIN}/res2.qrc.in COPYONLY)
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Sixth build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep5 "${timeformat}")
-if (NOT timeStep5 GREATER timeStep4)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the fitfh step!")
-endif()
-# Sleep, touch generated qrc newly added input file, rebuild and compare timestamp
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change.
-execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res2/inputAdded.txt")
-execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result)
-if (result)
-  message(SEND_ERROR "Seventh build of rccDepends failed.")
-endif()
-file(TIMESTAMP "${rccDependsBin}" timeStep6 "${timeformat}")
-if (NOT timeStep6 GREATER timeStep5)
-  message(SEND_ERROR "File (${rccDependsBin}) should have changed in the sixth step!")
-endif()
-
-
-# -- Test
-# Ensure a repeated build succeeds when a header containing a QObject changes
-set(timeformat "%Y%j%H%M%S")
-configure_file(mocRerun/test1a.h.in mocRerun/test1.h COPYONLY)
-try_compile(MOC_RERUN
-  "${CMAKE_CURRENT_BINARY_DIR}/mocRerun"
-  "${CMAKE_CURRENT_SOURCE_DIR}/mocRerun"
-  mocRerun
-  CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
-              "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
-              "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
-  OUTPUT_VARIABLE output
-)
-if (NOT MOC_RERUN)
-  message(SEND_ERROR "Initial build of mocRerun failed. Output: ${output}")
-endif()
-# Get name and timestamp of the output binary
-file(STRINGS "${CMAKE_CURRENT_BINARY_DIR}/mocRerun/target1.txt" target1List ENCODING UTF-8)
-list(GET target1List 0 binFile)
-file(TIMESTAMP "${binFile}" timeBegin "${timeformat}")
-# Change file content and rebuild
-execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
-configure_file(mocRerun/test1b.h.in mocRerun/test1.h COPYONLY)
-execute_process(COMMAND "${CMAKE_COMMAND}" --build .
-  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocRerun"
-  RESULT_VARIABLE mocRerun_result
-  )
-if (mocRerun_result)
-  message(SEND_ERROR "Second build of mocRerun failed.")
-endif()
-# Compare timestamps
-file(TIMESTAMP "${binFile}" timeStep1 "${timeformat}")
-if (NOT timeStep1 GREATER timeBegin)
-  message(SEND_ERROR "File (${binFile}) should have changed in the first step!")
-endif()
-
+include("mocRerun.cmake")
 
 # -- Test
 # Tests Q_PLUGIN_METADATA json file change detection
 if (NOT QT_TEST_VERSION STREQUAL 4)
-  try_compile(MOC_PLUGIN
-    "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin"
-    "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin"
-    mocPlugin
-    CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
-                "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
-    OUTPUT_VARIABLE output
-  )
-  if (NOT MOC_PLUGIN)
-    message(SEND_ERROR "Initial build of mocPlugin failed. Output: ${output}")
-  endif()
-
-  set(timeformat "%Y%j%H%M%S")
-  set(mocPlugSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin")
-  set(mocPlugBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin")
-  find_library(plAFile "PlugA" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
-  find_library(plBFile "PlugB" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
-  find_library(plCFile "PlugC" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
-  find_library(plDFile "PlugD" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
-
-  file(TIMESTAMP "${plAFile}" plABefore "${timeformat}")
-  file(TIMESTAMP "${plBFile}" plBBefore "${timeformat}")
-  file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}")
-  file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}")
-
-  # Ensure that the timestamp will change and change the json files
-  execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
-  configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC.json")
-  configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD.json")
-  execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}")
-
-  file(TIMESTAMP "${plAFile}" plAAfter "${timeformat}")
-  file(TIMESTAMP "${plBFile}" plBAfter "${timeformat}")
-  file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}")
-  file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}")
-
-  if (plAAfter GREATER plABefore)
-    message(SEND_ERROR "file (${plAFile}) should not have changed!")
-  endif()
-  if (plBAfter GREATER plBBefore)
-    message(SEND_ERROR "file (${plBFile}) should not have changed!")
-  endif()
-  if (NOT plCAfter GREATER plCBefore)
-    message(SEND_ERROR "file (${plCFile}) should have changed!")
-  endif()
-  if (NOT plDAfter GREATER plDBefore)
-    message(SEND_ERROR "file (${plDFile}) should have changed!")
-  endif()
-
-  # Test custom macro
-  file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}")
-  file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}")
-  execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
-  configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC_Custom.json")
-  configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD_Custom.json")
-  execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}")
-  file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}")
-  file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}")
-  if (NOT plCAfter GREATER plCBefore)
-    message(SEND_ERROR "file (${plCFile}) should have changed!")
-  endif()
-  if (NOT plDAfter GREATER plDBefore)
-    message(SEND_ERROR "file (${plDFile}) should have changed!")
-  endif()
-
+  include("mocPlugin.cmake")
 endif()
+
+# -- Test
+include("rccDepends.cmake")
diff --git a/Tests/QtAutogenRerun/mocPlugin.cmake b/Tests/QtAutogenRerun/mocPlugin.cmake
new file mode 100644
index 0000000..7ad5ccb
--- /dev/null
+++ b/Tests/QtAutogenRerun/mocPlugin.cmake
@@ -0,0 +1,96 @@
+
+# Utility variables
+set(timeformat "%Y%j%H%M%S")
+set(mocPlugSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin")
+set(mocPlugBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin")
+
+# Initial buid
+try_compile(MOC_PLUGIN
+  "${mocPlugBinDir}"
+  "${mocPlugSrcDir}"
+  mocPlugin
+  CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
+              "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
+  OUTPUT_VARIABLE output
+)
+if (NOT MOC_PLUGIN)
+  message(SEND_ERROR "Initial build of mocPlugin failed. Output: ${output}")
+endif()
+
+find_library(plAFile "PlugA" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
+find_library(plBFile "PlugB" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
+find_library(plCFile "PlugC" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
+find_library(plDFile "PlugD" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
+find_library(plEFile "PlugE" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH)
+
+# - Ensure that the timestamp will change.
+# - Change the json files referenced by Q_PLUGIN_METADATA
+# - Rebuild
+file(TIMESTAMP "${plAFile}" plABefore "${timeformat}")
+file(TIMESTAMP "${plBFile}" plBBefore "${timeformat}")
+file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}")
+file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}")
+file(TIMESTAMP "${plEFile}" plEBefore "${timeformat}")
+
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC.json")
+configure_file("${mocPlugSrcDir}/jsonIn/StyleE.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD.json")
+configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/StyleE.json")
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}")
+
+file(TIMESTAMP "${plAFile}" plAAfter "${timeformat}")
+file(TIMESTAMP "${plBFile}" plBAfter "${timeformat}")
+file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}")
+file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}")
+file(TIMESTAMP "${plEFile}" plEAfter "${timeformat}")
+
+if (plAAfter GREATER plABefore)
+  message(SEND_ERROR "file (${plAFile}) should not have changed!")
+endif()
+if (plBAfter GREATER plBBefore)
+  message(SEND_ERROR "file (${plBFile}) should not have changed!")
+endif()
+if (NOT plCAfter GREATER plCBefore)
+  message(SEND_ERROR "file (${plCFile}) should have changed!")
+endif()
+if (NOT plDAfter GREATER plDBefore)
+  message(SEND_ERROR "file (${plDFile}) should have changed!")
+endif()
+if (NOT plEAfter GREATER plEBefore)
+  # There's a bug in Ninja on Windows
+  # https://gitlab.kitware.com/cmake/cmake/issues/16776
+  if(NOT ("${CMAKE_GENERATOR}" MATCHES "Ninja"))
+    message(SEND_ERROR "file (${plEFile}) should have changed!")
+  endif()
+endif()
+
+# - Ensure that the timestamp will change.
+# - Change the json files referenced by A_CUSTOM_MACRO
+# - Rebuild
+file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}")
+file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}")
+file(TIMESTAMP "${plEFile}" plEBefore "${timeformat}")
+
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+configure_file("${mocPlugSrcDir}/jsonIn/StyleE.json" "${mocPlugBinDir}/jsonFiles/StyleC_Custom.json")
+configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD_Custom.json")
+configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleE_Custom.json")
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}")
+
+file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}")
+file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}")
+file(TIMESTAMP "${plEFile}" plEAfter "${timeformat}")
+
+if (NOT plCAfter GREATER plCBefore)
+  message(SEND_ERROR "file (${plCFile}) should have changed!")
+endif()
+if (NOT plDAfter GREATER plDBefore)
+  message(SEND_ERROR "file (${plDFile}) should have changed!")
+endif()
+if (NOT plEAfter GREATER plEBefore)
+  # There's a bug in Ninja on Windows
+  # https://gitlab.kitware.com/cmake/cmake/issues/16776
+  if(NOT ("${CMAKE_GENERATOR}" MATCHES "Ninja"))
+    message(SEND_ERROR "file (${plEFile}) should have changed!")
+  endif()
+endif()
diff --git a/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt b/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt
index 9b224fb..b7cc5e9 100644
--- a/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt
+++ b/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt
@@ -16,9 +16,11 @@
 endif()
 
 configure_file(jsonIn/StyleC.json jsonFiles/StyleC.json)
-configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json)
 configure_file(jsonIn/StyleC.json jsonFiles/StyleC_Custom.json)
+configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json)
 configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD_Custom.json)
+configure_file(jsonIn/StyleE.json jsonFiles/StyleE.json)
+configure_file(jsonIn/StyleE.json jsonFiles/StyleE_Custom.json)
 
 # Enable automoc
 set(CMAKE_AUTOMOC TRUE)
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp
index 1b6154d..35158a4 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp
@@ -1,13 +1,13 @@
 #ifndef STYLEA_HPP
 #define STYLEA_HPP
 
-#include "StyleCommon.hpp"
+#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
 class StyleA : public QStylePlugin
 {
   Q_OBJECT
-  // Json file in local directory
+  // Json file in source local directory
   Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json")
   A_CUSTOM_MACRO(SomeArg, "StyleA_Custom.json", AnotherArg)
 public:
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp
index 163c9b2..15b79c5 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp
@@ -1,13 +1,13 @@
 #ifndef STYLEB_HPP
 #define STYLEB_HPP
 
-#include "StyleCommon.hpp"
+#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
 class StyleB : public QStylePlugin
 {
   Q_OBJECT
-  // Json file in local subdirectory
+  // Json file in source local subdirectory
   Q_PLUGIN_METADATA(IID "org.styles.B" FILE "jsonIn/StyleB.json")
   A_CUSTOM_MACRO(SomeArg, "jsonIn/StyleB_Custom.json", AnotherArg)
 public:
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp
index 52a887a..b0a4115 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp
@@ -1,7 +1,7 @@
 #ifndef STYLEC_HPP
 #define STYLEC_HPP
 
-#include "StyleCommon.hpp"
+#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
 class StyleC : public QStylePlugin
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp
index df8a439..9696aaa 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp
@@ -1,7 +1,7 @@
 #ifndef STYLED_HPP
 #define STYLED_HPP
 
-#include "StyleCommon.hpp"
+#include "UtilityMacros.hpp"
 #include <QStylePlugin>
 
 class StyleD : public QStylePlugin
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp b/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp
index 8fc9a7f..3448319 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp
@@ -4,3 +4,6 @@
 {
   return 0;
 }
+
+// AUTOMOC the StyleEInclude.hpp header
+#include "moc_StyleEInclude.cpp"
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp
index e7915a8..a069034 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp
@@ -1,17 +1,10 @@
 #ifndef STYLEE_HPP
 #define STYLEE_HPP
 
-#include "StyleCommon.hpp"
-#include <QStylePlugin>
-
-class StyleE : public QStylePlugin
-{
-  Q_OBJECT
-  // No Json file
-  Q_PLUGIN_METADATA(IID "org.styles.E")
-  A_CUSTOM_MACRO(SomeArg, InvalidFileArg, AnotherArg)
-public:
-  QStyle* create(const QString& key);
-};
+// The included file is not in the sources list and won't be detected by
+// AUTOMOC source file with the same base name.
+// It is registered to AUTOMOCed via a moc_<NAME>.cpp include in StyleE.cpp
+// though.
+#include "StyleEInclude.hpp"
 
 #endif
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp
new file mode 100644
index 0000000..f9734db
--- /dev/null
+++ b/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp
@@ -0,0 +1,17 @@
+#ifndef STYLEE_INCLUDE_HPP
+#define STYLEE_INCLUDE_HPP
+
+#include "UtilityMacros.hpp"
+#include <QStylePlugin>
+
+class StyleE : public QStylePlugin
+{
+  Q_OBJECT
+  // Json files in global root directory
+  Q_PLUGIN_METADATA(IID "org.styles.E" FILE "StyleE.json")
+  A_CUSTOM_MACRO(SomeArg, "StyleE_Custom.json", AnotherArg)
+public:
+  QStyle* create(const QString& key);
+};
+
+#endif
diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleCommon.hpp b/Tests/QtAutogenRerun/mocPlugin/UtilityMacros.hpp
similarity index 65%
rename from Tests/QtAutogenRerun/mocPlugin/StyleCommon.hpp
rename to Tests/QtAutogenRerun/mocPlugin/UtilityMacros.hpp
index f1a7ec6..53a4284 100644
--- a/Tests/QtAutogenRerun/mocPlugin/StyleCommon.hpp
+++ b/Tests/QtAutogenRerun/mocPlugin/UtilityMacros.hpp
@@ -1,5 +1,5 @@
-#ifndef STYLECOMMON_HPP
-#define STYLECOMMON_HPP
+#ifndef UTILITYMACROS_HPP
+#define UTILITYMACROS_HPP
 
 // Empty test macro definition
 #define A_CUSTOM_MACRO(name, jsonFile, pluginRegistrations)
diff --git a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json
index 129cac4..cd155dc 100644
--- a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json
+++ b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json
@@ -1 +1 @@
-{ "Keys": [ "Rocket", "StarbusterB" ] }
+{ "Keys": [ "Red", "Green" ] }
diff --git a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json
new file mode 100644
index 0000000..5412c94
--- /dev/null
+++ b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json
@@ -0,0 +1 @@
+{ "Keys": [ "Floor", "Ceiling" ] }
diff --git a/Tests/QtAutogenRerun/mocRerun.cmake b/Tests/QtAutogenRerun/mocRerun.cmake
new file mode 100644
index 0000000..a92912b
--- /dev/null
+++ b/Tests/QtAutogenRerun/mocRerun.cmake
@@ -0,0 +1,60 @@
+
+set(timeformat "%Y%j%H%M%S")
+set(mocRerunSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocRerun")
+set(mocRerunBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocRerun")
+
+# Initial build
+configure_file("${mocRerunSrcDir}/test1a.h.in" "${mocRerunBinDir}/test1.h" COPYONLY)
+try_compile(MOC_RERUN
+  "${mocRerunBinDir}"
+  "${mocRerunSrcDir}"
+  mocRerun
+  CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+              "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
+              "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
+  OUTPUT_VARIABLE output
+)
+if (NOT MOC_RERUN)
+  message(SEND_ERROR "Initial build of mocRerun failed. Output: ${output}")
+endif()
+# Get name of the output binary
+file(STRINGS "${mocRerunBinDir}/mocRerun.txt" mocRerunList ENCODING UTF-8)
+list(GET mocRerunList 0 mocRerunBin)
+
+message("Changing the header content for a MOC rerun")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${mocRerunBin}" timeBefore "${timeformat}")
+# - Ensure that the timestamp will change
+# - Change header file content and rebuild
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+configure_file("${mocRerunSrcDir}/test1b.h.in" "${mocRerunBinDir}/test1.h" COPYONLY)
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocRerunBinDir}" RESULT_VARIABLE result )
+if (result)
+  message(SEND_ERROR "Second build of mocRerun failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${mocRerunBin}" timeAfter "${timeformat}")
+# - Test if timestamps changed
+if (NOT timeAfter GREATER timeBefore)
+  message(SEND_ERROR "File (${mocRerunBin}) should have changed!")
+endif()
+
+
+message("Changing nothing for a MOC rerun")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${mocRerunBin}" timeBefore "${timeformat}")
+# - Ensure that the timestamp would change
+# - Change nothing
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocRerunBinDir}" RESULT_VARIABLE result )
+if (result)
+  message(SEND_ERROR "Third build of mocRerun failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${mocRerunBin}" timeAfter "${timeformat}")
+# - Test if timestamps changed
+if (timeAfter GREATER timeBefore)
+  message(SEND_ERROR "File (${mocRerunBin}) should not have changed!")
+endif()
diff --git a/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt b/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt
index 7380bdd..bafd9cf 100644
--- a/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt
+++ b/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt
@@ -19,17 +19,15 @@
 
 # Generated source file
 add_custom_command(OUTPUT main.cpp
-                   COMMAND ${CMAKE_COMMAND} -E sleep 2
-                   COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
-                   )
+  COMMAND ${CMAKE_COMMAND} -E sleep 2
+  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/main.cpp)
 
 add_executable(mocRerun
   ${CMAKE_CURRENT_BINARY_DIR}/test1.h
   ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
-  res1.qrc
-  )
+  res1.qrc)
 target_include_directories(mocRerun PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 target_link_libraries(mocRerun ${QT_CORE_TARGET})
 # Write target name to text file
 add_custom_command(TARGET mocRerun POST_BUILD COMMAND
-  ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:mocRerun>" > target1.txt)
+  ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:mocRerun>" > mocRerun.txt)
diff --git a/Tests/QtAutogenRerun/rccDepends.cmake b/Tests/QtAutogenRerun/rccDepends.cmake
new file mode 100644
index 0000000..68e1482
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends.cmake
@@ -0,0 +1,131 @@
+# When a .qrc or a file listed in a .qrc file changes,
+# the target must be rebuilt
+set(timeformat "%Y%j%H%M%S")
+set(rccDepSD "${CMAKE_CURRENT_SOURCE_DIR}/rccDepends")
+set(rccDepBD "${CMAKE_CURRENT_BINARY_DIR}/rccDepends")
+
+# Initial build
+configure_file(${rccDepSD}/resPlainA.qrc.in ${rccDepBD}/resPlain.qrc COPYONLY)
+configure_file(${rccDepSD}/resGenA.qrc.in ${rccDepBD}/resGen.qrc.in COPYONLY)
+try_compile(RCC_DEPENDS
+  "${rccDepBD}"
+  "${rccDepSD}"
+  rccDepends
+  CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+              "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
+              "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}"
+  OUTPUT_VARIABLE output
+)
+if (NOT RCC_DEPENDS)
+  message(SEND_ERROR "Initial build of rccDepends failed. Output: ${output}")
+endif()
+
+# Get name of the output binaries
+file(STRINGS "${rccDepBD}/targetPlain.txt" targetListPlain ENCODING UTF-8)
+file(STRINGS "${rccDepBD}/targetGen.txt" targetListGen ENCODING UTF-8)
+list(GET targetListPlain 0 rccDepBinPlain)
+list(GET targetListGen 0 rccDepBinGen)
+message("Target that uses a plain .qrc file is:\n  ${rccDepBinPlain}")
+message("Target that uses a GENERATED .qrc file is:\n  ${rccDepBinGen}")
+
+
+message("Changing a resource files listed in the .qrc file")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}")
+# - Ensure that the timestamp will change
+# - Change a resource files listed in the .qrc file
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resPlain/input.txt")
+execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resGen/input.txt")
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result)
+if (result)
+  message(SEND_ERROR "Second build of rccDepends failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}")
+# - Test if timestamps changed
+if (NOT rdPlainAfter GREATER rdPlainBefore)
+  message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!")
+endif()
+if (NOT rdGenAfter GREATER rdGenBefore)
+  message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!")
+endif()
+
+
+message("Changing a the .qrc file")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}")
+# - Ensure that the timestamp will change
+# - Change the .qrc file
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+configure_file(${rccDepSD}/resPlainB.qrc.in ${rccDepBD}/resPlain.qrc COPYONLY)
+configure_file(${rccDepSD}/resGenB.qrc.in ${rccDepBD}/resGen.qrc.in COPYONLY)
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result)
+if (result)
+  message(SEND_ERROR "Third build of rccDepends failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}")
+# - Test if timestamps changed
+if (NOT rdPlainAfter GREATER rdPlainBefore)
+  message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!")
+endif()
+if (NOT rdGenAfter GREATER rdGenBefore)
+  message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!")
+endif()
+
+
+message("Changing a newly added resource files listed in the .qrc file")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}")
+# - Ensure that the timestamp will change
+# - Change a newly added resource files listed in the .qrc file
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resPlain/inputAdded.txt")
+execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resGen/inputAdded.txt")
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result)
+if (result)
+  message(SEND_ERROR "Fourth build of rccDepends failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}")
+# - Test if timestamps changed
+if (NOT rdPlainAfter GREATER rdPlainBefore)
+  message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!")
+endif()
+if (NOT rdGenAfter GREATER rdGenBefore)
+  message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!")
+endif()
+
+
+message("Changing nothing in the .qrc file")
+# - Acquire binary timestamps before the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}")
+# - Ensure that the timestamp will change
+# - Change nothing
+# - Rebuild
+execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result)
+if (result)
+  message(SEND_ERROR "Fifth build of rccDepends failed.")
+endif()
+# - Acquire binary timestamps after the build
+file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}")
+file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}")
+# - Test if timestamps changed
+if (rdPlainAfter GREATER rdPlainBefore)
+  message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should NOT have changed!")
+endif()
+if (rdGenAfter GREATER rdGenBefore)
+  message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should NOT have changed!")
+endif()
diff --git a/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt b/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt
index edc0ac3..291592e 100644
--- a/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt
+++ b/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt
@@ -1,8 +1,6 @@
 cmake_minimum_required(VERSION 3.9)
 project(rccDepends CXX)
 
-set(CMAKE_AUTORCC ON)
-
 if (QT_TEST_VERSION STREQUAL 4)
   find_package(Qt4 REQUIRED)
   set(QT_CORE_TARGET Qt4::QtCore)
@@ -15,21 +13,29 @@
   set(QT_CORE_TARGET Qt5::Core)
 endif()
 
-configure_file(res/input1.txt.in res1/input.txt COPYONLY)
-configure_file(res/input1.txt.in res1/inputAdded.txt COPYONLY)
-configure_file(res/input2.txt.in res2/input.txt COPYONLY)
-configure_file(res/input2.txt.in res2/inputAdded.txt COPYONLY)
-# Dependency generated qrc file
-add_custom_command(OUTPUT res2.qrc
-                   DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc.in
-                   COMMAND ${CMAKE_COMMAND} -E sleep 2
-                   COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc
-                   )
+# Enable AUTORCC for all targets
+set(CMAKE_AUTORCC ON)
 
-add_executable(rccDepends
-  main.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/res1.qrc
-  ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc )
-target_link_libraries(rccDepends ${QT_CORE_TARGET})
-add_custom_command(TARGET rccDepends POST_BUILD COMMAND
-  ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDepends>" > target.txt)
+# Initial resource files setup
+configure_file(resPlain/input.txt.in resPlain/input.txt COPYONLY)
+configure_file(resPlain/input.txt.in resPlain/inputAdded.txt COPYONLY)
+configure_file(resGen/input.txt.in resGen/input.txt COPYONLY)
+configure_file(resGen/input.txt.in resGen/inputAdded.txt COPYONLY)
+
+# Generated qrc file with dependency
+add_custom_command(OUTPUT resGen.qrc
+  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc.in
+  COMMAND ${CMAKE_COMMAND} -E sleep 2
+  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc)
+
+# Target that uses a plain .qrc file
+add_executable(rccDependsPlain main.cpp ${CMAKE_CURRENT_BINARY_DIR}/resPlain.qrc)
+target_link_libraries(rccDependsPlain ${QT_CORE_TARGET})
+add_custom_command(TARGET rccDependsPlain POST_BUILD COMMAND
+  ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDependsPlain>" > targetPlain.txt)
+
+# Target that uses a GENERATED .qrc file
+add_executable(rccDependsGen main.cpp ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc )
+target_link_libraries(rccDependsGen ${QT_CORE_TARGET})
+add_custom_command(TARGET rccDependsGen POST_BUILD COMMAND
+  ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDependsGen>" > targetGen.txt)
diff --git a/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in b/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in
deleted file mode 100644
index da62762..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in
+++ /dev/null
@@ -1 +0,0 @@
-Res1 input.
diff --git a/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in b/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in
deleted file mode 100644
index 08e14b7..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in
+++ /dev/null
@@ -1 +0,0 @@
-Res2 input.
diff --git a/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in b/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in
deleted file mode 100644
index d111ffb..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
-    <qresource prefix="/Texts1">
-        <file>res1/input.txt</file>
-    </qresource>
-</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in b/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in
deleted file mode 100644
index 4cb3f04..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
-    <qresource prefix="/Texts1">
-        <file>res1/input.txt</file>
-        <file alias="Added">res1/inputAdded.txt</file>
-    </qresource>
-</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in b/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in
deleted file mode 100644
index 19f34ac..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
-    <qresource prefix="/Texts2">
-        <file>res2/input.txt</file>
-    </qresource>
-</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in b/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in
deleted file mode 100644
index 19e8ba1..0000000
--- a/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
-    <qresource prefix="/Texts2">
-        <file>res2/input.txt</file>
-        <file alias="Added">res2/inputAdded.txt</file>
-    </qresource>
-</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in b/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in
new file mode 100644
index 0000000..4f24589
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in
@@ -0,0 +1 @@
+Generated resource input.
diff --git a/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in b/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in
new file mode 100644
index 0000000..4f24589
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in
@@ -0,0 +1 @@
+Generated resource input.
diff --git a/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in b/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in
new file mode 100644
index 0000000..c131a34
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/TextsGenerated">
+        <file>resGen/input.txt</file>
+    </qresource>
+</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in b/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in
new file mode 100644
index 0000000..8c7e643
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in
@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/TextsGenerated">
+        <file>resGen/input.txt</file>
+        <file alias="Added">resGen/inputAdded.txt</file>
+    </qresource>
+</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in b/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in
new file mode 100644
index 0000000..a5e407a
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in
@@ -0,0 +1 @@
+Plaint resource input.
diff --git a/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in b/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in
new file mode 100644
index 0000000..a5e407a
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in
@@ -0,0 +1 @@
+Plaint resource input.
diff --git a/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in b/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in
new file mode 100644
index 0000000..c135d85
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/TextsPlain">
+        <file>resPlain/input.txt</file>
+    </qresource>
+</RCC>
diff --git a/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in b/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in
new file mode 100644
index 0000000..186b653
--- /dev/null
+++ b/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in
@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/TextsPlain">
+        <file>resPlain/input.txt</file>
+        <file alias="Added">resPlain/inputAdded.txt</file>
+    </qresource>
+</RCC>