diff options
Diffstat (limited to 'Source/cmExecuteProcessCommand.cxx')
-rw-r--r-- | Source/cmExecuteProcessCommand.cxx | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx new file mode 100644 index 0000000..92cdf64 --- /dev/null +++ b/Source/cmExecuteProcessCommand.cxx @@ -0,0 +1,359 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExecuteProcessCommand.h" + +#include <cmsys/Process.h> +#include <ctype.h> /* isspace */ +#include <sstream> +#include <stdio.h> + +#include "cmMakefile.h" +#include "cmProcessOutput.h" +#include "cmSystemTools.h" + +class cmExecutionStatus; + +static bool cmExecuteProcessCommandIsWhitespace(char c) +{ + return (isspace((int)c) || c == '\n' || c == '\r'); +} + +void cmExecuteProcessCommandFixText(std::vector<char>& output, + bool strip_trailing_whitespace); +void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, + int length); + +// cmExecuteProcessCommand +bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, + cmExecutionStatus&) +{ + if (args.empty()) { + this->SetError("called with incorrect number of arguments"); + return false; + } + std::vector<std::vector<const char*> > cmds; + std::string arguments; + bool doing_command = false; + size_t command_index = 0; + bool output_quiet = false; + bool error_quiet = false; + bool output_strip_trailing_whitespace = false; + bool error_strip_trailing_whitespace = false; + std::string timeout_string; + std::string input_file; + std::string output_file; + std::string error_file; + std::string output_variable; + std::string error_variable; + std::string result_variable; + std::string working_directory; + cmProcessOutput::Encoding encoding = cmProcessOutput::None; + for (size_t i = 0; i < args.size(); ++i) { + if (args[i] == "COMMAND") { + doing_command = true; + command_index = cmds.size(); + cmds.push_back(std::vector<const char*>()); + } else if (args[i] == "OUTPUT_VARIABLE") { + doing_command = false; + if (++i < args.size()) { + output_variable = args[i]; + } else { + this->SetError(" called with no value for OUTPUT_VARIABLE."); + return false; + } + } else if (args[i] == "ERROR_VARIABLE") { + doing_command = false; + if (++i < args.size()) { + error_variable = args[i]; + } else { + this->SetError(" called with no value for ERROR_VARIABLE."); + return false; + } + } else if (args[i] == "RESULT_VARIABLE") { + doing_command = false; + if (++i < args.size()) { + result_variable = args[i]; + } else { + this->SetError(" called with no value for RESULT_VARIABLE."); + return false; + } + } else if (args[i] == "WORKING_DIRECTORY") { + doing_command = false; + if (++i < args.size()) { + working_directory = args[i]; + } else { + this->SetError(" called with no value for WORKING_DIRECTORY."); + return false; + } + } else if (args[i] == "INPUT_FILE") { + doing_command = false; + if (++i < args.size()) { + input_file = args[i]; + } else { + this->SetError(" called with no value for INPUT_FILE."); + return false; + } + } else if (args[i] == "OUTPUT_FILE") { + doing_command = false; + if (++i < args.size()) { + output_file = args[i]; + } else { + this->SetError(" called with no value for OUTPUT_FILE."); + return false; + } + } else if (args[i] == "ERROR_FILE") { + doing_command = false; + if (++i < args.size()) { + error_file = args[i]; + } else { + this->SetError(" called with no value for ERROR_FILE."); + return false; + } + } else if (args[i] == "TIMEOUT") { + doing_command = false; + if (++i < args.size()) { + timeout_string = args[i]; + } else { + this->SetError(" called with no value for TIMEOUT."); + return false; + } + } else if (args[i] == "OUTPUT_QUIET") { + doing_command = false; + output_quiet = true; + } else if (args[i] == "ERROR_QUIET") { + doing_command = false; + error_quiet = true; + } else if (args[i] == "OUTPUT_STRIP_TRAILING_WHITESPACE") { + doing_command = false; + output_strip_trailing_whitespace = true; + } else if (args[i] == "ERROR_STRIP_TRAILING_WHITESPACE") { + doing_command = false; + error_strip_trailing_whitespace = true; + } else if (args[i] == "ENCODING") { + doing_command = false; + if (++i < args.size()) { + encoding = cmProcessOutput::FindEncoding(args[i]); + } else { + this->SetError(" called with no value for ENCODING."); + return false; + } + } else if (doing_command) { + cmds[command_index].push_back(args[i].c_str()); + } else { + std::ostringstream e; + e << " given unknown argument \"" << args[i] << "\"."; + this->SetError(e.str()); + return false; + } + } + + if (!this->Makefile->CanIWriteThisFile(output_file.c_str())) { + std::string e = "attempted to output into a file: " + output_file + + " into a source directory."; + this->SetError(e); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + // Check for commands given. + if (cmds.empty()) { + this->SetError(" called with no COMMAND argument."); + return false; + } + for (unsigned int i = 0; i < cmds.size(); ++i) { + if (cmds[i].empty()) { + this->SetError(" given COMMAND argument with no value."); + return false; + } + // Add the null terminating pointer to the command argument list. + cmds[i].push_back(CM_NULLPTR); + } + + // Parse the timeout string. + double timeout = -1; + if (!timeout_string.empty()) { + if (sscanf(timeout_string.c_str(), "%lg", &timeout) != 1) { + this->SetError(" called with TIMEOUT value that could not be parsed."); + return false; + } + } + + // Create a process instance. + cmsysProcess* cp = cmsysProcess_New(); + + // Set the command sequence. + for (unsigned int i = 0; i < cmds.size(); ++i) { + cmsysProcess_AddCommand(cp, &*cmds[i].begin()); + } + + // Set the process working directory. + if (!working_directory.empty()) { + cmsysProcess_SetWorkingDirectory(cp, working_directory.c_str()); + } + + // Always hide the process window. + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + + // Check the output variables. + bool merge_output = false; + if (!input_file.empty()) { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, input_file.c_str()); + } + if (!output_file.empty()) { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT, + output_file.c_str()); + } + if (!error_file.empty()) { + if (error_file == output_file) { + merge_output = true; + } else { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, + error_file.c_str()); + } + } + if (!output_variable.empty() && output_variable == error_variable) { + merge_output = true; + } + if (merge_output) { + cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); + } + + // Set the timeout if any. + if (timeout >= 0) { + cmsysProcess_SetTimeout(cp, timeout); + } + + // Start the process. + cmsysProcess_Execute(cp); + + // Read the process output. + std::vector<char> tempOutput; + std::vector<char> tempError; + int length; + char* data; + int p; + cmProcessOutput processOutput(encoding); + std::string strdata; + while ((p = cmsysProcess_WaitForData(cp, &data, &length, CM_NULLPTR), p)) { + // Put the output in the right place. + if (p == cmsysProcess_Pipe_STDOUT && !output_quiet) { + if (output_variable.empty()) { + processOutput.DecodeText(data, length, strdata, 1); + cmSystemTools::Stdout(strdata.c_str(), strdata.size()); + } else { + cmExecuteProcessCommandAppend(tempOutput, data, length); + } + } else if (p == cmsysProcess_Pipe_STDERR && !error_quiet) { + if (error_variable.empty()) { + processOutput.DecodeText(data, length, strdata, 2); + cmSystemTools::Stderr(strdata.c_str(), strdata.size()); + } else { + cmExecuteProcessCommandAppend(tempError, data, length); + } + } + } + if (!output_quiet && output_variable.empty()) { + processOutput.DecodeText(std::string(), strdata, 1); + if (!strdata.empty()) { + cmSystemTools::Stdout(strdata.c_str(), strdata.size()); + } + } + if (!error_quiet && error_variable.empty()) { + processOutput.DecodeText(std::string(), strdata, 2); + if (!strdata.empty()) { + cmSystemTools::Stderr(strdata.c_str(), strdata.size()); + } + } + + // All output has been read. Wait for the process to exit. + cmsysProcess_WaitForExit(cp, CM_NULLPTR); + processOutput.DecodeText(tempOutput, tempOutput); + processOutput.DecodeText(tempError, tempError); + + // Fix the text in the output strings. + cmExecuteProcessCommandFixText(tempOutput, output_strip_trailing_whitespace); + cmExecuteProcessCommandFixText(tempError, error_strip_trailing_whitespace); + + // Store the output obtained. + if (!output_variable.empty() && !tempOutput.empty()) { + this->Makefile->AddDefinition(output_variable, &*tempOutput.begin()); + } + if (!merge_output && !error_variable.empty() && !tempError.empty()) { + this->Makefile->AddDefinition(error_variable, &*tempError.begin()); + } + + // Store the result of running the process. + if (!result_variable.empty()) { + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + int v = cmsysProcess_GetExitValue(cp); + char buf[100]; + sprintf(buf, "%d", v); + this->Makefile->AddDefinition(result_variable, buf); + } break; + case cmsysProcess_State_Exception: + this->Makefile->AddDefinition(result_variable, + cmsysProcess_GetExceptionString(cp)); + break; + case cmsysProcess_State_Error: + this->Makefile->AddDefinition(result_variable, + cmsysProcess_GetErrorString(cp)); + break; + case cmsysProcess_State_Expired: + this->Makefile->AddDefinition(result_variable, + "Process terminated due to timeout"); + break; + } + } + + // Delete the process instance. + cmsysProcess_Delete(cp); + + return true; +} + +void cmExecuteProcessCommandFixText(std::vector<char>& output, + bool strip_trailing_whitespace) +{ + // Remove \0 characters and the \r part of \r\n pairs. + unsigned int in_index = 0; + unsigned int out_index = 0; + while (in_index < output.size()) { + char c = output[in_index++]; + if ((c != '\r' || + !(in_index < output.size() && output[in_index] == '\n')) && + c != '\0') { + output[out_index++] = c; + } + } + + // Remove trailing whitespace if requested. + if (strip_trailing_whitespace) { + while (out_index > 0 && + cmExecuteProcessCommandIsWhitespace(output[out_index - 1])) { + --out_index; + } + } + + // Shrink the vector to the size needed. + output.resize(out_index); + + // Put a terminator on the text string. + output.push_back('\0'); +} + +void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, + int length) +{ +#if defined(__APPLE__) + // HACK on Apple to work around bug with inserting at the + // end of an empty vector. This resulted in random failures + // that were hard to reproduce. + if (output.empty() && length > 0) { + output.push_back(data[0]); + ++data; + --length; + } +#endif + output.insert(output.end(), data, data + length); +} |