浏览代码

Autogen: Use JSON instead of CMake script for info files

We used to store information for the _autogen target in a CMake script
file AutogenInfo.cmake, which was imported by a temporary cmake instance in
the _autogen target.  This introduced the overhead of creating a temporary
cmake instance and inherited the limitations of the CMake language which
only supports lists.

This patch introduces JSON files to pass information to AUTORCC and
autogen_ targets.  JSON files are more flexible for passing data, e.g. they
support nested lists.

The patch has the side effects that

- AutogenInfo.cmake is renamed to AutogenInfo.json
- AutogenOldSettings.txt is renamed to AutogenUsed.txt
- RCC<qrcBaseName><checksum>Info.cmake is renamed to
  AutoRcc_<qrcBaseName>_<checksum>_Info.json
- RCC<qrcBaseName><checksum>.lock is renamed to
  AutoRcc_<qrcBaseName>_<checksum>_Lock.lock
- RCC<qrcBaseName><checksum>Settings.txt is renamed to
  AutoRcc_<qrcBaseName>_<checksum>_Used.txt
Sebastian Holtermann 6 年之前
父节点
当前提交
d867e05892

+ 10 - 1
Source/cmQtAutoGen.cxx

@@ -72,7 +72,6 @@ void MergeOptions(std::vector<std::string>& baseOpts,
 // - Class definitions
 
 unsigned int const cmQtAutoGen::ParallelMax = 64;
-std::string const cmQtAutoGen::ListSep = "<<<S>>>";
 
 cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
 {
@@ -162,6 +161,16 @@ std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
   return res;
 }
 
+std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename)
+{
+  auto slashPos = filename.rfind('/');
+  if (slashPos != cm::string_view::npos) {
+    filename.remove_prefix(slashPos + 1);
+  }
+  auto dotPos = filename.rfind('.');
+  return std::string(filename.substr(0, dotPos));
+}
+
 std::string cmQtAutoGen::ParentDir(cm::string_view filename)
 {
   auto slashPos = filename.rfind('/');

+ 3 - 2
Source/cmQtAutoGen.h

@@ -62,8 +62,6 @@ public:
     RCC  // AUTORCC
   };
 
-  /// @brief Nested lists separator
-  static std::string const ListSep;
   /// @brief Maximum number of parallel threads/processes in a generator
   static unsigned int const ParallelMax;
 
@@ -81,6 +79,9 @@ public:
 
   static std::string QuotedCommand(std::vector<std::string> const& command);
 
+  /// @brief Returns the file name without path and extension (thread safe)
+  static std::string FileNameWithoutLastExtension(cm::string_view filename);
+
   /// @brief Returns the parent directory of the file (thread safe)
   static std::string ParentDir(cm::string_view filename);
 

+ 427 - 365
Source/cmQtAutoGenInitializer.cxx

@@ -8,7 +8,7 @@
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
-#include "cmFilePathChecksum.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -17,7 +17,6 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocationKind.h"
@@ -27,6 +26,8 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cm_jsoncpp_value.h"
+#include "cm_jsoncpp_writer.h"
 #include "cmake.h"
 #include "cmsys/SystemInformation.hxx"
 
@@ -34,6 +35,7 @@
 #include <deque>
 #include <initializer_list>
 #include <map>
+#include <ostream>
 #include <set>
 #include <string>
 #include <unordered_set>
@@ -41,6 +43,7 @@
 #include <vector>
 
 #include <cm/algorithm>
+#include <cm/iterator>
 #include <cm/memory>
 
 namespace {
@@ -157,99 +160,134 @@ std::vector<std::string> SearchPathSanitizer::operator()(
   }
   return res;
 }
-} // End of unnamed namespace
 
-cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename)
+/** @brief Writes a CMake info file.  */
+class InfoWriter
 {
-  Ofs_.SetCopyIfDifferent(true);
-  Ofs_.Open(filename, false, true);
-}
+public:
+  // -- Single value
+  void Set(std::string const& key, std::string const& value)
+  {
+    Value_[key] = value;
+  }
+  void SetConfig(std::string const& key,
+                 cmQtAutoGenInitializer::ConfigString const& cfgStr);
+  void SetBool(std::string const& key, bool value) { Value_[key] = value; }
+  void SetUInt(std::string const& key, unsigned int value)
+  {
+    Value_[key] = value;
+  }
+
+  // -- Array utility
+  template <typename CONT>
+  static bool MakeArray(Json::Value& jval, CONT const& container);
+
+  template <typename CONT>
+  static void MakeStringArray(Json::Value& jval, CONT const& container);
+
+  // -- Array value
+  template <typename CONT>
+  void SetArray(std::string const& key, CONT const& container);
+  template <typename CONT>
+  void SetConfigArray(
+    std::string const& key,
+    cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr);
 
-template <class IT>
-std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin,
-                                                         IT it_end)
+  // -- Array of arrays
+  template <typename CONT, typename FUNC>
+  void SetArrayArray(std::string const& key, CONT const& container, FUNC func);
+
+  // -- Save to json file
+  bool Save(std::string const& filename);
+
+private:
+  Json::Value Value_;
+};
+
+void InfoWriter::SetConfig(std::string const& key,
+                           cmQtAutoGenInitializer::ConfigString const& cfgStr)
 {
-  std::string res;
-  for (IT it = it_begin; it != it_end; ++it) {
-    if (it != it_begin) {
-      res += ';';
-    }
-    for (const char* c = it->c_str(); *c; ++c) {
-      if (*c == '"') {
-        // Escape the double quote to avoid ending the argument.
-        res += "\\\"";
-      } else if (*c == '$') {
-        // Escape the dollar to avoid expanding variables.
-        res += "\\$";
-      } else if (*c == '\\') {
-        // Escape the backslash to avoid other escapes.
-        res += "\\\\";
-      } else if (*c == ';') {
-        // Escape the semicolon to avoid list expansion.
-        res += "\\;";
-      } else {
-        // Other characters will be parsed correctly.
-        res += *c;
-      }
-    }
+  Set(key, cfgStr.Default);
+  for (auto const& item : cfgStr.Config) {
+    Set(cmStrCat(key, '_', item.first), item.second);
   }
-  return res;
 }
 
-inline std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey(
-  cm::string_view key, std::string const& config)
+template <typename CONT>
+bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container)
 {
-  return cmStrCat(key, "_", config);
+  jval = Json::arrayValue;
+  std::size_t const listSize = cm::size(container);
+  if (listSize == 0) {
+    return false;
+  }
+  jval.resize(static_cast<unsigned int>(listSize));
+  return true;
 }
 
-void cmQtAutoGenInitializer::InfoWriter::Write(cm::string_view key,
-                                               std::string const& value)
+template <typename CONT>
+void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container)
 {
-  Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
-       << ")\n";
-};
-
-void cmQtAutoGenInitializer::InfoWriter::WriteUInt(cm::string_view key,
-                                                   unsigned int value)
-{
-  Ofs_ << "set(" << key << " " << value << ")\n";
-};
+  if (MakeArray(jval, container)) {
+    Json::ArrayIndex ii = 0;
+    for (std::string const& item : container) {
+      jval[ii++] = item;
+    }
+  }
+}
 
-template <class C>
-void cmQtAutoGenInitializer::InfoWriter::WriteStrings(cm::string_view key,
-                                                      C const& container)
+template <typename CONT>
+void InfoWriter::SetArray(std::string const& key, CONT const& container)
 {
-  Ofs_ << "set(" << key << " \""
-       << ListJoin(container.begin(), container.end()) << "\")\n";
+  MakeStringArray(Value_[key], container);
 }
 
-void cmQtAutoGenInitializer::InfoWriter::WriteConfig(
-  cm::string_view key, std::map<std::string, std::string> const& map)
+template <typename CONT, typename FUNC>
+void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
+                               FUNC func)
 {
-  for (auto const& item : map) {
-    Write(ConfigKey(key, item.first), item.second);
+  Json::Value& jval = Value_[key];
+  if (MakeArray(jval, container)) {
+    Json::ArrayIndex ii = 0;
+    for (auto const& citem : container) {
+      Json::Value& aval = jval[ii++];
+      aval = Json::arrayValue;
+      func(aval, citem);
+    }
   }
-};
+}
 
