From f765fdea032b820f82789485172616c1456fb815 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 14 Jan 2020 09:36:21 +0100 Subject: [PATCH] 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 --- Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst | 3 + Source/cmQtAutoGenInitializer.cxx | 1 + Source/cmQtAutoMocUic.cxx | 92 ++++++++++++++++--- .../RerunMocPlugin/MocPlugin/StyleA.hpp | 2 +- .../RerunMocPlugin/MocPlugin/StyleB.hpp | 2 +- .../RerunMocPlugin/MocPlugin/StyleC.hpp | 2 +- .../RerunMocPlugin/MocPlugin/StyleD.hpp | 2 +- .../MocPlugin/StyleEInclude.hpp | 2 +- .../MocPlugin/UtilityMacros.hpp | 4 +- Tests/RunCMake/NinjaMultiConfig/Qt5.cmake | 8 ++ 10 files changed, 97 insertions(+), 21 deletions(-) diff --git a/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst b/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst index 69957bf182..6eda26c4f6 100644 --- a/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst +++ b/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. diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index ebb522b0a9..d9b0aff324 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/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); diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index fa5129d589..82464a7f45 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/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 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 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(mapping, std::move(reason)); + Gen()->WorkerPool().EmplaceJob( + 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; + // 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 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) { diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp index e1fdf0bbd1..198ae983c0 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleA.hpp +++ b/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); }; diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp index 7550d0c3b6..8ce8d776e8 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleB.hpp +++ b/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); }; diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp index ec71becdac..53171e32cb 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleC.hpp +++ b/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); }; diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp index 3c093b914b..29674f9836 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleD.hpp +++ b/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); }; diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp index 5f10fb4a07..731822045c 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/StyleEInclude.hpp +++ b/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); }; diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/UtilityMacros.hpp b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/UtilityMacros.hpp index 53a4284ee9..2f558a8eb3 100644 --- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/UtilityMacros.hpp +++ b/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 diff --git a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake b/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake index 6a80b1d1c6..dfc0f50314 100644 --- a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake +++ b/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")