cmLocalGenerator: Migrate custom command output lookup from cmMakefile

Since commit 777ceaea94 (cmMakefile: Delay custom command creation,
2019-10-17, v3.17.0-rc1~352^2) we process custom command declarations at
generate time.  Therefore we do not need to look up what source file
holds the custom command producing a given output until generate time.
This commit is contained in:
Brad King 2020-10-07 15:19:58 -04:00
parent 26464da5d3
commit 0090a11a42
6 changed files with 276 additions and 271 deletions

View File

@ -2986,7 +2986,8 @@ void cmTargetTraceDependencies::FollowName(std::string const& name)
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->Makefile->GetSourcesWithOutput(name);
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.
@ -2996,7 +2997,7 @@ void cmTargetTraceDependencies::FollowName(std::string const& name)
cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name);
fullname = cmSystemTools::CollapseFullPath(
fullname, this->Makefile->GetHomeOutputDirectory());
sources = this->Makefile->GetSourcesWithOutput(fullname);
sources = this->LocalGenerator->GetSourcesWithOutput(fullname);
}
i = this->NameMap.emplace_hint(i, name, sources);
}

View File

@ -763,9 +763,9 @@ bool cmGhsMultiTargetGenerator::VisitCustomCommand(
/* set temporary mark; check if revisit*/
if (temp.insert(si).second) {
for (auto& di : si->GetCustomCommand()->GetDepends()) {
cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator()
->GetMakefile()
->GetSourceFileWithOutput(di);
cmSourceFile const* sf =
this->GeneratorTarget->GetLocalGenerator()->GetSourceFileWithOutput(
di);
/* if sf exists then visit */
if (sf && this->VisitCustomCommand(temp, perm, order, sf)) {
return true;

View File

@ -3918,10 +3918,35 @@ cmSourceFile* AddCustomCommand(
cc->SetJobPool(job_pool);
file->SetCustomCommand(std::move(cc));
mf->AddSourceOutputs(file, outputs, byproducts);
lg.AddSourceOutputs(file, outputs, byproducts);
}
return file;
}
bool AnyOutputMatches(const std::string& name,
const std::vector<std::string>& outputs)
{
for (std::string const& output : outputs) {
std::string::size_type pos = output.rfind(name);
// If the output matches exactly
if (pos != std::string::npos && pos == output.size() - name.size() &&
(pos == 0 || output[pos - 1] == '/')) {
return true;
}
}
return false;
}
bool AnyTargetCommandOutputMatches(
const std::string& name, const std::vector<cmCustomCommand>& commands)
{
for (cmCustomCommand const& command : commands) {
if (AnyOutputMatches(name, command.GetByproducts())) {
return true;
}
}
return false;
}
}
namespace detail {
@ -3937,8 +3962,6 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg,
const std::string& job_pool,
bool command_expand_lists, bool stdPipesUTF8)
{
cmMakefile* mf = lg.GetMakefile();
// Always create the byproduct sources and mark them generated.
CreateGeneratedSources(lg, byproducts, origin, lfbt);
@ -3964,7 +3987,7 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg,
break;
}
mf->AddTargetByproducts(target, byproducts);
lg.AddTargetByproducts(target, byproducts);
}
cmSourceFile* AddCustomCommandToOutput(
@ -3996,7 +4019,7 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg,
const cmCustomCommandLines& commandLines)
{
// Lookup an existing command.
if (cmSourceFile* sf = lg.GetMakefile()->GetSourceFileWithOutput(output)) {
if (cmSourceFile* sf = lg.GetSourceFileWithOutput(output)) {
if (cmCustomCommand* cc = sf->GetCustomCommand()) {
cc->AppendCommands(commandLines);
cc->AppendDepends(depends);
@ -4040,7 +4063,7 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
/*replace=*/false, escapeOldStyle, uses_terminal, command_expand_lists,
/*depfile=*/"", job_pool, stdPipesUTF8);
if (rule) {
lg.GetMakefile()->AddTargetByproducts(target, byproducts);
lg.AddTargetByproducts(target, byproducts);
}
if (!force.NameCMP0049.empty()) {
@ -4088,3 +4111,166 @@ std::vector<std::string> ComputeISPCExtraObjects(
return computedObjects;
}
}
cmSourcesWithOutput cmLocalGenerator::GetSourcesWithOutput(
const std::string& name) const
{
// Linear search? Also see GetSourceFileWithOutput for detail.
if (!cmSystemTools::FileIsFullPath(name)) {
cmSourcesWithOutput sources;
sources.Target = this->LinearGetTargetWithOutput(name);
sources.Source = this->LinearGetSourceFileWithOutput(
name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct);
return sources;
}
// Otherwise we use an efficient lookup map.
auto o = this->OutputToSource.find(name);
if (o != this->OutputToSource.end()) {
return o->second.Sources;
}
return {};
}
cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput(
const std::string& name, cmSourceOutputKind kind) const
{
// If the queried path is not absolute we use the backward compatible
// linear-time search for an output with a matching suffix.
if (!cmSystemTools::FileIsFullPath(name)) {
bool byproduct = false;
return this->LinearGetSourceFileWithOutput(name, kind, byproduct);
}
// Otherwise we use an efficient lookup map.
auto o = this->OutputToSource.find(name);
if (o != this->OutputToSource.end() &&
(!o->second.Sources.SourceIsByproduct ||
kind == cmSourceOutputKind::OutputOrByproduct)) {
// Source file could also be null pointer for example if we found the
// byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD
// command of a target, or a not yet created custom command.
return o->second.Sources.Source;
}
return nullptr;
}
void cmLocalGenerator::AddTargetByproducts(
cmTarget* target, const std::vector<std::string>& byproducts)
{
for (std::string const& o : byproducts) {
this->UpdateOutputToSourceMap(o, target);
}
}
void cmLocalGenerator::AddSourceOutputs(
cmSourceFile* source, const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts)
{
for (std::string const& o : outputs) {
this->UpdateOutputToSourceMap(o, source, false);
}
for (std::string const& o : byproducts) {
this->UpdateOutputToSourceMap(o, source, true);
}
}
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct,
cmTarget* target)
{
SourceEntry entry;
entry.Sources.Target = target;
auto pr = this->OutputToSource.emplace(byproduct, entry);
if (!pr.second) {
SourceEntry& current = pr.first->second;
// Has the target already been set?
if (!current.Sources.Target) {
current.Sources.Target = target;
} else {
// Multiple custom commands/targets produce the same output (source file
// or target). See also comment in other UpdateOutputToSourceMap
// overload.
//
// TODO: Warn the user about this case.
}
}
}
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output,
cmSourceFile* source,
bool byproduct)
{
SourceEntry entry;
entry.Sources.Source = source;
entry.Sources.SourceIsByproduct = byproduct;
auto pr = this->OutputToSource.emplace(output, entry);
if (!pr.second) {
SourceEntry& current = pr.first->second;
// Outputs take precedence over byproducts
if (!current.Sources.Source ||
(current.Sources.SourceIsByproduct && !byproduct)) {
current.Sources.Source = source;
current.Sources.SourceIsByproduct = false;
} else {
// Multiple custom commands produce the same output but may
// be attached to a different source file (MAIN_DEPENDENCY).
// LinearGetSourceFileWithOutput would return the first one,
// so keep the mapping for the first one.
//
// TODO: Warn the user about this case. However, the VS 8 generator
// triggers it for separate generate.stamp rules in ZERO_CHECK and
// individual targets.
}
}
}
cmTarget* cmLocalGenerator::LinearGetTargetWithOutput(
const std::string& name) const
{
// We go through the ordered vector of targets to get reproducible results
// should multiple names match.
for (cmTarget* t : this->Makefile->GetOrderedTargets()) {
// Does the output of any command match the source file name?
if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) {
return t;
}
if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) {
return t;
}
if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) {
return t;
}
}
return nullptr;
}
cmSourceFile* cmLocalGenerator::LinearGetSourceFileWithOutput(
const std::string& name, cmSourceOutputKind kind, bool& byproduct) const
{
// Outputs take precedence over byproducts.
byproduct = false;
cmSourceFile* fallback = nullptr;
// Look through all the source files that have custom commands and see if the
// custom command has the passed source file as an output.
for (const auto& src : this->Makefile->GetSourceFiles()) {
// Does this source file have a custom command?
if (src->GetCustomCommand()) {
// Does the output of the custom command match the source file name?
if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) {
// Return the first matching output.
return src.get();
}
if (kind == cmSourceOutputKind::OutputOrByproduct) {
if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) {
// Do not return the source yet as there might be a matching output.
fallback = src.get();
}
}
}
}
// Did we find a byproduct?
byproduct = fallback != nullptr;
return fallback;
}