-template <class C>
-void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings(
-  cm::string_view key, std::map<std::string, C> const& map)
+template <typename CONT>
+void InfoWriter::SetConfigArray(
+  std::string const& key,
+  cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr)
 {
-  for (auto const& item : map) {
-    WriteStrings(ConfigKey(key, item.first), item.second);
+  SetArray(key, cfgStr.Default);
+  for (auto const& item : cfgStr.Config) {
+    SetArray(cmStrCat(key, '_', item.first), item.second);
   }
 }
 
-void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists(
-  cm::string_view key, std::vector<std::vector<std::string>> const& lists)
+bool InfoWriter::Save(std::string const& filename)
 {
-  std::vector<std::string> seplist;
-  seplist.reserve(lists.size());
-  for (std::vector<std::string> const& list : lists) {
-    seplist.push_back(cmStrCat("{", ListJoin(list.begin(), list.end()), "}"));
+  cmGeneratedFileStream fileStream;
+  fileStream.SetCopyIfDifferent(true);
+  fileStream.Open(filename, false, true);
+  if (!fileStream) {
+    return false;
   }
-  Write(key, cmJoin(seplist, cmQtAutoGen::ListSep));
-};
+
+  Json::StyledStreamWriter jsonWriter;
+  try {
+    jsonWriter.write(fileStream, Value_);
+  } catch (...) {
+    return false;
+  }
+
+  return fileStream.Close();
+}
+
+} // End of unnamed namespace
 
 cmQtAutoGenInitializer::cmQtAutoGenInitializer(
   cmQtAutoGenGlobalInitializer* globalInitializer,
@@ -261,6 +299,7 @@ cmQtAutoGenInitializer::cmQtAutoGenInitializer(
   , GlobalGen(genTarget->GetGlobalGenerator())
   , LocalGen(genTarget->GetLocalGenerator())
   , Makefile(genTarget->Makefile)
+  , PathCheckSum(genTarget->Makefile)
   , QtVersion(qtVersion)
 {
   AutogenTarget.GlobalTarget = globalAutogenTarget;
@@ -280,12 +319,20 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
   }
 
   // Verbosity
-  this->Verbosity = this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
-  if (!this->Verbosity.empty()) {
-    unsigned long iVerb = 0;
-    if (!cmStrToULong(this->Verbosity, &iVerb)) {
-      // Non numeric verbosity
-      this->Verbosity = cmIsOn(this->Verbosity) ? "1" : "0";
+  {
+    std::string def =
+      this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
+    if (!def.empty()) {
+      unsigned long iVerb = 0;
+      if (cmStrToULong(def, &iVerb)) {
+        // Numeric verbosity
+        this->Verbosity = static_cast<unsigned int>(iVerb);
+      } else {
+        // Non numeric verbosity
+        if (cmIsOn(def)) {
+          this->Verbosity = 1;
+        }
+      }
     }
   }
 
@@ -352,15 +399,9 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
     cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
 
     // Include directory
-    this->Dir.Include = cmStrCat(this->Dir.Build, "/include");
-    // Per config include directories
-    if (this->MultiConfig) {
-      for (std::string const& cfg : this->ConfigsList) {
-        std::string& dir = this->Dir.ConfigInclude[cfg];
-        dir = cmStrCat(this->Dir.Build, "/include_", cfg);
-      }
-    }
-    this->Dir.IncludeGenExp = this->Dir.Include;
+    ConfigFileNames(this->Dir.Include, cmStrCat(this->Dir.Build, "/include"),
+                    "");
+    this->Dir.IncludeGenExp = this->Dir.Include.Default;
     if (this->MultiConfig) {
       this->Dir.IncludeGenExp += "_$<CONFIG>";
     }
@@ -383,36 +424,31 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
       cmStrCat(this->GenTarget->GetName(), "_autogen");
 
     // Autogen target parallel processing
-    this->AutogenTarget.Parallel =
-      this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
-    if (this->AutogenTarget.Parallel.empty() ||
-        (this->AutogenTarget.Parallel == "AUTO")) {
-      // Autodetect number of CPUs
-      this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount());
+    {
+      std::string prop = this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
+      if (prop.empty() || (prop == "AUTO")) {
+        // Autodetect number of CPUs
+        this->AutogenTarget.Parallel = GetParallelCPUCount();
+      } else {
+        this->AutogenTarget.Parallel = 1;
+      }
     }
 
     // Autogen target info and settings files
     {
+      // Info file
       this->AutogenTarget.InfoFile =
-        cmStrCat(this->Dir.Info, "/AutogenInfo.cmake");
+        cmStrCat(this->Dir.Info, "/AutogenInfo.json");
 
-      this->AutogenTarget.SettingsFile =
-        cmStrCat(this->Dir.Info, "/AutogenOldSettings.txt");
-
-      if (this->MultiConfig) {
-        for (std::string const& cfg : this->ConfigsList) {
-          std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg];
-          filename =
-            AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg);
-          this->AddCleanFile(filename);
-        }
-      } else {
-        this->AddCleanFile(this->AutogenTarget.SettingsFile);
-      }
+      // Used settings file
+      ConfigFileNames(this->AutogenTarget.SettingsFile,
+                      cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
+      ConfigFileClean(this->AutogenTarget.SettingsFile);
 
-      this->AutogenTarget.ParseCacheFile =
-        cmStrCat(this->Dir.Info, "/ParseCache.txt");
-      this->AddCleanFile(this->AutogenTarget.ParseCacheFile);
+      // Parse cache file
+      ConfigFileNames(this->AutogenTarget.ParseCacheFile,
+                      cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
+      ConfigFileClean(this->AutogenTarget.ParseCacheFile);
     }
 
     // Autogen target: Compute user defined dependencies
@@ -435,9 +471,15 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
       }
     }
 
-    // CMAKE_AUTOMOC_RELAXED_MODE deprecation warning
     if (this->Moc.Enabled) {
+      // Path prefix
+      if (cmIsOn(this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"))) {
+        this->Moc.PathPrefix = true;
+      }
+
+      // CMAKE_AUTOMOC_RELAXED_MODE
       if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) {
+        this->Moc.RelaxedMode = true;
         this->Makefile->IssueMessage(
           MessageType::AUTHOR_WARNING,
           cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
@@ -445,6 +487,32 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
                    "disabling it and converting the target ",
                    this->GenTarget->GetName(), " to regular mode."));
       }
+
+      // Options
+      cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"),
+                   this->Moc.Options);
+      // Filters
+      cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
+                   this->Moc.MacroNames);
+      {
+        auto filterList = cmExpandedList(
+          this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
+        if ((filterList.size() % 2) != 0) {
+          cmSystemTools::Error(
+            cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ",
+                     filterList.size(), " is not a multiple of 2."));
+          return false;
+        }
+        this->Moc.DependFilters.reserve(1 + (filterList.size() / 2));
+        this->Moc.DependFilters.emplace_back(
+          "Q_PLUGIN_METADATA",
+          "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+          "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+        for (std::size_t ii = 0; ii != filterList.size(); ii += 2) {
+          this->Moc.DependFilters.emplace_back(filterList[ii],
+                                               filterList[ii + 1]);
+        }
+      }
     }
   }
 
@@ -479,20 +547,27 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
 bool cmQtAutoGenInitializer::InitMoc()
 {
   // Mocs compilation file
-  this->Moc.MocsCompilation =
+  this->Moc.CompilationFile =
     cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
 
-  // Moc predefs command
+  // Moc predefs
   if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
       (this->QtVersion >= IntegerVersion(5, 8))) {
-    this->Moc.PredefsCmd = this->Makefile->GetSafeDefinition(
-      "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
+    // Command
+    cmExpandList(this->Makefile->GetSafeDefinition(
+                   "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"),
+                 this->Moc.PredefsCmd);
+    // Header
+    if (!this->Moc.PredefsCmd.empty()) {
+      ConfigFileNames(this->Moc.PredefsFile,
+                      cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
+    }
   }
 
   // Moc includes
   {
     SearchPathSanitizer sanitizer(this->Makefile);
-    auto GetIncludeDirs =
+    auto getDirs =
       [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
       // Get the include dirs for this target, without stripping the implicit
       // include dirs off, see issue #13667.
@@ -504,22 +579,22 @@ bool cmQtAutoGenInitializer::InitMoc()
     };
 
     // Default configuration include directories
-    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
+    this->Moc.Includes.Default = getDirs(this->ConfigDefault);
     // Other configuration settings
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::vector<std::string> dirs = GetIncludeDirs(cfg);
-        if (dirs != this->Moc.Includes) {
-          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
+        std::vector<std::string> dirs = getDirs(cfg);
+        if (dirs == this->Moc.Includes.Default) {
+          continue;
         }
+        this->Moc.Includes.Config[cfg] = std::move(dirs);
       }
     }
   }
 
   // Moc compile definitions
   {
-    auto GetCompileDefinitions =
-      [this](std::string const& cfg) -> std::set<std::string> {
+    auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
       std::set<std::string> defines;
       this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines);
 #ifdef _WIN32
@@ -532,14 +607,15 @@ bool cmQtAutoGenInitializer::InitMoc()
     };
 
     // Default configuration defines
-    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
+    this->Moc.Defines.Default = getDefs(this->ConfigDefault);
     // Other configuration defines
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::set<std::string> defines = GetCompileDefinitions(cfg);
-        if (defines != this->Moc.Defines) {
-          this->Moc.ConfigDefines[cfg] = std::move(defines);
+        std::set<std::string> defines = getDefs(cfg);
+        if (defines == this->Moc.Defines.Default) {
+          continue;
         }
+        this->Moc.Defines.Config[cfg] = std::move(defines);
       }
     }
   }
@@ -572,23 +648,22 @@ bool cmQtAutoGenInitializer::InitUic()
   }
   // Uic target options
   {
-    auto UicGetOpts =
-      [this](std::string const& cfg) -> std::vector<std::string> {
+    auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> {
       std::vector<std::string> opts;
       this->GenTarget->GetAutoUicOptions(opts, cfg);
       return opts;
     };
 
-    // Default settings
-    this->Uic.Options = UicGetOpts(this->ConfigDefault);
-
-    // Configuration specific settings
+    // Default options
+    this->Uic.Options.Default = getOpts(this->ConfigDefault);
+    // Configuration specific options
     if (this->MultiConfig) {
       for (std::string const& cfg : this->ConfigsList) {
-        std::vector<std::string> options = UicGetOpts(cfg);
-        if (options != this->Uic.Options) {
-          this->Uic.ConfigOptions[cfg] = std::move(options);
+        std::vector<std::string> options = getOpts(cfg);
+        if (options == this->Uic.Options.Default) {
+          continue;
         }
+        this->Uic.Options.Config[cfg] = std::move(options);
       }
     }
   }
@@ -822,8 +897,7 @@ bool cmQtAutoGenInitializer::InitScanFiles()
           // Check if the .ui file has uic options
           std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
           if (!uicOpts.empty()) {
-            this->Uic.FileFiles.push_back(fullPath);
-            this->Uic.FileOptions.push_back(cmExpandedList(uicOpts));
+            this->Uic.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts));
           }
         } else {
           // Register skipped .ui file
@@ -887,31 +961,17 @@ bool cmQtAutoGenInitializer::InitScanFiles()
       }
     }
     // Path checksum and file names
-    {
-      cmFilePathChecksum const fpathCheckSum(this->Makefile);
-      for (Qrc& qrc : this->Rcc.Qrcs) {
-        // Path checksum
-        qrc.QrcPathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
-        // Output file name
-        qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
-                                  "/qrc_", qrc.QrcName, ".cpp");
-        {
-          cm::string_view const baseSuffix = qrc.Unique
-            ? cm::string_view()
-            : cm::string_view(qrc.QrcPathChecksum);
-          std::string const base =
-            cmStrCat(this->Dir.Info, "/RCC", qrc.QrcName, baseSuffix);
-          qrc.LockFile = cmStrCat(base, ".lock");
-          qrc.InfoFile = cmStrCat(base, "Info.cmake");
-          qrc.SettingsFile = cmStrCat(base, "Settings.txt");
-          if (this->MultiConfig) {
-            for (std::string const& cfg : this->ConfigsList) {
-              qrc.ConfigSettingsFile[cfg] =
-                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
-            }
-          }
-        }
-      }
+    for (Qrc& qrc : this->Rcc.Qrcs) {
+      // Path checksum
+      qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
+      // Output file name
+      qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                "/qrc_", qrc.QrcName, ".cpp");
+      std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
+                                        qrc.QrcName, '_', qrc.QrcPathChecksum);
+      qrc.LockFile = cmStrCat(base, "_Lock.lock");
+      qrc.InfoFile = cmStrCat(base, "_Info.json");
+      ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt");
     }
     // rcc options
     for (Qrc& qrc : this->Rcc.Qrcs) {
@@ -959,8 +1019,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
   // Files provided by the autogen target
   std::vector<std::string> autogenProvides;
   if (this->Moc.Enabled) {
-    this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true);
-    autogenProvides.push_back(this->Moc.MocsCompilation);
+    this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
+    autogenProvides.push_back(this->Moc.CompilationFile);
   }
 
   // Compose target comment
