Merge topic 'split-custom-command-creation'

0e1faa28cb cmMakefile: Separate custom command setup from actual creation
56c204e8eb cmMakefile: Refactor AddCustomCommandOldStyle to be delay friendly
3061dc6ac9 add_custom_command: Add tests for rejecting literal quotes in commands
e893ab94ba cmMakefile: Validate command line for all custom commands
f1e846fdde cmMakefile: Extract custom command validation method
4926ab2454 cmMakefile: Create all generated byproducts as known sources

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3822
This commit is contained in:
Brad King
2019-09-20 14:22:52 +00:00
committed by Kitware Robot
18 changed files with 305 additions and 107 deletions
+214 -92
View File
@@ -821,7 +821,23 @@ void cmMakefile::ConfigureFinalPass()
}
}
void cmMakefile::AddCustomCommandToTarget(
bool cmMakefile::ValidateCustomCommand(
const cmCustomCommandLines& commandLines) const
{
// TODO: More strict?
for (cmCustomCommandLine const& cl : commandLines) {
if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') {
std::ostringstream e;
e << "COMMAND may not contain literal quotes:\n " << cl[0] << "\n";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
}
return true;
}
cmTarget* cmMakefile::AddCustomCommandToTarget(
const std::string& target, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
@@ -864,31 +880,51 @@ void cmMakefile::AddCustomCommandToTarget(
this->IssueMessage(messageType, e.str());
}
return;
return nullptr;
}
cmTarget& t = ti->second;
cmTarget* t = &ti->second;
if (objLibraryCommands == RejectObjectLibraryCommands &&
t.GetType() == cmStateEnums::OBJECT_LIBRARY) {
t->GetType() == cmStateEnums::OBJECT_LIBRARY) {
std::ostringstream e;
e << "Target \"" << target
<< "\" is an OBJECT library "
"that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands.";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
return;
return nullptr;
}
if (t.GetType() == cmStateEnums::INTERFACE_LIBRARY) {
if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
std::ostringstream e;
e << "Target \"" << target
<< "\" is an INTERFACE library "
"that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands.";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
return;
return nullptr;
}
// Validate custom commands.
if (!this->ValidateCustomCommand(commandLines)) {
return t;
}
// Always create the byproduct sources and mark them generated.
this->CreateGeneratedSources(byproducts);
this->CommitCustomCommandToTarget(
t, byproducts, depends, commandLines, type, comment, workingDir,
escapeOldStyle, uses_terminal, depfile, job_pool, command_expand_lists);
return t;
}
void cmMakefile::CommitCustomCommandToTarget(
cmTarget* target, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
const char* comment, const char* workingDir, bool escapeOldStyle,
bool uses_terminal, const std::string& depfile, const std::string& job_pool,
bool command_expand_lists)
{
// Add the command to the appropriate build step for the target.
std::vector<std::string> no_output;
cmCustomCommand cc(this, no_output, byproducts, depends, commandLines,
@@ -901,16 +937,16 @@ void cmMakefile::AddCustomCommandToTarget(
cc.SetJobPool(job_pool);
switch (type) {
case cmTarget::PRE_BUILD:
t.AddPreBuildCommand(cc);
target->AddPreBuildCommand(cc);
break;
case cmTarget::PRE_LINK:
t.AddPreLinkCommand(cc);
target->AddPreLinkCommand(cc);
break;
case cmTarget::POST_BUILD:
t.AddPostBuildCommand(cc);
target->AddPostBuildCommand(cc);
break;
}
this->UpdateOutputToSourceMap(byproducts, &t);
this->UpdateOutputToSourceMap(byproducts, target);
}
void cmMakefile::UpdateOutputToSourceMap(
@@ -943,6 +979,23 @@ void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct,
}
}
cmSourceFile* cmMakefile::AddCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const std::string& main_dependency, const cmCustomCommandLines& commandLines,
const char* comment, const char* workingDir, bool replace,
bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
const std::string& depfile, const std::string& job_pool)
{
std::vector<std::string> outputs;
outputs.push_back(output);
std::vector<std::string> no_byproducts;
cmImplicitDependsList no_implicit_depends;
return this->AddCustomCommandToOutput(
outputs, no_byproducts, depends, main_dependency, no_implicit_depends,
commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
command_expand_lists, depfile, job_pool);
}
cmSourceFile* cmMakefile::AddCustomCommandToOutput(
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
@@ -959,20 +1012,31 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
return nullptr;
}
// Validate custom commands. TODO: More strict?
for (cmCustomCommandLine const& cl : commandLines) {
if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') {
std::ostringstream e;
e << "COMMAND may not contain literal quotes:\n " << cl[0] << "\n";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
return nullptr;
}
// Validate custom commands.
if (!this->ValidateCustomCommand(commandLines)) {
return nullptr;
}
// Always create the output sources and mark them generated.
this->CreateGeneratedSources(outputs, cmSourceFileLocationKind::Known);
this->CreateGeneratedSources(byproducts, cmSourceFileLocationKind::Known);
this->CreateGeneratedSources(outputs);
this->CreateGeneratedSources(byproducts);
return this->CommitCustomCommandToOutput(
outputs, byproducts, depends, main_dependency, implicit_depends,
commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
command_expand_lists, depfile, job_pool);
}
cmSourceFile* cmMakefile::CommitCustomCommandToOutput(
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,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace, bool escapeOldStyle,
bool uses_terminal, bool command_expand_lists, const std::string& depfile,
const std::string& job_pool)
{
// Choose a source file on which to store the custom command.
cmSourceFile* file = nullptr;
if (!commandLines.empty() && !main_dependency.empty()) {
@@ -1081,23 +1145,6 @@ void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
}
}
cmSourceFile* cmMakefile::AddCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const std::string& main_dependency, const cmCustomCommandLines& commandLines,
const char* comment, const char* workingDir, bool replace,
bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
const std::string& depfile, const std::string& job_pool)
{
std::vector<std::string> outputs;
outputs.push_back(output);
std::vector<std::string> no_byproducts;
cmImplicitDependsList no_implicit_depends;
return this->AddCustomCommandToOutput(
outputs, no_byproducts, depends, main_dependency, no_implicit_depends,
commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
command_expand_lists, depfile, job_pool);
}
void cmMakefile::AddCustomCommandOldStyle(
const std::string& target, const std::vector<std::string>& outputs,
const std::vector<std::string>& depends, const std::string& source,
@@ -1116,41 +1163,50 @@ void cmMakefile::AddCustomCommandOldStyle(
return;
}
// Each output must get its own copy of this rule.
cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|"
"rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
"hm|hpp|hxx|in|txx|inl)$");
for (std::string const& oi : outputs) {
// Get the name of this output.
const char* output = oi.c_str();
cmSourceFile* sf;
// Choose whether to use a main dependency.
if (sourceFiles.find(source)) {
// The source looks like a real file. Use it as the main dependency.
sf = this->AddCustomCommandToOutput(output, depends, source,
commandLines, comment, nullptr);
} else {
// The source may not be a real file. Do not use a main dependency.
std::string no_main_dependency;
std::vector<std::string> depends2 = depends;
depends2.push_back(source);
sf = this->AddCustomCommandToOutput(output, depends2, no_main_dependency,
commandLines, comment, nullptr);
}
auto ti = this->Targets.find(target);
cmTarget* t = ti != this->Targets.end() ? &ti->second : nullptr;
auto addRuleFileToTarget = [=](cmSourceFile* sf) {
// If the rule was added to the source (and not a .rule file),
// then add the source to the target to make sure the rule is
// included.
if (sf && !sf->GetPropertyAsBool("__CMAKE_RULE")) {
auto ti = this->Targets.find(target);
if (ti != this->Targets.end()) {
ti->second.AddSource(sf->ResolveFullPath());
if (!sf->GetPropertyAsBool("__CMAKE_RULE")) {
if (t) {
t->AddSource(sf->ResolveFullPath());
} else {
cmSystemTools::Error("Attempt to add a custom rule to a target "
"that does not exist yet for target " +
target);
return;
}
}
};
// Each output must get its own copy of this rule.
cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|"
"rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
"hm|hpp|hxx|in|txx|inl)$");
// Choose whether to use a main dependency.
if (sourceFiles.find(source)) {
// The source looks like a real file. Use it as the main dependency.
for (std::string const& output : outputs) {
cmSourceFile* sf = this->AddCustomCommandToOutput(
output, depends, source, commandLines, comment, nullptr);
if (sf) {
addRuleFileToTarget(sf);
}
}
} else {
std::string no_main_dependency;
std::vector<std::string> depends2 = depends;
depends2.push_back(source);
// The source may not be a real file. Do not use a main dependency.
for (std::string const& output : outputs) {
cmSourceFile* sf = this->AddCustomCommandToOutput(
output, depends2, no_main_dependency, commandLines, comment, nullptr);
if (sf) {
addRuleFileToTarget(sf);
}
}
}
@@ -1160,6 +1216,26 @@ bool cmMakefile::AppendCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines)
{
// Check as good as we can if there will be a command for this output.
if (!this->MightHaveCustomCommand(output)) {
return false;
}
// Validate custom commands.
if (this->ValidateCustomCommand(commandLines)) {
// Add command factory to allow generator expressions in output.
this->CommitAppendCustomCommandToOutput(output, depends, implicit_depends,
commandLines);
}
return true;
}
void cmMakefile::CommitAppendCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines)
{
// Lookup an existing command.
if (cmSourceFile* sf = this->GetSourceFileWithOutput(output)) {
@@ -1167,10 +1243,8 @@ bool cmMakefile::AppendCustomCommandToOutput(
cc->AppendCommands(commandLines);
cc->AppendDepends(depends);
cc->AppendImplicitDepends(implicit_depends);
return true;
}
}
return false;
}
cmTarget* cmMakefile::AddUtilityCommand(
@@ -1202,39 +1276,69 @@ cmTarget* cmMakefile::AddUtilityCommand(
target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
}
if (!comment) {
// Use an empty comment to avoid generation of default comment.
comment = "";
// Validate custom commands.
if (!this->ValidateCustomCommand(commandLines) ||
(commandLines.empty() && depends.empty())) {
return target;
}
// Store the custom command in the target.
if (!commandLines.empty() || !depends.empty()) {
// Always create the byproduct sources and mark them generated.
this->CreateGeneratedSources(byproducts, cmSourceFileLocationKind::Known);
std::string force =
cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", utilityName);
std::vector<std::string> forced;
forced.push_back(force);
std::string no_main_dependency;
cmImplicitDependsList no_implicit_depends;
bool no_replace = false;
this->AddCustomCommandToOutput(
forced, byproducts, depends, no_main_dependency, no_implicit_depends,
commandLines, comment, workingDirectory, no_replace, escapeOldStyle,
uses_terminal, command_expand_lists, /*depfile=*/"", job_pool);
cmSourceFile* sf = target->AddSourceCMP0049(force);
// Always create the byproduct sources and mark them generated.
this->CreateGeneratedSources(byproducts);
std::string force =
cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", utilityName);
this->CreateGeneratedSource(force);
std::string forceCMP0049 = target->GetSourceCMP0049(force);
{
cmSourceFile* sf = nullptr;
if (!forceCMP0049.empty()) {
sf = this->GetOrCreateSource(forceCMP0049, false,
cmSourceFileLocationKind::Known);
}
// The output is not actually created so mark it symbolic.
if (sf) {
sf->SetProperty("SYMBOLIC", "1");
} else {
cmSystemTools::Error("Could not get source file entry for " + force);
}
}
if (!comment) {
// Use an empty comment to avoid generation of default comment.
comment = "";
}
this->CommitUtilityCommand(target, force, forceCMP0049, workingDirectory,
byproducts, depends, commandLines, escapeOldStyle,
comment, uses_terminal, command_expand_lists,
job_pool);
return target;
}
void cmMakefile::CommitUtilityCommand(
cmTarget* target, const std::string& force, const std::string& forceCMP0049,
const char* workingDirectory, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, bool escapeOldStyle,
const char* comment, bool uses_terminal, bool command_expand_lists,
const std::string& job_pool)
{
std::vector<std::string> forced;
forced.push_back(force);
std::string no_main_dependency;
cmImplicitDependsList no_implicit_depends;
bool no_replace = false;
cmSourceFile* sf = this->AddCustomCommandToOutput(
forced, byproducts, depends, no_main_dependency, no_implicit_depends,
commandLines, comment, workingDirectory, no_replace, escapeOldStyle,
uses_terminal, command_expand_lists, /*depfile=*/"", job_pool);
if (!forceCMP0049.empty()) {
target->AddSource(forceCMP0049);
}
if (sf) {
this->UpdateOutputToSourceMap(byproducts, target);
}
return target;
}
static void s_AddDefineFlag(std::string const& flag, std::string& dflags)
@@ -2170,6 +2274,18 @@ cmSourceFile* cmMakefile::GetSourceFileWithOutput(
return nullptr;
}
bool cmMakefile::MightHaveCustomCommand(const std::string& name) const
{
// This will have to be changed for delaying custom command creation, because
// GetSourceFileWithOutput requires the command to be already created.
if (cmSourceFile* sf = this->GetSourceFileWithOutput(name)) {
if (sf->GetCustomCommand()) {
return true;
}
}
return false;
}
#if !defined(CMAKE_BOOTSTRAP)
cmSourceGroup* cmMakefile::GetSourceGroup(
const std::vector<std::string>& name) const
@@ -3414,13 +3530,19 @@ cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName,
return this->CreateSource(sourceName, generated, kind);
}
void cmMakefile::CreateGeneratedSource(const std::string& output)
{
if (cmSourceFile* out = this->GetOrCreateSource(
output, true, cmSourceFileLocationKind::Known)) {
out->SetProperty("GENERATED", "1");
}
}
void cmMakefile::CreateGeneratedSources(
const std::vector<std::string>& outputs, cmSourceFileLocationKind kind)
const std::vector<std::string>& outputs)
{
for (std::string const& output : outputs) {
if (cmSourceFile* out = this->GetOrCreateSource(output, true, kind)) {
out->SetProperty("GENERATED", "1");
}
this->CreateGeneratedSource(output);
}
}
+46 -8
View File
@@ -172,7 +172,7 @@ public:
};
/** Add a custom command to the build. */
void AddCustomCommandToTarget(
cmTarget* AddCustomCommandToTarget(
const std::string& target, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
@@ -181,18 +181,18 @@ public:
const std::string& job_pool = "", bool command_expand_lists = false,
ObjectLibraryCommands objLibraryCommands = RejectObjectLibraryCommands);
cmSourceFile* AddCustomCommandToOutput(
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const std::string& output, const std::vector<std::string>& depends,
const std::string& main_dependency,
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace = false, bool escapeOldStyle = true,
bool uses_terminal = false, bool command_expand_lists = false,
const std::string& depfile = "", const std::string& job_pool = "");
cmSourceFile* AddCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
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,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace = false, bool escapeOldStyle = true,
bool uses_terminal = false, bool command_expand_lists = false,
@@ -1059,9 +1059,42 @@ private:
bool atOnly, const char* filename,
long line, bool replaceAt) const;
void CreateGeneratedSources(
bool ValidateCustomCommand(const cmCustomCommandLines& commandLines) const;
void CommitCustomCommandToTarget(
cmTarget* target, const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
const char* comment, const char* workingDir, bool escapeOldStyle,
bool uses_terminal, const std::string& depfile,
const std::string& job_pool, bool command_expand_lists);
cmSourceFile* CommitCustomCommandToOutput(
const std::vector<std::string>& outputs,
cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous);
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const std::string& main_dependency,
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines, const char* comment,
const char* workingDir, bool replace, bool escapeOldStyle,
bool uses_terminal, bool command_expand_lists, const std::string& depfile,
const std::string& job_pool);
void CommitAppendCustomCommandToOutput(
const std::string& output, const std::vector<std::string>& depends,
const cmImplicitDependsList& implicit_depends,
const cmCustomCommandLines& commandLines);
void CommitUtilityCommand(cmTarget* target, const std::string& force,
const std::string& forceCMP0049,
const char* workingDirectory,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines,
bool escapeOldStyle, const char* comment,
bool uses_terminal, bool command_expand_lists,
const std::string& job_pool);
void CreateGeneratedSource(const std::string& output);
void CreateGeneratedSources(const std::vector<std::string>& outputs);
/**
* See LinearGetSourceFileWithOutput for background information
@@ -1096,6 +1129,11 @@ private:
void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
bool byproduct);
/**
* Return if the provided source file might have a custom command.
*/
bool MightHaveCustomCommand(const std::string& name) const;
bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature,
std::string* error = nullptr) const;
+2 -6
View File
@@ -695,13 +695,9 @@ std::string cmTargetInternals::ProcessSourceItemCMP0049(const std::string& s)
return src;
}
cmSourceFile* cmTarget::AddSourceCMP0049(const std::string& s)
std::string cmTarget::GetSourceCMP0049(const std::string& s)
{
std::string src = impl->ProcessSourceItemCMP0049(s);
if (!s.empty() && src.empty()) {
return nullptr;
}
return this->AddSource(src);
return impl->ProcessSourceItemCMP0049(s);
}
struct CreateLocation
+1 -1
View File
@@ -103,7 +103,7 @@ public:
//! Add sources to the target.
void AddSources(std::vector<std::string> const& srcs);
void AddTracedSources(std::vector<std::string> const& srcs);
cmSourceFile* AddSourceCMP0049(const std::string& src);
std::string GetSourceCMP0049(const std::string& src);
cmSourceFile* AddSource(const std::string& src, bool before = false);
//! how we identify a library, by name and type
@@ -0,0 +1 @@
1
@@ -0,0 +1,7 @@
CMake Error at AppendLiteralQuotes.cmake:2 \(add_custom_command\):
COMMAND may not contain literal quotes:
"c"
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
@@ -0,0 +1,2 @@
add_custom_command(OUTPUT a COMMAND b)
add_custom_command(OUTPUT a COMMAND "\"c\"" APPEND)
@@ -0,0 +1 @@
1
@@ -0,0 +1,7 @@
CMake Error at LiteralQuotes.cmake:1 \(add_custom_command\):
COMMAND may not contain literal quotes:
"b"
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
@@ -0,0 +1 @@
add_custom_command(OUTPUT a COMMAND "\"b\"")
@@ -1,15 +1,18 @@
include(RunCMake)
run_cmake(AppendLiteralQuotes)
run_cmake(AppendNoOutput)
run_cmake(AppendNotOutput)
run_cmake(BadArgument)
run_cmake(GeneratedProperty)
run_cmake(LiteralQuotes)
run_cmake(NoArguments)
run_cmake(NoOutputOrTarget)
run_cmake(OutputAndTarget)
run_cmake(SourceByproducts)
run_cmake(SourceUsesTerminal)
run_cmake(TargetImported)
run_cmake(TargetLiteralQuotes)
run_cmake(TargetNotInDir)
if(${RunCMake_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])")
@@ -0,0 +1 @@
1
@@ -0,0 +1,7 @@
CMake Error at TargetLiteralQuotes.cmake:2 \(add_custom_command\):
COMMAND may not contain literal quotes:
"b"
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
@@ -0,0 +1,2 @@
add_library(a)
add_custom_command(TARGET a POST_BUILD COMMAND "\"b\"")
@@ -0,0 +1 @@
1
@@ -0,0 +1,7 @@
CMake Error at LiteralQuotes.cmake:1 \(add_custom_target\):
COMMAND may not contain literal quotes:
"b"
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
@@ -0,0 +1 @@
add_custom_target(a COMMAND "\"b\"")
@@ -5,6 +5,7 @@ run_cmake(GeneratedProperty)
run_cmake(NoArguments)
run_cmake(BadTargetName)
run_cmake(ByproductsNoCommand)
run_cmake(LiteralQuotes)
run_cmake(UsesTerminalNoCommand)
function(run_TargetOrder)