diff options
Diffstat (limited to 'Source/cmAddCustomCommandCommand.cxx')
-rw-r--r-- | Source/cmAddCustomCommandCommand.cxx | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx new file mode 100644 index 0000000..400be77 --- /dev/null +++ b/Source/cmAddCustomCommandCommand.cxx @@ -0,0 +1,364 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + 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 "cmAddCustomCommandCommand.h" + +#include "cmTarget.h" + +#include "cmSourceFile.h" + +// cmAddCustomCommandCommand +bool cmAddCustomCommandCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus&) +{ + /* Let's complain at the end of this function about the lack of a particular + arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE + are required. + */ + if (args.size() < 4) { + this->SetError("called with wrong number of arguments."); + return false; + } + + std::string source, target, main_dependency, working; + std::string comment_buffer; + const char* comment = CM_NULLPTR; + std::vector<std::string> depends, outputs, output, byproducts; + bool verbatim = false; + bool append = false; + bool uses_terminal = false; + std::string implicit_depends_lang; + cmCustomCommand::ImplicitDependsList implicit_depends; + + // Accumulate one command line at a time. + cmCustomCommandLine currentLine; + + // Save all command lines. + cmCustomCommandLines commandLines; + + cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD; + + enum tdoing + { + doing_source, + doing_command, + doing_target, + doing_depends, + doing_implicit_depends_lang, + doing_implicit_depends_file, + doing_main_dependency, + doing_output, + doing_outputs, + doing_byproducts, + doing_comment, + doing_working_directory, + doing_nothing + }; + + tdoing doing = doing_nothing; + + for (unsigned int j = 0; j < args.size(); ++j) { + std::string const& copy = args[j]; + + if (copy == "SOURCE") { + doing = doing_source; + } else if (copy == "COMMAND") { + doing = doing_command; + + // Save the current command before starting the next command. + if (!currentLine.empty()) { + commandLines.push_back(currentLine); + currentLine.clear(); + } + } else if (copy == "PRE_BUILD") { + cctype = cmTarget::PRE_BUILD; + } else if (copy == "PRE_LINK") { + cctype = cmTarget::PRE_LINK; + } else if (copy == "POST_BUILD") { + cctype = cmTarget::POST_BUILD; + } else if (copy == "VERBATIM") { + verbatim = true; + } else if (copy == "APPEND") { + append = true; + } else if (copy == "USES_TERMINAL") { + uses_terminal = true; + } else if (copy == "TARGET") { + doing = doing_target; + } else if (copy == "ARGS") { + // Ignore this old keyword. + } else if (copy == "DEPENDS") { + doing = doing_depends; + } else if (copy == "OUTPUTS") { + doing = doing_outputs; + } else if (copy == "OUTPUT") { + doing = doing_output; + } else if (copy == "BYPRODUCTS") { + doing = doing_byproducts; + } else if (copy == "WORKING_DIRECTORY") { + doing = doing_working_directory; + } else if (copy == "MAIN_DEPENDENCY") { + doing = doing_main_dependency; + } else if (copy == "IMPLICIT_DEPENDS") { + doing = doing_implicit_depends_lang; + } else if (copy == "COMMENT") { + doing = doing_comment; + } else { + std::string filename; + switch (doing) { + case doing_output: + case doing_outputs: + case doing_byproducts: + if (!cmSystemTools::FileIsFullPath(copy.c_str())) { + // This is an output to be generated, so it should be + // under the build tree. CMake 2.4 placed this under the + // source tree. However the only case that this change + // will break is when someone writes + // + // add_custom_command(OUTPUT out.txt ...) + // + // and later references "${CMAKE_CURRENT_SOURCE_DIR}/out.txt". + // This is fairly obscure so we can wait for someone to + // complain. + filename = this->Makefile->GetCurrentBinaryDirectory(); + filename += "/"; + } + filename += copy; + cmSystemTools::ConvertToUnixSlashes(filename); + break; + case doing_source: + // We do not want to convert the argument to SOURCE because + // that option is only available for backward compatibility. + // Old-style use of this command may use the SOURCE==TARGET + // trick which we must preserve. If we convert the source + // to a full path then it will no longer equal the target. + default: + break; + } + + if (cmSystemTools::FileIsFullPath(filename.c_str())) { + filename = cmSystemTools::CollapseFullPath(filename); + } + switch (doing) { + case doing_working_directory: + working = copy; + break; + case doing_source: + source = copy; + break; + case doing_output: + output.push_back(filename); + break; + case doing_main_dependency: + main_dependency = copy; + break; + case doing_implicit_depends_lang: + implicit_depends_lang = copy; + doing = doing_implicit_depends_file; + break; + case doing_implicit_depends_file: { + // An implicit dependency starting point is also an + // explicit dependency. + std::string dep = copy; + cmSystemTools::ConvertToUnixSlashes(dep); + depends.push_back(dep); + + // Add the implicit dependency language and file. + cmCustomCommand::ImplicitDependsPair entry(implicit_depends_lang, + dep); + implicit_depends.push_back(entry); + + // Switch back to looking for a language. + doing = doing_implicit_depends_lang; + } break; + case doing_command: + currentLine.push_back(copy); + break; + case doing_target: + target = copy; + break; + case doing_depends: { + std::string dep = copy; + cmSystemTools::ConvertToUnixSlashes(dep); + depends.push_back(dep); + } break; + case doing_outputs: + outputs.push_back(filename); + break; + case doing_byproducts: + byproducts.push_back(filename); + break; + case doing_comment: + comment_buffer = copy; + comment = comment_buffer.c_str(); + break; + default: + this->SetError("Wrong syntax. Unknown type of argument."); + return false; + } + } + } + + // Store the last command line finished. + if (!currentLine.empty()) { + commandLines.push_back(currentLine); + currentLine.clear(); + } + + // At this point we could complain about the lack of arguments. For + // the moment, let's say that COMMAND, TARGET are always required. + if (output.empty() && target.empty()) { + this->SetError("Wrong syntax. A TARGET or OUTPUT must be specified."); + return false; + } + + if (source.empty() && !target.empty() && !output.empty()) { + this->SetError( + "Wrong syntax. A TARGET and OUTPUT can not both be specified."); + return false; + } + if (append && output.empty()) { + this->SetError("given APPEND option with no OUTPUT."); + return false; + } + + // Make sure the output names and locations are safe. + if (!this->CheckOutputs(output) || !this->CheckOutputs(outputs) || + !this->CheckOutputs(byproducts)) { + return false; + } + + // Check for an append request. + if (append) { + // Lookup an existing command. + if (cmSourceFile* sf = + this->Makefile->GetSourceFileWithOutput(output[0])) { + if (cmCustomCommand* cc = sf->GetCustomCommand()) { + cc->AppendCommands(commandLines); + cc->AppendDepends(depends); + cc->AppendImplicitDepends(implicit_depends); + return true; + } + } + + // No command for this output exists. + std::ostringstream e; + e << "given APPEND option with output \"" << output[0] + << "\" which is not already a custom command output."; + this->SetError(e.str()); + return false; + } + + // Convert working directory to a full path. + if (!working.empty()) { + const char* build_dir = this->Makefile->GetCurrentBinaryDirectory(); + working = cmSystemTools::CollapseFullPath(working, build_dir); + } + + // Choose which mode of the command to use. + bool escapeOldStyle = !verbatim; + if (source.empty() && output.empty()) { + // Source is empty, use the target. + std::vector<std::string> no_depends; + this->Makefile->AddCustomCommandToTarget( + target, byproducts, no_depends, commandLines, cctype, comment, + working.c_str(), escapeOldStyle, uses_terminal); + } else if (target.empty()) { + // Target is empty, use the output. + this->Makefile->AddCustomCommandToOutput( + output, byproducts, depends, main_dependency, commandLines, comment, + working.c_str(), false, escapeOldStyle, uses_terminal); + + // Add implicit dependency scanning requests if any were given. + if (!implicit_depends.empty()) { + bool okay = false; + if (cmSourceFile* sf = + this->Makefile->GetSourceFileWithOutput(output[0])) { + if (cmCustomCommand* cc = sf->GetCustomCommand()) { + okay = true; + cc->SetImplicitDepends(implicit_depends); + } + } + if (!okay) { + std::ostringstream e; + e << "could not locate source file with a custom command producing \"" + << output[0] << "\" even though this command tried to create it!"; + this->SetError(e.str()); + return false; + } + } + } else if (!byproducts.empty()) { + this->SetError("BYPRODUCTS may not be specified with SOURCE signatures"); + return false; + } else if (uses_terminal) { + this->SetError("USES_TERMINAL may not be used with SOURCE signatures"); + return false; + } else { + bool issueMessage = true; + std::ostringstream e; + cmake::MessageType messageType = cmake::AUTHOR_WARNING; + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0050)) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0050) << "\n"; + break; + case cmPolicies::OLD: + issueMessage = false; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + messageType = cmake::FATAL_ERROR; + break; + } + + if (issueMessage) { + e << "The SOURCE signatures of add_custom_command are no longer " + "supported."; + this->Makefile->IssueMessage(messageType, e.str()); + if (messageType == cmake::FATAL_ERROR) { + return false; + } + } + + // Use the old-style mode for backward compatibility. + this->Makefile->AddCustomCommandOldStyle(target, outputs, depends, source, + commandLines, comment); + } + + return true; +} + +bool cmAddCustomCommandCommand::CheckOutputs( + const std::vector<std::string>& outputs) +{ + for (std::vector<std::string>::const_iterator o = outputs.begin(); + o != outputs.end(); ++o) { + // Make sure the file will not be generated into the source + // directory during an out of source build. + if (!this->Makefile->CanIWriteThisFile(o->c_str())) { + std::string e = "attempted to have a file \"" + *o + + "\" in a source directory as an output of custom command."; + this->SetError(e); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + // Make sure the output file name has no invalid characters. + std::string::size_type pos = o->find_first_of("#<>"); + if (pos != o->npos) { + std::ostringstream msg; + msg << "called with OUTPUT containing a \"" << (*o)[pos] + << "\". This character is not allowed."; + this->SetError(msg.str()); + return false; + } + } + return true; +} |