diff options
author | Brad King <brad.king@kitware.com> | 2024-05-16 19:47:25 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2024-05-20 23:12:19 (GMT) |
commit | ed983b1a5d1a32d7d469e3524e24c92e9bc44c14 (patch) | |
tree | 085a3fc604d6b6ba78ff6777ac06e072e73947d1 | |
parent | ba5a1be70a6670fe3bedea8e67f7523b7151aead (diff) | |
download | CMake-ed983b1a5d1a32d7d469e3524e24c92e9bc44c14.zip CMake-ed983b1a5d1a32d7d469e3524e24c92e9bc44c14.tar.gz CMake-ed983b1a5d1a32d7d469e3524e24c92e9bc44c14.tar.bz2 |
cmTargetTraceDependencies: Factor out of cmGeneratorTarget
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 248 | ||||
-rw-r--r-- | Source/cmTargetTraceDependencies.cxx | 239 | ||||
-rw-r--r-- | Source/cmTargetTraceDependencies.h | 46 | ||||
-rwxr-xr-x | bootstrap | 1 |
5 files changed, 289 insertions, 247 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 9953caf..dff685d 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -437,6 +437,8 @@ add_library( cmTargetPropertyComputer.cxx cmTargetPropertyComputer.h cmTargetExport.h + cmTargetTraceDependencies.cxx + cmTargetTraceDependencies.h cmTest.cxx cmTest.h cmTestGenerator.cxx diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 8d1cd84..b8a9136 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -12,7 +12,6 @@ #include <cstdlib> #include <cstring> #include <iterator> -#include <queue> #include <sstream> #include <type_traits> #include <unordered_set> @@ -29,7 +28,6 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" #include "cmCryptoHash.h" -#include "cmCustomCommandGenerator.h" #include "cmCxxModuleUsageEffects.h" #include "cmEvaluatedTargetProperty.h" #include "cmExperimental.h" @@ -61,6 +59,7 @@ #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" #include "cmTargetPropertyComputer.h" +#include "cmTargetTraceDependencies.h" #include "cmake.h" namespace { @@ -3167,251 +3166,6 @@ cmGeneratorTarget::GetLinkImplementationClosure( return tgts; } -class cmTargetTraceDependencies -{ -public: - cmTargetTraceDependencies(cmGeneratorTarget* target); - void Trace(); - -private: - cmGeneratorTarget* GeneratorTarget; - cmMakefile* Makefile; - cmLocalGenerator* LocalGenerator; - cmGlobalGenerator const* GlobalGenerator; - using SourceEntry = cmGeneratorTarget::SourceEntry; - SourceEntry* CurrentEntry; - std::queue<cmSourceFile*> SourceQueue; - std::set<cmSourceFile*> SourcesQueued; - using NameMapType = std::map<std::string, cmSourcesWithOutput>; - NameMapType NameMap; - std::vector<std::string> NewSources; - - void QueueSource(cmSourceFile* sf); - void FollowName(std::string const& name); - void FollowNames(std::vector<std::string> const& names); - bool IsUtility(std::string const& dep); - void CheckCustomCommand(cmCustomCommand const& cc); - void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); -}; - -cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) - : GeneratorTarget(target) -{ - // Convenience. - this->Makefile = target->Target->GetMakefile(); - this->LocalGenerator = target->GetLocalGenerator(); - this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); - this->CurrentEntry = nullptr; - - // Queue all the source files already specified for the target. - std::set<cmSourceFile*> emitted; - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& c : configs) { - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles(sources, c); - for (cmSourceFile* sf : sources) { - const std::set<cmGeneratorTarget const*> tgts = - this->GlobalGenerator->GetFilenameTargetDepends(sf); - if (cm::contains(tgts, this->GeneratorTarget)) { - std::ostringstream e; - e << "Evaluation output file\n \"" << sf->ResolveFullPath() - << "\"\ndepends on the sources of a target it is used in. This " - "is a dependency loop and is not allowed."; - this->GeneratorTarget->LocalGenerator->IssueMessage( - MessageType::FATAL_ERROR, e.str()); - return; - } - if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - } - } - } - - // Queue pre-build, pre-link, and post-build rule dependencies. - this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); -} - -void cmTargetTraceDependencies::Trace() -{ - // Process one dependency at a time until the queue is empty. - while (!this->SourceQueue.empty()) { - // Get the next source from the queue. - cmSourceFile* sf = this->SourceQueue.front(); - this->SourceQueue.pop(); - this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; - - // Queue dependencies added explicitly by the user. - if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { - cmList objDeps{ *additionalDeps }; - for (auto& objDep : objDeps) { - if (cmSystemTools::FileIsFullPath(objDep)) { - objDep = cmSystemTools::CollapseFullPath(objDep); - } - } - this->FollowNames(objDeps); - } - - // Queue the source needed to generate this file, if any. - this->FollowName(sf->ResolveFullPath()); - - // Queue dependencies added programmatically by commands. - this->FollowNames(sf->GetDepends()); - - // Queue custom command dependencies. - if (cmCustomCommand const* cc = sf->GetCustomCommand()) { - this->CheckCustomCommand(*cc); - } - } - this->CurrentEntry = nullptr; - - this->GeneratorTarget->AddTracedSources(this->NewSources); -} - -void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) -{ - if (this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - - // Make sure this file is in the target at the end. - this->NewSources.push_back(sf->ResolveFullPath()); - } -} - -void cmTargetTraceDependencies::FollowName(std::string const& name) -{ - // Use lower bound with key comparison to not repeat the search for the - // insert position if the name could not be found (which is the common case). - auto i = this->NameMap.lower_bound(name); - if (i == this->NameMap.end() || i->first != name) { - // Check if we know how to generate this file. - cmSourcesWithOutput sources = - this->LocalGenerator->GetSourcesWithOutput(name); - // If we failed to find a target or source and we have a relative path, it - // might be a valid source if made relative to the current binary - // directory. - if (!sources.Target && !sources.Source && - !cmSystemTools::FileIsFullPath(name)) { - auto fullname = - cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); - fullname = cmSystemTools::CollapseFullPath( - fullname, this->Makefile->GetHomeOutputDirectory()); - sources = this->LocalGenerator->GetSourcesWithOutput(fullname); - } - i = this->NameMap.emplace_hint(i, name, sources); - } - if (cmTarget* t = i->second.Target) { - // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or - // POST_BUILD command. - this->GeneratorTarget->Target->AddUtility(t->GetName(), false); - } - if (cmSourceFile* sf = i->second.Source) { - // For now only follow the dependency if the source file is not a - // byproduct. Semantics of byproducts in a non-Ninja context will have to - // be defined first. - if (!i->second.SourceIsByproduct) { - // Record the dependency we just followed. - if (this->CurrentEntry) { - this->CurrentEntry->Depends.push_back(sf); - } - this->QueueSource(sf); - } - } -} - -void cmTargetTraceDependencies::FollowNames( - std::vector<std::string> const& names) -{ - for (std::string const& name : names) { - this->FollowName(name); - } -} - -bool cmTargetTraceDependencies::IsUtility(std::string const& dep) -{ - // Dependencies on targets (utilities) are supposed to be named by - // just the target name. However for compatibility we support - // naming the output file generated by the target (assuming there is - // no output-name property which old code would not have set). In - // that case the target name will be the file basename of the - // dependency. - std::string util = cmSystemTools::GetFilenameName(dep); - if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { - util = cmSystemTools::GetFilenameWithoutLastExtension(util); - } - - // Check for a target with this name. - if (cmGeneratorTarget* t = - this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( - util)) { - // If we find the target and the dep was given as a full path, - // then make sure it was not a full path to something else, and - // the fact that the name matched a target was just a coincidence. - if (cmSystemTools::FileIsFullPath(dep)) { - if (t->GetType() >= cmStateEnums::EXECUTABLE && - t->GetType() <= cmStateEnums::MODULE_LIBRARY) { - // This is really only for compatibility so we do not need to - // worry about configuration names and output names. - std::string tLocation = t->GetLocationForBuild(); - tLocation = cmSystemTools::GetFilenamePath(tLocation); - std::string depLocation = cmSystemTools::GetFilenamePath(dep); - depLocation = cmSystemTools::CollapseFullPath(depLocation); - tLocation = cmSystemTools::CollapseFullPath(tLocation); - if (depLocation == tLocation) { - this->GeneratorTarget->Target->AddUtility(util, false); - return true; - } - } - } else { - // The original name of the dependency was not a full path. It - // must name a target, so add the target-level dependency. - this->GeneratorTarget->Target->AddUtility(util, true); - return true; - } - } - - // The dependency does not name a target built in this project. - return false; -} - -void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) -{ - // Collect dependencies referenced by all configurations. - std::set<std::string> depends; - for (std::string const& config : - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { - for (cmCustomCommandGenerator const& ccg : - this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { - // Collect target-level dependencies referenced in command lines. - for (auto const& util : ccg.GetUtilities()) { - this->GeneratorTarget->Target->AddUtility(util); - } - - // Collect file-level dependencies referenced in DEPENDS. - depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); - } - } - - // Queue file-level dependencies. - for (std::string const& dep : depends) { - if (!this->IsUtility(dep)) { - // The dependency does not name a target and may be a file we - // know how to generate. Queue it. - this->FollowName(dep); - } - } -} - -void cmTargetTraceDependencies::CheckCustomCommands( - const std::vector<cmCustomCommand>& commands) -{ - for (cmCustomCommand const& command : commands) { - this->CheckCustomCommand(command); - } -} - void cmGeneratorTarget::TraceDependencies() { // CMake-generated targets have no dependencies to trace. Normally tracing diff --git a/Source/cmTargetTraceDependencies.cxx b/Source/cmTargetTraceDependencies.cxx new file mode 100644 index 0000000..cc91a42 --- /dev/null +++ b/Source/cmTargetTraceDependencies.cxx @@ -0,0 +1,239 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTargetTraceDependencies.h" + +#include <sstream> +#include <utility> + +#include <cmext/algorithm> + +#include "cmCustomCommandGenerator.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) + : GeneratorTarget(target) +{ + // Convenience. + this->Makefile = target->Target->GetMakefile(); + this->LocalGenerator = target->GetLocalGenerator(); + this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); + this->CurrentEntry = nullptr; + + // Queue all the source files already specified for the target. + std::set<cmSourceFile*> emitted; + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& c : configs) { + std::vector<cmSourceFile*> sources; + this->GeneratorTarget->GetSourceFiles(sources, c); + for (cmSourceFile* sf : sources) { + const std::set<cmGeneratorTarget const*> tgts = + this->GlobalGenerator->GetFilenameTargetDepends(sf); + if (cm::contains(tgts, this->GeneratorTarget)) { + std::ostringstream e; + e << "Evaluation output file\n \"" << sf->ResolveFullPath() + << "\"\ndepends on the sources of a target it is used in. This " + "is a dependency loop and is not allowed."; + this->GeneratorTarget->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, e.str()); + return; + } + if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + } + } + } + + // Queue pre-build, pre-link, and post-build rule dependencies. + this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); +} + +void cmTargetTraceDependencies::Trace() +{ + // Process one dependency at a time until the queue is empty. + while (!this->SourceQueue.empty()) { + // Get the next source from the queue. + cmSourceFile* sf = this->SourceQueue.front(); + this->SourceQueue.pop(); + this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; + + // Queue dependencies added explicitly by the user. + if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { + cmList objDeps{ *additionalDeps }; + for (auto& objDep : objDeps) { + if (cmSystemTools::FileIsFullPath(objDep)) { + objDep = cmSystemTools::CollapseFullPath(objDep); + } + } + this->FollowNames(objDeps); + } + + // Queue the source needed to generate this file, if any. + this->FollowName(sf->ResolveFullPath()); + + // Queue dependencies added programmatically by commands. + this->FollowNames(sf->GetDepends()); + + // Queue custom command dependencies. + if (cmCustomCommand const* cc = sf->GetCustomCommand()) { + this->CheckCustomCommand(*cc); + } + } + this->CurrentEntry = nullptr; + + this->GeneratorTarget->AddTracedSources(this->NewSources); +} + +void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) +{ + if (this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + + // Make sure this file is in the target at the end. + this->NewSources.push_back(sf->ResolveFullPath()); + } +} + +void cmTargetTraceDependencies::FollowName(std::string const& name) +{ + // Use lower bound with key comparison to not repeat the search for the + // insert position if the name could not be found (which is the common case). + auto i = this->NameMap.lower_bound(name); + if (i == this->NameMap.end() || i->first != name) { + // Check if we know how to generate this file. + cmSourcesWithOutput sources = + this->LocalGenerator->GetSourcesWithOutput(name); + // If we failed to find a target or source and we have a relative path, it + // might be a valid source if made relative to the current binary + // directory. + if (!sources.Target && !sources.Source && + !cmSystemTools::FileIsFullPath(name)) { + auto fullname = + cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); + fullname = cmSystemTools::CollapseFullPath( + fullname, this->Makefile->GetHomeOutputDirectory()); + sources = this->LocalGenerator->GetSourcesWithOutput(fullname); + } + i = this->NameMap.emplace_hint(i, name, sources); + } + if (cmTarget* t = i->second.Target) { + // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or + // POST_BUILD command. + this->GeneratorTarget->Target->AddUtility(t->GetName(), false); + } + if (cmSourceFile* sf = i->second.Source) { + // For now only follow the dependency if the source file is not a + // byproduct. Semantics of byproducts in a non-Ninja context will have to + // be defined first. + if (!i->second.SourceIsByproduct) { + // Record the dependency we just followed. + if (this->CurrentEntry) { + this->CurrentEntry->Depends.push_back(sf); + } + this->QueueSource(sf); + } + } +} + +void cmTargetTraceDependencies::FollowNames( + std::vector<std::string> const& names) +{ + for (std::string const& name : names) { + this->FollowName(name); + } +} + +bool cmTargetTraceDependencies::IsUtility(std::string const& dep) +{ + // Dependencies on targets (utilities) are supposed to be named by + // just the target name. However for compatibility we support + // naming the output file generated by the target (assuming there is + // no output-name property which old code would not have set). In + // that case the target name will be the file basename of the + // dependency. + std::string util = cmSystemTools::GetFilenameName(dep); + if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { + util = cmSystemTools::GetFilenameWithoutLastExtension(util); + } + + // Check for a target with this name. + if (cmGeneratorTarget* t = + this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( + util)) { + // If we find the target and the dep was given as a full path, + // then make sure it was not a full path to something else, and + // the fact that the name matched a target was just a coincidence. + if (cmSystemTools::FileIsFullPath(dep)) { + if (t->GetType() >= cmStateEnums::EXECUTABLE && + t->GetType() <= cmStateEnums::MODULE_LIBRARY) { + // This is really only for compatibility so we do not need to + // worry about configuration names and output names. + std::string tLocation = t->GetLocationForBuild(); + tLocation = cmSystemTools::GetFilenamePath(tLocation); + std::string depLocation = cmSystemTools::GetFilenamePath(dep); + depLocation = cmSystemTools::CollapseFullPath(depLocation); + tLocation = cmSystemTools::CollapseFullPath(tLocation); + if (depLocation == tLocation) { + this->GeneratorTarget->Target->AddUtility(util, false); + return true; + } + } + } else { + // The original name of the dependency was not a full path. It + // must name a target, so add the target-level dependency. + this->GeneratorTarget->Target->AddUtility(util, true); + return true; + } + } + + // The dependency does not name a target built in this project. + return false; +} + +void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) +{ + // Collect dependencies referenced by all configurations. + std::set<std::string> depends; + for (std::string const& config : + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + for (cmCustomCommandGenerator const& ccg : + this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { + // Collect target-level dependencies referenced in command lines. + for (auto const& util : ccg.GetUtilities()) { + this->GeneratorTarget->Target->AddUtility(util); + } + + // Collect file-level dependencies referenced in DEPENDS. + depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); + } + } + + // Queue file-level dependencies. + for (std::string const& dep : depends) { + if (!this->IsUtility(dep)) { + // The dependency does not name a target and may be a file we + // know how to generate. Queue it. + this->FollowName(dep); + } + } +} + +void cmTargetTraceDependencies::CheckCustomCommands( + const std::vector<cmCustomCommand>& commands) +{ + for (cmCustomCommand const& command : commands) { + this->CheckCustomCommand(command); + } +} diff --git a/Source/cmTargetTraceDependencies.h b/Source/cmTargetTraceDependencies.h new file mode 100644 index 0000000..5bbe8df --- /dev/null +++ b/Source/cmTargetTraceDependencies.h @@ -0,0 +1,46 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <queue> +#include <set> +#include <string> +#include <vector> + +#include "cmGeneratorTarget.h" +#include "cmLocalGenerator.h" + +class cmGlobalGenerator; +class cmSourceFile; +class cmCustomCommand; +class cmMakefile; + +class cmTargetTraceDependencies +{ +public: + cmTargetTraceDependencies(cmGeneratorTarget* target); + void Trace(); + +private: + cmGeneratorTarget* GeneratorTarget; + cmMakefile* Makefile; + cmLocalGenerator* LocalGenerator; + cmGlobalGenerator const* GlobalGenerator; + using SourceEntry = cmGeneratorTarget::SourceEntry; + SourceEntry* CurrentEntry; + std::queue<cmSourceFile*> SourceQueue; + std::set<cmSourceFile*> SourcesQueued; + using NameMapType = std::map<std::string, cmSourcesWithOutput>; + NameMapType NameMap; + std::vector<std::string> NewSources; + + void QueueSource(cmSourceFile* sf); + void FollowName(std::string const& name); + void FollowNames(std::vector<std::string> const& names); + bool IsUtility(std::string const& dep); + void CheckCustomCommand(cmCustomCommand const& cc); + void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); +}; @@ -492,6 +492,7 @@ CMAKE_CXX_SOURCES="\ cmTargetPropCommandBase \ cmTargetPropertyComputer \ cmTargetSourcesCommand \ + cmTargetTraceDependencies \ cmTest \ cmTestGenerator \ cmTimestamp \ |