mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-16 05:26:58 +08:00
AutoGen: Use depfiles for the XXX_autogen ninja targets
The XXX_autogen targets are implemented as utility commands, which means they always run, even if there weren't any changes. For the Ninja generator and Qt >= 5.15 we're taking a different approach: This commit adds custom commands that create XXX_autogen/timestamp files. Those custom commands have a depfile assigned that is generated from the depfiles that were created by moc. The XXX_autogen targets merely wrap the XXX_autogen/timestamp custom commands. Fixes: #18749
This commit is contained in:

committed by
Brad King

parent
f765fdea03
commit
aebfbcaa46
@@ -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
|
// Create autogen target
|
||||||
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
|
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
|
||||||
this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
|
this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
|
||||||
/*byproducts=*/autogenProvides,
|
/*byproducts=*/autogenProvides,
|
||||||
std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
|
/*depends=*/dependencies, commandLines, false, autogenComment.c_str());
|
||||||
this->AutogenTarget.DependFiles.end()),
|
|
||||||
commandLines, false, autogenComment.c_str());
|
|
||||||
// Create autogen generator target
|
// Create autogen generator target
|
||||||
this->LocalGen->AddGeneratorTarget(
|
this->LocalGen->AddGeneratorTarget(
|
||||||
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
|
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
|
||||||
@@ -1189,9 +1227,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
|
|||||||
autogenTarget->AddUtility(depName.Value, this->Makefile);
|
autogenTarget->AddUtility(depName.Value, this->Makefile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add additional autogen target dependencies to autogen target
|
if (!useNinjaDepfile) {
|
||||||
for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
|
// Add additional autogen target dependencies to autogen target
|
||||||
autogenTarget->AddUtility(depTarget->GetName(), this->Makefile);
|
for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
|
||||||
|
autogenTarget->AddUtility(depTarget->GetName(), this->Makefile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set FOLDER property in autogen target
|
// Set FOLDER property in autogen target
|
||||||
@@ -1416,6 +1456,8 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
|
|||||||
info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
|
info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
|
||||||
info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
|
info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
|
||||||
info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
|
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",
|
info.SetArray("HEADER_EXTENSIONS",
|
||||||
this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
|
this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
|
||||||
info.SetArrayArray(
|
info.SetArrayArray(
|
||||||
|
@@ -191,6 +191,8 @@ private:
|
|||||||
bool DependOrigin = false;
|
bool DependOrigin = false;
|
||||||
std::set<std::string> DependFiles;
|
std::set<std::string> DependFiles;
|
||||||
std::set<cmTarget*> DependTargets;
|
std::set<cmTarget*> DependTargets;
|
||||||
|
std::string DepFile;
|
||||||
|
std::string DepFileRuleName;
|
||||||
// Sources to process
|
// Sources to process
|
||||||
std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
|
std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
|
||||||
std::unordered_map<cmSourceFile*, MUFileHandle> Sources;
|
std::unordered_map<cmSourceFile*, MUFileHandle> Sources;
|
||||||
|
@@ -181,6 +181,8 @@ public:
|
|||||||
std::string CMakeExecutable;
|
std::string CMakeExecutable;
|
||||||
cmFileTime CMakeExecutableTime;
|
cmFileTime CMakeExecutableTime;
|
||||||
std::string ParseCacheFile;
|
std::string ParseCacheFile;
|
||||||
|
std::string DepFile;
|
||||||
|
std::string DepFileRuleName;
|
||||||
std::vector<std::string> HeaderExtensions;
|
std::vector<std::string> HeaderExtensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -516,6 +518,12 @@ public:
|
|||||||
void Process() override;
|
void Process() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JobDepFilesMergeT : public JobFenceT
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void Process() override;
|
||||||
|
};
|
||||||
|
|
||||||
/** @brief The last job. */
|
/** @brief The last job. */
|
||||||
class JobFinishT : public JobFenceT
|
class JobFinishT : public JobFenceT
|
||||||
{
|
{
|
||||||
@@ -1926,6 +1934,11 @@ void cmQtAutoMocUicT::JobProbeDepsFinishT::Process()
|
|||||||
Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
|
Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!BaseConst().DepFile.empty()) {
|
||||||
|
// Add job to merge dep files
|
||||||
|
Gen()->WorkerPool().EmplaceJob<JobDepFilesMergeT>();
|
||||||
|
}
|
||||||
|
|
||||||
// Add finish job
|
// Add finish job
|
||||||
Gen()->WorkerPool().EmplaceJob<JobFinishT>();
|
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()
|
void cmQtAutoMocUicT::JobFinishT::Process()
|
||||||
{
|
{
|
||||||
Gen()->AbortSuccess();
|
Gen()->AbortSuccess();
|
||||||
@@ -2139,6 +2252,9 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
|
|||||||
!info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
|
!info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
|
||||||
!info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile,
|
!info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile,
|
||||||
true) ||
|
true) ||
|
||||||
|
!info.GetString("DEP_FILE", BaseConst_.DepFile, false) ||
|
||||||
|
!info.GetString("DEP_FILE_RULE_NAME", BaseConst_.DepFileRuleName,
|
||||||
|
false) ||
|
||||||
!info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
|
!info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
|
||||||
!info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
|
!info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
|
||||||
!info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
|
!info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
|
||||||
|
@@ -17,6 +17,10 @@ if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation.cpp")
|
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)
|
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)
|
if(moc_writes_depfiles)
|
||||||
|
Reference in New Issue
Block a user