cmake: Use shared parsing code for all cmake argv parsing

This commit is contained in:
Robert Maynard 2020-12-28 16:41:18 -05:00 committed by Brad King
parent 25a1cdef95
commit 0fb78576b0
3 changed files with 235 additions and 164 deletions

View File

@ -0,0 +1,133 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
template <typename FunctionSignature>
struct cmCommandLineArgument
{
enum class Values
{
Zero,
One,
Two,
};
std::string InvalidSyntaxMessage;
std::string InvalidValueMessage;
std::string Name;
Values Type;
std::function<FunctionSignature> StoreCall;
template <typename FunctionType>
cmCommandLineArgument(std::string n, 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>
cmCommandLineArgument(std::string n, std::string failedMsg, 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 (this->Type == Values::Zero) ? (input == this->Name)
: cmHasPrefix(input, this->Name);
}
template <typename T, typename... CallState>
bool parse(std::string const& input, T& index,
std::vector<std::string> const& allArgs,
CallState&&... state) const
{
enum class ParseMode
{
Valid,
Invalid,
SyntaxError,
ValueError
};
ParseMode parseState = ParseMode::Valid;
if (this->Type == Values::Zero) {
if (input.size() == this->Name.size()) {
parseState =
this->StoreCall(std::string{}, std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
} else {
parseState = ParseMode::SyntaxError;
}
} else if (this->Type == 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], std::forward<CallState>(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),
std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
if (parseState == ParseMode::Valid) {
parseState = this->StoreCall(std::string(possible_value),
std::forward<CallState>(state)...)
? ParseMode::Valid
: ParseMode::Invalid;
}
}
} else if (this->Type == 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]),
std::forward<CallState>(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);
}
};

View File

@ -29,6 +29,7 @@
#include "cm_sys_stat.h"
#include "cmCMakePresetsFile.h"
#include "cmCommandLineArgument.h"
#include "cmCommands.h"
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
@ -132,131 +133,13 @@ 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;
};
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
} // namespace
static bool cmakeCheckStampFile(const std::string& stampName);

View File

@ -19,6 +19,7 @@
#include <cm3p/uv.h>
#include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h"
#include "cmDocumentationEntry.h" // IWYU pragma: keep
#include "cmGlobalGenerator.h"
@ -213,61 +214,115 @@ int do_cmake(int ac, char const* const* av)
}
#endif
bool wizard_mode = false;
bool sysinfo = false;
bool list_cached = false;
bool list_all_cached = false;
bool list_help = false;
bool view_only = false;
cmake::WorkingMode workingMode = cmake::NORMAL_MODE;
std::vector<std::string> args;
for (int i = 0; i < ac; ++i) {
if (strcmp(av[i], "-i") == 0) {
/* clang-format off */
std::cerr <<
"The \"cmake -i\" wizard mode is no longer supported.\n"
"Use the -D option to set cache values on the command line.\n"
"Use cmake-gui or ccmake for an interactive dialog.\n";
/* clang-format on */
return 1;
}
if (strcmp(av[i], "--system-information") == 0) {
sysinfo = true;
} else if (strcmp(av[i], "-N") == 0) {
view_only = true;
} else if (strcmp(av[i], "-L") == 0) {
list_cached = true;
} else if (strcmp(av[i], "-LA") == 0) {
list_all_cached = true;
} else if (strcmp(av[i], "-LH") == 0) {
list_cached = true;
list_help = true;
} else if (strcmp(av[i], "-LAH") == 0) {
list_all_cached = true;
list_help = true;
} else if (cmHasLiteralPrefix(av[i], "-P")) {
if (i == ac - 1) {
cmSystemTools::Error("No script specified for argument -P");
return 1;
std::vector<std::string> parsedArgs;
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value)>;
std::vector<CommandArgument> arguments = {
CommandArgument{
"-i", CommandArgument::Values::Zero,
[&wizard_mode](std::string const&) -> bool {
/* clang-format off */
std::cerr <<
"The \"cmake -i\" wizard mode is no longer supported.\n"
"Use the -D option to set cache values on the command line.\n"
"Use cmake-gui or ccmake for an interactive dialog.\n";
/* clang-format on */
wizard_mode = true;
return true;
} },
CommandArgument{ "--system-information", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
sysinfo = true;
return true;
} },
CommandArgument{ "-N", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
view_only = true;
return true;
} },
CommandArgument{ "-LAH", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
list_all_cached = true;
list_help = true;
return true;
} },
CommandArgument{ "-LA", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
list_all_cached = true;
return true;
} },
CommandArgument{ "-LH", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
list_cached = true;
list_help = true;
return true;
} },
CommandArgument{ "-L", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
list_cached = true;
return true;
} },
CommandArgument{ "-P", "No script specified for argument -P",
CommandArgument::Values::One,
[&](std::string const& value) -> bool {
workingMode = cmake::SCRIPT_MODE;
parsedArgs.emplace_back("-P");
parsedArgs.push_back(std::move(value));
return true;
} },
CommandArgument{ "--find-package", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
workingMode = cmake::FIND_PACKAGE_MODE;
parsedArgs.emplace_back("--find-package");
return true;
} },
CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
[&](std::string const&) -> bool {
workingMode = cmake::HELP_MODE;
parsedArgs.emplace_back("--list-presets");
return true;
} },
};
std::vector<std::string> inputArgs;
inputArgs.reserve(ac);
cm::append(inputArgs, av, av + ac);
for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) {
std::string const& arg = inputArgs[i];
bool matched = false;
for (auto const& m : arguments) {
if (m.matches(arg)) {
matched = true;
if (m.parse(arg, i, inputArgs)) {
break;
} else {
return 1;
}
}
workingMode = cmake::SCRIPT_MODE;
args.emplace_back(av[i]);
i++;
args.emplace_back(av[i]);
} else if (cmHasLiteralPrefix(av[i], "--find-package")) {
workingMode = cmake::FIND_PACKAGE_MODE;
args.emplace_back(av[i]);
} else if (strcmp(av[i], "--list-presets") == 0) {
workingMode = cmake::HELP_MODE;
args.emplace_back(av[i]);
} else {
args.emplace_back(av[i]);
}
if (!matched) {
parsedArgs.emplace_back(av[i]);
}
}
if (wizard_mode) {
return 1;
}
if (sysinfo) {
cmake cm(cmake::RoleProject, cmState::Project);
cm.SetHomeDirectory("");
cm.SetHomeOutputDirectory("");
int ret = cm.GetSystemInformation(args);
int ret = cm.GetSystemInformation(parsedArgs);
return ret;
}
cmake::Role const role =
@ -297,7 +352,7 @@ int do_cmake(int ac, char const* const* av)
});
cm.SetWorkingMode(workingMode);
int res = cm.Run(args, view_only);
int res = cm.Run(parsedArgs, view_only);
if (list_cached || list_all_cached) {
std::cout << "-- Cache values" << std::endl;
std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys();