[vcpkg] Refactor argument parsing to use common code paths.

This commit is contained in:
Robert Schumacher 2017-11-02 15:20:42 -07:00
parent 38136a2d05
commit 6a91d1ece1
31 changed files with 427 additions and 290 deletions

View File

@ -57,4 +57,16 @@ namespace vcpkg
{
return {v.data(), v.size()};
}
template<class T>
constexpr T* begin(Span<T> sp)
{
return sp.begin();
}
template<class T>
constexpr T* end(Span<T> sp)
{
return sp.end();
}
}

View File

@ -30,7 +30,7 @@ namespace vcpkg::Util
}
template<class Cont, class Func>
using FmapOut = decltype(std::declval<Func>()(*begin(std::declval<Cont>())));
using FmapOut = decltype(std::declval<Func&>()(*begin(std::declval<Cont&>())));
template<class Cont, class Func, class Out = FmapOut<Cont, Func>>
std::vector<Out> fmap(Cont&& xs, Func&& f)

View File

@ -18,10 +18,10 @@ namespace vcpkg::Build
{
namespace Command
{
void perform_and_exit(const FullPackageSpec& full_spec,
const fs::path& port_dir,
const ParsedArguments& options,
const VcpkgPaths& paths);
void perform_and_exit_ex(const FullPackageSpec& full_spec,
const fs::path& port_dir,
const ParsedArguments& options,
const VcpkgPaths& paths);
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet);
}

View File

@ -23,16 +23,19 @@ namespace vcpkg::Commands
namespace CI
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet);
}
namespace Env
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet);
}
namespace Create
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
@ -49,16 +52,19 @@ namespace vcpkg::Commands
namespace Search
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace List
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace Owns
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
@ -99,6 +105,7 @@ namespace vcpkg::Commands
namespace Contact
{
extern const CommandStructure COMMAND_STRUCTURE;
const std::string& email();
void perform_and_exit(const VcpkgCmdArguments& args);
}

View File

@ -17,13 +17,47 @@ namespace vcpkg
std::unordered_map<std::string, std::string> settings;
};
struct VcpkgPaths;
struct CommandSwitch
{
std::string name;
CStringView short_help_text;
};
struct CommandSetting
{
std::string name;
CStringView short_help_text;
};
struct CommandOptionsStructure
{
Span<const CommandSwitch> switches;
Span<const CommandSetting> settings;
};
struct CommandStructure
{
std::string example_text;
size_t minimum_arity;
size_t maximum_arity;
CommandOptionsStructure options;
std::vector<std::string> (*valid_arguments)(const VcpkgPaths& paths);
};
#if defined(_WIN32)
using CommandLineCharType = wchar_t;
#else
using CommandLineCharType = char;
#endif
struct VcpkgCmdArguments
{
#if defined(_WIN32)
static VcpkgCmdArguments create_from_command_line(const int argc, const wchar_t* const* const argv);
#else
static VcpkgCmdArguments create_from_command_line(const int argc, const char* const* const argv);
#endif
static VcpkgCmdArguments create_from_command_line(const int argc, const CommandLineCharType* const* const argv);
static VcpkgCmdArguments create_from_arg_sequence(const std::string* arg_begin, const std::string* arg_end);
std::unique_ptr<std::string> vcpkg_root_dir;
@ -35,32 +69,9 @@ namespace vcpkg
std::string command;
std::vector<std::string> command_arguments;
ParsedArguments check_and_get_optional_command_arguments(const std::vector<std::string>& valid_switches,
const std::vector<std::string>& valid_settings) const;
void check_max_arg_count(const size_t expected_arg_count) const;
void check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const;
void check_min_arg_count(const size_t expected_arg_count) const;
void check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const;
void check_exact_arg_count(const size_t expected_arg_count) const;
void check_exact_arg_count(const size_t expected_arg_count, const std::string& example_text) const;
ParsedArguments parse_arguments(const CommandStructure& command_structure) const;
private:
std::unordered_map<std::string, Optional<std::string>> optional_command_arguments;
};
struct VcpkgPaths;
struct CommandStructure
{
CStringView example_text;
size_t minimum_arity;
size_t maximum_arity;
Span<const std::string> switches;
Span<const std::string> settings;
std::vector<std::string> (*valid_arguments)(const VcpkgPaths& paths);
};
}

