Browse Source

Merge topic 'autogen_autorcc_no_libuv'

7b98a6eb68 Autogen: Rename cmQtAutoGeneratorRcc to cmQtAutoRcc
9710d4aacf Autogen: Move libuv loop from cmQtAutoGenerator to cmQtAutoGeneratorMocUic
95e72c0157 Autogen: Make cmQtAutoGenerator::FileSystem Logger free
f161cfe5a8 Autogen: Move Logger and FileSystem member variables to generator classes
7baec5e94b AutoRcc: Don't use cmQtAutoGenerator::FileSystem methods
191269d247 AutoRcc: Remove libuv event loop from cmQtAutoGeneratorRcc
95de172b68 AutoRcc: Make rcc parsing function private
521475b41b AutoRcc: Use cmQtAutoGen::RccLister in initializer and generator
...

Acked-by: Kitware Robot <[email protected]>
Merge-request: !3202
Brad King 6 năm trước cách đây
mục cha
commit
7e60e77c3c

+ 2 - 2
Source/CMakeLists.txt

@@ -350,8 +350,8 @@ set(SRCS
   cmQtAutoGenInitializer.h
   cmQtAutoGenInitializer.h
   cmQtAutoGeneratorMocUic.cxx
   cmQtAutoGeneratorMocUic.cxx
   cmQtAutoGeneratorMocUic.h
   cmQtAutoGeneratorMocUic.h
-  cmQtAutoGeneratorRcc.cxx
-  cmQtAutoGeneratorRcc.h
+  cmQtAutoRcc.cxx
+  cmQtAutoRcc.h
   cmRST.cxx
   cmRST.cxx
   cmRST.h
   cmRST.h
   cmScriptGenerator.h
   cmScriptGenerator.h

+ 107 - 11
Source/cmQtAutoGen.cxx

@@ -1,9 +1,12 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGen.h"
+
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
+#include "cmDuration.h"
+#include "cmProcessOutput.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-
+#include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 
 #include <algorithm>
 #include <algorithm>
@@ -237,8 +240,8 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
   MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
   MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
 }
 }
 
 
-void cmQtAutoGen::RccListParseContent(std::string const& content,
-                                      std::vector<std::string>& files)
+static void RccListParseContent(std::string const& content,
+                                std::vector<std::string>& files)
 {
 {
   cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
   cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
   cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
   cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
@@ -255,10 +258,10 @@ void cmQtAutoGen::RccListParseContent(std::string const& content,
   }
   }
 }
 }
 
 
-bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut,
-                                     std::string const& rccStdErr,
-                                     std::vector<std::string>& files,
-                                     std::string& error)
+static bool RccListParseOutput(std::string const& rccStdOut,
+                               std::string const& rccStdErr,
+                               std::vector<std::string>& files,
+                               std::string& error)
 {
 {
   // Lambda to strip CR characters
   // Lambda to strip CR characters
   auto StripCR = [](std::string& line) {
   auto StripCR = [](std::string& line) {
@@ -305,11 +308,104 @@ bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut,
   return true;
   return true;
 }
 }
 
 
-void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir,
-                                         std::vector<std::string>& files)
+cmQtAutoGen::RccLister::RccLister() = default;
+
+cmQtAutoGen::RccLister::RccLister(std::string rccExecutable,
+                                  std::vector<std::string> listOptions)
+  : RccExcutable_(std::move(rccExecutable))
+  , ListOptions_(std::move(listOptions))
+{
+}
+
+bool cmQtAutoGen::RccLister::list(std::string const& qrcFile,
+                                  std::vector<std::string>& files,
+                                  std::string& error, bool verbose) const
 {
 {
+  error.clear();
+
+  if (!cmSystemTools::FileExists(qrcFile, true)) {
+    error = "The resource file ";
+    error += Quoted(qrcFile);
+    error += " does not exist.";
+    return false;
+  }
+
+  // Run rcc list command in the directory of the qrc file with the pathless
+  // qrc file name argument.  This way rcc prints relative paths.
+  // This avoids issues on Windows when the qrc file is in a path that
+  // contains non-ASCII characters.
+  std::string const fileDir = cmSystemTools::GetFilenamePath(qrcFile);
+
+  if (!this->RccExcutable_.empty() &&
+      cmSystemTools::FileExists(this->RccExcutable_, true) &&
+      !this->ListOptions_.empty()) {
+
+    bool result = false;
+    int retVal = 0;
+    std::string rccStdOut;
+    std::string rccStdErr;
+    {
+      std::vector<std::string> cmd;
+      cmd.emplace_back(this->RccExcutable_);
+      cmd.insert(cmd.end(), this->ListOptions_.begin(),
+                 this->ListOptions_.end());
+      cmd.emplace_back(cmSystemTools::GetFilenameName(qrcFile));
+
+      // Log command
+      if (verbose) {
+        std::string msg = "Running command:\n";
+        msg += QuotedCommand(cmd);
+        msg += '\n';
+        cmSystemTools::Stdout(msg);
+      }
+
+      result = cmSystemTools::RunSingleCommand(
+        cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
+        cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
+    }
+    if (!result || retVal) {
+      error = "The rcc list process failed for ";
+      error += Quoted(qrcFile);
+      error += "\n";
+      if (!rccStdOut.empty()) {
+        error += rccStdOut;
+        error += "\n";
+      }
+      if (!rccStdErr.empty()) {
+        error += rccStdErr;
+        error += "\n";
+      }
+      return false;
+    }
+    if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
+      return false;
+    }
+  } else {
+    // We can't use rcc for the file listing.
+    // Read the qrc file content into string and parse it.
+    {
+      std::string qrcContents;
+      {
+        cmsys::ifstream ifs(qrcFile.c_str());
+        if (ifs) {
+          std::ostringstream osst;
+          osst << ifs.rdbuf();
+          qrcContents = osst.str();
+        } else {
+          error = "The resource file ";
+          error += Quoted(qrcFile);
+          error += " is not readable\n";
+          return false;
+        }
+      }
+      // Parse string content
+      RccListParseContent(qrcContents, files);
+    }
+  }
+
+  // Convert relative paths to absolute paths
   for (std::string& entry : files) {
   for (std::string& entry : files) {
-    std::string tmp = cmSystemTools::CollapseFullPath(entry, qrcFileDir);
-    entry = std::move(tmp);
+    entry = cmSystemTools::CollapseFullPath(entry, fileDir);
   }
   }
+  return true;
 }
 }

+ 38 - 15
Source/cmQtAutoGen.h

@@ -85,21 +85,44 @@ public:
                               std::vector<std::string> const& newOpts,
                               std::vector<std::string> const& newOpts,
                               bool isQt5);
                               bool isQt5);
 
 
-  /// @brief Parses the content of a qrc file
-  ///
-  /// Use when rcc does not support the "--list" option
-  static void RccListParseContent(std::string const& content,
-                                  std::vector<std::string>& files);
-
-  /// @brief Parses the output of the "rcc --list ..." command
-  static bool RccListParseOutput(std::string const& rccStdOut,
-                                 std::string const& rccStdErr,
-                                 std::vector<std::string>& files,
-                                 std::string& error);
-
-  /// @brief Converts relative qrc entry paths to full paths
-  static void RccListConvertFullPath(std::string const& qrcFileDir,
-                                     std::vector<std::string>& files);
+  /** @class RccLister
+   * @brief Lists files in qrc resource files
+   */
+  class RccLister
+  {
+  public:
+    RccLister();
+    RccLister(std::string rccExecutable, std::vector<std::string> listOptions);
+
+    //! The rcc executable
+    std::string const& RccExcutable() const { return RccExcutable_; }
+    void SetRccExecutable(std::string const& rccExecutable)
+    {
+      RccExcutable_ = rccExecutable;
+    }
+
+    //! The rcc executable list options
+    std::vector<std::string> const& ListOptions() const
+    {
+      return ListOptions_;
+    }
+    void SetListOptions(std::vector<std::string> const& listOptions)
+    {
+      ListOptions_ = listOptions;
+    }
+
+    /**
+     * @brief Lists a files in the qrcFile
+     * @arg files The file names are appended to this list
+     * @arg error contains the error message when the function fails
+     */
+    bool list(std::string const& qrcFile, std::vector<std::string>& files,
+              std::string& error, bool verbose = false) const;
+
+  private:
+    std::string RccExcutable_;
+    std::vector<std::string> ListOptions_;
+  };
 };
 };
 
 
 #endif
 #endif

+ 2 - 88
Source/cmQtAutoGenInitializer.cxx

@@ -7,7 +7,6 @@
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
 #include "cmCustomCommandLines.h"
-#include "cmDuration.h"
 #include "cmFilePathChecksum.h"
 #include "cmFilePathChecksum.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
@@ -19,7 +18,6 @@
 #include "cmMessageType.h"
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmPolicies.h"
-#include "cmProcessOutput.h"
 #include "cmSourceFile.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmSourceGroup.h"
 #include "cmSourceGroup.h"
@@ -28,7 +26,6 @@
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTarget.h"
 #include "cmake.h"
 #include "cmake.h"
-#include "cmsys/FStream.hxx"
 #include "cmsys/SystemInformation.hxx"
 #include "cmsys/SystemInformation.hxx"
 
 
 #include <algorithm>
 #include <algorithm>
@@ -36,7 +33,6 @@
 #include <deque>
 #include <deque>
 #include <map>
 #include <map>
 #include <set>
 #include <set>
-#include <sstream>
 #include <string>
 #include <string>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