View File

@ -36,6 +36,24 @@ class cmState;
class cmTarget;
class cmake;
/** Flag if byproducts shall also be considered. */
enum class cmSourceOutputKind
{
OutputOnly,
OutputOrByproduct
};
/** Target and source file which have a specific output. */
struct cmSourcesWithOutput
{
/** Target with byproduct. */
cmTarget* Target = nullptr;
/** Source file with output or byproduct. */
cmSourceFile* Source = nullptr;
bool SourceIsByproduct = false;
};
/** \class cmLocalGenerator
* \brief Create required build files for a directory.
*
@ -337,6 +355,34 @@ public:
bool command_expand_lists = false, const std::string& job_pool = "",
bool stdPipesUTF8 = false);
/**
* Add target byproducts.
*/
void AddTargetByproducts(cmTarget* target,
const std::vector<std::string>& byproducts);
/**
* Add source file outputs.
*/
void AddSourceOutputs(cmSourceFile* source,
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts);
/**
* Return the target if the provided source name is a byproduct of a utility
* target or a PRE_BUILD, PRE_LINK, or POST_BUILD command.
* Return the source file which has the provided source name as output.
*/
cmSourcesWithOutput GetSourcesWithOutput(const std::string& name) const;
/**
* Is there a source file that has the provided source name as an output?
* If so then return it.
*/
cmSourceFile* GetSourceFileWithOutput(
const std::string& name,
cmSourceOutputKind kind = cmSourceOutputKind::OutputOnly) const;
std::string GetProjectName() const;
/** Compute the language used to compile the given source file. */
@ -532,6 +578,33 @@ protected:
bool BackwardsCompatibilityFinal;
private:
/**
* See LinearGetSourceFileWithOutput for background information
*/
cmTarget* LinearGetTargetWithOutput(const std::string& name) const;
/**
* Generalized old version of GetSourceFileWithOutput kept for
* backward-compatibility. It implements a linear search and supports
* relative file paths. It is used as a fall back by GetSourceFileWithOutput
* and GetSourcesWithOutput.
*/
cmSourceFile* LinearGetSourceFileWithOutput(const std::string& name,
cmSourceOutputKind kind,
bool& byproduct) const;
struct SourceEntry
{
cmSourcesWithOutput Sources;
};
// A map for fast output to input look up.
using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>;
OutputToSourceMap OutputToSource;
void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target);
void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
bool byproduct);
void AddSharedFlags(std::string& flags, const std::string& lang,
bool shared);
bool GetShouldUseOldFlags(bool shared, const std::string& lang) const;

