diff options
Diffstat (limited to 'Source/CTest/cmCTestHandlerCommand.cxx')
-rw-r--r-- | Source/CTest/cmCTestHandlerCommand.cxx | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx new file mode 100644 index 0000000..5a7baf5 --- /dev/null +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -0,0 +1,295 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCTestHandlerCommand.h" + +#include "cmCTest.h" +#include "cmCTestGenericHandler.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmWorkingDirectory.h" +#include "cmake.h" + +#include <sstream> +#include <stdlib.h> + +class cmExecutionStatus; + +cmCTestHandlerCommand::cmCTestHandlerCommand() +{ + const size_t INIT_SIZE = 100; + size_t cc; + this->Arguments.reserve(INIT_SIZE); + for (cc = 0; cc < INIT_SIZE; ++cc) { + this->Arguments.push_back(nullptr); + } + this->Arguments[ct_RETURN_VALUE] = "RETURN_VALUE"; + this->Arguments[ct_CAPTURE_CMAKE_ERROR] = "CAPTURE_CMAKE_ERROR"; + this->Arguments[ct_SOURCE] = "SOURCE"; + this->Arguments[ct_BUILD] = "BUILD"; + this->Arguments[ct_SUBMIT_INDEX] = "SUBMIT_INDEX"; + this->Last = ct_LAST; + this->AppendXML = false; + this->Quiet = false; +} + +namespace { +// class to save and restore the error state for ctest_* commands +// if a ctest_* command has a CAPTURE_CMAKE_ERROR then put the error +// state into there and restore the system wide error to what +// it was before the command ran +class SaveRestoreErrorState +{ +public: + SaveRestoreErrorState() + { + this->InitialErrorState = cmSystemTools::GetErrorOccuredFlag(); + cmSystemTools::ResetErrorOccuredFlag(); // rest the error state + this->CaptureCMakeErrorValue = false; + } + // if the function has a CAPTURE_CMAKE_ERROR then we should restore + // the error state to what it was before the function was run + // if not then let the error state be what it is + void CaptureCMakeError() { this->CaptureCMakeErrorValue = true; } + ~SaveRestoreErrorState() + { + // if we are not saving the return value then make sure + // if it was in error it goes back to being in error + // otherwise leave it be what it is + if (!this->CaptureCMakeErrorValue) { + if (this->InitialErrorState) { + cmSystemTools::SetErrorOccured(); + } + return; + } + // if we have saved the error in a return variable + // then put things back exactly like they were + bool currentState = cmSystemTools::GetErrorOccuredFlag(); + // if the state changed during this command we need + // to handle it, if not then nothing needs to be done + if (currentState != this->InitialErrorState) { + // restore the initial error state + if (this->InitialErrorState) { + cmSystemTools::SetErrorOccured(); + } else { + cmSystemTools::ResetErrorOccuredFlag(); + } + } + } + +private: + bool InitialErrorState; + bool CaptureCMakeErrorValue; +}; +} + +bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, + cmExecutionStatus& /*unused*/) +{ + // save error state and restore it if needed + SaveRestoreErrorState errorState; + // Allocate space for argument values. + this->Values.clear(); + this->Values.resize(this->Last, nullptr); + + // Process input arguments. + this->ArgumentDoing = ArgumentDoingNone; + // look at all arguments and do not short circuit on the first + // bad one so that CAPTURE_CMAKE_ERROR can override setting the + // global error state + bool foundBadArgument = false; + for (std::string const& arg : args) { + // Check this argument. + if (!this->CheckArgumentKeyword(arg) && !this->CheckArgumentValue(arg)) { + std::ostringstream e; + e << "called with unknown argument \"" << arg << "\"."; + this->SetError(e.str()); + foundBadArgument = true; + } + // note bad argument + if (this->ArgumentDoing == ArgumentDoingError) { + foundBadArgument = true; + } + } + bool capureCMakeError = (this->Values[ct_CAPTURE_CMAKE_ERROR] && + *this->Values[ct_CAPTURE_CMAKE_ERROR]); + // now that arguments are parsed check to see if there is a + // CAPTURE_CMAKE_ERROR specified let the errorState object know. + if (capureCMakeError) { + errorState.CaptureCMakeError(); + } + // if we found a bad argument then exit before running command + if (foundBadArgument) { + // store the cmake error + if (capureCMakeError) { + this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], + "-1"); + std::string const err = this->GetName() + " " + this->GetError(); + if (!cmSystemTools::FindLastString(err.c_str(), "unknown error.")) { + cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n"); + } + // return success because failure is recorded in CAPTURE_CMAKE_ERROR + return true; + } + // return failure because of bad argument + return false; + } + + // Set the config type of this ctest to the current value of the + // CTEST_CONFIGURATION_TYPE script variable if it is defined. + // The current script value trumps the -C argument on the command + // line. + const char* ctestConfigType = + this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE"); + if (ctestConfigType) { + this->CTest->SetConfigType(ctestConfigType); + } + + if (this->Values[ct_BUILD]) { + this->CTest->SetCTestConfiguration( + "BuildDirectory", + cmSystemTools::CollapseFullPath(this->Values[ct_BUILD]).c_str(), + this->Quiet); + } else { + const char* bdir = + this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY"); + if (bdir) { + this->CTest->SetCTestConfiguration( + "BuildDirectory", cmSystemTools::CollapseFullPath(bdir).c_str(), + this->Quiet); + } else { + cmCTestLog(this->CTest, ERROR_MESSAGE, "CTEST_BINARY_DIRECTORY not set" + << std::endl;); + } + } + if (this->Values[ct_SOURCE]) { + cmCTestLog(this->CTest, DEBUG, "Set source directory to: " + << this->Values[ct_SOURCE] << std::endl); + this->CTest->SetCTestConfiguration( + "SourceDirectory", + cmSystemTools::CollapseFullPath(this->Values[ct_SOURCE]).c_str(), + this->Quiet); + } else { + this->CTest->SetCTestConfiguration( + "SourceDirectory", + cmSystemTools::CollapseFullPath( + this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")) + .c_str(), + this->Quiet); + } + + if (const char* changeId = + this->Makefile->GetDefinition("CTEST_CHANGE_ID")) { + this->CTest->SetCTestConfiguration("ChangeId", changeId, this->Quiet); + } + + cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;); + cmCTestGenericHandler* handler = this->InitializeHandler(); + if (!handler) { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot instantiate test handler " + << this->GetName() << std::endl); + if (capureCMakeError) { + this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], + "-1"); + const char* err = this->GetError(); + if (err && !cmSystemTools::FindLastString(err, "unknown error.")) { + cmCTestLog(this->CTest, ERROR_MESSAGE, err << " error from command\n"); + } + return true; + } + return false; + } + + handler->SetAppendXML(this->AppendXML); + + handler->PopulateCustomVectors(this->Makefile); + if (this->Values[ct_SUBMIT_INDEX]) { + if (!this->CTest->GetDropSiteCDash() && + this->CTest->GetDartVersion() <= 1) { + cmCTestLog( + this->CTest, ERROR_MESSAGE, + "Dart before version 2.0 does not support collecting submissions." + << std::endl + << "Please upgrade the server to Dart 2 or higher, or do not use " + "SUBMIT_INDEX." + << std::endl); + } else { + handler->SetSubmitIndex(atoi(this->Values[ct_SUBMIT_INDEX])); + } + } + cmWorkingDirectory workdir( + this->CTest->GetCTestConfiguration("BuildDirectory")); + int res = handler->ProcessHandler(); + if (this->Values[ct_RETURN_VALUE] && *this->Values[ct_RETURN_VALUE]) { + std::ostringstream str; + str << res; + this->Makefile->AddDefinition(this->Values[ct_RETURN_VALUE], + str.str().c_str()); + } + this->ProcessAdditionalValues(handler); + // log the error message if there was an error + if (capureCMakeError) { + const char* returnString = "0"; + if (cmSystemTools::GetErrorOccuredFlag()) { + returnString = "-1"; + const char* err = this->GetError(); + // print out the error if it is not "unknown error" which means + // there was no message + if (err && !cmSystemTools::FindLastString(err, "unknown error.")) { + cmCTestLog(this->CTest, ERROR_MESSAGE, err); + } + } + // store the captured cmake error state 0 or -1 + this->Makefile->AddDefinition(this->Values[ct_CAPTURE_CMAKE_ERROR], + returnString); + } + return true; +} + +void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) +{ +} + +bool cmCTestHandlerCommand::CheckArgumentKeyword(std::string const& arg) +{ + // Look for non-value arguments common to all commands. + if (arg == "APPEND") { + this->ArgumentDoing = ArgumentDoingNone; + this->AppendXML = true; + return true; + } + if (arg == "QUIET") { + this->ArgumentDoing = ArgumentDoingNone; + this->Quiet = true; + return true; + } + + // Check for a keyword in our argument/value table. + for (unsigned int k = 0; k < this->Arguments.size(); ++k) { + if (this->Arguments[k] && arg == this->Arguments[k]) { + this->ArgumentDoing = ArgumentDoingKeyword; + this->ArgumentIndex = k; + return true; + } + } + return false; +} + +bool cmCTestHandlerCommand::CheckArgumentValue(std::string const& arg) +{ + if (this->ArgumentDoing == ArgumentDoingKeyword) { + this->ArgumentDoing = ArgumentDoingNone; + unsigned int k = this->ArgumentIndex; + if (this->Values[k]) { + std::ostringstream e; + e << "Called with more than one value for " << this->Arguments[k]; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); + this->ArgumentDoing = ArgumentDoingError; + return true; + } + this->Values[k] = arg.c_str(); + cmCTestLog(this->CTest, DEBUG, "Set " << this->Arguments[k] << " to " + << arg << "\n"); + return true; + } + return false; +} |