Always accept = or space as delimiters when parsing common command line parameters.
Resolves
MODIFY
src/vcpkg-test/arguments.cpp
src/vcpkg-test/arguments.cpp
@@ -107,3 +107,21 @@ TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arg
REQUIRE(v.command_arguments.size() == 0);
}
}
+
+TEST_CASE ("vcpkg_root parse with arg separator", "[arguments]")
+{
+ std::vector<std::string> t = {"--vcpkg-root", "C:\\vcpkg"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+ auto& vcpkg_root_dir = v.vcpkg_root_dir;
+ REQUIRE(vcpkg_root_dir);
+ REQUIRE(*vcpkg_root_dir == "C:\\vcpkg");
+}
+
+TEST_CASE ("vcpkg_root parse with equal separator", "[arguments]")
+{
+ std::vector<std::string> t = {"--vcpkg-root=C:\\vcpkg"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+ auto& vcpkg_root_dir = v.vcpkg_root_dir;
+ REQUIRE(vcpkg_root_dir);
+ REQUIRE(*vcpkg_root_dir == "C:\\vcpkg");
+}

MODIFY
src/vcpkg/vcpkgcmdarguments.cpp
src/vcpkg/vcpkgcmdarguments.cpp
@@ -58,30 +58,6 @@ namespace vcpkg
}
}
- static void parse_value(const std::string* arg_begin,
- const std::string* arg_end,
- StringView option_name,
- std::unique_ptr<std::string>& option_field)
- {
- if (arg_begin == arg_end)
- {
- System::print2(System::Color::error, "Error: expected value after --", option_name, '\n');
- Metrics::g_metrics.lock()->track_property("error", "error option name");
- print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- if (option_field != nullptr)
- {
- System::print2(System::Color::error, "Error: --", option_name, " specified multiple times\n");
- Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times");
- print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- option_field = std::make_unique<std::string>(*arg_begin);
- }
-
static void parse_cojoined_value(StringView new_value,
StringView option_name,
std::unique_ptr<std::string>& option_field)
@@ -176,27 +152,46 @@ namespace vcpkg
return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size());
}
- // returns true if this does parse this argument as this option
+ enum class TryParseArgumentResult
+ {
+ NotFound,
+ Found,
+ FoundAndConsumedLookahead
+ };
+
template<class T, class F>
- static bool try_parse_argument_as_option(StringView option, StringView arg, T& place, F parser)
+ static TryParseArgumentResult try_parse_argument_as_option(
+ StringView arg, Optional<StringView> lookahead, StringView option, T& place, F parser)
{
- if (arg.size() <= option.size() + 1)
+ if (Strings::starts_with(arg, "x-") && !Strings::starts_with(option, "x-"))
{
- // it is impossible for this argument to be this option
- return false;
+ arg = arg.substr(2);
}
- if (Strings::starts_with(arg, "x-") && !Strings::starts_with(option, "x-"))
+ if (Strings::starts_with(arg, option))
{
- arg = arg.substr(2);
+ if (arg.size() == option.size())
+ {
+ if (auto next = lookahead.get())
+ {
+ parser(*next, option, place);
+ return TryParseArgumentResult::FoundAndConsumedLookahead;
}
- if (Strings::starts_with(arg, option) && arg.byte_at_index(option.size()) == '=')
+
+ System::print2(System::Color::error, "Error: expected value after ", option, '\n');
+ Metrics::g_metrics.lock()->track_property("error", "error option name");
+ print_usage();
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ if (arg.byte_at_index(option.size()) == '=')
{
parser(arg.substr(option.size() + 1), option, place);
- return true;
+ return TryParseArgumentResult::Found;
+ }
}
- return false;
+ return TryParseArgumentResult::NotFound;
}
static bool equals_modulo_experimental(StringView arg, StringView option)
@@ -230,13 +225,13 @@ namespace vcpkg
return false;
}
- VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin,
- const std::string* arg_end)
+ VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_first,
+ const std::string* arg_last)
{
VcpkgCmdArguments args;
std::vector<std::string> feature_flags;
- for (auto it = arg_begin; it != arg_end; ++it)
+ for (auto it = arg_first; it != arg_last; ++it)
{
std::string basic_arg = *it;
@@ -269,23 +264,10 @@ namespace vcpkg
Strings::ascii_to_lowercase(std::begin(basic_arg), first_eq);
// basic_arg[0] == '-' && basic_arg[1] == '-'
StringView arg = StringView(basic_arg).substr(2);
-
- // command switch
- if (arg == VCPKG_ROOT_DIR_ARG)
- {
- ++it;
- parse_value(it, arg_end, VCPKG_ROOT_DIR_ARG, args.vcpkg_root_dir);
- continue;
- }
- if (arg == TRIPLET_ARG)
- {
- ++it;
- parse_value(it, arg_end, TRIPLET_ARG, args.triplet);
- continue;
- }
-
constexpr static std::pair<StringView, std::unique_ptr<std::string> VcpkgCmdArguments::*>
cojoined_values[] = {
+ {VCPKG_ROOT_DIR_ARG, &VcpkgCmdArguments::vcpkg_root_dir},
+ {TRIPLET_ARG, &VcpkgCmdArguments::triplet},
{MANIFEST_ROOT_DIR_ARG, &VcpkgCmdArguments::manifest_root_dir},
{BUILDTREES_ROOT_DIR_ARG, &VcpkgCmdArguments::buildtrees_root_dir},
{DOWNLOADS_ROOT_DIR_ARG, &VcpkgCmdArguments::downloads_root_dir},
@@ -312,30 +294,45 @@ namespace vcpkg
{JSON_SWITCH, &VcpkgCmdArguments::json},
};
+ Optional<StringView> lookahead;
+ if (it + 1 != arg_last)
+ {
+ lookahead = it[1];
+ }
+
bool found = false;
for (const auto& pr : cojoined_values)
{
- if (try_parse_argument_as_option(pr.first, arg, args.*pr.second, parse_cojoined_value))
+ switch (try_parse_argument_as_option(arg, lookahead, pr.first, args.*pr.second, parse_cojoined_value))
{
- found = true;
- break;
+ case TryParseArgumentResult::FoundAndConsumedLookahead: ++it; [[fallthrough]];
+ case TryParseArgumentResult::Found: found = true; break;
+ case TryParseArgumentResult::NotFound: break;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
if (found) continue;
for (const auto& pr : cojoined_multivalues)
{
- if (try_parse_argument_as_option(pr.first, arg, args.*pr.second, parse_cojoined_multivalue))
+ switch (
+ try_parse_argument_as_option(arg, lookahead, pr.first, args.*pr.second, parse_cojoined_multivalue))
{
- found = true;
- break;
+ case TryParseArgumentResult::FoundAndConsumedLookahead: ++it; [[fallthrough]];
+ case TryParseArgumentResult::Found: found = true; break;
+ case TryParseArgumentResult::NotFound: break;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
if (found) continue;
- if (try_parse_argument_as_option(FEATURE_FLAGS_ARG, arg, feature_flags, parse_cojoined_list_multivalue))
+ switch (try_parse_argument_as_option(
+ arg, lookahead, FEATURE_FLAGS_ARG, feature_flags, parse_cojoined_list_multivalue))
{
- continue;
+ case TryParseArgumentResult::FoundAndConsumedLookahead: ++it; [[fallthrough]];
+ case TryParseArgumentResult::Found: found = true; break;
+ case TryParseArgumentResult::NotFound: break;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
}
for (const auto& pr : switches)
@@ -515,7 +512,7 @@ namespace vcpkg
}
}
- if (!switches_copy.empty())
+ if (!switches_copy.empty() || !options_copy.empty())
{
System::printf(System::Color::error, "Unknown option(s) for command '%s':\n", this->command);
for (auto&& switch_ : switches_copy)
@@ -609,7 +606,7 @@ namespace vcpkg
return Strings::concat("--", arg, joiner, value);
};
- table.format(opt(TRIPLET_ARG, " ", "<t>"), "Specify the target architecture triplet. See 'vcpkg help triplet'");
+ table.format(opt(TRIPLET_ARG, "=", "<t>"), "Specify the target architecture triplet. See 'vcpkg help triplet'");
table.format("", "(default: " + format_environment_variable("VCPKG_DEFAULT_TRIPLET") + ')');
table.format(opt(OVERLAY_PORTS_ARG, "=", "<path>"), "Specify directories to be used when searching for ports");
table.format("", "(also: " + format_environment_variable("VCPKG_OVERLAY_PORTS") + ')');
@@ -619,7 +616,7 @@ namespace vcpkg
"Add sources for binary caching. See 'vcpkg help binarycaching'");
table.format(opt(DOWNLOADS_ROOT_DIR_ARG, "=", "<path>"), "Specify the downloads root directory");
table.format("", "(default: " + format_environment_variable("VCPKG_DOWNLOADS") + ')');
- table.format(opt(VCPKG_ROOT_DIR_ARG, " ", "<path>"), "Specify the vcpkg root directory");
+ table.format(opt(VCPKG_ROOT_DIR_ARG, "=", "<path>"), "Specify the vcpkg root directory");
table.format("", "(default: " + format_environment_variable("VCPKG_ROOT") + ')');
table.format(opt(BUILDTREES_ROOT_DIR_ARG, "=", "<path>"),
"(Experimental) Specify the buildtrees root directory");

Always accept = or space as delimiters when parsing common command line parameters. (#13857)

Resolves #11456.