cmCTestHandlerCommand.cxx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 <sstream>
  7. #include <cm/string_view>
  8. #include "cmCTest.h"
  9. #include "cmCTestGenericHandler.h"
  10. #include "cmExecutionStatus.h"
  11. #include "cmMakefile.h"
  12. #include "cmStringAlgorithms.h"
  13. #include "cmSystemTools.h"
  14. #include "cmValue.h"
  15. #include "cmWorkingDirectory.h"
  16. namespace {
  17. // class to save and restore the error state for ctest_* commands
  18. // if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error
  19. // state into there and restore the system wide error to what
  20. // it was before the command ran
  21. class SaveRestoreErrorState
  22. {
  23. public:
  24. SaveRestoreErrorState()
  25. {
  26. this->InitialErrorState = cmSystemTools::GetErrorOccurredFlag();
  27. cmSystemTools::ResetErrorOccurredFlag(); // rest the error state
  28. this->CaptureCMakeErrorValue = false;
  29. }
  30. // if the function has a CAPTURE_CMAKE_ERROR then we should restore
  31. // the error state to what it was before the function was run
  32. // if not then let the error state be what it is
  33. void CaptureCMakeError() { this->CaptureCMakeErrorValue = true; }
  34. ~SaveRestoreErrorState()
  35. {
  36. // if we are not saving the return value then make sure
  37. // if it was in error it goes back to being in error
  38. // otherwise leave it be what it is
  39. if (!this->CaptureCMakeErrorValue) {
  40. if (this->InitialErrorState) {
  41. cmSystemTools::SetErrorOccurred();
  42. }
  43. return;
  44. }
  45. // if we have saved the error in a return variable
  46. // then put things back exactly like they were
  47. bool currentState = cmSystemTools::GetErrorOccurredFlag();
  48. // if the state changed during this command we need
  49. // to handle it, if not then nothing needs to be done
  50. if (currentState != this->InitialErrorState) {
  51. // restore the initial error state
  52. if (this->InitialErrorState) {
  53. cmSystemTools::SetErrorOccurred();
  54. } else {
  55. cmSystemTools::ResetErrorOccurredFlag();
  56. }
  57. }
  58. }
  59. SaveRestoreErrorState(const SaveRestoreErrorState&) = delete;
  60. SaveRestoreErrorState& operator=(const SaveRestoreErrorState&) = delete;
  61. private:
  62. bool InitialErrorState;
  63. bool CaptureCMakeErrorValue;
  64. };
  65. }
  66. bool cmCTestHandlerCommand::InvokeImpl(
  67. BasicArguments& args, std::vector<std::string> const& unparsed,
  68. cmExecutionStatus& status, std::function<bool()> handler)
  69. {
  70. // save error state and restore it if needed
  71. SaveRestoreErrorState errorState;
  72. if (!args.CaptureCMakeError.empty()) {
  73. errorState.CaptureCMakeError();
  74. }
  75. bool success = [&]() -> bool {
  76. if (args.MaybeReportError(status.GetMakefile())) {
  77. return true;
  78. }
  79. std::sort(args.ParsedKeywords.begin(), args.ParsedKeywords.end());
  80. auto const it = std::adjacent_find(args.ParsedKeywords.begin(),
  81. args.ParsedKeywords.end());
  82. if (it != args.ParsedKeywords.end()) {
  83. status.SetError(cmStrCat("called with more than one value for ", *it));
  84. return false;
  85. }
  86. if (!unparsed.empty()) {
  87. status.SetError(
  88. cmStrCat("called with unknown argument \"", unparsed.front(), "\"."));
  89. return false;
  90. }
  91. return handler();
  92. }();
  93. if (args.CaptureCMakeError.empty()) {
  94. return success;
  95. }
  96. if (!success) {
  97. cmCTestLog(this->CTest, ERROR_MESSAGE,
  98. this->GetName() << ' ' << status.GetError() << '\n');
  99. }
  100. cmMakefile& mf = status.GetMakefile();
  101. success = success && !cmSystemTools::GetErrorOccurredFlag();
  102. mf.AddDefinition(args.CaptureCMakeError, success ? "0" : "-1");
  103. return true;
  104. }
  105. bool cmCTestHandlerCommand::ExecuteHandlerCommand(
  106. HandlerArguments& args, cmExecutionStatus& /*status*/)
  107. {
  108. // Process input arguments.
  109. this->CheckArguments(args);
  110. // Set the config type of this ctest to the current value of the
  111. // CTEST_CONFIGURATION_TYPE script variable if it is defined.
  112. // The current script value trumps the -C argument on the command
  113. // line.
  114. cmValue ctestConfigType =
  115. this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE");
  116. if (ctestConfigType) {
  117. this->CTest->SetConfigType(*ctestConfigType);
  118. }
  119. if (!args.Build.empty()) {
  120. this->CTest->SetCTestConfiguration(
  121. "BuildDirectory", cmSystemTools::CollapseFullPath(args.Build),
  122. args.Quiet);
  123. } else {
  124. std::string const& bdir =
  125. this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY");
  126. if (!bdir.empty()) {
  127. this->CTest->SetCTestConfiguration(
  128. "BuildDirectory", cmSystemTools::CollapseFullPath(bdir), args.Quiet);
  129. } else {
  130. cmCTestLog(this->CTest, ERROR_MESSAGE,
  131. "CTEST_BINARY_DIRECTORY not set" << std::endl);
  132. }
  133. }
  134. if (!args.Source.empty()) {
  135. cmCTestLog(this->CTest, DEBUG,
  136. "Set source directory to: " << args.Source << std::endl);
  137. this->CTest->SetCTestConfiguration(
  138. "SourceDirectory", cmSystemTools::CollapseFullPath(args.Source),
  139. args.Quiet);
  140. } else {
  141. this->CTest->SetCTestConfiguration(
  142. "SourceDirectory",
  143. cmSystemTools::CollapseFullPath(
  144. this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")),
  145. args.Quiet);
  146. }
  147. if (cmValue changeId = this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
  148. this->CTest->SetCTestConfiguration("ChangeId", *changeId, args.Quiet);
  149. }
  150. cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl);
  151. auto handler = this->InitializeHandler(args);
  152. if (!handler) {
  153. cmCTestLog(this->CTest, ERROR_MESSAGE,
  154. "Cannot instantiate test handler " << this->GetName()
  155. << std::endl);
  156. return false;
  157. }
  158. handler->SetAppendXML(args.Append);
  159. handler->PopulateCustomVectors(this->Makefile);
  160. if (!args.SubmitIndex.empty()) {
  161. handler->SetSubmitIndex(atoi(args.SubmitIndex.c_str()));
  162. }
  163. cmWorkingDirectory workdir(
  164. this->CTest->GetCTestConfiguration("BuildDirectory"));
  165. if (workdir.Failed()) {
  166. this->SetError(workdir.GetError());
  167. return false;
  168. }
  169. // reread time limit, as the variable may have been modified.
  170. this->CTest->SetTimeLimit(this->Makefile->GetDefinition("CTEST_TIME_LIMIT"));
  171. handler->SetCMakeInstance(this->Makefile->GetCMakeInstance());
  172. int res = handler->ProcessHandler();
  173. if (!args.ReturnValue.empty()) {
  174. this->Makefile->AddDefinition(args.ReturnValue, std::to_string(res));
  175. }
  176. this->ProcessAdditionalValues(handler.get(), args);
  177. return true;
  178. }
  179. void cmCTestHandlerCommand::CheckArguments(HandlerArguments&)
  180. {
  181. }
  182. std::unique_ptr<cmCTestGenericHandler>
  183. cmCTestHandlerCommand::InitializeHandler(HandlerArguments&)
  184. {
  185. return nullptr;
  186. };
  187. void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*,
  188. HandlerArguments const&)
  189. {
  190. }