@@ -1214,229 +1274,185 @@ bool cmQtAutoGenInitializer::SetupCustomTargets()
 
 bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
 {
-  InfoWriter ofs(this->AutogenTarget.InfoFile);
-  if (ofs) {
-    // Utility lambdas
-    auto MfDef = [this](const char* key) {
-      return this->Makefile->GetSafeDefinition(key);
-    };
+  // Utility lambdas
+  auto MfDef = [this](std::string const& key) {
+    return this->Makefile->GetSafeDefinition(key);
+  };
 
-    // Write common settings
-    ofs.Write("# Meta\n");
-    ofs.Write("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
-    ofs.Write("AM_PARALLEL", this->AutogenTarget.Parallel);
-    ofs.Write("AM_VERBOSITY", this->Verbosity);
-
-    ofs.Write("# Directories\n");
-    ofs.Write("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
-    ofs.Write("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
-    ofs.Write("AM_CMAKE_CURRENT_SOURCE_DIR",
-              MfDef("CMAKE_CURRENT_SOURCE_DIR"));
-    ofs.Write("AM_CMAKE_CURRENT_BINARY_DIR",
-              MfDef("CMAKE_CURRENT_BINARY_DIR"));
-    ofs.Write("AM_BUILD_DIR", this->Dir.Build);
-    ofs.Write("AM_INCLUDE_DIR", this->Dir.Include);
-    ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude);
-
-    std::vector<std::string> headers;
-    std::vector<std::string> headersFlags;
-    std::vector<std::string> headersBuildPaths;
-    std::vector<std::string> sources;
-    std::vector<std::string> sourcesFlags;
-    std::set<std::string> moc_skip;
-    std::set<std::string> uic_skip;
-
-    // Filter headers
-    {
-      auto headerCount = this->AutogenTarget.Headers.size();
-      headers.reserve(headerCount);
-      headersFlags.reserve(headerCount);
+  // Filtered headers and sources
+  std::set<std::string> moc_skip;
+  std::set<std::string> uic_skip;
+  std::vector<MUFile const*> headers;
+  std::vector<MUFile const*> sources;
 
-      std::vector<MUFile const*> sortedHeaders;
-      {
-        sortedHeaders.reserve(headerCount);
-        for (auto const& pair : this->AutogenTarget.Headers) {
-          sortedHeaders.emplace_back(pair.second.get());
-        }
-        std::sort(sortedHeaders.begin(), sortedHeaders.end(),
-                  [](MUFile const* a, MUFile const* b) {
-                    return (a->FullPath < b->FullPath);
-                  });
+  // Filter headers
+  {
+    headers.reserve(this->AutogenTarget.Headers.size());
+    for (auto const& pair : this->AutogenTarget.Headers) {
+      MUFile const* const muf = pair.second.get();
+      if (muf->Generated && !this->CMP0071Accept) {
+        continue;
       }
-
-      for (MUFile const* const muf : sortedHeaders) {
-        if (muf->Generated && !this->CMP0071Accept) {
-          continue;
-        }
-        if (muf->SkipMoc) {
-          moc_skip.insert(muf->FullPath);
-        }
-        if (muf->SkipUic) {
-          uic_skip.insert(muf->FullPath);
-        }
-        if (muf->MocIt || muf->UicIt) {
-          headers.emplace_back(muf->FullPath);
-          headersFlags.emplace_back(
-            cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'));
-        }
+      if (muf->SkipMoc) {
+        moc_skip.insert(muf->FullPath);
       }
-    }
-    // Header build paths
-    {
-      cmFilePathChecksum const fpathCheckSum(this->Makefile);
-      std::unordered_set<std::string> emitted;
-      for (std::string const& hdr : headers) {
-        std::string const basePath =
-          cmStrCat(fpathCheckSum.getPart(hdr), "/moc_",
-                   cmSystemTools::GetFilenameWithoutLastExtension(hdr));
-        std::string suffix;
-        for (int ii = 0; ii != 1024; ++ii) {
-          std::string path = cmStrCat(basePath, suffix, ".cpp");
-          if (emitted.emplace(path).second) {
-            headersBuildPaths.emplace_back(std::move(path));
-            break;
-          }
-          suffix = cmStrCat('_', ii + 1);
-        }
+      if (muf->SkipUic) {
+        uic_skip.insert(muf->FullPath);
+      }
+      if (muf->MocIt || muf->UicIt) {
+        headers.emplace_back(muf);
       }
     }
+    std::sort(headers.begin(), headers.end(),
+              [](MUFile const* a, MUFile const* b) {
+                return (a->FullPath < b->FullPath);
+              });
+  }
 
-    // Filter sources
-    {
-      auto sourcesCount = this->AutogenTarget.Sources.size();
-      sources.reserve(sourcesCount);
-      sourcesFlags.reserve(sourcesCount);
-
-      std::vector<MUFile const*> sorted;
-      sorted.reserve(sourcesCount);
-      for (auto const& pair : this->AutogenTarget.Sources) {
-        sorted.emplace_back(pair.second.get());
+  // Filter sources
+  {
+    sources.reserve(this->AutogenTarget.Sources.size());
+    for (auto const& pair : this->AutogenTarget.Sources) {
+      MUFile const* const muf = pair.second.get();
+      if (muf->Generated && !this->CMP0071Accept) {
+        continue;
       }
-      std::sort(sorted.begin(), sorted.end(),
-                [](MUFile const* a, MUFile const* b) {
-                  return (a->FullPath < b->FullPath);
-                });
-
-      for (MUFile const* const muf : sorted) {
-        if (muf->Generated && !this->CMP0071Accept) {
-          continue;
-        }
-        if (muf->SkipMoc) {
-          moc_skip.insert(muf->FullPath);
-        }
-        if (muf->SkipUic) {
-          uic_skip.insert(muf->FullPath);
-        }
-        if (muf->MocIt || muf->UicIt) {
-          sources.emplace_back(muf->FullPath);
-          sourcesFlags.emplace_back(
-            cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'));
-        }
+      if (muf->SkipMoc) {
+        moc_skip.insert(muf->FullPath);
+      }
+      if (muf->SkipUic) {
+        uic_skip.insert(muf->FullPath);
+      }
+      if (muf->MocIt || muf->UicIt) {
+        sources.emplace_back(muf);
       }
     }
+    std::sort(sources.begin(), sources.end(),
+              [](MUFile const* a, MUFile const* b) {
+                return (a->FullPath < b->FullPath);
+              });
+  }
 
-    ofs.Write("# Qt\n");
-    ofs.WriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major);
-    ofs.Write("AM_QT_MOC_EXECUTABLE", this->Moc.Executable);
-    ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable);
-
-    ofs.Write("# Files\n");
-    ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
-    ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile);
-    ofs.WriteConfig("AM_SETTINGS_FILE",
-                    this->AutogenTarget.ConfigSettingsFile);
-    ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
-    ofs.WriteStrings("AM_HEADERS", headers);
-    ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags);
-    ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths);
-    ofs.WriteStrings("AM_SOURCES", sources);
-    ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags);
-
-    // Write moc settings
-    if (this->Moc.Enabled) {
-      ofs.Write("# MOC settings\n");
-      ofs.WriteStrings("AM_MOC_SKIP", moc_skip);
-      ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines);
-      ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines);
-      ofs.WriteStrings("AM_MOC_INCLUDES", this->Moc.Includes);
-      ofs.WriteConfigStrings("AM_MOC_INCLUDES", this->Moc.ConfigIncludes);
-      ofs.Write("AM_MOC_OPTIONS",
-                this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"));
-      ofs.Write("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE"));
-      ofs.Write("AM_MOC_PATH_PREFIX",
-                this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"));
-      ofs.Write("AM_MOC_MACRO_NAMES",
-                this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"));
-      ofs.Write("AM_MOC_DEPEND_FILTERS",
-                this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
-      ofs.Write("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
-    }
-
-    // Write uic settings
-    if (this->Uic.Enabled) {
-      // Add skipped .ui files
-      uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
+  // Info writer
+  InfoWriter info;
+
+  // General
+  info.SetBool("MULTI_CONFIG", this->MultiConfig);
+  info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
+  info.SetUInt("VERBOSITY", this->Verbosity);
+
+  // Directories
+  info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
+  info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
+  info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
+  info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
+  info.Set("BUILD_DIR", this->Dir.Build);
+  info.SetConfig("INCLUDE_DIR", this->Dir.Include);
+
+  info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
+  info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
+  info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
+
+  info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
+  info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
+  info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
+  info.SetArray("HEADER_EXTENSIONS",
+                this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
+  info.SetArrayArray(
+    "HEADERS", headers, [this](Json::Value& jval, MUFile const* muf) {
+      jval.resize(3u);
+      jval[0u] = muf->FullPath;
+      jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
+      jval[2u] = this->GetMocBuildPath(*muf);
+    });
+  info.SetArrayArray(
+    "SOURCES", sources, [](Json::Value& jval, MUFile const* muf) {
+      jval.resize(2u);
+      jval[0u] = muf->FullPath;
+      jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
+    });
+
+  // Write moc settings
+  if (this->Moc.Enabled) {
+    info.SetArray("MOC_SKIP", moc_skip);
+    info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines);
+    info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes);
+    info.SetArray("MOC_OPTIONS", this->Moc.Options);
+    info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
+    info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
+    info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
+    info.SetArrayArray(
+      "MOC_DEPEND_FILTERS", this->Moc.DependFilters,
+      [](Json::Value& jval, std::pair<std::string, std::string> const& pair) {
+        jval.resize(2u);
+        jval[0u] = pair.first;
+        jval[1u] = pair.second;
+      });
+    info.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
+    info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
+    info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
+  }
 
-      ofs.Write("# UIC settings\n");
-      ofs.WriteStrings("AM_UIC_SKIP", uic_skip);
-      ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options);
-      ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions);
-      ofs.WriteStrings("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles);
-      ofs.WriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions);
-      ofs.WriteStrings("AM_UIC_SEARCH_PATHS", this->Uic.SearchPaths);
-    }
-  } else {
-    cmSystemTools::Error(cmStrCat("AutoGen: Could not write file ",
-                                  this->AutogenTarget.InfoFile));
-    return false;
+  // Write uic settings
+  if (this->Uic.Enabled) {
+    // Add skipped .ui files
+    uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
+
+    info.SetArray("UIC_SKIP", uic_skip);
+    info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFiles,
+                       [](Json::Value& jval, UicT::UiFileT const& uiFile) {
+                         jval.resize(2u);
+                         jval[0u] = uiFile.first;
+                         InfoWriter::MakeStringArray(jval[1u], uiFile.second);
+                       });
+    info.SetConfigArray("UIC_OPTIONS", this->Uic.Options);
+    info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths);
   }
 
+  info.Save(this->AutogenTarget.InfoFile);
+
   return true;
 }
 
 bool cmQtAutoGenInitializer::SetupWriteRccInfo()
 {
   for (Qrc const& qrc : this->Rcc.Qrcs) {
-    InfoWriter ofs(qrc.InfoFile);
-    if (ofs) {
-      // Utility lambdas
-      auto MfDef = [this](const char* key) {
-        return this->Makefile->GetSafeDefinition(key);
-      };
+    // Utility lambdas
+    auto MfDef = [this](std::string const& key) {
+      return this->Makefile->GetSafeDefinition(key);
+    };
 
-      // Write
-      ofs.Write("# Configurations\n");
-      ofs.Write("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
-      ofs.Write("ARCC_VERBOSITY", this->Verbosity);
-      ofs.Write("# Settings file\n");
-      ofs.Write("ARCC_SETTINGS_FILE", qrc.SettingsFile);
-      ofs.WriteConfig("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile);
-
-      ofs.Write("# Directories\n");
-      ofs.Write("ARCC_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
-      ofs.Write("ARCC_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
-      ofs.Write("ARCC_BUILD_DIR", this->Dir.Build);
-      ofs.Write("ARCC_INCLUDE_DIR", this->Dir.Include);
-      ofs.WriteConfig("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude);
-
-      ofs.Write("# Rcc executable\n");
-      ofs.Write("ARCC_RCC_EXECUTABLE", this->Rcc.Executable);
-      ofs.WriteStrings("ARCC_RCC_LIST_OPTIONS",
-                       this->Rcc.ExecutableFeatures->ListOptions);
-
-      ofs.Write("# Rcc job\n");
-      ofs.Write("ARCC_LOCK_FILE", qrc.LockFile);
-      ofs.Write("ARCC_SOURCE", qrc.QrcFile);
-      ofs.Write("ARCC_OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
-      ofs.Write("ARCC_OUTPUT_NAME",
-                cmSystemTools::GetFilenameName(qrc.OutputFile));
-      ofs.WriteStrings("ARCC_OPTIONS", qrc.Options);
-      ofs.WriteStrings("ARCC_INPUTS", qrc.Resources);
-    } else {
-      cmSystemTools::Error(
-        cmStrCat("AutoRcc: Could not write file ", qrc.InfoFile));
-      return false;
-    }
+    InfoWriter info;
+
+    // General
+    info.SetBool("MULTI_CONFIG", this->MultiConfig);
+    info.SetUInt("VERBOSITY", this->Verbosity);
+
+    // Files
+    info.Set("LOCK_FILE", qrc.LockFile);
+    info.SetConfig("SETTINGS_FILE", qrc.SettingsFile);
+
+    // Directories
+    info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
+    info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
+    info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
+    info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
+    info.Set("BUILD_DIR", this->Dir.Build);
+    info.SetConfig("INCLUDE_DIR", this->Dir.Include);
+
+    // rcc executable
+    info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
+    info.SetArray("RCC_LIST_OPTIONS",
+                  this->Rcc.ExecutableFeatures->ListOptions);
+
+    // qrc file
+    info.Set("SOURCE", qrc.QrcFile);
+    info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
+    info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
+    info.SetArray("OPTIONS", qrc.Options);
+    info.SetArray("INPUTS", qrc.Resources);
+
+    info.Save(qrc.InfoFile);
   }
 
   return true;
@@ -1509,6 +1525,28 @@ void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
                                           fileName.c_str(), false);
 }
 
+void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
+                                             cm::string_view prefix,
+                                             cm::string_view suffix)
+{
+  configString.Default = cmStrCat(prefix, suffix);
+  if (this->MultiConfig) {
+    for (auto const& cfg : this->ConfigsList) {
+      configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix);
+    }
+  }
+}
+
+void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
+{
+  this->AddCleanFile(configString.Default);
+  if (this->MultiConfig) {
+    for (auto const& pair : configString.Config) {
+      this->AddCleanFile(pair.second);
+    }
+  }
+}
+
 std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
 cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
 {
@@ -1581,6 +1619,30 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
   return res;
 }
 
+std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf)
+{
+  std::string res;
+  if (!muf.MocIt) {
+    return res;
+  }
+  {
+    std::string const basePath =
+      cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_",
+               FileNameWithoutLastExtension(muf.FullPath));
+    std::string suffix;
+    constexpr std::size_t num_tries_max = 256;
+    for (std::size_t ii = 0; ii != num_tries_max; ++ii) {
+      res = cmStrCat(basePath, suffix, ".cpp");
+      if (this->Moc.EmittedBuildPaths.emplace(res).second) {
+        break;
+      }
+      // Compute new suffix
+      suffix = cmStrCat('_', ii + 1);
+    }
+  }
+  return res;
+}
+
 bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
                                              const std::string& executable,
                                              bool ignoreMissingTarget) const

+ 56 - 58
Source/cmQtAutoGenInitializer.h

@@ -4,17 +4,16 @@
 #define cmQtAutoGenInitializer_h
 
 #include "cmConfigure.h" // IWYU pragma: keep
-#include "cmGeneratedFileStream.h"
+#include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 
 #include <cm/string_view>
 
-#include <map>
 #include <memory>
-#include <ostream>
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -32,6 +31,23 @@ class cmTarget;
 class cmQtAutoGenInitializer : public cmQtAutoGen
 {
 public:
+  /** String value with per configuration variants.  */
+  class ConfigString
+  {
+  public:
+    std::string Default;
+    std::unordered_map<std::string, std::string> Config;
+  };
+
+  /** String values with per configuration variants.  */
+  template <typename C>
+  class ConfigStrings
+  {
+  public:
+    C Default;
+    std::unordered_map<std::string, C> Config;
+  };
+
   /** rcc job.  */
   class Qrc
   {
@@ -41,8 +57,7 @@ public:
     std::string QrcName;
     std::string QrcPathChecksum;
     std::string InfoFile;
-    std::string SettingsFile;
-    std::map<std::string, std::string> ConfigSettingsFile;
+    ConfigString SettingsFile;
     std::string OutputFile;
     bool Generated = false;
     bool Unique = false;
@@ -63,7 +78,7 @@ public:
   };
   using MUFileHandle = std::unique_ptr<MUFile>;
 
-  /** Abstract moc/uic/rcc generator variables base class  */
+  /** Abstract moc/uic/rcc generator variables base class.  */
   struct GenVarsT
   {
     bool Enabled = false;
@@ -81,39 +96,6 @@ public:
       , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){};
   };
 
-  /** Writes a CMake info file.  */
-  class InfoWriter
-  {
-  public:
-    /** Open the given file.  */
-    InfoWriter(std::string const& filename);
-
-    /** @return True if the file is open.  */
-    explicit operator bool() const { return static_cast<bool>(Ofs_); }
-
-    void Write(cm::string_view text) { Ofs_ << text; }
-    void Write(cm::string_view, std::string const& value);
-    void WriteUInt(cm::string_view, unsigned int value);
-
-    template <class C>
-    void WriteStrings(cm::string_view, C const& container);
-    void WriteConfig(cm::string_view,
-                     std::map<std::string, std::string> const& map);
-    template <class C>
-    void WriteConfigStrings(cm::string_view,
-                            std::map<std::string, C> const& map);
-    void WriteNestedLists(cm::string_view,
-                          std::vector<std::vector<std::string>> const& lists);
-
-  private:
-    template <class IT>
-    static std::string ListJoin(IT it_begin, IT it_end);
-    static std::string ConfigKey(cm::string_view, std::string const& config);
-
-  private:
-    cmGeneratedFileStream Ofs_;
-  };
-
 public:
   /** @return The detected Qt version and the required Qt major version.  */
   static std::pair<IntegerVersion, unsigned int> GetQtVersion(
@@ -153,6 +135,12 @@ private:
                         cm::string_view genNameUpper);
   void AddCleanFile(std::string const& fileName);
 
+  void ConfigFileNames(ConfigString& configString, cm::string_view prefix,
+                       cm::string_view suffix);
+  void ConfigFileClean(ConfigString& configString);
+
+  std::string GetMocBuildPath(MUFile const& muf);
+
   bool GetQtExecutable(GenVarsT& genVars, const std::string& executable,
                        bool ignoreMissingTarget) const;
 
@@ -162,16 +150,17 @@ private:
   cmGlobalGenerator* GlobalGen = nullptr;
   cmLocalGenerator* LocalGen = nullptr;
   cmMakefile* Makefile = nullptr;
+  cmFilePathChecksum const PathCheckSum;
 
   // -- Configuration
   IntegerVersion QtVersion;
+  unsigned int Verbosity = 0;
   bool MultiConfig = false;
+  bool CMP0071Accept = false;
+  bool CMP0071Warn = false;
   std::string ConfigDefault;
   std::vector<std::string> ConfigsList;
-  std::string Verbosity;
   std::string TargetsFolder;
-  bool CMP0071Accept = false;
-  bool CMP0071Warn = false;
 
   /** Common directories.  */
   struct
@@ -179,8 +168,7 @@ private:
     std::string Info;
     std::string Build;
     std::string Work;
-    std::string Include;
-    std::map<std::string, std::string> ConfigInclude;
+    ConfigString Include;
     std::string IncludeGenExp;
   } Dir;
 
@@ -190,12 +178,11 @@ private:
     std::string Name;
     bool GlobalTarget = false;
     // Settings
-    std::string Parallel;
+    unsigned int Parallel = 1;
     // Configuration files
     std::string InfoFile;
-    std::string SettingsFile;
-    std::string ParseCacheFile;
-    std::map<std::string, std::string> ConfigSettingsFile;
+    ConfigString SettingsFile;
+    ConfigString ParseCacheFile;
     // Dependencies
     bool DependOrigin = false;
     std::set<std::string> DependFiles;
@@ -212,26 +199,37 @@ private:
     MocT()
       : GenVarsT(GenT::MOC){};
 
-    std::string PredefsCmd;
-    std::vector<std::string> Includes;
-    std::map<std::string, std::vector<std::string>> ConfigIncludes;
-    std::set<std::string> Defines;
-    std::map<std::string, std::set<std::string>> ConfigDefines;
-    std::string MocsCompilation;
+    bool RelaxedMode = false;
+    bool PathPrefix = false;
+    std::string CompilationFile;
+    // Compiler implicit pre defines
+    std::vector<std::string> PredefsCmd;
+    ConfigString PredefsFile;
+    // Defines
+    ConfigStrings<std::set<std::string>> Defines;
+    // Includes
+    ConfigStrings<std::vector<std::string>> Includes;
+    // Options
+    std::vector<std::string> Options;
+    // Filters
+    std::vector<std::string> MacroNames;
+    std::vector<std::pair<std::string, std::string>> DependFilters;
+    // Utility
+    std::unordered_set<std::string> EmittedBuildPaths;
   } Moc;
 
   /** uic variables.  */
   struct UicT : public GenVarsT
   {
+    using UiFileT = std::pair<std::string, std::vector<std::string>>;
+
     UicT()
       : GenVarsT(GenT::UIC){};
 
     std::set<std::string> SkipUi;
+    std::vector<UiFileT> UiFiles;
+    ConfigStrings<std::vector<std::string>> Options;
     std::vector<std::string> SearchPaths;
-    std::vector<std::string> Options;
-    std::map<std::string, std::vector<std::string>> ConfigOptions;
-    std::vector<std::string> FileFiles;
-    std::vector<std::vector<std::string>> FileOptions;
   } Uic;
 
   /** rcc variables.  */

+ 222 - 38
Source/cmQtAutoGenerator.cxx

@@ -2,19 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenerator.h"
 
-#include <cm/memory>
-
-#include "cmsys/FStream.hxx"
-
-#include "cmGlobalGenerator.h"
-#include "cmMakefile.h"
 #include "cmQtAutoGen.h"
-#include "cmState.h"
-#include "cmStateDirectory.h"
-#include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmake.h"
+#include "cm_jsoncpp_reader.h"
+#include "cmsys/FStream.hxx"
 
 cmQtAutoGenerator::Logger::Logger()
 {
@@ -44,13 +36,10 @@ cmQtAutoGenerator::Logger::Logger()
 
 cmQtAutoGenerator::Logger::~Logger() = default;
 
-void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value)
+void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value)
 {
-  unsigned long verbosity = 0;
-  if (cmStrToULong(value, &verbosity)) {
-    if (this->Verbosity_ < verbosity) {
-      this->Verbosity_ = static_cast<unsigned int>(verbosity);
-    }
+  if (this->Verbosity_ < value) {
+    this->Verbosity_ = value;
   }
 }
 
@@ -214,7 +203,10 @@ bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
   return differs;
 }
 
