diff --git a/scripts/vcpkgTools.xml b/scripts/vcpkgTools.xml index 0c50f6da5..738c27e2c 100644 --- a/scripts/vcpkgTools.xml +++ b/scripts/vcpkgTools.xml @@ -60,10 +60,10 @@ f477842d0cebefcd6bf9c6d536ab8ea20ec5b0aa967ee963ab6a101aeff9df8742ca600d35f39e2e7158d76d8231f1ed2bef6104dce84d2bf8d6b07d17d706a1 - 4.8.1 + 5.5.1 nuget.exe - https://dist.nuget.org/win-x86-commandline/v4.8.1/nuget.exe - 42cb744338af8decc033a75bce5b4c4df28e102bafc45f9a8ba86d7bc010f5b43ebacae80d7b28c4f85ac900eefc2a349620ae65f27f6ca1c21c53b63b92924b + https://dist.nuget.org/win-x86-commandline/v5.5.1/nuget.exe + 22ea847d8017cd977664d0b13c889cfb13c89143212899a511be217345a4e243d4d8d4099700114a11d26a087e83eb1a3e2b03bdb5e0db48f10403184cd26619 3.1.81 diff --git a/toolsrc/include/vcpkg/base/strings.h b/toolsrc/include/vcpkg/base/strings.h index 425d846df..17bef7675 100644 --- a/toolsrc/include/vcpkg/base/strings.h +++ b/toolsrc/include/vcpkg/base/strings.h @@ -175,6 +175,8 @@ namespace vcpkg::Strings std::vector split(const std::string& s, const char delimiter); + const char* find_first_of(StringView searched, StringView candidates); + std::vector find_all_enclosed(StringView input, StringView left_delim, StringView right_delim); StringView find_exactly_one_enclosed(StringView input, StringView left_tag, StringView right_tag); diff --git a/toolsrc/include/vcpkg/base/system.process.h b/toolsrc/include/vcpkg/base/system.process.h index 0e6a92444..91faa5985 100644 --- a/toolsrc/include/vcpkg/base/system.process.h +++ b/toolsrc/include/vcpkg/base/system.process.h @@ -23,6 +23,18 @@ namespace vcpkg::System const fs::path& cmake_script, const std::vector& pass_variables); + struct CmdLineBuilder + { + CmdLineBuilder& path_arg(const fs::path& p) { return string_arg(p.u8string()); } + CmdLineBuilder& string_arg(StringView s); + std::string extract() noexcept { return std::move(buf); } + + operator ZStringView() const { return buf; } + + private: + std::string buf; + }; + fs::path get_exe_path_of_current_process(); struct ExitCodeAndOutput diff --git a/toolsrc/include/vcpkg/base/util.h b/toolsrc/include/vcpkg/base/util.h index 89f2c51d6..a5b56028b 100644 --- a/toolsrc/include/vcpkg/base/util.h +++ b/toolsrc/include/vcpkg/base/util.h @@ -49,6 +49,19 @@ namespace vcpkg::Util } } + template> + std::vector filter(const Range& xs, Pred&& f) + { + std::vector ret; + + for (auto&& x : xs) + { + if (f(x)) ret.push_back(x); + } + + return ret; + } + template using FmapOut = std::remove_reference_t()(*std::declval().begin()))>; diff --git a/toolsrc/include/vcpkg/binarycaching.h b/toolsrc/include/vcpkg/binarycaching.h index 88f529c22..d343d6c42 100644 --- a/toolsrc/include/vcpkg/binarycaching.h +++ b/toolsrc/include/vcpkg/binarycaching.h @@ -8,6 +8,7 @@ namespace vcpkg::Dependencies { struct InstallPlanAction; + struct ActionPlan; } namespace vcpkg::Build { @@ -27,10 +28,17 @@ namespace vcpkg struct IBinaryProvider { virtual ~IBinaryProvider() = default; - virtual void prefetch() = 0; + /// Gives the BinaryProvider an opportunity to batch any downloading or server communication for executing + /// `plan`. + virtual void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) = 0; + /// Attempts to restore the package referenced by `action` into the packages directory. virtual RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; + /// Called upon a successful build of `action` virtual void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; + /// Called upon a failure during the build of `action` virtual void push_failure(const VcpkgPaths& paths, const std::string& abi_tag, const PackageSpec& spec) = 0; + /// Requests the result of `try_restore()` without actually downloading the package. Used by CI to determine + /// missing packages. virtual RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action, bool purge_tombstones) = 0; @@ -42,4 +50,6 @@ namespace vcpkg View args); ExpectedS> create_binary_provider_from_configs_pure(const std::string& env_string, View args); + + void help_topic_binary_caching(const VcpkgPaths& paths); } diff --git a/toolsrc/include/vcpkg/binarycaching.private.h b/toolsrc/include/vcpkg/binarycaching.private.h new file mode 100644 index 000000000..f1fd046de --- /dev/null +++ b/toolsrc/include/vcpkg/binarycaching.private.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +namespace vcpkg +{ + std::string reformat_version(const std::string& version, const std::string& abi_tag); + + struct NugetReference + { + explicit NugetReference(const Dependencies::InstallPlanAction& action) + : NugetReference(action.spec, + action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO) + .source_control_file->core_paragraph->version, + action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi) + { + } + + NugetReference(const PackageSpec& spec, const std::string& raw_version, const std::string& abi_tag) + : id(spec.dir()), version(reformat_version(raw_version, abi_tag)) + { + } + + std::string id; + std::string version; + + std::string nupkg_filename() const { return Strings::concat(id, '.', version, ".nupkg"); } + }; + + std::string generate_nuspec(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + const NugetReference& ref); + + struct XmlSerializer + { + std::string buf; + int indent = 0; + + XmlSerializer& emit_declaration(); + XmlSerializer& open_tag(StringLiteral sl); + XmlSerializer& start_complex_open_tag(StringLiteral sl); + XmlSerializer& text_attr(StringLiteral name, StringView content); + XmlSerializer& finish_complex_open_tag(); + XmlSerializer& finish_self_closing_complex_tag(); + XmlSerializer& close_tag(StringLiteral sl); + XmlSerializer& text(StringView sv); + XmlSerializer& simple_tag(StringLiteral tag, StringView content); + XmlSerializer& line_break(); + }; + +} \ No newline at end of file diff --git a/toolsrc/include/vcpkg/build.h b/toolsrc/include/vcpkg/build.h index e2e28b08a..804cb6673 100644 --- a/toolsrc/include/vcpkg/build.h +++ b/toolsrc/include/vcpkg/build.h @@ -284,7 +284,8 @@ namespace vcpkg::Build struct AbiInfo { std::unique_ptr pre_build_info; - const Toolset* toolset; + Optional toolset; + Optional triplet_abi; std::string package_abi; Optional abi_tag_file; }; diff --git a/toolsrc/include/vcpkg/vcpkgcmdarguments.h b/toolsrc/include/vcpkg/vcpkgcmdarguments.h index 378aa9703..690245d08 100644 --- a/toolsrc/include/vcpkg/vcpkgcmdarguments.h +++ b/toolsrc/include/vcpkg/vcpkgcmdarguments.h @@ -92,6 +92,7 @@ namespace vcpkg void example(StringView example_text); void header(StringView name); void blank(); + void text(StringView text, int indent = 0); std::string m_str; }; diff --git a/toolsrc/src/vcpkg-test/binarycaching.cpp b/toolsrc/src/vcpkg-test/binarycaching.cpp new file mode 100644 index 000000000..817b85e03 --- /dev/null +++ b/toolsrc/src/vcpkg-test/binarycaching.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace vcpkg; + +TEST_CASE ("reformat_version semver-ish", "[reformat_version]") +{ + REQUIRE(reformat_version("0.0.0", "abitag") == "0.0.0-abitag"); + REQUIRE(reformat_version("1.0.1", "abitag") == "1.0.1-abitag"); + REQUIRE(reformat_version("1.01.000", "abitag") == "1.1.0-abitag"); + REQUIRE(reformat_version("1.2", "abitag") == "1.2.0-abitag"); + REQUIRE(reformat_version("v52", "abitag") == "52.0.0-abitag"); + REQUIRE(reformat_version("v09.01.02", "abitag") == "9.1.2-abitag"); + REQUIRE(reformat_version("1.1.1q", "abitag") == "1.1.1-abitag"); + REQUIRE(reformat_version("1", "abitag") == "1.0.0-abitag"); +} + +TEST_CASE ("reformat_version date", "[reformat_version]") +{ + REQUIRE(reformat_version("2020-06-26", "abitag") == "2020.6.26-abitag"); + REQUIRE(reformat_version("20-06-26", "abitag") == "0.0.0-abitag"); + REQUIRE(reformat_version("2020-06-26-release", "abitag") == "2020.6.26-abitag"); + REQUIRE(reformat_version("2020-06-26000", "abitag") == "2020.6.26-abitag"); +} + +TEST_CASE ("reformat_version generic", "[reformat_version]") +{ + REQUIRE(reformat_version("apr", "abitag") == "0.0.0-abitag"); + REQUIRE(reformat_version("", "abitag") == "0.0.0-abitag"); +} + +TEST_CASE ("generate_nuspec", "[generate_nuspec]") +{ + auto& fsWrapper = Files::get_real_filesystem(); + VcpkgCmdArguments args = VcpkgCmdArguments::create_from_arg_sequence(nullptr, nullptr); + args.packages_root_dir = std::make_unique("/"); + VcpkgPaths paths(fsWrapper, args); + + auto pghs = Paragraphs::parse_paragraphs(R"( +Source: zlib2 +Version: 1.5 +Build-Depends: zlib +Description: a spiffy compression library wrapper + +Feature: a +Description: a feature + +Feature: b +Description: enable bzip capabilities +Build-Depends: bzip +)", + ""); + REQUIRE(pghs.has_value()); + auto maybe_scf = SourceControlFile::parse_control_file(fs::path(), std::move(*pghs.get())); + REQUIRE(maybe_scf.has_value()); + SourceControlFileLocation scfl{std::move(*maybe_scf.get()), fs::path()}; + + Dependencies::InstallPlanAction ipa(PackageSpec{"zlib2", Triplet::X64_WINDOWS}, + scfl, + Dependencies::RequestType::USER_REQUESTED, + {{"a", {}}, {"b", {}}}); + + ipa.abi_info = Build::AbiInfo{}; + ipa.abi_info.get()->package_abi = "packageabi"; + std::string tripletabi("tripletabi"); + ipa.abi_info.get()->triplet_abi = tripletabi; + + NugetReference ref(ipa); + + REQUIRE(ref.nupkg_filename() == "zlib2_x64-windows.1.5.0-packageabi.nupkg"); + + auto nuspec = generate_nuspec(paths, ipa, ref); +#ifdef _WIN32 +#define PKGPATH "C:\\zlib2_x64-windows\\**" +#else +#define PKGPATH "/zlib2_x64-windows/**" +#endif + std::string expected = R"( + + zlib2_x64-windows + 1.5.0-packageabi + vcpkg + NOT FOR DIRECT USE. Automatically generated cache package. + +a spiffy compression library wrapper + +Version: 1.5 +Triplet/Compiler hash: tripletabi +Features: a, b +Dependencies: + + + + + +)"; + auto expected_lines = Strings::split(expected, '\n'); + auto nuspec_lines = Strings::split(nuspec, '\n'); + for (size_t i = 0; i < expected_lines.size() && i < nuspec_lines.size(); ++i) + { + INFO("on line: " << i); + REQUIRE(nuspec_lines[i] == expected_lines[i]); + } + REQUIRE(nuspec_lines.size() == expected_lines.size()); +} + +TEST_CASE ("XmlSerializer", "[XmlSerializer]") +{ + XmlSerializer xml; + xml.open_tag("a"); + xml.open_tag("b"); + xml.simple_tag("c", "d"); + xml.close_tag("b"); + xml.text("escaping: & < > \" '"); + + REQUIRE(xml.buf == R"(descaping: & < > " ')"); + + xml = XmlSerializer(); + xml.emit_declaration(); + xml.start_complex_open_tag("a").text_attr("b", "<").text_attr("c", " ").finish_self_closing_complex_tag(); + REQUIRE(xml.buf == R"()"); + + xml = XmlSerializer(); + xml.start_complex_open_tag("a").finish_complex_open_tag(); + REQUIRE(xml.buf == R"()"); + + xml = XmlSerializer(); + xml.line_break(); + xml.open_tag("a").line_break().line_break(); + xml.close_tag("a").line_break().line_break(); + REQUIRE(xml.buf == "\n\n \n \n\n"); +} \ No newline at end of file diff --git a/toolsrc/src/vcpkg-test/binaryconfigparser.cpp b/toolsrc/src/vcpkg-test/binaryconfigparser.cpp index 910d0836f..53e19a8f6 100644 --- a/toolsrc/src/vcpkg-test/binaryconfigparser.cpp +++ b/toolsrc/src/vcpkg-test/binaryconfigparser.cpp @@ -57,6 +57,78 @@ TEST_CASE ("BinaryConfigParser files provider", "[binaryconfigparser]") } } +TEST_CASE ("BinaryConfigParser nuget source provider", "[binaryconfigparser]") +{ + { + auto parsed = create_binary_provider_from_configs_pure("nuget", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget,relative-path", {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget,http://example.org/", {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget," ABSOLUTE_PATH, {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget," ABSOLUTE_PATH ",nonsense", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget," ABSOLUTE_PATH ",upload", {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget," ABSOLUTE_PATH ",upload,extra", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nuget,,upload", {}); + REQUIRE(!parsed.has_value()); + } +} + +TEST_CASE ("BinaryConfigParser nuget config provider", "[binaryconfigparser]") +{ + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig,relative-path", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig,http://example.org/", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig," ABSOLUTE_PATH, {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig," ABSOLUTE_PATH ",nonsense", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig," ABSOLUTE_PATH ",upload", {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig," ABSOLUTE_PATH ",upload,extra", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("nugetconfig,,upload", {}); + REQUIRE(!parsed.has_value()); + } +} + TEST_CASE ("BinaryConfigParser default provider", "[binaryconfigparser]") { { @@ -89,6 +161,18 @@ TEST_CASE ("BinaryConfigParser clear provider", "[binaryconfigparser]") } } +TEST_CASE ("BinaryConfigParser interactive provider", "[binaryconfigparser]") +{ + { + auto parsed = create_binary_provider_from_configs_pure("interactive", {}); + REQUIRE(parsed.has_value()); + } + { + auto parsed = create_binary_provider_from_configs_pure("interactive,upload", {}); + REQUIRE(!parsed.has_value()); + } +} + TEST_CASE ("BinaryConfigParser multiple providers", "[binaryconfigparser]") { { diff --git a/toolsrc/src/vcpkg-test/strings.cpp b/toolsrc/src/vcpkg-test/strings.cpp index eb1f5697f..3172b557f 100644 --- a/toolsrc/src/vcpkg-test/strings.cpp +++ b/toolsrc/src/vcpkg-test/strings.cpp @@ -41,3 +41,13 @@ TEST_CASE ("split by char", "[strings]") REQUIRE(split(" hello world ", ' ') == result_t{"hello", "world"}); REQUIRE(split("no delimiters", ',') == result_t{"no delimiters"}); } + +TEST_CASE ("find_first_of", "[strings]") +{ + using vcpkg::Strings::find_first_of; + REQUIRE(find_first_of("abcdefg", "hij") == std::string()); + REQUIRE(find_first_of("abcdefg", "a") == std::string("abcdefg")); + REQUIRE(find_first_of("abcdefg", "g") == std::string("g")); + REQUIRE(find_first_of("abcdefg", "bg") == std::string("bcdefg")); + REQUIRE(find_first_of("abcdefg", "gb") == std::string("bcdefg")); +} diff --git a/toolsrc/src/vcpkg-test/system.cpp b/toolsrc/src/vcpkg-test/system.cpp index 6e87f9b3e..8b44f8f24 100644 --- a/toolsrc/src/vcpkg-test/system.cpp +++ b/toolsrc/src/vcpkg-test/system.cpp @@ -7,16 +7,17 @@ #include #include #include +#include +using vcpkg::nullopt; using vcpkg::Optional; using vcpkg::StringView; using vcpkg::ZStringView; using vcpkg::Checks::check_exit; -using vcpkg::System::get_environment_variable; -using vcpkg::System::to_cpu_architecture; -using vcpkg::System::guess_visual_studio_prompt_target_architecture; -using vcpkg::nullopt; using vcpkg::System::CPUArchitecture; +using vcpkg::System::get_environment_variable; +using vcpkg::System::guess_visual_studio_prompt_target_architecture; +using vcpkg::System::to_cpu_architecture; namespace { @@ -126,30 +127,43 @@ TEST_CASE ("guess_visual_studio_prompt", "[system]") set_environment_variable("VSCMD_ARG_TGT_ARCH", nullopt); CHECK(!guess_visual_studio_prompt_target_architecture().has_value()); set_environment_variable("VSCMD_ARG_TGT_ARCH", "x86"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); set_environment_variable("VSCMD_ARG_TGT_ARCH", "x64"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64); set_environment_variable("VSCMD_ARG_TGT_ARCH", "arm"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM); set_environment_variable("VSCMD_ARG_TGT_ARCH", "arm64"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64); // check that apparent "nested" prompts defer to "vsdevcmd" set_environment_variable("VCINSTALLDIR", "anything"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64); set_environment_variable("VSCMD_ARG_TGT_ARCH", nullopt); set_environment_variable("Platform", nullopt); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); set_environment_variable("Platform", "x86"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86); set_environment_variable("Platform", "x64"); - CHECK(guess_visual_studio_prompt_target_architecture() - .value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64); + CHECK(guess_visual_studio_prompt_target_architecture().value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64); +} + +TEST_CASE ("cmdlinebuilder", "[system]") +{ + using vcpkg::System::CmdLineBuilder; + + CmdLineBuilder cmd; + cmd.path_arg(fs::u8path("relative/path.exe")); + cmd.string_arg("abc"); + cmd.string_arg("hello world!"); + cmd.string_arg("|"); + cmd.string_arg(";"); + REQUIRE(cmd.extract() == "relative/path.exe abc \"hello world!\" \"|\" \";\""); + + cmd.path_arg(fs::u8path("trailing\\slash\\")); + cmd.string_arg("inner\"quotes"); +#ifdef _WIN32 + REQUIRE(cmd.extract() == "\"trailing\\slash\\\\\" \"inner\\\"quotes\""); +#else + REQUIRE(cmd.extract() == "\"trailing\\\\slash\\\\\" \"inner\\\"quotes\""); +#endif } diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index 167053d70..e267d6864 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -176,6 +176,11 @@ std::vector Strings::split(const std::string& s, const char delimit } } +const char* Strings::find_first_of(StringView input, StringView chars) +{ + return std::find_first_of(input.begin(), input.end(), chars.begin(), chars.end()); +} + std::vector Strings::find_all_enclosed(StringView input, StringView left_delim, StringView right_delim) { auto it_left = input.begin(); diff --git a/toolsrc/src/vcpkg/base/system.process.cpp b/toolsrc/src/vcpkg/base/system.process.cpp index 2f95c5b0e..96b4a9b4a 100644 --- a/toolsrc/src/vcpkg/base/system.process.cpp +++ b/toolsrc/src/vcpkg/base/system.process.cpp @@ -30,7 +30,7 @@ namespace vcpkg { struct CtrlCStateMachine { - CtrlCStateMachine() : m_number_of_external_processes(0), m_global_job(NULL), m_in_interactive(0) { } + CtrlCStateMachine() : m_number_of_external_processes(0), m_global_job(NULL), m_in_interactive(0) {} void transition_to_spawn_process() noexcept { @@ -167,7 +167,7 @@ namespace vcpkg } System::CMakeVariable::CMakeVariable(const StringView varname, const char* varvalue) - : s(Strings::format(R"("-D%s=%s")", varname, varvalue)) + : s(Strings::format("-D%s=%s", varname, varvalue)) { } System::CMakeVariable::CMakeVariable(const StringView varname, const std::string& varvalue) @@ -183,10 +183,62 @@ namespace vcpkg const fs::path& cmake_script, const std::vector& pass_variables) { - return Strings::format(R"("%s" %s -P "%s")", - cmake_tool_path.u8string(), - Strings::join(" ", pass_variables, [](auto&& v) { return v.s; }), - cmake_script.generic_u8string()); + System::CmdLineBuilder cmd; + cmd.path_arg(cmake_tool_path); + for (auto&& var : pass_variables) + { + cmd.string_arg(var.s); + } + cmd.string_arg("-P").path_arg(cmake_script); + return cmd.extract(); + } + + System::CmdLineBuilder& System::CmdLineBuilder::string_arg(StringView s) + { + if (!buf.empty()) buf.push_back(' '); + if (Strings::find_first_of(s, " \t\n\r\"\\,;&`^|'") != s.end()) + { + // TODO: improve this to properly handle all escaping +#if _WIN32 + // On Windows, `\`s before a double-quote must be doubled. Inner double-quotes must be escaped. + buf.push_back('"'); + size_t n_slashes = 0; + for (auto ch : s) + { + if (ch == '\\') + { + ++n_slashes; + } + else if (ch == '"') + { + buf.append(n_slashes + 1, '\\'); + n_slashes = 0; + } + else + { + n_slashes = 0; + } + buf.push_back(ch); + } + buf.append(n_slashes, '\\'); + buf.push_back('"'); +#else + // On non-Windows, `\` is the escape character and always requires doubling. Inner double-quotes must be + // escaped. + buf.push_back('"'); + for (auto ch : s) + { + if (ch == '\\' || ch == '"') buf.push_back('\\'); + buf.push_back(ch); + } + buf.push_back('"'); +#endif + } + else + { + Strings::append(buf, s); + } + return *this; } #if defined(_WIN32) @@ -330,7 +382,7 @@ namespace vcpkg #if defined(_WIN32) struct ProcessInfo { - constexpr ProcessInfo() noexcept : proc_info{} { } + constexpr ProcessInfo() noexcept : proc_info{} {} ProcessInfo(ProcessInfo&& other) noexcept : proc_info(other.proc_info) { other.proc_info.hProcess = nullptr; @@ -672,6 +724,6 @@ namespace vcpkg SetConsoleCtrlHandler(reinterpret_cast(ctrl_handler), TRUE); } #else - void System::register_console_ctrl_handler() { } + void System::register_console_ctrl_handler() {} #endif } diff --git a/toolsrc/src/vcpkg/binarycaching.cpp b/toolsrc/src/vcpkg/binarycaching.cpp index aa2ed6103..aa674e2ca 100644 --- a/toolsrc/src/vcpkg/binarycaching.cpp +++ b/toolsrc/src/vcpkg/binarycaching.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -66,7 +68,7 @@ namespace { } ~ArchivesBinaryProvider() = default; - void prefetch() override {} + void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override {} RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override { const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; @@ -244,12 +246,361 @@ namespace return RestoreResult::missing; } + private: std::vector m_read_dirs, m_write_dirs; }; + static std::string trim_leading_zeroes(std::string v) + { + auto n = v.find_first_not_of('0'); + if (n == std::string::npos) + { + v = "0"; + } + else if (n > 0) + { + v.erase(0, n); + } + return v; + } + + struct NugetBinaryProvider : IBinaryProvider + { + NugetBinaryProvider(std::vector&& read_sources, + std::vector&& write_sources, + std::vector&& read_configs, + std::vector&& write_configs, + bool interactive) + : m_read_sources(std::move(read_sources)) + , m_write_sources(std::move(write_sources)) + , m_read_configs(std::move(read_configs)) + , m_write_configs(std::move(write_configs)) + , m_interactive(interactive) + { + } + void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override + { + if (m_read_sources.empty() && m_read_configs.empty()) return; + if (plan.install_actions.empty()) return; + + auto& fs = paths.get_filesystem(); + + std::vector> nuget_refs; + + for (auto&& action : plan.install_actions) + { + auto& spec = action.spec; + fs.remove_all_inside(paths.package_dir(spec), VCPKG_LINE_INFO); + + nuget_refs.emplace_back(spec, NugetReference(action)); + } + + System::print2("Attempting to fetch ", plan.install_actions.size(), " packages from nuget.\n"); + + auto packages_config = paths.buildtrees / fs::u8path("packages.config"); + + auto generate_packages_config = [&] { + XmlSerializer xml; + xml.emit_declaration().line_break(); + xml.open_tag("packages").line_break(); + + for (auto&& nuget_ref : nuget_refs) + xml.start_complex_open_tag("package") + .text_attr("id", nuget_ref.second.id) + .text_attr("version", nuget_ref.second.version) + .finish_self_closing_complex_tag() + .line_break(); + + xml.close_tag("packages").line_break(); + paths.get_filesystem().write_contents(packages_config, xml.buf, VCPKG_LINE_INFO); + }; + + const auto& nuget_exe = paths.get_tool_exe("nuget"); + std::vector cmdlines; + + if (!m_read_sources.empty()) + { + // First check using all sources + System::CmdLineBuilder cmdline; + cmdline.path_arg(nuget_exe) + .string_arg("install") + .path_arg(packages_config) + .string_arg("-OutputDirectory") + .path_arg(paths.packages) + .string_arg("-Source") + .string_arg(Strings::join(";", m_read_sources)) + .string_arg("-ExcludeVersion") + .string_arg("-NoCache") + .string_arg("-PreRelease") + .string_arg("-DirectDownload") + .string_arg("-PackageSaveMode") + .string_arg("nupkg") + .string_arg("-Verbosity") + .string_arg("detailed") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + cmdlines.push_back(cmdline.extract()); + } + for (auto&& cfg : m_read_configs) + { + // Then check using each config + System::CmdLineBuilder cmdline; + cmdline.path_arg(nuget_exe) + .string_arg("install") + .path_arg(packages_config) + .string_arg("-OutputDirectory") + .path_arg(paths.packages) + .string_arg("-ConfigFile") + .path_arg(cfg) + .string_arg("-ExcludeVersion") + .string_arg("-NoCache") + .string_arg("-PreRelease") + .string_arg("-DirectDownload") + .string_arg("-PackageSaveMode") + .string_arg("nupkg") + .string_arg("-Verbosity") + .string_arg("detailed") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + cmdlines.push_back(cmdline.extract()); + } + + size_t num_restored = 0; + + for (const auto& cmdline : cmdlines) + { + if (nuget_refs.empty()) break; + + [&] { + generate_packages_config(); + if (Debug::g_debugging) + System::cmd_execute(cmdline); + else + { + auto res = System::cmd_execute_and_capture_output(cmdline); + if (res.output.find("Authentication may require manual action.") != std::string::npos) + { + System::print2( + System::Color::warning, + "One or more NuGet credential providers requested manual action. Add the binary " + "source 'interactive' to allow interactivity.\n"); + } + } + }(); + + Util::erase_remove_if(nuget_refs, [&](const std::pair& nuget_ref) -> bool { + auto nupkg_path = paths.package_dir(nuget_ref.first) / fs::u8path(nuget_ref.second.id + ".nupkg"); + if (fs.exists(nupkg_path, ignore_errors)) + { + fs.remove(nupkg_path, VCPKG_LINE_INFO); + Checks::check_exit(VCPKG_LINE_INFO, + !fs.exists(nupkg_path, ignore_errors), + "Unable to remove nupkg after restoring: %s", + nupkg_path.u8string()); + m_restored.emplace(nuget_ref.first); + ++num_restored; + return true; + } + else + { + return false; + } + }); + } + + System::print2("Restored ", num_restored, " packages. Use --debug for more information.\n"); + } + RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction& action) override + { + if (Util::Sets::contains(m_restored, action.spec)) + return RestoreResult::success; + else + return RestoreResult::missing; + } + void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + if (m_write_sources.empty() && m_write_configs.empty()) return; + auto& spec = action.spec; + + NugetReference nuget_ref(action); + auto nuspec_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".nuspec"); + paths.get_filesystem().write_contents( + nuspec_path, generate_nuspec(paths, action, nuget_ref), VCPKG_LINE_INFO); + + const auto& nuget_exe = paths.get_tool_exe("nuget"); + System::CmdLineBuilder cmdline; + cmdline.path_arg(nuget_exe) + .string_arg("pack") + .path_arg(nuspec_path) + .string_arg("-OutputDirectory") + .path_arg(paths.buildtrees) + .string_arg("-NoDefaultExcludes") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + + auto pack_rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmdline); + else + return System::cmd_execute_and_capture_output(cmdline).exit_code; + }(); + + if (pack_rc != 0) + { + System::print2(System::Color::error, "Packing NuGet failed. Use --debug for more information.\n"); + } + else + { + auto nupkg_path = paths.buildtrees / nuget_ref.nupkg_filename(); + for (auto&& write_src : m_write_sources) + { + System::CmdLineBuilder cmd; + cmd.path_arg(nuget_exe) + .string_arg("push") + .path_arg(nupkg_path) + .string_arg("-ApiKey") + .string_arg("AzureDevOps") + .string_arg("-ForceEnglishOutput") + .string_arg("-Source") + .string_arg(write_src); + if (!m_interactive) cmd.string_arg("-NonInteractive"); + + System::print2("Uploading binaries for ", spec, " to NuGet source ", write_src, ".\n"); + + auto rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmd); + else + return System::cmd_execute_and_capture_output(cmd).exit_code; + }(); + + if (rc != 0) + { + System::print2(System::Color::error, + "Pushing NuGet to ", + write_src, + " failed. Use --debug for more information.\n"); + } + } + for (auto&& write_cfg : m_write_configs) + { + System::CmdLineBuilder cmd; + cmd.path_arg(nuget_exe) + .string_arg("push") + .path_arg(nupkg_path) + .string_arg("-ApiKey") + .string_arg("AzureDevOps") + .string_arg("-ForceEnglishOutput") + .string_arg("-ConfigFile") + .path_arg(write_cfg); + if (!m_interactive) cmd.string_arg("-NonInteractive"); + + System::print2( + "Uploading binaries for ", spec, " using NuGet config ", write_cfg.u8string(), ".\n"); + + auto rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmd); + else + return System::cmd_execute_and_capture_output(cmd).exit_code; + }(); + + if (rc != 0) + { + System::print2(System::Color::error, + "Pushing NuGet with ", + write_cfg.u8string(), + " failed. Use --debug for more information.\n"); + } + } + paths.get_filesystem().remove(nupkg_path, ignore_errors); + } + } + void push_failure(const VcpkgPaths&, const std::string&, const PackageSpec&) override {} + RestoreResult precheck(const VcpkgPaths&, const Dependencies::InstallPlanAction&, bool) override + { + return RestoreResult::missing; + } + + private: + std::vector m_read_sources; + std::vector m_write_sources; + + std::vector m_read_configs; + std::vector m_write_configs; + + std::set m_restored; + bool m_interactive; + }; + + struct MergeBinaryProviders : IBinaryProvider + { + explicit MergeBinaryProviders(std::vector>&& providers) + : m_providers(std::move(providers)) + { + } + + void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override + { + for (auto&& provider : m_providers) + { + provider->prefetch(paths, plan); + } + } + RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + for (auto&& provider : m_providers) + { + auto result = provider->try_restore(paths, action); + switch (result) + { + case RestoreResult::build_failed: + case RestoreResult::success: return result; + case RestoreResult::missing: continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + return RestoreResult::missing; + } + void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + for (auto&& provider : m_providers) + { + provider->push_success(paths, action); + } + } + void push_failure(const VcpkgPaths& paths, const std::string& abi_tag, const PackageSpec& spec) override + { + for (auto&& provider : m_providers) + { + provider->push_failure(paths, abi_tag, spec); + } + } + RestoreResult precheck(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + bool purge_tombstones) override + { + for (auto&& provider : m_providers) + { + auto result = provider->precheck(paths, action, purge_tombstones); + switch (result) + { + case RestoreResult::build_failed: + case RestoreResult::success: return result; + case RestoreResult::missing: continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + return RestoreResult::missing; + } + + private: + std::vector> m_providers; + }; + struct NullBinaryProvider : IBinaryProvider { - void prefetch() override {} + void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override {} RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override { return RestoreResult::missing; @@ -263,6 +614,89 @@ namespace }; } +XmlSerializer& XmlSerializer::emit_declaration() +{ + buf.append(R"()"); + return *this; +} +XmlSerializer& XmlSerializer::open_tag(StringLiteral sl) +{ + Strings::append(buf, '<', sl, '>'); + indent += 2; + return *this; +} +XmlSerializer& XmlSerializer::start_complex_open_tag(StringLiteral sl) +{ + Strings::append(buf, '<', sl); + indent += 2; + return *this; +} +XmlSerializer& XmlSerializer::text_attr(StringLiteral name, StringView content) +{ + Strings::append(buf, ' ', name, "=\""); + text(content); + Strings::append(buf, '"'); + return *this; +} +XmlSerializer& XmlSerializer::finish_complex_open_tag() +{ + Strings::append(buf, '>'); + return *this; +} +XmlSerializer& XmlSerializer::finish_self_closing_complex_tag() +{ + Strings::append(buf, "/>"); + indent -= 2; + return *this; +} +XmlSerializer& XmlSerializer::close_tag(StringLiteral sl) +{ + Strings::append(buf, "'); + indent -= 2; + return *this; +} +XmlSerializer& XmlSerializer::text(StringView sv) +{ + for (auto ch : sv) + { + if (ch == '&') + { + buf.append("&"); + } + else if (ch == '<') + { + buf.append("<"); + } + else if (ch == '>') + { + buf.append(">"); + } + else if (ch == '"') + { + buf.append("""); + } + else if (ch == '\'') + { + buf.append("'"); + } + else + { + buf.push_back(ch); + } + } + return *this; +} +XmlSerializer& XmlSerializer::simple_tag(StringLiteral tag, StringView content) +{ + return open_tag(tag).text(content).close_tag(tag); +} +XmlSerializer& XmlSerializer::line_break() +{ + buf.push_back('\n'); + buf.append(indent, ' '); + return *this; +} + IBinaryProvider& vcpkg::null_binary_provider() { static NullBinaryProvider p; @@ -287,13 +721,42 @@ ExpectedS> vcpkg::create_binary_provider_from_c ExpectedS> vcpkg::create_binary_provider_from_configs_pure( const std::string& env_string, View args) { - struct BinaryConfigParser : Parse::ParserBase + struct State { - using Parse::ParserBase::ParserBase; + bool m_cleared = false; + bool interactive = false; std::vector archives_to_read; std::vector archives_to_write; + std::vector sources_to_read; + std::vector sources_to_write; + + std::vector configs_to_read; + std::vector configs_to_write; + + void clear() + { + m_cleared = true; + interactive = false; + archives_to_read.clear(); + archives_to_write.clear(); + sources_to_read.clear(); + sources_to_write.clear(); + configs_to_read.clear(); + configs_to_write.clear(); + } + }; + + struct BinaryConfigParser : Parse::ParserBase + { + BinaryConfigParser(StringView text, StringView origin, State* state) + : Parse::ParserBase(text, origin), state(state) + { + } + + State* state; + void parse() { while (!at_eof()) @@ -352,8 +815,7 @@ ExpectedS> vcpkg::create_binary_provider_from_c if (segments.size() != 1) return add_error("unexpected arguments: binary config 'clear' does not take arguments", segments[1].first); - archives_to_read.clear(); - archives_to_write.clear(); + state->clear(); } else if (segments[0].second == "files") { @@ -381,10 +843,80 @@ ExpectedS> vcpkg::create_binary_provider_from_c } else { - archives_to_write.push_back(p); + state->archives_to_write.push_back(p); } } - archives_to_read.push_back(std::move(p)); + state->archives_to_read.push_back(std::move(p)); + } + else if (segments[0].second == "interactive") + { + if (segments.size() > 1) + return add_error("unexpected arguments: binary config 'interactive' does not accept any arguments", + segments[1].first); + state->interactive = true; + } + else if (segments[0].second == "nugetconfig") + { + if (segments.size() < 2) + return add_error( + "expected arguments: binary config 'nugetconfig' requires at least a source argument", + segments[0].first); + + auto p = fs::u8path(segments[1].second); + if (!p.is_absolute()) + return add_error("expected arguments: path arguments for binary config strings must be absolute", + segments[1].first); + + if (segments.size() > 3) + { + return add_error( + "unexpected arguments: binary config 'nugetconfig' does not take more than 2 arguments", + segments[3].first); + } + else if (segments.size() == 3) + { + if (segments[2].second != "upload") + { + return add_error( + "unexpected arguments: binary config 'nugetconfig' can only accept 'upload' as " + "a second argument", + segments[2].first); + } + else + { + state->configs_to_write.push_back(p); + } + } + state->configs_to_read.push_back(std::move(p)); + } + else if (segments[0].second == "nuget") + { + if (segments.size() < 2) + return add_error("expected arguments: binary config 'nuget' requires at least a source argument", + segments[0].first); + + auto&& p = segments[1].second; + if (p.empty()) + return add_error("unexpected arguments: binary config 'nuget' requires non-empty source"); + if (segments.size() > 3) + { + return add_error("unexpected arguments: binary config 'nuget' does not take more than 2 arguments", + segments[3].first); + } + else if (segments.size() == 3) + { + if (segments[2].second != "upload") + { + return add_error("unexpected arguments: binary config 'nuget' can only accept 'upload' as " + "a second argument", + segments[2].first); + } + else + { + state->sources_to_write.push_back(p); + } + } + state->sources_to_read.push_back(std::move(p)); } else if (segments[0].second == "default") { @@ -409,30 +941,152 @@ ExpectedS> vcpkg::create_binary_provider_from_c } else { - archives_to_write.push_back(p); + state->archives_to_write.push_back(p); } } - archives_to_read.push_back(std::move(p)); + state->archives_to_read.push_back(std::move(p)); } else { - return add_error("unknown binary provider type: valid providers are 'clear', 'default', and 'files'", - segments[0].first); + return add_error( + "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', 'nugetconfig', " + "'interactive', and 'files'", + segments[0].first); } } }; - BinaryConfigParser env_parser(env_string, "VCPKG_BINARY_SOURCES"); + State s; + + BinaryConfigParser env_parser(env_string, "VCPKG_BINARY_SOURCES", &s); env_parser.parse(); if (auto err = env_parser.get_error()) return err->format(); for (auto&& arg : args) { - BinaryConfigParser arg_parser(arg, ""); + BinaryConfigParser arg_parser(arg, "", &s); arg_parser.parse(); if (auto err = arg_parser.get_error()) return err->format(); - Util::Vectors::append(&env_parser.archives_to_read, arg_parser.archives_to_read); - Util::Vectors::append(&env_parser.archives_to_write, arg_parser.archives_to_write); } - return {std::make_unique(std::move(env_parser.archives_to_read), - std::move(env_parser.archives_to_write))}; + + std::vector> providers; + if (!s.archives_to_read.empty() || !s.archives_to_write.empty()) + providers.push_back( + std::make_unique(std::move(s.archives_to_read), std::move(s.archives_to_write))); + if (!s.sources_to_read.empty() || !s.sources_to_write.empty() || !s.configs_to_read.empty() || + !s.configs_to_write.empty()) + providers.push_back(std::make_unique(std::move(s.sources_to_read), + std::move(s.sources_to_write), + std::move(s.configs_to_read), + std::move(s.configs_to_write), + s.interactive)); + + return {std::make_unique(std::move(providers))}; +} + +std::string vcpkg::reformat_version(const std::string& version, const std::string& abi_tag) +{ + static const std::regex semver_matcher(R"(v?(\d+)(\.\d+|$)(\.\d+)?.*)"); + + std::smatch sm; + if (std::regex_match(version.cbegin(), version.cend(), sm, semver_matcher)) + { + auto major = trim_leading_zeroes(sm.str(1)); + auto minor = sm.size() > 2 && !sm.str(2).empty() ? trim_leading_zeroes(sm.str(2).substr(1)) : "0"; + auto patch = sm.size() > 3 && !sm.str(3).empty() ? trim_leading_zeroes(sm.str(3).substr(1)) : "0"; + return Strings::concat(major, '.', minor, '.', patch, "-", abi_tag); + } + + static const std::regex date_matcher(R"((\d\d\d\d)-(\d\d)-(\d\d).*)"); + if (std::regex_match(version.cbegin(), version.cend(), sm, date_matcher)) + { + return Strings::concat(trim_leading_zeroes(sm.str(1)), + '.', + trim_leading_zeroes(sm.str(2)), + '.', + trim_leading_zeroes(sm.str(3)), + "-", + abi_tag); + } + + return Strings::concat("0.0.0-", abi_tag); +} + +std::string vcpkg::generate_nuspec(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + const vcpkg::NugetReference& ref) +{ + auto& spec = action.spec; + auto& scf = *action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_control_file; + auto& version = scf.core_paragraph->version; + std::string description = + Strings::concat("NOT FOR DIRECT USE. Automatically generated cache package.\n\n", + scf.core_paragraph->description, + "\n\nVersion: ", + version, + "\nTriplet/Compiler hash: ", + action.abi_info.value_or_exit(VCPKG_LINE_INFO).triplet_abi.value_or_exit(VCPKG_LINE_INFO), + "\nFeatures:", + Strings::join(",", action.feature_list, [](const std::string& s) { return " " + s; }), + "\nDependencies:\n"); + + for (auto&& dep : action.package_dependencies) + { + Strings::append(description, " ", dep.name(), '\n'); + } + XmlSerializer xml; + xml.open_tag("package").line_break(); + xml.open_tag("metadata").line_break(); + xml.simple_tag("id", ref.id).line_break(); + xml.simple_tag("version", ref.version).line_break(); + if (!scf.core_paragraph->homepage.empty()) xml.simple_tag("projectUrl", scf.core_paragraph->homepage); + xml.simple_tag("authors", "vcpkg").line_break(); + xml.simple_tag("description", description).line_break(); + xml.open_tag("packageTypes"); + xml.start_complex_open_tag("packageType").text_attr("name", "vcpkg").finish_self_closing_complex_tag(); + xml.close_tag("packageTypes").line_break(); + xml.close_tag("metadata").line_break(); + xml.open_tag("files"); + xml.start_complex_open_tag("file") + .text_attr("src", (paths.package_dir(spec) / fs::u8path("**")).u8string()) + .text_attr("target", "") + .finish_self_closing_complex_tag(); + xml.close_tag("files").line_break(); + xml.close_tag("package").line_break(); + return std::move(xml.buf); +} + +void vcpkg::help_topic_binary_caching(const VcpkgPaths&) +{ + System::print2( + System::Color::warning, + "** The following help documentation covers an experimental feature that will change at any time **\n\n"); + + HelpTableFormatter tbl; + tbl.text( + "Vcpkg can cache compiled packages to accelerate restoration on a single machine or across the network." + " This functionality is currently disabled by default and must be enabled by either passing `--binarycaching` " + "to every vcpkg command line or setting the environment variable `VCPKG_FEATURE_FLAGS` to `binarycaching`."); + tbl.blank(); + tbl.blank(); + tbl.text( + "Once caching is enabled, it can be further configured by either passing `--x-binarysource=` options " + "to every command line or setting the `VCPKG_BINARY_SOURCES` environment variable to a set of sources (Ex: " + "\";;...\"). Command line sources are interpreted after environment sources."); + tbl.blank(); + tbl.blank(); + tbl.header("Valid source strings"); + tbl.format("clear", "Removes all previous sources"); + tbl.format("default[,upload]", "Adds the default file-based source location (~/.vcpkg/archives)."); + tbl.format("files,[,upload]", "Adds a custom file-based source location."); + tbl.format("nuget,[,upload]", + "Adds a NuGet-based source; equivalent to the `-Source` parameter of the NuGet CLI."); + tbl.format("nugetconfig,[,upload]", + "Adds a NuGet-config-file-based source; equivalent to the `-Config` parameter of the NuGet CLI. This " + "config should specify `defaultPushSource` for uploads."); + tbl.format("interactive", "Enables interactive credential management for some source types"); + tbl.blank(); + tbl.text("The `upload` optional parameter for certain source strings controls whether on-demand builds will be " + "uploaded to that remote."); + + System::print2(tbl.m_str); } diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 3f7d3e250..7a74f3e83 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -280,7 +280,8 @@ namespace vcpkg::Build const System::Environment& EnvCache::get_action_env(const VcpkgPaths& paths, const AbiInfo& abi_info) { #if defined(_WIN32) - std::string build_env_cmd = make_build_env_cmd(*abi_info.pre_build_info, *abi_info.toolset); + std::string build_env_cmd = + make_build_env_cmd(*abi_info.pre_build_info, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO)); const auto& base_env = envs.get_lazy(abi_info.pre_build_info->passthrough_env_vars, [&]() -> EnvMapEntry { std::unordered_map env; @@ -324,6 +325,7 @@ namespace vcpkg::Build const std::string& EnvCache::get_triplet_info(const VcpkgPaths& paths, const AbiInfo& abi_info) { const auto& fs = paths.get_filesystem(); + Checks::check_exit(VCPKG_LINE_INFO, abi_info.pre_build_info != nullptr); const fs::path triplet_file_path = paths.get_triplet_file_path(abi_info.pre_build_info->triplet); auto tcfile = abi_info.pre_build_info->toolchain_file(); @@ -446,7 +448,7 @@ namespace vcpkg::Build {"CURRENT_BUILDTREES_DIR", buildpath}, {"CURRENT_PACKAGES_DIR", paths.packages / ("detect_compiler_" + triplet.canonical_name())}, }; - get_generic_cmake_build_args(paths, triplet, *abi_info.toolset, cmake_args); + get_generic_cmake_build_args(paths, triplet, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO), cmake_args); auto command = vcpkg::make_cmake_cmd(paths, paths.ports_cmake, std::move(cmake_args)); @@ -517,7 +519,10 @@ namespace vcpkg::Build {"ALL_FEATURES", all_features}, }; get_generic_cmake_build_args( - paths, triplet, *action.abi_info.value_or_exit(VCPKG_LINE_INFO).toolset, variables); + paths, + triplet, + action.abi_info.value_or_exit(VCPKG_LINE_INFO).toolset.value_or_exit(VCPKG_LINE_INFO), + variables); if (Util::Enum::to_bool(action.build_options.only_downloads)) { @@ -738,11 +743,9 @@ namespace vcpkg::Build return result; } - static void abi_entries_from_abi_info(const VcpkgPaths& paths, - const AbiInfo& abi_info, - std::vector& abi_tag_entries) + static void abi_entries_from_abi_info(const AbiInfo& abi_info, std::vector& abi_tag_entries) { - abi_tag_entries.emplace_back("triplet", paths.get_triplet_info(abi_info)); + abi_tag_entries.emplace_back("triplet", abi_info.triplet_abi.value_or_exit(VCPKG_LINE_INFO)); const auto& pre_build_info = *abi_info.pre_build_info; if (pre_build_info.public_abi_override) @@ -771,7 +774,7 @@ namespace vcpkg::Build std::vector abi_tag_entries(dependency_abis.begin(), dependency_abis.end()); - abi_entries_from_abi_info(paths, action.abi_info.value_or_exit(VCPKG_LINE_INFO), abi_tag_entries); + abi_entries_from_abi_info(action.abi_info.value_or_exit(VCPKG_LINE_INFO), abi_tag_entries); // If there is an unusually large number of files in the port then // something suspicious is going on. Rather than hash all of them @@ -833,8 +836,7 @@ namespace vcpkg::Build System::print2(message); } - auto abi_tag_entries_missing = abi_tag_entries; - Util::erase_remove_if(abi_tag_entries_missing, [](const AbiEntry& p) { return !p.value.empty(); }); + auto abi_tag_entries_missing = Util::filter(abi_tag_entries, [](const AbiEntry& p) { return p.value.empty(); }); if (abi_tag_entries_missing.empty()) { @@ -899,7 +901,8 @@ namespace vcpkg::Build abi_info.pre_build_info = std::make_unique( paths, action.spec.triplet(), var_provider.get_tag_vars(action.spec).value_or_exit(VCPKG_LINE_INFO)); - abi_info.toolset = &paths.get_toolset(*abi_info.pre_build_info); + abi_info.toolset = paths.get_toolset(*abi_info.pre_build_info); + abi_info.triplet_abi = paths.get_triplet_info(abi_info); auto maybe_abi_tag_and_file = compute_abi_tag(paths, action, dependency_abis); if (auto p = maybe_abi_tag_and_file.get()) diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index f18bbffea..46fac622b 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include +#include #include #include #include @@ -13,7 +14,7 @@ namespace vcpkg::Help { using topic_function = void (*)(const VcpkgPaths& paths); - constexpr Topic(CStringView n, topic_function fn) : name(n), print(fn) { } + constexpr Topic(CStringView n, topic_function fn) : name(n), print(fn) {} CStringView name; topic_function print; @@ -40,10 +41,11 @@ namespace vcpkg::Help nullptr, }; - static constexpr std::array topics = {{ + static constexpr std::array topics = {{ + {"binarycaching", help_topic_binary_caching}, {"create", command_topic_fn}, - {"edit", command_topic_fn}, {"depend-info", command_topic_fn}, + {"edit", command_topic_fn}, {"env", command_topic_fn}, {"export", command_topic_fn}, {"help", command_topic_fn}, @@ -54,13 +56,12 @@ namespace vcpkg::Help {"remove", command_topic_fn}, {"search", command_topic_fn}, {"topics", help_topics}, + {"triplet", help_topic_valid_triplet}, }}; static void help_topics(const VcpkgPaths&) { - System::print2("Available help topics:\n" - " triplet\n" - " integrate", + System::print2("Available help topics:", Strings::join("", topics, [](const Topic& topic) { return std::string("\n ") + topic.name; }), "\n"); } @@ -110,7 +111,7 @@ namespace vcpkg::Help Checks::exit_success(VCPKG_LINE_INFO); } const auto& topic = args.command_arguments[0]; - if (topic == "triplet" || topic == "triplets" || topic == "triple") + if (topic == "triplets" || topic == "triple") { help_topic_valid_triplet(paths); Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 940034c6c..30c997498 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -470,6 +470,8 @@ namespace vcpkg::Install Build::compute_all_abis(paths, action_plan, var_provider, status_db); + binaryprovider.prefetch(paths, action_plan); + for (auto&& action : action_plan.install_actions) { with_tracking(action.spec, [&]() { diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp index 0d69bf7da..fd044c298 100644 --- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp +++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp @@ -628,6 +628,8 @@ namespace vcpkg target.append(34, ' '); } + static constexpr ptrdiff_t S_MAX_LINE_LENGTH = 100; + void HelpTableFormatter::format(StringView col1, StringView col2) { // 2 space, 31 col1, 1 space, 65 col2 = 99 @@ -641,29 +643,8 @@ namespace vcpkg { m_str.append(32 - col1.size(), ' '); } - const char* line_start = col2.begin(); - const char* const e = col2.end(); - const char* best_break = std::find_if(line_start, e, [](char ch) { return ch == ' ' || ch == '\n'; }); + text(col2, 34); - while (best_break != e) - { - const char* next_break = std::find_if(best_break + 1, e, [](char ch) { return ch == ' ' || ch == '\n'; }); - if (next_break - line_start > 65 || *best_break == '\n') - { - m_str.append(line_start, best_break); - line_start = best_break + 1; - best_break = next_break; - if (line_start != e) - { - help_table_newline_indent(m_str); - } - } - else - { - best_break = next_break; - } - } - m_str.append(line_start, best_break); m_str.push_back('\n'); } @@ -681,4 +662,31 @@ namespace vcpkg } void HelpTableFormatter::blank() { m_str.push_back('\n'); } + + // Note: this formatting code does not properly handle unicode, however all of our documentation strings are English + // ASCII. + void HelpTableFormatter::text(StringView text, int indent) + { + const char* line_start = text.begin(); + const char* const e = text.end(); + const char* best_break = std::find_if(line_start, e, [](char ch) { return ch == ' ' || ch == '\n'; }); + + while (best_break != e) + { + const char* next_break = std::find_if(best_break + 1, e, [](char ch) { return ch == ' ' || ch == '\n'; }); + if (*best_break == '\n' || next_break - line_start + indent > S_MAX_LINE_LENGTH) + { + m_str.append(line_start, best_break); + m_str.push_back('\n'); + line_start = best_break + 1; + best_break = next_break; + m_str.append(indent, ' '); + } + else + { + best_break = next_break; + } + } + m_str.append(line_start, best_break); + } }