Forráskód Böngészése

AutoGen: Use moc's feature to output dependencies

In Qt version 5.15.0 moc learned to output the dependencies
of the generated file.

This commit enhances JobCompileMocT to read the dependency file
written by moc. The dependencies are stored in the same cache that's
used for the dependencies determined by dependency filters.

The dependency filter functionality is turned off if moc's dependency
output feature is used.

Fixes: #17750
Fixes: #19058
Joerg Bornemann 5 éve
szülő
commit
f765fdea03

+ 3 - 0
Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst

@@ -26,6 +26,9 @@ See :prop_tgt:`AUTOGEN_TARGET_DEPENDS` for reference.
 By default :prop_tgt:`AUTOMOC_DEPEND_FILTERS` is initialized from
 :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS`, which is empty by default.
 
+From Qt 5.15.0 on this variable is ignored as moc is able to output the correct
+dependencies.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
 

+ 1 - 0
Source/cmQtAutoGenInitializer.cxx

@@ -1409,6 +1409,7 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
   info.SetConfig("INCLUDE_DIR", this->Dir.Include);
 
   info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
+  info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
   info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
   info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
 

+ 78 - 14
Source/cmQtAutoMocUic.cxx

@@ -25,6 +25,8 @@
 
 #include "cmCryptoHash.h"
 #include "cmFileTime.h"
+#include "cmGccDepfileReader.h"
+#include "cmGccDepfileReaderTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
@@ -170,7 +172,7 @@ public:
     // -- Attributes
     // - Config
     bool MultiConfig = false;
-    unsigned int QtVersionMajor = 4;
+    IntegerVersion QtVersion = { 4, 0 };
     unsigned int ThreadCount = 0;
     // - Directories
     std::string AutogenBuildDir;
@@ -216,6 +218,7 @@ public:
     bool SettingsChanged = false;
     bool RelaxedMode = false;
     bool PathPrefix = false;
+    bool CanOutputDependencies = false;
     cmFileTime ExecutableTime;
     std::string Executable;
     std::string CompFileAbs;
@@ -485,8 +488,17 @@ public:
   class JobCompileMocT : public JobCompileT
   {
   public:
-    using JobCompileT::JobCompileT;
+    JobCompileMocT(MappingHandleT uicMapping,
+                   std::unique_ptr<std::string> reason,
+                   ParseCacheT::FileHandleT cacheEntry)
+      : JobCompileT(std::move(uicMapping), std::move(reason))
+      , CacheEntry(std::move(cacheEntry))
+    {
+    }
     void Process() override;
+
+  protected:
+    ParseCacheT::FileHandleT CacheEntry;
   };
 
   /** uic compiles a file.  */
@@ -546,6 +558,9 @@ private:
   void Abort(bool error);
   // -- Generation
   bool CreateDirectories();
+  // -- Support for depfiles
+  static std::vector<std::string> dependenciesFromDepFile(
+    const char* filePath);
 
 private:
   // -- Settings
@@ -951,7 +966,7 @@ void cmQtAutoMocUicT::JobParseT::MocMacro()
 
 void cmQtAutoMocUicT::JobParseT::MocDependecies()
 {
-  if (MocConst().DependFilters.empty()) {
+  if (MocConst().DependFilters.empty() || MocConst().CanOutputDependencies) {
     return;
   }
 
@@ -1674,8 +1689,13 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Generate(MappingHandleT const& mapping,
   if (Probe(*mapping, reason.get())) {
     // Register the parent directory for creation
     MocEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile));
+    // Fetch the cache entry for the source file
+    std::string const& sourceFile = mapping->SourceFile->FileName;
+    ParseCacheT::GetOrInsertT cacheEntry =
+      BaseEval().ParseCache.GetOrInsert(sourceFile);
     // Add moc job
-    Gen()->WorkerPool().EmplaceJob<JobCompileMocT>(mapping, std::move(reason));
+    Gen()->WorkerPool().EmplaceJob<JobCompileMocT>(
+      mapping, std::move(reason), std::move(cacheEntry.first));
     // Check if a moc job for a mocs_compilation.cpp entry was generated
     if (compFile) {
       MocEval().CompUpdated = true;
@@ -1779,6 +1799,14 @@ cmQtAutoMocUicT::JobProbeDepsMocT::FindDependency(
   std::string const& sourceDir, std::string const& includeString) const
 {
   using ResPair = std::pair<std::string, cmFileTime>;
+  // moc's dependency file contains absolute paths
+  if (MocConst().CanOutputDependencies) {
+    ResPair res{ includeString, {} };
+    if (res.second.Load(res.first)) {
+      return res;
+    }
+    return {};
+  }
   // Search in vicinity of the source
   {
     ResPair res{ sourceDir + includeString, {} };
@@ -1947,6 +1975,9 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
     }
     // Add extra options
     cm::append(cmd, MocConst().OptionsExtra);
+    if (MocConst().CanOutputDependencies) {
+      cmd.emplace_back("--output-dep-file");
+    }
     // Add output file
     cmd.emplace_back("-o");
     cmd.push_back(outputFile);
@@ -1956,12 +1987,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
 
   // Execute moc command
   cmWorkerPool::ProcessResultT result;
-  if (RunProcess(GenT::MOC, result, cmd, Reason.get())) {
-    // Moc command success. Print moc output.
-    if (!result.StdOut.empty()) {
-      Log().Info(GenT::MOC, result.StdOut);
-    }
-  } else {
+  if (!RunProcess(GenT::MOC, result, cmd, Reason.get())) {
     // Moc command failed
     std::string includers;
     if (!Mapping->IncluderFiles.empty()) {
@@ -1976,6 +2002,28 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
                              MessagePath(outputFile), '\n', includers,
                              result.ErrorMessage),
                     cmd, result.StdOut);
+    return;
+  }
+
+  // Moc command success. Print moc output.
+  if (!result.StdOut.empty()) {
+    Log().Info(GenT::MOC, result.StdOut);
+  }
+
+  // Extract dependencies from the dep file moc generated for us
+  if (MocConst().CanOutputDependencies) {
+    const std::string depfile = outputFile + ".d";
+    if (Log().Verbose()) {
+      Log().Info(GenT::MOC,
+                 "Reading dependencies from " + MessagePath(depfile));
+    }
+    if (!cmSystemTools::FileExists(depfile)) {
+      Log().Warning(GenT::MOC,
+                    "Dependency file " + MessagePath(depfile) +
+                      " does not exist.");
+      return;
+    }
+    CacheEntry->Moc.Depends = dependenciesFromDepFile(depfile.c_str());
   }
 }
 
@@ -1992,7 +2040,7 @@ void cmQtAutoMocUicT::JobCompileUicT::Process()
     auto optionIt = UicConst().UiFiles.find(sourceFile);
     if (optionIt != UicConst().UiFiles.end()) {
       UicMergeOptions(allOpts, optionIt->second.Options,
-                      (BaseConst().QtVersionMajor == 5));
+                      (BaseConst().QtVersion.Major == 5));
     }
     cm::append(cmd, allOpts);
   }
@@ -2082,7 +2130,8 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
 {
   // -- Required settings
   if (!info.GetBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) ||
-      !info.GetUInt("QT_VERSION_MAJOR", BaseConst_.QtVersionMajor, true) ||
+      !info.GetUInt("QT_VERSION_MAJOR", BaseConst_.QtVersion.Major, true) ||
+      !info.GetUInt("QT_VERSION_MINOR", BaseConst_.QtVersion.Minor, true) ||
       !info.GetUInt("PARALLEL", BaseConst_.ThreadCount, false) ||
       !info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) ||
       !info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir,
@@ -2143,8 +2192,10 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
       MocConst_.MacroFilters.emplace_back(
         item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
     }
-    // Dependency filters
-    {
+    // Can moc output dependencies or do we need to setup dependency filters?
+    if (BaseConst_.QtVersion >= IntegerVersion(5, 15)) {
+      MocConst_.CanOutputDependencies = true;
+    } else {
       Json::Value const& val = info.GetValue("MOC_DEPEND_FILTERS");
       if (!val.isArray()) {
         return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array.");
@@ -2660,6 +2711,19 @@ bool cmQtAutoMocUicT::CreateDirectories()
   return true;
 }
 
+std::vector<std::string> cmQtAutoMocUicT::dependenciesFromDepFile(
+  const char* filePath)
+{
+  cmGccDepfileContent content = cmReadGccDepfile(filePath);
+  if (content.empty()) {
+    return {};
+  }
+
+  // Moc outputs a depfile with exactly one rule.
+  // Discard the rule and return the dependencies.
+  return content.front().paths;
+}
+
 void cmQtAutoMocUicT::Abort(bool error)
 {
   if (error) {

+ 1 - 1
Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp

@@ -10,7 +10,7 @@ class StyleA : public QStylePlugin
   Q_OBJECT
   // Json file in source local directory
   Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json")
-  A_CUSTOM_MACRO(SomeArg, "StyleA_Custom.json", AnotherArg)
+  A_CUSTOM_MACRO(org.styles.A, "StyleA_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 1
Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp

@@ -10,7 +10,7 @@ class StyleB : public QStylePlugin
   Q_OBJECT
   // Json file in source local subdirectory
   Q_PLUGIN_METADATA(IID "org.styles.B" FILE "jsonIn/StyleB.json")
-  A_CUSTOM_MACRO(SomeArg, "jsonIn/StyleB_Custom.json", AnotherArg)
+  A_CUSTOM_MACRO(org.styles.B, "jsonIn/StyleB_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 1
Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp

@@ -10,7 +10,7 @@ class StyleC : public QStylePlugin
   Q_OBJECT
   // Json file in global root directory
   Q_PLUGIN_METADATA(IID "org.styles.C" FILE "StyleC.json")
-  A_CUSTOM_MACRO(SomeArg, "StyleC_Custom.json", AnotherArg)
+  A_CUSTOM_MACRO(org.styles.C, "StyleC_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 1
Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp

@@ -10,7 +10,7 @@ class StyleD : public QStylePlugin
   Q_OBJECT
   // Json file in global sub director
   Q_PLUGIN_METADATA(IID "org.styles.D" FILE "sub/StyleD.json")
-  A_CUSTOM_MACRO(SomeArg, "sub/StyleD_Custom.json", AnotherArg)
+  A_CUSTOM_MACRO(org.styles.D, "sub/StyleD_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 1
Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp

@@ -10,7 +10,7 @@ class StyleE : public QStylePlugin
   Q_OBJECT
   // Json files in global root directory
   Q_PLUGIN_METADATA(IID "org.styles.E" FILE "StyleE.json")
-  A_CUSTOM_MACRO(SomeArg, "StyleE_Custom.json", AnotherArg)
+  A_CUSTOM_MACRO(org.styles.E, "StyleE_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 2 - 2
Tests/QtAutogen/RerunMocPlugin/MocPlugin/UtilityMacros.hpp

@@ -1,7 +1,7 @@
 #ifndef UTILITYMACROS_HPP
 #define UTILITYMACROS_HPP
 
-// Empty test macro definition
-#define A_CUSTOM_MACRO(name, jsonFile, pluginRegistrations)
+#define A_CUSTOM_MACRO(url, jsonFile, pluginRegistrations)                    \
+  Q_PLUGIN_METADATA(IID #url FILE jsonFile)
 
 #endif

+ 8 - 0
Tests/RunCMake/NinjaMultiConfig/Qt5.cmake

@@ -11,8 +11,16 @@ target_link_libraries(exe PRIVATE Qt5::Core)
 include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake)
 generate_output_files(exe)
 
+set(moc_writes_depfiles 0)
+if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
+  set(moc_writes_depfiles 1)
+endif()
+
 set(autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation.cpp")
 foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
   list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp")
+  if(moc_writes_depfiles)
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d")
+  endif()
 endforeach()
 file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n")