| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmExecProgramCommand.h" | |
| #include <cstdio> | |
| #include <utility> | |
| #include "cmsys/Process.h" | |
| #include "cmExecutionStatus.h" | |
| #include "cmMakefile.h" | |
| #include "cmProcessOutput.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| using Encoding = cmProcessOutput::Encoding; | |
| namespace { | |
| bool RunCommand(std::string command, std::string& output, int& retVal, | |
| char const* directory = nullptr, bool verbose = true, | |
| Encoding encoding = cmProcessOutput::Auto); | |
| } | |
| // cmExecProgramCommand | |
| bool cmExecProgramCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args.empty()) { | |
| status.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()) { | |
| status.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()) { | |
| status.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 = cmStrCat(std::move(arguments), arg, ' '); | |
| count++; | |
| } | |
| } | |
| std::string command; | |
| if (!arguments.empty()) { | |
| command = cmStrCat(cmSystemTools::ConvertToRunCommandPath(args[0]), ' ', | |
| 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 = RunCommand(command, output, retVal, args[1].c_str(), verbose); | |
| } else { | |
| result = RunCommand(command, 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); | |
| status.GetMakefile().AddDefinition(output_variable, coutput); | |
| } | |
| if (!return_variable.empty()) { | |
| char buffer[100]; | |
| snprintf(buffer, sizeof(buffer), "%d", retVal); | |
| status.GetMakefile().AddDefinition(return_variable, buffer); | |
| } | |
| return true; | |
| } | |
| namespace { | |
| bool RunCommand(std::string command, std::string& output, int& retVal, | |
| char const* 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 | |
| if (!command.empty() && command[0] == '\"') { | |
| // count the number of quotes | |
| int count = 0; | |
| for (char c : command) { | |
| if (c == '\"') { | |
| 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 shortCmd; | |
| std::string cmd = quoted.match(1); | |
| std::string args = quoted.match(2); | |
| if (!cmSystemTools::FileExists(cmd)) { | |
| shortCmd = std::move(cmd); | |
| } else if (!cmSystemTools::GetShortPath(cmd, shortCmd)) { | |
| cmSystemTools::Error("GetShortPath failed for " + cmd); | |
| return false; | |
| } | |
| shortCmd = cmStrCat(std::move(shortCmd), ' ', std::move(args)); | |
| command = shortCmd; | |
| } 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); | |
| char const* cmd[] = { command.c_str(), nullptr }; | |
| cmsysProcess_SetCommand(cp, cmd); | |
| #else | |
| std::string commandInDir; | |
| if (dir) { | |
| commandInDir = cmStrCat("cd \"", dir, "\" && ", command); | |
| } else { | |
| commandInDir = command; | |
| } | |
| # ifndef __VMS | |
| commandInDir += " 2>&1"; | |
| # endif | |
| command = commandInDir; | |
| if (verbose) { | |
| cmSystemTools::Stdout(cmStrCat("running ", command, '\n')); | |
| } | |
| fflush(stdout); | |
| fflush(stderr); | |
| char const* cmd[] = { "/bin/sh", "-c", command.c_str(), 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))) { | |
| if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) { | |
| if (verbose) { | |
| processOutput.DecodeText(data, length, strdata); | |
| cmSystemTools::Stdout(strdata); | |
| } | |
| output.append(data, length); | |
| } | |
| } | |
| if (verbose) { | |
| processOutput.DecodeText(std::string(), strdata); | |
| if (!strdata.empty()) { | |
| cmSystemTools::Stdout(strdata); | |
| } | |
| } | |
| // 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 = cmStrCat(std::move(msg), "\nProcess terminated due to: ", | |
| cmsysProcess_GetExceptionString(cp)); | |
| break; | |
| case cmsysProcess_State_Error: | |
| retVal = -1; | |
| msg = cmStrCat(std::move(msg), "\nProcess failed because: ", | |
| 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 = cmStrCat(std::move(msg), "\n\nfor command: ", command); | |
| if (dir) { | |
| msg = cmStrCat(std::move(msg), "\nin dir: ", dir); | |
| } | |
| msg += "\n"; | |
| if (verbose) { | |
| cmSystemTools::Stdout(msg); | |
| } | |
| output += msg; | |
| #else | |
| // Old UNIX process execution only put message in output. | |
| output += msg; | |
| #endif | |
| } | |
| // Delete the process instance. | |
| cmsysProcess_Delete(cp); | |
| return true; | |
| } | |
| } |