View File

@ -34,9 +34,12 @@ namespace UnitTest1
TEST_METHOD(create_from_arg_sequence_valued_options)
{
std::array<CommandSetting, 1> settings = { {{"--a", ""}} };
CommandStructure cmdstruct = { "", 0, SIZE_MAX, {{}, settings }, nullptr };
std::vector<std::string> t = {"--a=b", "command", "argument"};
auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
auto opts = v.check_and_get_optional_command_arguments({}, {"--a"});
auto opts = v.parse_arguments(cmdstruct);
Assert::AreEqual("b", opts.settings["--a"].c_str());
Assert::AreEqual(size_t{1}, v.command_arguments.size());
Assert::AreEqual("argument", v.command_arguments[0].c_str());
@ -45,9 +48,13 @@ namespace UnitTest1
TEST_METHOD(create_from_arg_sequence_valued_options2)
{
std::array<CommandSwitch, 2> switches = { {{"--a", ""}, {"--c", ""}} };
std::array<CommandSetting, 2> settings = { { {"--b", ""}, {"--d", ""}} };
CommandStructure cmdstruct = {"", 0, SIZE_MAX, {switches, settings}, nullptr};
std::vector<std::string> t = {"--a", "--b=c"};
auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
auto opts = v.check_and_get_optional_command_arguments({"--a", "--c"}, {"--b", "--d"});
auto opts = v.parse_arguments(cmdstruct);
Assert::AreEqual("c", opts.settings["--b"].c_str());
Assert::IsTrue(opts.settings.find("--d") == opts.settings.end());
Assert::IsTrue(opts.switches.find("--a") != opts.switches.end());

View File

@ -28,10 +28,10 @@ namespace vcpkg::Build::Command
static const std::string OPTION_CHECKS_ONLY = "--checks-only";
void perform_and_exit(const FullPackageSpec& full_spec,
const fs::path& port_dir,
const ParsedArguments& options,
const VcpkgPaths& paths)
void perform_and_exit_ex(const FullPackageSpec& full_spec,
const fs::path& port_dir,
const ParsedArguments& options,
const VcpkgPaths& paths)
{
const PackageSpec& spec = full_spec.package_spec;
if (Util::Sets::contains(options.switches, OPTION_CHECKS_ONLY))
@ -97,21 +97,32 @@ namespace vcpkg::Build::Command
Checks::exit_success(VCPKG_LINE_INFO);
}
static const std::array<CommandSwitch, 1> BUILD_SWITCHES = {{
{OPTION_CHECKS_ONLY, "Only run checks, do not rebuild package"},
}};
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("build zlib:x64-windows"),
1,
1,
{BUILD_SWITCHES, {}},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string EXAMPLE = Help::create_example_string("build zlib:x64-windows");
// Build only takes a single package and all dependencies must already be installed
args.check_exact_arg_count(1, EXAMPLE);
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::string command_argument = args.command_arguments.at(0);
const FullPackageSpec spec = Input::check_and_get_full_package_spec(command_argument, default_triplet, EXAMPLE);
const FullPackageSpec spec =
Input::check_and_get_full_package_spec(command_argument, default_triplet, COMMAND_STRUCTURE.example_text);
Input::check_triplet(spec.package_spec.triplet(), paths);
if (!spec.features.empty() && !GlobalState::feature_packages)
{
Checks::exit_with_message(
VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag.");
}
const ParsedArguments options = args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY}, {});
perform_and_exit(spec, paths.port_dir(spec.package_spec), options, paths);
perform_and_exit_ex(spec, paths.port_dir(spec.package_spec), options, paths);
}
}

View File