View File

@ -2145,191 +2145,6 @@ cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName,
}
namespace {
bool AnyOutputMatches(const std::string& name,
const std::vector<std::string>& outputs)
{
for (std::string const& output : outputs) {
std::string::size_type pos = output.rfind(name);
// If the output matches exactly
if (pos != std::string::npos && pos == output.size() - name.size() &&
(pos == 0 || output[pos - 1] == '/')) {
return true;
}
}
return false;
}
bool AnyTargetCommandOutputMatches(
const std::string& name, const std::vector<cmCustomCommand>& commands)
{
for (cmCustomCommand const& command : commands) {
if (AnyOutputMatches(name, command.GetByproducts())) {
return true;
}
}
return false;
}
}
cmTarget* cmMakefile::LinearGetTargetWithOutput(const std::string& name) const
{
// We go through the ordered vector of targets to get reproducible results
// should multiple names match.
for (cmTarget* t : this->OrderedTargets) {
// Does the output of any command match the source file name?
if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) {
return t;
}
if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) {
return t;
}
if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) {
return t;
}
}
return nullptr;
}
cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput(
const std::string& name, cmSourceOutputKind kind, bool& byproduct) const
{
// Outputs take precedence over byproducts.
byproduct = false;
cmSourceFile* fallback = nullptr;
// Look through all the source files that have custom commands and see if the
// custom command has the passed source file as an output.
for (const auto& src : this->SourceFiles) {
// Does this source file have a custom command?
if (src->GetCustomCommand()) {
// Does the output of the custom command match the source file name?
if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) {
// Return the first matching output.
return src.get();
}
if (kind == cmSourceOutputKind::OutputOrByproduct) {
if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) {
// Do not return the source yet as there might be a matching output.
fallback = src.get();
}
}
}
}
// Did we find a byproduct?
byproduct = fallback != nullptr;
return fallback;
}
cmSourcesWithOutput cmMakefile::GetSourcesWithOutput(
const std::string& name) const
{
// Linear search? Also see GetSourceFileWithOutput for detail.
if (!cmSystemTools::FileIsFullPath(name)) {
cmSourcesWithOutput sources;
sources.Target = this->LinearGetTargetWithOutput(name);
sources.Source = this->LinearGetSourceFileWithOutput(
name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct);
return sources;
}
// Otherwise we use an efficient lookup map.
auto o = this->OutputToSource.find(name);
if (o != this->OutputToSource.end()) {
return o->second.Sources;
}
return {};
}
cmSourceFile* cmMakefile::GetSourceFileWithOutput(
const std::string& name, cmSourceOutputKind kind) const
{
// If the queried path is not absolute we use the backward compatible
// linear-time search for an output with a matching suffix.
if (!cmSystemTools::FileIsFullPath(name)) {
bool byproduct = false;
return this->LinearGetSourceFileWithOutput(name, kind, byproduct);
}
// Otherwise we use an efficient lookup map.
auto o = this->OutputToSource.find(name);
if (o != this->OutputToSource.end() &&
(!o->second.Sources.SourceIsByproduct ||
kind == cmSourceOutputKind::OutputOrByproduct)) {
// Source file could also be null pointer for example if we found the
// byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD
// command of a target, or a not yet created custom command.
return o->second.Sources.Source;
}
return nullptr;
}
void cmMakefile::AddTargetByproducts(
cmTarget* target, const std::vector<std::string>& byproducts)
{
for (std::string const& o : byproducts) {
this->UpdateOutputToSourceMap(o, target);
}
}
void cmMakefile::AddSourceOutputs(cmSourceFile* source,
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts)
{
for (std::string const& o : outputs) {
this->UpdateOutputToSourceMap(o, source, false);
}
for (std::string const& o : byproducts) {
this->UpdateOutputToSourceMap(o, source, true);
}
}
void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct,
cmTarget* target)
{
SourceEntry entry;
entry.Sources.Target = target;
auto pr = this->OutputToSource.emplace(byproduct, entry);
if (!pr.second) {
SourceEntry& current = pr.first->second;
// Has the target already been set?
if (!current.Sources.Target) {
current.Sources.Target = target;
} else {
// Multiple custom commands/targets produce the same output (source file
// or target). See also comment in other UpdateOutputToSourceMap
// overload.
//
// TODO: Warn the user about this case.
}
}
}
void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
cmSourceFile* source, bool byproduct)
{
SourceEntry entry;
entry.Sources.Source = source;
entry.Sources.SourceIsByproduct = byproduct;
auto pr = this->OutputToSource.emplace(output, entry);
if (!pr.second) {
SourceEntry& current = pr.first->second;
// Outputs take precedence over byproducts
if (!current.Sources.Source ||
(current.Sources.SourceIsByproduct && !byproduct)) {
current.Sources.Source = source;
current.Sources.SourceIsByproduct = false;
} else {
// Multiple custom commands produce the same output but may
// be attached to a different source file (MAIN_DEPENDENCY).
// LinearGetSourceFileWithOutput would return the first one,
// so keep the mapping for the first one.
//
// TODO: Warn the user about this case. However, the VS 8 generator
// triggers it for separate generate.stamp rules in ZERO_CHECK and
// individual targets.
}
}
}
#if !defined(CMAKE_BOOTSTRAP)

