cmExecProgramCommand.cxx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmExecProgramCommand.h"
  4. #include "cmsys/Process.h"
  5. #include <stdio.h>
  6. #include "cmExecutionStatus.h"
  7. #include "cmMakefile.h"
  8. #include "cmProcessOutput.h"
  9. #include "cmStringAlgorithms.h"
  10. #include "cmSystemTools.h"
  11. using Encoding = cmProcessOutput::Encoding;
  12. namespace {
  13. bool RunCommand(std::string command, std::string& output, int& retVal,
  14. const char* directory = nullptr, bool verbose = true,
  15. Encoding encoding = cmProcessOutput::Auto);
  16. }
  17. // cmExecProgramCommand
  18. bool cmExecProgramCommand(std::vector<std::string> const& args,
  19. cmExecutionStatus& status)
  20. {
  21. if (args.empty()) {
  22. status.SetError("called with incorrect number of arguments");
  23. return false;
  24. }
  25. std::string arguments;
  26. bool doingargs = false;
  27. int count = 0;
  28. std::string output_variable;
  29. bool haveoutput_variable = false;
  30. std::string return_variable;
  31. bool havereturn_variable = false;
  32. for (std::string const& arg : args) {
  33. if (arg == "OUTPUT_VARIABLE") {
  34. count++;
  35. doingargs = false;
  36. havereturn_variable = false;
  37. haveoutput_variable = true;
  38. } else if (haveoutput_variable) {
  39. if (!output_variable.empty()) {
  40. status.SetError("called with incorrect number of arguments");
  41. return false;
  42. }
  43. output_variable = arg;
  44. haveoutput_variable = false;
  45. count++;
  46. } else if (arg == "RETURN_VALUE") {
  47. count++;
  48. doingargs = false;
  49. haveoutput_variable = false;
  50. havereturn_variable = true;
  51. } else if (havereturn_variable) {
  52. if (!return_variable.empty()) {
  53. status.SetError("called with incorrect number of arguments");
  54. return false;
  55. }
  56. return_variable = arg;
  57. havereturn_variable = false;
  58. count++;
  59. } else if (arg == "ARGS") {
  60. count++;
  61. havereturn_variable = false;
  62. haveoutput_variable = false;
  63. doingargs = true;
  64. } else if (doingargs) {
  65. arguments += arg;
  66. arguments += " ";
  67. count++;
  68. }
  69. }
  70. std::string command;
  71. if (!arguments.empty()) {
  72. command = cmStrCat(cmSystemTools::ConvertToRunCommandPath(args[0]), ' ',
  73. arguments);
  74. } else {
  75. command = args[0];
  76. }
  77. bool verbose = true;
  78. if (!output_variable.empty()) {
  79. verbose = false;
  80. }
  81. int retVal = 0;
  82. std::string output;
  83. bool result = true;
  84. if (args.size() - count == 2) {
  85. cmSystemTools::MakeDirectory(args[1]);
  86. result = RunCommand(command, output, retVal, args[1].c_str(), verbose);
  87. } else {
  88. result = RunCommand(command, output, retVal, nullptr, verbose);
  89. }
  90. if (!result) {
  91. retVal = -1;
  92. }
  93. if (!output_variable.empty()) {
  94. std::string::size_type first = output.find_first_not_of(" \n\t\r");
  95. std::string::size_type last = output.find_last_not_of(" \n\t\r");
  96. if (first == std::string::npos) {
  97. first = 0;
  98. }
  99. if (last == std::string::npos) {
  100. last = output.size() - 1;
  101. }
  102. std::string coutput = std::string(output, first, last - first + 1);
  103. status.GetMakefile().AddDefinition(output_variable, coutput);
  104. }
  105. if (!return_variable.empty()) {
  106. char buffer[100];
  107. sprintf(buffer, "%d", retVal);
  108. status.GetMakefile().AddDefinition(return_variable, buffer);
  109. }
  110. return true;
  111. }
  112. namespace {
  113. bool RunCommand(std::string command, std::string& output, int& retVal,
  114. const char* dir, bool verbose, Encoding encoding)
  115. {
  116. if (cmSystemTools::GetRunCommandOutput()) {
  117. verbose = false;
  118. }
  119. #if defined(_WIN32) && !defined(__CYGWIN__)
  120. // if the command does not start with a quote, then
  121. // try to find the program, and if the program can not be
  122. // found use system to run the command as it must be a built in
  123. // shell command like echo or dir
  124. if (!command.empty() && command[0] == '\"') {
  125. // count the number of quotes
  126. int count = 0;
  127. for (char c : command) {
  128. if (c == '\"') {
  129. count++;
  130. if (count > 2) {
  131. break;
  132. }
  133. }
  134. }
  135. // if there are more than two double quotes use
  136. // GetShortPathName, the cmd.exe program in windows which
  137. // is used by system fails to execute if there are more than
  138. // one set of quotes in the arguments
  139. if (count > 2) {
  140. cmsys::RegularExpression quoted("^\"([^\"]*)\"[ \t](.*)");
  141. if (quoted.find(command)) {
  142. std::string shortCmd;
  143. std::string cmd = quoted.match(1);
  144. std::string args = quoted.match(2);
  145. if (!cmSystemTools::FileExists(cmd)) {
  146. shortCmd = cmd;
  147. } else if (!cmSystemTools::GetShortPath(cmd, shortCmd)) {
  148. cmSystemTools::Error("GetShortPath failed for " + cmd);
  149. return false;
  150. }
  151. shortCmd += " ";
  152. shortCmd += args;
  153. command = shortCmd;
  154. } else {
  155. cmSystemTools::Error("Could not parse command line with quotes " +
  156. command);
  157. }
  158. }
  159. }
  160. #endif
  161. // Allocate a process instance.
  162. cmsysProcess* cp = cmsysProcess_New();
  163. if (!cp) {
  164. cmSystemTools::Error("Error allocating process instance.");
  165. return false;
  166. }
  167. #if defined(_WIN32) && !defined(__CYGWIN__)
  168. if (dir) {
  169. cmsysProcess_SetWorkingDirectory(cp, dir);
  170. }
  171. if (cmSystemTools::GetRunCommandHideConsole()) {
  172. cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
  173. }
  174. cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
  175. const char* cmd[] = { command.c_str(), nullptr };
  176. cmsysProcess_SetCommand(cp, cmd);
  177. #else
  178. std::string commandInDir;
  179. if (dir) {
  180. commandInDir = cmStrCat("cd \"", dir, "\" && ", command);
  181. } else {
  182. commandInDir = command;
  183. }
  184. # ifndef __VMS
  185. commandInDir += " 2>&1";
  186. # endif
  187. command = commandInDir;
  188. if (verbose) {
  189. cmSystemTools::Stdout("running ");
  190. cmSystemTools::Stdout(command);
  191. cmSystemTools::Stdout("\n");
  192. }
  193. fflush(stdout);
  194. fflush(stderr);
  195. const char* cmd[] = { "/bin/sh", "-c", command.c_str(), nullptr };
  196. cmsysProcess_SetCommand(cp, cmd);
  197. #endif
  198. cmsysProcess_Execute(cp);
  199. // Read the process output.
  200. int length;
  201. char* data;
  202. int p;
  203. cmProcessOutput processOutput(encoding);
  204. std::string strdata;
  205. while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
  206. if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) {
  207. if (verbose) {
  208. processOutput.DecodeText(data, length, strdata);
  209. cmSystemTools::Stdout(strdata);
  210. }
  211. output.append(data, length);
  212. }
  213. }
  214. if (verbose) {
  215. processOutput.DecodeText(std::string(), strdata);
  216. if (!strdata.empty()) {
  217. cmSystemTools::Stdout(strdata);
  218. }
  219. }
  220. // All output has been read. Wait for the process to exit.
  221. cmsysProcess_WaitForExit(cp, nullptr);
  222. processOutput.DecodeText(output, output);
  223. // Check the result of running the process.
  224. std::string msg;
  225. switch (cmsysProcess_GetState(cp)) {
  226. case cmsysProcess_State_Exited:
  227. retVal = cmsysProcess_GetExitValue(cp);
  228. break;
  229. case cmsysProcess_State_Exception:
  230. retVal = -1;
  231. msg += "\nProcess terminated due to: ";
  232. msg += cmsysProcess_GetExceptionString(cp);
  233. break;
  234. case cmsysProcess_State_Error:
  235. retVal = -1;
  236. msg += "\nProcess failed because: ";
  237. msg += cmsysProcess_GetErrorString(cp);
  238. break;
  239. case cmsysProcess_State_Expired:
  240. retVal = -1;
  241. msg += "\nProcess terminated due to timeout.";
  242. break;
  243. }
  244. if (!msg.empty()) {
  245. #if defined(_WIN32) && !defined(__CYGWIN__)
  246. // Old Windows process execution printed this info.
  247. msg += "\n\nfor command: ";
  248. msg += command;
  249. if (dir) {
  250. msg += "\nin dir: ";
  251. msg += dir;
  252. }
  253. msg += "\n";
  254. if (verbose) {
  255. cmSystemTools::Stdout(msg);
  256. }
  257. output += msg;
  258. #else
  259. // Old UNIX process execution only put message in output.
  260. output += msg;
  261. #endif
  262. }
  263. // Delete the process instance.
  264. cmsysProcess_Delete(cp);
  265. return true;
  266. }
  267. }