-cmQtAutoGenerator::cmQtAutoGenerator() = default;
+cmQtAutoGenerator::cmQtAutoGenerator(GenT genType)
+  : GenType_(genType)
+{
+}
 
 cmQtAutoGenerator::~cmQtAutoGenerator() = default;
 
@@ -223,7 +215,7 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile,
 {
   // Info settings
   InfoFile_ = infoFile;
-  cmSystemTools::ConvertToUnixSlashes(InfoFile_);
+  cmSystemTools::CollapseFullPath(InfoFile_);
   if (!InfoFileTime_.Load(InfoFile_)) {
     cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ",
                                    Quoted(InfoFile_), " is not readable\n"));
@@ -232,29 +224,221 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile,
   InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
   InfoConfig_ = config;
 
-  bool success = false;
+  // Read info file
   {
-    cmake cm(cmake::RoleScript, cmState::Unknown);
-    cm.SetHomeOutputDirectory(InfoDir());
-    cm.SetHomeDirectory(InfoDir());
-    cm.GetCurrentSnapshot().SetDefaultDefinitions();
-    cmGlobalGenerator gg(&cm);
-
-    cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
-    snapshot.GetDirectory().SetCurrentBinary(InfoDir());
-    snapshot.GetDirectory().SetCurrentSource(InfoDir());
-
-    auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
-    // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
-    // https://gitlab.kitware.com/cmake/cmake/issues/17570
-    makefile->SetPolicyVersion("3.9", std::string());
-    gg.SetCurrentMakefile(makefile.get());
-    success = this->Init(makefile.get());
+    cmsys::ifstream ifs(InfoFile_.c_str(), (std::ios::in | std::ios::binary));
+    if (!ifs) {
+      Log().Error(GenType_,
+                  cmStrCat("Could not to open info file ", Quoted(InfoFile_)));
+      return false;
+    }
+    try {
+      ifs >> Info_;
+    } catch (...) {
+      Log().Error(GenType_,
+                  cmStrCat("Could not read info file ", Quoted(InfoFile_)));
+      return false;
+    }
   }