View File

@ -59,24 +59,6 @@ class cmTestGenerator;
class cmVariableWatch;
class cmake;
/** Flag if byproducts shall also be considered. */
enum class cmSourceOutputKind
{
OutputOnly,
OutputOrByproduct
};
/** Target and source file which have a specific output. */
struct cmSourcesWithOutput
{
/** Target with byproduct. */
cmTarget* Target = nullptr;
/** Source file with output or byproduct. */
cmSourceFile* Source = nullptr;
bool SourceIsByproduct = false;
};
/** A type-safe wrapper for a string representing a directory id. */
class cmDirectoryId
{
@ -230,19 +212,6 @@ public:
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines);
/**
* Add target byproducts.
*/
void AddTargetByproducts(cmTarget* target,
const std::vector<std::string>& byproducts);
/**
* Add source file outputs.
*/
void AddSourceOutputs(cmSourceFile* source,
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts);
/**
* Add a define flag to the build.
*/
@ -753,20 +722,10 @@ public:
return this->SourceFiles;
}
/**
* Return the target if the provided source name is a byproduct of a utility
* target or a PRE_BUILD, PRE_LINK, or POST_BUILD command.
* Return the source file which has the provided source name as output.
*/
cmSourcesWithOutput GetSourcesWithOutput(const std::string& name) const;
/**
* Is there a source file that has the provided source name as an output?
* If so then return it.
*/
cmSourceFile* GetSourceFileWithOutput(
const std::string& name,
cmSourceOutputKind kind = cmSourceOutputKind::OutputOnly) const;
std::vector<cmTarget*> const& GetOrderedTargets() const
{
return this->OrderedTargets;
}
//! Add a new cmTest to the list of tests for this makefile.
cmTest* CreateTest(const std::string& testName);
@ -983,8 +942,7 @@ protected:
mutable cmTargetMap Targets;
std::map<std::string, std::string> AliasTargets;
using TargetsVec = std::vector<cmTarget*>;
TargetsVec OrderedTargets;
std::vector<cmTarget*> OrderedTargets;
std::vector<std::unique_ptr<cmSourceFile>> SourceFiles;
@ -1134,34 +1092,6 @@ private:
std::vector<BT<GeneratorAction>> GeneratorActions;
bool GeneratorActionsInvoked = false;
/**
* See LinearGetSourceFileWithOutput for background information
*/
cmTarget* LinearGetTargetWithOutput(const std::string& name) const;
/**
* Generalized old version of GetSourceFileWithOutput kept for
* backward-compatibility. It implements a linear search and supports
* relative file paths. It is used as a fall back by GetSourceFileWithOutput
* and GetSourcesWithOutput.
*/
cmSourceFile* LinearGetSourceFileWithOutput(const std::string& name,
cmSourceOutputKind kind,
bool& byproduct) const;
struct SourceEntry
{
cmSourcesWithOutput Sources;
};
// A map for fast output to input look up.
using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>;
OutputToSourceMap OutputToSource;
void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target);
void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
bool byproduct);
bool CheckSystemVars;
bool CheckCMP0000;
std::set<std::string> WarnedCMP0074;