cmExecProgramCommand.cxx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 "cmSystemTools.h"
  10. typedef cmProcessOutput::Encoding Encoding;
  11. namespace {
  12. bool RunCommand(std::string command, std::string& output, int& retVal,
  13. const char* directory = nullptr, bool verbose = true,
  14. Encoding encoding = cmProcessOutput::Auto);
  15. }
  16. // cmExecProgramCommand
  17. bool cmExecProgramCommand(std::vector<std::string> const& args,
  18. cmExecutionStatus& status)
  19. {
  20. if (args.empty()) {
  21. status.SetError("called with incorrect number of arguments");
  22. return false;
  23. }
  24. std::string arguments;
  25. bool doingargs = false;
  26. int count = 0;
  27. std::string output_variable;
  28. bool haveoutput_variable = false;
  29. std::string return_variable;
  30. bool havereturn_variable = false;
  31. for (std::string const& arg : args) {
  32. if (arg == "OUTPUT_VARIABLE") {
  33. count++;
  34. doingargs = false;
  35. havereturn_variable = false;
  36. haveoutput_variable = true;
  37. } else if (haveoutput_variable) {
  38. if (!output_variable.empty()) {
  39. status.SetError("called with incorrect number of arguments");
  40. return false;
  41. }
  42. output_variable = arg;
  43. haveoutput_variable = false;
  44. count++;
  45. } else if (arg == "RETURN_VALUE") {
  46. count++;
  47. doingargs = false;
  48. haveoutput_variable = false;
  49. havereturn_variable = true;
  50. } else if (havereturn_variable) {
  51. if (!return_variable.empty()) {
  52. status.SetError("called with incorrect number of arguments");
  53. return false;
  54. }
  55. return_variable = arg;
  56. havereturn_variable = false;
  57. count++;
  58. } else if (arg == "ARGS") {
  59. count++;
  60. havereturn_variable = false;
  61. haveoutput_variable = false;
  62. doingargs = true;
  63. } else if (doingargs) {
  64. arguments += arg;
  65. arguments += " ";
  66. count++;
  67. }
  68. }
  69. std::string command;
  70. if (!arguments.empty()) {
  71. command = cmSystemTools::ConvertToRunCommandPath(args[0]);
  72. command += " ";
  73. command += 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 = "cd \"";
  181. commandInDir += dir;
  182. commandInDir += "\" && ";
  183. commandInDir += command;
  184. } else {
  185. commandInDir = command;
  186. }
  187. # ifndef __VMS
  188. commandInDir += " 2>&1";
  189. # endif
  190. command = commandInDir;
  191. if (verbose) {
  192. cmSystemTools::Stdout("running ");
  193. cmSystemTools::Stdout(command);
  194. cmSystemTools::Stdout("\n");
  195. }
  196. fflush(stdout);
  197. fflush(stderr);
  198. const char* cmd[] = { "/bin/sh", "-c", command.c_str(), nullptr };
  199. cmsysProcess_SetCommand(cp, cmd);
  200. #endif
  201. cmsysProcess_Execute(cp);
  202. // Read the process output.
  203. int length;
  204. char* data;
  205. int p;
  206. cmProcessOutput processOutput(encoding);
  207. std::string strdata;
  208. while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
  209. if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) {
  210. if (verbose) {
  211. processOutput.DecodeText(data, length, strdata);
  212. cmSystemTools::Stdout(strdata);
  213. }
  214. output.append(data, length);
  215. }
  216. }
  217. if (verbose) {
  218. processOutput.DecodeText(std::string(), strdata);
  219. if (!strdata.empty()) {
  220. cmSystemTools::Stdout(strdata);
  221. }
  222. }
  223. // All output has been read. Wait for the process to exit.
  224. cmsysProcess_WaitForExit(cp, nullptr);
  225. processOutput.DecodeText(output, output);
  226. // Check the result of running the process.
  227. std::string msg;
  228. switch (cmsysProcess_GetState(cp)) {
  229. case cmsysProcess_State_Exited:
  230. retVal = cmsysProcess_GetExitValue(cp);
  231. break;
  232. case cmsysProcess_State_Exception:
  233. retVal = -1;
  234. msg += "\nProcess terminated due to: ";
  235. msg += cmsysProcess_GetExceptionString(cp);
  236. break;
  237. case cmsysProcess_State_Error:
  238. retVal = -1;
  239. msg += "\nProcess failed because: ";
  240. msg += cmsysProcess_GetErrorString(cp);
  241. break;
  242. case cmsysProcess_State_Expired:
  243. retVal = -1;
  244. msg += "\nProcess terminated due to timeout.";
  245. break;
  246. }
  247. if (!msg.empty()) {
  248. #if defined(_WIN32) && !defined(__CYGWIN__)
  249. // Old Windows process execution printed this info.
  250. msg += "\n\nfor command: ";
  251. msg += command;
  252. if (dir) {
  253. msg += "\nin dir: ";
  254. msg += dir;
  255. }
  256. msg += "\n";
  257. if (verbose) {
  258. cmSystemTools::Stdout(msg);
  259. }
  260. output += msg;
  261. #else
  262. // Old UNIX process execution only put message in output.
  263. output += msg;
  264. #endif
  265. }
  266. // Delete the process instance.
  267. cmsysProcess_Delete(cp);
  268. return true;
  269. }
  270. }