mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-11 00:11:07 -06:00
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
This commit is contained in:
committed by
Brad King
parent
d800c26ce9
commit
98290782b6
939
Source/cmake.cxx
939
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) {
|
||||
|
||||
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
|
||||
state->UnprocessedPresetVariables.erase(var);
|
||||
#endif
|
||||
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;
|
||||
};
|
||||
|
||||
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("error=", nameStartPosition) == nameStartPosition) {
|
||||
foundError = true;
|
||||
nameStartPosition += 6;
|
||||
}
|
||||
|
||||
name = entry.substr(nameStartPosition);
|
||||
if (name.empty()) {
|
||||
cmSystemTools::Error("No warning name provided.");
|
||||
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);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
state->UnprocessedPresetVariables.erase(currentEntry);
|
||||
#endif
|
||||
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 (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)) {
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
this->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;
|
||||
}
|
||||
}
|
||||
|
||||
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("error=", nameStartPosition) == nameStartPosition) {
|
||||
foundError = true;
|
||||
nameStartPosition += 6;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now remove them from the cache
|
||||
for (std::string const& currentEntry : entriesToDelete) {
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
this->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) {
|
||||
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;
|
||||
}
|
||||
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)
|
||||
else if ((i < args.size() - 1) &&
|
||||
cmHasLiteralPrefix(arg, "--vs-solution-file")) {
|
||||
this->VSSolutionFile = args[++i];
|
||||
}
|
||||
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");
|
||||
}
|
||||
} else if (cmHasLiteralPrefix(arg, "--preset=")) {
|
||||
presetName = arg.substr(strlen("--preset="));
|
||||
if (presetName.empty()) {
|
||||
cmSystemTools::Error("No preset specified for --preset");
|
||||
}
|
||||
} else if (cmHasLiteralPrefix(arg, "--list-presets")) {
|
||||
listPresets = true;
|
||||
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;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// no option assume it is the path to the source or an existing build
|
||||
else {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
^CMake Error: No preset specified for --preset$
|
||||
^CMake Error: No preset specified for --preset
|
||||
CMake Error: Run 'cmake --help' for all supported options.$
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
^CMake Error: -C must be followed by a file name.
|
||||
CMake Error: Problem processing arguments. Aborting.$
|
||||
CMake Error: Run 'cmake --help' for all supported options.$
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
^CMake Error: -D must be followed with VAR=VALUE.
|
||||
CMake Error: Problem processing arguments. Aborting.$
|
||||
CMake Error: Run 'cmake --help' for all supported options.$
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
^CMake Error: -U must be followed with VAR.
|
||||
CMake Error: Problem processing arguments. Aborting.$
|
||||
CMake Error: Run 'cmake --help' for all supported options.$
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CMake Error: -W must be followed with \[no-\]<name>.
|
||||
CMake Error: Problem processing arguments. Aborting.
|
||||
CMake Error: Run 'cmake --help' for all supported options.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CMake Error: No warning name provided.
|
||||
CMake Error: Problem processing arguments. Aborting.
|
||||
CMake Error: Run 'cmake --help' for all supported options.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CMake Error: No warning name provided.
|
||||
CMake Error: Problem processing arguments. Aborting.
|
||||
CMake Error: Run 'cmake --help' for all supported options.
|
||||
|
||||
Reference in New Issue
Block a user