@ -120,7 +120,6 @@ namespace vcpkg::Commands::Autocomplete
};
static constexpr CommandEntry COMMANDS[] = {
CommandEntry{"install", R"###(^install\s(.*\s|)(\S*)$)###", Install::COMMAND_STRUCTURE},
CommandEntry{"install", R"###(^install\s(.*\s|)(\S*)$)###", Install::COMMAND_STRUCTURE},
CommandEntry{"edit", R"###(^edit\s(.*\s|)(\S*)$)###", Edit::COMMAND_STRUCTURE},
CommandEntry{"remove", R"###(^remove\s(.*\s|)(\S*)$)###", Remove::COMMAND_STRUCTURE},
@ -137,11 +136,16 @@ namespace vcpkg::Commands::Autocomplete
const bool is_option = Strings::case_insensitive_ascii_starts_with(prefix, "-");
if (is_option)
{
results = Util::fmap(command.structure.switches, [](auto&& s) -> std::string { return s; });
results =
Util::fmap(command.structure.options.switches, [](const CommandSwitch& s) { return s.name; });
auto settings = Util::fmap(command.structure.options.settings, [](auto&& s) { return s.name; });
results.insert(results.end(), settings.begin(), settings.end());
}
else
{
results = command.structure.valid_arguments(paths);
if (command.structure.valid_arguments != nullptr)
results = command.structure.valid_arguments(paths);
}
Util::unstable_keep_if(results, [&](const std::string& s) {

View File

@ -7,17 +7,23 @@
namespace vcpkg::Commands::BuildExternal
{
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"),
2,
2,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string EXAMPLE =
Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)");
args.check_exact_arg_count(2, EXAMPLE);
const FullPackageSpec spec =
Input::check_and_get_full_package_spec(args.command_arguments.at(0), default_triplet, EXAMPLE);
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const FullPackageSpec spec = Input::check_and_get_full_package_spec(
args.command_arguments.at(0), default_triplet, COMMAND_STRUCTURE.example_text);
Input::check_triplet(spec.package_spec.triplet(), paths);
const ParsedArguments options = args.check_and_get_optional_command_arguments({}, {});
const fs::path port_dir = args.command_arguments.at(1);
Build::Command::perform_and_exit(spec, port_dir, options, paths);
Build::Command::perform_and_exit_ex(spec, port_dir, options, paths);
}
}

View File

@ -26,13 +26,19 @@ namespace vcpkg::Commands::Cache
return output;
}
const CommandStructure COMMAND_STRUCTURE = {
Strings::format(
"The argument should be a substring to search for, or no argument to display all cached libraries.\n%s",
Help::create_example_string("cache png")),
0,
1,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format(
"The argument should be a substring to search for, or no argument to display all cached libraries.\n%s",
Help::create_example_string("cache png"));
args.check_max_arg_count(1, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const std::vector<BinaryParagraph> binary_paragraphs = read_all_binary_paragraphs(paths);
if (binary_paragraphs.empty())

View File

@ -58,13 +58,23 @@ namespace vcpkg::Commands::CI
Install::InstallSummary summary;
};
static const std::string OPTION_EXCLUDE = "--exclude";
static const std::array<CommandSetting, 1> CI_SETTINGS = {{
{OPTION_EXCLUDE, "Comma separated list of ports to skip"},
}};
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("ci x64-windows"),
0,
SIZE_MAX,
{{}, CI_SETTINGS},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string OPTION_EXCLUDE = "--exclude";
static const std::string EXAMPLE = Help::create_example_string("ci x64-windows");
const ParsedArguments options = args.check_and_get_optional_command_arguments({}, {OPTION_EXCLUDE});
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::vector<std::string> exclusions = Strings::split(options.settings.at(OPTION_EXCLUDE), ",");
const std::set<std::string> exclusions_set(exclusions.cbegin(), exclusions.cend());

View File

@ -2,6 +2,7 @@
#include <vcpkg/base/system.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
namespace vcpkg::Commands::Contact
{
@ -11,10 +12,17 @@ namespace vcpkg::Commands::Contact
return S_EMAIL;
}
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("contact"),
0,
0,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args)
{
args.check_exact_arg_count(0);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
System::println("Send an email to %s with any feedback.", email());
Checks::exit_success(VCPKG_LINE_INFO);

View File

@ -13,7 +13,7 @@ namespace vcpkg::Commands
Span<const PackageNameAndFunction<CommandTypeA>> get_available_commands_type_a()
{
static std::vector<PackageNameAndFunction<CommandTypeA>> t = {
{"install", &Install::perform_and_exit},
PackageNameAndFunction<CommandTypeA>{"install", &Install::perform_and_exit},
{"ci", &CI::perform_and_exit},
{"remove", &Remove::perform_and_exit},
{"build", &Build::Command::perform_and_exit},

View File

@ -8,13 +8,18 @@
namespace vcpkg::Commands::Create
{
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string(
R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###"),
2,
3,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Help::create_example_string(
R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###");
args.check_max_arg_count(3, EXAMPLE);
args.check_min_arg_count(2, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const std::string port_name = args.command_arguments.at(0);
const std::string url = args.command_arguments.at(1);

View File

@ -9,11 +9,17 @@
namespace vcpkg::Commands::DependInfo
{
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string(R"###(depend-info [pat])###"),
0,
1,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Help::create_example_string(R"###(depend-info [pat])###");
args.check_max_arg_count(1, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
std::vector<std::unique_ptr<SourceControlFile>> source_control_files =
Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);

View File

@ -34,11 +34,6 @@ namespace vcpkg::Commands::Edit
static const std::string OPTION_BUILDTREES = "--buildtrees";
static const std::array<std::string, 1> SWITCHES = {
OPTION_BUILDTREES,
};
static const std::array<std::string, 0> SETTINGS;
static std::vector<std::string> valid_arguments(const VcpkgPaths& paths)
{
auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
@ -47,12 +42,15 @@ namespace vcpkg::Commands::Edit
[](auto&& pgh) -> std::string { return pgh->core_paragraph->name; });
}
static const std::array<CommandSwitch, 1> EDIT_SWITCHES = {{
{OPTION_BUILDTREES, "Open editor into the port-specific buildtree subfolder"},
}};
const CommandStructure COMMAND_STRUCTURE = {
"edit zlib",
Help::create_example_string("edit zlib"),
1,
1,
SWITCHES,
SETTINGS,
{EDIT_SWITCHES, {}},
&valid_arguments,
};
@ -63,9 +61,7 @@ namespace vcpkg::Commands::Edit
auto& fs = paths.get_filesystem();
static const std::string EXAMPLE = Help::create_example_string("edit zlib");
args.check_exact_arg_count(1, EXAMPLE);
const ParsedArguments options = args.check_and_get_optional_command_arguments({OPTION_BUILDTREES}, {});
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::string port_name = args.command_arguments.at(0);
const fs::path portpath = paths.ports / port_name;

View File

@ -7,11 +7,17 @@
namespace vcpkg::Commands::Env
{
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("env --triplet x64-windows"),
0,
0,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string EXAMPLE = Help::create_example_string(R"(env --triplet x64-windows)");
args.check_exact_arg_count(0, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet);
const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path);

View File

@ -28,13 +28,18 @@ namespace vcpkg::Commands::Hash
System::println(hash);
}
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be a file path\n%s",
Help::create_example_string("hash boost_1_62_0.tar.bz2")),
1,
2,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args)
{
static const std::string EXAMPLE = Strings::format("The argument should be a file path\n%s",
Help::create_example_string("hash boost_1_62_0.tar.bz2"));
args.check_min_arg_count(1, EXAMPLE);
args.check_max_arg_count(2, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
if (args.command_arguments.size() == 1)
{

View File

@ -92,12 +92,18 @@ namespace vcpkg::Commands::Import
fs.write_contents(control_file_path, Strings::serialize(control_file_data));
}
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string(
R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"),
3,
3,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Help::create_example_string(
R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)");
args.check_exact_arg_count(3, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const fs::path control_file_path(args.command_arguments[0]);
const fs::path include_directory(args.command_arguments[1]);

View File

@ -333,10 +333,6 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
static const std::string PROJECT = "project";
}
static const std::array<std::string, 0> INSTALL_SWITCHES;
static const std::array<std::string, 0> INSTALL_SETTINGS;
static std::vector<std::string> valid_arguments(const VcpkgPaths&)
{
return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT};
@ -348,18 +344,13 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
INTEGRATE_COMMAND_HELPSTRING),
1,
1,
INSTALL_SWITCHES,
INSTALL_SETTINGS,
{},
&valid_arguments,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format("Commands:\n"
"%s",
INTEGRATE_COMMAND_HELPSTRING);
args.check_exact_arg_count(1, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
#if defined(_WIN32)
if (args.command_arguments[0] == Subcommand::INSTALL)

View File

@ -24,13 +24,23 @@ namespace vcpkg::Commands::List
}
}
static const std::array<CommandSwitch, 1> LIST_SWITCHES = {{
{OPTION_FULLDESC, "Do not truncate long text"},
}};
const CommandStructure COMMAND_STRUCTURE = {
Strings::format(
"The argument should be a substring to search for, or no argument to display all installed libraries.\n%s",
Help::create_example_string("list png")),
0,
1,
{LIST_SWITCHES, {}},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format(
"The argument should be a substring to search for, or no argument to display all installed libraries.\n%s",
Help::create_example_string("list png"));
args.check_max_arg_count(1, EXAMPLE);
const ParsedArguments options = args.check_and_get_optional_command_arguments({OPTION_FULLDESC}, {});
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const StatusParagraphs status_paragraphs = database_load_check(paths);
std::vector<StatusParagraph*> installed_packages = get_installed_ports(status_paragraphs);

View File

@ -23,13 +23,18 @@ namespace vcpkg::Commands::Owns
}
}
}
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be a pattern to search for. %s",
Help::create_example_string("owns zlib.dll")),
1,
1,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format("The argument should be a pattern to search for. %s",
Help::create_example_string("owns zlib.dll"));
args.check_exact_arg_count(1, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const StatusParagraphs status_db = database_load_check(paths);
search_file(paths, args.command_arguments[0], status_db);

View File

@ -114,13 +114,18 @@ namespace vcpkg::Commands::PortsDiff
VCPKG_LINE_INFO, output.output == VALID_COMMIT_OUTPUT, "Invalid commit id %s", git_commit_id);
}
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be a branch/tag/hash to checkout.\n%s",
Help::create_example_string("portsdiff mybranchname")),
1,
2,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format("The argument should be a branch/tag/hash to checkout.\n%s",
Help::create_example_string("portsdiff mybranchname"));
args.check_min_arg_count(1, EXAMPLE);
args.check_max_arg_count(2, EXAMPLE);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
const fs::path& git_exe = paths.get_git_exe();

