diff options
author | Sam Freed <safreed@microsoft.com> | 2020-12-14 18:06:08 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2021-02-01 16:59:40 (GMT) |
commit | 676ecf0d37cf5cfda390f52ce60babcb5da78108 (patch) | |
tree | 79129100ed585db7a91b82778166c1e1d3d6f7e5 /Source/cmCTest.cxx | |
parent | 4f4f2028b8e2c45eb6940d1ec5069caad9b606a2 (diff) | |
download | CMake-676ecf0d37cf5cfda390f52ce60babcb5da78108.zip CMake-676ecf0d37cf5cfda390f52ce60babcb5da78108.tar.gz CMake-676ecf0d37cf5cfda390f52ce60babcb5da78108.tar.bz2 |
cmake-presets: Add build and test presets
Fixes: #21391
Diffstat (limited to 'Source/cmCTest.cxx')
-rw-r--r-- | Source/cmCTest.cxx | 340 |
1 files changed, 339 insertions, 1 deletions
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6c1071d..4bea0d5 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -18,6 +18,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -38,6 +39,7 @@ # include <unistd.h> // IWYU pragma: keep #endif +#include "cmCMakePresetsFile.h" #include "cmCTestBuildAndTestHandler.h" #include "cmCTestBuildHandler.h" #include "cmCTestConfigureHandler.h" @@ -2257,6 +2259,311 @@ bool cmCTest::AddVariableDefinition(const std::string& arg) return false; } +void cmCTest::SetPersistentOptionIfNotEmpty(const std::string& value, + const std::string& optionName) +{ + if (!value.empty()) { + this->GetTestHandler()->SetPersistentOption(optionName, value.c_str()); + this->GetMemCheckHandler()->SetPersistentOption(optionName, value.c_str()); + } +} + +bool cmCTest::SetArgsFromPreset(const std::string& presetName, + bool listPresets) +{ + const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory(); + + cmCMakePresetsFile settingsFile; + auto result = settingsFile.ReadProjectPresets(workingDirectory); + if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + cmSystemTools::Error(cmStrCat("Could not read presets from ", + workingDirectory, ": ", + cmCMakePresetsFile::ResultToString(result))); + return false; + } + + if (listPresets) { + settingsFile.PrintTestPresetList(); + return true; + } + + auto presetPair = settingsFile.TestPresets.find(presetName); + if (presetPair == settingsFile.TestPresets.end()) { + cmSystemTools::Error(cmStrCat("No such test preset in ", workingDirectory, + ": \"", presetName, '"')); + settingsFile.PrintTestPresetList(); + return false; + } + + if (presetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden test preset in ", + workingDirectory, ": \"", presetName, '"')); + settingsFile.PrintTestPresetList(); + return false; + } + + auto const& expandedPreset = presetPair->second.Expanded; + if (!expandedPreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate test preset \"", + presetName, "\": Invalid macro expansion")); + settingsFile.PrintTestPresetList(); + return false; + } + + auto configurePresetPair = + settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset); + if (configurePresetPair == settingsFile.ConfigurePresets.end()) { + cmSystemTools::Error(cmStrCat("No such configure preset in ", + workingDirectory, ": \"", + expandedPreset->ConfigurePreset, '"')); + settingsFile.PrintConfigurePresetList(); + return false; + } + + if (configurePresetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ", + workingDirectory, ": \"", + expandedPreset->ConfigurePreset, '"')); + settingsFile.PrintConfigurePresetList(); + return false; + } + + auto const& expandedConfigurePreset = configurePresetPair->second.Expanded; + if (!expandedConfigurePreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"", + expandedPreset->ConfigurePreset, + "\": Invalid macro expansion")); + return false; + } + + auto presetEnvironment = expandedPreset->Environment; + for (auto const& var : presetEnvironment) { + if (var.second) { + cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second)); + } + } + + if (!expandedPreset->Configuration.empty()) { + this->SetConfigType(expandedPreset->Configuration); + } + + // Set build directory to value specified by the configure preset. + this->AddCTestConfigurationOverwrite( + cmStrCat("BuildDirectory=", expandedConfigurePreset->BinaryDir)); + for (const auto& kvp : expandedPreset->OverwriteConfigurationFile) { + this->AddCTestConfigurationOverwrite(kvp); + } + + if (expandedPreset->Output) { + this->Impl->TestProgressOutput = + expandedPreset->Output->ShortProgress.value_or(false); + + if (expandedPreset->Output->Verbosity) { + const auto& verbosity = *expandedPreset->Output->Verbosity; + switch (verbosity) { + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Extra: + this->Impl->ExtraVerbose = true; + // intentional fallthrough + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Verbose: + this->Impl->Verbose = true; + break; + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Default: + default: + // leave default settings + break; + } + } + + this->Impl->Debug = expandedPreset->Output->Debug.value_or(false); + this->Impl->ShowLineNumbers = + expandedPreset->Output->Debug.value_or(false); + this->Impl->OutputTestOutputOnTestFailure = + expandedPreset->Output->OutputOnFailure.value_or(false); + this->Impl->Quiet = expandedPreset->Output->Quiet.value_or(false); + + if (!expandedPreset->Output->OutputLogFile.empty()) { + this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile); + } + + this->Impl->LabelSummary = + expandedPreset->Output->LabelSummary.value_or(true); + this->Impl->SubprojectSummary = + expandedPreset->Output->SubprojectSummary.value_or(true); + + if (expandedPreset->Output->MaxPassedTestOutputSize) { + this->Impl->TestHandler.SetTestOutputSizePassed( + *expandedPreset->Output->MaxPassedTestOutputSize); + } + + if (expandedPreset->Output->MaxFailedTestOutputSize) { + this->Impl->TestHandler.SetTestOutputSizeFailed( + *expandedPreset->Output->MaxFailedTestOutputSize); + } + + if (expandedPreset->Output->MaxTestNameWidth) { + this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth; + } + } + + if (expandedPreset->Filter) { + if (expandedPreset->Filter->Include) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Name, "IncludeRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Label, "LabelRegularExpression"); + + if (expandedPreset->Filter->Include->Index) { + if (expandedPreset->Filter->Include->Index->IndexFile.empty()) { + const auto& start = expandedPreset->Filter->Include->Index->Start; + const auto& end = expandedPreset->Filter->Include->Index->End; + const auto& stride = expandedPreset->Filter->Include->Index->Stride; + std::string indexOptions; + indexOptions += (start ? std::to_string(*start) : "") + ","; + indexOptions += (end ? std::to_string(*end) : "") + ","; + indexOptions += (stride ? std::to_string(*stride) : "") + ","; + indexOptions += + cmJoin(expandedPreset->Filter->Include->Index->SpecificTests, ","); + + this->SetPersistentOptionIfNotEmpty(indexOptions, + "TestsToRunInformation"); + } else { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Index->IndexFile, + "TestsToRunInformation"); + } + } + + if (expandedPreset->Filter->Include->UseUnion.value_or(false)) { + this->GetTestHandler()->SetPersistentOption("UseUnion", "true"); + this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true"); + } + } + + if (expandedPreset->Filter->Exclude) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Name, "ExcludeRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Label, + "ExcludeLabelRegularExpression"); + + if (expandedPreset->Filter->Exclude->Fixtures) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Any, + "ExcludeFixtureRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Setup, + "ExcludeFixtureSetupRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Cleanup, + "ExcludeFixtureCleanupRegularExpression"); + } + } + } + + if (expandedPreset->Execution) { + this->Impl->StopOnFailure = + expandedPreset->Execution->StopOnFailure.value_or(false); + this->Impl->Failover = + expandedPreset->Execution->EnableFailover.value_or(false); + + if (expandedPreset->Execution->Jobs) { + auto jobs = *expandedPreset->Execution->Jobs; + this->SetParallelLevel(jobs); + this->Impl->ParallelLevelSetInCli = true; + } + + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Execution->ResourceSpecFile, "ResourceSpecFile"); + + if (expandedPreset->Execution->TestLoad) { + auto testLoad = *expandedPreset->Execution->TestLoad; + this->SetTestLoad(testLoad); + } + + if (expandedPreset->Execution->ShowOnly) { + this->Impl->ShowOnly = true; + + switch (*expandedPreset->Execution->ShowOnly) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum:: + JsonV1: + this->Impl->Quiet = true; + this->Impl->OutputAsJson = true; + this->Impl->OutputAsJsonVersion = 1; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum:: + Human: + // intentional fallthrough (human is the default) + default: + break; + } + } + + if (expandedPreset->Execution->RerunFailed.value_or(false)) { + this->GetTestHandler()->SetPersistentOption("RerunFailed", "true"); + this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true"); + } + + if (expandedPreset->Execution->Repeat) { + this->Impl->RepeatCount = expandedPreset->Execution->Repeat->Count; + switch (expandedPreset->Execution->Repeat->Mode) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::UntilFail: + this->Impl->RepeatMode = cmCTest::Repeat::UntilFail; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::UntilPass: + this->Impl->RepeatMode = cmCTest::Repeat::UntilPass; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::AfterTimeout: + this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout; + break; + default: + // should never default since mode is required + return false; + } + } + + if (expandedPreset->Execution->InteractiveDebugging) { + this->Impl->InteractiveDebugMode = + *expandedPreset->Execution->InteractiveDebugging; + } + + if (expandedPreset->Execution->ScheduleRandom.value_or(false)) { + this->Impl->ScheduleType = "Random"; + } + + if (expandedPreset->Execution->Timeout) { + this->Impl->GlobalTimeout = + cmDuration(*expandedPreset->Execution->Timeout); + } + + if (expandedPreset->Execution->NoTestsAction) { + switch (*expandedPreset->Execution->NoTestsAction) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Error: + this->Impl->NoTestsMode = cmCTest::NoTests::Error; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Ignore: + this->Impl->NoTestsMode = cmCTest::NoTests::Ignore; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Default: + break; + default: + // should never default + return false; + } + } + } + + return true; +} + // the main entry point of ctest, called from main int cmCTest::Run(std::vector<std::string>& args, std::string* output) { @@ -2268,6 +2575,37 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // copy the command line cm::append(this->Impl->InitialCommandLineArguments, args); + // check if a test preset was specified + + bool listPresets = + find(args.begin(), args.end(), "--list-presets") != args.end(); + auto it = find(args.begin(), args.end(), "--preset"); + if (listPresets || it != args.end()) { + std::string errormsg; + bool success; + + if (listPresets) { + // If listing presets we don't need a presetName + success = this->SetArgsFromPreset("", listPresets); + } else { + if (++it != args.end()) { + auto presetName = *it; + success = this->SetArgsFromPreset(presetName, listPresets); + } else { + cmSystemTools::Error("'--preset' requires an argument"); + success = false; + } + } + + if (listPresets) { + return success ? 0 : 1; + } + + if (!success) { + return 1; + } + } + // process the command line arguments for (size_t i = 1; i < args.size(); ++i) { // handle the simple commandline arguments @@ -2339,7 +2677,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) this->Impl->ScheduleType = "Random"; } - // pass the argument to all the handlers as well, but i may no longer be + // pass the argument to all the handlers as well, but it may no longer be // set to what it was originally so I'm not sure this is working as // intended for (auto& handler : this->Impl->GetTestingHandlers()) { |