@@ -935,7 +931,8 @@ bool cmQtAutoGenInitializer::InitScanFiles()
     for (Qrc& qrc : this->Rcc.Qrcs) {
     for (Qrc& qrc : this->Rcc.Qrcs) {
       if (!qrc.Generated) {
       if (!qrc.Generated) {
         std::string error;
         std::string error;
-        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
+        RccLister const lister(this->Rcc.Executable, this->Rcc.ListOptions);
+        if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
           cmSystemTools::Error(error);
           cmSystemTools::Error(error);
           return false;
           return false;
         }
         }
@@ -1630,86 +1627,3 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
 
 
   return true;
   return true;
 }
 }
-
-/// @brief Reads the resource files list from from a .qrc file
-/// @arg fileName Must be the absolute path of the .qrc file
-/// @return True if the rcc file was successfully read
-bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName,
-                                           std::vector<std::string>& files,
-                                           std::string& error)
-{
-  if (!cmSystemTools::FileExists(fileName)) {
-    error = "rcc resource file does not exist:\n  ";
-    error += Quoted(fileName);
-    error += "\n";
-    return false;
-  }
-  if (this->Rcc.ExecutableExists && !this->Rcc.ListOptions.empty()) {
-    // Use rcc for file listing
-    if (this->Rcc.Executable.empty()) {
-      error = "rcc executable not available";
-      return false;
-    }
-
-    // Run rcc list command in the directory of the qrc file with the
-    // pathless
-    // qrc file name argument. This way rcc prints relative paths.
-    // This avoids issues on Windows when the qrc file is in a path that
-    // contains non-ASCII characters.
-    std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
-    std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
-
-    bool result = false;
-    int retVal = 0;
-    std::string rccStdOut;
-    std::string rccStdErr;
-    {
-      std::vector<std::string> cmd;
-      cmd.push_back(this->Rcc.Executable);
-      cmd.insert(cmd.end(), this->Rcc.ListOptions.begin(),
-                 this->Rcc.ListOptions.end());
-      cmd.push_back(fileNameName);
-      result = cmSystemTools::RunSingleCommand(
-        cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
-        cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
-    }
-    if (!result || retVal) {
-      error = "rcc list process failed for:\n  ";
-      error += Quoted(fileName);
-      error += "\n";
-      error += rccStdOut;
-      error += "\n";
-      error += rccStdErr;
-      error += "\n";
-      return false;
-    }
-    if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
-      return false;
-    }
-  } else {
-    // We can't use rcc for the file listing.
-    // Read the qrc file content into string and parse it.
-    {
-      std::string qrcContents;
-      {
-        cmsys::ifstream ifs(fileName.c_str());
-        if (ifs) {
-          std::ostringstream osst;
-          osst << ifs.rdbuf();
-          qrcContents = osst.str();
-        } else {
-          error = "rcc file not readable:\n  ";
-          error += Quoted(fileName);
-          error += "\n";
-          return false;
-        }
-      }
-      // Parse string content
-      RccListParseContent(qrcContents, files);
-    }
-  }
-
-  // Convert relative paths to absolute paths
-  RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files);
-  return true;
-}

+ 0 - 4
Source/cmQtAutoGenInitializer.h

@@ -149,10 +149,6 @@ private:
   bool GetQtExecutable(GenVarsT& genVars, const std::string& executable,
   bool GetQtExecutable(GenVarsT& genVars, const std::string& executable,
                        bool ignoreMissingTarget, std::string* output) const;
                        bool ignoreMissingTarget, std::string* output) const;
 
 
-  bool RccListInputs(std::string const& fileName,
-                     std::vector<std::string>& files,
-                     std::string& errorMessage);
-
 private:
 private:
   cmQtAutoGenGlobalInitializer* GlobalInitializer;
   cmQtAutoGenGlobalInitializer* GlobalInitializer;
   cmGeneratorTarget* Target;
   cmGeneratorTarget* Target;

+ 121 - 144
Source/cmQtAutoGenerator.cxx

@@ -20,6 +20,34 @@
 
 
 // -- Class methods
 // -- Class methods
 
 
+cmQtAutoGenerator::Logger::Logger()
+{
+  // Initialize logger
+  {
+    std::string verbose;
+    if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) {
+      unsigned long iVerbose = 0;
+      if (cmSystemTools::StringToULong(verbose.c_str(), &iVerbose)) {
+        SetVerbosity(static_cast<unsigned int>(iVerbose));
+      } else {
+        // Non numeric verbosity
+        SetVerbose(cmSystemTools::IsOn(verbose));
+      }
+    }
+  }
+  {
+    std::string colorEnv;
+    cmSystemTools::GetEnv("COLOR", colorEnv);
+    if (!colorEnv.empty()) {
+      SetColorOutput(cmSystemTools::IsOn(colorEnv));
+    } else {
+      SetColorOutput(true);
+    }
+  }
+}
+
+cmQtAutoGenerator::Logger::~Logger() = default;
+
 void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value)
 void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value)
 {
 {
   unsigned long verbosity = 0;
   unsigned long verbosity = 0;
@@ -152,6 +180,91 @@ void cmQtAutoGenerator::Logger::ErrorCommand(
   }
   }
 }
 }
 
 
+bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename)
+{
+  bool success = true;
+  std::string const dirName = cmSystemTools::GetFilenamePath(filename);
+  if (!dirName.empty()) {
+    success = cmSystemTools::MakeDirectory(dirName);
+  }
+  return success;
+}
+
+bool cmQtAutoGenerator::FileRead(std::string& content,
+                                 std::string const& filename,
+                                 std::string* error)
+{
+  content.clear();
+  if (!cmSystemTools::FileExists(filename, true)) {
+    if (error != nullptr) {
+      error->append("Not a file.");
+    }
+    return false;
+  }
+
+  unsigned long const length = cmSystemTools::FileLength(filename);
+  cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
+
+  // Use lambda to save destructor calls of ifs
+  return [&ifs, length, &content, error]() -> bool {
+    if (!ifs) {
+      if (error != nullptr) {
+        error->append("Opening the file for reading failed.");
+      }
+      return false;
+    }
+    content.reserve(length);
+    typedef std::istreambuf_iterator<char> IsIt;
+    content.assign(IsIt{ ifs }, IsIt{});
+    if (!ifs) {
+      content.clear();
+      if (error != nullptr) {
+        error->append("Reading from the file failed.");
+      }
+      return false;
+    }
+    return true;
+  }();
+}
+
+bool cmQtAutoGenerator::FileWrite(std::string const& filename,
+                                  std::string const& content,
+                                  std::string* error)
+{
+  // Make sure the parent directory exists
+  if (!cmQtAutoGenerator::MakeParentDirectory(filename)) {
+    if (error != nullptr) {
+      error->assign("Could not create parent directory.");
+    }
+    return false;
+  }
+  cmsys::ofstream ofs;
+  ofs.open(filename.c_str(),
+           (std::ios::out | std::ios::binary | std::ios::trunc));
+
+  // Use lambda to save destructor calls of ofs
+  return [&ofs, &content, error]() -> bool {
+    if (!ofs) {
+      if (error != nullptr) {
+        error->assign("Opening file for writing failed.");
+      }
+      return false;
+    }
+    ofs << content;
+    if (!ofs.good()) {
+      if (error != nullptr) {
+        error->assign("File writing failed.");
+      }
+      return false;
+    }
+    return true;
+  }();
+}
+
+cmQtAutoGenerator::FileSystem::FileSystem() = default;
+
+cmQtAutoGenerator::FileSystem::~FileSystem() = default;
+
 std::string cmQtAutoGenerator::FileSystem::GetRealPath(
 std::string cmQtAutoGenerator::FileSystem::GetRealPath(
   std::string const& filename)
   std::string const& filename)
 {
 {
@@ -267,91 +380,16 @@ bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content,
                                              std::string const& filename,
                                              std::string const& filename,
                                              std::string* error)
                                              std::string* error)
 {
 {
-  bool success = false;
-  if (FileExists(filename, true)) {
-    unsigned long const length = FileLength(filename);
-    {
-      std::lock_guard<std::mutex> lock(Mutex_);
-      cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
-      if (ifs) {
-        content.reserve(length);
-        content.assign(std::istreambuf_iterator<char>{ ifs },
-                       std::istreambuf_iterator<char>{});
-        if (ifs) {
-          success = true;
-        } else {
-          content.clear();
-          if (error != nullptr) {
-            error->append("Reading from the file failed.");
-          }
-        }
-      } else if (error != nullptr) {
-        error->append("Opening the file for reading failed.");
-      }
-    }
-  } else if (error != nullptr) {
-    error->append(
-      "The file does not exist, is not readable or is a directory.");
-  }
-  return success;
-}
-
-bool cmQtAutoGenerator::FileSystem::FileRead(GenT genType,
-                                             std::string& content,
-                                             std::string const& filename)
-{
-  std::string error;
-  if (!FileRead(content, filename, &error)) {
-    Log()->ErrorFile(genType, filename, error);
-    return false;
-  }
-  return true;
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmQtAutoGenerator::FileRead(content, filename, error);
 }
 }
 
 
 bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
 bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
                                               std::string const& content,
                                               std::string const& content,
                                               std::string* error)
                                               std::string* error)
 {
 {
-  bool success = false;
-  // Make sure the parent directory exists
-  if (MakeParentDirectory(filename)) {
-    std::lock_guard<std::mutex> lock(Mutex_);
-    cmsys::ofstream outfile;
-    outfile.open(filename.c_str(),
-                 (std::ios::out | std::ios::binary | std::ios::trunc));
-    if (outfile) {
-      outfile << content;
-      // Check for write errors
-      if (outfile.good()) {
-        success = true;
-      } else {
-        if (error != nullptr) {
-          error->assign("File writing failed");
-        }
-      }
-    } else {
-      if (error != nullptr) {
-        error->assign("Opening file for writing failed");
-      }
-    }
-  } else {
-    if (error != nullptr) {
-      error->assign("Could not create parent directory");
-    }
-  }
-  return success;
-}
-
-bool cmQtAutoGenerator::FileSystem::FileWrite(GenT genType,
-                                              std::string const& filename,
-                                              std::string const& content)
-{
-  std::string error;
-  if (!FileWrite(filename, content, &error)) {
-    Log()->ErrorFile(genType, filename, error);
-    return false;
-  }
-  return true;
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmQtAutoGenerator::FileWrite(filename, content, error);
 }
 }
 
 
 bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
 bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
