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
This commit is contained in:
Joerg Bornemann 2020-01-14 09:36:21 +01:00 committed by Brad King
parent f8c505d4b3
commit f765fdea03
10 changed files with 97 additions and 21 deletions

View File

@ -26,6 +26,9 @@ See :prop_tgt:`AUTOGEN_TARGET_DEPENDS` for reference.
By default :prop_tgt:`AUTOMOC_DEPEND_FILTERS` is initialized from By default :prop_tgt:`AUTOMOC_DEPEND_FILTERS` is initialized from
:variable:`CMAKE_AUTOMOC_DEPEND_FILTERS`, which is empty by default. :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 See the :manual:`cmake-qt(7)` manual for more information on using CMake
with Qt. with Qt.

View File

@ -1409,6 +1409,7 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.SetConfig("INCLUDE_DIR", this->Dir.Include); info.SetConfig("INCLUDE_DIR", this->Dir.Include);
info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major); 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_MOC_EXECUTABLE", this->Moc.Executable);
info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable); info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);

View File

@ -25,6 +25,8 @@
#include "cmCryptoHash.h" #include "cmCryptoHash.h"
#include "cmFileTime.h" #include "cmFileTime.h"
#include "cmGccDepfileReader.h"
#include "cmGccDepfileReaderTypes.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmQtAutoGen.h" #include "cmQtAutoGen.h"
#include "cmQtAutoGenerator.h" #include "cmQtAutoGenerator.h"
@ -170,7 +172,7 @@ public:
// -- Attributes // -- Attributes
// - Config // - Config
bool MultiConfig = false; bool MultiConfig = false;
unsigned int QtVersionMajor = 4; IntegerVersion QtVersion = { 4, 0 };
unsigned int ThreadCount = 0; unsigned int ThreadCount = 0;
// - Directories // - Directories
std::string AutogenBuildDir; std::string AutogenBuildDir;
@ -216,6 +218,7 @@ public:
bool SettingsChanged = false; bool SettingsChanged = false;
bool RelaxedMode = false; bool RelaxedMode = false;
bool PathPrefix = false; bool PathPrefix = false;
bool CanOutputDependencies = false;
cmFileTime ExecutableTime; cmFileTime ExecutableTime;
std::string Executable; std::string Executable;
std::string CompFileAbs; std::string CompFileAbs;
@ -485,8 +488,17 @@ public:
class JobCompileMocT : public JobCompileT class JobCompileMocT : public JobCompileT
{ {
public: 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; void Process() override;
protected:
ParseCacheT::FileHandleT CacheEntry;
}; };
/** uic compiles a file. */ /** uic compiles a file. */
@ -546,6 +558,9 @@ private:
void Abort(bool error); void Abort(bool error);
// -- Generation // -- Generation
bool CreateDirectories(); bool CreateDirectories();
// -- Support for depfiles
static std::vector<std::string> dependenciesFromDepFile(
const char* filePath);
private: private:
// -- Settings // -- Settings
@ -951,7 +966,7 @@ void cmQtAutoMocUicT::JobParseT::MocMacro()
void cmQtAutoMocUicT::JobParseT::MocDependecies() void cmQtAutoMocUicT::JobParseT::MocDependecies()
{ {
if (MocConst().DependFilters.empty()) { if (MocConst().DependFilters.empty() || MocConst().CanOutputDependencies) {
return; return;
} }
@ -1674,8 +1689,13 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Generate(MappingHandleT const& mapping,
if (Probe(*mapping, reason.get())) { if (Probe(*mapping, reason.get())) {
// Register the parent directory for creation // Register the parent directory for creation
MocEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile)); 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 // 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 // Check if a moc job for a mocs_compilation.cpp entry was generated
if (compFile) { if (compFile) {
MocEval().CompUpdated = true; MocEval().CompUpdated = true;
@ -1779,6 +1799,14 @@ cmQtAutoMocUicT::JobProbeDepsMocT::FindDependency(
std::string const& sourceDir, std::string const& includeString) const std::string const& sourceDir, std::string const& includeString) const
{ {
using ResPair = std::pair<std::string, cmFileTime>; 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 // Search in vicinity of the source
{ {
ResPair res{ sourceDir + includeString, {} }; ResPair res{ sourceDir + includeString, {} };
@ -1947,6 +1975,9 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
} }
// Add extra options // Add extra options
cm::append(cmd, MocConst().OptionsExtra); cm::append(cmd, MocConst().OptionsExtra);
if (MocConst().CanOutputDependencies) {
cmd.emplace_back("--output-dep-file");
}
// Add output file // Add output file
cmd.emplace_back("-o"); cmd.emplace_back("-o");
cmd.push_back(outputFile); cmd.push_back(outputFile);
@ -1956,12 +1987,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
// Execute moc command // Execute moc command
cmWorkerPool::ProcessResultT result; cmWorkerPool::ProcessResultT result;
if (RunProcess(GenT::MOC, result, cmd, Reason.get())) { 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 {
// Moc command failed // Moc command failed
std::string includers; std::string includers;
if (!Mapping->IncluderFiles.empty()) { if (!Mapping->IncluderFiles.empty()) {
@ -1976,6 +2002,28 @@ void cmQtAutoMocUicT::JobCompileMocT::Process()
MessagePath(outputFile), '\n', includers, MessagePath(outputFile), '\n', includers,
result.ErrorMessage), result.ErrorMessage),
cmd, result.StdOut); 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); auto optionIt = UicConst().UiFiles.find(sourceFile);
if (optionIt != UicConst().UiFiles.end()) { if (optionIt != UicConst().UiFiles.end()) {
UicMergeOptions(allOpts, optionIt->second.Options, UicMergeOptions(allOpts, optionIt->second.Options,
(BaseConst().QtVersionMajor == 5)); (BaseConst().QtVersion.Major == 5));
} }
cm::append(cmd, allOpts); cm::append(cmd, allOpts);
} }
@ -2082,7 +2130,8 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
{ {
// -- Required settings // -- Required settings
if (!info.GetBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) || 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.GetUInt("PARALLEL", BaseConst_.ThreadCount, false) ||
!info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) || !info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) ||
!info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir, !info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir,
@ -2143,8 +2192,10 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
MocConst_.MacroFilters.emplace_back( MocConst_.MacroFilters.emplace_back(
item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); 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"); Json::Value const& val = info.GetValue("MOC_DEPEND_FILTERS");
if (!val.isArray()) { if (!val.isArray()) {
return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array."); return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array.");
@ -2660,6 +2711,19 @@ bool cmQtAutoMocUicT::CreateDirectories()
return true; 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) void cmQtAutoMocUicT::Abort(bool error)
{ {
if (error) { if (error) {

View File

@ -10,7 +10,7 @@ class StyleA : public QStylePlugin
Q_OBJECT Q_OBJECT
// Json file in source local directory // Json file in source local directory
Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json") 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: public:
QStyle* create(const QString& key); QStyle* create(const QString& key);
}; };

View File

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

View File

@ -10,7 +10,7 @@ class StyleC : public QStylePlugin
Q_OBJECT Q_OBJECT
// Json file in global root directory // Json file in global root directory
Q_PLUGIN_METADATA(IID "org.styles.C" FILE "StyleC.json") 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: public:
QStyle* create(const QString& key); QStyle* create(const QString& key);
}; };

View File

@ -10,7 +10,7 @@ class StyleD : public QStylePlugin
Q_OBJECT Q_OBJECT
// Json file in global sub director // Json file in global sub director
Q_PLUGIN_METADATA(IID "org.styles.D" FILE "sub/StyleD.json") 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: public:
QStyle* create(const QString& key); QStyle* create(const QString& key);
}; };

View File

@ -10,7 +10,7 @@ class StyleE : public QStylePlugin
Q_OBJECT Q_OBJECT
// Json files in global root directory // Json files in global root directory
Q_PLUGIN_METADATA(IID "org.styles.E" FILE "StyleE.json") 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: public:
QStyle* create(const QString& key); QStyle* create(const QString& key);
}; };

View File

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

View File

@ -11,8 +11,16 @@ target_link_libraries(exe PRIVATE Qt5::Core)
include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake)
generate_output_files(exe) 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") set(autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation.cpp")
foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES) foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp") 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() endforeach()
file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n") file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n")