View File

@ -79,14 +79,24 @@ namespace vcpkg::Commands::Search
}
}
static std::array<CommandSwitch, 2> SEARCH_SWITCHES = {{
{OPTION_GRAPH, "Open editor into the port-specific buildtree subfolder"},
{OPTION_FULLDESC, "Do not truncate long text"},
}};
const CommandStructure COMMAND_STRUCTURE = {
Strings::format(
"The argument should be a substring to search for, or no argument to display all libraries.\n%s",
Help::create_example_string("search png")),
0,
1,
{SEARCH_SWITCHES, {}},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
static const std::string EXAMPLE = Strings::format(
"The argument should be a substring to search for, or no argument to display all libraries.\n%s",
Help::create_example_string("search png"));
args.check_max_arg_count(1, EXAMPLE);
const ParsedArguments options =
args.check_and_get_optional_command_arguments({OPTION_GRAPH, OPTION_FULLDESC}, {});
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC);
auto source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);

View File

@ -2,6 +2,7 @@
#include <vcpkg/base/system.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
#include <vcpkg/metrics.h>
#define STRINGIFY(...) #__VA_ARGS__
@ -61,11 +62,17 @@ namespace vcpkg::Commands::Version
}
}
}
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("version"),
0,
0,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args)
{
args.check_exact_arg_count(0);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
System::println("Vcpkg package management program version %s\n"
"\n"

View File

@ -257,51 +257,57 @@ namespace vcpkg::Export
std::vector<PackageSpec> specs;
};
static const std::string OPTION_DRY_RUN = "--dry-run";
static const std::string OPTION_RAW = "--raw";
static const std::string OPTION_NUGET = "--nuget";
static const std::string OPTION_IFW = "--ifw";
static const std::string OPTION_ZIP = "--zip";
static const std::string OPTION_SEVEN_ZIP = "--7zip";
static const std::string OPTION_NUGET_ID = "--nuget-id";
static const std::string OPTION_NUGET_VERSION = "--nuget-version";
static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url";
static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path";
static const std::string OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path";
static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path";
static const std::string OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path";
static const std::array<CommandSwitch, 6> EXPORT_SWITCHES = {{
{OPTION_DRY_RUN, "Do not actually export"},
{OPTION_RAW, "Export to an uncompressed directory"},
{OPTION_NUGET, "Export a NuGet package"},
{OPTION_IFW, "Export to an IFW-based installer"},
{OPTION_ZIP, "Export to a zip file"},
{OPTION_SEVEN_ZIP, "Export to a 7zip (.7z) file"},
}};
static const std::array<CommandSetting, 7> EXPORT_SETTINGS = {{
{OPTION_NUGET_ID, "Specify the id for the exported NuGet package"},
{OPTION_NUGET_VERSION, "Specify the version for the exported NuGet package"},
{OPTION_IFW_REPOSITORY_URL, ""},
{OPTION_IFW_PACKAGES_DIR_PATH, ""},
{OPTION_IFW_REPOSITORY_DIR_PATH, ""},
{OPTION_IFW_CONFIG_FILE_PATH, ""},
{OPTION_IFW_INSTALLER_FILE_PATH, ""},
}};
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("export zlib zlib:x64-windows boost --nuget"),
0,
SIZE_MAX,
{EXPORT_SWITCHES, EXPORT_SETTINGS},
nullptr,
};
static ExportArguments handle_export_command_arguments(const VcpkgCmdArguments& args,
const Triplet& default_triplet)
{
ExportArguments ret;
static const std::string OPTION_DRY_RUN = "--dry-run";
static const std::string OPTION_RAW = "--raw";
static const std::string OPTION_NUGET = "--nuget";
static const std::string OPTION_IFW = "--ifw";
static const std::string OPTION_ZIP = "--zip";
static const std::string OPTION_SEVEN_ZIP = "--7zip";
static const std::string OPTION_NUGET_ID = "--nuget-id";
static const std::string OPTION_NUGET_VERSION = "--nuget-version";
static const std::string OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url";
static const std::string OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path";
static const std::string OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path";
static const std::string OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path";
static const std::string OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path";
const auto options = args.parse_arguments(COMMAND_STRUCTURE);
// input sanitization
static const std::string EXAMPLE = Help::create_example_string("export zlib zlib:x64-windows boost --nuget");
args.check_min_arg_count(1, EXAMPLE);
ret.specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE);
return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
});
const auto options = args.check_and_get_optional_command_arguments(
{
OPTION_DRY_RUN,
OPTION_RAW,
OPTION_NUGET,
OPTION_IFW,
OPTION_ZIP,
OPTION_SEVEN_ZIP,
},
{
OPTION_NUGET_ID,
OPTION_NUGET_VERSION,
OPTION_IFW_REPOSITORY_URL,
OPTION_IFW_PACKAGES_DIR_PATH,
OPTION_IFW_REPOSITORY_DIR_PATH,
OPTION_IFW_CONFIG_FILE_PATH,
OPTION_IFW_INSTALLER_FILE_PATH,
});
ret.dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend();
ret.raw = options.switches.find(OPTION_RAW) != options.switches.cend();
ret.nuget = options.switches.find(OPTION_NUGET) != options.switches.cend();
@ -313,7 +319,7 @@ namespace vcpkg::Export
{
System::println(System::Color::error,
"Must provide at least one export type: --raw --nuget --ifw --zip --7zip");
System::print(EXAMPLE);
System::print(COMMAND_STRUCTURE.example_text);
Checks::exit_fail(VCPKG_LINE_INFO);
}