@@ -386,35 +424,11 @@ bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname)
   return cmSystemTools::MakeDirectory(dirname);
   return cmSystemTools::MakeDirectory(dirname);
 }
 }
 
 
-bool cmQtAutoGenerator::FileSystem::MakeDirectory(GenT genType,
-                                                  std::string const& dirname)
-{
-  if (!MakeDirectory(dirname)) {
-    Log()->ErrorFile(genType, dirname, "Could not create directory");
-    return false;
-  }
-  return true;
-}
-
 bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
 bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
   std::string const& filename)
   std::string const& filename)
 {
 {
-  bool success = true;
-  std::string const dirName = cmSystemTools::GetFilenamePath(filename);
-  if (!dirName.empty()) {
-    success = MakeDirectory(dirName);
-  }
-  return success;
-}
-
-bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
-  GenT genType, std::string const& filename)
-{
-  if (!MakeParentDirectory(filename)) {
-    Log()->ErrorFile(genType, filename, "Could not create parent directory");
-    return false;
-  }
-  return true;
+  std::lock_guard<std::mutex> lock(Mutex_);
+  return cmQtAutoGenerator::MakeParentDirectory(filename);
 }
 }
 
 
 int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
 int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
@@ -643,46 +657,9 @@ void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
   }
   }
 }
 }
 
 
-cmQtAutoGenerator::cmQtAutoGenerator()
-  : FileSys_(&Logger_)
-{
-  // Initialize logger
-  {
-    std::string verbose;
-    if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) {
-      unsigned long iVerbose = 0;
-      if (cmSystemTools::StringToULong(verbose.c_str(), &iVerbose)) {
-        Logger_.SetVerbosity(static_cast<unsigned int>(iVerbose));
-      } else {
-        // Non numeric verbosity
-        Logger_.SetVerbose(cmSystemTools::IsOn(verbose));
-      }
-    }
-  }
-  {
-    std::string colorEnv;
-    cmSystemTools::GetEnv("COLOR", colorEnv);
-    if (!colorEnv.empty()) {
-      Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv));
-    } else {
-      Logger_.SetColorOutput(true);
-    }
-  }
+cmQtAutoGenerator::cmQtAutoGenerator() = default;
 
 
-  // Initialize libuv loop
-  uv_disable_stdio_inheritance();
-#ifdef CMAKE_UV_SIGNAL_HACK
-  UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
-#endif
-  UVLoop_ = cm::make_unique<uv_loop_t>();
-  uv_loop_init(UVLoop());
-}
-
-cmQtAutoGenerator::~cmQtAutoGenerator()
-{
-  // Close libuv loop
-  uv_loop_close(UVLoop());
-}
+cmQtAutoGenerator::~cmQtAutoGenerator() = default;
 
 
 bool cmQtAutoGenerator::Run(std::string const& infoFile,
 bool cmQtAutoGenerator::Run(std::string const& infoFile,
                             std::string const& config)
                             std::string const& config)

+ 14 - 37
Source/cmQtAutoGenerator.h

@@ -8,7 +8,6 @@
 #include "cmFilePathChecksum.h"
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGen.h"
 #include "cmUVHandlePtr.h"
 #include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
 #include "cm_uv.h"
 #include "cm_uv.h"
 
 
 #include <array>
 #include <array>
@@ -31,12 +30,16 @@ public:
   class Logger
   class Logger
   {
   {
   public:
   public:
+    // -- Construction
+    Logger();
+    ~Logger();
     // -- Verbosity
     // -- Verbosity
     unsigned int Verbosity() const { return this->Verbosity_; }
     unsigned int Verbosity() const { return this->Verbosity_; }
     void SetVerbosity(unsigned int value) { this->Verbosity_ = value; }
     void SetVerbosity(unsigned int value) { this->Verbosity_ = value; }
     void RaiseVerbosity(std::string const& value);
     void RaiseVerbosity(std::string const& value);
     bool Verbose() const { return (this->Verbosity_ != 0); }
     bool Verbose() const { return (this->Verbosity_ != 0); }
     void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; }
     void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; }
+    // -- Color output
     bool ColorOutput() const { return this->ColorOutput_; }
     bool ColorOutput() const { return this->ColorOutput_; }
     void SetColorOutput(bool value);
     void SetColorOutput(bool value);
     // -- Log info
     // -- Log info
@@ -62,17 +65,20 @@ public:
     bool ColorOutput_ = false;
     bool ColorOutput_ = false;
   };
   };
 
 
+  // -- File system methods
+  static bool MakeParentDirectory(std::string const& filename);
+  static bool FileRead(std::string& content, std::string const& filename,
+                       std::string* error = nullptr);
+  static bool FileWrite(std::string const& filename,
+                        std::string const& content,
+                        std::string* error = nullptr);
+
   /// @brief Thread safe file system interface
   /// @brief Thread safe file system interface
   class FileSystem
   class FileSystem
   {
   {
   public:
   public:
-    FileSystem(Logger* log)
-      : Log_(log)
-    {
-    }
-
-    /// @brief Logger
-    Logger* Log() const { return Log_; }
+    FileSystem();
+    ~FileSystem();
 
 
     // -- Paths
     // -- Paths
     /// @brief Wrapper for cmSystemTools::GetRealPath
     /// @brief Wrapper for cmSystemTools::GetRealPath
@@ -113,15 +119,9 @@ public:
 
 
     bool FileRead(std::string& content, std::string const& filename,
     bool FileRead(std::string& content, std::string const& filename,
                   std::string* error = nullptr);
                   std::string* error = nullptr);
-    /// @brief Error logging version
-    bool FileRead(GenT genType, std::string& content,
-                  std::string const& filename);
 
 
     bool FileWrite(std::string const& filename, std::string const& content,
     bool FileWrite(std::string const& filename, std::string const& content,
                    std::string* error = nullptr);
                    std::string* error = nullptr);
-    /// @brief Error logging version
-    bool FileWrite(GenT genType, std::string const& filename,
-                   std::string const& content);
 
 
     bool FileDiffers(std::string const& filename, std::string const& content);
     bool FileDiffers(std::string const& filename, std::string const& content);
 
 
@@ -130,17 +130,11 @@ public:
 
 
     // -- Directory access
     // -- Directory access
     bool MakeDirectory(std::string const& dirname);
     bool MakeDirectory(std::string const& dirname);
-    /// @brief Error logging version
-    bool MakeDirectory(GenT genType, std::string const& dirname);
-
     bool MakeParentDirectory(std::string const& filename);
     bool MakeParentDirectory(std::string const& filename);
-    /// @brief Error logging version
-    bool MakeParentDirectory(GenT genType, std::string const& filename);
 
 
   private:
   private:
     std::mutex Mutex_;
     std::mutex Mutex_;
     cmFilePathChecksum FilePathChecksum_;
     cmFilePathChecksum FilePathChecksum_;
-    Logger* Log_;
   };
   };
 
 
   /// @brief Return value and output of an external process
   /// @brief Return value and output of an external process
@@ -250,18 +244,10 @@ public:
   // -- Run
   // -- Run
   bool Run(std::string const& infoFile, std::string const& config);
   bool Run(std::string const& infoFile, std::string const& config);
 
 
-  // -- Accessors
-  // Logging
-  Logger& Log() { return Logger_; }
-  // File System
-  FileSystem& FileSys() { return FileSys_; }
   // InfoFile
   // InfoFile
   std::string const& InfoFile() const { return InfoFile_; }
   std::string const& InfoFile() const { return InfoFile_; }
   std::string const& InfoDir() const { return InfoDir_; }
   std::string const& InfoDir() const { return InfoDir_; }
   std::string const& InfoConfig() const { return InfoConfig_; }
   std::string const& InfoConfig() const { return InfoConfig_; }
-  // libuv loop
-  uv_loop_t* UVLoop() { return UVLoop_.get(); }
-  cm::uv_async_ptr& UVRequest() { return UVRequest_; }
 
 
   // -- Utility
   // -- Utility
   static std::string SettingsFind(std::string const& content, const char* key);
   static std::string SettingsFind(std::string const& content, const char* key);
@@ -272,19 +258,10 @@ protected:
   virtual bool Process() = 0;
   virtual bool Process() = 0;
 
 
 private:
 private:
-  // -- Logging
-  Logger Logger_;
-  FileSystem FileSys_;
   // -- Info settings
   // -- Info settings
   std::string InfoFile_;
   std::string InfoFile_;
   std::string InfoDir_;
   std::string InfoDir_;
   std::string InfoConfig_;
   std::string InfoConfig_;
