cmCTestHandlerCommand.cxx 9.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 "cmCTestHandlerCommand.h"
  4. #include "cmCTest.h"
  5. #include "cmCTestGenericHandler.h"
  6. #include "cmMakefile.h"
  7. #include "cmSystemTools.h"
  8. #include "cmake.h"
  9. #include <sstream>
  10. #include <stdlib.h>
  11. class cmExecutionStatus;
  12. cmCTestHandlerCommand::cmCTestHandlerCommand()
  13. {
  14. const size_t INIT_SIZE = 100;
  15. size_t cc;
  16. this->Arguments.reserve(INIT_SIZE);
  17. for (cc = 0; cc < INIT_SIZE; ++cc) {
  18. this->Arguments.push_back(CM_NULLPTR);
  19. }
  20. this->Arguments[ct_RETURN_VALUE] = "RETURN_VALUE";
  21. this->Arguments[ct_CAPTURE_CMAKE_ERROR] = "CAPTURE_CMAKE_ERROR";
  22. this->Arguments[ct_SOURCE] = "SOURCE";
  23. this->Arguments[ct_BUILD] = "BUILD";
  24. this->Arguments[ct_SUBMIT_INDEX] = "SUBMIT_INDEX";
  25. this->Last = ct_LAST;
  26. this->AppendXML = false;
  27. this->Quiet = false;
  28. }
  29. namespace {
  30. // class to save and restore the error state for ctest_* commands
  31. // if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error
  32. // state into there and restore the system wide error to what
  33. // it was before the command ran
  34. class SaveRestoreErrorState
  35. {
  36. public:
  37. SaveRestoreErrorState()
  38. {
  39. this->InitialErrorState = cmSystemTools::GetErrorOccuredFlag();
  40. cmSystemTools::ResetErrorOccuredFlag(); // rest the error state
  41. this->CaptureCMakeErrorValue = false;
  42. }
  43. // if the function has a CAPTURE_CMAKE_ERROR then we should restore
  44. // the error state to what it was before the function was run
  45. // if not then let the error state be what it is
  46. void CaptureCMakeError() { this->CaptureCMakeErrorValue = true; }
  47. ~SaveRestoreErrorState()
  48. {
  49. // if we are not saving the return value then make sure
  50. // if it was in error it goes back to being in error
  51. // otherwise leave it be what it is
  52. if (!this->CaptureCMakeErrorValue) {
  53. if (this->InitialErrorState) {
  54. cmSystemTools::SetErrorOccured();
  55. }
  56. return;
  57. }
  58. // if we have saved the error in a return variable
  59. // then put things back exactly like they were
  60. bool currentState = cmSystemTools::GetErrorOccuredFlag();
  61. // if the state changed during this command we need
  62. // to handle it, if not then nothing needs to be done
  63. if (currentState != this->InitialErrorState) {
  64. // restore the initial error state
  65. if (this->InitialErrorState) {
  66. cmSystemTools::SetErrorOccured();
  67. } else {
  68. cmSystemTools::ResetErrorOccuredFlag();
  69. }
  70. }
  71. }
  72. private:
  73. bool InitialErrorState;
  74. bool CaptureCMakeErrorValue;
  75. };
  76. }
  77. bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
  78. cmExecutionStatus& /*unused*/)
  79. {
  80. // save error state and restore it if needed
  81. SaveRestoreErrorState errorState;
  82. // Allocate space for argument values.
  83. this->Values.clear();
  84. this->Values.resize(this->Last, CM_NULLPTR);
  85. // Process input arguments.
  86. this->ArgumentDoing = ArgumentDoingNone;
  87. // look at all arguments and do not short circuit on the first
  88. // bad one so that CAPTURE_CMAKE_ERROR can override setting the
  89. // global error state
  90. bool foundBadArgument = false;
  91. for (unsigned int i = 0; i < args.size(); ++i) {
  92. // Check this argument.
  93. if (!this->CheckArgumentKeyword(args[i]) &&
  94. !this->CheckArgumentValue(args[i])) {
  95. std::ostringstream e;
  96. e << "called with unknown argument \"" << args[i] << "\".";
  97. this->SetError(e.str());
  98. foundBadArgument = true;
  99. }
  100. // note bad argument
  101. if (this->ArgumentDoing == ArgumentDoingError) {
  102. foundBadArgument = true;
  103. }
  104. }
  105. bool capureCMakeError = (this->Values[ct_CAPTURE_CMAKE_ERROR] &&
  106. *this->Values[ct_CAPTURE_CMAKE_ERROR]);
  107. // now that arguments are parsed check to see if there is a
  108. // CAPTURE_CMAKE_ERROR specified let the errorState object know.
  109. if (capureCMakeError) {
  110. errorState.CaptureCMakeError();
  111. }
  112. // if we found a bad argument then exit before running command
  113. if (foundBadArgument) {
  114. // store the cmake error
  115. if (capureCMakeError) {
  116. this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
  117. "-1");
  118. const char* err = this->GetError();
  119. if (err && !cmSystemTools::FindLastString(err, "unknown error.")) {
  120. cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
  121. }
  122. // return success because failure is recorded in CAPTURE_CMAKE_ERROR
  123. return true;
  124. }
  125. // return failure because of bad argument
  126. return false;
  127. }
  128. // Set the config type of this ctest to the current value of the
  129. // CTEST_CONFIGURATION_TYPE script variable if it is defined.
  130. // The current script value trumps the -C argument on the command
  131. // line.
  132. const char* ctestConfigType =
  133. this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE");
  134. if (ctestConfigType) {
  135. this->CTest->SetConfigType(ctestConfigType);
  136. }
  137. if (this->Values[ct_BUILD]) {
  138. this->CTest->SetCTestConfiguration(
  139. "BuildDirectory",
  140. cmSystemTools::CollapseFullPath(this->Values[ct_BUILD]).c_str(),
  141. this->Quiet);
  142. } else {
  143. const char* bdir =
  144. this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY");
  145. if (bdir) {
  146. this->CTest->SetCTestConfiguration(
  147. "BuildDirectory", cmSystemTools::CollapseFullPath(bdir).c_str(),
  148. this->Quiet);
  149. } else {
  150. cmCTestLog(this->CTest, ERROR_MESSAGE, "CTEST_BINARY_DIRECTORY not set"
  151. << std::endl;);
  152. }
  153. }
  154. if (this->Values[ct_SOURCE]) {
  155. cmCTestLog(this->CTest, DEBUG, "Set source directory to: "
  156. << this->Values[ct_SOURCE] << std::endl);
  157. this->CTest->SetCTestConfiguration(
  158. "SourceDirectory",
  159. cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(),
  160. this->Quiet);
  161. } else {
  162. this->CTest->SetCTestConfiguration(
  163. "SourceDirectory",
  164. cmSystemTools::CollapseFullPath(
  165. this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY"))
  166. .c_str(),
  167. this->Quiet);
  168. }
  169. if (const char* changeId =
  170. this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
  171. this->CTest->SetCTestConfiguration("ChangeId", changeId, this->Quiet);
  172. }
  173. cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;);
  174. cmCTestGenericHandler* handler = this->InitializeHandler();
  175. if (!handler) {
  176. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot instantiate test handler "
  177. << this->GetName() << std::endl);
  178. if (capureCMakeError) {
  179. this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
  180. "-1");
  181. const char* err = this->GetError();
  182. if (err && !cmSystemTools::FindLastString(err, "unknown error.")) {
  183. cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
  184. }
  185. return true;
  186. }
  187. return false;
  188. }
  189. handler->SetAppendXML(this->AppendXML);
  190. handler->PopulateCustomVectors(this->Makefile);
  191. if (this->Values[ct_SUBMIT_INDEX]) {
  192. if (!this->CTest->GetDropSiteCDash() &&
  193. this->CTest->GetDartVersion() <= 1) {
  194. cmCTestLog(
  195. this->CTest, ERROR_MESSAGE,
  196. "Dart before version 2.0 does not support collecting submissions."
  197. << std::endl
  198. << "Please upgrade the server to Dart 2 or higher, or do not use "
  199. "SUBMIT_INDEX."
  200. << std::endl);
  201. } else {
  202. handler->SetSubmitIndex(atoi(this->Values[ct_SUBMIT_INDEX]));
  203. }
  204. }
  205. std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory();
  206. cmSystemTools::ChangeDirectory(
  207. this->CTest->GetCTestConfiguration("BuildDirectory"));
  208. int res = handler->ProcessHandler();
  209. if (this->Values[ct_RETURN_VALUE] && *this->Values[ct_RETURN_VALUE]) {
  210. std::ostringstream str;
  211. str << res;
  212. this->Makefile->AddDefinition(this->Values[ct_RETURN_VALUE],
  213. str.str().c_str());
  214. }
  215. // log the error message if there was an error
  216. if (capureCMakeError) {
  217. const char* returnString = "0";
  218. if (cmSystemTools::GetErrorOccuredFlag()) {
  219. returnString = "-1";
  220. const char* err = this->GetError();
  221. // print out the error if it is not "unknown error" which means
  222. // there was no message
  223. if (err && !cmSystemTools::FindLastString(err, "unknown error.")) {
  224. cmCTestLog(this->CTest, ERROR_MESSAGE, err);
  225. }
  226. }
  227. // store the captured cmake error state 0 or -1
  228. this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR],
  229. returnString);
  230. }
  231. cmSystemTools::ChangeDirectory(current_dir);
  232. return true;
  233. }
  234. bool cmCTestHandlerCommand::CheckArgumentKeyword(std::string const& arg)
  235. {
  236. // Look for non-value arguments common to all commands.
  237. if (arg == "APPEND") {
  238. this->ArgumentDoing = ArgumentDoingNone;
  239. this->AppendXML = true;
  240. return true;
  241. }
  242. if (arg == "QUIET") {
  243. this->ArgumentDoing = ArgumentDoingNone;
  244. this->Quiet = true;
  245. return true;
  246. }
  247. // Check for a keyword in our argument/value table.
  248. for (unsigned int k = 0; k < this->Arguments.size(); ++k) {
  249. if (this->Arguments[k] && arg == this->Arguments[k]) {
  250. this->ArgumentDoing = ArgumentDoingKeyword;
  251. this->ArgumentIndex = k;
  252. return true;
  253. }
  254. }
  255. return false;
  256. }
  257. bool cmCTestHandlerCommand::CheckArgumentValue(std::string const& arg)
  258. {
  259. if (this->ArgumentDoing == ArgumentDoingKeyword) {
  260. this->ArgumentDoing = ArgumentDoingNone;
  261. unsigned int k = this->ArgumentIndex;
  262. if (this->Values[k]) {
  263. std::ostringstream e;
  264. e << "Called with more than one value for " << this->Arguments[k];
  265. this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
  266. this->ArgumentDoing = ArgumentDoingError;
  267. return true;
  268. }
  269. this->Values[k] = arg.c_str();
  270. cmCTestLog(this->CTest, DEBUG, "Set " << this->Arguments[k] << " to "
  271. << arg << "\n");
  272. return true;
  273. }
  274. return false;
  275. }