diff options
author | Brad King <brad.king@kitware.com> | 2020-12-14 12:22:34 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2020-12-14 12:22:45 (GMT) |
commit | fedfe763ee062456d8ee352dc9ebfc835973893f (patch) | |
tree | 8b7cdc5e17675cf69a19d21d789b796e022414b9 /Source/cmLocalGenerator.cxx | |
parent | 979af92e9efb136518669ba69b1e299b8f0d4e13 (diff) | |
parent | c257c25419c68e755b0f8289d8d563437bf9e0c2 (diff) | |
download | CMake-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.cxx | 223 |
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 { |