cmMakefile: Add lookup from source name to targets via byproducts

Given an output source name it is now possible to query which target has a
byproduct of this name or has a PRE_BUILD, PRE_LINK, or POST_BUILD build event
with a byproduct of this name.

In a call to GetSourceFileWithOutput a matching byproduct can now optionally be
returned as fallback if there is no matching output of a custom command.

Default behavior is not changed by this commit.
This commit is contained in:
Daniel Eiband
2019-09-11 12:19:43 +02:00
parent 62d5932389
commit 2edb0b71ed
2 changed files with 194 additions and 36 deletions

View File

@@ -908,6 +908,37 @@ void cmMakefile::AddCustomCommandToTarget(
t.AddPostBuildCommand(cc);
break;
}
this->UpdateOutputToSourceMap(byproducts, &t);
}
void cmMakefile::UpdateOutputToSourceMap(
std::vector<std::string> const& byproducts, cmTarget* target)
{
for (std::string const& o : byproducts) {
this->UpdateOutputToSourceMap(o, target);
}
}
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.
}
}
}
cmSourceFile* cmMakefile::AddCustomCommandToOutput(
@@ -1003,35 +1034,47 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
cc->SetDepfile(depfile);
cc->SetJobPool(job_pool);
file->SetCustomCommand(cc);
this->UpdateOutputToSourceMap(outputs, file);
this->UpdateOutputToSourceMap(outputs, file, false);
this->UpdateOutputToSourceMap(byproducts, file, true);
}
return file;
}
void cmMakefile::UpdateOutputToSourceMap(
std::vector<std::string> const& outputs, cmSourceFile* source)
std::vector<std::string> const& outputs, cmSourceFile* source,
bool byproduct)
{
for (std::string const& o : outputs) {
this->UpdateOutputToSourceMap(o, source);
this->UpdateOutputToSourceMap(o, source, byproduct);
}
}
void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
cmSourceFile* source)
cmSourceFile* source, bool byproduct)
{
auto i = this->OutputToSource.find(output);
if (i != this->OutputToSource.end()) {
// 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.
return;
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.
}
}
this->OutputToSource[output] = source;
}
cmSourceFile* cmMakefile::AddCustomCommandToOutput(
@@ -1194,6 +1237,8 @@ cmTarget* cmMakefile::AddUtilityCommand(
} else {
cmSystemTools::Error("Could not get source file entry for " + force);
}
this->UpdateOutputToSourceMap(byproducts, target);
}
return target;
}
@@ -2007,6 +2052,7 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type,
this->Targets
.emplace(name, cmTarget(name, type, cmTarget::VisibilityNormal, this))
.first;
this->OrderedTargets.push_back(&it->second);
this->GetGlobalGenerator()->IndexTarget(&it->second);
this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name);
return &it->second;
@@ -2026,11 +2072,45 @@ bool AnyOutputMatches(const std::string& name,
}
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) const
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 (cmSourceFile* src : this->SourceFiles) {
@@ -2041,25 +2121,57 @@ cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput(
// Return the first matching output.
return src;
}
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;
}
}
}
}
// otherwise return NULL
return nullptr;
// Did we find a byproduct?
byproduct = fallback != nullptr;
return fallback;
}
cmSourceFile* cmMakefile::GetSourceFileWithOutput(
cmSourcesWithOutput cmMakefile::GetSourcesWithOutput(
const std::string& name) const
{
// If the queried path is not absolute we use the backward compatible
// linear-time search for an output with a matching suffix.
// Linear search? Also see GetSourceFileWithOutput for detail.
if (!cmSystemTools::FileIsFullPath(name)) {
return this->LinearGetSourceFileWithOutput(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;
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 or a PRE_BUILD, PRE_LINK, or POST_BUILD
// command of a target.
return o->second.Sources.Source;
}
return nullptr;
}

View File

@@ -51,6 +51,24 @@ 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
{
@@ -687,10 +705,19 @@ public:
}
/**
* Is there a source file that has the provided source file as an output?
* if so then return it
* 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.
*/
cmSourceFile* GetSourceFileWithOutput(const std::string& outName) const;
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;
//! Add a new cmTest to the list of tests for this makefile.
cmTest* CreateTest(const std::string& testName);
@@ -914,6 +941,9 @@ protected:
mutable cmTargetMap Targets;
std::map<std::string, std::string> AliasTargets;
using TargetsVec = std::vector<cmTarget*>;
TargetsVec OrderedTargets;
using SourceFileVec = std::vector<cmSourceFile*>;
SourceFileVec SourceFiles;
@@ -1036,21 +1066,37 @@ private:
cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous);
/**
* Old version of GetSourceFileWithOutput(const std::string&) kept for
* backward-compatibility. It implements a linear search and support
* relative file paths. It is used as a fall back by
* GetSourceFileWithOutput(const std::string&).
* See LinearGetSourceFileWithOutput for background information
*/
cmSourceFile* LinearGetSourceFileWithOutput(const std::string& cname) const;
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, cmSourceFile*>;
using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>;
OutputToSourceMap OutputToSource;
void UpdateOutputToSourceMap(std::vector<std::string> const& byproducts,
cmTarget* target);
void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target);
void UpdateOutputToSourceMap(std::vector<std::string> const& outputs,
cmSourceFile* source);
void UpdateOutputToSourceMap(std::string const& output,
cmSourceFile* source);
cmSourceFile* source, bool byproduct);
void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
bool byproduct);
bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature,
std::string* error = nullptr) const;