-// -- libuv loop
-#ifdef CMAKE_UV_SIGNAL_HACK
-  std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
-#endif
-  std::unique_ptr<uv_loop_t> UVLoop_;
-  cm::uv_async_ptr UVRequest_;
 };
 };
 
 
 #endif
 #endif

+ 39 - 11
Source/cmQtAutoGeneratorMocUic.cxx

@@ -638,13 +638,15 @@ void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk)
     if (!result.error()) {
     if (!result.error()) {
       if (!fileExists ||
       if (!fileExists ||
           wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
           wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
-        if (wrk.FileSys().FileWrite(GenT::MOC, wrk.Moc().PredefsFileAbs,
-                                    result.StdOut)) {
+        std::string error;
+        if (wrk.FileSys().FileWrite(wrk.Moc().PredefsFileAbs, result.StdOut,
+                                    &error)) {
           // Success
           // Success
         } else {
         } else {
           std::string emsg = "Writing ";
           std::string emsg = "Writing ";
           emsg += Quoted(wrk.Moc().PredefsFileRel);
           emsg += Quoted(wrk.Moc().PredefsFileRel);
-          emsg += " failed.";
+          emsg += " failed. ";
+          emsg += error;
           wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg);
           wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg);
         }
         }
       } else {
       } else {
@@ -835,7 +837,12 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk)
 void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
 void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
 {
 {
   // Make sure the parent directory exists
   // Make sure the parent directory exists
-  if (wrk.FileSys().MakeParentDirectory(GenT::MOC, BuildFile)) {
+  if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
+    wrk.LogFileError(GenT::MOC, BuildFile,
+                     "Could not create parent directory.");
+    return;
+  }
+  {
     // Compose moc command
     // Compose moc command
     std::vector<std::string> cmd;
     std::vector<std::string> cmd;
     cmd.push_back(wrk.Moc().Executable);
     cmd.push_back(wrk.Moc().Executable);
@@ -950,7 +957,12 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk)
 void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
 void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
 {
 {
   // Make sure the parent directory exists
   // Make sure the parent directory exists
-  if (wrk.FileSys().MakeParentDirectory(GenT::UIC, BuildFile)) {
+  if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
+    wrk.LogFileError(GenT::UIC, BuildFile,
+                     "Could not create parent directory.");
+    return;
+  }
+  {
     // Compose uic command
     // Compose uic command
     std::vector<std::string> cmd;
     std::vector<std::string> cmd;
     cmd.push_back(wrk.Uic().Executable);
     cmd.push_back(wrk.Uic().Executable);
@@ -1136,11 +1148,23 @@ cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
   Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
   Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
                              "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
                              "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
 
 
+  // Initialize libuv loop
+  uv_disable_stdio_inheritance();
+#ifdef CMAKE_UV_SIGNAL_HACK
+  UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
+#endif
+  UVLoop_ = cm::make_unique<uv_loop_t>();
+  uv_loop_init(UVLoop());
+
   // Initialize libuv asynchronous iteration request
   // Initialize libuv asynchronous iteration request
   UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
   UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
 }
 }
 
 
-cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() = default;
+cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
+{
+  // Close libuv loop
+  uv_loop_close(UVLoop());
+}
 
 
 bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
 bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
 {
 {
@@ -1691,9 +1715,10 @@ void cmQtAutoGeneratorMocUic::SettingsFileWrite()
       SettingAppend("uic", SettingsStringUic_);
       SettingAppend("uic", SettingsStringUic_);
     }
     }
     // Write settings file
     // Write settings file
-    if (!FileSys().FileWrite(GenT::GEN, SettingsFile_, content)) {
+    std::string error;
+    if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
       Log().ErrorFile(GenT::GEN, SettingsFile_,
       Log().ErrorFile(GenT::GEN, SettingsFile_,
-                      "Settings file writing failed");
+                      "Settings file writing failed. " + error);
       // Remove old settings file to trigger a full rebuild on the next run
       // Remove old settings file to trigger a full rebuild on the next run
       FileSys().FileRemove(SettingsFile_);
       FileSys().FileRemove(SettingsFile_);
       RegisterJobError();
       RegisterJobError();
@@ -1704,7 +1729,9 @@ void cmQtAutoGeneratorMocUic::SettingsFileWrite()
 void cmQtAutoGeneratorMocUic::CreateDirectories()
 void cmQtAutoGeneratorMocUic::CreateDirectories()
 {
 {
   // Create AUTOGEN include directory
   // Create AUTOGEN include directory
-  if (!FileSys().MakeDirectory(GenT::GEN, Base().AutogenIncludeDir)) {
+  if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
+    Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
+                    "Could not create directory.");
     RegisterJobError();
     RegisterJobError();
   }
   }
 }
 }
@@ -2003,9 +2030,10 @@ void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
         if (Log().Verbose()) {
         if (Log().Verbose()) {
           Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
           Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
         }
         }
-        if (!FileSys().FileWrite(GenT::MOC, compAbs, content)) {
+        std::string error;
+        if (!FileSys().FileWrite(compAbs, content, &error)) {
           Log().ErrorFile(GenT::MOC, compAbs,
           Log().ErrorFile(GenT::MOC, compAbs,
-                          "mocs compilation file writing failed");
+                          "mocs compilation file writing failed. " + error);
           RegisterJobError();
           RegisterJobError();
           return;
           return;
         }
         }

+ 16 - 1
Source/cmQtAutoGeneratorMocUic.h

@@ -8,6 +8,7 @@
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
 #include "cmQtAutoGenerator.h"
 #include "cmUVHandlePtr.h"
 #include "cmUVHandlePtr.h"
+#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
 #include "cm_uv.h"
 #include "cm_uv.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 
@@ -387,6 +388,12 @@ public:
   void ParallelMocAutoUpdated();
   void ParallelMocAutoUpdated();
 
 
 private:
 private:
+  // -- Utility accessors
+  Logger& Log() { return Logger_; }
+  FileSystem& FileSys() { return FileSys_; }
+  // -- libuv loop accessors
+  uv_loop_t* UVLoop() { return UVLoop_.get(); }
+  cm::uv_async_ptr& UVRequest() { return UVRequest_; }
   // -- Abstract processing interface
   // -- Abstract processing interface
   bool Init(cmMakefile* makefile) override;
   bool Init(cmMakefile* makefile) override;
   bool Process() override;
   bool Process() override;
@@ -407,11 +414,19 @@ private:
   void MocGenerateCompilation();
   void MocGenerateCompilation();
 
 
 private:
 private:
+  // -- Utility
+  Logger Logger_;
+  FileSystem FileSys_;
   // -- Settings
   // -- Settings
   BaseSettingsT Base_;
   BaseSettingsT Base_;
   MocSettingsT Moc_;
   MocSettingsT Moc_;
   UicSettingsT Uic_;
   UicSettingsT Uic_;
-  // -- Progress
+  // -- libuv loop
+#ifdef CMAKE_UV_SIGNAL_HACK
+  std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
+#endif
+  std::unique_ptr<uv_loop_t> UVLoop_;
+  cm::uv_async_ptr UVRequest_;
   StageT Stage_ = StageT::SETTINGS_READ;
   StageT Stage_ = StageT::SETTINGS_READ;
   // -- Job queues
   // -- Job queues
   std::mutex JobsMutex_;
   std::mutex JobsMutex_;

+ 0 - 666
Source/cmQtAutoGeneratorRcc.cxx

@@ -1,666 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmQtAutoGeneratorRcc.h"
-#include "cmQtAutoGen.h"
-
-#include "cmAlgorithms.h"
-#include "cmCryptoHash.h"
-#include "cmFileLockResult.h"
-#include "cmMakefile.h"
-#include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-
-// -- Class methods
-
-cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc()
-{
-  // Initialize libuv asynchronous iteration request
-  UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this);
-}
-
-cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc() = default;
-
-bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile)
-{
-  // -- Utility lambdas
-  auto InfoGet = [makefile](std::string const& key) {
-    return makefile->GetSafeDefinition(key);
-  };
-  auto InfoGetList =
-    [makefile](std::string const& key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
-    return list;
-  };
-  auto InfoGetConfig = [makefile,
-                        this](std::string const& key) -> std::string {
-    const char* valueConf = nullptr;
-    {
-      std::string keyConf = key;
-      keyConf += '_';
-      keyConf += InfoConfig();
-      valueConf = makefile->GetDefinition(keyConf);
-    }
-    if (valueConf == nullptr) {
-      return makefile->GetSafeDefinition(key);
-    }
-    return std::string(valueConf);
-  };
-  auto InfoGetConfigList =
-    [&InfoGetConfig](std::string const& key) -> std::vector<std::string> {
-    std::vector<std::string> list;
-    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
-    return list;
-  };
-
-  // -- Read info file
-  if (!makefile->ReadListFile(InfoFile())) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "File processing failed");
-    return false;
-  }
-
-  // - Configurations
-  Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
-  MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
-
-  // - Directories
-  AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
-  if (AutogenBuildDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Build directory empty");
-    return false;
-  }
-
-  IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
-  if (IncludeDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Include directory empty");
-    return false;
-  }
-
-  // - Rcc executable
-  RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
-  RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
-
-  // - Job
-  LockFile_ = InfoGet("ARCC_LOCK_FILE");
-  QrcFile_ = InfoGet("ARCC_SOURCE");
-  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()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Lock file name missing");
-    return false;
-  }
-  if (SettingsFile_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Settings file name missing");
-    return false;
-  }
-  if (AutogenBuildDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Autogen build directory missing");
-    return false;
-  }
-  if (RccExecutable_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc executable missing");
-    return false;
-  }
-  if (QrcFile_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc input file missing");
-    return false;
-  }
-  if (RccFileName_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc output file missing");
-    return false;
-  }
-
-  // Init derived information
-  // ------------------------
-
-  RccFilePublic_ = AutogenBuildDir_;
-  RccFilePublic_ += '/';
-  RccFilePublic_ += RccPathChecksum_;
-  RccFilePublic_ += '/';
-  RccFilePublic_ += RccFileName_;
-
-  // Compute rcc output file name
-  if (IsMultiConfig()) {
-    RccFileOutput_ = IncludeDir_;
-    RccFileOutput_ += '/';
-    RccFileOutput_ += MultiConfigOutput();
-  } else {
-    RccFileOutput_ = RccFilePublic_;
-  }
-
-  return true;
-}
-
-bool cmQtAutoGeneratorRcc::Process()
-{
-  // Run libuv event loop
-  UVRequest().send();
-  if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
-    if (Error_) {
-      return false;
-    }
-  } else {
-    return false;
-  }
-  return true;
-}
-
-void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle)
-{
-  reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage();
-}
-
-void cmQtAutoGeneratorRcc::PollStage()
-{
-  switch (Stage_) {
-    // -- Initialize
-    case StageT::SETTINGS_READ:
-      if (SettingsFileRead()) {
-        SetStage(StageT::TEST_QRC_RCC_FILES);
-      } else {
-        SetStage(StageT::FINISH);
-      }
-      break;
-
-    // -- Change detection
-    case StageT::TEST_QRC_RCC_FILES:
-      if (TestQrcRccFiles()) {
-        SetStage(StageT::GENERATE);
-      } else {
-        SetStage(StageT::TEST_RESOURCES_READ);
-      }
-      break;
-    case StageT::TEST_RESOURCES_READ:
-      if (TestResourcesRead()) {
-        SetStage(StageT::TEST_RESOURCES);
-      }
-      break;
-    case StageT::TEST_RESOURCES:
-      if (TestResources()) {
-        SetStage(StageT::GENERATE);
-      } else {
-        SetStage(StageT::TEST_INFO_FILE);
-      }
-      break;
-    case StageT::TEST_INFO_FILE:
-      TestInfoFile();
-      SetStage(StageT::GENERATE_WRAPPER);
-      break;
-
-    // -- Generation
-    case StageT::GENERATE:
-      GenerateParentDir();
-      SetStage(StageT::GENERATE_RCC);
-      break;
-    case StageT::GENERATE_RCC:
-      if (GenerateRcc()) {
-        SetStage(StageT::GENERATE_WRAPPER);
-      }
-      break;
-    case StageT::GENERATE_WRAPPER:
-      GenerateWrapper();
-      SetStage(StageT::SETTINGS_WRITE);
-      break;
-
-    // -- Finalize
-    case StageT::SETTINGS_WRITE:
-      SettingsFileWrite();
-      SetStage(StageT::FINISH);
-      break;
-    case StageT::FINISH:
-      // Clear all libuv handles
-      UVRequest().reset();
-      // Set highest END stage manually
-      Stage_ = StageT::END;
-      break;
-    case StageT::END:
-      break;
-  }
-}
-
-void cmQtAutoGeneratorRcc::SetStage(StageT stage)
-{
-  if (Error_) {
-    stage = StageT::FINISH;
-  }
-  // Only allow to increase the stage
-  if (Stage_ < stage) {
-    Stage_ = stage;
-    UVRequest().send();
-  }
-}
-
-std::string cmQtAutoGeneratorRcc::MultiConfigOutput() const
-{
-  static std::string const suffix = "_CMAKE_";
-  std::string res;
-  res += RccPathChecksum_;
-  res += '/';
-  res += AppendFilenameSuffix(RccFileName_, suffix);
-  return res;
-}
-
-bool cmQtAutoGeneratorRcc::SettingsFileRead()
-{
-  // Compose current settings strings
-  {
-    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
-    std::string const sep(" ~~~ ");
-    {
-      std::string str;
-      str += RccExecutable_;
-      str += sep;
-      str += cmJoin(RccListOptions_, ";");
-      str += sep;
-      str += QrcFile_;
-      str += sep;
-      str += RccPathChecksum_;
-      str += sep;
-      str += RccFileName_;
-      str += sep;
-      str += cmJoin(Options_, ";");
-      str += sep;
-      str += cmJoin(Inputs_, ";");
-      str += sep;
-      SettingsString_ = crypt.HashString(str);
-    }
-  }
-
-  // Make sure the settings file exists
-  if (!FileSys().FileExists(SettingsFile_, true)) {
-    // Touch the settings file to make sure it exists
-    FileSys().Touch(SettingsFile_, true);
-  }
-
-  // Lock the lock file
-  {
-    // Make sure the lock file exists
-    if (!FileSys().FileExists(LockFile_, true)) {
-      if (!FileSys().Touch(LockFile_, true)) {
-        Log().ErrorFile(GenT::RCC, LockFile_, "Lock file creation failed");
-        Error_ = true;
-        return false;
-      }
-    }
-    // Lock the lock file
-    cmFileLockResult lockResult =
-      LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1));
-    if (!lockResult.IsOk()) {
-      Log().ErrorFile(GenT::RCC, LockFile_,
-                      "File lock failed: " + lockResult.GetOutputMessage());
-      Error_ = true;
-      return false;
-    }
-  }
-
-  // Read old settings
-  {
-    std::string content;
-    if (FileSys().FileRead(content, SettingsFile_)) {
-      SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc"));
-      // In case any setting changed clear the old settings file.
-      // This triggers a full rebuild on the next run if the current
-      // build is aborted before writing the current settings in the end.
-      if (SettingsChanged_) {
-        FileSys().FileWrite(GenT::RCC, SettingsFile_, "");
-      }
-    } else {
-      SettingsChanged_ = true;
-    }
-  }
-
-  return true;
-}
-
-void cmQtAutoGeneratorRcc::SettingsFileWrite()
-{
-  // Only write if any setting changed
-  if (SettingsChanged_) {
-    if (Log().Verbose()) {
-      Log().Info(GenT::RCC, "Writing settings file " + Quoted(SettingsFile_));
-    }
-    // Write settings file
-    std::string content = "rcc:";
-    content += SettingsString_;
-    content += '\n';
-    if (!FileSys().FileWrite(GenT::RCC, SettingsFile_, content)) {
-      Log().ErrorFile(GenT::RCC, SettingsFile_,
-                      "Settings file writing failed");
-      // Remove old settings file to trigger a full rebuild on the next run
-      FileSys().FileRemove(SettingsFile_);
-      Error_ = true;
-    }
-  }
-
-  // Unlock the lock file
-  LockFileLock_.Release();
-}
-
-bool cmQtAutoGeneratorRcc::TestQrcRccFiles()
-{
-  // Do basic checks if rcc generation is required
-
-  // Test if the rcc output file exists
-  if (!FileSys().FileExists(RccFileOutput_)) {
-    if (Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(RccFileOutput_);
-      reason += " from its source file ";
-      reason += Quoted(QrcFile_);
-      reason += " because it doesn't exist";
-      Log().Info(GenT::RCC, reason);
-    }
-    Generate_ = true;
-    return Generate_;
-  }
-
-  // Test if the settings changed
-  if (SettingsChanged_) {
-    if (Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(RccFileOutput_);
-      reason += " from ";
-      reason += Quoted(QrcFile_);
-      reason += " because the RCC settings changed";
-      Log().Info(GenT::RCC, reason);
-    }
-    Generate_ = true;
-    return Generate_;
-  }
-
-  // Test if the rcc output file is older than the .qrc file
-  {
-    bool isOlder = false;
-    {
-      std::string error;
-      isOlder = FileSys().FileIsOlderThan(RccFileOutput_, QrcFile_, &error);
-      if (!error.empty()) {
-        Log().ErrorFile(GenT::RCC, QrcFile_, error);
-        Error_ = true;
-      }
-    }
-    if (isOlder) {
-      if (Log().Verbose()) {
-        std::string reason = "Generating ";
-        reason += Quoted(RccFileOutput_);
-        reason += " because it is older than ";
-        reason += Quoted(QrcFile_);
-        Log().Info(GenT::RCC, reason);
-      }
-      Generate_ = true;
-    }
-  }
-
-  return Generate_;
-}
-
-bool cmQtAutoGeneratorRcc::TestResourcesRead()
-{
-  if (!Inputs_.empty()) {
-    // Inputs are known already
-    return true;
-  }
-
-  if (!RccListOptions_.empty()) {
-    // Start a rcc list process and parse the output
-    if (Process_) {
-      // Process is running already
-      if (Process_->IsFinished()) {
-        // Process is finished
-        if (!ProcessResult_.error()) {
-          // Process success
-          std::string parseError;
-          if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr,
-                                  Inputs_, parseError)) {
-            Log().ErrorFile(GenT::RCC, QrcFile_, parseError);
-            Error_ = true;
-          }
-        } else {
-          Log().ErrorFile(GenT::RCC, QrcFile_, ProcessResult_.ErrorMessage);
-          Error_ = true;
-        }
-        // Clean up
-        Process_.reset();
-        ProcessResult_.reset();
-      } else {
-        // Process is not finished, yet.
-        return false;
-      }
-    } else {
-      // Start a new process
-      // rcc prints relative entry paths when started in the directory of the
-      // qrc file with a pathless qrc file name argument.
-      // This is important because on Windows absolute paths returned by rcc
-      // might contain bad multibyte characters when the qrc file path
-      // contains non-ASCII pcharacters.
-      std::vector<std::string> cmd;
-      cmd.push_back(RccExecutable_);
-      cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end());
-      cmd.push_back(QrcFileName_);
-      // We're done here if the process fails to start
-      return !StartProcess(QrcFileDir_, cmd, false);
-    }
-  } else {
-    // rcc does not support the --list command.
-    // Read the qrc file content and parse it.
-    std::string qrcContent;
-    if (FileSys().FileRead(GenT::RCC, qrcContent, QrcFile_)) {
-      RccListParseContent(qrcContent, Inputs_);
-    }
-  }
-
-  if (!Inputs_.empty()) {
-    // Convert relative paths to absolute paths
-    RccListConvertFullPath(QrcFileDir_, Inputs_);
-  }
-
-  return true;
-}
-
-bool cmQtAutoGeneratorRcc::TestResources()
-{
-  if (Inputs_.empty()) {
-    return true;
-  }
-  {
-    std::string error;
-    for (std::string const& resFile : Inputs_) {
-      // Check if the resource file exists
-      if (!FileSys().FileExists(resFile)) {
-        error = "Could not find the resource file\n  ";
-        error += Quoted(resFile);
-        error += '\n';
-        Log().ErrorFile(GenT::RCC, QrcFile_, error);
-        Error_ = true;
-        break;
-      }
-      // Check if the resource file is newer than the build file
-      if (FileSys().FileIsOlderThan(RccFileOutput_, resFile, &error)) {
-        if (Log().Verbose()) {
-          std::string reason = "Generating ";
-          reason += Quoted(RccFileOutput_);
-          reason += " from ";
-          reason += Quoted(QrcFile_);
-          reason += " because it is older than ";
-          reason += Quoted(resFile);
-          Log().Info(GenT::RCC, reason);
-        }
-        Generate_ = true;
-        break;
-      }
-      // Print error and break on demand
-      if (!error.empty()) {
-        Log().ErrorFile(GenT::RCC, QrcFile_, error);
-        Error_ = true;
-        break;
-      }
-    }
-  }
-
-  return Generate_;
-}
-
-void cmQtAutoGeneratorRcc::TestInfoFile()
-{
-  // Test if the rcc output file is older than the info file
-  {
-    bool isOlder = false;
-    {
-      std::string error;
-      isOlder = FileSys().FileIsOlderThan(RccFileOutput_, InfoFile(), &error);
-      if (!error.empty()) {
-        Log().ErrorFile(GenT::RCC, QrcFile_, error);
-        Error_ = true;
-      }
-    }
-    if (isOlder) {
-      if (Log().Verbose()) {
-        std::string reason = "Touching ";
-        reason += Quoted(RccFileOutput_);
-        reason += " because it is older than ";
-        reason += Quoted(InfoFile());
-        Log().Info(GenT::RCC, reason);
-      }
-      // Touch build file
-      FileSys().Touch(RccFileOutput_);
-      BuildFileChanged_ = true;
-    }
-  }
-}
-
-void cmQtAutoGeneratorRcc::GenerateParentDir()
-{
-  // Make sure the parent directory exists
-  if (!FileSys().MakeParentDirectory(GenT::RCC, RccFileOutput_)) {
-    Error_ = true;
-  }
-}
-
-/**
- * @return True when finished
- */
-bool cmQtAutoGeneratorRcc::GenerateRcc()
-{
-  if (!Generate_) {
-    // Nothing to do
-    return true;
-  }
-
-  if (Process_) {
-    // Process is running already
-    if (Process_->IsFinished()) {
-      // Process is finished
-      if (!ProcessResult_.error()) {
-        // Rcc process success
-        // Print rcc output
-        if (!ProcessResult_.StdOut.empty()) {
-          Log().Info(GenT::RCC, ProcessResult_.StdOut);
-        }
-        BuildFileChanged_ = true;
-      } else {
-        // Rcc process failed
-        {
-          std::string emsg = "The rcc process failed to compile\n  ";
-          emsg += Quoted(QrcFile_);
-          emsg += "\ninto\n  ";
-          emsg += Quoted(RccFileOutput_);
-          if (ProcessResult_.error()) {
-            emsg += "\n";
-            emsg += ProcessResult_.ErrorMessage;
-          }
-          Log().ErrorCommand(GenT::RCC, emsg, Process_->Setup().Command,
-                             ProcessResult_.StdOut);
-        }
-        FileSys().FileRemove(RccFileOutput_);
-        Error_ = true;
-      }
-      // Clean up
-      Process_.reset();
-      ProcessResult_.reset();
-    } else {
-      // Process is not finished, yet.
-      return false;
-    }
-  } else {
-    // Start a rcc process
-    std::vector<std::string> cmd;
-    cmd.push_back(RccExecutable_);
-    cmd.insert(cmd.end(), Options_.begin(), Options_.end());
-    cmd.emplace_back("-o");
-    cmd.push_back(RccFileOutput_);
-    cmd.push_back(QrcFile_);
-    // We're done here if the process fails to start
-    return !StartProcess(AutogenBuildDir_, cmd, true);
-  }
-
-  return true;
-}
-
-void cmQtAutoGeneratorRcc::GenerateWrapper()
-{
-  // Generate a wrapper source file on demand
-  if (IsMultiConfig()) {
-    // Wrapper file content
-    std::string content;
-    content += "// This is an autogenerated configuration wrapper file.\n";
-    content += "// Changes will be overwritten.\n";
-    content += "#include <";
-    content += MultiConfigOutput();
-    content += ">\n";
-
-    // Write content to file
-    if (FileSys().FileDiffers(RccFilePublic_, content)) {
-      // Write new wrapper file
-      if (Log().Verbose()) {
-        Log().Info(GenT::RCC, "Generating RCC wrapper file " + RccFilePublic_);
-      }
-      if (!FileSys().FileWrite(GenT::RCC, RccFilePublic_, content)) {
-        Log().ErrorFile(GenT::RCC, RccFilePublic_,
-                        "RCC wrapper file writing failed");
-        Error_ = true;
-      }
-    } else if (BuildFileChanged_) {
-      // Just touch the wrapper file
-      if (Log().Verbose()) {
-        Log().Info(GenT::RCC, "Touching RCC wrapper file " + RccFilePublic_);
-      }
-      FileSys().Touch(RccFilePublic_);
-    }
-  }
-}
-
-bool cmQtAutoGeneratorRcc::StartProcess(
-  std::string const& workingDirectory, std::vector<std::string> const& command,
-  bool mergedOutput)
-{
-  // Log command
-  if (Log().Verbose()) {
-    std::string msg = "Running command:\n";
-    msg += QuotedCommand(command);
-    msg += '\n';
-    Log().Info(GenT::RCC, msg);
-  }
-
-  // Create process handler
-  Process_ = cm::make_unique<ReadOnlyProcessT>();
-  Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory);
-  // Start process
-  if (!Process_->start(UVLoop(), [this] { UVRequest().send(); })) {
-    Log().ErrorFile(GenT::RCC, QrcFile_, ProcessResult_.ErrorMessage);
-    Error_ = true;
-    // Clean up
-    Process_.reset();
-    ProcessResult_.reset();
-    return false;
-  }
-  return true;
-}

