cmTryRunCommand.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmTryRunCommand.h"
  14. #include "cmCacheManager.h"
  15. #include "cmTryCompileCommand.h"
  16. // cmTryRunCommand
  17. bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv)
  18. {
  19. if(argv.size() < 4)
  20. {
  21. return false;
  22. }
  23. // build an arg list for TryCompile and extract the runArgs
  24. std::vector<std::string> tryCompile;
  25. this->CompileResultVariable = "";
  26. this->RunResultVariable = "";
  27. this->OutputVariable = "";
  28. this->RunOutputVariable = "";
  29. this->CompileOutputVariable = "";
  30. std::string runArgs;
  31. unsigned int i;
  32. for (i = 1; i < argv.size(); ++i)
  33. {
  34. if (argv[i] == "ARGS")
  35. {
  36. ++i;
  37. while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
  38. argv[i] != "CMAKE_FLAGS")
  39. {
  40. runArgs += " ";
  41. runArgs += argv[i];
  42. ++i;
  43. }
  44. if (i < argv.size())
  45. {
  46. tryCompile.push_back(argv[i]);
  47. }
  48. }
  49. else
  50. {
  51. if (argv[i] == "OUTPUT_VARIABLE")
  52. {
  53. if ( argv.size() <= (i+1) )
  54. {
  55. cmSystemTools::Error(
  56. "OUTPUT_VARIABLE specified but there is no variable");
  57. return false;
  58. }
  59. i++;
  60. this->OutputVariable = argv[i];
  61. }
  62. else if (argv[i] == "RUN_OUTPUT_VARIABLE")
  63. {
  64. if (argv.size() <= (i + 1))
  65. {
  66. cmSystemTools::Error(
  67. "RUN_OUTPUT_VARIABLE specified but there is no variable");
  68. return false;
  69. }
  70. i++;
  71. this->RunOutputVariable = argv[i];
  72. }
  73. else if (argv[i] == "COMPILE_OUTPUT_VARIABLE")
  74. {
  75. if (argv.size() <= (i + 1))
  76. {
  77. cmSystemTools::Error(
  78. "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
  79. return false;
  80. }
  81. i++;
  82. this->CompileOutputVariable = argv[i];
  83. }
  84. else
  85. {
  86. tryCompile.push_back(argv[i]);
  87. }
  88. }
  89. }
  90. // although they could be used together, don't allow it, because
  91. // using OUTPUT_VARIABLE makes crosscompiling harder
  92. if (this->OutputVariable.size()
  93. && ((this->RunOutputVariable.size())
  94. || (this->CompileOutputVariable.size())))
  95. {
  96. cmSystemTools::Error(
  97. "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
  98. "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
  99. "RUN_OUTPUT_VARIABLE.");
  100. return false;
  101. }
  102. bool captureRunOutput = false;
  103. if (this->OutputVariable.size())
  104. {
  105. captureRunOutput = true;
  106. tryCompile.push_back("OUTPUT_VARIABLE");
  107. tryCompile.push_back(this->OutputVariable);
  108. }
  109. if (this->CompileOutputVariable.size())
  110. {
  111. tryCompile.push_back("OUTPUT_VARIABLE");
  112. tryCompile.push_back(this->CompileOutputVariable);
  113. }
  114. if (this->RunOutputVariable.size())
  115. {
  116. captureRunOutput = true;
  117. }
  118. this->RunResultVariable = argv[0];
  119. this->CompileResultVariable = argv[1];
  120. // do the try compile
  121. int res = this->TryCompileCode(tryCompile);
  122. // now try running the command if it compiled
  123. if (!res)
  124. {
  125. if (this->OutputFile.size() == 0)
  126. {
  127. cmSystemTools::Error(this->FindErrorMessage.c_str());
  128. }
  129. else
  130. {
  131. // "run" it and capture the output
  132. std::string runOutputContents;
  133. if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING"))
  134. {
  135. this->DoNotRunExecutable(runArgs,
  136. argv[3],
  137. captureRunOutput ? &runOutputContents : 0);
  138. }
  139. else
  140. {
  141. this->RunExecutable(runArgs, &runOutputContents);
  142. }
  143. // now put the output into the variables
  144. if(this->RunOutputVariable.size())
  145. {
  146. this->Makefile->AddDefinition(this->RunOutputVariable.c_str(),
  147. runOutputContents.c_str());
  148. }
  149. if(this->OutputVariable.size())
  150. {
  151. // if the TryCompileCore saved output in this outputVariable then
  152. // prepend that output to this output
  153. const char* compileOutput
  154. = this->Makefile->GetDefinition(this->OutputVariable.c_str());
  155. if (compileOutput)
  156. {
  157. runOutputContents = std::string(compileOutput) + runOutputContents;
  158. }
  159. this->Makefile->AddDefinition(this->OutputVariable.c_str(),
  160. runOutputContents.c_str());
  161. }
  162. }
  163. }
  164. // if we created a directory etc, then cleanup after ourselves
  165. if(!this->Makefile->GetCMakeInstance()->GetDebugTryCompile())
  166. {
  167. this->CleanupFiles(this->BinaryDirectory.c_str());
  168. }
  169. return true;
  170. }
  171. void cmTryRunCommand::RunExecutable(const std::string& runArgs,
  172. std::string* out)
  173. {
  174. int retVal = -1;
  175. std::string finalCommand = cmSystemTools::ConvertToRunCommandPath(
  176. this->OutputFile.c_str());
  177. if (runArgs.size())
  178. {
  179. finalCommand += runArgs;
  180. }
  181. int timeout = 0;
  182. bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(),
  183. out, &retVal,
  184. 0, false, timeout);
  185. // set the run var
  186. char retChar[1000];
  187. if (worked)
  188. {
  189. sprintf(retChar, "%i", retVal);
  190. }
  191. else
  192. {
  193. strcpy(retChar, "FAILED_TO_RUN");
  194. }
  195. this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(), retChar,
  196. "Result of TRY_RUN",
  197. cmCacheManager::INTERNAL);
  198. }
  199. /* This is only used when cross compiling. Instead of running the
  200. executable, two cache variables are created which will hold the results
  201. the executable would have produced.
  202. */
  203. void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
  204. const std::string& srcFile,
  205. std::string* out
  206. )
  207. {
  208. std::string internalRunOutputName = this->RunResultVariable+"__"
  209. +this->CompileResultVariable+"__TRYRUN_OUTPUT";
  210. bool error = false;
  211. std::string info = "Source file: ";
  212. info += srcFile + "\n";
  213. info += "Run arguments: ";
  214. info += runArgs;
  215. info += "\n";
  216. info += " Called from: " + this->Makefile->GetListFileStack();
  217. if (this->Makefile->GetDefinition(this->RunResultVariable.c_str()) == 0)
  218. {
  219. // if the variables doesn't exist, create it with a helpful error text
  220. // and mark it as advanced
  221. std::string comment;
  222. comment += "Run result of TRY_RUN().\n"
  223. "This variable should indicate whether the executable would "
  224. "have been able to run if it was executed on its target "
  225. "platform.\n"
  226. "If it would have been able to run, enter the exit code here "
  227. "(in many cases 0 for success). If not, enter "
  228. "\"FAILED_TO_RUN\" here.";
  229. if (out!=0)
  230. {
  231. comment += "If it was able to run, also check the variable ";
  232. comment += internalRunOutputName;
  233. comment += " and set it appropriately.";
  234. }
  235. comment += "\n";
  236. comment += info;
  237. this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(),
  238. "PLEASE_FILL_OUT-FAILED_TO_RUN",
  239. comment.c_str(),
  240. cmCacheManager::STRING);
  241. cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
  242. GetCacheIterator(this->RunResultVariable.c_str());
  243. if ( !it.IsAtEnd() )
  244. {
  245. it.SetProperty("ADVANCED", "1");
  246. }
  247. error = true;
  248. }
  249. // is the output from the executable used ?
  250. if (out!=0)
  251. {
  252. if (this->Makefile->GetDefinition(internalRunOutputName.c_str()) == 0)
  253. {
  254. // if the variables doesn't exist, create it with a helpful error text
  255. // and mark it as advanced
  256. std::string comment;
  257. comment += "Output of TRY_RUN().\n"
  258. "This variable should contain the text, which the executable "
  259. "run by TRY_RUN() would have printed on stdout and stderr, "
  260. "if it was executed on its target platform.\n"
  261. "The accompanying variable ";
  262. comment += this->RunResultVariable;
  263. comment += " indicates whether the executable would have been able to "
  264. "run and its exit code."
  265. "If the executable would not have been able to run, set ";
  266. comment += internalRunOutputName;
  267. comment += " empty. Otherwise check if the output is evaluated by the "
  268. "calling CMake code. If this is the case, check the source "
  269. "file what it would have printed if called with the given "
  270. "arguments.\n";
  271. comment += info;
  272. this->Makefile->AddCacheDefinition(internalRunOutputName.c_str(),
  273. "PLEASE_FILL_OUT-NOTFOUND",
  274. comment.c_str(),
  275. cmCacheManager::STRING);
  276. cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
  277. GetCacheIterator(internalRunOutputName.c_str());
  278. if ( !it.IsAtEnd() )
  279. {
  280. it.SetProperty("ADVANCED", "1");
  281. }
  282. error = true;
  283. }
  284. }
  285. if (error)
  286. {
  287. static bool firstTryRun = true;
  288. std::string fileName = this->Makefile->GetHomeOutputDirectory();
  289. fileName += "/TryRunResults.cmake";
  290. std::ofstream file(fileName.c_str(),
  291. firstTryRun ? std::ios::out : std::ios::app);
  292. if ( file )
  293. {
  294. if (firstTryRun)
  295. {
  296. file << "# This file was generated by CMake because it detected "
  297. "TRY_RUN() commands\n"
  298. "# in crosscompiling mode. It will be overwritten by the next "
  299. "CMake run.\n"
  300. "# Copy it to a safe location, set the variables to "
  301. "appropriate values\n"
  302. "# and use it then to preset the CMake cache (using -C).\n\n";
  303. }
  304. std::string comment ="\n";
  305. comment += this->RunResultVariable;
  306. comment += "\nindicates whether the executable would have been able to "
  307. "run if it was\n"
  308. "executed on its target platform. If it would have been able to "
  309. "run, set it to\n"
  310. "the exit code (in many cases 0 for success). If not, enter "
  311. "\"FAILED_TO_RUN\".\n\n";
  312. if (out!=0)
  313. {
  314. comment += internalRunOutputName;
  315. comment += "\ncontains the text, which the executable "
  316. " would have printed on stdout and stderr.\n"
  317. "If the executable would not have been able to run, set it empty.\n"
  318. "Otherwise check if the output is evaluated by the "
  319. "calling CMake code. If so,\n"
  320. "check what the source file would have printed when called with "
  321. "the given arguments.\n\n";
  322. }
  323. comment += info;
  324. cmsys::SystemTools::ReplaceString(comment, "\n", "\n# ");
  325. file << comment << "\n\n";
  326. file << "SET( " << this->RunResultVariable << " \n \""
  327. << this->Makefile->GetDefinition(this->RunResultVariable.c_str())
  328. << "\"\n CACHE STRING \"Result from TRY_RUN\" FORCE)\n\n";
  329. if (out!=0)
  330. {
  331. file << "SET( " << internalRunOutputName << " \n \""
  332. << this->Makefile->GetDefinition(internalRunOutputName.c_str())
  333. << "\"\n CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n";
  334. }
  335. file.close();
  336. }
  337. firstTryRun = false;
  338. std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, "
  339. "please set the following cache variables "
  340. "appropriatly:\n";
  341. errorMessage += " " + this->RunResultVariable + " (advanced)\n";
  342. if (out!=0)
  343. {
  344. errorMessage += " " + internalRunOutputName + " (advanced)\n";
  345. }
  346. errorMessage += info;
  347. cmSystemTools::Error(errorMessage.c_str());
  348. return;
  349. }
  350. if (out!=0)
  351. {
  352. (*out) = this->Makefile->GetDefinition(internalRunOutputName.c_str());
  353. }
  354. }