diff options
| author | Barath Kannan <barathsotd@gmail.com> | 2017-11-06 00:33:04 +1100 |
|---|---|---|
| committer | Barath Kannan <barathsotd@gmail.com> | 2017-11-06 00:33:04 +1100 |
| commit | b959f70a9969551a132d691fbd12046d5ea0702e (patch) | |
| tree | d15d129bd480b1ba173322a0e50140ccf1ff487a /toolsrc/src | |
| parent | 3a5b383bbec74dbaf0f1056e1a5d315e43d79375 (diff) | |
| parent | 330b8d8bab6a3d07165bf7c05fea09a8e0d56348 (diff) | |
| download | vcpkg-b959f70a9969551a132d691fbd12046d5ea0702e.tar.gz vcpkg-b959f70a9969551a132d691fbd12046d5ea0702e.zip | |
merge from master
Diffstat (limited to 'toolsrc/src')
38 files changed, 1026 insertions, 521 deletions
diff --git a/toolsrc/src/tests.arguments.cpp b/toolsrc/src/tests.arguments.cpp index 25bf0f085..0f082222d 100644 --- a/toolsrc/src/tests.arguments.cpp +++ b/toolsrc/src/tests.arguments.cpp @@ -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());
diff --git a/toolsrc/src/tests.installplan.cpp b/toolsrc/src/tests.installplan.cpp index 120009db5..129926317 100644 --- a/toolsrc/src/tests.installplan.cpp +++ b/toolsrc/src/tests.installplan.cpp @@ -17,8 +17,9 @@ namespace Microsoft::VisualStudio::CppUnitTestFramework case Dependencies::InstallPlanType::ALREADY_INSTALLED: return L"ALREADY_INSTALLED"; case Dependencies::InstallPlanType::BUILD_AND_INSTALL: return L"BUILD_AND_INSTALL"; case Dependencies::InstallPlanType::INSTALL: return L"INSTALL"; + case Dependencies::InstallPlanType::EXCLUDED: return L"EXCLUDED"; case Dependencies::InstallPlanType::UNKNOWN: return L"UNKNOWN"; - default: return ToString((int)t); + default: return ToString(static_cast<int>(t)); } } @@ -30,7 +31,7 @@ namespace Microsoft::VisualStudio::CppUnitTestFramework case Dependencies::RequestType::AUTO_SELECTED: return L"AUTO_SELECTED"; case Dependencies::RequestType::USER_REQUESTED: return L"USER_REQUESTED"; case Dependencies::RequestType::UNKNOWN: return L"UNKNOWN"; - default: return ToString((int)t); + default: return ToString(static_cast<int>(t)); } } } @@ -460,20 +461,6 @@ namespace UnitTest1 FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}), StatusParagraphs(std::move(status_paragraphs))); - /*Assert::AreEqual(size_t(8), install_plan.size()); - auto iterator_pos = [&](const PackageSpec& spec, size_t start) -> int { - auto it = std::find_if(install_plan.begin() + start, install_plan.end(), [&](auto& action) { - return action.spec == spec; - }); - Assert::IsTrue(it != install_plan.end()); - return (int)(it - install_plan.begin()); - }; - int a_64_1 = iterator_pos(spec_a_64.package_spec, 0), a_86_1 = iterator_pos(spec_a_86.package_spec, 0), - b_64 = iterator_pos(spec_b_64.package_spec, 0), b_86 = iterator_pos(spec_b_86.package_spec, 0), - c_64 = iterator_pos(spec_c_64.package_spec, 0), c_86 = iterator_pos(spec_c_86.package_spec, 0), - a_64_2 = iterator_pos(spec_a_64.package_spec, a_64_1 + 1), - a_86_2 = iterator_pos(spec_a_86.package_spec, a_86_1 + 1);*/ - remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS); remove_plan_check(&install_plan[1], "a"); features_check(&install_plan[2], "b", {"core"}, Triplet::X64_WINDOWS); @@ -483,5 +470,24 @@ namespace UnitTest1 features_check(&install_plan[6], "a", {"a1", "core"}); features_check(&install_plan[7], "c", {"core"}); } + + TEST_METHOD(install_all_features_test) + { + std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}), {"core"}}; + + auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); + Assert::IsTrue(install_specs.has_value()); + if (!install_specs.has_value()) return; + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + Assert::IsTrue(install_plan.size() == 1); + features_check(&install_plan[0], "a", {"0", "1", "core"}, Triplet::X64_WINDOWS); + } }; -}
\ No newline at end of file +} diff --git a/toolsrc/src/tests.packagespec.cpp b/toolsrc/src/tests.packagespec.cpp index cb5c41af0..25498a799 100644 --- a/toolsrc/src/tests.packagespec.cpp +++ b/toolsrc/src/tests.packagespec.cpp @@ -3,6 +3,7 @@ #include <vcpkg/paragraphs.h> #include <vcpkg/base/strings.h> +#include <vcpkg/base/util.h> #pragma comment(lib, "version") #pragma comment(lib, "winhttp") @@ -103,6 +104,39 @@ namespace UnitTest1 Assert::AreEqual("", spec->triplet.c_str()); } + TEST_METHOD(parsed_specifier_wildcard_feature) + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[*]"); + Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error()); + auto spec = maybe_spec.get(); + Assert::AreEqual("zlib", spec->name.c_str()); + Assert::IsTrue(spec->features.size() == 1); + Assert::AreEqual("*", spec->features[0].c_str()); + Assert::AreEqual("", spec->triplet.c_str()); + } + + TEST_METHOD(expand_wildcards) + { + auto zlib = + vcpkg::FullPackageSpec::from_string("zlib[0,1]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); + auto openssl = + vcpkg::FullPackageSpec::from_string("openssl[*]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); + auto specs = FullPackageSpec::to_feature_specs({zlib, openssl}); + Util::sort(specs); + auto spectargets = FeatureSpec::from_strings_and_triplet( + { + "openssl", + "zlib", + "openssl[*]", + "zlib[0]", + "zlib[1]", + }, + Triplet::X86_UWP); + Util::sort(spectargets); + Assert::IsTrue(specs.size() == spectargets.size()); + Assert::IsTrue(Util::all_equal(specs, spectargets)); + } + TEST_METHOD(utf8_to_utf16) { auto str = vcpkg::Strings::to_utf16("abc"); diff --git a/toolsrc/src/tests.paragraph.cpp b/toolsrc/src/tests.paragraph.cpp index d9301abd0..13052610f 100644 --- a/toolsrc/src/tests.paragraph.cpp +++ b/toolsrc/src/tests.paragraph.cpp @@ -190,6 +190,20 @@ namespace UnitTest1 Assert::AreEqual("c", pgh.depends[2].c_str()); } + TEST_METHOD(BinaryParagraph_Abi) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Abi", "abcd123"}, + }); + + Assert::AreEqual(size_t(0), pgh.depends.size()); + Assert::IsTrue(pgh.abi == "abcd123"); + } + TEST_METHOD(parse_paragraphs_empty) { const char* str = ""; @@ -385,5 +399,21 @@ namespace UnitTest1 Assert::AreEqual(size_t(1), pghs.size()); Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str()); } + + TEST_METHOD(BinaryParagraph_serialize_abi) + { + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + {"Abi", "123abc"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + Assert::AreEqual(size_t(1), pghs.size()); + Assert::AreEqual("123abc", pghs[0]["Abi"].c_str()); + } }; } diff --git a/toolsrc/src/tests.statusparagraphs.cpp b/toolsrc/src/tests.statusparagraphs.cpp new file mode 100644 index 000000000..4cc1130b1 --- /dev/null +++ b/toolsrc/src/tests.statusparagraphs.cpp @@ -0,0 +1,85 @@ +#include <CppUnitTest.h> + +#include <vcpkg/base/util.h> +#include <vcpkg/paragraphs.h> +#include <vcpkg/statusparagraph.h> +#include <vcpkg/statusparagraphs.h> + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +using namespace vcpkg; +using namespace vcpkg::Paragraphs; + +namespace UnitTest1 +{ + class StatusParagraphsTests : public TestClass<StatusParagraphsTests> + { + TEST_METHOD(find_installed) + { + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed +)"); + Assert::IsTrue(!!pghs); + if (!pghs) return; + + StatusParagraphs status_db(Util::fmap( + *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); })); + + auto it = status_db.find_installed("ffmpeg", Triplet::X64_WINDOWS); + Assert::IsTrue(it != status_db.end()); + } + + TEST_METHOD(find_not_installed) + { + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: purge ok not-installed +)"); + Assert::IsTrue(!!pghs); + if (!pghs) return; + + StatusParagraphs status_db(Util::fmap( + *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); })); + + auto it = status_db.find_installed("ffmpeg", Triplet::X64_WINDOWS); + Assert::IsTrue(it == status_db.end()); + } + + TEST_METHOD(find_with_feature_packages) + { + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed + +Package: ffmpeg +Feature: openssl +Depends: openssl +Architecture: x64-windows +Multi-Arch: same +Description: +Status: purge ok not-installed +)"); + Assert::IsTrue(!!pghs); + if (!pghs) return; + + StatusParagraphs status_db(Util::fmap( + *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); })); + + auto it = status_db.find_installed("ffmpeg", Triplet::X64_WINDOWS); + Assert::IsTrue(it != status_db.end()); + } + }; +}
\ No newline at end of file diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index b4c2d0fe2..2a73c5b0f 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -245,8 +245,8 @@ int main(const int argc, const char* const* const argv) GlobalState::g_init_console_cp = GetConsoleCP(); GlobalState::g_init_console_output_cp = GetConsoleOutputCP(); - SetConsoleCP(65001); - SetConsoleOutputCP(65001); + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); const std::string trimmed_command_line = trim_path_from_command_line(Strings::to_utf8(GetCommandLineW())); #endif diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index af41eed9a..27cfcbe71 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -114,24 +114,29 @@ namespace vcpkg::Strings #endif } - void trim(std::string* s) + std::string replace_all(std::string&& s, const std::string& search, const std::string& rep) { - s->erase(std::find_if_not(s->rbegin(), s->rend(), details::isspace).base(), s->end()); - s->erase(s->begin(), std::find_if_not(s->begin(), s->end(), details::isspace)); + size_t pos = 0; + while ((pos = s.find(search, pos)) != std::string::npos) + { + s.replace(pos, search.size(), rep); + pos += rep.size(); + } + return std::move(s); } - std::string trimmed(const std::string& s) + std::string trim(std::string&& s) { - auto whitespace_back = std::find_if_not(s.rbegin(), s.rend(), details::isspace).base(); - auto whitespace_front = std::find_if_not(s.begin(), whitespace_back, details::isspace); - return std::string(whitespace_front, whitespace_back); + s.erase(std::find_if_not(s.rbegin(), s.rend(), details::isspace).base(), s.end()); + s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), details::isspace)); + return std::move(s); } void trim_all_and_remove_whitespace_strings(std::vector<std::string>* strings) { for (std::string& s : *strings) { - trim(&s); + s = trim(std::move(s)); } Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); }); diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp index b04f79414..47096ed63 100644 --- a/toolsrc/src/vcpkg/base/system.cpp +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -3,6 +3,7 @@ #include <vcpkg/base/checks.h> #include <vcpkg/base/system.h> #include <vcpkg/globalstate.h> +#include <vcpkg/metrics.h> #include <time.h> @@ -269,11 +270,46 @@ namespace vcpkg::System #endif } - std::string create_powershell_script_cmd(const fs::path& script_path, const CStringView args) + std::string powershell_execute_and_capture_output(const std::string& title, + const fs::path& script_path, + const CStringView args) { // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - return Strings::format( + const std::string cmd = Strings::format( R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args); + + auto rc = System::cmd_execute_and_capture_output(cmd); + + if (rc.exit_code) + { + System::println(Color::error, + "%s\n" + "Could not run:\n" + " '%s'\n" + "Error message was:\n" + " %s", + title, + script_path.generic_string(), + rc.output); + + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_property("error", "powershell script failed"); + locked_metrics->track_property("title", title); + } + + Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code); + } + + // Remove newline from all output. + // Powershell returns newlines when it hits the column count of the console. + // For example, this is 80 in cmd on Windows 7. If the expected output is longer than 80 lines, we get + // newlines in-between the data. + // To solve this, we design our interaction with powershell to not depend on newlines, + // and then strip all newlines here. + rc.output = Strings::replace_all(std::move(rc.output), "\n", ""); + + return rc.output; } void println() { putchar('\n'); } diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp index 3a493eb4c..c7136b713 100644 --- a/toolsrc/src/vcpkg/binaryparagraph.cpp +++ b/toolsrc/src/vcpkg/binaryparagraph.cpp @@ -16,6 +16,7 @@ namespace vcpkg namespace Fields { + static const std::string ABI = "Abi"; static const std::string FEATURE = "Feature"; static const std::string DESCRIPTION = "Description"; static const std::string MAINTAINER = "Maintainer"; @@ -47,6 +48,8 @@ namespace vcpkg this->description = parser.optional_field(Fields::DESCRIPTION); this->maintainer = parser.optional_field(Fields::MAINTAINER); + this->abi = parser.optional_field(Fields::ABI); + std::string multi_arch; parser.required_field(Fields::MULTI_ARCH, multi_arch); @@ -118,6 +121,7 @@ namespace vcpkg out_str.append("Multi-Arch: same\n"); if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n'); + if (!pgh.abi.empty()) out_str.append("Abi: ").append(pgh.abi).push_back('\n'); if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n'); } } diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index cf16c8f9c..ca0712a06 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -28,13 +28,13 @@ 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 std::unordered_set<std::string>& 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 (options.find(OPTION_CHECKS_ONLY) != options.end()) + if (Util::Sets::contains(options.switches, OPTION_CHECKS_ONLY)) { const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, spec.triplet()); const auto build_info = Build::read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec)); @@ -52,22 +52,20 @@ namespace vcpkg::Build::Command Checks::exit_fail(VCPKG_LINE_INFO); } - for (const std::string& str : full_spec.features) - { - System::println("%s \n", str); - } const auto& scf = source_control_file.value_or_exit(VCPKG_LINE_INFO); Checks::check_exit(VCPKG_LINE_INFO, spec.name() == scf->core_paragraph->name, - "The Name: field inside the CONTROL does not match the port directory: '%s' != '%s'", + "The Source field inside the CONTROL file does not match the port directory: '%s' != '%s'", scf->core_paragraph->name, spec.name()); const StatusParagraphs status_db = database_load_check(paths); const Build::BuildPackageOptions build_package_options{Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; + const std::unordered_set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end()); + const Build::BuildPackageConfig build_config{ - *scf->core_paragraph, spec.triplet(), paths.port_dir(spec), build_package_options}; + *scf, spec.triplet(), fs::path{port_dir}, build_package_options, features_as_set}; const auto build_timer = Chrono::ElapsedTime::create_started(); const auto result = Build::build_package(paths, build_config, status_db); @@ -87,6 +85,8 @@ namespace vcpkg::Build::Command Checks::exit_fail(VCPKG_LINE_INFO); } + Checks::check_exit(VCPKG_LINE_INFO, result.code != BuildResult::EXCLUDED); + if (result.code != BuildResult::SUCCEEDED) { System::println(System::Color::error, Build::create_error_message(result.code, spec)); @@ -97,17 +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); - const std::unordered_set<std::string> options = - args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY}); - perform_and_exit(spec, paths.port_dir(spec.package_spec), options, paths); + if (!spec.features.empty() && !GlobalState::feature_packages) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag."); + } + perform_and_exit_ex(spec, paths.port_dir(spec.package_spec), options, paths); } } @@ -365,6 +380,7 @@ namespace vcpkg::Build static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS"; static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED"; static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES"; + static const std::string EXCLUDED_STRING = "EXCLUDED"; switch (build_result) { @@ -372,8 +388,9 @@ namespace vcpkg::Build case BuildResult::SUCCEEDED: return SUCCEEDED_STRING; case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING; case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING; - case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING; + case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; + case BuildResult::EXCLUDED: return EXCLUDED_STRING; default: Checks::unreachable(VCPKG_LINE_INFO); } } @@ -472,7 +489,7 @@ namespace vcpkg::Build {"CMAKE_TRIPLET_FILE", triplet_file_path}, }); const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0); + Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output); const std::vector<std::string> lines = Strings::split(ec_data.output, "\n"); diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp index addb9edaf..0df7ec5eb 100644 --- a/toolsrc/src/vcpkg/commands.autocomplete.cpp +++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp @@ -39,7 +39,8 @@ namespace vcpkg::Commands::Autocomplete { const std::string requested_command = match[1].str(); - std::vector<std::string> valid_commands = { + // First try public commands + std::vector<std::string> public_commands = { "install", "search", "remove", @@ -57,11 +58,31 @@ namespace vcpkg::Commands::Autocomplete "contact", }; - Util::unstable_keep_if(valid_commands, [&](const std::string& s) { + Util::unstable_keep_if(public_commands, [&](const std::string& s) { return Strings::case_insensitive_ascii_starts_with(s, requested_command); }); - output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(valid_commands)); + if (!public_commands.empty()) + { + output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(public_commands)); + } + + // If no public commands match, try private commands + std::vector<std::string> private_commands = { + "build", + "buildexternal", + "ci", + "depend-info", + "env", + "import", + "portsdiff", + }; + + Util::unstable_keep_if(private_commands, [&](const std::string& s) { + return Strings::case_insensitive_ascii_starts_with(s, requested_command); + }); + + output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(private_commands)); } // Handles vcpkg install package:<triplet> @@ -100,9 +121,9 @@ 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}, + CommandEntry{"integrate", R"###(^integrate(\s+)(\S*)$)###", Integrate::COMMAND_STRUCTURE}, }; for (auto&& command : COMMANDS) @@ -112,20 +133,26 @@ namespace vcpkg::Commands::Autocomplete const auto prefix = match[2].str(); std::vector<std::string> results; - if (Strings::case_insensitive_ascii_starts_with(prefix, "-")) + 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) { return Strings::case_insensitive_ascii_starts_with(s, prefix); }); - if (command.name == "install" && results.size() == 1) + if (command.name == "install" && results.size() == 1 && !is_option) { const auto port_at_each_triplet = combine_port_with_triplets(results[0], paths.get_available_triplets()); diff --git a/toolsrc/src/vcpkg/commands.buildexternal.cpp b/toolsrc/src/vcpkg/commands.buildexternal.cpp index 3991beb62..82d03db48 100644 --- a/toolsrc/src/vcpkg/commands.buildexternal.cpp +++ b/toolsrc/src/vcpkg/commands.buildexternal.cpp @@ -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 std::unordered_set<std::string> 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); } } diff --git a/toolsrc/src/vcpkg/commands.cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp index 77f0a20f6..6fd123b7c 100644 --- a/toolsrc/src/vcpkg/commands.cache.cpp +++ b/toolsrc/src/vcpkg/commands.cache.cpp @@ -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()) diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp index 1c98d1d83..dce294004 100644 --- a/toolsrc/src/vcpkg/commands.ci.cpp +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -1,6 +1,5 @@ #include "pch.h" -#include <vcpkg/base/chrono.h> #include <vcpkg/base/files.h> #include <vcpkg/base/system.h> #include <vcpkg/base/util.h> @@ -10,7 +9,6 @@ #include <vcpkg/help.h> #include <vcpkg/input.h> #include <vcpkg/install.h> -#include <vcpkg/paragraphs.h> #include <vcpkg/vcpkglib.h> namespace vcpkg::Commands::CI @@ -19,43 +17,92 @@ namespace vcpkg::Commands::CI using Dependencies::InstallPlanAction; using Dependencies::InstallPlanType; - static std::vector<PackageSpec> load_all_package_specs(Files::Filesystem& fs, - const fs::path& ports_directory, - const Triplet& triplet) + static Install::InstallSummary run_ci_on_triplet(const Triplet& triplet, + const VcpkgPaths& paths, + const std::vector<std::string>& ports, + const std::set<std::string>& exclusions_set) { - auto ports = Paragraphs::load_all_ports(fs, ports_directory); - return Util::fmap(ports, [&](auto&& control_file) -> PackageSpec { - return PackageSpec::from_name_and_triplet(control_file->core_paragraph->name, triplet) - .value_or_exit(VCPKG_LINE_INFO); - }); - } - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) - { - static const std::string EXAMPLE = Help::create_example_string("ci x64-windows"); - args.check_max_arg_count(1, EXAMPLE); - const Triplet triplet = args.command_arguments.size() == 1 - ? Triplet::from_canonical_name(args.command_arguments.at(0)) - : default_triplet; Input::check_triplet(triplet, paths); - args.check_and_get_optional_command_arguments({}); - const std::vector<PackageSpec> specs = load_all_package_specs(paths.get_filesystem(), paths.ports, triplet); + + const std::vector<PackageSpec> specs = PackageSpec::to_package_specs(ports, triplet); StatusParagraphs status_db = database_load_check(paths); const auto& paths_port_file = Dependencies::PathsPortFile(paths); std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths_port_file, specs, status_db); + + for (InstallPlanAction& plan : install_plan) + { + if (Util::Sets::contains(exclusions_set, plan.spec.name())) + { + plan.plan_type = InstallPlanType::EXCLUDED; + } + } + Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty"); const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO, Build::AllowDownloads::YES}; const std::vector<Dependencies::AnyAction> action_plan = - Util::fmap(install_plan, [](InstallPlanAction& install_action) { + Util::fmap(install_plan, [&install_plan_options](InstallPlanAction& install_action) { + install_action.build_options = install_plan_options; return Dependencies::AnyAction(std::move(install_action)); }); - Install::perform_and_exit_ex( - action_plan, install_plan_options, Install::KeepGoing::YES, Install::PrintSummary::YES, paths, status_db); + return Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db); + } + + struct TripletAndSummary + { + Triplet triplet; + 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) + { + 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()); + + std::vector<Triplet> triplets; + for (const std::string& triplet : args.command_arguments) + { + triplets.push_back(Triplet::from_canonical_name(triplet)); + } + + if (triplets.empty()) + { + triplets.push_back(default_triplet); + } + + const std::vector<std::string> ports = Install::get_all_port_names(paths); + std::vector<TripletAndSummary> results; + for (const Triplet& triplet : triplets) + { + Install::InstallSummary summary = run_ci_on_triplet(triplet, paths, ports, exclusions_set); + results.push_back({triplet, std::move(summary)}); + } + + for (auto&& result : results) + { + System::println("\nTriplet: %s", result.triplet); + System::println("Total elapsed time: %s", result.summary.total_elapsed_time); + result.summary.print(); + } Checks::exit_success(VCPKG_LINE_INFO); } diff --git a/toolsrc/src/vcpkg/commands.contact.cpp b/toolsrc/src/vcpkg/commands.contact.cpp index 421b8a230..5d62faeea 100644 --- a/toolsrc/src/vcpkg/commands.contact.cpp +++ b/toolsrc/src/vcpkg/commands.contact.cpp @@ -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); diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp index 9d969ea28..15b10c7ea 100644 --- a/toolsrc/src/vcpkg/commands.cpp +++ b/toolsrc/src/vcpkg/commands.cpp @@ -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}, diff --git a/toolsrc/src/vcpkg/commands.create.cpp b/toolsrc/src/vcpkg/commands.create.cpp index 85763f25e..c7183d257 100644 --- a/toolsrc/src/vcpkg/commands.create.cpp +++ b/toolsrc/src/vcpkg/commands.create.cpp @@ -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); diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp index 89c7e0c7f..bb300d96e 100644 --- a/toolsrc/src/vcpkg/commands.dependinfo.cpp +++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp @@ -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); diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp index 668ec9b1a..e40e394fb 100644 --- a/toolsrc/src/vcpkg/commands.edit.cpp +++ b/toolsrc/src/vcpkg/commands.edit.cpp @@ -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,10 +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 std::unordered_set<std::string> 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; @@ -95,7 +90,7 @@ namespace vcpkg::Commands::Edit } const fs::path env_editor = *it; - if (options.find(OPTION_BUILDTREES) != options.cend()) + if (Util::Sets::contains(options.switches, OPTION_BUILDTREES)) { const auto buildtrees_current_dir = paths.buildtrees / port_name; diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp index 9f8ae5207..98b5aced9 100644 --- a/toolsrc/src/vcpkg/commands.env.cpp +++ b/toolsrc/src/vcpkg/commands.env.cpp @@ -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); diff --git a/toolsrc/src/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/commands.hash.cpp index 4bc58b509..a5940ea1e 100644 --- a/toolsrc/src/vcpkg/commands.hash.cpp +++ b/toolsrc/src/vcpkg/commands.hash.cpp @@ -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) { diff --git a/toolsrc/src/vcpkg/commands.import.cpp b/toolsrc/src/vcpkg/commands.import.cpp index 119aee022..24394207b 100644 --- a/toolsrc/src/vcpkg/commands.import.cpp +++ b/toolsrc/src/vcpkg/commands.import.cpp @@ -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]); diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index 0d185476e..31b9ec722 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -93,9 +93,9 @@ namespace vcpkg::Commands::Integrate </package> )"; - std::string content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); - content = std::regex_replace(content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string()); - content = std::regex_replace(content, std::regex("@VERSION@"), nupkg_version); + std::string content = Strings::replace_all(CONTENT_TEMPLATE, "@NUGET_ID@", nuget_id); + content = Strings::replace_all(std::move(content), "@VCPKG_DIR@", vcpkg_root_dir.string()); + content = Strings::replace_all(std::move(content), "@VERSION@", nupkg_version); return content; } @@ -308,7 +308,7 @@ CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", System::println(System::Color::success, "Created nupkg: %s", nuget_package.string()); auto source_path = buildsystems_dir.u8string(); - source_path = std::regex_replace(source_path, std::regex("`"), "``"); + source_path = Strings::replace_all(std::move(source_path), "`", "``"); System::println(R"( With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste: @@ -326,24 +326,42 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console " vcpkg integrate remove Remove user-wide integration\n" " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"; + namespace Subcommand + { + static const std::string INSTALL = "install"; + static const std::string REMOVE = "remove"; + static const std::string PROJECT = "project"; + } + + static std::vector<std::string> valid_arguments(const VcpkgPaths&) + { + return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT}; + } + + const CommandStructure COMMAND_STRUCTURE = { + Strings::format("Commands:\n" + "%s", + INTEGRATE_COMMAND_HELPSTRING), + 1, + 1, + {}, + &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] == "install") + if (args.command_arguments[0] == Subcommand::INSTALL) { return integrate_install(paths); } - if (args.command_arguments[0] == "remove") + if (args.command_arguments[0] == Subcommand::REMOVE) { return integrate_remove(paths.get_filesystem()); } - if (args.command_arguments[0] == "project") + if (args.command_arguments[0] == Subcommand::PROJECT) { return integrate_project(paths); } diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp index d0e8e00b4..1f2387843 100644 --- a/toolsrc/src/vcpkg/commands.list.cpp +++ b/toolsrc/src/vcpkg/commands.list.cpp @@ -24,14 +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 std::unordered_set<std::string> 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); @@ -52,7 +61,7 @@ namespace vcpkg::Commands::List { for (const StatusParagraph* status_paragraph : installed_packages) { - do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + do_print(*status_paragraph, Util::Sets::contains(options.switches, OPTION_FULLDESC)); } } else @@ -66,7 +75,7 @@ namespace vcpkg::Commands::List continue; } - do_print(*status_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + do_print(*status_paragraph, Util::Sets::contains(options.switches, OPTION_FULLDESC)); } } diff --git a/toolsrc/src/vcpkg/commands.owns.cpp b/toolsrc/src/vcpkg/commands.owns.cpp index 69079e829..52249187b 100644 --- a/toolsrc/src/vcpkg/commands.owns.cpp +++ b/toolsrc/src/vcpkg/commands.owns.cpp @@ -23,15 +23,20 @@ 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); - StatusParagraphs status_db = database_load_check(paths); + const StatusParagraphs status_db = database_load_check(paths); search_file(paths, args.command_arguments[0], status_db); Checks::exit_success(VCPKG_LINE_INFO); } diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp index a8c043751..0277c8bdb 100644 --- a/toolsrc/src/vcpkg/commands.portsdiff.cpp +++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp @@ -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(); diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp index 3ba8707de..01291ddfb 100644 --- a/toolsrc/src/vcpkg/commands.search.cpp +++ b/toolsrc/src/vcpkg/commands.search.cpp @@ -79,18 +79,29 @@ 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 std::unordered_set<std::string> 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); - if (options.find(OPTION_GRAPH) != options.cend()) + if (Util::Sets::contains(options.switches, OPTION_GRAPH)) { const std::string graph_as_string = create_graph_as_string(source_paragraphs); System::println(graph_as_string); @@ -101,12 +112,10 @@ namespace vcpkg::Commands::Search { for (const auto& source_control_file : source_paragraphs) { - do_print(*source_control_file->core_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + do_print(*source_control_file->core_paragraph, full_description); for (auto&& feature_paragraph : source_control_file->feature_paragraphs) { - do_print(source_control_file->core_paragraph->name, - *feature_paragraph, - options.find(OPTION_FULLDESC) != options.cend()); + do_print(source_control_file->core_paragraph->name, *feature_paragraph, full_description); } } } @@ -120,10 +129,10 @@ namespace vcpkg::Commands::Search { auto&& sp = *source_control_file->core_paragraph; - bool contains_name = icontains(sp.name, args_zero); + const bool contains_name = icontains(sp.name, args_zero); if (contains_name || icontains(sp.description, args_zero)) { - do_print(sp, options.find(OPTION_FULLDESC) != options.cend()); + do_print(sp, full_description); } for (auto&& feature_paragraph : source_control_file->feature_paragraphs) @@ -131,7 +140,7 @@ namespace vcpkg::Commands::Search if (contains_name || icontains(feature_paragraph->name, args_zero) || icontains(feature_paragraph->description, args_zero)) { - do_print(sp.name, *feature_paragraph, options.find(OPTION_FULLDESC) != options.cend()); + do_print(sp.name, *feature_paragraph, full_description); } } } diff --git a/toolsrc/src/vcpkg/commands.version.cpp b/toolsrc/src/vcpkg/commands.version.cpp index 403c355b5..3f44cf1a2 100644 --- a/toolsrc/src/vcpkg/commands.version.cpp +++ b/toolsrc/src/vcpkg/commands.version.cpp @@ -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" diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp index 02b2ec785..6f599afd4 100644 --- a/toolsrc/src/vcpkg/dependencies.cpp +++ b/toolsrc/src/vcpkg/dependencies.cpp @@ -112,33 +112,41 @@ namespace vcpkg::Dependencies std::vector<PackageSpec> AnyParagraph::dependencies(const Triplet& triplet) const { - auto to_package_specs = [&](const std::vector<std::string>& dependencies_as_string) { - return Util::fmap(dependencies_as_string, [&](const std::string s) { - return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO); - }); - }; - - if (auto p = this->status_paragraph.get()) + if (const auto p = this->status_paragraph.get()) { - return to_package_specs(p->package.depends); + return PackageSpec::to_package_specs(p->package.depends, triplet); } - if (auto p = this->binary_control_file.get()) + if (const auto p = this->binary_control_file.get()) { auto deps = Util::fmap_flatten(p->features, [](const BinaryParagraph& pgh) { return pgh.depends; }); - deps.insert(deps.end(), p->core_paragraph.depends.begin(), p->core_paragraph.depends.end()); - return to_package_specs(deps); + deps.insert(deps.end(), p->core_paragraph.depends.cbegin(), p->core_paragraph.depends.cend()); + return PackageSpec::to_package_specs(deps, triplet); } - if (auto p = this->source_paragraph.get()) + if (const auto p = this->source_paragraph.get()) { - return to_package_specs(filter_dependencies(p->depends, triplet)); + return PackageSpec::to_package_specs(filter_dependencies(p->depends, triplet), triplet); } Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot get dependencies because there was none of: source/binary/status paragraphs"); } + std::string to_output_string(RequestType request_type, + const CStringView s, + const Build::BuildPackageOptions& options) + { + const char* const from_head = options.use_head_version == Build::UseHeadVersion::YES ? " (from HEAD)" : ""; + + switch (request_type) + { + case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s", s, from_head); + case RequestType::USER_REQUESTED: return Strings::format(" %s%s", s, from_head); + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + std::string to_output_string(RequestType request_type, const CStringView s) { switch (request_type) @@ -611,10 +619,38 @@ namespace vcpkg::Dependencies { Cluster& spec_cluster = graph.get(spec.spec()); spec_cluster.request_type = RequestType::USER_REQUESTED; - auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); + if (spec.feature() == "*") + { + if (auto p_scf = spec_cluster.source_control_file.value_or(nullptr)) + { + for (auto&& feature : p_scf->feature_paragraphs) + { + auto res = mark_plus(feature->name, spec_cluster, graph, graph_plan); + + Checks::check_exit(VCPKG_LINE_INFO, + res == MarkPlusResult::SUCCESS, + "Error: Unable to locate feature %s", + spec); + } + + auto res = mark_plus("core", spec_cluster, graph, graph_plan); + + Checks::check_exit( + VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); + } + else + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", spec.spec()); + } + } + else + { + auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); - Checks::check_exit( - VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); + Checks::check_exit( + VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); + } graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster}); } diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 5e27d4126..9b86863eb 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -4,6 +4,7 @@ #include <vcpkg/base/util.h> #include <vcpkg/commands.h> #include <vcpkg/dependencies.h> +#include <vcpkg/export.h> #include <vcpkg/export.ifw.h> #include <vcpkg/help.h> #include <vcpkg/input.h> @@ -44,12 +45,12 @@ namespace vcpkg::Export </package> )"; - std::string nuspec_file_content = std::regex_replace(CONTENT_TEMPLATE, std::regex("@NUGET_ID@"), nuget_id); - nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version); + std::string nuspec_file_content = Strings::replace_all(CONTENT_TEMPLATE, "@NUGET_ID@", nuget_id); + nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content), "@VERSION@", nupkg_version); nuspec_file_content = - std::regex_replace(nuspec_file_content, std::regex("@RAW_EXPORTED_DIR@"), raw_exported_dir); + Strings::replace_all(std::move(nuspec_file_content), "@RAW_EXPORTED_DIR@", raw_exported_dir); nuspec_file_content = - std::regex_replace(nuspec_file_content, std::regex("@TARGETS_REDIRECT_PATH@"), targets_redirect_path); + Strings::replace_all(std::move(nuspec_file_content), "@TARGETS_REDIRECT_PATH@", targets_redirect_path); return nuspec_file_content; } @@ -68,6 +69,8 @@ namespace vcpkg::Export { static constexpr std::array<ExportPlanType, 2> ORDER = {ExportPlanType::ALREADY_BUILT, ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT}; + static constexpr Build::BuildPackageOptions build_options = {Build::UseHeadVersion::NO, + Build::AllowDownloads::YES}; for (const ExportPlanType plan_type : ORDER) { @@ -80,7 +83,7 @@ namespace vcpkg::Export std::vector<const ExportPlanAction*> cont = it->second; std::sort(cont.begin(), cont.end(), &ExportPlanAction::compare_by_name); const std::string as_string = Strings::join("\n", cont, [](const ExportPlanAction* p) { - return Dependencies::to_output_string(p->request_type, p->spec.to_string()); + return Dependencies::to_output_string(p->request_type, p->spec.to_string(), build_options); }); switch (plan_type) @@ -255,51 +258,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, "Specify the remote repository URL for the online installer"}, + {OPTION_IFW_PACKAGES_DIR_PATH, "Specify the temporary directory path for the repacked packages"}, + {OPTION_IFW_REPOSITORY_DIR_PATH, "Specify the directory path for the exported repository"}, + {OPTION_IFW_CONFIG_FILE_PATH, "Specify the temporary file path for the installer configuration"}, + {OPTION_IFW_INSTALLER_FILE_PATH, "Specify the file path for the exported installer"}, + }}; + + const CommandStructure vcpkg::Export::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(); @@ -311,7 +320,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); } diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index 2a0578211..c83f0277b 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -2,15 +2,68 @@ #include <vcpkg/base/system.h> #include <vcpkg/commands.h> +#include <vcpkg/export.h> +#include <vcpkg/help.h> +#include <vcpkg/install.h> +#include <vcpkg/remove.h> namespace vcpkg::Help { - void help_topics() + struct Topic + { + using topic_function = void (*)(const VcpkgPaths& paths); + + constexpr Topic(CStringView n, topic_function fn) : name(n), print(fn) {} + + CStringView name; + topic_function print; + }; + + template<const CommandStructure& S> + static void command_topic_fn(const VcpkgPaths&) + { + display_usage(S); + } + + static void integrate_topic_fn(const VcpkgPaths&) + { + System::print("Commands:\n" + "%s", + Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING); + } + + static void help_topics(const VcpkgPaths&); + + const CommandStructure COMMAND_STRUCTURE = { + Help::create_example_string("help"), + 0, + 1, + {}, + nullptr, + }; + + static constexpr std::array<Topic, 12> topics = {{ + {"create", command_topic_fn<Commands::Create::COMMAND_STRUCTURE>}, + {"edit", command_topic_fn<Commands::Edit::COMMAND_STRUCTURE>}, + {"env", command_topic_fn<Commands::Env::COMMAND_STRUCTURE>}, + {"export", command_topic_fn<Export::COMMAND_STRUCTURE>}, + {"help", command_topic_fn<Help::COMMAND_STRUCTURE>}, + {"install", command_topic_fn<Install::COMMAND_STRUCTURE>}, + {"integrate", integrate_topic_fn}, + {"list", command_topic_fn<Commands::List::COMMAND_STRUCTURE>}, + {"owns", command_topic_fn<Commands::Owns::COMMAND_STRUCTURE>}, + {"remove", command_topic_fn<Remove::COMMAND_STRUCTURE>}, + {"search", command_topic_fn<Commands::Search::COMMAND_STRUCTURE>}, + {"topics", help_topics}, + }}; + + static void help_topics(const VcpkgPaths&) { System::println("Available help topics:\n" " triplet\n" - " integrate\n" - " export"); + " integrate" + "%s", + Strings::join("", topics, [](const Topic& topic) { return std::string("\n ") + topic.name; })); } void help_topic_valid_triplet(const VcpkgPaths& paths) @@ -22,21 +75,6 @@ namespace vcpkg::Help } } - void help_topic_export() - { - System::println("Summary:\n" - " vcpkg export [options] <pkgs>...\n" - "\n" - "Options:\n" - " --7zip Export to a 7zip (.7z) file\n" - " --dry-run Do not actually export\n" - " --nuget Export a NuGet package\n" - " --nuget-id=<id> Specify the id for the exported NuGet package\n" - " --nuget-version=<ver> Specify the version for the exported NuGet package\n" - " --raw Export to an uncompressed directory\n" - " --zip Export to a zip file"); - } - void print_usage() { System::println( @@ -86,15 +124,9 @@ namespace vcpkg::Help return cs; } - void print_example(const std::string& command_and_arguments) - { - System::println(create_example_string(command_and_arguments)); - } - 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()) { @@ -105,27 +137,18 @@ namespace vcpkg::Help if (topic == "triplet" || topic == "triplets" || topic == "triple") { help_topic_valid_triplet(paths); + Checks::exit_success(VCPKG_LINE_INFO); } - else if (topic == "export") - { - help_topic_export(); - } - else if (topic == "integrate") - { - System::print("Commands:\n" - "%s", - Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING); - } - else if (topic == "topics") - { - help_topics(); - } - else + + auto it_topic = Util::find_if(topics, [&](const Topic& t) { return t.name == topic; }); + if (it_topic != topics.end()) { - System::println(System::Color::error, "Error: unknown topic %s", topic); - help_topics(); - Checks::exit_fail(VCPKG_LINE_INFO); + it_topic->print(paths); + Checks::exit_success(VCPKG_LINE_INFO); } - Checks::exit_success(VCPKG_LINE_INFO); + + System::println(System::Color::error, "Error: unknown topic %s", topic); + help_topics(paths); + Checks::exit_fail(VCPKG_LINE_INFO); } } diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index adbd8c943..20ffd3164 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -250,7 +250,6 @@ namespace vcpkg::Install BuildResult perform_install_plan_action(const VcpkgPaths& paths, const InstallPlanAction& action, - const Build::BuildPackageOptions& build_package_options, StatusParagraphs& status_db) { const InstallPlanType& plan_type = action.plan_type; @@ -259,7 +258,7 @@ namespace vcpkg::Install GlobalState::feature_packages ? action.displayname() : display_name; const bool is_user_requested = action.request_type == RequestType::USER_REQUESTED; - const bool use_head_version = to_bool(build_package_options.use_head_version); + const bool use_head_version = to_bool(action.build_options.use_head_version); if (plan_type == InstallPlanType::ALREADY_INSTALLED) { @@ -285,7 +284,7 @@ namespace vcpkg::Install *action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO), action.spec.triplet(), paths.port_dir(action.spec), - build_package_options, + action.build_options, action.feature_list}; return Build::build_package(paths, build_config, status_db); } @@ -295,7 +294,7 @@ namespace vcpkg::Install action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO), action.spec.triplet(), paths.port_dir(action.spec), - build_package_options}; + action.build_options}; return Build::build_package(paths, build_config, status_db); } }(); @@ -342,16 +341,23 @@ namespace vcpkg::Install } } + if (plan_type == InstallPlanType::EXCLUDED) + { + System::println(System::Color::warning, "Package %s is excluded", display_name); + return BuildResult::EXCLUDED; + } + Checks::unreachable(VCPKG_LINE_INFO); } - static void print_plan(const std::vector<AnyAction>& action_plan, bool is_recursive) + static void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive) { std::vector<const RemovePlanAction*> remove_plans; std::vector<const InstallPlanAction*> rebuilt_plans; std::vector<const InstallPlanAction*> only_install_plans; std::vector<const InstallPlanAction*> new_plans; std::vector<const InstallPlanAction*> already_installed_plans; + std::vector<const InstallPlanAction*> excluded; const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { if (auto iplan = package.install_plan.get()) @@ -382,6 +388,7 @@ namespace vcpkg::Install already_installed_plans.emplace_back(install_action); break; case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; + case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break; default: Checks::unreachable(VCPKG_LINE_INFO); } } @@ -397,39 +404,40 @@ namespace vcpkg::Install std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name); + + static auto actions_to_output_string = [](const std::vector<const InstallPlanAction*>& v) { + return Strings::join("\n", v, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname(), p->build_options); + }); + }; + + if (excluded.size() > 0) + { + System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded)); + } if (already_installed_plans.size() > 0) { - const std::string already_string = - Strings::join("\n", already_installed_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages are already installed:\n%s", already_string); + System::println("The following packages are already installed:\n%s", + actions_to_output_string(already_installed_plans)); } if (rebuilt_plans.size() > 0) { - const std::string rebuilt_string = Strings::join("\n", rebuilt_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be rebuilt:\n%s", rebuilt_string); + System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans)); } if (new_plans.size() > 0) { - const std::string new_string = Strings::join("\n", new_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be built and installed:\n%s", new_string); + System::println("The following packages will be built and installed:\n%s", + actions_to_output_string(new_plans)); } if (only_install_plans.size() > 0) { - const std::string only_install_string = - Strings::join("\n", only_install_plans, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname()); - }); - System::println("The following packages will be directly installed:\n%s", only_install_string); + System::println("The following packages will be directly installed:\n%s", + actions_to_output_string(only_install_plans)); } if (has_non_user_requested_packages) @@ -444,15 +452,40 @@ namespace vcpkg::Install } } - void perform_and_exit_ex(const std::vector<AnyAction>& action_plan, - const Build::BuildPackageOptions& install_plan_options, - const KeepGoing keep_going, - const PrintSummary print_summary, - const VcpkgPaths& paths, - StatusParagraphs& status_db) + void InstallSummary::print() const { - std::vector<BuildResult> results; - std::vector<std::string> timing; + System::println("RESULTS"); + + for (const SpecSummary& result : this->results) + { + System::println(" %s: %s: %s", result.spec, Build::to_string(result.result), result.timing); + } + + std::map<BuildResult, int> summary; + for (const BuildResult& v : Build::BUILD_RESULT_VALUES) + { + summary[v] = 0; + } + + for (const SpecSummary& r : this->results) + { + summary[r.result]++; + } + + System::println("\nSUMMARY"); + for (const std::pair<const BuildResult, int>& entry : summary) + { + System::println(" %s: %d", Build::to_string(entry.first), entry.second); + } + } + + InstallSummary perform(const std::vector<AnyAction>& action_plan, + const KeepGoing keep_going, + const VcpkgPaths& paths, + StatusParagraphs& status_db) + { + std::vector<SpecSummary> results; + const auto timer = Chrono::ElapsedTime::create_started(); size_t counter = 0; const size_t package_count = action_plan.size(); @@ -462,23 +495,22 @@ namespace vcpkg::Install const auto build_timer = Chrono::ElapsedTime::create_started(); counter++; - const std::string display_name = action.spec().to_string(); + const PackageSpec& spec = action.spec(); + const std::string display_name = spec.to_string(); System::println("Starting package %d/%d: %s", counter, package_count, display_name); - timing.push_back("0"); - results.push_back(BuildResult::NULLVALUE); + results.push_back(SpecSummary{spec}); if (const auto install_action = action.install_plan.get()) { - const BuildResult result = - perform_install_plan_action(paths, *install_action, install_plan_options, status_db); + const BuildResult result = perform_install_plan_action(paths, *install_action, status_db); if (result != BuildResult::SUCCEEDED && keep_going == KeepGoing::NO) { System::println(Build::create_user_troubleshooting_message(install_action->spec)); Checks::exit_fail(VCPKG_LINE_INFO); } - results.back() = result; + results.back().result = result; } else if (const auto remove_action = action.remove_plan.get()) { @@ -490,38 +522,11 @@ namespace vcpkg::Install Checks::unreachable(VCPKG_LINE_INFO); } - timing.back() = build_timer.to_string(); + results.back().timing = build_timer.to_string(); System::println("Elapsed time for package %s: %s", display_name, build_timer.to_string()); } - System::println("Total time taken: %s", timer.to_string()); - - if (print_summary == PrintSummary::YES) - { - for (size_t i = 0; i < results.size(); i++) - { - System::println("%s: %s: %s", action_plan[i].spec(), Build::to_string(results[i]), timing[i]); - } - - std::map<BuildResult, int> summary; - for (const BuildResult& v : Build::BUILD_RESULT_VALUES) - { - summary[v] = 0; - } - - for (const BuildResult& r : results) - { - summary[r]++; - } - - System::println("\n\nSUMMARY"); - for (const std::pair<const BuildResult, int>& entry : summary) - { - System::println(" %s: %d", Build::to_string(entry.first), entry.second); - } - } - - Checks::exit_success(VCPKG_LINE_INFO); + return InstallSummary{results, timer.to_string()}; } static const std::string OPTION_DRY_RUN = "--dry-run"; @@ -530,16 +535,16 @@ 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; - static std::vector<std::string> valid_arguments(const VcpkgPaths& paths) + std::vector<std::string> get_all_port_names(const VcpkgPaths& paths) { auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports); @@ -548,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, - &valid_arguments, + {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) @@ -576,14 +579,11 @@ namespace vcpkg::Install } } - const std::unordered_set<std::string> 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 = options.find(OPTION_DRY_RUN) != options.cend(); - const bool use_head_version = options.find(OPTION_USE_HEAD_VERSION) != options.cend(); - const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend(); - const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); - const KeepGoing keep_going = to_keep_going(options.find(OPTION_KEEP_GOING) != options.cend()); - const PrintSummary print_summary = to_print_summary(keep_going == KeepGoing::YES); + 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)); + const bool is_recursive = Util::Sets::contains(options.switches, (OPTION_RECURSE)); + const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING)); // create the plan StatusParagraphs status_db = database_load_check(paths); @@ -591,11 +591,12 @@ namespace vcpkg::Install const Build::BuildPackageOptions install_plan_options = {Build::to_use_head_version(use_head_version), Build::to_allow_downloads(!no_downloads)}; + // Note: action_plan will hold raw pointers to SourceControlFiles from this map + std::unordered_map<std::string, SourceControlFile> scf_map; std::vector<AnyAction> action_plan; if (GlobalState::feature_packages) { - std::unordered_map<std::string, SourceControlFile> scf_map; auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); for (auto&& port : all_ports) { @@ -613,6 +614,16 @@ namespace vcpkg::Install install_plan, [](InstallPlanAction& install_action) { return AnyAction(std::move(install_action)); }); } + for (auto&& action : action_plan) + { + if (auto p_install = action.install_plan.get()) + { + p_install->build_options = install_plan_options; + if (p_install->request_type != RequestType::USER_REQUESTED) + p_install->build_options.use_head_version = Build::UseHeadVersion::NO; + } + } + // install plan will be empty if it is already installed - need to change this at status paragraph part Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty(), "Install plan cannot be empty"); @@ -634,8 +645,17 @@ namespace vcpkg::Install Checks::exit_success(VCPKG_LINE_INFO); } - perform_and_exit_ex(action_plan, install_plan_options, keep_going, print_summary, paths, status_db); + const InstallSummary summary = perform(action_plan, keep_going, paths, status_db); + + System::println("\nTotal elapsed time: %s", summary.total_elapsed_time); + + if (keep_going == KeepGoing::YES) + { + summary.print(); + } Checks::exit_success(VCPKG_LINE_INFO); } + + SpecSummary::SpecSummary(const PackageSpec& spec) : spec(spec), result(BuildResult::NULLVALUE), timing("0") {} } diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp index 6e40d70f5..eeb9981af 100644 --- a/toolsrc/src/vcpkg/packagespec.cpp +++ b/toolsrc/src/vcpkg/packagespec.cpp @@ -92,6 +92,14 @@ namespace vcpkg return p; } + std::vector<PackageSpec> PackageSpec::to_package_specs(const std::vector<std::string>& ports, + const Triplet& triplet) + { + return Util::fmap(ports, [&](const std::string s) { + return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO); + }); + } + const std::string& PackageSpec::name() const { return this->m_name; } const Triplet& PackageSpec::triplet() const { return this->m_triplet; } diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 2ec32855a..7f40fb16e 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -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> REMOVE_SWITCHES = { - OPTION_PURGE, - OPTION_NO_PURGE, - OPTION_RECURSE, - OPTION_DRY_RUN, - OPTION_OUTDATED, - }; - static const std::array<std::string, 0> REMOVE_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, - REMOVE_SWITCHES, - REMOVE_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 std::unordered_set<std::string> 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 (options.find(OPTION_OUTDATED) != options.cend()) + 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,26 +217,30 @@ 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) Input::check_triplet(spec.triplet(), paths); } - const bool no_purge_was_passed = options.find(OPTION_NO_PURGE) != options.end(); - const bool purge_was_passed = options.find(OPTION_PURGE) != options.end(); + const bool no_purge_was_passed = Util::Sets::contains(options.switches, OPTION_NO_PURGE); + const bool purge_was_passed = Util::Sets::contains(options.switches, OPTION_PURGE); 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); - const bool is_recursive = options.find(OPTION_RECURSE) != options.cend(); - const bool dry_run = options.find(OPTION_DRY_RUN) != options.cend(); + const bool is_recursive = Util::Sets::contains(options.switches, OPTION_RECURSE); + const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN); const std::vector<RemovePlanAction> remove_plan = Dependencies::create_remove_plan(specs, status_db); Checks::check_exit(VCPKG_LINE_INFO, !remove_plan.empty(), "Remove plan cannot be empty"); diff --git a/toolsrc/src/vcpkg/statusparagraphs.cpp b/toolsrc/src/vcpkg/statusparagraphs.cpp index 647ed6d3f..d4afc0427 100644 --- a/toolsrc/src/vcpkg/statusparagraphs.cpp +++ b/toolsrc/src/vcpkg/statusparagraphs.cpp @@ -10,22 +10,6 @@ namespace vcpkg StatusParagraphs::StatusParagraphs(std::vector<std::unique_ptr<StatusParagraph>>&& ps) : paragraphs(std::move(ps)){}; - StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) const - { - return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { - const PackageSpec& spec = pgh->package.spec; - return spec.name() == name && spec.triplet() == triplet; - }); - } - - StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) - { - return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { - const PackageSpec& spec = pgh->package.spec; - return spec.name() == name && spec.triplet() == triplet; - }); - } - std::vector<std::unique_ptr<StatusParagraph>*> StatusParagraphs::find_all(const std::string& name, const Triplet& triplet) { @@ -50,6 +34,16 @@ namespace vcpkg }); } + StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, + const Triplet& triplet, + const std::string& feature) const + { + return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { + const PackageSpec& spec = pgh->package.spec; + return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature; + }); + } + StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name, const Triplet& triplet) const { diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp index 168949bc1..61a3f89c5 100644 --- a/toolsrc/src/vcpkg/update.cpp +++ b/toolsrc/src/vcpkg/update.cpp @@ -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); diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp index 209f6a3f2..671a89892 100644 --- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp +++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp @@ -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; + const size_t actual_arg_count = command_arguments.size(); + + if (command_structure.minimum_arity == command_structure.maximum_arity) + { + 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 (const std::string& option : valid_switches) + for (auto&& option : command_structure.options.switches) { - 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()) { // 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,38 @@ namespace vcpkg { 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 <t>"); - System::println(" --vcpkg-root <path>"); + System::println(); + failed = true; + } + if (failed) + { + display_usage(command_structure); 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 + void display_usage(const CommandStructure& command_structure) { - 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) + if (!command_structure.example_text.empty()) { - 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); + System::println("%s", command_structure.example_text); } - } - 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("Options:"); + for (auto&& option : command_structure.options.switches) { - 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); + System::println(" %-40s %s", option.name, option.short_help_text); } - } - - 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) + for (auto&& option : command_structure.options.settings) { - 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); + System::println(" %-40s %s", (option.name + "=..."), option.short_help_text); } + System::println(" %-40s %s", "--triplet <t>", "Set the default triplet for unqualified packages"); + System::println(" %-40s %s", + "--vcpkg-root <path>", + "Specify the vcpkg directory to use instead of current directory or tool directory"); } } diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index 90f9cb00b..a553f4199 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -62,6 +62,23 @@ namespace vcpkg return nullopt; } + static std::vector<std::string> keep_data_lines(const std::string& data_blob) + { + static const std::regex DATA_LINE_REGEX(R"(<sol>::(.+?)(?=::<eol>))"); + + std::vector<std::string> data_lines; + + const std::sregex_iterator it(data_blob.cbegin(), data_blob.cend(), DATA_LINE_REGEX); + const std::sregex_iterator end; + for (std::sregex_iterator i = it; i != end; ++i) + { + const std::smatch match = *i; + data_lines.push_back(match[1].str()); + } + + return data_lines; + } + static fs::path fetch_dependency(const fs::path& scripts_folder, const std::string& tool_name, const fs::path& expected_downloaded_path, @@ -74,25 +91,15 @@ namespace vcpkg tool_name, version_as_string); const fs::path script = scripts_folder / "fetchDependency.ps1"; - const auto install_cmd = - System::create_powershell_script_cmd(script, Strings::format("-Dependency %s", tool_name)); - const System::ExitCodeAndOutput rc = System::cmd_execute_and_capture_output(install_cmd); - if (rc.exit_code) - { - System::println(System::Color::error, - "Launching powershell failed or was denied when trying to fetch %s version %s.\n" - "(No sufficient installed version was found)", - tool_name, - version_as_string); - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_property("error", "powershell install failed"); - locked_metrics->track_property("dependency", tool_name); - } - Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code); - } + const std::string title = "Fetching %s version %s (No sufficient installed version was found)"; + const std::string output = + System::powershell_execute_and_capture_output(title, script, Strings::format("-Dependency %s", tool_name)); + + const std::vector<std::string> dependency_path = keep_data_lines(output); + Checks::check_exit( + VCPKG_LINE_INFO, dependency_path.size() == 1, "Expected dependency path, but got %s", output); - const fs::path actual_downloaded_path = Strings::trimmed(rc.output); + const fs::path actual_downloaded_path = Strings::trim(std::string{dependency_path.at(0)}); std::error_code ec; const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec); Checks::check_exit(VCPKG_LINE_INFO, @@ -105,10 +112,10 @@ namespace vcpkg static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array<int, 3> EXPECTED_VERSION = {3, 9, 3}; + static constexpr std::array<int, 3> EXPECTED_VERSION = {3, 9, 5}; static const std::string VERSION_CHECK_ARGUMENTS = "--version"; - const fs::path downloaded_copy = downloads_folder / "cmake-3.9.3-win32-x86" / "bin" / "cmake.exe"; + const fs::path downloaded_copy = downloads_folder / "cmake-3.9.5-win32-x86" / "bin" / "cmake.exe"; const std::vector<fs::path> from_path = Files::find_from_PATH("cmake"); std::vector<fs::path> candidate_paths; @@ -131,9 +138,9 @@ namespace vcpkg fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array<int, 3> EXPECTED_VERSION = {4, 3, 0}; + static constexpr std::array<int, 3> EXPECTED_VERSION = {4, 4, 0}; - const fs::path downloaded_copy = downloads_folder / "nuget-4.3.0" / "nuget.exe"; + const fs::path downloaded_copy = downloads_folder / "nuget-4.4.0" / "nuget.exe"; const std::vector<fs::path> from_path = Files::find_from_PATH("nuget"); std::vector<fs::path> candidate_paths; @@ -151,10 +158,10 @@ namespace vcpkg fs::path get_git_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array<int, 3> EXPECTED_VERSION = {2, 14, 1}; + static constexpr std::array<int, 3> EXPECTED_VERSION = {2, 15, 0}; static const std::string VERSION_CHECK_ARGUMENTS = "--version"; - const fs::path downloaded_copy = downloads_folder / "MinGit-2.14.1-32-bit" / "cmd" / "git.exe"; + const fs::path downloaded_copy = downloads_folder / "MinGit-2.15.0-32-bit" / "cmd" / "git.exe"; const std::vector<fs::path> from_path = Files::find_from_PATH("git"); std::vector<fs::path> candidate_paths; @@ -324,20 +331,34 @@ namespace vcpkg static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths) { const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; - const std::string cmd = System::create_powershell_script_cmd(script); - const System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd); - Checks::check_exit( - VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect Visual Studio instances"); + const std::string output = + System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script); - const std::vector<std::string> instances_as_strings = Strings::split(ec_data.output, "\n"); - std::vector<VisualStudioInstance> output; + const std::vector<std::string> instances_as_strings = keep_data_lines(output); + Checks::check_exit(VCPKG_LINE_INFO, + !instances_as_strings.empty(), + "Could not detect any Visual Studio instances.\n" + "Powershell script:\n" + " %s\n" + "returned:\n" + "%s", + script.generic_string(), + output); + + std::vector<VisualStudioInstance> instances; for (const std::string& instance_as_string : instances_as_strings) { const std::vector<std::string> split = Strings::split(instance_as_string, "::"); - output.push_back({split.at(3), split.at(2), split.at(1), split.at(0)}); + Checks::check_exit(VCPKG_LINE_INFO, + split.size() == 4, + "Invalid Visual Studio instance format.\n" + "Expected: PreferenceWeight::ReleaseType::Version::PathToVisualStudio\n" + "Actual : %s\n", + instance_as_string); + instances.push_back({split.at(3), split.at(2), split.at(1), split.at(0)}); } - return output; + return instances; } static std::vector<Toolset> find_toolset_instances(const VcpkgPaths& paths) |