+ 516 - 0
Source/cmQtAutoRcc.cxx

@@ -0,0 +1,516 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmQtAutoRcc.h"
+#include "cmQtAutoGen.h"
+
+#include "cmAlgorithms.h"
+#include "cmCryptoHash.h"
+#include "cmDuration.h"
+#include "cmFileLockResult.h"
+#include "cmMakefile.h"
+#include "cmProcessOutput.h"
+#include "cmSystemTools.h"
+
+// -- Class methods
+
+cmQtAutoRcc::cmQtAutoRcc() = default;
+
+cmQtAutoRcc::~cmQtAutoRcc() = default;
+
+bool cmQtAutoRcc::Init(cmMakefile* makefile)
+{
+  // -- Utility lambdas
+  auto InfoGet = [makefile](std::string const& key) {
+    return makefile->GetSafeDefinition(key);
+  };
+  auto InfoGetList =
+    [makefile](std::string const& key) -> std::vector<std::string> {
+    std::vector<std::string> list;
+    cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
+    return list;
+  };
+  auto InfoGetConfig = [makefile,
+                        this](std::string const& key) -> std::string {
+    const char* valueConf = nullptr;
+    {
+      std::string keyConf = key;
+      keyConf += '_';
+      keyConf += InfoConfig();
+      valueConf = makefile->GetDefinition(keyConf);
+    }
+    if (valueConf == nullptr) {
+      return makefile->GetSafeDefinition(key);
+    }
+    return std::string(valueConf);
+  };
+  auto InfoGetConfigList =
+    [&InfoGetConfig](std::string const& key) -> std::vector<std::string> {
+    std::vector<std::string> list;
+    cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
+    return list;
+  };
+
+  // -- Read info file
+  if (!makefile->ReadListFile(InfoFile())) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "File processing failed.");
+    return false;
+  }
+
+  // - Configurations
+  Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
+  MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
+
+  // - Directories
+  AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
+  if (AutogenBuildDir_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "Build directory empty.");
+    return false;
+  }
+
+  IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
+  if (IncludeDir_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "Include directory empty.");
+    return false;
+  }
+
+  // - Rcc executable
+  RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
+  RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
+
+  // - Job
+  LockFile_ = InfoGet("ARCC_LOCK_FILE");
+  QrcFile_ = InfoGet("ARCC_SOURCE");
+  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()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "Lock file name missing.");
+    return false;
+  }
+  if (SettingsFile_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "Settings file name missing.");
+    return false;
+  }
+  if (AutogenBuildDir_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "Autogen build directory missing.");
+    return false;
+  }
+  if (RccExecutable_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc executable missing.");
+    return false;
+  }
+  if (QrcFile_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc input file missing.");
+    return false;
+  }
+  if (RccFileName_.empty()) {
+    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc output file missing.");
+    return false;
+  }
+
+  // Init derived information
+  // ------------------------
+
+  RccFilePublic_ = AutogenBuildDir_;
+  RccFilePublic_ += '/';
+  RccFilePublic_ += RccPathChecksum_;
+  RccFilePublic_ += '/';
+  RccFilePublic_ += RccFileName_;
+
+  // Compute rcc output file name
+  if (IsMultiConfig()) {
+    RccFileOutput_ = IncludeDir_;
+    RccFileOutput_ += '/';
+    RccFileOutput_ += MultiConfigOutput();
+  } else {
+    RccFileOutput_ = RccFilePublic_;
+  }
+
+  return true;
+}
+
+bool cmQtAutoRcc::Process()
+{
+  if (!SettingsFileRead()) {
+    return false;
+  }
+
+  // Test if the rcc output needs to be regenerated
+  bool generate = false;
+  if (!TestQrcRccFiles(generate)) {
+    return false;
+  }
+  if (!generate && !TestResources(generate)) {
+    return false;
+  }
+  // Generate on demand
+  if (generate) {
+    if (!GenerateRcc()) {
+      return false;
+    }
+  } else {
+    // Test if the info file is newer than the output file
+    if (!TestInfoFile()) {
+      return false;
+    }
+  }
+
+  if (!GenerateWrapper()) {
+    return false;
+  }
+
+  return SettingsFileWrite();
+}
+
+std::string cmQtAutoRcc::MultiConfigOutput() const
+{
+  static std::string const suffix = "_CMAKE_";
+  std::string res;
+  res += RccPathChecksum_;
+  res += '/';
+  res += AppendFilenameSuffix(RccFileName_, suffix);
+  return res;
+}
+
+bool cmQtAutoRcc::SettingsFileRead()
+{
+  // Compose current settings strings
+  {
+    cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
+    std::string const sep(" ~~~ ");
+    {
+      std::string str;
+      str += RccExecutable_;
+      str += sep;
+      str += cmJoin(RccListOptions_, ";");
+      str += sep;
+      str += QrcFile_;
+      str += sep;
+      str += RccPathChecksum_;
+      str += sep;
+      str += RccFileName_;
+      str += sep;
+      str += cmJoin(Options_, ";");
+      str += sep;
+      str += cmJoin(Inputs_, ";");
+      str += sep;
+      SettingsString_ = crypt.HashString(str);
+    }
+  }
+
+  // Make sure the settings file exists
+  if (!cmSystemTools::FileExists(SettingsFile_, true)) {
+    // Touch the settings file to make sure it exists
+    if (!cmSystemTools::Touch(SettingsFile_, true)) {
+      Log().ErrorFile(GenT::RCC, SettingsFile_,
+                      "Settings file creation failed.");
+      return false;
+    }
+  }
+
+  // Lock the lock file
+  {
+    // Make sure the lock file exists
+    if (!cmSystemTools::FileExists(LockFile_, true)) {
+      if (!cmSystemTools::Touch(LockFile_, true)) {
+        Log().ErrorFile(GenT::RCC, LockFile_, "Lock file creation failed.");
+        return false;
+      }
+    }
+    // Lock the lock file
+    cmFileLockResult lockResult =
+      LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1));
+    if (!lockResult.IsOk()) {
+      Log().ErrorFile(GenT::RCC, LockFile_,
+                      "File lock failed: " + lockResult.GetOutputMessage());
+      return false;
+    }
+  }
+
+  // Read old settings
+  {
+    std::string content;
+    if (FileRead(content, SettingsFile_)) {
+      SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc"));
+      // In case any setting changed clear the old settings file.
+      // This triggers a full rebuild on the next run if the current
+      // build is aborted before writing the current settings in the end.
+      if (SettingsChanged_) {
+        std::string error;
+        if (!FileWrite(SettingsFile_, "", &error)) {
+          Log().ErrorFile(GenT::RCC, SettingsFile_,
+                          "Settings file clearing failed. " + error);
+          return false;
+        }
+      }
+    } else {
+      SettingsChanged_ = true;
+    }
+  }
+
+  return true;
+}
+
+bool cmQtAutoRcc::SettingsFileWrite()
+{
+  // Only write if any setting changed
+  if (SettingsChanged_) {
+    if (Log().Verbose()) {
+      Log().Info(GenT::RCC, "Writing settings file " + Quoted(SettingsFile_));
+    }
+    // Write settings file
+    std::string content = "rcc:";
+    content += SettingsString_;
+    content += '\n';
+    std::string error;
+    if (!FileWrite(SettingsFile_, content, &error)) {
+      Log().ErrorFile(GenT::RCC, SettingsFile_,
+                      "Settings file writing failed. " + error);
+      // Remove old settings file to trigger a full rebuild on the next run
+      cmSystemTools::RemoveFile(SettingsFile_);
+      return false;
+    }
+  }
+
+  // Unlock the lock file
+  LockFileLock_.Release();
+  return true;
+}
+
+/// Do basic checks if rcc generation is required
+bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
+{
+  // Test if the rcc input file exists
+  if (!QrcFileTime_.Load(QrcFile_)) {
+    std::string error;
+    error = "The resources file ";
+    error += Quoted(QrcFile_);
+    error += " does not exist";
+    Log().ErrorFile(GenT::RCC, QrcFile_, error);
+    return false;
+  }
+
+  // Test if the rcc output file exists
+  if (!RccFileTime_.Load(RccFileOutput_)) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(RccFileOutput_);
+      reason += " from its source file ";
+      reason += Quoted(QrcFile_);
+      reason += " because it doesn't exist";
+      Log().Info(GenT::RCC, reason);
+    }
+    generate = true;
+    return true;
+  }
+
+  // Test if the settings changed
+  if (SettingsChanged_) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(RccFileOutput_);
+      reason += " from ";
+      reason += Quoted(QrcFile_);
+      reason += " because the RCC settings changed";
+      Log().Info(GenT::RCC, reason);
+    }
+    generate = true;
+    return true;
+  }
+
+  // Test if the rcc output file is older than the .qrc file
+  if (RccFileTime_.Older(QrcFileTime_)) {
+    if (Log().Verbose()) {
+      std::string reason = "Generating ";
+      reason += Quoted(RccFileOutput_);
+      reason += " because it is older than ";
+      reason += Quoted(QrcFile_);
+      Log().Info(GenT::RCC, reason);
+    }
+    generate = true;
+    return true;
+  }
+
+  return true;
+}
+
+bool cmQtAutoRcc::TestResources(bool& generate)
+{
+  // Read resource files list
+  if (Inputs_.empty()) {
+    std::string error;
+    RccLister const lister(RccExecutable_, RccListOptions_);
+    if (!lister.list(QrcFile_, Inputs_, error, Log().Verbose())) {
+      Log().ErrorFile(GenT::RCC, QrcFile_, error);
+      return false;
+    }
+  }
+
+  for (std::string const& resFile : Inputs_) {
+    // Check if the resource file exists
+    cmFileTime fileTime;
+    if (!fileTime.Load(resFile)) {
+      std::string error;
+      error = "Could not find the resource file\n  ";
+      error += Quoted(resFile);
+      error += '\n';
+      Log().ErrorFile(GenT::RCC, QrcFile_, error);
+      return false;
+    }
+    // Check if the resource file is newer than the build file
+    if (RccFileTime_.Older(fileTime)) {
+      if (Log().Verbose()) {
+        std::string reason = "Generating ";
+        reason += Quoted(RccFileOutput_);
+        reason += " from ";
+        reason += Quoted(QrcFile_);
+        reason += " because it is older than ";
+        reason += Quoted(resFile);
+        Log().Info(GenT::RCC, reason);
+      }
+      generate = true;
+      break;
+    }
+  }
+  return true;
+}
+
+bool cmQtAutoRcc::TestInfoFile()
+{
+  // Test if the rcc output file is older than the info file
+
+  cmFileTime infoFileTime;
+  if (!infoFileTime.Load(InfoFile())) {
+    std::string error;
+    error = "Could not find the info file ";
+    error += Quoted(InfoFile());
+    error += '\n';
+    Log().ErrorFile(GenT::RCC, QrcFile_, error);
+    return false;
+  }
+  if (RccFileTime_.Older(infoFileTime)) {
+    if (Log().Verbose()) {
+      std::string reason = "Touching ";
+      reason += Quoted(RccFileOutput_);
+      reason += " because it is older than ";
+      reason += Quoted(InfoFile());
+      Log().Info(GenT::RCC, reason);
+    }
+    // Touch build file
+    if (!cmSystemTools::Touch(RccFileOutput_, false)) {
+      Log().ErrorFile(GenT::RCC, RccFileOutput_, "Build file touch failed");
+      return false;
+    }
+    BuildFileChanged_ = true;
+  }
+
+  return true;
+}
+
+bool cmQtAutoRcc::GenerateRcc()
+{
+  // Make parent directory
+  if (!MakeParentDirectory(RccFileOutput_)) {
+    Log().ErrorFile(GenT::RCC, RccFileOutput_,
+                    "Could not create parent directory");
+    return false;
+  }
+
+  // Start a rcc process
+  std::vector<std::string> cmd;
+  cmd.push_back(RccExecutable_);
+  cmd.insert(cmd.end(), Options_.begin(), Options_.end());
+  cmd.emplace_back("-o");
+  cmd.push_back(RccFileOutput_);
+  cmd.push_back(QrcFile_);
+
+  // Log command
+  if (Log().Verbose()) {
+    std::string msg = "Running command:\n";
+    msg += QuotedCommand(cmd);
+    msg += '\n';
+    cmSystemTools::Stdout(msg);
+  }
+
+  std::string rccStdOut;
+  std::string rccStdErr;
+  int retVal = 0;
+  bool result = cmSystemTools::RunSingleCommand(
+    cmd, &rccStdOut, &rccStdErr, &retVal, AutogenBuildDir_.c_str(),
+    cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
+  if (!result || (retVal != 0)) {
+    // rcc process failed
+    {
+      std::string err = "The rcc process failed to compile\n  ";
+      err += Quoted(QrcFile_);
+      err += "\ninto\n  ";
+      err += Quoted(RccFileOutput_);
+      Log().ErrorCommand(GenT::RCC, err, cmd, rccStdOut + rccStdErr);
+    }
+    cmSystemTools::RemoveFile(RccFileOutput_);
+    return false;
+  }
+
+  // rcc process success
+  // Print rcc output
+  if (!rccStdOut.empty()) {
+    Log().Info(GenT::RCC, rccStdOut);
+  }
+  BuildFileChanged_ = true;
+
+  return true;
+}
+
+bool cmQtAutoRcc::GenerateWrapper()
+{
+  // Generate a wrapper source file on demand
+  if (IsMultiConfig()) {
+    // Wrapper file content
+    std::string content;
+    content += "// This is an autogenerated configuration wrapper file.\n";
+    content += "// Changes will be overwritten.\n";
+    content += "#include <";
+    content += MultiConfigOutput();
+    content += ">\n";
+
+    // Compare with existing file content
+    bool fileDiffers = true;
+    {
+      std::string oldContents;
+      if (FileRead(oldContents, RccFilePublic_)) {
+        fileDiffers = (oldContents != content);
+      }
+    }
+    if (fileDiffers) {
+      // Write new wrapper file
+      if (Log().Verbose()) {
+        Log().Info(GenT::RCC, "Generating RCC wrapper file " + RccFilePublic_);
+      }
+      std::string error;
+      if (!FileWrite(RccFilePublic_, content, &error)) {
+        Log().ErrorFile(GenT::RCC, RccFilePublic_,
+                        "RCC wrapper file writing failed. " + error);
+        return false;
+      }
+    } else if (BuildFileChanged_) {
+      // Just touch the wrapper file
+      if (Log().Verbose()) {
+        Log().Info(GenT::RCC, "Touching RCC wrapper file " + RccFilePublic_);
+      }
+      if (!cmSystemTools::Touch(RccFilePublic_, false)) {
+        Log().ErrorFile(GenT::RCC, RccFilePublic_,
+                        "RCC wrapper file touch failed.");
+        return false;
+      }
+    }
+  }
+  return true;
+}

+ 21 - 50
Source/cmQtAutoGeneratorRcc.h → Source/cmQtAutoRcc.h

@@ -1,13 +1,13 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#ifndef cmQtAutoGeneratorRcc_h
-#define cmQtAutoGeneratorRcc_h
+#ifndef cmQtAutoRcc_h
+#define cmQtAutoRcc_h
 
 
 #include "cmConfigure.h" // IWYU pragma: keep
 #include "cmConfigure.h" // IWYU pragma: keep
 
 
 #include "cmFileLock.h"
 #include "cmFileLock.h"
+#include "cmFileTime.h"
 #include "cmQtAutoGenerator.h"
 #include "cmQtAutoGenerator.h"
-#include "cm_uv.h"
 
 
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -15,62 +15,38 @@
 class cmMakefile;
 class cmMakefile;
 
 
 // @brief AUTORCC generator
 // @brief AUTORCC generator
-class cmQtAutoGeneratorRcc : public cmQtAutoGenerator
+class cmQtAutoRcc : public cmQtAutoGenerator
 {
 {
 public:
 public:
-  cmQtAutoGeneratorRcc();
-  ~cmQtAutoGeneratorRcc() override;
+  cmQtAutoRcc();
+  ~cmQtAutoRcc() override;
 
 
-  cmQtAutoGeneratorRcc(cmQtAutoGeneratorRcc const&) = delete;
-  cmQtAutoGeneratorRcc& operator=(cmQtAutoGeneratorRcc const&) = delete;
+  cmQtAutoRcc(cmQtAutoRcc const&) = delete;
+  cmQtAutoRcc& operator=(cmQtAutoRcc const&) = delete;
 
 
 private:
 private:
-  // -- Types
-
-  /// @brief Processing stage
-  enum class StageT : unsigned char
-  {
-    SETTINGS_READ,
-    TEST_QRC_RCC_FILES,
-    TEST_RESOURCES_READ,
-    TEST_RESOURCES,
-    TEST_INFO_FILE,
-    GENERATE,
-    GENERATE_RCC,
-    GENERATE_WRAPPER,
-    SETTINGS_WRITE,
-    FINISH,
-    END
-  };
+  // -- Utility
+  Logger& Log() { return Logger_; }
+  bool IsMultiConfig() const { return MultiConfig_; }
+  std::string MultiConfigOutput() const;
 
 
   // -- Abstract processing interface
   // -- Abstract processing interface
   bool Init(cmMakefile* makefile) override;
   bool Init(cmMakefile* makefile) override;
   bool Process() override;
   bool Process() override;
-  // -- Process stage
-  static void UVPollStage(uv_async_t* handle);
-  void PollStage();
-  void SetStage(StageT stage);
   // -- Settings file
   // -- Settings file
   bool SettingsFileRead();
   bool SettingsFileRead();
-  void SettingsFileWrite();
+  bool SettingsFileWrite();
   // -- Tests
   // -- Tests
-  bool TestQrcRccFiles();
-  bool TestResourcesRead();
-  bool TestResources();
-  void TestInfoFile();
+  bool TestQrcRccFiles(bool& generate);
+  bool TestResources(bool& generate);
+  bool TestInfoFile();
   // -- Generation
   // -- Generation
-  void GenerateParentDir();
   bool GenerateRcc();
   bool GenerateRcc();
-  void GenerateWrapper();
-
-  // -- Utility
-  bool IsMultiConfig() const { return MultiConfig_; }
-  std::string MultiConfigOutput() const;
-  bool StartProcess(std::string const& workingDirectory,
-                    std::vector<std::string> const& command,
-                    bool mergedOutput);
+  bool GenerateWrapper();
 
 
 private:
 private:
+  // -- Logging
+  Logger Logger_;
   // -- Config settings
   // -- Config settings
   bool MultiConfig_ = false;
   bool MultiConfig_ = false;
   // -- Directories
   // -- Directories
@@ -85,23 +61,18 @@ private:
   std::string QrcFile_;
   std::string QrcFile_;
   std::string QrcFileName_;
   std::string QrcFileName_;
   std::string QrcFileDir_;
   std::string QrcFileDir_;
+  cmFileTime QrcFileTime_;
   std::string RccPathChecksum_;
   std::string RccPathChecksum_;
   std::string RccFileName_;
   std::string RccFileName_;
   std::string RccFileOutput_;
   std::string RccFileOutput_;
   std::string RccFilePublic_;
   std::string RccFilePublic_;
+  cmFileTime RccFileTime_;
   std::vector<std::string> Options_;
   std::vector<std::string> Options_;
   std::vector<std::string> Inputs_;
   std::vector<std::string> Inputs_;
-  // -- Subprocess
-  ProcessResultT ProcessResult_;
-  std::unique_ptr<ReadOnlyProcessT> Process_;
   // -- Settings file
   // -- Settings file
   std::string SettingsFile_;
   std::string SettingsFile_;
   std::string SettingsString_;
   std::string SettingsString_;
   bool SettingsChanged_ = false;
   bool SettingsChanged_ = false;
-  // -- libuv loop
-  StageT Stage_ = StageT::SETTINGS_READ;
-  bool Error_ = false;
-  bool Generate_ = false;
   bool BuildFileChanged_ = false;
   bool BuildFileChanged_ = false;
 };
 };
 
 

+ 2 - 2
Source/cmcmd.cxx

@@ -8,7 +8,7 @@
 #include "cmLocalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
 #include "cmQtAutoGeneratorMocUic.h"
 #include "cmQtAutoGeneratorMocUic.h"
-#include "cmQtAutoGeneratorRcc.h"
+#include "cmQtAutoRcc.h"
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateDirectory.h"
@@ -1024,7 +1024,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
       return autoGen.Run(infoDir, config) ? 0 : 1;
       return autoGen.Run(infoDir, config) ? 0 : 1;
     }
     }
     if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
     if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
-      cmQtAutoGeneratorRcc autoGen;
+      cmQtAutoRcc autoGen;
       std::string const& infoFile = args[2];
       std::string const& infoFile = args[2];
       std::string config;
       std::string config;
       if (args.size() > 3) {
       if (args.size() > 3) {