diff options
Diffstat (limited to 'Source/cmAddCustomTargetCommand.cxx')
-rw-r--r-- | Source/cmAddCustomTargetCommand.cxx | 248 |
1 files changed, 151 insertions, 97 deletions
diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index 4eba88636..87e114780 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -1,42 +1,40 @@ -/*============================================================================ - CMake - Cross Platform Makefile Generator - Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmAddCustomTargetCommand.h" - Distributed under the OSI-approved BSD License (the "License"); - see accompanying file Copyright.txt for details. +#include <sstream> - This software is distributed WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the License for more information. -============================================================================*/ -#include "cmAddCustomTargetCommand.h" +#include "cmCustomCommandLines.h" +#include "cmGeneratorExpression.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmPolicies.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmake.h" + +class cmExecutionStatus; // cmAddCustomTargetCommand -bool cmAddCustomTargetCommand -::InitialPass(std::vector<std::string> const& args, - cmExecutionStatus&) +bool cmAddCustomTargetCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus&) { - if(args.size() < 1 ) - { + if (args.empty()) { this->SetError("called with incorrect number of arguments"); return false; - } + } + + std::string const& targetName = args[0]; // Check the target name. - if(args[0].find_first_of("/\\") != args[0].npos) - { - if(!this->Makefile->NeedBackwardsCompatibility(2,2)) - { - cmOStringStream e; - e << "called with invalid target name \"" << args[0] - << "\". Target names may not contain a slash. " - << "Use ADD_CUSTOM_COMMAND to generate files. " - << "Set CMAKE_BACKWARDS_COMPATIBILITY to 2.2 " - << "or lower to skip this check."; - this->SetError(e.str().c_str()); - return false; - } - } + if (targetName.find_first_of("/\\") != std::string::npos) { + std::ostringstream e; + e << "called with invalid target name \"" << targetName + << "\". Target names may not contain a slash. " + << "Use ADD_CUSTOM_COMMAND to generate files."; + this->SetError(e.str()); + return false; + } // Accumulate one command line at a time. cmCustomCommandLine currentLine; @@ -45,145 +43,201 @@ bool cmAddCustomTargetCommand cmCustomCommandLines commandLines; // Accumulate dependencies. - std::vector<std::string> depends; + std::vector<std::string> depends, byproducts; std::string working_directory; bool verbatim = false; + bool uses_terminal = false; + bool command_expand_lists = false; std::string comment_buffer; - const char* comment = 0; + const char* comment = CM_NULLPTR; std::vector<std::string> sources; // Keep track of parser state. - enum tdoing { + enum tdoing + { doing_command, doing_depends, + doing_byproducts, doing_working_directory, doing_comment, doing_source, - doing_verbatim + doing_nothing }; tdoing doing = doing_command; // Look for the ALL option. bool excludeFromAll = true; unsigned int start = 1; - if(args.size() > 1) - { - if(args[1] == "ALL") - { + if (args.size() > 1) { + if (args[1] == "ALL") { excludeFromAll = false; start = 2; - } } + } // Parse the rest of the arguments. - for(unsigned int j = start; j < args.size(); ++j) - { + for (unsigned int j = start; j < args.size(); ++j) { std::string const& copy = args[j]; - if(copy == "DEPENDS") - { + if (copy == "DEPENDS") { doing = doing_depends; - } - else if(copy == "WORKING_DIRECTORY") - { + } else if (copy == "BYPRODUCTS") { + doing = doing_byproducts; + } else if (copy == "WORKING_DIRECTORY") { doing = doing_working_directory; - } - else if(copy == "VERBATIM") - { - doing = doing_verbatim; + } else if (copy == "VERBATIM") { + doing = doing_nothing; verbatim = true; - } - else if (copy == "COMMENT") - { + } else if (copy == "USES_TERMINAL") { + doing = doing_nothing; + uses_terminal = true; + } else if (copy == "COMMAND_EXPAND_LISTS") { + doing = doing_nothing; + command_expand_lists = true; + } else if (copy == "COMMENT") { doing = doing_comment; - } - else if(copy == "COMMAND") - { + } else if (copy == "COMMAND") { doing = doing_command; // Save the current command before starting the next command. - if(!currentLine.empty()) - { + if (!currentLine.empty()) { commandLines.push_back(currentLine); currentLine.clear(); - } } - else if(copy == "SOURCES") - { + } else if (copy == "SOURCES") { doing = doing_source; - } - else - { - switch (doing) - { + } else { + switch (doing) { case doing_working_directory: working_directory = copy; break; case doing_command: currentLine.push_back(copy); break; - case doing_depends: - { + case doing_byproducts: { + std::string filename; + if (!cmSystemTools::FileIsFullPath(copy.c_str())) { + filename = this->Makefile->GetCurrentBinaryDirectory(); + filename += "/"; + } + filename += copy; + cmSystemTools::ConvertToUnixSlashes(filename); + byproducts.push_back(filename); + } break; + case doing_depends: { std::string dep = copy; cmSystemTools::ConvertToUnixSlashes(dep); depends.push_back(dep); - } + } break; + case doing_comment: + comment_buffer = copy; + comment = comment_buffer.c_str(); break; - case doing_comment: - comment_buffer = copy; - comment = comment_buffer.c_str(); - break; case doing_source: sources.push_back(copy); break; default: this->SetError("Wrong syntax. Unknown type of argument."); return false; - } } } + } - std::string::size_type pos = args[0].find_first_of("#<>"); - if(pos != args[0].npos) - { - cmOStringStream msg; - msg << "called with target name containing a \"" << args[0][pos] + std::string::size_type pos = targetName.find_first_of("#<>"); + if (pos != std::string::npos) { + std::ostringstream msg; + msg << "called with target name containing a \"" << targetName[pos] << "\". This character is not allowed."; - this->SetError(msg.str().c_str()); + this->SetError(msg.str()); return false; + } + + // Some requirements on custom target names already exist + // and have been checked at this point. + // The following restrictions overlap but depend on policy CMP0037. + bool nameOk = cmGeneratorExpression::IsValidTargetName(targetName) && + !cmGlobalGenerator::IsReservedTarget(targetName); + if (nameOk) { + nameOk = targetName.find(':') == std::string::npos; + } + if (!nameOk) { + cmake::MessageType messageType = cmake::AUTHOR_WARNING; + std::ostringstream e; + bool issueMessage = false; + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0037)) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n"; + issueMessage = true; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = cmake::FATAL_ERROR; + } + if (issueMessage) { + /* clang-format off */ + e << "The target name \"" << targetName << + "\" is reserved or not valid for certain " + "CMake features, such as generator expressions, and may result " + "in undefined behavior."; + /* clang-format on */ + this->Makefile->IssueMessage(messageType, e.str()); + + if (messageType == cmake::FATAL_ERROR) { + return false; + } } + } // Store the last command line finished. - if(!currentLine.empty()) - { + if (!currentLine.empty()) { commandLines.push_back(currentLine); currentLine.clear(); - } + } // Enforce name uniqueness. { - std::string msg; - if(!this->Makefile->EnforceUniqueName(args[0], msg, true)) - { - this->SetError(msg.c_str()); - return false; + std::string msg; + if (!this->Makefile->EnforceUniqueName(targetName, msg, true)) { + this->SetError(msg); + return false; } } // Convert working directory to a full path. - if(!working_directory.empty()) - { - const char* build_dir = this->Makefile->GetCurrentOutputDirectory(); + if (!working_directory.empty()) { + const char* build_dir = this->Makefile->GetCurrentBinaryDirectory(); working_directory = - cmSystemTools::CollapseFullPath(working_directory.c_str(), build_dir); - } + cmSystemTools::CollapseFullPath(working_directory, build_dir); + } + + if (commandLines.empty() && !byproducts.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "BYPRODUCTS may not be specified without any COMMAND"); + return true; + } + if (commandLines.empty() && uses_terminal) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "USES_TERMINAL may not be specified without any COMMAND"); + return true; + } + if (commandLines.empty() && command_expand_lists) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "COMMAND_EXPAND_LISTS may not be specified without any COMMAND"); + return true; + } // Add the utility target to the makefile. bool escapeOldStyle = !verbatim; - cmTarget* target = - this->Makefile->AddUtilityCommand(args[0].c_str(), excludeFromAll, - working_directory.c_str(), depends, - commandLines, escapeOldStyle, comment); + cmTarget* target = this->Makefile->AddUtilityCommand( + targetName, excludeFromAll, working_directory.c_str(), byproducts, depends, + commandLines, escapeOldStyle, comment, uses_terminal, + command_expand_lists); // Add additional user-specified source files to the target. target->AddSources(sources); |