mirror of
https://github.com/Kitware/CMake.git
synced 2026-02-20 06:00:30 -06:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user