summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/cmQtAutoGenInitializer.cxx54
-rw-r--r--Source/cmQtAutoGenInitializer.h2
-rw-r--r--Source/cmQtAutoMocUic.cxx116
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/Qt5.cmake4
4 files changed, 170 insertions, 6 deletions
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index d9b0aff..4a26714 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -1172,13 +1172,51 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
}
}
+ std::vector<std::string> dependencies(
+ this->AutogenTarget.DependFiles.begin(),
+ this->AutogenTarget.DependFiles.end());
+
+ const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) &&
+ this->GlobalGen->GetName().find("Ninja") != std::string::npos;
+ if (useNinjaDepfile) {
+ // Create a custom command that generates a timestamp file and
+ // has a depfile assigned. The depfile is created by JobDepFilesMergeT.
+
+ // Add additional autogen target dependencies
+ for (const cmTarget* t : this->AutogenTarget.DependTargets) {
+ dependencies.push_back(t->GetName());
+ }
+ const char timestampFileName[] = "timestamp";
+ const std::string outputFile =
+ cmStrCat(this->Dir.Build, "/", timestampFileName);
+ this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
+ this->AutogenTarget.DepFileRuleName =
+ cmStrCat(this->GenTarget->GetName(), "_autogen/", timestampFileName);
+ commandLines.push_back(cmMakeCommandLine(
+ { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
+
+ this->AddGeneratedSource(outputFile, this->Moc);
+ const std::string no_main_dependency;
+ this->LocalGen->AddCustomCommandToOutput(
+ outputFile, dependencies, no_main_dependency, commandLines,
+ autogenComment.c_str(), this->Dir.Work.c_str(), /*replace=*/false,
+ /*escapeOldStyle=*/false,
+ /*uses_terminal=*/false,
+ /*command_expand_lists=*/false, this->AutogenTarget.DepFile);
+
+ // Alter variables for the autogen target which now merely wraps the
+ // custom command
+ dependencies.clear();
+ dependencies.push_back(outputFile);
+ commandLines.clear();
+ autogenComment.clear();
+ }
+
// Create autogen target
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
/*byproducts=*/autogenProvides,
- std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
- this->AutogenTarget.DependFiles.end()),
- commandLines, false, autogenComment.c_str());
+ /*depends=*/dependencies, commandLines, false, autogenComment.c_str());
// Create autogen generator target
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
@@ -1189,9 +1227,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
autogenTarget->AddUtility(depName.Value, this->Makefile);
}
}
- // Add additional autogen target dependencies to autogen target
- for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
- autogenTarget->AddUtility(depTarget->GetName(), this->Makefile);
+ if (!useNinjaDepfile) {
+ // Add additional autogen target dependencies to autogen target
+ for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
+ autogenTarget->AddUtility(depTarget->GetName(), this->Makefile);
+ }
}
// Set FOLDER property in autogen target
@@ -1416,6 +1456,8 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
+ info.Set("DEP_FILE", this->AutogenTarget.DepFile);
+ info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
info.SetArray("HEADER_EXTENSIONS",
this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
info.SetArrayArray(
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index 8cedf14..48ec1a0 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -191,6 +191,8 @@ private:
bool DependOrigin = false;
std::set<std::string> DependFiles;
std::set<cmTarget*> DependTargets;
+ std::string DepFile;
+ std::string DepFileRuleName;
// Sources to process
std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
std::unordered_map<cmSourceFile*, MUFileHandle> Sources;
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 82464a7..893bd6b 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -181,6 +181,8 @@ public:
std::string CMakeExecutable;
cmFileTime CMakeExecutableTime;
std::string ParseCacheFile;
+ std::string DepFile;
+ std::string DepFileRuleName;
std::vector<std::string> HeaderExtensions;
};
@@ -516,6 +518,12 @@ public:
void Process() override;
};
+ class JobDepFilesMergeT : public JobFenceT
+ {
+ private:
+ void Process() override;
+ };
+
/** @brief The last job. */
class JobFinishT : public JobFenceT
{
@@ -1926,6 +1934,11 @@ void cmQtAutoMocUicT::JobProbeDepsFinishT::Process()
Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
}
+ if (!BaseConst().DepFile.empty()) {
+ // Add job to merge dep files
+ Gen()->WorkerPool().EmplaceJob<JobDepFilesMergeT>();
+ }
+
// Add finish job
Gen()->WorkerPool().EmplaceJob<JobFinishT>();
}
@@ -2115,6 +2128,106 @@ void cmQtAutoMocUicT::JobMocsCompilationT::Process()
}
}
+/*
+ * Escapes paths for Ninja depfiles.
+ * This is a re-implementation of what moc does when writing depfiles.
+ */
+std::string escapeDependencyPath(cm::string_view path)
+{
+ std::string escapedPath;
+ escapedPath.reserve(path.size());
+ const size_t s = path.size();
+ int backslashCount = 0;
+ for (size_t i = 0; i < s; ++i) {
+ if (path[i] == '\\') {
+ ++backslashCount;
+ } else {
+ if (path[i] == '$') {
+ escapedPath.push_back('$');
+ } else if (path[i] == '#') {
+ escapedPath.push_back('\\');
+ } else if (path[i] == ' ') {
+ // Double the amount of written backslashes,
+ // and add one more to escape the space.
+ while (backslashCount-- >= 0) {
+ escapedPath.push_back('\\');
+ }
+ }
+ backslashCount = 0;
+ }
+ escapedPath.push_back(path[i]);
+ }
+ return escapedPath;
+}
+
+void cmQtAutoMocUicT::JobDepFilesMergeT::Process()
+{
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Merging MOC dependencies");
+ }
+ auto processDepFile =
+ [](const std::string& mocOutputFile) -> std::vector<std::string> {
+ std::string f = mocOutputFile + ".d";
+ if (!cmSystemTools::FileExists(f)) {
+ return {};
+ }
+ return dependenciesFromDepFile(f.c_str());
+ };
+
+ std::vector<std::string> dependencies;
+ ParseCacheT& parseCache = BaseEval().ParseCache;
+ auto processMappingEntry = [&](const MappingMapT::value_type& m) {
+ auto cacheEntry = parseCache.GetOrInsert(m.first);
+ if (cacheEntry.first->Moc.Depends.empty()) {
+ cacheEntry.first->Moc.Depends = processDepFile(m.second->OutputFile);
+ }
+ dependencies.insert(dependencies.end(),
+ cacheEntry.first->Moc.Depends.begin(),
+ cacheEntry.first->Moc.Depends.end());
+ };
+
+ std::for_each(MocEval().HeaderMappings.begin(),
+ MocEval().HeaderMappings.end(), processMappingEntry);
+ std::for_each(MocEval().SourceMappings.begin(),
+ MocEval().SourceMappings.end(), processMappingEntry);
+
+ // Remove duplicates to make the depfile smaller
+ std::sort(dependencies.begin(), dependencies.end());
+ dependencies.erase(std::unique(dependencies.begin(), dependencies.end()),
+ dependencies.end());
+
+ // Add form files
+ for (const auto& uif : UicEval().UiFiles) {
+ dependencies.push_back(uif.first);
+ }
+
+ // Write the file
+ cmsys::ofstream ofs;
+ ofs.open(BaseConst().DepFile.c_str(),
+ (std::ios::out | std::ios::binary | std::ios::trunc));
+ if (!ofs) {
+ LogError(GenT::GEN,
+ cmStrCat("Cannot open ", MessagePath(BaseConst().DepFile),
+ " for writing."));
+ return;
+ }
+ ofs << BaseConst().DepFileRuleName << ": \\" << std::endl;
+ for (const std::string& file : dependencies) {
+ ofs << '\t' << escapeDependencyPath(file) << " \\" << std::endl;
+ if (!ofs.good()) {
+ LogError(GenT::GEN,
+ cmStrCat("Writing depfile", MessagePath(BaseConst().DepFile),
+ " failed."));
+ return;
+ }
+ }
+
+ // Add the CMake executable to re-new cache data if necessary.
+ // Also, this is the last entry, so don't add a backslash.
+ ofs << '\t' << escapeDependencyPath(BaseConst().CMakeExecutable)
+ << std::endl;
+}
+
void cmQtAutoMocUicT::JobFinishT::Process()
{
Gen()->AbortSuccess();
@@ -2139,6 +2252,9 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
!info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
!info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile,
true) ||
+ !info.GetString("DEP_FILE", BaseConst_.DepFile, false) ||
+ !info.GetString("DEP_FILE_RULE_NAME", BaseConst_.DepFileRuleName,
+ false) ||
!info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
!info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
!info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
diff --git a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake b/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake
index dfc0f50..3a1c7f5 100644
--- a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake
@@ -17,6 +17,10 @@ if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
endif()
set(autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation.cpp")
+if(moc_writes_depfiles)
+ list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps")
+ list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp")
+endif()
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)