View File

@ -91,10 +91,17 @@ namespace vcpkg::Help
System::println(create_example_string(command_and_arguments));
}
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("help"),
0,
1,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
args.check_max_arg_count(1);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
if (args.command_arguments.empty())
{

View File

@ -535,13 +535,13 @@ namespace vcpkg::Install
static const std::string OPTION_RECURSE = "--recurse";
static const std::string OPTION_KEEP_GOING = "--keep-going";
static const std::array<std::string, 5> INSTALL_SWITCHES = {
OPTION_DRY_RUN,
OPTION_USE_HEAD_VERSION,
OPTION_NO_DOWNLOADS,
OPTION_RECURSE,
OPTION_KEEP_GOING,
};
static const std::array<CommandSwitch, 5> INSTALL_SWITCHES = {{
{OPTION_DRY_RUN, "Do not actually build or install"},
{OPTION_USE_HEAD_VERSION, "Install the libraries on the command line using the latest upstream sources"},
{OPTION_NO_DOWNLOADS, "Do not download new sources"},
{OPTION_RECURSE, "Allow removal of packages as part of installation"},
{OPTION_KEEP_GOING, "Continue installing packages on failure"},
}};
static const std::array<std::string, 0> INSTALL_SETTINGS;
std::vector<std::string> get_all_port_names(const VcpkgPaths& paths)
@ -553,22 +553,20 @@ namespace vcpkg::Install
}
const CommandStructure COMMAND_STRUCTURE = {
"install zlib zlib:x64-windows curl boost",
Help::create_example_string("install zlib zlib:x64-windows curl boost"),
1,
SIZE_MAX,
INSTALL_SWITCHES,
INSTALL_SETTINGS,
{INSTALL_SWITCHES, {}},
&get_all_port_names,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
// input sanitization
static const std::string EXAMPLE = Help::create_example_string("install zlib zlib:x64-windows curl boost");
args.check_min_arg_count(1, EXAMPLE);
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(arg, default_triplet, EXAMPLE);
return Input::check_and_get_full_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
@ -581,8 +579,6 @@ namespace vcpkg::Install
}
}
const ParsedArguments options = args.check_and_get_optional_command_arguments(
{OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS, OPTION_RECURSE, OPTION_KEEP_GOING}, {});
const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
const bool use_head_version = Util::Sets::contains(options.switches, (OPTION_USE_HEAD_VERSION));
const bool no_downloads = Util::Sets::contains(options.switches, (OPTION_NO_DOWNLOADS));

