|
|
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information.
=========================================================================*/ #include "cmExecuteProcessCommand.h"
#include "cmSystemTools.h"
#include <cmsys/Process.h>
#include <ctype.h> /* isspace */
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) { if(args.size() < 1 ) { 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; 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(doing_command) { cmds[command_index].push_back(args[i].c_str()); } else { cmOStringStream e; e << " given unknown argument \"" << args[i] << "\"."; this->SetError(e.str().c_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.c_str()); 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; } else { // Add the null terminating pointer to the command argument list.
cmds[i].push_back(0); } }
// 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 = (output_variable == error_variable); if(error_variable.empty() && !error_quiet) { cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); } 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()) { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, error_file.c_str()); }
// 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; while((p = cmsysProcess_WaitForData(cp, &data, &length, 0), p)) { // Put the output in the right place.
if(p == cmsysProcess_Pipe_STDOUT && !output_quiet || p == cmsysProcess_Pipe_STDERR && !error_quiet && merge_output) { if(output_variable.empty()) { cmSystemTools::Stdout(data, length); } else { cmExecuteProcessCommandAppend(tempOutput, data, length); } } else if(p == cmsysProcess_Pipe_STDERR && !error_quiet) { if(!error_variable.empty()) { cmExecuteProcessCommandAppend(tempError, data, length); } } }
// All output has been read. Wait for the process to exit.
cmsysProcess_WaitForExit(cp, 0);
// 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.size()) { this->Makefile->AddDefinition(output_variable.c_str(), &*tempOutput.begin()); } if(!merge_output && !error_variable.empty() && tempError.size()) { this->Makefile->AddDefinition(error_variable.c_str(), &*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.c_str(), buf); } break; case cmsysProcess_State_Exception: this->Makefile->AddDefinition(result_variable.c_str(), cmsysProcess_GetExceptionString(cp)); break; case cmsysProcess_State_Error: this->Makefile->AddDefinition(result_variable.c_str(), cmsysProcess_GetErrorString(cp)); break; case cmsysProcess_State_Expired: this->Makefile->AddDefinition(result_variable.c_str(), "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); }
|