/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExecProgramCommand.h" #include "cmsys/Process.h" #include #include "cmMakefile.h" #include "cmProcessOutput.h" #include "cmSystemTools.h" class cmExecutionStatus; // cmExecProgramCommand bool cmExecProgramCommand::InitialPass(std::vector const& args, cmExecutionStatus&) { if (args.empty()) { this->SetError("called with incorrect number of arguments"); return false; } std::string arguments; bool doingargs = false; int count = 0; std::string output_variable; bool haveoutput_variable = false; std::string return_variable; bool havereturn_variable = false; for (std::string const& arg : args) { if (arg == "OUTPUT_VARIABLE") { count++; doingargs = false; havereturn_variable = false; haveoutput_variable = true; } else if (haveoutput_variable) { if (!output_variable.empty()) { this->SetError("called with incorrect number of arguments"); return false; } output_variable = arg; haveoutput_variable = false; count++; } else if (arg == "RETURN_VALUE") { count++; doingargs = false; haveoutput_variable = false; havereturn_variable = true; } else if (havereturn_variable) { if (!return_variable.empty()) { this->SetError("called with incorrect number of arguments"); return false; } return_variable = arg; havereturn_variable = false; count++; } else if (arg == "ARGS") { count++; havereturn_variable = false; haveoutput_variable = false; doingargs = true; } else if (doingargs) { arguments += arg; arguments += " "; count++; } } std::string command; if (!arguments.empty()) { command = cmSystemTools::ConvertToRunCommandPath(args[0].c_str()); command += " "; command += arguments; } else { command = args[0]; } bool verbose = true; if (!output_variable.empty()) { verbose = false; } int retVal = 0; std::string output; bool result = true; if (args.size() - count == 2) { cmSystemTools::MakeDirectory(args[1]); result = cmExecProgramCommand::RunCommand(command.c_str(), output, retVal, args[1].c_str(), verbose); } else { result = cmExecProgramCommand::RunCommand(command.c_str(), output, retVal, nullptr, verbose); } if (!result) { retVal = -1; } if (!output_variable.empty()) { std::string::size_type first = output.find_first_not_of(" \n\t\r"); std::string::size_type last = output.find_last_not_of(" \n\t\r"); if (first == std::string::npos) { first = 0; } if (last == std::string::npos) { last = output.size() - 1; } std::string coutput = std::string(output, first, last - first + 1); this->Makefile->AddDefinition(output_variable, coutput.c_str()); } if (!return_variable.empty()) { char buffer[100]; sprintf(buffer, "%d", retVal); this->Makefile->AddDefinition(return_variable, buffer); } return true; } bool cmExecProgramCommand::RunCommand(const char* command, std::string& output, int& retVal, const char* dir, bool verbose, Encoding encoding) { if (cmSystemTools::GetRunCommandOutput()) { verbose = false; } #if defined(_WIN32) && !defined(__CYGWIN__) // if the command does not start with a quote, then // try to find the program, and if the program can not be // found use system to run the command as it must be a built in // shell command like echo or dir int count = 0; std::string shortCmd; if (command[0] == '\"') { // count the number of quotes for (const char* s = command; *s != 0; ++s) { if (*s == '\"') { count++; if (count > 2) { break; } } } // if there are more than two double quotes use // GetShortPathName, the cmd.exe program in windows which // is used by system fails to execute if there are more than // one set of quotes in the arguments if (count > 2) { cmsys::RegularExpression quoted("^\"([^\"]*)\"[ \t](.*)"); if (quoted.find(command)) { std::string cmd = quoted.match(1); std::string args = quoted.match(2); if (!cmSystemTools::FileExists(cmd)) { shortCmd = cmd; } else if (!cmSystemTools::GetShortPath(cmd.c_str(), shortCmd)) { cmSystemTools::Error("GetShortPath failed for ", cmd.c_str()); return false; } shortCmd += " "; shortCmd += args; command = shortCmd.c_str(); } else { cmSystemTools::Error("Could not parse command line with quotes ", command); } } } #endif // Allocate a process instance. cmsysProcess* cp = cmsysProcess_New(); if (!cp) { cmSystemTools::Error("Error allocating process instance."); return false; } #if defined(_WIN32) && !defined(__CYGWIN__) if (dir) { cmsysProcess_SetWorkingDirectory(cp, dir); } if (cmSystemTools::GetRunCommandHideConsole()) { cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); } cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1); const char* cmd[] = { command, 0 }; cmsysProcess_SetCommand(cp, cmd); #else std::string commandInDir; if (dir) { commandInDir = "cd \""; commandInDir += dir; commandInDir += "\" && "; commandInDir += command; } else { commandInDir = command; } #ifndef __VMS commandInDir += " 2>&1"; #endif command = commandInDir.c_str(); if (verbose) { cmSystemTools::Stdout("running "); cmSystemTools::Stdout(command); cmSystemTools::Stdout("\n"); } fflush(stdout); fflush(stderr); const char* cmd[] = { "/bin/sh", "-c", command, nullptr }; cmsysProcess_SetCommand(cp, cmd); #endif cmsysProcess_Execute(cp); // Read the process output. int length; char* data; int p; cmProcessOutput processOutput(encoding); std::string strdata; while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr), p)) { if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) { if (verbose) { processOutput.DecodeText(data, length, strdata); cmSystemTools::Stdout(strdata.c_str(), strdata.size()); } output.append(data, length); } } if (verbose) { processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { cmSystemTools::Stdout(strdata.c_str(), strdata.size()); } } // All output has been read. Wait for the process to exit. cmsysProcess_WaitForExit(cp, nullptr); processOutput.DecodeText(output, output); // Check the result of running the process. std::string msg; switch (cmsysProcess_GetState(cp)) { case cmsysProcess_State_Exited: retVal = cmsysProcess_GetExitValue(cp); break; case cmsysProcess_State_Exception: retVal = -1; msg += "\nProcess terminated due to: "; msg += cmsysProcess_GetExceptionString(cp); break; case cmsysProcess_State_Error: retVal = -1; msg += "\nProcess failed because: "; msg += cmsysProcess_GetErrorString(cp); break; case cmsysProcess_State_Expired: retVal = -1; msg += "\nProcess terminated due to timeout."; break; } if (!msg.empty()) { #if defined(_WIN32) && !defined(__CYGWIN__) // Old Windows process execution printed this info. msg += "\n\nfor command: "; msg += command; if (dir) { msg += "\nin dir: "; msg += dir; } msg += "\n"; if (verbose) { cmSystemTools::Stdout(msg.c_str()); } output += msg; #else // Old UNIX process execution only put message in output. output += msg; #endif } // Delete the process instance. cmsysProcess_Delete(cp); return true; }