cmExecProgramCommand.cxx 7.6 KB

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