summaryrefslogtreecommitdiffstats
path: root/Source/cmake.cxx
diff options
context:
space:
mode:
authorRobert Maynard <robert.maynard@kitware.com>2020-10-30 17:07:48 (GMT)
committerBrad King <brad.king@kitware.com>2020-11-19 13:53:58 (GMT)
commit98290782b65f6d3cdd68bc0acaaf7b3b5d19070c (patch)
tree15ff713c4aa51280824818fe01554a26c864f797 /Source/cmake.cxx
parentd800c26ce947c01dc32871614c137f7c9bdce3cb (diff)
downloadCMake-98290782b65f6d3cdd68bc0acaaf7b3b5d19070c.zip
CMake-98290782b65f6d3cdd68bc0acaaf7b3b5d19070c.tar.gz
CMake-98290782b65f6d3cdd68bc0acaaf7b3b5d19070c.tar.bz2
cmake: redesign command-line argument parsing
Make handling more consistent: "-S" -> invalid "-S/path/to/source" -> valid "-S /path/to/source" -> valid "-S=/path/to/source" -> now valid "-D" -> invalid "-DStr" -> valid "-D Str" -> valid "-D=Str" -> now valid "--log-level=" -> invalid "--log-level" -> invalid "--log-level=2" -> valid "--log-level 2" -> now valid
Diffstat (limited to 'Source/cmake.cxx')
-rw-r--r--Source/cmake.cxx915
1 files changed, 548 insertions, 367 deletions
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 4fb0f2b..9783407 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -132,6 +132,131 @@ namespace {
using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
#endif
+struct CommandArgument
+{
+ enum struct Values
+ {
+ Zero,
+ One,
+ Two,
+ };
+
+ std::string InvalidSyntaxMessage;
+ std::string InvalidValueMessage;
+ std::string Name;
+ CommandArgument::Values Type;
+ std::function<bool(std::string const& value, cmake* state)> StoreCall;
+
+ template <typename FunctionType>
+ CommandArgument(std::string n, CommandArgument::Values t,
+ FunctionType&& func)
+ : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
+ , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
+ , Name(std::move(n))
+ , Type(t)
+ , StoreCall(std::forward<FunctionType>(func))
+ {
+ }
+
+ template <typename FunctionType>
+ CommandArgument(std::string n, std::string failedMsg,
+ CommandArgument::Values t, FunctionType&& func)
+ : InvalidSyntaxMessage(cmStrCat("Invalid syntax used with ", n))
+ , InvalidValueMessage(std::move(failedMsg))
+ , Name(std::move(n))
+ , Type(t)
+ , StoreCall(std::forward<FunctionType>(func))
+ {
+ }
+
+ bool matches(std::string const& input) const
+ {
+ return cmHasPrefix(input, this->Name);
+ }
+
+ template <typename T>
+ bool parse(std::string const& input, T& index,
+ std::vector<std::string> const& allArgs, cmake* state) const
+ {
+ enum struct ParseMode
+ {
+ Valid,
+ Invalid,
+ SyntaxError,
+ ValueError
+ };
+ ParseMode parseState = ParseMode::Valid;
+
+ // argument is the next parameter
+ if (this->Type == CommandArgument::Values::Zero) {
+ if (input.size() == this->Name.size()) {
+ parseState = this->StoreCall(input, state) ? ParseMode::Valid
+ : ParseMode::Invalid;
+ } else {
+ parseState = ParseMode::SyntaxError;
+ }
+
+ } else if (this->Type == CommandArgument::Values::One) {
+ if (input.size() == this->Name.size()) {
+ ++index;
+ if (index >= allArgs.size() || allArgs[index][0] == '-') {
+ parseState = ParseMode::ValueError;
+ } else {
+ parseState = this->StoreCall(allArgs[index], state)
+ ? ParseMode::Valid
+ : ParseMode::Invalid;
+ }
+ } else {
+ // parse the string to get the value
+ auto possible_value = cm::string_view(input).substr(this->Name.size());
+ if (possible_value.empty()) {
+ parseState = ParseMode::SyntaxError;
+ parseState = ParseMode::ValueError;
+ } else if (possible_value[0] == '=') {
+ possible_value.remove_prefix(1);
+ if (possible_value.empty()) {
+ parseState = ParseMode::ValueError;
+ } else {
+ parseState = this->StoreCall(std::string(possible_value), state)
+ ? ParseMode::Valid
+ : ParseMode::Invalid;
+ }
+ }
+ if (parseState == ParseMode::Valid) {
+ parseState = this->StoreCall(std::string(possible_value), state)
+ ? ParseMode::Valid
+ : ParseMode::Invalid;
+ }
+ }
+ } else if (this->Type == CommandArgument::Values::Two) {
+ if (input.size() == this->Name.size()) {
+ if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
+ allArgs[index + 2][0] == '-') {
+ parseState = ParseMode::ValueError;
+ } else {
+ index += 2;
+ parseState =
+ this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
+ state)
+ ? ParseMode::Valid
+ : ParseMode::Invalid;
+ }
+ }
+ }
+
+ if (parseState == ParseMode::SyntaxError) {
+ cmSystemTools::Error(this->InvalidSyntaxMessage);
+ } else if (parseState == ParseMode::ValueError) {
+ cmSystemTools::Error(this->InvalidValueMessage);
+ }
+ return (parseState == ParseMode::Valid);
+ }
+};
+
+auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool {
+ return true;
+};
+
} // namespace
static bool cmakeCheckStampFile(const std::string& stampName);
@@ -384,157 +509,147 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
{
auto findPackageMode = false;
auto seenScriptOption = false;
- for (unsigned int i = 1; i < args.size(); ++i) {
- std::string const& arg = args[i];
- if (cmHasLiteralPrefix(arg, "-D")) {
- std::string entry = arg.substr(2);
- if (entry.empty()) {
- ++i;
- if (i < args.size()) {
- entry = args[i];
- } else {
- cmSystemTools::Error("-D must be followed with VAR=VALUE.");
- return false;
- }
- }
- std::string var;
- std::string value;
- cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
- if (cmState::ParseCacheEntry(entry, var, value, type)) {
+
+ auto DefineLambda = [](std::string const& entry, cmake* state) -> bool {
+ std::string var;
+ std::string value;
+ cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
+ if (cmState::ParseCacheEntry(entry, var, value, type)) {
#ifndef CMAKE_BOOTSTRAP
- this->UnprocessedPresetVariables.erase(var);
+ state->UnprocessedPresetVariables.erase(var);
#endif
- this->ProcessCacheArg(var, value, type);
- } else {
- cmSystemTools::Error("Parse error in command line argument: " + arg +
- "\n" + "Should be: VAR:type=value\n");
- return false;
- }
- } else if (cmHasLiteralPrefix(arg, "-W")) {
- std::string entry = arg.substr(2);
- if (entry.empty()) {
- ++i;
- if (i < args.size()) {
- entry = args[i];
- } else {
- cmSystemTools::Error("-W must be followed with [no-]<name>.");
- return false;
- }
- }
+ state->ProcessCacheArg(var, value, type);
+ } else {
+ cmSystemTools::Error(cmStrCat("Parse error in command line argument: ",
+ entry, "\n Should be: VAR:type=value\n"));
+ return false;
+ }
+ return true;
+ };
- std::string name;
- bool foundNo = false;
- bool foundError = false;
- unsigned int nameStartPosition = 0;
+ auto WarningLambda = [](std::string const& entry, cmake* state) -> bool {
+ std::string name;
+ bool foundNo = false;
+ bool foundError = false;
+ unsigned int nameStartPosition = 0;
- if (entry.find("no-", nameStartPosition) == nameStartPosition) {
- foundNo = true;
- nameStartPosition += 3;
- }
+ if (entry.find("no-", nameStartPosition) == nameStartPosition) {
+ foundNo = true;
+ nameStartPosition += 3;
+ }
- if (entry.find("error=", nameStartPosition) == nameStartPosition) {
- foundError = true;
- nameStartPosition += 6;
- }
+ if (entry.find("error=", nameStartPosition) == nameStartPosition) {
+ foundError = true;
+ nameStartPosition += 6;
+ }
- name = entry.substr(nameStartPosition);
- if (name.empty()) {
- cmSystemTools::Error("No warning name provided.");
- return false;
- }
+ name = entry.substr(nameStartPosition);
+ if (name.empty()) {
+ cmSystemTools::Error("No warning name provided.");
+ return false;
+ }
- if (!foundNo && !foundError) {
- // -W<name>
- this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN);
- } else if (foundNo && !foundError) {
- // -Wno<name>
- this->DiagLevels[name] = DIAG_IGNORE;
- } else if (!foundNo && foundError) {
- // -Werror=<name>
- this->DiagLevels[name] = DIAG_ERROR;
- } else {
- // -Wno-error=<name>
- // This can downgrade an error to a warning, but should not enable
- // or disable a warning in the first place.
- auto dli = this->DiagLevels.find(name);
- if (dli != this->DiagLevels.end()) {
- dli->second = std::min(dli->second, DIAG_WARN);
- }
- }
- } else if (cmHasLiteralPrefix(arg, "-U")) {
- std::string entryPattern = arg.substr(2);
- if (entryPattern.empty()) {
- ++i;
- if (i < args.size()) {
- entryPattern = args[i];
- } else {
- cmSystemTools::Error("-U must be followed with VAR.");
- return false;
- }
+ if (!foundNo && !foundError) {
+ // -W<name>
+ state->DiagLevels[name] = std::max(state->DiagLevels[name], DIAG_WARN);
+ } else if (foundNo && !foundError) {
+ // -Wno<name>
+ state->DiagLevels[name] = DIAG_IGNORE;
+ } else if (!foundNo && foundError) {
+ // -Werror=<name>
+ state->DiagLevels[name] = DIAG_ERROR;
+ } else {
+ // -Wno-error=<name>
+ // This can downgrade an error to a warning, but should not enable
+ // or disable a warning in the first place.
+ auto dli = state->DiagLevels.find(name);
+ if (dli != state->DiagLevels.end()) {
+ dli->second = std::min(dli->second, DIAG_WARN);
}
- cmsys::RegularExpression regex(
- cmsys::Glob::PatternToRegex(entryPattern, true, true));
- // go through all cache entries and collect the vars which will be
- // removed
- std::vector<std::string> entriesToDelete;
- std::vector<std::string> cacheKeys = this->State->GetCacheEntryKeys();
- for (std::string const& ck : cacheKeys) {
- cmStateEnums::CacheEntryType t = this->State->GetCacheEntryType(ck);
- if (t != cmStateEnums::STATIC) {
- if (regex.find(ck)) {
- entriesToDelete.push_back(ck);
- }
+ }
+ return true;
+ };
+
+ auto UnSetLambda = [](std::string const& entryPattern,
+ cmake* state) -> bool {
+ cmsys::RegularExpression regex(
+ cmsys::Glob::PatternToRegex(entryPattern, true, true));
+ // go through all cache entries and collect the vars which will be
+ // removed
+ std::vector<std::string> entriesToDelete;
+ std::vector<std::string> cacheKeys = state->State->GetCacheEntryKeys();
+ for (std::string const& ck : cacheKeys) {
+ cmStateEnums::CacheEntryType t = state->State->GetCacheEntryType(ck);
+ if (t != cmStateEnums::STATIC) {
+ if (regex.find(ck)) {
+ entriesToDelete.push_back(ck);
}
}
+ }
- // now remove them from the cache
- for (std::string const& currentEntry : entriesToDelete) {
+ // now remove them from the cache
+ for (std::string const& currentEntry : entriesToDelete) {
#ifndef CMAKE_BOOTSTRAP
- this->UnprocessedPresetVariables.erase(currentEntry);
+ state->UnprocessedPresetVariables.erase(currentEntry);
#endif
- this->State->RemoveCacheEntry(currentEntry);
- }
- } else if (cmHasLiteralPrefix(arg, "-C")) {
- std::string path = arg.substr(2);
- if (path.empty()) {
- ++i;
- if (i < args.size()) {
- path = args[i];
- } else {
- cmSystemTools::Error("-C must be followed by a file name.");
- return false;
- }
- }
- cmSystemTools::Stdout("loading initial cache file " + path + "\n");
- // Resolve script path specified on command line relative to $PWD.
- path = cmSystemTools::CollapseFullPath(path);
- this->ReadListFile(args, path);
- } else if (cmHasLiteralPrefix(arg, "-P")) {
- i++;
- if (i >= args.size()) {
- cmSystemTools::Error("-P must be followed by a file name.");
- return false;
- }
- std::string path = args[i];
- if (path.empty()) {
- cmSystemTools::Error("No cmake script provided.");
- return false;
- }
- // Register fake project commands that hint misuse in script mode.
- GetProjectCommandsInScriptMode(this->GetState());
- // Documented behaviour of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
- // set to $PWD for -P mode.
- this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
- this->SetHomeOutputDirectory(
- cmSystemTools::GetCurrentWorkingDirectory());
- this->ReadListFile(args, path);
- seenScriptOption = true;
- } else if (arg == "--" && seenScriptOption) {
+ state->State->RemoveCacheEntry(currentEntry);
+ }
+ return true;
+ };
+
+ auto ScriptLambda = [&](std::string const& path, cmake* state) -> bool {
+ // Register fake project commands that hint misuse in script mode.
+ GetProjectCommandsInScriptMode(state->GetState());
+ // Documented behaviour of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
+ // set to $PWD for -P mode.
+ state->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
+ state->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
+ state->ReadListFile(args, path);
+ seenScriptOption = true;
+ return true;
+ };
+
+ std::vector<CommandArgument> arguments = {
+ CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
+ CommandArgument::Values::One, DefineLambda },
+ CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
+ CommandArgument::Values::One, WarningLambda },
+ CommandArgument{ "-U", "-U must be followed with VAR.",
+ CommandArgument::Values::One, UnSetLambda },
+ CommandArgument{ "-C", "-C must be followed by a file name.",
+ CommandArgument::Values::One,
+ [&](std::string const& value, cmake* state) -> bool {
+ cmSystemTools::Stdout("loading initial cache file " +
+ value + "\n");
+ // Resolve script path specified on command line
+ // relative to $PWD.
+ auto path = cmSystemTools::CollapseFullPath(value);
+ state->ReadListFile(args, path);
+ return true;
+ } },
+ CommandArgument{ "-P", "-P must be followed by a file name.",
+ CommandArgument::Values::One, ScriptLambda },
+ CommandArgument{ "--find-package", CommandArgument::Values::Zero,
+ [&](std::string const&, cmake*) -> bool {
+ findPackageMode = true;
+ return true;
+ } },
+ };
+ for (decltype(args.size()) i = 1; i < args.size(); ++i) {
+ std::string const& arg = args[i];
+
+ if (arg == "--" && seenScriptOption) {
// Stop processing CMake args and avoid possible errors
// when arbitrary args are given to CMake script.
break;
- } else if (cmHasLiteralPrefix(arg, "--find-package")) {
- findPackageMode = true;
+ }
+ for (auto const& m : arguments) {
+ if (m.matches(arg)) {
+ const bool parsedCorrectly = m.parse(arg, i, args, this);
+ if (!parsedCorrectly) {
+ return false;
+ }
+ }
}
}
@@ -739,249 +854,315 @@ void cmake::SetArgs(const std::vector<std::string>& args)
std::string presetName;
bool listPresets = false;
#endif
- for (unsigned int i = 1; i < args.size(); ++i) {
- std::string const& arg = args[i];
- if (cmHasLiteralPrefix(arg, "-H") || cmHasLiteralPrefix(arg, "-S")) {
- std::string path = arg.substr(2);
- if (path.empty()) {
- ++i;
- if (i >= args.size()) {
- cmSystemTools::Error("No source directory specified for -S");
- return;
- }
- path = args[i];
- if (path[0] == '-') {
- cmSystemTools::Error("No source directory specified for -S");
- return;
- }
- }
- path = cmSystemTools::CollapseFullPath(path);
- cmSystemTools::ConvertToUnixSlashes(path);
- this->SetHomeDirectory(path);
- // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
- // NOLINTNEXTLINE(bugprone-branch-clone)
- } else if (cmHasLiteralPrefix(arg, "-O")) {
- // There is no local generate anymore. Ignore -O option.
- } else if (cmHasLiteralPrefix(arg, "-B")) {
- std::string path = arg.substr(2);
- if (path.empty()) {
- ++i;
- if (i >= args.size()) {
- cmSystemTools::Error("No build directory specified for -B");
- return;
- }
- path = args[i];
- if (path[0] == '-') {
- cmSystemTools::Error("No build directory specified for -B");
- return;
- }
- }
+ auto SourceArgLambda = [](std::string const& value, cmake* state) -> bool {
+ std::string path = cmSystemTools::CollapseFullPath(value);
+ cmSystemTools::ConvertToUnixSlashes(path);
+ state->SetHomeDirectory(path);
+ return true;
+ };
- path = cmSystemTools::CollapseFullPath(path);
- cmSystemTools::ConvertToUnixSlashes(path);
- this->SetHomeOutputDirectory(path);
- haveBArg = true;
- } else if ((i < args.size() - 2) &&
- cmHasLiteralPrefix(arg, "--check-build-system")) {
- this->CheckBuildSystemArgument = args[++i];
- this->ClearBuildSystem = (atoi(args[++i].c_str()) > 0);
- } else if ((i < args.size() - 1) &&
- cmHasLiteralPrefix(arg, "--check-stamp-file")) {
- this->CheckStampFile = args[++i];
- } else if ((i < args.size() - 1) &&
- cmHasLiteralPrefix(arg, "--check-stamp-list")) {
- this->CheckStampList = args[++i];
- } else if (arg == "--regenerate-during-build"_s) {
- this->RegenerateDuringBuild = true;
+ auto BuildArgLambda = [&](std::string const& value, cmake* state) -> bool {
+ std::string path = cmSystemTools::CollapseFullPath(value);
+ cmSystemTools::ConvertToUnixSlashes(path);
+ state->SetHomeOutputDirectory(path);
+ haveBArg = true;
+ return true;
+ };
+
+ auto PlatformLambda = [&](std::string const& value, cmake* state) -> bool {
+ if (havePlatform) {
+ cmSystemTools::Error("Multiple -A options not allowed");
+ return false;
}
-#if defined(CMAKE_HAVE_VS_GENERATORS)
- else if ((i < args.size() - 1) &&
- cmHasLiteralPrefix(arg, "--vs-solution-file")) {
- this->VSSolutionFile = args[++i];
+ state->SetGeneratorPlatform(value);
+ havePlatform = true;
+ return true;
+ };
+
+ auto ToolsetLamda = [&](std::string const& value, cmake* state) -> bool {
+ if (haveToolset) {
+ cmSystemTools::Error("Multiple -T options not allowed");
+ return false;
}
+ state->SetGeneratorToolset(value);
+ haveToolset = true;
+ return true;
+ };
+
+ std::vector<CommandArgument> arguments = {
+ CommandArgument{ "-S", "No source directory specified for -S",
+ CommandArgument::Values::One, SourceArgLambda },
+ CommandArgument{ "-H", "No source directory specified for -H",
+ CommandArgument::Values::One, SourceArgLambda },
+ CommandArgument{ "-O", CommandArgument::Values::Zero,
+ IgnoreAndTrueLambda },
+ CommandArgument{ "-B", "No build directory specified for -B",
+ CommandArgument::Values::One, BuildArgLambda },
+ CommandArgument{ "-P", "-P must be followed by a file name.",
+ CommandArgument::Values::One, IgnoreAndTrueLambda },
+ CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
+ CommandArgument::Values::One, IgnoreAndTrueLambda },
+ CommandArgument{ "-C", "-C must be followed by a file name.",
+ CommandArgument::Values::One, IgnoreAndTrueLambda },
+ CommandArgument{ "-U", "-U must be followed with VAR.",
+ CommandArgument::Values::One, IgnoreAndTrueLambda },
+ CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
+ CommandArgument::Values::One, IgnoreAndTrueLambda },
+ CommandArgument{ "-A", "No platform specified for -A",
+ CommandArgument::Values::One, PlatformLambda },
+ CommandArgument{ "-T", "No toolset specified for -T",
+ CommandArgument::Values::One, ToolsetLamda },
+
+ CommandArgument{ "--check-build-system", CommandArgument::Values::Two,
+ [](std::string const& value, cmake* state) -> bool {
+ std::vector<std::string> values = cmExpandedList(value);
+ state->CheckBuildSystemArgument = values[0];
+ state->ClearBuildSystem = (atoi(values[1].c_str()) > 0);
+ return true;
+ } },
+ CommandArgument{ "--check-stamp-file", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ state->CheckStampFile = value;
+ return true;
+ } },
+ CommandArgument{ "--check-stamp-list", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ state->CheckStampList = value;
+ return true;
+ } },
+ CommandArgument{ "--regenerate-during-build",
+ CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ state->RegenerateDuringBuild = true;
+ return true;
+ } },
+
+ CommandArgument{ "--find-package", CommandArgument::Values::Zero,
+ IgnoreAndTrueLambda },
+
+ CommandArgument{ "--graphviz", "No file specified for --graphviz",
+ CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ std::string path =
+ cmSystemTools::CollapseFullPath(value);
+ cmSystemTools::ConvertToUnixSlashes(path);
+ state->GraphVizFile = path;
+ return true;
+ } },
+
+ CommandArgument{ "--debug-trycompile", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "debug trycompile on\n";
+ state->DebugTryCompileOn();
+ return true;
+ } },
+ CommandArgument{ "--debug-output", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Running with debug output on.\n";
+ state->SetDebugOutputOn(true);
+ return true;
+ } },
+
+ CommandArgument{ "--log-level", "Invalid level specified for --log-level",
+ CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ const auto logLevel = StringToLogLevel(value);
+ if (logLevel == LogLevel::LOG_UNDEFINED) {
+ cmSystemTools::Error(
+ "Invalid level specified for --log-level");
+ return false;
+ }
+ state->SetLogLevel(logLevel);
+ state->LogLevelWasSetViaCLI = true;
+ return true;
+ } },
+ // This is supported for backward compatibility. This option only
+ // appeared in the 3.15.x release series and was renamed to
+ // --log-level in 3.16.0
+ CommandArgument{ "--loglevel", "Invalid level specified for --loglevel",
+ CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ const auto logLevel = StringToLogLevel(value);
+ if (logLevel == LogLevel::LOG_UNDEFINED) {
+ cmSystemTools::Error(
+ "Invalid level specified for --loglevel");
+ return false;
+ }
+ state->SetLogLevel(logLevel);
+ state->LogLevelWasSetViaCLI = true;
+ return true;
+ } },
+
+ CommandArgument{ "--log-context", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ state->SetShowLogContext(true);
+ return true;
+ } },
+ CommandArgument{
+ "--debug-find", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Running with debug output on for the `find` commands.\n";
+ state->SetDebugFindOutputOn(true);
+ return true;
+ } },
+ CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Running with expanded trace output on.\n";
+ state->SetTrace(true);
+ state->SetTraceExpand(true);
+ return true;
+ } },
+ CommandArgument{ "--trace-format", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ state->SetTrace(true);
+ const auto traceFormat = StringToTraceFormat(value);
+ if (traceFormat == TraceFormat::TRACE_UNDEFINED) {
+ cmSystemTools::Error(
+ "Invalid format specified for --trace-format. "
+ "Valid formats are human, json-v1.");
+ return false;
+ }
+ state->SetTraceFormat(traceFormat);
+ return true;
+ } },
+ CommandArgument{ "--trace-source", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ std::string file(value);
+ cmSystemTools::ConvertToUnixSlashes(file);
+ state->AddTraceSource(file);
+ state->SetTrace(true);
+ return true;
+ } },
+ CommandArgument{ "--trace-redirect", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ std::string file(value);
+ cmSystemTools::ConvertToUnixSlashes(file);
+ state->SetTraceFile(file);
+ state->SetTrace(true);
+ return true;
+ } },
+ CommandArgument{ "--trace", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Running with trace output on.\n";
+ state->SetTrace(true);
+ state->SetTraceExpand(false);
+ return true;
+ } },
+ CommandArgument{ "--warn-uninitialized", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Warn about uninitialized values.\n";
+ state->SetWarnUninitialized(true);
+ return true;
+ } },
+ CommandArgument{ "--warn-unused-vars", CommandArgument::Values::Zero,
+ IgnoreAndTrueLambda }, // Option was removed.
+ CommandArgument{ "--no-warn-unused-cli", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout
+ << "Not searching for unused variables given on the "
+ << "command line.\n";
+ state->SetWarnUnusedCli(false);
+ return true;
+ } },
+ CommandArgument{
+ "--check-system-vars", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Also check system files when warning about unused and "
+ << "uninitialized variables.\n";
+ state->SetCheckSystemVars(true);
+ return true;
+ } }
+ };
+
+#if defined(CMAKE_HAVE_VS_GENERATORS)
+ arguments.emplace_back("--vs-solution-file", CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ state->VSSolutionFile = value;
+ return true;
+ });
#endif
- else if (cmHasLiteralPrefix(arg, "-D") || cmHasLiteralPrefix(arg, "-U") ||
- cmHasLiteralPrefix(arg, "-C")) {
- // skip for now
- // in case '-[DUC] argval' var' is given, also skip the next
- // in case '-[DUC]argval' is given, don't skip the next
- if (arg.size() == 2) {
- ++i;
- }
- // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
- // NOLINTNEXTLINE(bugprone-branch-clone)
- } else if (cmHasLiteralPrefix(arg, "-P")) {
- // skip for now
- i++;
- } else if (cmHasLiteralPrefix(arg, "--find-package")) {
- // skip for now
- i++;
- } else if (cmHasLiteralPrefix(arg, "-W")) {
- // skip for now
- } else if (cmHasLiteralPrefix(arg, "--graphviz=")) {
- std::string path = arg.substr(strlen("--graphviz="));
- path = cmSystemTools::CollapseFullPath(path);
- cmSystemTools::ConvertToUnixSlashes(path);
- this->GraphVizFile = path;
- if (this->GraphVizFile.empty()) {
- cmSystemTools::Error("No file specified for --graphviz");
- return;
- }
- } else if (cmHasLiteralPrefix(arg, "--debug-trycompile")) {
- std::cout << "debug trycompile on\n";
- this->DebugTryCompileOn();
- } else if (cmHasLiteralPrefix(arg, "--debug-output")) {
- std::cout << "Running with debug output on.\n";
- this->SetDebugOutputOn(true);
- } else if (cmHasLiteralPrefix(arg, "--log-level=")) {
- const auto logLevel =
- StringToLogLevel(arg.substr(sizeof("--log-level=") - 1));
- if (logLevel == LogLevel::LOG_UNDEFINED) {
- cmSystemTools::Error("Invalid level specified for --log-level");
- return;
- }
- this->SetLogLevel(logLevel);
- this->LogLevelWasSetViaCLI = true;
- } else if (cmHasLiteralPrefix(arg, "--loglevel=")) {
- // This is supported for backward compatibility. This option only
- // appeared in the 3.15.x release series and was renamed to
- // --log-level in 3.16.0
- const auto logLevel =
- StringToLogLevel(arg.substr(sizeof("--loglevel=") - 1));
- if (logLevel == LogLevel::LOG_UNDEFINED) {
- cmSystemTools::Error("Invalid level specified for --loglevel");
- return;
- }
- this->SetLogLevel(logLevel);
- this->LogLevelWasSetViaCLI = true;
- } else if (arg == "--log-context"_s) {
- this->SetShowLogContext(true);
- } else if (cmHasLiteralPrefix(arg, "--debug-find")) {
- std::cout << "Running with debug output on for the `find` commands.\n";
- this->SetDebugFindOutputOn(true);
- } else if (cmHasLiteralPrefix(arg, "--trace-expand")) {
- std::cout << "Running with expanded trace output on.\n";
- this->SetTrace(true);
- this->SetTraceExpand(true);
- } else if (cmHasLiteralPrefix(arg, "--trace-format=")) {
- this->SetTrace(true);
- const auto traceFormat =
- StringToTraceFormat(arg.substr(strlen("--trace-format=")));
- if (traceFormat == TraceFormat::TRACE_UNDEFINED) {
- cmSystemTools::Error("Invalid format specified for --trace-format. "
- "Valid formats are human, json-v1.");
- return;
- }
- this->SetTraceFormat(traceFormat);
- } else if (cmHasLiteralPrefix(arg, "--trace-source=")) {
- std::string file = arg.substr(strlen("--trace-source="));
- cmSystemTools::ConvertToUnixSlashes(file);
- this->AddTraceSource(file);
- this->SetTrace(true);
- } else if (cmHasLiteralPrefix(arg, "--trace-redirect=")) {
- std::string file = arg.substr(strlen("--trace-redirect="));
- cmSystemTools::ConvertToUnixSlashes(file);
- this->SetTraceFile(file);
- this->SetTrace(true);
- } else if (cmHasLiteralPrefix(arg, "--trace")) {
- std::cout << "Running with trace output on.\n";
- this->SetTrace(true);
- this->SetTraceExpand(false);
- } else if (cmHasLiteralPrefix(arg, "--warn-uninitialized")) {
- std::cout << "Warn about uninitialized values.\n";
- this->SetWarnUninitialized(true);
- } else if (cmHasLiteralPrefix(arg, "--warn-unused-vars")) {
- // Option was removed.
- } else if (cmHasLiteralPrefix(arg, "--no-warn-unused-cli")) {
- std::cout << "Not searching for unused variables given on the "
- << "command line.\n";
- this->SetWarnUnusedCli(false);
- } else if (cmHasLiteralPrefix(arg, "--check-system-vars")) {
- std::cout << "Also check system files when warning about unused and "
- << "uninitialized variables.\n";
- this->SetCheckSystemVars(true);
- } else if (cmHasLiteralPrefix(arg, "-A")) {
- std::string value = arg.substr(2);
- if (value.empty()) {
- ++i;
- if (i >= args.size()) {
- cmSystemTools::Error("No platform specified for -A");
- return;
- }
- value = args[i];
- }
- if (havePlatform) {
- cmSystemTools::Error("Multiple -A options not allowed");
- return;
- }
- this->SetGeneratorPlatform(value);
- havePlatform = true;
- } else if (cmHasLiteralPrefix(arg, "-T")) {
- std::string value = arg.substr(2);
- if (value.empty()) {
- ++i;
- if (i >= args.size()) {
- cmSystemTools::Error("No toolset specified for -T");
- return;
- }
- value = args[i];
- }
- if (haveToolset) {
- cmSystemTools::Error("Multiple -T options not allowed");
- return;
- }
- this->SetGeneratorToolset(value);
- haveToolset = true;
- } else if (cmHasLiteralPrefix(arg, "-G")) {
- std::string value = arg.substr(2);
- if (value.empty()) {
- ++i;
- if (i >= args.size()) {
- cmSystemTools::Error("No generator specified for -G");
- this->PrintGeneratorList();
- return;
- }
- value = args[i];
- }
- if (!this->CreateAndSetGlobalGenerator(value, true)) {
- return;
- }
+
#if !defined(CMAKE_BOOTSTRAP)
- } else if (cmHasLiteralPrefix(arg, "--profiling-format=")) {
- profilingFormat = arg.substr(strlen("--profiling-format="));
- if (profilingFormat.empty()) {
- cmSystemTools::Error("No format specified for --profiling-format");
- }
- } else if (cmHasLiteralPrefix(arg, "--profiling-output=")) {
- profilingOutput = arg.substr(strlen("--profiling-output="));
- profilingOutput = cmSystemTools::CollapseFullPath(profilingOutput);
+ arguments.emplace_back("--profiling-format",
+ "No format specified for --profiling-format",
+ CommandArgument::Values::One,
+ [&](std::string const& value, cmake*) -> bool {
+ profilingFormat = value;
+ return true;
+ });
+ arguments.emplace_back(
+ "--profiling-output", "No path specified for --profiling-output",
+ CommandArgument::Values::One,
+ [&](std::string const& value, cmake*) -> bool {
+ profilingOutput = cmSystemTools::CollapseFullPath(value);
cmSystemTools::ConvertToUnixSlashes(profilingOutput);
- if (profilingOutput.empty()) {
- cmSystemTools::Error("No path specified for --profiling-output");
+ return true;
+ });
+ arguments.emplace_back("--preset", "No preset specified for --preset",
+ CommandArgument::Values::One,
+ [&](std::string const& value, cmake*) -> bool {
+ presetName = value;
+ return true;
+ });
+ arguments.emplace_back("--list-presets", CommandArgument::Values::Zero,
+ [&](std::string const&, cmake*) -> bool {
+ listPresets = true;
+ return true;
+ });
+
+#endif
+
+ bool badGeneratorName = false;
+ CommandArgument generatorCommand(
+ "-G", "No generator specified for -G", CommandArgument::Values::One,
+ [&](std::string const& value, cmake* state) -> bool {
+ bool valid = state->CreateAndSetGlobalGenerator(value, true);
+ badGeneratorName = !valid;
+ return valid;
+ });
+
+ for (decltype(args.size()) i = 1; i < args.size(); ++i) {
+ // iterate each argument
+ std::string const& arg = args[i];
+
+ // Generator flag has special handling for when to print help
+ // so it becomes the exception
+ if (generatorCommand.matches(arg)) {
+ bool parsed = generatorCommand.parse(arg, i, args, this);
+ if (!parsed && !badGeneratorName) {
+ this->PrintGeneratorList();
+ return;
}
- } else if (cmHasLiteralPrefix(arg, "--preset=")) {
- presetName = arg.substr(strlen("--preset="));
- if (presetName.empty()) {
- cmSystemTools::Error("No preset specified for --preset");
+ continue;
+ }
+
+ bool matched = false;
+ bool parsedCorrectly = true; // needs to be true so we can ignore
+ // arguments so as -E
+ for (auto const& m : arguments) {
+ if (m.matches(arg)) {
+ matched = true;
+ parsedCorrectly = m.parse(arg, i, args, this);
+ break;
}
- } else if (cmHasLiteralPrefix(arg, "--list-presets")) {
- listPresets = true;
-#endif
}
- // no option assume it is the path to the source or an existing build
- else {
+ if (!parsedCorrectly) {
+ cmSystemTools::Error("Run 'cmake --help' for all supported options.");
+ exit(1);
+ } else if (!matched) {
this->SetDirectoriesFromFile(arg);
}
- // Empty instance, platform and toolset if only a generator is specified
- if (this->GlobalGenerator) {
- this->GeneratorInstance = "";
- if (!this->GeneratorPlatformSet) {
- this->GeneratorPlatform = "";
- }
- if (!this->GeneratorToolsetSet) {
- this->GeneratorToolset = "";
- }
+ }
+
+ // Empty instance, platform and toolset if only a generator is specified
+ if (this->GlobalGenerator) {
+ this->GeneratorInstance = "";
+ if (!this->GeneratorPlatformSet) {
+ this->GeneratorPlatform = "";
+ }
+ if (!this->GeneratorToolsetSet) {
+ this->GeneratorToolset = "";
}
}
@@ -2118,7 +2299,7 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
#endif
// Add any cache args
if (!this->SetCacheArgs(args)) {
- cmSystemTools::Error("Problem processing arguments. Aborting.\n");
+ cmSystemTools::Error("Run 'cmake --help' for all supported options.");
return -1;
}
#ifndef CMAKE_BOOTSTRAP