cmCTestHandlerCommand.cxx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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_static_string_view.hxx"
  9. #include "cmCTest.h"
  10. #include "cmCTestGenericHandler.h"
  11. #include "cmExecutionStatus.h"
  12. #include "cmMakefile.h"
  13. #include "cmMessageType.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. #include "cmWorkingDirectory.h"
  17. namespace {
  18. // class to save and restore the error state for ctest_* commands
  19. // if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error
  20. // state into there and restore the system wide error to what
  21. // it was before the command ran
  22. class SaveRestoreErrorState
  23. {
  24. public:
  25. SaveRestoreErrorState()
  26. {
  27. this->InitialErrorState = cmSystemTools::GetErrorOccuredFlag();
  28. cmSystemTools::ResetErrorOccuredFlag(); // rest the error state
  29. this->CaptureCMakeErrorValue = false;
  30. }
  31. // if the function has a CAPTURE_CMAKE_ERROR then we should restore
  32. // the error state to what it was before the function was run
  33. // if not then let the error state be what it is
  34. void CaptureCMakeError() { this->CaptureCMakeErrorValue = true; }
  35. ~SaveRestoreErrorState()
  36. {
  37. // if we are not saving the return value then make sure
  38. // if it was in error it goes back to being in error
  39. // otherwise leave it be what it is
  40. if (!this->CaptureCMakeErrorValue) {
  41. if (this->InitialErrorState) {
  42. cmSystemTools::SetErrorOccured();
  43. }
  44. return;
  45. }
  46. // if we have saved the error in a return variable
  47. // then put things back exactly like they were
  48. bool currentState = cmSystemTools::GetErrorOccuredFlag();
  49. // if the state changed during this command we need
  50. // to handle it, if not then nothing needs to be done
  51. if (currentState != this->InitialErrorState) {
  52. // restore the initial error state
  53. if (this->InitialErrorState) {
  54. cmSystemTools::SetErrorOccured();
  55. } else {
  56. cmSystemTools::ResetErrorOccuredFlag();
  57. }
  58. }
  59. }
  60. SaveRestoreErrorState(const SaveRestoreErrorState&) = delete;
  61. SaveRestoreErrorState& operator=(const SaveRestoreErrorState&) = delete;
  62. private:
  63. bool InitialErrorState;
  64. bool CaptureCMakeErrorValue;
  65. };
  66. }
  67. bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
  68. cmExecutionStatus& status)
  69. {
  70. // save error state and restore it if needed
  71. SaveRestoreErrorState errorState;
  72. // Allocate space for argument values.
  73. this->BindArguments();
  74. // Process input arguments.
  75. std::vector<std::string> unparsedArguments;
  76. std::vector<std::string> keywordsMissingValue;
  77. std::vector<std::string> parsedKeywords;
  78. this->Parse(args, &unparsedArguments, &keywordsMissingValue,
  79. &parsedKeywords);
  80. this->CheckArguments(keywordsMissingValue);
  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. const char* 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).c_str(),
  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).c_str(),
  133. this->Quiet);
  134. } else {
  135. cmCTestLog(this->CTest, ERROR_MESSAGE,
  136. "CTEST_BINARY_DIRECTORY not set" << std::endl;);
  137. }
  138. }
  139. if (!this->Source.empty()) {
  140. cmCTestLog(this->CTest, DEBUG,
  141. "Set source directory to: " << this->Source << std::endl);
  142. this->CTest->SetCTestConfiguration(
  143. "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(),
  144. this->Quiet);
  145. } else {
  146. this->CTest->SetCTestConfiguration(
  147. "SourceDirectory",
  148. cmSystemTools::CollapseFullPath(
  149. this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY"))
  150. .c_str(),
  151. this->Quiet);
  152. }
  153. if (const char* changeId =
  154. this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
  155. this->CTest->SetCTestConfiguration("ChangeId", changeId, this->Quiet);
  156. }
  157. cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;);
  158. cmCTestGenericHandler* handler = this->InitializeHandler();
  159. if (!handler) {
  160. cmCTestLog(this->CTest, ERROR_MESSAGE,
  161. "Cannot instantiate test handler " << this->GetName()
  162. << std::endl);
  163. if (captureCMakeError) {
  164. this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
  165. std::string const& err = status.GetError();
  166. if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
  167. cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n");
  168. }
  169. return true;
  170. }
  171. return false;
  172. }
  173. handler->SetAppendXML(this->Append);
  174. handler->PopulateCustomVectors(this->Makefile);
  175. if (!this->SubmitIndex.empty()) {
  176. handler->SetSubmitIndex(atoi(this->SubmitIndex.c_str()));
  177. }
  178. cmWorkingDirectory workdir(
  179. this->CTest->GetCTestConfiguration("BuildDirectory"));
  180. if (workdir.Failed()) {
  181. this->SetError("failed to change directory to " +
  182. this->CTest->GetCTestConfiguration("BuildDirectory") +
  183. " : " + std::strerror(workdir.GetLastResult()));
  184. if (captureCMakeError) {
  185. this->Makefile->AddDefinition(this->CaptureCMakeError, "-1");
  186. cmCTestLog(this->CTest, ERROR_MESSAGE,
  187. this->GetName() << " " << status.GetError() << "\n");
  188. // return success because failure is recorded in CAPTURE_CMAKE_ERROR
  189. return true;
  190. }
  191. return false;
  192. }
  193. int res = handler->ProcessHandler();
  194. if (!this->ReturnValue.empty()) {
  195. this->Makefile->AddDefinition(this->ReturnValue, std::to_string(res));
  196. }
  197. this->ProcessAdditionalValues(handler);
  198. // log the error message if there was an error
  199. if (captureCMakeError) {
  200. const char* returnString = "0";
  201. if (cmSystemTools::GetErrorOccuredFlag()) {
  202. returnString = "-1";
  203. std::string const& err = status.GetError();
  204. // print out the error if it is not "unknown error" which means
  205. // there was no message
  206. if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) {
  207. cmCTestLog(this->CTest, ERROR_MESSAGE, err);
  208. }
  209. }
  210. // store the captured cmake error state 0 or -1
  211. this->Makefile->AddDefinition(this->CaptureCMakeError, returnString);
  212. }
  213. return true;
  214. }
  215. void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*)
  216. {
  217. }
  218. void cmCTestHandlerCommand::BindArguments()
  219. {
  220. this->Bind("APPEND"_s, this->Append);
  221. this->Bind("QUIET"_s, this->Quiet);
  222. this->Bind("RETURN_VALUE"_s, this->ReturnValue);
  223. this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
  224. this->Bind("SOURCE"_s, this->Source);
  225. this->Bind("BUILD"_s, this->Build);
  226. this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex);
  227. }
  228. void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&)
  229. {
  230. }