cmCTestHandlerCommand.cxx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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 <algorithm>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <sstream>
  8. #include <cm/string_view>
  9. #include <cmext/string_view>
  10. #include "cmCTest.h"
  11. #include "cmCTestGenericHandler.h"
  12. #include "cmExecutionStatus.h"
  13. #include "cmMakefile.h"
  14. #include "cmMessageType.h"
  15. #include "cmStringAlgorithms.h"
  16. #include "cmSystemTools.h"
  17. #include "cmValue.h"
  18. #include "cmWorkingDirectory.h"
  19. namespace {
  20. // class to save and restore the error state for ctest_* commands
  21. // if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error
  22. // state into there and restore the system wide error to what
  23. // it was before the command ran
  24. class SaveRestoreErrorState
  25. {
  26. public:
  27. SaveRestoreErrorState()
  28. {
  29. this->InitialErrorState = cmSystemTools::GetErrorOccurredFlag();
  30. cmSystemTools::ResetErrorOccurredFlag(); // rest the error state
  31. this->CaptureCMakeErrorValue = false;
  32. }
  33. // if the function has a CAPTURE_CMAKE_ERROR then we should restore
  34. // the error state to what it was before the function was run
  35. // if not then let the error state be what it is
  36. void CaptureCMakeError() { this->CaptureCMakeErrorValue = true; }
  37. ~SaveRestoreErrorState()
  38. {
  39. // if we are not saving the return value then make sure
  40. // if it was in error it goes back to being in error
  41. // otherwise leave it be what it is
  42. if (!this->CaptureCMakeErrorValue) {
  43. if (this->InitialErrorState) {
  44. cmSystemTools::SetErrorOccurred();
  45. }
  46. return;
  47. }
  48. // if we have saved the error in a return variable
  49. // then put things back exactly like they were
  50. bool currentState = cmSystemTools::GetErrorOccurredFlag();
  51. // if the state changed during this command we need
  52. // to handle it, if not then nothing needs to be done
  53. if (currentState != this->InitialErrorState) {
  54. // restore the initial error state
  55. if (this->InitialErrorState) {
  56. cmSystemTools::SetErrorOccurred();
  57. } else {
  58. cmSystemTools::ResetErrorOccurredFlag();
  59. }
  60. }
  61. }
  62. SaveRestoreErrorState(const SaveRestoreErrorState&) = delete;
  63. SaveRestoreErrorState& operator=(const SaveRestoreErrorState&) = delete;
  64. private:
  65. bool InitialErrorState;
  66. bool CaptureCMakeErrorValue;
  67. };
  68. }
  69. bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
  70. cmExecutionStatus& status)
  71. {
  72. // save error state and restore it if needed
  73. SaveRestoreErrorState errorState;
  74. // Allocate space for argument values.
  75. this->BindArguments();
  76. // Process input arguments.
  77. std::vector<std::string> unparsedArguments;
  78. std::vector<cm::string_view> parsedKeywords;
  79. this->Parse(args, &unparsedArguments, &parsedKeywords);
  80. this->CheckArguments();
  81. std::sort(parsedKeywords.begin(), parsedKeywords.end());
  82. auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end());
  83. if (it != parsedKeywords.end()) {
  84. this->Makefile->IssueMessage(
  85. MessageType::FATAL_ERROR,
  86. cmStrCat("Called with more than one value for ", *it));
  87. }
  88. bool const foundBadArgument = !unparsedArguments.empty();
  89. if (foundBadArgument) {
  90. this->SetError(cmStrCat("called with unknown argument \"",
  91. unparsedArguments.front(), "\"."));
  92. }
  93. bool const captureCMakeError = !this->CaptureCMakeError.empty();
  94. // now that arguments are parsed check to see if there is a
  95. // CAPTURE_CMAKE_ERROR specified let the errorState object know.
  96. if (captureCMakeError) {
  97. errorState.CaptureCMakeError();
  98. }
  99. // if we found a bad argument then exit before running command
  100. if (foundBadArgument) {
  101. // store the cmake error
  102. if (captureCMakeError) {
  103. this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
  104. std::string const err = this->GetName() + " " + status.GetError();
  105. if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
  106. cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
  107. }
  108. // return success because failure is recorded in CAPTURE_CMAKE_ERROR
  109. return true;
  110. }
  111. // return failure because of bad argument
  112. return false;
  113. }
  114. // Set the config type of this ctest to the current value of the
  115. // CTEST_CONFIGURATION_TYPE script variable if it is defined.
  116. // The current script value trumps the -C argument on the command
  117. // line.
  118. cmValue ctestConfigType =
  119. this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE");
  120. if (ctestConfigType) {
  121. this->CTest->SetConfigType(*ctestConfigType);
  122. }
  123. if (!this->Build.empty()) {
  124. this->CTest->SetCTestConfiguration(
  125. "BuildDirectory", cmSystemTools::CollapseFullPath(this->Build),
  126. this->Quiet);
  127. } else {
  128. std::string const& bdir =
  129. this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY");
  130. if (!bdir.empty()) {
  131. this->CTest->SetCTestConfiguration(
  132. "BuildDirectory", cmSystemTools::CollapseFullPath(bdir), this->Quiet);
  133. } else {
  134. cmCTestLog(this->CTest, ERROR_MESSAGE,
  135. "CTEST_BINARY_DIRECTORY not set" << std::endl;);
  136. }
  137. }
  138. if (!this->Source.empty()) {
  139. cmCTestLog(this->CTest, DEBUG,
  140. "Set source directory to: " << this->Source << std::endl);
  141. this->CTest->SetCTestConfiguration(
  142. "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source),
  143. this->Quiet);
  144. } else {
  145. this->CTest->SetCTestConfiguration(
  146. "SourceDirectory",
  147. cmSystemTools::CollapseFullPath(
  148. this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")),
  149. this->Quiet);
  150. }
  151. if (cmValue changeId = this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
  152. this->CTest->SetCTestConfiguration("ChangeId", *changeId, this->Quiet);
  153. }
  154. cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;);
  155. cmCTestGenericHandler* handler = this->InitializeHandler();
  156. if (!handler) {
  157. cmCTestLog(this->CTest, ERROR_MESSAGE,
  158. "Cannot instantiate test handler " << this->GetName()
  159. << std::endl);
  160. if (captureCMakeError) {
  161. this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
  162. std::string const& err = status.GetError();
  163. if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
  164. cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
  165. }
  166. return true;
  167. }
  168. return false;
  169. }
  170. handler->SetAppendXML(this->Append);
  171. handler->PopulateCustomVectors(this->Makefile);
  172. if (!this->SubmitIndex.empty()) {
  173. handler->SetSubmitIndex(atoi(this->SubmitIndex.c_str()));
  174. }
  175. cmWorkingDirectory workdir(
  176. this->CTest->GetCTestConfiguration("BuildDirectory"));
  177. if (workdir.Failed()) {
  178. this->SetError("failed to change directory to " +
  179. this->CTest->GetCTestConfiguration("BuildDirectory") +
  180. " : " + std::strerror(workdir.GetLastResult()));
  181. if (captureCMakeError) {
  182. this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
  183. cmCTestLog(this->CTest, ERROR_MESSAGE,
  184. this->GetName() << " " << status.GetError() << "\n");
  185. // return success because failure is recorded in CAPTURE_CMAKE_ERROR
  186. return true;
  187. }
  188. return false;
  189. }
  190. int res = handler->ProcessHandler();
  191. if (!this->ReturnValue.empty()) {
  192. this->Makefile->AddDefinition(this->ReturnValue, std::to_string(res));
  193. }
  194. this->ProcessAdditionalValues(handler);
  195. // log the error message if there was an error
  196. if (captureCMakeError) {
  197. const char* returnString = "0";
  198. if (cmSystemTools::GetErrorOccurredFlag()) {
  199. returnString = "-1";
  200. std::string const& err = status.GetError();
  201. // print out the error if it is not "unknown error" which means
  202. // there was no message
  203. if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
  204. cmCTestLog(this->CTest, ERROR_MESSAGE, err);
  205. }
  206. }
  207. // store the captured cmake error state 0 or -1
  208. this->Makefile->AddDefinition(this->CaptureCMakeError, returnString);
  209. }
  210. return true;
  211. }
  212. void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*)
  213. {
  214. }
  215. void cmCTestHandlerCommand::BindArguments()
  216. {
  217. this->Bind("APPEND"_s, this->Append);
  218. this->Bind("QUIET"_s, this->Quiet);
  219. this->Bind("RETURN_VALUE"_s, this->ReturnValue);
  220. this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
  221. this->Bind("SOURCE"_s, this->Source);
  222. this->Bind("BUILD"_s, this->Build);
  223. this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex);
  224. }
  225. void cmCTestHandlerCommand::CheckArguments()
  226. {
  227. }