mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-07 06:09:52 -06:00
``` git grep -lz 'Copyright.txt or https://cmake.org/licensing ' | while IFS= read -r -d $'\0' f ; do sed -i '/Copyright.txt or https:\/\/cmake.org\/licensing / { s/Copyright.txt/LICENSE.rst/ }' "$f" ; done ```
219 lines
6.9 KiB
C++
219 lines
6.9 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmCTestHandlerCommand.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <sstream>
|
|
|
|
#include <cm/string_view>
|
|
|
|
#include "cmCTest.h"
|
|
#include "cmCTestGenericHandler.h"
|
|
#include "cmExecutionStatus.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmValue.h"
|
|
#include "cmWorkingDirectory.h"
|
|
|
|
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::GetErrorOccurredFlag();
|
|
cmSystemTools::ResetErrorOccurredFlag(); // 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::SetErrorOccurred();
|
|
}
|
|
return;
|
|
}
|
|
// if we have saved the error in a return variable
|
|
// then put things back exactly like they were
|
|
bool currentState = cmSystemTools::GetErrorOccurredFlag();
|
|
// 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::SetErrorOccurred();
|
|
} else {
|
|
cmSystemTools::ResetErrorOccurredFlag();
|
|
}
|
|
}
|
|
}
|
|
SaveRestoreErrorState(SaveRestoreErrorState const&) = delete;
|
|
SaveRestoreErrorState& operator=(SaveRestoreErrorState const&) = delete;
|
|
|
|
private:
|
|
bool InitialErrorState;
|
|
bool CaptureCMakeErrorValue;
|
|
};
|
|
}
|
|
|
|
bool cmCTestHandlerCommand::InvokeImpl(
|
|
BasicArguments& args, std::vector<std::string> const& unparsed,
|
|
cmExecutionStatus& status, std::function<bool()> handler) const
|
|
{
|
|
// save error state and restore it if needed
|
|
SaveRestoreErrorState errorState;
|
|
if (!args.CaptureCMakeError.empty()) {
|
|
errorState.CaptureCMakeError();
|
|
}
|
|
|
|
bool success = [&]() -> bool {
|
|
if (args.MaybeReportError(status.GetMakefile())) {
|
|
return true;
|
|
}
|
|
|
|
std::sort(args.ParsedKeywords.begin(), args.ParsedKeywords.end());
|
|
auto const it = std::adjacent_find(args.ParsedKeywords.begin(),
|
|
args.ParsedKeywords.end());
|
|
if (it != args.ParsedKeywords.end()) {
|
|
status.SetError(cmStrCat("called with more than one value for ", *it));
|
|
return false;
|
|
}
|
|
|
|
if (!unparsed.empty()) {
|
|
status.SetError(
|
|
cmStrCat("called with unknown argument \"", unparsed.front(), "\"."));
|
|
return false;
|
|
}
|
|
|
|
return handler();
|
|
}();
|
|
|
|
if (args.CaptureCMakeError.empty()) {
|
|
return success;
|
|
}
|
|
|
|
if (!success) {
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
this->GetName() << ' ' << status.GetError() << '\n');
|
|
}
|
|
|
|
cmMakefile& mf = status.GetMakefile();
|
|
success = success && !cmSystemTools::GetErrorOccurredFlag();
|
|
mf.AddDefinition(args.CaptureCMakeError, success ? "0" : "-1");
|
|
return true;
|
|
}
|
|
|
|
bool cmCTestHandlerCommand::ExecuteHandlerCommand(
|
|
HandlerArguments& args, cmExecutionStatus& status) const
|
|
{
|
|
cmMakefile& mf = status.GetMakefile();
|
|
|
|
// Process input arguments.
|
|
this->CheckArguments(args, status);
|
|
|
|
// 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.
|
|
cmValue ctestConfigType = mf.GetDefinition("CTEST_CONFIGURATION_TYPE");
|
|
if (ctestConfigType) {
|
|
this->CTest->SetConfigType(*ctestConfigType);
|
|
}
|
|
|
|
if (!args.Build.empty()) {
|
|
this->CTest->SetCTestConfiguration(
|
|
"BuildDirectory", cmSystemTools::CollapseFullPath(args.Build),
|
|
args.Quiet);
|
|
} else {
|
|
std::string const& bdir = mf.GetSafeDefinition("CTEST_BINARY_DIRECTORY");
|
|
if (!bdir.empty()) {
|
|
this->CTest->SetCTestConfiguration(
|
|
"BuildDirectory", cmSystemTools::CollapseFullPath(bdir), args.Quiet);
|
|
} else {
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"CTEST_BINARY_DIRECTORY not set" << std::endl);
|
|
}
|
|
}
|
|
if (!args.Source.empty()) {
|
|
cmCTestLog(this->CTest, DEBUG,
|
|
"Set source directory to: " << args.Source << std::endl);
|
|
this->CTest->SetCTestConfiguration(
|
|
"SourceDirectory", cmSystemTools::CollapseFullPath(args.Source),
|
|
args.Quiet);
|
|
} else {
|
|
this->CTest->SetCTestConfiguration(
|
|
"SourceDirectory",
|
|
cmSystemTools::CollapseFullPath(
|
|
mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY")),
|
|
args.Quiet);
|
|
}
|
|
|
|
if (cmValue changeId = mf.GetDefinition("CTEST_CHANGE_ID")) {
|
|
this->CTest->SetCTestConfiguration("ChangeId", *changeId, args.Quiet);
|
|
}
|
|
|
|
cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl);
|
|
auto handler = this->InitializeHandler(args, status);
|
|
if (!handler) {
|
|
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
|
"Cannot instantiate test handler " << this->GetName()
|
|
<< std::endl);
|
|
return false;
|
|
}
|
|
|
|
handler->SetAppendXML(args.Append);
|
|
|
|
handler->PopulateCustomVectors(&mf);
|
|
if (!args.SubmitIndex.empty()) {
|
|
handler->SetSubmitIndex(atoi(args.SubmitIndex.c_str()));
|
|
}
|
|
cmWorkingDirectory workdir(
|
|
this->CTest->GetCTestConfiguration("BuildDirectory"));
|
|
if (workdir.Failed()) {
|
|
status.SetError(workdir.GetError());
|
|
return false;
|
|
}
|
|
|
|
// reread time limit, as the variable may have been modified.
|
|
this->CTest->SetTimeLimit(mf.GetDefinition("CTEST_TIME_LIMIT"));
|
|
handler->SetCMakeInstance(mf.GetCMakeInstance());
|
|
|
|
int res = handler->ProcessHandler();
|
|
if (!args.ReturnValue.empty()) {
|
|
mf.AddDefinition(args.ReturnValue, std::to_string(res));
|
|
}
|
|
this->ProcessAdditionalValues(handler.get(), args, status);
|
|
return true;
|
|
}
|
|
|
|
void cmCTestHandlerCommand::CheckArguments(HandlerArguments&,
|
|
cmExecutionStatus&) const
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<cmCTestGenericHandler>
|
|
cmCTestHandlerCommand::InitializeHandler(HandlerArguments&,
|
|
cmExecutionStatus&) const
|
|
{
|
|
return nullptr;
|
|
};
|
|
|
|
void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*,
|
|
HandlerArguments const&,
|
|
cmExecutionStatus&) const
|
|
{
|
|
}
|