in Source/cmCTest.cxx [1851:2654]
int cmCTest::Run(std::vector<std::string> const& args)
{
char const* ctestExec = "ctest";
bool cmakeAndTest = false;
bool processSteps = false;
bool SRArgumentSpecified = false;
std::vector<std::pair<std::string, bool>> runScripts;
// 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 =
std::find_if(args.begin(), args.end(), [](std::string const& arg) -> bool {
return arg == "--preset" || cmHasLiteralPrefix(arg, "--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 (cmHasLiteralPrefix(*it, "--preset=")) {
auto const& presetName = it->substr(9);
success = this->SetArgsFromPreset(presetName, listPresets);
} else if (++it != args.end()) {
auto const& 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;
}
}
auto const dashD = [this, &processSteps](std::string const& targ) -> bool {
// AddTestsForDashboard parses the dashboard type and converts it
// into the separate stages
if (this->AddTestsForDashboardType(targ)) {
processSteps = true;
return true;
}
if (this->AddVariableDefinition(targ)) {
return true;
}
this->ErrorMessageUnknownDashDValue(targ);
return false;
};
auto const dashT = [this, &processSteps,
ctestExec](std::string const& action) -> bool {
if (!this->SetTest(action, false)) {
cmCTestLog(this, ERROR_MESSAGE,
"CTest -T called with incorrect option: " << action << '\n');
/* clang-format off */
cmCTestLog(this, ERROR_MESSAGE,
"Available options are:\n"
" " << ctestExec << " -T all\n"
" " << ctestExec << " -T start\n"
" " << ctestExec << " -T update\n"
" " << ctestExec << " -T configure\n"
" " << ctestExec << " -T build\n"
" " << ctestExec << " -T test\n"
" " << ctestExec << " -T coverage\n"
" " << ctestExec << " -T memcheck\n"
" " << ctestExec << " -T notes\n"
" " << ctestExec << " -T submit\n");
/* clang-format on */
return false;
}
processSteps = true;
return true;
};
auto const dashM = [this, &processSteps,
ctestExec](std::string const& model) -> bool {
if (cmSystemTools::LowerCase(model) == "nightly"_s) {
this->SetTestModel(cmCTest::NIGHTLY);
} else if (cmSystemTools::LowerCase(model) == "continuous"_s) {
this->SetTestModel(cmCTest::CONTINUOUS);
} else if (cmSystemTools::LowerCase(model) == "experimental"_s) {
this->SetTestModel(cmCTest::EXPERIMENTAL);
} else {
cmCTestLog(this, ERROR_MESSAGE,
"CTest -M called with incorrect option: " << model << '\n');
/* clang-format off */
cmCTestLog(this, ERROR_MESSAGE,
"Available options are:\n"
" " << ctestExec << " -M Continuous\n"
" " << ctestExec << " -M Experimental\n"
" " << ctestExec << " -M Nightly\n");
/* clang-format on */
return false;
}
processSteps = true;
return true;
};
auto const dashSP =
[&runScripts, &SRArgumentSpecified](std::string const& script) -> bool {
// -SR is an internal argument, -SP should be ignored when it is passed
if (!SRArgumentSpecified) {
runScripts.emplace_back(cmSystemTools::ToNormalizedPathOnDisk(script),
false);
}
return true;
};
auto const dashSR =
[&runScripts, &SRArgumentSpecified](std::string const& script) -> bool {
// -SR should be processed only once
if (!SRArgumentSpecified) {
SRArgumentSpecified = true;
runScripts.emplace_back(cmSystemTools::ToNormalizedPathOnDisk(script),
true);
}
return true;
};
auto const dash_S =
[&runScripts, &SRArgumentSpecified](std::string const& script) -> bool {
// -SR is an internal argument, -S should be ignored when it is passed
if (!SRArgumentSpecified) {
runScripts.emplace_back(cmSystemTools::ToNormalizedPathOnDisk(script),
true);
}
return true;
};
auto const dashJ = [this](cm::string_view arg,
std::string const& j) -> bool {
cm::optional<size_t> parallelLevel;
// No value or an empty value tells ctest to choose a default.
if (!j.empty()) {
// A non-empty value must be a non-negative integer.
unsigned long plevel = 0;
if (!cmStrToULong(j, &plevel)) {
cmSystemTools::Error(
cmStrCat('\'', arg, "' given invalid value '", j, '\''));
return false;
}
parallelLevel = plevel;
}
this->SetParallelLevel(parallelLevel);
this->Impl->ParallelLevelSetInCli = true;
return true;
};
auto const dashC = [this](std::string const& config) -> bool {
this->SetConfigType(config);
return true;
};
auto const dashGroup = [this](std::string const& group) -> bool {
this->Impl->SpecificGroup = group;
return true;
};
auto const dashQ = [this](std::string const&) -> bool {
this->Impl->Quiet = true;
return true;
};
auto const dashV = [this](std::string const&) -> bool {
this->Impl->Verbose = true;
return true;
};
auto const dashVV = [this](std::string const&) -> bool {
this->Impl->ExtraVerbose = true;
this->Impl->Verbose = true;
return true;
};
auto const dashO = [this](std::string const& log) -> bool {
this->SetOutputLogFileName(log);
return true;
};
auto const dashW = [this](std::string const& width) -> bool {
this->Impl->MaxTestNameWidth = atoi(width.c_str());
return true;
};
auto const dashA = [this, &processSteps](std::string const& notes) -> bool {
processSteps = true;
this->SetTest("Notes");
this->SetNotesFiles(notes);
return true;
};
auto const dashI = [this](std::string const& tests) -> bool {
this->Impl->TestOptions.TestsToRunInformation = tests;
return true;
};
auto const dashU = [this](std::string const&) -> bool {
this->Impl->TestOptions.UseUnion = true;
return true;
};
auto const dashR = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.IncludeRegularExpression = expr;
return true;
};
auto const dashE = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.ExcludeRegularExpression = expr;
return true;
};
auto const dashL = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.LabelRegularExpression.push_back(expr);
return true;
};
auto const dashLE = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.ExcludeLabelRegularExpression.push_back(expr);
return true;
};
auto const dashFA = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.ExcludeFixtureRegularExpression = expr;
return true;
};
auto const dashFS = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.ExcludeFixtureSetupRegularExpression = expr;
return true;
};
auto const dashFC = [this](std::string const& expr) -> bool {
this->Impl->TestOptions.ExcludeFixtureCleanupRegularExpression = expr;
return true;
};
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value)>;
auto const arguments = std::vector<CommandArgument>{
CommandArgument{ "--dashboard", CommandArgument::Values::One, dashD },
CommandArgument{ "-D",
"-D must be followed by dashboard mode or VAR=VALUE.",
CommandArgument::Values::One, dashD },
CommandArgument{
"-D", "-D must be followed by dashboard mode or VAR=VALUE.",
CommandArgument::Values::One, CommandArgument::RequiresSeparator::No,
[this](std::string const& def) -> bool {
// Unsuccessful parsing of VAR=VALUE has historically
// been ignored.
this->AddVariableDefinition(def);
return true;
} },
CommandArgument{ "-T", CommandArgument::Values::One, dashT },
CommandArgument{ "--test-action", CommandArgument::Values::One, dashT },
CommandArgument{ "-M", CommandArgument::Values::One, dashM },
CommandArgument{ "--test-model", CommandArgument::Values::One, dashM },
CommandArgument{ "--extra-submit", CommandArgument::Values::One,
[this, &processSteps](std::string const& extra) -> bool {
processSteps = true;
this->SetTest("Submit");
return this->SubmitExtraFiles(extra);
} },
CommandArgument{
"--build-and-test", "--build-and-test must have source and binary dir",
CommandArgument::Values::Two,
[this, &cmakeAndTest](std::string const& dirs) -> bool {
cmakeAndTest = true;
cmList dirList{ dirs };
if (dirList.size() != 2) {
return false;
}
this->Impl->BuildAndTest.SourceDir =
cmSystemTools::ToNormalizedPathOnDisk(dirList[0]);
this->Impl->BuildAndTest.BinaryDir =
cmSystemTools::ToNormalizedPathOnDisk(dirList[1]);
cmSystemTools::MakeDirectory(this->Impl->BuildAndTest.BinaryDir);
return true;
} },
CommandArgument{ "--build-target", CommandArgument::Values::One,
[this](std::string const& t) -> bool {
this->Impl->BuildAndTest.BuildTargets.emplace_back(t);
return true;
} },
CommandArgument{ "--build-noclean", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->BuildAndTest.BuildNoClean = true;
return true;
} },
CommandArgument{ "--build-nocmake", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->BuildAndTest.BuildNoCMake = true;
return true;
} },
CommandArgument{ "--build-two-config", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->BuildAndTest.BuildTwoConfig = true;
return true;
} },
CommandArgument{ "--build-run-dir", CommandArgument::Values::One,
[this](std::string const& dir) -> bool {
this->Impl->BuildAndTest.BuildRunDir = dir;
return true;
} },
CommandArgument{ "--build-exe-dir", CommandArgument::Values::One,
[this](std::string const& dir) -> bool {
this->Impl->BuildAndTest.ExecutableDirectory = dir;
return true;
} },
CommandArgument{ "--test-timeout", CommandArgument::Values::One,
[this](std::string const& t) -> bool {
this->Impl->BuildAndTest.Timeout =
cmDuration(atof(t.c_str()));
return true;
} },
CommandArgument{ "--build-generator", CommandArgument::Values::One,
[this](std::string const& g) -> bool {
this->Impl->BuildAndTest.BuildGenerator = g;
return true;
} },
CommandArgument{ "--build-generator-platform",
CommandArgument::Values::One,
[this](std::string const& p) -> bool {
this->Impl->BuildAndTest.BuildGeneratorPlatform = p;
return true;
} },
CommandArgument{ "--build-generator-toolset", CommandArgument::Values::One,
[this](std::string const& t) -> bool {
this->Impl->BuildAndTest.BuildGeneratorToolset = t;
return true;
} },
CommandArgument{ "--build-project", CommandArgument::Values::One,
[this](std::string const& p) -> bool {
this->Impl->BuildAndTest.BuildProject = p;
return true;
} },
CommandArgument{ "--build-makeprogram", CommandArgument::Values::One,
[this](std::string const& p) -> bool {
this->Impl->BuildAndTest.BuildMakeProgram = p;
return true;
} },
CommandArgument{ "--build-config-sample", CommandArgument::Values::One,
[this](std::string const& s) -> bool {
this->Impl->BuildAndTest.ConfigSample = s;
return true;
} },
CommandArgument{ "-SP", CommandArgument::Values::One, dashSP },
CommandArgument{ "--script-new-process", CommandArgument::Values::One,
dashSP },
CommandArgument{ "-SR", CommandArgument::Values::One, dashSR },
CommandArgument{ "--script-run", CommandArgument::Values::One, dashSR },
CommandArgument{ "-S", CommandArgument::Values::One, dash_S },
CommandArgument{ "--script", CommandArgument::Values::One, dash_S },
CommandArgument{ "-F", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->Failover = true;
return true;
} },
CommandArgument{
"-j", CommandArgument::Values::ZeroOrOne,
[&dashJ](std::string const& j) -> bool { return dashJ("-j"_s, j); } },
CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne,
[&dashJ](std::string const& j) -> bool {
return dashJ("--parallel"_s, j);
} },
CommandArgument{ "-j", CommandArgument::Values::One,
CommandArgument::RequiresSeparator::No,
[this](std::string const& j) -> bool {
// The value must be a non-negative integer.
unsigned long plevel = 0;
if (!cmStrToULong(j, &plevel)) {
cmSystemTools::Error(
cmStrCat("'-j' given invalid value '", j, '\''));
return false;
}
this->SetParallelLevel(plevel);
this->Impl->ParallelLevelSetInCli = true;
return true;
} },
CommandArgument{
"--repeat-until-fail", "'--repeat-until-fail' requires an argument",
CommandArgument::Values::One,
[this](std::string const& r) -> bool {
if (this->Impl->RepeatMode != cmCTest::Repeat::Never) {
cmSystemTools::Error("At most one '--repeat' option may be used.");
return false;
}
long repeat = 1;
if (!cmStrToLong(r, &repeat)) {
cmSystemTools::Error(cmStrCat(
"'--repeat-until-fail' given non-integer value '", r, '\''));
return false;
}
this->Impl->RepeatCount = static_cast<int>(repeat);
if (repeat > 1) {
this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
}
return true;
} },
CommandArgument{
"--repeat", CommandArgument::Values::One,
[this](std::string const& r) -> bool {
if (this->Impl->RepeatMode != cmCTest::Repeat::Never) {
cmSystemTools::Error("At most one '--repeat' option may be used.");
return false;
}
cmsys::RegularExpression repeatRegex(
"^(until-fail|until-pass|after-timeout):([0-9]+)$");
if (repeatRegex.find(r)) {
std::string const& count = repeatRegex.match(2);
unsigned long n = 1;
cmStrToULong(count, &n); // regex guarantees success
this->Impl->RepeatCount = static_cast<int>(n);
if (this->Impl->RepeatCount > 1) {
std::string const& mode = repeatRegex.match(1);
if (mode == "until-fail") {
this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
} else if (mode == "until-pass") {
this->Impl->RepeatMode = cmCTest::Repeat::UntilPass;
} else if (mode == "after-timeout") {
this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout;
}
}
} else {
cmSystemTools::Error(
cmStrCat("'--repeat' given invalid value '", r, '\''));
return false;
}
return true;
} },
CommandArgument{ "--test-load", CommandArgument::Values::One,
[this](std::string const& l) -> bool {
unsigned long load;
if (cmStrToULong(l, &load)) {
this->SetTestLoad(load);
} else {
cmCTestLog(
this, WARNING,
"Invalid value for 'Test Load' : " << l << '\n');
}
return true;
} },
CommandArgument{ "--no-compress-output", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->CompressTestOutput = false;
return true;
} },
CommandArgument{ "--print-labels", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->PrintLabels = true;
return true;
} },
CommandArgument{ "--http1.0", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->UseHTTP10 = true;
return true;
} },
CommandArgument{ "--timeout", CommandArgument::Values::One,
[this](std::string const& t) -> bool {
auto timeout = cmDuration(atof(t.c_str()));
this->Impl->GlobalTimeout = timeout;
return true;
} },
CommandArgument{ "--stop-time", CommandArgument::Values::One,
[this](std::string const& t) -> bool {
this->SetStopTime(t);
return true;
} },
CommandArgument{ "--stop-on-failure", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->StopOnFailure = true;
return true;
} },
CommandArgument{ "-C", CommandArgument::Values::One, dashC },
CommandArgument{ "--build-config", CommandArgument::Values::One, dashC },
CommandArgument{ "--debug", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->Debug = true;
return true;
} },
CommandArgument{ "--group", CommandArgument::Values::One, dashGroup },
// This is an undocumented / deprecated option.
// "Track" has been renamed to "Group".
CommandArgument{ "--track", CommandArgument::Values::One, dashGroup },
CommandArgument{ "--show-line-numbers", CommandArgument::Values::Zero,
[](std::string const&) -> bool {
// Silently ignore this never-documented and now-removed
// option.
return true;
} },
CommandArgument{ "--no-label-summary", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->LabelSummary = false;
return true;
} },
CommandArgument{ "--no-subproject-summary", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->SubprojectSummary = false;
return true;
} },
CommandArgument{ "--progress", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->TestProgressOutput = true;
return true;
} },
CommandArgument{ "-Q", CommandArgument::Values::Zero, dashQ },
CommandArgument{ "--quiet", CommandArgument::Values::Zero, dashQ },
CommandArgument{ "-V", CommandArgument::Values::Zero, dashV },
CommandArgument{ "--verbose", CommandArgument::Values::Zero, dashV },
CommandArgument{ "-VV", CommandArgument::Values::Zero, dashVV },
CommandArgument{ "--extra-verbose", CommandArgument::Values::Zero,
dashVV },
CommandArgument{ "--output-on-failure", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->OutputTestOutputOnTestFailure = true;
return true;
} },
CommandArgument{ "--test-output-size-passed", CommandArgument::Values::One,
[this](std::string const& sz) -> bool {
long outputSize;
if (cmStrToLong(sz, &outputSize)) {
this->Impl->TestOptions.OutputSizePassed =
static_cast<int>(outputSize);
} else {
cmCTestLog(
this, WARNING,
"Invalid value for '--test-output-size-passed': "
<< sz << "\n");
}
return true;
} },
CommandArgument{ "--test-output-size-failed", CommandArgument::Values::One,
[this](std::string const& sz) -> bool {
long outputSize;
if (cmStrToLong(sz, &outputSize)) {
this->Impl->TestOptions.OutputSizeFailed =
static_cast<int>(outputSize);
} else {
cmCTestLog(
this, WARNING,
"Invalid value for '--test-output-size-failed': "
<< sz << "\n");
}
return true;
} },
CommandArgument{
"--test-output-truncation", CommandArgument::Values::One,
[this](std::string const& mode) -> bool {
if (!SetTruncationMode(this->Impl->TestOptions.OutputTruncation,
mode)) {
cmSystemTools::Error(
cmStrCat("Invalid value for '--test-output-truncation': ", mode));
return false;
}
return true;
} },
CommandArgument{ "--show-only", CommandArgument::Values::ZeroOrOne,
[this](std::string const& format) -> bool {
this->Impl->ShowOnly = true;
// Check if a specific format is requested.
// Defaults to human readable text.
if (format == "json-v1") {
// Force quiet mode so the only output
// is the json object model.
this->Impl->Quiet = true;
this->Impl->OutputAsJson = true;
this->Impl->OutputAsJsonVersion = 1;
} else if (format == "human") {
} else if (!format.empty()) {
cmSystemTools::Error(
cmStrCat("'--show-only=' given unknown value '",
format, '\''));
return false;
}
return true;
} },
CommandArgument{ "-N", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->ShowOnly = true;
return true;
} },
CommandArgument{ "-O", CommandArgument::Values::One, dashO },
CommandArgument{ "--output-log", CommandArgument::Values::One, dashO },
CommandArgument{ "--tomorrow-tag", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->TomorrowTag = true;
return true;
} },
CommandArgument{ "--force-new-ctest-process",
CommandArgument::Values::Zero,
[](std::string const&) -> bool {
// Silently ignore now-removed option.
return true;
} },
CommandArgument{ "-W", CommandArgument::Values::One, dashW },
CommandArgument{ "--max-width", CommandArgument::Values::One, dashW },
CommandArgument{ "--interactive-debug-mode", CommandArgument::Values::One,
[this](std::string const& idm) -> bool {
this->Impl->InteractiveDebugMode = cmIsOn(idm);
return true;
} },
CommandArgument{ "--http-header", CommandArgument::Values::One,
[this](std::string const& h) -> bool {
this->Impl->CommandLineHttpHeaders.push_back(h);
return true;
} },
CommandArgument{ "--submit-index", CommandArgument::Values::One,
[this](std::string const& index) -> bool {
this->Impl->SubmitIndex = atoi(index.c_str());
if (this->Impl->SubmitIndex < 0) {
this->Impl->SubmitIndex = 0;
}
return true;
} },
CommandArgument{ "--overwrite", CommandArgument::Values::One,
[this](std::string const& opt) -> bool {
this->AddCTestConfigurationOverwrite(opt);
return true;
} },
CommandArgument{ "-A", CommandArgument::Values::One, dashA },
CommandArgument{ "--add-notes", CommandArgument::Values::One, dashA },
CommandArgument{ "--test-dir", "'--test-dir' requires an argument",
CommandArgument::Values::One,
[this](std::string const& dir) -> bool {
this->Impl->TestDir = dir;
return true;
} },
CommandArgument{ "--output-junit", CommandArgument::Values::One,
[this](std::string const& file) -> bool {
this->SetOutputJUnitFileName(file);
return true;
} },
CommandArgument{ "--no-tests", CommandArgument::Values::One,
[this](std::string const& action) -> bool {
if (action == "error"_s) {
this->Impl->NoTestsMode = cmCTest::NoTests::Error;
} else if (action == "ignore"_s) {
this->Impl->NoTestsMode = cmCTest::NoTests::Ignore;
} else {
cmSystemTools::Error(
cmStrCat("'--no-tests=' given unknown value '",
action, '\''));
return false;
}
this->Impl->NoTestsModeSetInCli = true;
return true;
} },
CommandArgument{ "-I", CommandArgument::Values::One, dashI },
CommandArgument{ "--tests-information", CommandArgument::Values::One,
dashI },
CommandArgument{ "-U", CommandArgument::Values::One, dashU },
CommandArgument{ "--union", CommandArgument::Values::One, dashU },
CommandArgument{ "-R", CommandArgument::Values::One, dashR },
CommandArgument{ "--tests-regex", CommandArgument::Values::One, dashR },
CommandArgument{ "-E", CommandArgument::Values::One, dashE },
CommandArgument{ "--exclude-regex", CommandArgument::Values::One, dashE },
CommandArgument{ "-L", CommandArgument::Values::One, dashL },
CommandArgument{ "--label-regex", CommandArgument::Values::One, dashL },
CommandArgument{ "-LE", CommandArgument::Values::One, dashLE },
CommandArgument{ "--label-exclude", CommandArgument::Values::One, dashLE },
CommandArgument{ "-FA", CommandArgument::Values::One, dashFA },
CommandArgument{ "--fixture-exclude-any", CommandArgument::Values::One,
dashFA },
CommandArgument{ "-FS", CommandArgument::Values::One, dashFS },
CommandArgument{ "--fixture-exclude-setup", CommandArgument::Values::One,
dashFS },
CommandArgument{ "-FC", CommandArgument::Values::One, dashFC },
CommandArgument{ "--fixture-exclude-cleanup", CommandArgument::Values::One,
dashFC },
CommandArgument{ "--resource-spec-file", CommandArgument::Values::One,
[this](std::string const& file) -> bool {
this->Impl->TestOptions.ResourceSpecFile = file;
return true;
} },
CommandArgument{ "--tests-from-file", CommandArgument::Values::One,
[this](std::string const& file) -> bool {
this->Impl->TestOptions.TestListFile = file;
return true;
} },
CommandArgument{ "--exclude-from-file", CommandArgument::Values::One,
[this](std::string const& file) -> bool {
this->Impl->TestOptions.ExcludeTestListFile = file;
return true;
} },
CommandArgument{ "--schedule-random", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->TestOptions.ScheduleRandom = true;
return true;
} },
CommandArgument{ "--rerun-failed", CommandArgument::Values::Zero,
[this](std::string const&) -> bool {
this->Impl->TestOptions.RerunFailed = true;
return true;
} },
};
// process the command line arguments
for (size_t i = 1; i < args.size(); ++i) {
std::string const& arg = args[i];
bool matched = false;
for (auto const& m : arguments) {
if (m.matches(arg)) {
matched = true;
if (!m.parse(arg, i, args)) {
return 1;
}
break;
}
}
if (!matched && arg == "--build-options"_s) {
matched = true;
while (i + 1 < args.size() && args[i + 1] != "--build-target"_s &&
args[i + 1] != "--test-command"_s) {
++i;
this->Impl->BuildAndTest.BuildOptions.emplace_back(args[i]);
}
}
if (!matched && arg == "--test-command"_s && i + 1 < args.size()) {
matched = true;
++i;
this->Impl->BuildAndTest.TestCommand = args[i];
while (i + 1 < args.size()) {
++i;
this->Impl->BuildAndTest.TestCommandArgs.emplace_back(args[i]);
}
}
if (!matched && cmHasLiteralPrefix(arg, "-") &&
!cmHasLiteralPrefix(arg, "--preset")) {
cmSystemTools::Error(cmStrCat("Unknown argument: ", arg));
cmSystemTools::Error("Run 'ctest --help' for all supported options.");
return 1;
}
}
// handle CTEST_PARALLEL_LEVEL environment variable
if (!this->Impl->ParallelLevelSetInCli) {
if (cm::optional<std::string> parallelEnv =
cmSystemTools::GetEnvVar("CTEST_PARALLEL_LEVEL")) {
if (parallelEnv->empty() ||
parallelEnv->find_first_not_of(" \t") == std::string::npos) {
// An empty value tells ctest to choose a default.
this->SetParallelLevel(cm::nullopt);
} else {
// A non-empty value must be a non-negative integer.
// Otherwise, ignore it.
unsigned long plevel = 0;
if (cmStrToULong(*parallelEnv, &plevel)) {
this->SetParallelLevel(plevel);
}
}
}
}
// handle CTEST_NO_TESTS_ACTION environment variable
if (!this->Impl->NoTestsModeSetInCli) {
std::string action;
if (cmSystemTools::GetEnv("CTEST_NO_TESTS_ACTION", action) &&
!action.empty()) {
if (action == "error"_s) {
this->Impl->NoTestsMode = cmCTest::NoTests::Error;
} else if (action == "ignore"_s) {
this->Impl->NoTestsMode = cmCTest::NoTests::Ignore;
} else {
cmCTestLog(this, ERROR_MESSAGE,
"Unknown value for CTEST_NO_TESTS_ACTION: '" << action
<< '\'');
return 1;
}
}
}
// TestProgressOutput only supported if console supports it and not logging
// to a file
this->Impl->TestProgressOutput = this->Impl->TestProgressOutput &&
!this->Impl->OutputLogFile && this->ProgressOutputSupportedByConsole();
#ifdef _WIN32
if (this->Impl->TestProgressOutput) {
// Disable output line buffering so we can print content without
// a newline.
std::setvbuf(stdout, nullptr, _IONBF, 0);
}
#endif
// now what should cmake do? if --build-and-test was specified then
// we run the build and test handler and return
if (cmakeAndTest) {
return this->RunCMakeAndTest();
}
// -S, -SP, and/or -SP was specified
if (!runScripts.empty()) {
return this->RunScripts(runScripts);
}
// Establish the working directory.
std::string const currDir = cmSystemTools::GetLogicalWorkingDirectory();
std::string workDir = currDir;
if (!this->Impl->TestDir.empty()) {
workDir = cmSystemTools::ToNormalizedPathOnDisk(this->Impl->TestDir);
}
cmWorkingDirectory changeDir(workDir);
if (changeDir.Failed()) {
cmCTestLog(this, ERROR_MESSAGE, changeDir.GetError() << std::endl);
return 1;
}
this->Impl->BinaryDir = workDir;
// -D, -T, and/or -M was specified
if (processSteps) {
return this->ProcessSteps();
}
return this->ExecuteTests(args);
}