#include "pch.h" #include "VcpkgCmdArguments.h" #include "metrics.h" #include "vcpkg_Commands.h" #include "vcpkg_GlobalState.h" #include "vcpkg_System.h" namespace vcpkg { static void parse_value(const std::string* arg_begin, const std::string* arg_end, const std::string& option_name, std::unique_ptr& option_field) { if (arg_begin == arg_end) { System::println(System::Color::error, "Error: expected value after %s", option_name); Metrics::g_metrics.lock()->track_property("error", "error option name"); Commands::Help::print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } if (option_field != nullptr) { System::println(System::Color::error, "Error: %s specified multiple times", option_name); Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times"); Commands::Help::print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } option_field = std::make_unique(*arg_begin); } static void parse_switch(bool new_setting, const std::string& option_name, Optional& option_field) { if (option_field && option_field != new_setting) { System::println(System::Color::error, "Error: conflicting values specified for --%s", option_name); Metrics::g_metrics.lock()->track_property("error", "error conflicting switches"); Commands::Help::print_usage(); Checks::exit_fail(VCPKG_LINE_INFO); } option_field = new_setting; } VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const wchar_t* const* const argv) { std::vector v; for (int i = 1; i < argc; ++i) { v.push_back(Strings::to_utf8(argv[i])); } return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size()); } VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin, const std::string* arg_end) { VcpkgCmdArguments args; for (; arg_begin != arg_end; ++arg_begin) { std::string arg = *arg_begin; if (arg.empty()) { continue; } if (arg[0] == '-' && arg[1] != '-') { Metrics::g_metrics.lock()->track_property("error", "error short options are not supported"); Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg); } if (arg[0] == '-' && arg[1] == '-') { // make argument case insensitive auto& f = std::use_facet>(std::locale()); f.tolower(&arg[0], &arg[0] + arg.size()); // command switch if (arg == "--vcpkg-root") { ++arg_begin; parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir); continue; } if (arg == "--triplet") { ++arg_begin; parse_value(arg_begin, arg_end, "--triplet", args.triplet); continue; } if (arg == "--debug") { parse_switch(true, "debug", args.debug); continue; } if (arg == "--sendmetrics") { parse_switch(true, "sendmetrics", args.sendmetrics); continue; } if (arg == "--printmetrics") { parse_switch(true, "printmetrics", args.printmetrics); continue; } if (arg == "--no-sendmetrics") { parse_switch(false, "sendmetrics", args.sendmetrics); continue; } if (arg == "--no-printmetrics") { parse_switch(false, "printmetrics", args.printmetrics); continue; } if (arg == "--featurepackages") { GlobalState::feature_packages = true; continue; } const auto eq_pos = arg.find('='); if (eq_pos != std::string::npos) { args.optional_command_arguments.emplace(arg.substr(0, eq_pos), arg.substr(eq_pos + 1)); } else { args.optional_command_arguments.emplace(arg, nullopt); } continue; } if (args.command.empty()) { args.command = arg; } else { args.command_arguments.push_back(arg); } } return args; } ParsedArguments VcpkgCmdArguments::check_and_get_optional_command_arguments( const std::vector& valid_switches, const std::vector& valid_settings) const { bool failed = false; ParsedArguments output; auto options_copy = this->optional_command_arguments; for (const std::string& option : valid_switches) { const auto it = options_copy.find(option); if (it != options_copy.end()) { if (it->second.has_value()) { // Having a string value indicates it was passed like '--a=xyz' System::println(System::Color::error, "The option '%s' does not accept an argument.", option); failed = true; } else { output.switches.insert(option); options_copy.erase(it); } } } for (const std::string& option : valid_settings) { const auto it = options_copy.find(option); if (it != options_copy.end()) { if (!it->second.has_value()) { // Not having a string value indicates it was passed like '--a' System::println(System::Color::error, "The option '%s' must be passed an argument.", option); failed = true; } else { output.settings.emplace(option, it->second.value_or_exit(VCPKG_LINE_INFO)); options_copy.erase(it); } } } if (!options_copy.empty()) { System::println(System::Color::error, "Unknown option(s) for command '%s':", this->command); for (auto&& option : options_copy) { System::println(" %s", option.first); } System::println("\nValid options are:", this->command); for (auto&& option : valid_switches) { System::println(" %s", option); } for (auto&& option : valid_settings) { System::println(" %s=...", option); } System::println(" --triplet "); System::println(" --vcpkg-root "); Checks::exit_fail(VCPKG_LINE_INFO); } if (failed) Checks::exit_fail(VCPKG_LINE_INFO); return output; } void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count) const { return check_max_arg_count(expected_arg_count, Strings::EMPTY); } void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count) const { return check_min_arg_count(expected_arg_count, Strings::EMPTY); } void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count) const { return check_exact_arg_count(expected_arg_count, Strings::EMPTY); } void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const { const size_t actual_arg_count = command_arguments.size(); if (actual_arg_count > expected_arg_count) { System::println(System::Color::error, "Error: `%s` requires at most %u arguments, but %u were provided", this->command, expected_arg_count, actual_arg_count); System::print(example_text); Checks::exit_fail(VCPKG_LINE_INFO); } } void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const { const size_t actual_arg_count = command_arguments.size(); if (actual_arg_count < expected_arg_count) { System::println(System::Color::error, "Error: `%s` requires at least %u arguments, but %u were provided", this->command, expected_arg_count, actual_arg_count); System::print(example_text); Checks::exit_fail(VCPKG_LINE_INFO); } } void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count, const std::string& example_text) const { const size_t actual_arg_count = command_arguments.size(); if (actual_arg_count != expected_arg_count) { System::println(System::Color::error, "Error: `%s` requires %u arguments, but %u were provided", this->command, expected_arg_count, actual_arg_count); System::print(example_text); Checks::exit_fail(VCPKG_LINE_INFO); } } }