-  if (success) {
-    success = this->Process();
+  // Info: setup logger
+  {
+    unsigned int value = 0;
+    if (!InfoUInt("VERBOSITY", value, false)) {
+      return false;
+    }
+    Logger_.RaiseVerbosity(value);
   }
-  return success;
+  // Info: setup project directories
+  if (!InfoString("CMAKE_SOURCE_DIR", ProjectDirs_.Source, true) ||
+      !InfoString("CMAKE_BINARY_DIR", ProjectDirs_.Binary, true) ||
+      !InfoString("CMAKE_CURRENT_SOURCE_DIR", ProjectDirs_.CurrentSource,
+                  true) ||
+      !InfoString("CMAKE_CURRENT_BINARY_DIR", ProjectDirs_.CurrentBinary,
+                  true)) {
+    return false;
+  }
+
+  if (!this->InitFromInfo()) {
+    return false;
+  }
+  // Clear info
+  Info_ = Json::nullValue;
+
+  return this->Process();
+}
+
+bool cmQtAutoGenerator::LogInfoError(GenT genType,
+                                     cm::string_view message) const
+{
+  this->Log().Error(
+    genType,
+    cmStrCat("Info error in info file\n", Quoted(InfoFile()), ":\n", message));
+  return false;
+}
+
+bool cmQtAutoGenerator::LogInfoError(cm::string_view message) const
+{
+  return LogInfoError(GenType_, message);
+}
+
+bool cmQtAutoGenerator::JsonGetArray(std::vector<std::string>& list,
+                                     Json::Value const& jval)
+{
+  Json::ArrayIndex const arraySize = jval.size();
+  if (arraySize == 0) {
+    return false;
+  }
+
+  bool picked = false;
+  list.reserve(list.size() + arraySize);
+  for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+    Json::Value const& ival = jval[ii];
+    if (ival.isString()) {
+      list.emplace_back(ival.asString());
+      picked = true;
+    }
+  }
+  return picked;
+}
+
+bool cmQtAutoGenerator::JsonGetArray(std::unordered_set<std::string>& list,
+                                     Json::Value const& jval)
+{
+  Json::ArrayIndex const arraySize = jval.size();
+  if (arraySize == 0) {
+    return false;
+  }
+
+  bool picked = false;
+  list.reserve(list.size() + arraySize);
+  for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+    Json::Value const& ival = jval[ii];
+    if (ival.isString()) {
+      list.emplace(ival.asString());
+      picked = true;
+    }
+  }
+  return picked;
+}
+
+std::string cmQtAutoGenerator::InfoConfigKey(std::string const& key) const
+{
+  return cmStrCat(key, '_', InfoConfig());
+}
+
+bool cmQtAutoGenerator::InfoString(std::string const& key, std::string& value,
+                                   bool required) const
+{
+  Json::Value const& jval = Info()[key];
+  if (!jval.isString()) {
+    if (!jval.isNull() || required) {
+      return LogInfoError(cmStrCat(key, " is not a string."));
+    }
+  } else {
+    value = jval.asString();
+    if (value.empty() && required) {
+      return LogInfoError(cmStrCat(key, " is empty."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoStringConfig(std::string const& key,
+                                         std::string& value,
+
+                                         bool required) const
+{
+  { // Try config
+    std::string const configKey = InfoConfigKey(key);
+    Json::Value const& jval = Info_[configKey];
+    if (!jval.isNull()) {
+      if (!jval.isString()) {
+        return LogInfoError(cmStrCat(configKey, " is not a string."));
+      }
+      value = jval.asString();
+      if (required && value.empty()) {
+        return LogInfoError(cmStrCat(configKey, " is empty."));
+      }
+      return true;
+    }
+  }
+  // Try plain
+  return InfoString(key, value, required);
+}
+
+bool cmQtAutoGenerator::InfoBool(std::string const& key, bool& value,
+                                 bool required) const
+{
+  Json::Value const& jval = Info()[key];
+  if (jval.isBool()) {
+    value = jval.asBool();
+  } else {
+    if (!jval.isNull() || required) {
+      return LogInfoError(cmStrCat(key, " is not a boolean."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoUInt(std::string const& key, unsigned int& value,
+                                 bool required) const
+{
+  Json::Value const& jval = Info()[key];
+  if (jval.isUInt()) {
+    value = jval.asUInt();
+  } else {
+    if (!jval.isNull() || required) {
+      return LogInfoError(cmStrCat(key, " is not an unsigned integer."));
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoGenerator::InfoArray(std::string const& key,
+                                  std::vector<std::string>& list,
+                                  bool required) const
+{
+  Json::Value const& jval = Info()[key];
+  if (!jval.isArray()) {
+    if (!jval.isNull() || required) {
+      return LogInfoError(cmStrCat(key, " is not an array."));
+    }
+  }
+  return JsonGetArray(list, jval) || !required;
+}
+
+bool cmQtAutoGenerator::InfoArray(std::string const& key,
+                                  std::unordered_set<std::string>& list,
+                                  bool required) const
+{
+  Json::Value const& jval = Info()[key];
+  if (!jval.isArray()) {
+    if (!jval.isNull() || required) {
+      return LogInfoError(cmStrCat(key, " is not an array."));
+    }
+  }
+  return JsonGetArray(list, jval) || !required;
+}
+
+bool cmQtAutoGenerator::InfoArrayConfig(std::string const& key,
+                                        std::vector<std::string>& list,
+                                        bool required) const
+{
+  { // Try config
+    std::string const configKey = InfoConfigKey(key);
+    Json::Value const& jval = Info()[configKey];
+    if (!jval.isNull()) {
+      if (!jval.isArray()) {
+        return LogInfoError(cmStrCat(configKey, " is not an array string."));
+      }
+      if (!JsonGetArray(list, jval) && required) {
+        return LogInfoError(cmStrCat(configKey, " is empty."));
+      }
+      return true;
+    }
+  }
+  // Try plain
+  return InfoArray(key, list, required);
 }
 
 std::string cmQtAutoGenerator::SettingsFind(std::string const& content,

+ 42 - 7
Source/cmQtAutoGenerator.h

@@ -7,15 +7,15 @@
 
 #include "cmFileTime.h"
 #include "cmQtAutoGen.h"
+#include "cm_jsoncpp_value.h"
 
 #include <cm/string_view>
 
 #include <mutex>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
-class cmMakefile;
-
 /** \class cmQtAutoGenerator
  * \brief Base class for QtAutoGen generators
  */
@@ -34,7 +34,7 @@ public:
     // -- Verbosity
     unsigned int Verbosity() const { return this->Verbosity_; }
     void SetVerbosity(unsigned int value) { this->Verbosity_ = value; }
-    void RaiseVerbosity(std::string const& value);
+    void RaiseVerbosity(unsigned int value);
     bool Verbose() const { return (this->Verbosity_ != 0); }
     void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; }
     // -- Color output
@@ -80,7 +80,7 @@ public:
 
 public:
   // -- Constructors
-  cmQtAutoGenerator();
+  cmQtAutoGenerator(GenT genType);
   virtual ~cmQtAutoGenerator();
 
   cmQtAutoGenerator(cmQtAutoGenerator const&) = delete;
@@ -91,10 +91,39 @@ public:
 
   // -- InfoFile
   std::string const& InfoFile() const { return InfoFile_; }
+  Json::Value const& Info() const { return Info_; }
   cmFileTime const& InfoFileTime() const { return InfoFileTime_; }
   std::string const& InfoDir() const { return InfoDir_; }
   std::string const& InfoConfig() const { return InfoConfig_; }
 
+  bool LogInfoError(GenT genType, cm::string_view message) const;
+  bool LogInfoError(cm::string_view message) const;
+
+  /** Returns true if strings were appended to the list.  */
+  static bool JsonGetArray(std::vector<std::string>& list,
+                           Json::Value const& jval);
+  /** Returns true if strings were found in the JSON array.  */
+  static bool JsonGetArray(std::unordered_set<std::string>& list,
+                           Json::Value const& jval);
+
+  std::string InfoConfigKey(std::string const& key) const;
+
+  /** Returns false if the JSON value isn't a string.  */
+  bool InfoString(std::string const& key, std::string& value,
+                  bool required) const;
+  bool InfoStringConfig(std::string const& key, std::string& value,
+                        bool required) const;
+  bool InfoBool(std::string const& key, bool& value, bool required) const;
+  bool InfoUInt(std::string const& key, unsigned int& value,
+                bool required) const;
+  /** Returns false if the JSON value isn't an array.  */
+  bool InfoArray(std::string const& key, std::vector<std::string>& list,
+                 bool required) const;
+  bool InfoArray(std::string const& key, std::unordered_set<std::string>& list,
+                 bool required) const;
+  bool InfoArrayConfig(std::string const& key, std::vector<std::string>& list,
+                       bool required) const;
+
   // -- Directories
   ProjectDirsT const& ProjectDirs() const { return ProjectDirs_; }
 
@@ -104,16 +133,22 @@ public:
 
 protected:
   // -- Abstract processing interface
-  virtual bool Init(cmMakefile* makefile) = 0;
+  virtual bool InitFromInfo() = 0;
   virtual bool Process() = 0;
-  ProjectDirsT& ProjectDirsRef() { return ProjectDirs_; }
+  // - Utility classes
+  Logger const& Log() const { return Logger_; }
 
 private:
-  // -- Info settings
+  // -- Generator type
+  GenT GenType_;
+  // -- Logging
+  Logger Logger_;
+  // -- Info file
   std::string InfoFile_;
   cmFileTime InfoFileTime_;
   std::string InfoDir_;
   std::string InfoConfig_;
+  Json::Value Info_;
   // -- Directories
   ProjectDirsT ProjectDirs_;
 };

+ 244 - 269
Source/cmQtAutoMocUic.cxx

@@ -11,19 +11,22 @@
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmGeneratedFileStream.h"
-#include "cmMakefile.h"
 #include "cmQtAutoGen.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmake.h"
+#include "cm_jsoncpp_value.h"
 #include "cmsys/FStream.hxx"
 
 #if defined(__APPLE__)
 #  include <unistd.h>
 #endif
 
-static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
-static constexpr std::size_t UiUnderscoreLength = 3;  // Length of "ui_"
+namespace {
+
+constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
+constexpr std::size_t UiUnderscoreLength = 3;  // Length of "ui_"
+
+} // End of unnamed namespace
 
 cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key,
                                          std::size_t basePrefixLength)
@@ -1464,10 +1467,10 @@ void cmQtAutoMocUic::JobCompileUicT::Process()
   std::vector<std::string> cmd;
   cmd.push_back(UicConst().Executable);
   {
-    std::vector<std::string> allOpts = UicConst().TargetOptions;
-    auto optionIt = UicConst().Options.find(sourceFile);
-    if (optionIt != UicConst().Options.end()) {
-      UicMergeOptions(allOpts, optionIt->second,
+    std::vector<std::string> allOpts = UicConst().Options;
+    auto optionIt = UicConst().UiFiles.find(sourceFile);
+    if (optionIt != UicConst().UiFiles.end()) {
+      UicMergeOptions(allOpts, optionIt->second.Options,
                       (BaseConst().QtVersionMajor == 5));
     }
     cmAppend(cmd, allOpts);
@@ -1548,338 +1551,310 @@ void cmQtAutoMocUic::JobFinishT::Process()
   Gen()->AbortSuccess();
 }
 
-cmQtAutoMocUic::cmQtAutoMocUic() = default;
+cmQtAutoMocUic::cmQtAutoMocUic()
+  : cmQtAutoGenerator(GenT::GEN)
+{
+}
 cmQtAutoMocUic::~cmQtAutoMocUic() = default;
 
-bool cmQtAutoMocUic::Init(cmMakefile* makefile)
+bool cmQtAutoMocUic::InitFromInfo()
 {
-  // Utility lambdas
-  auto InfoGet = [makefile](cm::string_view key) {
-    return makefile->GetSafeDefinition(std::string(key));
-  };
-  auto InfoGetBool = [makefile](cm::string_view key) {
-    return makefile->IsOn(std::string(key));
-  };
-  auto InfoGetList =
-    [makefile](cm::string_view key) -> std::vector<std::string> {
-    return cmExpandedList(makefile->GetSafeDefinition(std::string(key)));
-  };
-  auto InfoGetLists =
-    [makefile](cm::string_view key) -> std::vector<std::vector<std::string>> {
-    std::vector<std::vector<std::string>> lists;
-    {
-      std::string const value = makefile->GetSafeDefinition(std::string(key));
-      std::string::size_type pos = 0;
-      while (pos < value.size()) {
-        std::string::size_type next = value.find(ListSep, pos);
-        std::string::size_type length =
-          (next != std::string::npos) ? next - pos : value.size() - pos;
-        // Remove enclosing braces
-        if (length >= 2) {
-          std::string::const_iterator itBeg = value.begin() + (pos + 1);
-          std::string::const_iterator itEnd = itBeg + (length - 2);
-          lists.emplace_back(cmExpandedList(std::string(itBeg, itEnd)));
-        }
-        pos += length;
-        pos += ListSep.size();
-      }
-    }
-    return lists;
-  };
-  auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string {
-    if (const char* valueConf =
-          makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) {
-      return std::string(valueConf);
-    }
-    return makefile->GetSafeDefinition(std::string(key));
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](cm::string_view key) -> std::vector<std::string> {
-    return cmExpandedList(InfoGetConfig(key));
-  };
-  auto LogInfoError = [this](cm::string_view msg) -> bool {
-    this->Log().Error(GenT::GEN,
-                      cmStrCat("In ", Quoted(this->InfoFile()), ":\n", msg));
+  // -- Required settings
+  if (!InfoBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) ||
+      !InfoUInt("QT_VERSION_MAJOR", BaseConst_.QtVersionMajor, true) ||
+      !InfoUInt("PARALLEL", BaseConst_.ThreadCount, false) ||
+      !InfoString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) ||
+      !InfoStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir, true) ||
+      !InfoString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
+      !InfoStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile, true) ||
+      !InfoStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
+      !InfoArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
+      !InfoString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
+      !InfoString("QT_UIC_EXECUTABLE", UicConst_.Executable, false)) {
     return false;
-  };
-  auto MatchSizes = [&LogInfoError](cm::string_view keyA, cm::string_view keyB,
-                                    std::size_t sizeA,
-                                    std::size_t sizeB) -> bool {
-    if (sizeA == sizeB) {
-      return true;
-    }
-    return LogInfoError(cmStrCat("Lists sizes mismatch ", keyA, '(', sizeA,
-                                 ") ", keyB, '(', sizeB, ')'));
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    return LogInfoError("File processing failed");
-  }
-
-  // -- Meta
-  Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY"));
-  BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
-  {
-    unsigned long num = 1;
-    if (cmStrToULong(InfoGet("AM_PARALLEL"), &num)) {
-      num = std::max<unsigned long>(num, 1);
-      num = std::min<unsigned long>(num, ParallelMax);
-    }
-    WorkerPool_.SetThreadCount(static_cast<unsigned int>(num));
   }
-  BaseConst_.HeaderExtensions =
-    makefile->GetCMakeInstance()->GetHeaderExtensions();
 
-  // - Files and directories
-  ProjectDirsRef().Source = InfoGet("AM_CMAKE_SOURCE_DIR");
-  ProjectDirsRef().Binary = InfoGet("AM_CMAKE_BINARY_DIR");
-  ProjectDirsRef().CurrentSource = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
-  ProjectDirsRef().CurrentBinary = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
-  BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
-  if (BaseConst_.AutogenBuildDir.empty()) {
-    return LogInfoError("Autogen build directory missing.");
-  }
-  BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
-  if (BaseConst_.AutogenIncludeDir.empty()) {
-    return LogInfoError("Autogen include directory missing.");
-  }
-  BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE");
-  if (BaseConst_.CMakeExecutable.empty()) {
-    return LogInfoError("CMake executable file name missing.");
-  }
+  // -- Checks
   if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) {
     return LogInfoError(cmStrCat("The CMake executable ",
                                  MessagePath(BaseConst_.CMakeExecutable),
                                  " does not exist."));
   }
-  BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE");
-  if (BaseConst_.ParseCacheFile.empty()) {
-    return LogInfoError("Parse cache file name missing.");
-  }
 
-  // - Settings file
-  SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
-  if (SettingsFile_.empty()) {
-    return LogInfoError("Settings file name missing.");
-  }
+  // -- Evaluate values
+  BaseConst_.ThreadCount = std::min(BaseConst_.ThreadCount, ParallelMax);
+  WorkerPool_.SetThreadCount(BaseConst_.ThreadCount);
 
-  // - Qt environment
-  {
-    unsigned long qtv = BaseConst_.QtVersionMajor;
-    if (cmStrToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) {
-      BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv);
-    }
-  }
-
-  // - Moc
-  MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
-  if (!MocConst().Executable.empty()) {
+  // -- Moc
+  if (!MocConst_.Executable.empty()) {
+    // -- Moc is enabled
     MocConst_.Enabled = true;
-    // Load the executable file time
-    if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
-      return LogInfoError(cmStrCat("The moc executable ",
-                                   MessagePath(MocConst_.Executable),
-                                   " does not exist."));
-    }
-    for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
-      MocConst_.SkipList.insert(std::move(sfl));
-    }
-    MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
-    MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
-    MocConst_.OptionsExtra = InfoGetList("AM_MOC_OPTIONS");
 
-    MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
-    MocConst_.PathPrefix = InfoGetBool("AM_MOC_PATH_PREFIX");
+    // -- Temporary buffers
+    struct
+    {
+      std::vector<std::string> MacroNames;
+      std::vector<std::string> DependFilters;
+    } tmp;
+
+    // -- Required settings
+    if (!InfoBool("MOC_RELAXED_MODE", MocConst_.RelaxedMode, false) ||
+        !InfoBool("MOC_PATH_PREFIX", MocConst_.PathPrefix, true) ||
+        !InfoArray("MOC_SKIP", MocConst_.SkipList, false) ||
+        !InfoArrayConfig("MOC_DEFINITIONS", MocConst_.Definitions, false) ||
+        !InfoArrayConfig("MOC_INCLUDES", MocConst_.IncludePaths, false) ||
+        !InfoArray("MOC_OPTIONS", MocConst_.OptionsExtra, false) ||
+        !InfoStringConfig("MOC_COMPILATION_FILE", MocConst_.CompFileAbs,
+                          true) ||
+        !InfoArray("MOC_PREDEFS_CMD", MocConst_.PredefsCmd, false) ||
+        !InfoStringConfig("MOC_PREDEFS_FILE", MocConst_.PredefsFileAbs,
+                          !MocConst_.PredefsCmd.empty()) ||
+        !InfoArray("MOC_MACRO_NAMES", tmp.MacroNames, true) ||
+        !InfoArray("MOC_DEPEND_FILTERS", tmp.DependFilters, false)) {
+      return false;
+    }
 
-    for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+    // -- Evaluate settings
+    for (std::string const& item : tmp.MacroNames) {
       MocConst_.MacroFilters.emplace_back(
         item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
     }
+    // Dependency filters
     {
-      auto addFilter = [this, &LogInfoError](std::string const& key,
-                                             std::string const& exp) -> bool {
-        auto filterErr = [&LogInfoError, &key,
-                          &exp](cm::string_view err) -> bool {
-          return LogInfoError(cmStrCat("AUTOMOC_DEPEND_FILTERS: ", err, '\n',
-                                       "  Key: ", Quoted(key), '\n',
-                                       "  Exp: ", Quoted(exp), '\n'));
+      Json::Value const& val = Info()["MOC_DEPEND_FILTERS"];
+      if (!val.isArray()) {
+        return LogInfoError("MOC_DEPEND_FILTERS JSON value is not an array.");
+      }
+      Json::ArrayIndex const arraySize = val.size();
+      for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+        // Test entry closure
+        auto testEntry = [this, ii](bool test,
+                                    cm::string_view message) -> bool {
+          if (!test) {
+            this->LogInfoError(
+              cmStrCat("MOC_DEPEND_FILTERS filter ", ii, ": ", message));
+          }
+          return !test;
         };
-        if (key.empty()) {
-          return filterErr("Key is empty");
+
+        Json::Value const& pairVal = val[ii];
+
+        if (testEntry(pairVal.isArray(), "JSON value is not an array.") ||
+            testEntry(pairVal.size() == 2, "JSON array size invalid.")) {
+          return false;
         }
-        if (exp.empty()) {
-          return filterErr("Regular expression is empty");
+
+        Json::Value const& keyVal = pairVal[0u];
+        Json::Value const& expVal = pairVal[1u];
+        if (testEntry(keyVal.isString(),
+                      "JSON value for keyword is not a string.") ||
+            testEntry(expVal.isString(),
+                      "JSON value for regular expression is not a string.")) {
+          return false;
         }
-        this->MocConst_.DependFilters.emplace_back(key, exp);
-        if (!this->MocConst_.DependFilters.back().Exp.is_valid()) {
-          return filterErr("Regular expression compiling failed");
+
+        std::string const key = keyVal.asString();
+        std::string const exp = expVal.asString();
+        if (testEntry(!key.empty(), "Keyword is empty.") ||
+            testEntry(!exp.empty(), "Regular expression is empty.")) {
+          return false;
         }
-        return true;
-      };
 
-      // Insert default filter for Q_PLUGIN_METADATA
-      if (BaseConst().QtVersionMajor != 4) {
-        if (!addFilter("Q_PLUGIN_METADATA",
-                       "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
-                       "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) {
+        this->MocConst_.DependFilters.emplace_back(key, exp);
+        if (testEntry(
+              this->MocConst_.DependFilters.back().Exp.is_valid(),
+              cmStrCat("Regular expression compilation failed.\nKeyword: ",
+                       Quoted(key), "\nExpression: ", Quoted(exp)))) {
           return false;
         }
       }
-      // Insert user defined dependency filters
-      std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
-      if ((flts.size() % 2) != 0) {
-        return LogInfoError(
-          "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+    }
+    // Check if moc executable exists (by reading the file time)
+    if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
+      return LogInfoError(cmStrCat("The moc executable ",
+                                   MessagePath(MocConst_.Executable),
+                                   " does not exist."));
+    }
+  }
+
+  // -- Uic
+  if (!UicConst_.Executable.empty()) {
+    // Uic is enabled
+    UicConst_.Enabled = true;
+
+    // -- Required settings
+    if (!InfoArray("UIC_SKIP", UicConst_.SkipList, false) ||
+        !InfoArray("UIC_SEARCH_PATHS", UicConst_.SearchPaths, false) ||
+        !InfoArrayConfig("UIC_OPTIONS", UicConst_.Options, false)) {
+      return false;
+    }
+    // .ui files
+    {
+      Json::Value const& val = Info()["UIC_UI_FILES"];
+      if (!val.isArray()) {
+        return LogInfoError("UIC_UI_FILES JSON value is not an array.");
       }
-      for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) {
-        if (!addFilter(*itC, *(itC + 1))) {
+      Json::ArrayIndex const arraySize = val.size();
+      for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+        // Test entry closure
+        auto testEntry = [this, ii](bool test,
+                                    cm::string_view message) -> bool {
+          if (!test) {
+            this->LogInfoError(
+              cmStrCat("UIC_UI_FILES entry ", ii, ": ", message));
+          }
+          return !test;
+        };
+
+        Json::Value const& entry = val[ii];
+        if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+            testEntry(entry.size() == 2, "JSON array size invalid.")) {
+          return false;
+        }
+
+        Json::Value const& entryName = entry[0u];
+        Json::Value const& entryOptions = entry[1u];
+        if (testEntry(entryName.isString(),
+                      "JSON value for name is not a string.") ||
+            testEntry(entryOptions.isArray(),
+                      "JSON value for options is not an array.")) {
           return false;
         }
+
+        auto& uiFile = UicConst_.UiFiles[entryName.asString()];
+        JsonGetArray(uiFile.Options, entryOptions);
       }
     }
-    MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
-  }
 
-  // - Uic
-  UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
-  if (!UicConst().Executable.empty()) {
-    UicConst_.Enabled = true;
-    // Load the executable file time
+    // -- Evaluate settings
+    // Check if uic executable exists (by reading the file time)
     if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
       return LogInfoError(cmStrCat("The uic executable ",
                                    MessagePath(UicConst_.Executable),
                                    " does not exist."));
     }
-    for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
-      UicConst_.SkipList.insert(std::move(sfl));
-    }
-    UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
-    UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
-    {
-      cm::string_view const keyFiles = "AM_UIC_OPTIONS_FILES";
-      cm::string_view const keyOpts = "AM_UIC_OPTIONS_OPTIONS";
-      auto sources = InfoGetList(keyFiles);
-      auto options = InfoGetLists(keyOpts);
-      if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) {
-        return false;
-      }
-      auto fitEnd = sources.cend();
-      auto fit = sources.begin();
-      auto oit = options.begin();
-      while (fit != fitEnd) {
-        UicConst_.Options[*fit] = std::move(*oit);
-        ++fit;
-        ++oit;
-      }
-    }
   }
 
-  // Headers
+  // -- Headers
   {
-    // Get file lists
-    cm::string_view const keyFiles = "AM_HEADERS";
-    cm::string_view const keyFlags = "AM_HEADERS_FLAGS";
-    std::vector<std::string> files = InfoGetList(keyFiles);
-    std::vector<std::string> flags = InfoGetList(keyFlags);
-    std::vector<std::string> builds;
-    if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
-      return false;
-    }
-    if (MocConst().Enabled) {
-      cm::string_view const keyPaths = "AM_HEADERS_BUILD_PATHS";
-      builds = InfoGetList(keyPaths);
-      if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) {
+    Json::Value const& val = Info()["HEADERS"];
+    if (!val.isArray()) {
+      return LogInfoError("HEADERS JSON value is not an array.");
+    }
+    Json::ArrayIndex const arraySize = val.size();
+    for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+      // Test entry closure
+      auto testEntry = [this, ii](bool test, cm::string_view message) -> bool {
+        if (!test) {
+          this->LogInfoError(cmStrCat("HEADERS entry ", ii, ": ", message));
+        }
+        return !test;
+      };
+
+      Json::Value const& entry = val[ii];
+      if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+          testEntry(entry.size() == 3, "JSON array size invalid.")) {
+        return false;
+      }
+
+      Json::Value const& entryName = entry[0u];
+      Json::Value const& entryFlags = entry[1u];
+      Json::Value const& entryBuild = entry[2u];
+      if (testEntry(entryName.isString(),
+                    "JSON value for name is not a string.") ||
+          testEntry(entryFlags.isString(),
+                    "JSON value for flags is not a string.") ||
+          testEntry(entryBuild.isString(),
+                    "JSON value for build path is not a string.")) {
         return false;
       }
-    }
 
-    // Process file lists
-    for (std::size_t ii = 0; ii != files.size(); ++ii) {
-      std::string& fileName(files[ii]);
-      std::string const& fileFlags(flags[ii]);
-      if (fileFlags.size() != 2) {
-        LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(),
-                              "in ", keyFlags));
+      std::string name = entryName.asString();
+      std::string flags = entryFlags.asString();
+      std::string build = entryBuild.asString();
+      if (testEntry(flags.size() == 2, "Invalid flags string size")) {
         return false;
       }
+
       cmFileTime fileTime;
-      if (!fileTime.Load(fileName)) {
-        LogInfoError(cmStrCat("The header file ", this->MessagePath(fileName),
+      if (!fileTime.Load(name)) {
+        LogInfoError(cmStrCat("The header file ", this->MessagePath(name),
                               " does not exist."));
         return false;
       }
 
-      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(fileName);
+      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
       sourceHandle->FileTime = fileTime;
       sourceHandle->IsHeader = true;
-      sourceHandle->Moc = (fileFlags[0] == 'M');
-      sourceHandle->Uic = (fileFlags[1] == 'U');
-
+      sourceHandle->Moc = (flags[0] == 'M');
+      sourceHandle->Uic = (flags[1] == 'U');
       if (sourceHandle->Moc && MocConst().Enabled) {
-        sourceHandle->BuildPath = std::move(builds[ii]);
-        if (sourceHandle->BuildPath.empty()) {
-          return LogInfoError("Header file build path is empty");
+        if (build.empty()) {
+          return LogInfoError(
+            cmStrCat("Header file ", ii, " build path is empty"));
         }
+        sourceHandle->BuildPath = std::move(build);
       }
-      BaseEval().Headers.emplace(std::move(fileName), std::move(sourceHandle));
+      BaseEval().Headers.emplace(std::move(name), std::move(sourceHandle));
     }
   }
 
-  // Sources
+  // -- Sources
   {
-    cm::string_view const keyFiles = "AM_SOURCES";
-    cm::string_view const keyFlags = "AM_SOURCES_FLAGS";
-    std::vector<std::string> files = InfoGetList(keyFiles);
-    std::vector<std::string> flags = InfoGetList(keyFlags);
-    if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
-      return false;
-    }
+    Json::Value const& val = Info()["SOURCES"];
+    if (!val.isArray()) {
+      return LogInfoError("SOURCES JSON value is not an array.");
+    }
+    Json::ArrayIndex const arraySize = val.size();
+    for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
+      // Test entry closure
+      auto testEntry = [this, ii](bool test, cm::string_view message) -> bool {
+        if (!test) {
+          this->LogInfoError(cmStrCat("SOURCES entry ", ii, ": ", message));
+        }
+        return !test;
+      };
+
+      Json::Value const& entry = val[ii];
+      if (testEntry(entry.isArray(), "JSON value is not an array.") ||
+          testEntry(entry.size() == 2, "JSON array size invalid.")) {
+        return false;
+      }
+
+      Json::Value const& entryName = entry[0u];
+      Json::Value const& entryFlags = entry[1u];
+      if (testEntry(entryName.isString(),
+                    "JSON value for name is not a string.") ||
+          testEntry(entryFlags.isString(),
+                    "JSON value for flags is not a string.")) {
+        return false;
+      }
 
-    // Process file lists
-    for (std::size_t ii = 0; ii != files.size(); ++ii) {
-      std::string& fileName(files[ii]);
-      std::string const& fileFlags(flags[ii]);
-      if (fileFlags.size() != 2) {
-        LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(),
-                              "in ", keyFlags));
+      std::string name = entryName.asString();
+      std::string flags = entryFlags.asString();
+      if (testEntry(flags.size() == 2, "Invalid flags string size")) {
         return false;
       }
+
       cmFileTime fileTime;
-      if (!fileTime.Load(fileName)) {
-        LogInfoError(cmStrCat("The source file ", this->MessagePath(fileName),
+      if (!fileTime.Load(name)) {
+        LogInfoError(cmStrCat("The source file ", this->MessagePath(name),
                               " does not exist."));
         return false;
       }
 
-      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(fileName);
+      SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
       sourceHandle->FileTime = fileTime;
       sourceHandle->IsHeader = false;
-      sourceHandle->Moc = (fileFlags[0] == 'M');
-      sourceHandle->Uic = (fileFlags[1] == 'U');
-      BaseEval().Sources.emplace(std::move(fileName), std::move(sourceHandle));
+      sourceHandle->Moc = (flags[0] == 'M');
+      sourceHandle->Uic = (flags[1] == 'U');
+      BaseEval().Sources.emplace(std::move(name), std::move(sourceHandle));
     }
   }
 
-  // Init derived information
-  // ------------------------
-
+  // -- Init derived information
   // Moc variables
   if (MocConst().Enabled) {
-    // Mocs compilation file
-    MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp");
-
-    // Moc predefs file
-    if (!MocConst_.PredefsCmd.empty()) {
-      std::string pathRel;
-      if (BaseConst_.MultiConfig) {
-        pathRel = cmStrCat("moc_predefs_", InfoConfig(), ".h");
-      } else {
-        pathRel = "moc_predefs.h";
-      }
-      MocConst_.PredefsFileAbs = AbsoluteBuildPath(pathRel);
-    }
-
     // Compose moc includes list
     {
       // Compute framework paths
@@ -2031,11 +2006,11 @@ void cmQtAutoMocUic::SettingsFileRead()
     if (UicConst().Enabled) {
       cryptoHash.Initialize();
       cha(UicConst().Executable);
-      std::for_each(UicConst().TargetOptions.begin(),
-                    UicConst().TargetOptions.end(), cha);
-      for (const auto& item : UicConst().Options) {
+      std::for_each(UicConst().Options.begin(), UicConst().Options.end(), cha);
+      for (const auto& item : UicConst().UiFiles) {
         cha(item.first);
-        std::for_each(item.second.begin(), item.second.end(), cha);
+        auto const& opts = item.second.Options;
+        std::for_each(opts.begin(), opts.end(), cha);
       }
       SettingsStringUic_ = cryptoHash.FinalizeHex();
     }
@@ -2080,7 +2055,7 @@ bool cmQtAutoMocUic::SettingsFileWrite()
     if (Log().Verbose()) {
       Log().Info(
         GenT::GEN,
-        cmStrCat("Writing settings file ", MessagePath(SettingsFile_)));
+        cmStrCat("Writing the settings file ", MessagePath(SettingsFile_)));
     }
     // Compose settings file content
     std::string content;

+ 20 - 25
Source/cmQtAutoMocUic.h

@@ -25,8 +25,6 @@
 #include <utility>
 #include <vector>
 
-class cmMakefile;
-
 /** \class cmQtAutoMocUic
  * \brief AUTOMOC and AUTOUIC generator
  */
@@ -42,17 +40,21 @@ public:
 public:
   // -- Types
 
+  /** Include string with sub parts.  */
+  struct IncludeKeyT
+  {
+    IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
+
+    std::string Key;  // Full include string
+    std::string Dir;  // Include directory
+    std::string Base; // Base part of the include file name
+  };
+
   /** Search key plus regular expression pair.  */
   struct KeyExpT
   {
     KeyExpT() = default;
 
-    KeyExpT(const char* key, const char* exp)
-      : Key(key)
-      , Exp(exp)
-    {
-    }
-
     KeyExpT(std::string key, std::string const& exp)
       : Key(std::move(key))
       , Exp(exp)
@@ -63,16 +65,6 @@ public:
     cmsys::RegularExpression Exp;
   };
 
-  /** Include string with sub parts.  */
-  struct IncludeKeyT
-  {
-    IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
-
-    std::string Key;  // Full include string
-    std::string Dir;  // Include directory
-    std::string Base; // Base part of the include file name
-  };
-
   /** Source file parsing cache.  */
   class ParseCacheT
   {
@@ -169,6 +161,7 @@ public:
     // - Config
     bool MultiConfig = false;
     unsigned int QtVersionMajor = 4;
+    unsigned int ThreadCount = 0;
     // - Directories
     std::string AutogenBuildDir;
     std::string AutogenIncludeDir;
@@ -251,6 +244,12 @@ public:
   /** Uic settings.  */
   class UicSettingsT
   {
+  public:
+    struct UiFile
+    {
+      std::vector<std::string> Options;
+    };
+
   public:
     UicSettingsT();
     ~UicSettingsT();
@@ -267,8 +266,8 @@ public:
     cmFileTime ExecutableTime;
     std::string Executable;
     std::unordered_set<std::string> SkipList;
-    std::vector<std::string> TargetOptions;
-    std::map<std::string, std::vector<std::string>> Options;
+    std::vector<std::string> Options;
+    std::unordered_map<std::string, UiFile> UiFiles;
     std::vector<std::string> SearchPaths;
     cmsys::RegularExpression RegExpInclude;
   };
@@ -523,10 +522,8 @@ public:
   std::string CollapseFullPathTS(std::string const& path) const;
 
 private:
-  // -- Utility accessors
-  Logger const& Log() const { return Logger_; }
   // -- Abstract processing interface
-  bool Init(cmMakefile* makefile) override;
+  bool InitFromInfo() override;
   void InitJobs();
   bool Process() override;
   // -- Settings file
@@ -541,8 +538,6 @@ private:
   bool CreateDirectories();
 
 private:
-  // -- Utility
-  Logger Logger_;
   // -- Settings
   BaseSettingsT BaseConst_;
   BaseEvalT BaseEval_;

+ 26 - 90
Source/cmQtAutoRcc.cxx

@@ -6,7 +6,6 @@
 #include "cmCryptoHash.h"
 #include "cmDuration.h"
 #include "cmFileLockResult.h"
-#include "cmMakefile.h"
 #include "cmProcessOutput.h"
 #include "cmQtAutoGen.h"
 #include "cmStringAlgorithms.h"
@@ -16,112 +15,49 @@
 
 #include <algorithm>
 
-cmQtAutoRcc::cmQtAutoRcc() = default;
+cmQtAutoRcc::cmQtAutoRcc()
+  : cmQtAutoGenerator(GenT::RCC)
+{
+}
 cmQtAutoRcc::~cmQtAutoRcc() = default;
 
-bool cmQtAutoRcc::Init(cmMakefile* makefile)
+bool cmQtAutoRcc::InitFromInfo()
 {
-  // -- Utility lambdas
-  auto InfoGet = [makefile](cm::string_view key) {
-    return makefile->GetSafeDefinition(std::string(key));
-  };
-  auto InfoGetList =
-    [makefile](cm::string_view key) -> std::vector<std::string> {
-    return cmExpandedList(makefile->GetSafeDefinition(std::string(key)));
-  };
-  auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string {
-    if (const char* valueConf =
-          makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) {
-      return std::string(valueConf);
-    }
-    return makefile->GetSafeDefinition(std::string(key));
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](cm::string_view key) -> std::vector<std::string> {
-    return cmExpandedList(InfoGetConfig(key));
-  };
-  auto LogInfoError = [this](cm::string_view msg) -> bool {
-    this->Log().Error(
-      GenT::RCC, cmStrCat("In ", MessagePath(this->InfoFile()), ":\n", msg));
+  // -- Required settings
+  if (!InfoBool("MULTI_CONFIG", MultiConfig_, true) ||
+      !InfoString("BUILD_DIR", AutogenBuildDir_, true) ||
+      !InfoStringConfig("INCLUDE_DIR", IncludeDir_, true) ||
+      !InfoString("RCC_EXECUTABLE", RccExecutable_, true) ||
+      !InfoArray("RCC_LIST_OPTIONS", RccListOptions_, false) ||
+      !InfoString("LOCK_FILE", LockFile_, true) ||
+      !InfoStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
+      !InfoString("SOURCE", QrcFile_, true) ||
+      !InfoString("OUTPUT_CHECKSUM", RccPathChecksum_, true) ||
+      !InfoString("OUTPUT_NAME", RccFileName_, true) ||
+      !InfoArray("OPTIONS", Options_, false) ||
+      !InfoArray("INPUTS", Inputs_, false)) {
     return false;
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    return LogInfoError("File processing failed.");
   }
 
-  // - Configurations
-  Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
-  MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
-
-  // - Directories
-  ProjectDirsRef().Source = InfoGet("ARCC_CMAKE_SOURCE_DIR");
-  ProjectDirsRef().Binary = InfoGet("ARCC_CMAKE_BINARY_DIR");
-  AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
-  if (AutogenBuildDir_.empty()) {
-    return LogInfoError("Build directory empty.");
-  }
-
-  IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
-  if (IncludeDir_.empty()) {
-    return LogInfoError("Include directory empty.");
-  }
-
-  // - Rcc executable
-  RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
-  if (!RccExecutableTime_.Load(RccExecutable_)) {
-    return LogInfoError(cmStrCat(
-      "The rcc executable ", MessagePath(RccExecutable_), " does not exist."));
-  }
-  RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
-
-  // - Job
-  LockFile_ = InfoGet("ARCC_LOCK_FILE");
-  QrcFile_ = InfoGet("ARCC_SOURCE");
+  // -- Derive information
   QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_);
   QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_);
-  RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM");
-  RccFileName_ = InfoGet("ARCC_OUTPUT_NAME");
-  Options_ = InfoGetConfigList("ARCC_OPTIONS");
-  Inputs_ = InfoGetList("ARCC_INPUTS");
-
-  // - Settings file
-  SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE");
-
-  // - Validity checks
-  if (LockFile_.empty()) {
-    return LogInfoError("Lock file name missing.");
-  }
-  if (SettingsFile_.empty()) {
-    return LogInfoError("Settings file name missing.");
-  }
-  if (AutogenBuildDir_.empty()) {
-    return LogInfoError("Autogen build directory missing.");
-  }
-  if (RccExecutable_.empty()) {
-    return LogInfoError("rcc executable missing.");
-  }
-  if (QrcFile_.empty()) {
-    return LogInfoError("rcc input file missing.");
-  }
-  if (RccFileName_.empty()) {
-    return LogInfoError("rcc output file missing.");
-  }
-
-  // Init derived information
-  // ------------------------
-
   RccFilePublic_ =
     cmStrCat(AutogenBuildDir_, '/', RccPathChecksum_, '/', RccFileName_);
 
-  // Compute rcc output file name
+  // rcc output file name
   if (IsMultiConfig()) {
     RccFileOutput_ = cmStrCat(IncludeDir_, '/', MultiConfigOutput());
   } else {
     RccFileOutput_ = RccFilePublic_;
   }
 
+  // -- Checks
+  if (!RccExecutableTime_.Load(RccExecutable_)) {
+    return LogInfoError(cmStrCat(
+      "The rcc executable ", MessagePath(RccExecutable_), " does not exist."));
+  }
+
   return true;
 }
 

+ 1 - 6
Source/cmQtAutoRcc.h

@@ -12,8 +12,6 @@
 #include <string>
 #include <vector>
 
-class cmMakefile;
-
 /** \class cmQtAutoRcc
  * \brief AUTORCC generator
  */
@@ -28,12 +26,11 @@ public:
 
 private:
   // -- Utility
-  Logger const& Log() const { return Logger_; }
   bool IsMultiConfig() const { return MultiConfig_; }
   std::string MultiConfigOutput() const;
 
   // -- Abstract processing interface
-  bool Init(cmMakefile* makefile) override;
+  bool InitFromInfo() override;
   bool Process() override;
   // -- Settings file
   bool SettingsFileRead();
@@ -47,8 +44,6 @@ private:
   bool GenerateWrapper();
 
 private:
-  // -- Logging
-  Logger Logger_;
   // -- Config settings
   bool MultiConfig_ = false;
   // -- Directories