summaryrefslogtreecommitdiffstats
path: root/Source/cmLocalGenerator.cxx
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2020-12-14 12:22:34 (GMT)
committerKitware Robot <kwrobot@kitware.com>2020-12-14 12:22:45 (GMT)
commitfedfe763ee062456d8ee352dc9ebfc835973893f (patch)
tree8b7cdc5e17675cf69a19d21d789b796e022414b9 /Source/cmLocalGenerator.cxx
parent979af92e9efb136518669ba69b1e299b8f0d4e13 (diff)
parentc257c25419c68e755b0f8289d8d563437bf9e0c2 (diff)
downloadCMake-fedfe763ee062456d8ee352dc9ebfc835973893f.zip
CMake-fedfe763ee062456d8ee352dc9ebfc835973893f.tar.gz
CMake-fedfe763ee062456d8ee352dc9ebfc835973893f.tar.bz2
Merge topic 'custom-command-output-genex'
c257c25419 add_custom_{command,target}: Add genex support to OUTPUT and BYPRODUCTS f36af9228b cmLocalGenerator: Evaluate generator expressions in custom command outputs c887cefd9a cmLocalGenerator: Simplify custom command output cmSourceFile creation 947ba01bf9 cmLocalGenerator: Factor out helper to expand custom command output paths 1902d28ebc cmLocalGenerator: Refactor UpdateOutputToSourceMap to avoid boolean trap e4034eabe9 cmLocalGenerator: Re-order logic in CreateGeneratedSource 706c48301d cmCustomCommandGenerator: Treat relative outputs w.r.t. build dir 5d23c5446e cmCustomCommandGenerator: Refactor OUTPUT and DEPENDS path evaluation ... Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Kyle Edwards <kyle.edwards@kitware.com> Acked-by: Pavel Solodovnikov <hellyeahdominate@gmail.com> Acked-by: Ben Boeckel <ben.boeckel@kitware.com> Merge-request: !5402
Diffstat (limited to 'Source/cmLocalGenerator.cxx')
-rw-r--r--Source/cmLocalGenerator.cxx223
1 files changed, 174 insertions, 49 deletions
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index c9dfdea..9f9d725 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -17,9 +17,11 @@
#include <cm/memory>
#include <cm/string_view>
#include <cmext/algorithm>
+#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
+#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
@@ -3812,38 +3814,95 @@ void cmLocalGenerator::GenerateFrameworkInfoPList(
}
namespace {
+cm::string_view CustomOutputRoleKeyword(cmLocalGenerator::OutputRole role)
+{
+ return (role == cmLocalGenerator::OutputRole::Primary ? "OUTPUT"_s
+ : "BYPRODUCTS"_s);
+}
+
void CreateGeneratedSource(cmLocalGenerator& lg, const std::string& output,
+ cmLocalGenerator::OutputRole role,
cmCommandOrigin origin,
const cmListFileBacktrace& lfbt)
{
- if (cmGeneratorExpression::Find(output) == std::string::npos) {
- // Outputs without generator expressions from the project are already
- // created and marked as generated. Do not mark them again, because
- // other commands might have overwritten the property.
- if (origin == cmCommandOrigin::Generator) {
- lg.GetMakefile()->GetOrCreateGeneratedSource(output);
- }
- } else {
+ if (cmGeneratorExpression::Find(output) != std::string::npos) {
lg.GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR,
"Generator expressions in custom command outputs are not implemented!",
lfbt);
+ return;
+ }
+
+ // Make sure the file will not be generated into the source
+ // directory during an out of source build.
+ if (!lg.GetMakefile()->CanIWriteThisFile(output)) {
+ lg.GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(CustomOutputRoleKeyword(role), " path\n ", output,
+ "\nin a source directory as an output of custom command."),
+ lfbt);
+ return;
+ }
+
+ // Make sure the output file name has no invalid characters.
+ std::string::size_type pos = output.find_first_of("#<>");
+ if (pos != std::string::npos) {
+ lg.GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(CustomOutputRoleKeyword(role), " containing a \"", output[pos],
+ "\" is not allowed."),
+ lfbt);
+ return;
+ }
+
+ // Outputs without generator expressions from the project are already
+ // created and marked as generated. Do not mark them again, because
+ // other commands might have overwritten the property.
+ if (origin == cmCommandOrigin::Generator) {
+ lg.GetMakefile()->GetOrCreateGeneratedSource(output);
}
}
-void CreateGeneratedSources(cmLocalGenerator& lg,
- const std::vector<std::string>& outputs,
- cmCommandOrigin origin,
- const cmListFileBacktrace& lfbt)
+std::string ComputeCustomCommandRuleFileName(cmLocalGenerator& lg,
+ cmListFileBacktrace const& bt,
+ std::string const& output)
{
- for (std::string const& o : outputs) {
- CreateGeneratedSource(lg, o, origin, lfbt);
+ // If the output path has no generator expressions, use it directly.
+ if (cmGeneratorExpression::Find(output) == std::string::npos) {
+ return output;
}
+
+ // The output path contains a generator expression, but we must choose
+ // a single source file path to which to attach the custom command.
+ // Use some heuristics to provie a nice-looking name when possible.
+
+ // If the only genex is $<CONFIG>, replace that gracefully.
+ {
+ std::string simple = output;
+ cmSystemTools::ReplaceString(simple, "$<CONFIG>", "(CONFIG)");
+ if (cmGeneratorExpression::Find(simple) == std::string::npos) {
+ return simple;
+ }
+ }
+
+ // If the genex evaluates to the same value in all configurations, use that.
+ {
+ std::vector<std::string> allConfigOutputs =
+ lg.ExpandCustomCommandOutputGenex(output, bt);
+ if (allConfigOutputs.size() == 1) {
+ return allConfigOutputs.front();
+ }
+ }
+
+ // Fall back to a deterministic unique name.
+ cmCryptoHash h(cmCryptoHash::AlgoSHA256);
+ return cmStrCat(lg.GetCurrentBinaryDirectory(), "/CMakeFiles/",
+ h.HashString(output).substr(0, 16));
}
cmSourceFile* AddCustomCommand(
cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
- const std::vector<std::string>& outputs,
+ cmCommandOrigin origin, const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends, const std::string& main_dependency,
const cmImplicitDependsList& implicit_depends,
@@ -3880,7 +3939,8 @@ cmSourceFile* AddCustomCommand(
cmGlobalGenerator* gg = lg.GetGlobalGenerator();
// Construct a rule file associated with the first output produced.
- std::string outName = gg->GenerateRuleFile(outputs[0]);
+ std::string outName = gg->GenerateRuleFile(
+ ComputeCustomCommandRuleFileName(lg, lfbt, outputs[0]));
// Check if the rule file already exists.
file = mf->GetSource(outName, cmSourceFileLocationKind::Known);
@@ -3923,7 +3983,10 @@ cmSourceFile* AddCustomCommand(
cc->SetJobPool(job_pool);
file->SetCustomCommand(std::move(cc));
- lg.AddSourceOutputs(file, outputs, byproducts);
+ lg.AddSourceOutputs(file, outputs, cmLocalGenerator::OutputRole::Primary,
+ lfbt, origin);
+ lg.AddSourceOutputs(file, byproducts,
+ cmLocalGenerator::OutputRole::Byproduct, lfbt, origin);
}
return file;
}
@@ -3967,9 +4030,6 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg,
const std::string& job_pool,
bool command_expand_lists, bool stdPipesUTF8)
{
- // Always create the byproduct sources and mark them generated.
- CreateGeneratedSources(lg, byproducts, origin, lfbt);
-
// Add the command to the appropriate build step for the target.
std::vector<std::string> no_output;
cmCustomCommand cc(no_output, byproducts, depends, commandLines, lfbt,
@@ -3992,7 +4052,7 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg,
break;
}
- lg.AddTargetByproducts(target, byproducts);
+ lg.AddTargetByproducts(target, byproducts, lfbt, origin);
}
cmSourceFile* AddCustomCommandToOutput(
@@ -4006,14 +4066,11 @@ cmSourceFile* AddCustomCommandToOutput(
bool uses_terminal, bool command_expand_lists, const std::string& depfile,
const std::string& job_pool, bool stdPipesUTF8)
{
- // Always create the output sources and mark them generated.
- CreateGeneratedSources(lg, outputs, origin, lfbt);
- CreateGeneratedSources(lg, byproducts, origin, lfbt);
-
- return AddCustomCommand(
- lg, lfbt, outputs, byproducts, depends, main_dependency, implicit_depends,
- commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
- command_expand_lists, depfile, job_pool, stdPipesUTF8);
+ return AddCustomCommand(lg, lfbt, origin, outputs, byproducts, depends,
+ main_dependency, implicit_depends, commandLines,
+ comment, workingDir, replace, escapeOldStyle,
+ uses_terminal, command_expand_lists, depfile,
+ job_pool, stdPipesUTF8);
}
void AppendCustomCommandToOutput(cmLocalGenerator& lg,
@@ -4024,7 +4081,22 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg,
const cmCustomCommandLines& commandLines)
{
// Lookup an existing command.
- if (cmSourceFile* sf = lg.GetSourceFileWithOutput(output)) {
+ cmSourceFile* sf = nullptr;
+ if (cmGeneratorExpression::Find(output) == std::string::npos) {
+ sf = lg.GetSourceFileWithOutput(output);
+ } else {
+ // This output path has a generator expression. Evaluate it to
+ // find the output for any configurations.
+ for (std::string const& out :
+ lg.ExpandCustomCommandOutputGenex(output, lfbt)) {
+ sf = lg.GetSourceFileWithOutput(out);
+ if (sf) {
+ break;
+ }
+ }
+ }
+
+ if (sf) {
if (cmCustomCommand* cc = sf->GetCustomCommand()) {
cc->AppendCommands(commandLines);
cc->AppendDepends(depends);
@@ -4051,10 +4123,6 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
bool uses_terminal, bool command_expand_lists,
const std::string& job_pool, bool stdPipesUTF8)
{
- // Always create the byproduct sources and mark them generated.
- CreateGeneratedSource(lg, force.Name, origin, lfbt);
- CreateGeneratedSources(lg, byproducts, origin, lfbt);
-
// Use an empty comment to avoid generation of default comment.
if (!comment) {
comment = "";
@@ -4063,12 +4131,12 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
std::string no_main_dependency;
cmImplicitDependsList no_implicit_depends;
cmSourceFile* rule = AddCustomCommand(
- lg, lfbt, { force.Name }, byproducts, depends, no_main_dependency,
+ lg, lfbt, origin, { force.Name }, byproducts, depends, no_main_dependency,
no_implicit_depends, commandLines, comment, workingDir,
/*replace=*/false, escapeOldStyle, uses_terminal, command_expand_lists,
/*depfile=*/"", job_pool, stdPipesUTF8);
if (rule) {
- lg.AddTargetByproducts(target, byproducts);
+ lg.AddTargetByproducts(target, byproducts, lfbt, origin);
}
if (!force.NameCMP0049.empty()) {
@@ -4166,34 +4234,87 @@ cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput(
return nullptr;
}
+std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputPaths(
+ cmCompiledGeneratorExpression const& cge, std::string const& config)
+{
+ std::vector<std::string> paths = cmExpandedList(cge.Evaluate(this, config));
+ for (std::string& p : paths) {
+ p = cmSystemTools::CollapseFullPath(p, this->GetCurrentBinaryDirectory());
+ }
+ return paths;
+}
+
+std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputGenex(
+ std::string const& o, cmListFileBacktrace const& bt)
+{
+ std::vector<std::string> allConfigOutputs;
+ cmGeneratorExpression ge(bt);
+ std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(o);
+ std::vector<std::string> configs =
+ this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+ for (std::string const& config : configs) {
+ std::vector<std::string> configOutputs =
+ this->ExpandCustomCommandOutputPaths(*cge, config);
+ allConfigOutputs.reserve(allConfigOutputs.size() + configOutputs.size());
+ std::move(configOutputs.begin(), configOutputs.end(),
+ std::back_inserter(allConfigOutputs));
+ }
+ auto endUnique =
+ cmRemoveDuplicates(allConfigOutputs.begin(), allConfigOutputs.end());
+ allConfigOutputs.erase(endUnique, allConfigOutputs.end());
+ return allConfigOutputs;
+}
+
void cmLocalGenerator::AddTargetByproducts(
- cmTarget* target, const std::vector<std::string>& byproducts)
+ cmTarget* target, const std::vector<std::string>& byproducts,
+ cmListFileBacktrace const& bt, cmCommandOrigin origin)
{
for (std::string const& o : byproducts) {
- this->UpdateOutputToSourceMap(o, target);
+ if (cmGeneratorExpression::Find(o) == std::string::npos) {
+ this->UpdateOutputToSourceMap(o, target, bt, origin);
+ continue;
+ }
+
+ // This byproduct path has a generator expression. Evaluate it to
+ // register the byproducts for all configurations.
+ for (std::string const& b : this->ExpandCustomCommandOutputGenex(o, bt)) {
+ this->UpdateOutputToSourceMap(b, target, bt, cmCommandOrigin::Generator);
+ }
}
}
void cmLocalGenerator::AddSourceOutputs(
cmSourceFile* source, const std::vector<std::string>& outputs,
- const std::vector<std::string>& byproducts)
+ OutputRole role, cmListFileBacktrace const& bt, cmCommandOrigin origin)
{
for (std::string const& o : outputs) {
- this->UpdateOutputToSourceMap(o, source, false);
- }
- for (std::string const& o : byproducts) {
- this->UpdateOutputToSourceMap(o, source, true);
+ if (cmGeneratorExpression::Find(o) == std::string::npos) {
+ this->UpdateOutputToSourceMap(o, source, role, bt, origin);
+ continue;
+ }
+
+ // This output path has a generator expression. Evaluate it to
+ // register the outputs for all configurations.
+ for (std::string const& out :
+ this->ExpandCustomCommandOutputGenex(o, bt)) {
+ this->UpdateOutputToSourceMap(out, source, role, bt,
+ cmCommandOrigin::Generator);
+ }
}
}
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct,
- cmTarget* target)
+ cmTarget* target,
+ cmListFileBacktrace const& bt,
+ cmCommandOrigin origin)
{
SourceEntry entry;
entry.Sources.Target = target;
auto pr = this->OutputToSource.emplace(byproduct, entry);
- if (!pr.second) {
+ if (pr.second) {
+ CreateGeneratedSource(*this, byproduct, OutputRole::Byproduct, origin, bt);
+ } else {
SourceEntry& current = pr.first->second;
// Has the target already been set?
if (!current.Sources.Target) {
@@ -4210,18 +4331,22 @@ void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct,
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output,
cmSourceFile* source,
- bool byproduct)
+ OutputRole role,
+ cmListFileBacktrace const& bt,
+ cmCommandOrigin origin)
{
SourceEntry entry;
entry.Sources.Source = source;
- entry.Sources.SourceIsByproduct = byproduct;
+ entry.Sources.SourceIsByproduct = role == OutputRole::Byproduct;
auto pr = this->OutputToSource.emplace(output, entry);
- if (!pr.second) {
+ if (pr.second) {
+ CreateGeneratedSource(*this, output, role, origin, bt);
+ } else {
SourceEntry& current = pr.first->second;
// Outputs take precedence over byproducts
if (!current.Sources.Source ||
- (current.Sources.SourceIsByproduct && !byproduct)) {
+ (current.Sources.SourceIsByproduct && role == OutputRole::Primary)) {
current.Sources.Source = source;
current.Sources.SourceIsByproduct = false;
} else {