cmExecProgramCommand.cxx 7.9 KB

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