View File

@ -169,14 +169,13 @@ namespace vcpkg::Remove
static const std::string OPTION_DRY_RUN = "--dry-run";
static const std::string OPTION_OUTDATED = "--outdated";
static const std::array<std::string, 5> SWITCHES = {
OPTION_PURGE,
OPTION_NO_PURGE,
OPTION_RECURSE,
OPTION_DRY_RUN,
OPTION_OUTDATED,
};
static const std::array<std::string, 0> SETTINGS;
static const std::array<CommandSwitch, 5> SWITCHES = {{
{OPTION_PURGE, "Remove the cached copy of the package (default)"},
{OPTION_NO_PURGE, "Do not remove the cached copy of the package"},
{OPTION_RECURSE, "Allow removal of packages not explicitly specified on the command line"},
{OPTION_DRY_RUN, "Print the packages to be removed, but do not remove them"},
{OPTION_OUTDATED, "Select all packages with versions that do not match the portfiles"},
}};
static std::vector<std::string> valid_arguments(const VcpkgPaths& paths)
{
@ -187,25 +186,26 @@ namespace vcpkg::Remove
}
const CommandStructure COMMAND_STRUCTURE = {
"remove zlib zlib:x64-windows curl boost",
1,
Help::create_example_string("remove zlib zlib:x64-windows curl boost"),
0,
SIZE_MAX,
SWITCHES,
SETTINGS,
{SWITCHES, {}},
&valid_arguments,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string EXAMPLE = Help::create_example_string("remove zlib zlib:x64-windows curl boost");
const ParsedArguments options = args.check_and_get_optional_command_arguments(
{OPTION_PURGE, OPTION_NO_PURGE, OPTION_RECURSE, OPTION_DRY_RUN, OPTION_OUTDATED}, {});
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
StatusParagraphs status_db = database_load_check(paths);
std::vector<PackageSpec> specs;
if (Util::Sets::contains(options.switches, OPTION_OUTDATED))
{
args.check_exact_arg_count(0, EXAMPLE);
if (args.command_arguments.size() != 0)
{
System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'");
Checks::exit_fail(VCPKG_LINE_INFO);
}
specs = Util::fmap(Update::find_outdated_packages(paths, status_db),
[](auto&& outdated) { return outdated.spec; });
@ -217,9 +217,13 @@ namespace vcpkg::Remove
}
else
{
args.check_min_arg_count(1, EXAMPLE);
if (args.command_arguments.size() < 1)
{
System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'");
Checks::exit_fail(VCPKG_LINE_INFO);
}
specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_package_spec(arg, default_triplet, EXAMPLE);
return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
@ -231,7 +235,7 @@ namespace vcpkg::Remove
if (purge_was_passed && no_purge_was_passed)
{
System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge.");
System::print(EXAMPLE);
System::print(COMMAND_STRUCTURE.example_text);
Checks::exit_fail(VCPKG_LINE_INFO);
}
const Purge purge = to_purge(purge_was_passed || !no_purge_was_passed);

View File

@ -38,10 +38,17 @@ namespace vcpkg::Update
return output;
}
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("update"),
0,
0,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
args.check_exact_arg_count(0);
args.check_and_get_optional_command_arguments({}, {});
args.parse_arguments(COMMAND_STRUCTURE);
System::println("Using local portfile versions. To update the local portfiles, use `git pull`.");
const StatusParagraphs status_db = database_load_check(paths);

View File

@ -45,11 +45,8 @@ namespace vcpkg
option_field = new_setting;
}
#if defined(_WIN32)
VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const wchar_t* const* const argv)
#else
VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const char* const* const argv)
#endif
VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc,
const CommandLineCharType* const* const argv)
{
std::vector<std::string> v;
for (int i = 1; i < argc; ++i)
@ -158,46 +155,83 @@ namespace vcpkg
return args;
}
ParsedArguments VcpkgCmdArguments::check_and_get_optional_command_arguments(
const std::vector<std::string>& valid_switches, const std::vector<std::string>& valid_settings) const
ParsedArguments VcpkgCmdArguments::parse_arguments(const CommandStructure& command_structure) const
{
bool failed = false;
ParsedArguments output;
auto options_copy = this->optional_command_arguments;
for (const std::string& option : valid_switches)
const size_t actual_arg_count = command_arguments.size();
if (command_structure.minimum_arity == command_structure.maximum_arity)
{
const auto it = options_copy.find(option);
if (actual_arg_count != command_structure.minimum_arity)
{
System::println(System::Color::error,
"Error: '%s' requires %u arguments, but %u were provided.",
this->command,
command_structure.minimum_arity,
actual_arg_count);
failed = true;
}
}
else
{
if (actual_arg_count < command_structure.minimum_arity)
{
System::println(System::Color::error,
"Error: '%s' requires at least %u arguments, but %u were provided",
this->command,
command_structure.minimum_arity,
actual_arg_count);
failed = true;
}
if (actual_arg_count > command_structure.maximum_arity)
{
System::println(System::Color::error,
"Error: '%s' requires at most %u arguments, but %u were provided",
this->command,
command_structure.maximum_arity,
actual_arg_count);
failed = true;
}
}
auto options_copy = this->optional_command_arguments;
for (auto&& option : command_structure.options.switches)
{
const auto it = options_copy.find(option.name);
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);
System::println(
System::Color::error, "Error: The option '%s' does not accept an argument.", option.name);
failed = true;
}
else
{
output.switches.insert(option);
output.switches.insert(option.name);
options_copy.erase(it);
}
}
}
for (const std::string& option : valid_settings)
for (auto&& option : command_structure.options.settings)
{
const auto it = options_copy.find(option);
const auto it = options_copy.find(option.name);
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);
System::println(
System::Color::error, "Error: The option '%s' must be passed an argument.", option.name);
failed = true;
}
else
{
output.settings.emplace(option, it->second.value_or_exit(VCPKG_LINE_INFO));
output.settings.emplace(option.name, it->second.value_or_exit(VCPKG_LINE_INFO));
options_copy.erase(it);
}
}
@ -210,83 +244,32 @@ namespace vcpkg
{
System::println(" %s", option.first);
}
System::println("\nValid options are:", this->command);
for (auto&& option : valid_switches)
System::println();
failed = true;
}
if (failed)
{
if (!command_structure.example_text.empty())
{
System::println(" %s", option);
System::println("%s", command_structure.example_text);
}
for (auto&& option : valid_settings)
System::println("Options:", this->command);
for (auto&& option : command_structure.options.switches)
{
System::println(" %s=...", option);
System::println(" %-40s %s", option.name, option.short_help_text);
}
for (auto&& option : command_structure.options.settings)
{
System::println(" %-40s %s", (option.name + "=..."), option.short_help_text);
}
System::println(" --triplet <t>");
System::println(" --vcpkg-root <path>");
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, "");
}
void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count) const
{
return check_min_arg_count(expected_arg_count, "");
}
void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count) const
{
return check_exact_arg_count(expected_arg_count, "");
}
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);
}
}
}