diff --git a/include/vcpkg/base/message-data.inc.h b/include/vcpkg/base/message-data.inc.h index c4db50bea3..035e5b6b62 100644 --- a/include/vcpkg/base/message-data.inc.h +++ b/include/vcpkg/base/message-data.inc.h @@ -1490,6 +1490,12 @@ DECLARE_MESSAGE(HelpBinaryCachingAzBlob, "**Experimental: will change or be removed without warning**\n" "Adds an Azure Blob Storage source. Uses Shared Access Signature validation. should include " "the container path. must be be prefixed with a \"?\".") +DECLARE_MESSAGE(HelpBinaryCachingAzUpkg, + (), + "Printed as the 'definition' for 'x-az-universal,,,[,]'.", + "**Experimental: will change or be removed without warning**\n" + "Adds a Universal Package Azure Artifacts source. Uses the Azure CLI " + "(az artifacts) for uploads and downloads.") DECLARE_MESSAGE(HelpBinaryCachingCos, (), "Printed as the 'definition' for 'x-cos,[,]'.", @@ -1811,6 +1817,10 @@ DECLARE_MESSAGE(InvalidArgumentRequiresBaseUrlAndToken, (msg::binary_source), "", "invalid argument: binary config '{binary_source}' requires at least a base-url and a SAS token") +DECLARE_MESSAGE(InvalidArgumentRequiresFourOrFiveArguments, + (msg::binary_source), + "", + "invalid argument: binary config '{binary_source}' requires 4 or 5 arguments") DECLARE_MESSAGE(InvalidArgumentRequiresNoneArguments, (msg::binary_source), "", @@ -2576,6 +2586,11 @@ DECLARE_MESSAGE(RestoredPackagesFromAWS, (msg::count, msg::elapsed), "", "Restored {count} package(s) from AWS in {elapsed}. Use --debug to see more details.") +DECLARE_MESSAGE(RestoredPackagesFromAZUPKG, + (msg::count, msg::elapsed), + "", + "Restored {count} package(s) from Universal Packages in {elapsed}. " + "Use --debug to see more details.") DECLARE_MESSAGE(RestoredPackagesFromCOS, (msg::count, msg::elapsed), "", diff --git a/include/vcpkg/binarycaching.h b/include/vcpkg/binarycaching.h index 5c70cf04d1..f9057847df 100644 --- a/include/vcpkg/binarycaching.h +++ b/include/vcpkg/binarycaching.h @@ -121,6 +121,13 @@ namespace vcpkg std::string commit; }; + struct AzureUpkgSource + { + std::string organization; + std::string project; + std::string feed; + }; + struct BinaryConfigParserState { bool nuget_interactive = false; @@ -147,6 +154,9 @@ namespace vcpkg bool gha_write = false; bool gha_read = false; + std::vector upkg_templates_to_get; + std::vector upkg_templates_to_put; + std::vector sources_to_read; std::vector sources_to_write; diff --git a/include/vcpkg/binarycaching.private.h b/include/vcpkg/binarycaching.private.h index e26d8fc27a..d5a24518fa 100644 --- a/include/vcpkg/binarycaching.private.h +++ b/include/vcpkg/binarycaching.private.h @@ -19,11 +19,11 @@ namespace vcpkg // - v?. -> ..0-vcpkg // - v?.. -> ..-vcpkg // - anything else -> 0.0.0-vcpkg - std::string format_version_for_nugetref(StringView version_text, StringView abi_tag); + std::string format_version_for_feedref(StringView version_text, StringView abi_tag); - struct NugetReference + struct FeedReference { - NugetReference(std::string id, std::string version) : id(std::move(id)), version(std::move(version)) { } + FeedReference(std::string id, std::string version) : id(std::move(id)), version(std::move(version)) { } std::string id; std::string version; @@ -31,7 +31,7 @@ namespace vcpkg std::string nupkg_filename() const { return Strings::concat(id, '.', version, ".nupkg"); } }; - NugetReference make_nugetref(const InstallPlanAction& action, StringView prefix); + FeedReference make_nugetref(const InstallPlanAction& action, StringView prefix); std::string generate_nuspec(const Path& package_dir, const InstallPlanAction& action, diff --git a/include/vcpkg/metrics.h b/include/vcpkg/metrics.h index ceec9d9522..42113e5ca3 100644 --- a/include/vcpkg/metrics.h +++ b/include/vcpkg/metrics.h @@ -24,6 +24,7 @@ namespace vcpkg BinaryCachingHttp, BinaryCachingNuget, BinaryCachingSource, + BinaryCachingUpkg, ErrorVersioningDisabled, ErrorVersioningNoBaseline, GitHubRepository, diff --git a/include/vcpkg/tools.h b/include/vcpkg/tools.h index 4f99ee22cc..80c64c1100 100644 --- a/include/vcpkg/tools.h +++ b/include/vcpkg/tools.h @@ -23,6 +23,7 @@ namespace vcpkg static constexpr StringLiteral GIT = "git"; static constexpr StringLiteral GSUTIL = "gsutil"; static constexpr StringLiteral AWSCLI = "aws"; + static constexpr StringLiteral AZCLI = "az"; static constexpr StringLiteral COSCLI = "coscli"; static constexpr StringLiteral MONO = "mono"; static constexpr StringLiteral NINJA = "ninja"; @@ -45,6 +46,11 @@ namespace vcpkg virtual const std::string& get_tool_version(StringView tool, MessageSink& status_sink) const = 0; }; + ExpectedL extract_prefixed_nonquote(StringLiteral prefix, + StringLiteral tool_name, + std::string&& output, + const Path& exe_path); + ExpectedL extract_prefixed_nonwhitespace(StringLiteral prefix, StringLiteral tool_name, std::string&& output, diff --git a/locales/messages.json b/locales/messages.json index be4a9a9054..c3dc7a5915 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -858,6 +858,8 @@ "HelpBinaryCachingAwsHeader": "Azure Web Services sources", "HelpBinaryCachingAzBlob": "**Experimental: will change or be removed without warning**\nAdds an Azure Blob Storage source. Uses Shared Access Signature validation. should include the container path. must be be prefixed with a \"?\".", "_HelpBinaryCachingAzBlob.comment": "Printed as the 'definition' for 'x-azblob,,[,]'.", + "HelpBinaryCachingAzUpkg": "**Experimental: will change or be removed without warning**\nAdds a Universal Package Azure Artifacts source. Uses the Azure CLI (az artifacts) for uploads and downloads.", + "_HelpBinaryCachingAzUpkg.comment": "Printed as the 'definition' for 'x-az-universal,,,[,]'.", "HelpBinaryCachingCos": "**Experimental: will change or be removed without warning**\nAdds an COS source. Uses the cos CLI for uploads and downloads. should include the scheme 'cos://' and be suffixed with a \"/\".", "_HelpBinaryCachingCos.comment": "Printed as the 'definition' for 'x-cos,[,]'.", "HelpBinaryCachingDefaults": "Adds the default file-based location. Based on your system settings, the default path to store binaries is \"{path}\". This consults %LOCALAPPDATA%/%APPDATA% on Windows and $XDG_CACHE_HOME or $HOME on other platforms.", @@ -997,6 +999,8 @@ "_InvalidArgumentRequiresBaseUrl.comment": "An example of {base_url} is azblob://. An example of {binary_source} is azblob.", "InvalidArgumentRequiresBaseUrlAndToken": "invalid argument: binary config '{binary_source}' requires at least a base-url and a SAS token", "_InvalidArgumentRequiresBaseUrlAndToken.comment": "An example of {binary_source} is azblob.", + "InvalidArgumentRequiresFourOrFiveArguments": "invalid argument: binary config '{binary_source}' requires 4 or 5 arguments", + "_InvalidArgumentRequiresFourOrFiveArguments.comment": "An example of {binary_source} is azblob.", "InvalidArgumentRequiresNoWildcards": "cannot fix Windows path case for path containing wildcards: {path}", "_InvalidArgumentRequiresNoWildcards.comment": "An example of {path} is /foo/bar.", "InvalidArgumentRequiresNoneArguments": "invalid argument: binary config '{binary_source}' does not take arguments", @@ -1359,6 +1363,8 @@ "_ResponseFileCode.comment": "Explains to the user that they can use response files on the command line, 'response_file' must have no spaces and be a legal file name.", "RestoredPackagesFromAWS": "Restored {count} package(s) from AWS in {elapsed}. Use --debug to see more details.", "_RestoredPackagesFromAWS.comment": "An example of {count} is 42. An example of {elapsed} is 3.532 min.", + "RestoredPackagesFromAZUPKG": "Restored {count} package(s) from Universal Packages in {elapsed}. Use --debug to see more details.", + "_RestoredPackagesFromAZUPKG.comment": "An example of {count} is 42. An example of {elapsed} is 3.532 min.", "RestoredPackagesFromCOS": "Restored {count} package(s) from COS in {elapsed}. Use --debug to see more details.", "_RestoredPackagesFromCOS.comment": "An example of {count} is 42. An example of {elapsed} is 3.532 min.", "RestoredPackagesFromFiles": "Restored {count} package(s) from {path} in {elapsed}. Use --debug to see more details.", diff --git a/src/vcpkg-test/binarycaching.cpp b/src/vcpkg-test/binarycaching.cpp index 64800c976a..a41fd6bb1a 100644 --- a/src/vcpkg-test/binarycaching.cpp +++ b/src/vcpkg-test/binarycaching.cpp @@ -166,30 +166,30 @@ TEST_CASE ("CacheStatus operations", "[BinaryCache]") REQUIRE(assignee.is_restored()); } -TEST_CASE ("format_version_for_nugetref semver-ish", "[format_version_for_nugetref]") +TEST_CASE ("format_version_for_feedref semver-ish", "[format_version_for_feedref]") { - REQUIRE(format_version_for_nugetref("0.0.0", "abitag") == "0.0.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("1.0.1", "abitag") == "1.0.1-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("1.01.000", "abitag") == "1.1.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("1.2", "abitag") == "1.2.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("v52", "abitag") == "52.0.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("v09.01.02", "abitag") == "9.1.2-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("1.1.1q", "abitag") == "1.1.1-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("1", "abitag") == "1.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("0.0.0", "abitag") == "0.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("1.0.1", "abitag") == "1.0.1-vcpkgabitag"); + REQUIRE(format_version_for_feedref("1.01.000", "abitag") == "1.1.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("1.2", "abitag") == "1.2.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("v52", "abitag") == "52.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("v09.01.02", "abitag") == "9.1.2-vcpkgabitag"); + REQUIRE(format_version_for_feedref("1.1.1q", "abitag") == "1.1.1-vcpkgabitag"); + REQUIRE(format_version_for_feedref("1", "abitag") == "1.0.0-vcpkgabitag"); } -TEST_CASE ("format_version_for_nugetref date", "[format_version_for_nugetref]") +TEST_CASE ("format_version_for_feedref date", "[format_version_for_feedref]") { - REQUIRE(format_version_for_nugetref("2020-06-26", "abitag") == "2020.6.26-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("20-06-26", "abitag") == "0.0.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("2020-06-26-release", "abitag") == "2020.6.26-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("2020-06-26000", "abitag") == "2020.6.26-vcpkgabitag"); + REQUIRE(format_version_for_feedref("2020-06-26", "abitag") == "2020.6.26-vcpkgabitag"); + REQUIRE(format_version_for_feedref("20-06-26", "abitag") == "0.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("2020-06-26-release", "abitag") == "2020.6.26-vcpkgabitag"); + REQUIRE(format_version_for_feedref("2020-06-26000", "abitag") == "2020.6.26-vcpkgabitag"); } -TEST_CASE ("format_version_for_nugetref generic", "[format_version_for_nugetref]") +TEST_CASE ("format_version_for_feedref generic", "[format_version_for_feedref]") { - REQUIRE(format_version_for_nugetref("apr", "abitag") == "0.0.0-vcpkgabitag"); - REQUIRE(format_version_for_nugetref("", "abitag") == "0.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("apr", "abitag") == "0.0.0-vcpkgabitag"); + REQUIRE(format_version_for_feedref("", "abitag") == "0.0.0-vcpkgabitag"); } TEST_CASE ("generate_nuspec", "[generate_nuspec]") @@ -236,11 +236,11 @@ Build-Depends: bzip compiler_info.version = "compilerversion"; ipa.abi_info.get()->compiler_info = compiler_info; - NugetReference ref2 = make_nugetref(ipa, "prefix_"); + FeedReference ref2 = make_nugetref(ipa, "prefix_"); REQUIRE(ref2.nupkg_filename() == "prefix_zlib2_x64-windows.1.5.0-vcpkgpackageabi.nupkg"); - NugetReference ref = make_nugetref(ipa, ""); + FeedReference ref = make_nugetref(ipa, ""); REQUIRE(ref.nupkg_filename() == "zlib2_x64-windows.1.5.0-vcpkgpackageabi.nupkg"); diff --git a/src/vcpkg-test/configparser.cpp b/src/vcpkg-test/configparser.cpp index 5e8453ef65..cc8832b344 100644 --- a/src/vcpkg-test/configparser.cpp +++ b/src/vcpkg-test/configparser.cpp @@ -560,6 +560,42 @@ TEST_CASE ("BinaryConfigParser HTTP provider", "[binaryconfigparser]") } } +TEST_CASE ("BinaryConfigParser Universal Packages provider", "[binaryconfigparser]") +{ + // Scheme: x-az-universal,,,[,] + { + auto parsed = + parse_binary_provider_configs("x-az-universal,test_organization,test_project_name,test_feed,read", {}); + auto state = parsed.value_or_exit(VCPKG_LINE_INFO); + REQUIRE(state.upkg_templates_to_get.size() == 1); + REQUIRE(state.upkg_templates_to_get[0].feed == "test_feed"); + REQUIRE(state.upkg_templates_to_get[0].organization == "test_organization"); + REQUIRE(state.upkg_templates_to_get[0].project == "test_project_name"); + } + { + auto parsed = + parse_binary_provider_configs("x-az-universal,test_organization,test_project_name,test_feed,readwrite", {}); + auto state = parsed.value_or_exit(VCPKG_LINE_INFO); + REQUIRE(state.upkg_templates_to_get.size() == 1); + REQUIRE(state.upkg_templates_to_put.size() == 1); + REQUIRE(state.upkg_templates_to_get[0].feed == "test_feed"); + REQUIRE(state.upkg_templates_to_get[0].organization == "test_organization"); + REQUIRE(state.upkg_templates_to_get[0].project == "test_project_name"); + REQUIRE(state.upkg_templates_to_put[0].feed == "test_feed"); + REQUIRE(state.upkg_templates_to_put[0].organization == "test_organization"); + REQUIRE(state.upkg_templates_to_put[0].project == "test_project_name"); + } + { + auto parsed = parse_binary_provider_configs( + "x-az-universal,test_organization,test_project_name,test_feed,extra_argument,readwrite", {}); + REQUIRE(!parsed.has_value()); + } + { + auto parsed = parse_binary_provider_configs("x-az-universal,missing_args,read", {}); + REQUIRE(!parsed.has_value()); + } +} + TEST_CASE ("AssetConfigParser azurl provider", "[assetconfigparser]") { CHECK(parse_download_configuration({})); diff --git a/src/vcpkg-test/tools.cpp b/src/vcpkg-test/tools.cpp index cc902cc70c..431309af4c 100644 --- a/src/vcpkg-test/tools.cpp +++ b/src/vcpkg-test/tools.cpp @@ -128,3 +128,15 @@ TEST_CASE ("extract_prefixed_nonwhitespace", "[tools]") CHECK(error_result.error() == "error: fooutil (fooutil.exe) produced unexpected output when attempting to " "determine the version:\nmalformed output"); } + +TEST_CASE ("extract_prefixed_nonquote", "[tools]") +{ + CHECK(extract_prefixed_nonquote("fooutil version ", "fooutil", "fooutil version 1.2\"", "fooutil.exe") + .value_or_exit(VCPKG_LINE_INFO) == "1.2"); + CHECK(extract_prefixed_nonquote("fooutil version ", "fooutil", "fooutil version 1.2 \" ", "fooutil.exe") + .value_or_exit(VCPKG_LINE_INFO) == "1.2 "); + auto error_result = extract_prefixed_nonquote("fooutil version ", "fooutil", "malformed output", "fooutil.exe"); + CHECK(!error_result.has_value()); + CHECK(error_result.error() == "error: fooutil (fooutil.exe) produced unexpected output when attempting to " + "determine the version:\nmalformed output"); +} diff --git a/src/vcpkg/binarycaching.cpp b/src/vcpkg/binarycaching.cpp index 1b093139cb..00171cd62f 100644 --- a/src/vcpkg/binarycaching.cpp +++ b/src/vcpkg/binarycaching.cpp @@ -186,13 +186,13 @@ namespace return ret; } - NugetReference make_nugetref(const PackageSpec& spec, const Version& version, StringView abi_tag, StringView prefix) + FeedReference make_feedref(const PackageSpec& spec, const Version& version, StringView abi_tag, StringView prefix) { - return {Strings::concat(prefix, spec.dir()), format_version_for_nugetref(version.text, abi_tag)}; + return {Strings::concat(prefix, spec.dir()), format_version_for_feedref(version.text, abi_tag)}; } - NugetReference make_nugetref(const BinaryPackageReadInfo& info, StringView prefix) + FeedReference make_feedref(const BinaryPackageReadInfo& info, StringView prefix) { - return make_nugetref(info.spec, info.version, info.package_abi, prefix); + return make_feedref(info.spec, info.version, info.package_abi, prefix); } void clean_prepare_dir(const Filesystem& fs, const Path& dir) @@ -650,7 +650,7 @@ namespace NuGetSource m_src; - static std::string generate_packages_config(View refs) + static std::string generate_packages_config(View refs) { XmlSerializer xml; xml.emit_declaration().line_break(); @@ -743,7 +743,7 @@ namespace } size_t count_stored = 0; - auto nupkg_path = m_buildtrees / make_nugetref(request, m_nuget_prefix).nupkg_filename(); + auto nupkg_path = m_buildtrees / make_feedref(request, m_nuget_prefix).nupkg_filename(); for (auto&& write_src : m_sources) { msg_sink.println( @@ -1255,6 +1255,162 @@ namespace Path m_tool; }; + struct AzureUpkgTool + { + AzureUpkgTool(const ToolCache& cache, MessageSink& sink) { az_cli = cache.get_tool_path(Tools::AZCLI, sink); } + + Command base_cmd(const AzureUpkgSource& src, + StringView package_name, + StringView package_version, + StringView verb) const + { + Command cmd{az_cli}; + cmd.string_arg("artifacts") + .string_arg("universal") + .string_arg(verb) + .string_arg("--organization") + .string_arg(src.organization) + .string_arg("--feed") + .string_arg(src.feed) + .string_arg("--name") + .string_arg(package_name) + .string_arg("--version") + .string_arg(package_version); + if (!src.project.empty()) + { + cmd.string_arg("--project").string_arg(src.project).string_arg("--scope").string_arg("project"); + } + return cmd; + } + + ExpectedL download(const AzureUpkgSource& src, + StringView package_name, + StringView package_version, + const Path& download_path, + MessageSink& sink) const + { + Command cmd = base_cmd(src, package_name, package_version, "download"); + cmd.string_arg("--path").string_arg(download_path); + return run_az_artifacts_cmd(cmd, sink); + } + + ExpectedL publish(const AzureUpkgSource& src, + StringView package_name, + StringView package_version, + const Path& package_dir, + StringView description, + MessageSink& sink) const + { + Command cmd = base_cmd(src, package_name, package_version, "publish"); + cmd.string_arg("--description").string_arg(description).string_arg("--path").string_arg(package_dir); + return run_az_artifacts_cmd(cmd, sink); + } + + ExpectedL run_az_artifacts_cmd(const Command& cmd, MessageSink& sink) const + { + RedirectedProcessLaunchSettings show_in_debug_settings; + show_in_debug_settings.echo_in_debug = EchoInDebug::Show; + return cmd_execute_and_capture_output(cmd, show_in_debug_settings) + .then([&](ExitCodeAndOutput&& res) -> ExpectedL { + if (res.exit_code == 0) + { + return {Unit{}}; + } + + // az command line error message: Before you can run Azure DevOps commands, you need to + // run the login command(az login if using AAD/MSA identity else az devops login if using PAT token) + // to setup credentials. + if (res.output.find("you need to run the login command") != std::string::npos) + { + sink.println(Color::warning, + msgFailedVendorAuthentication, + msg::vendor = "Universal Packages", + msg::url = "https://learn.microsoft.com/cli/azure/authenticate-azure-cli"); + } + return LocalizedString::from_raw(std::move(res).output); + }); + } + Path az_cli; + }; + + struct AzureUpkgPutBinaryProvider : public IWriteBinaryProvider + { + AzureUpkgPutBinaryProvider(const ToolCache& cache, MessageSink& sink, std::vector&& sources) + : m_azure_tool(cache, sink), m_sources(sources) + { + } + + size_t push_success(const BinaryPackageWriteInfo& request, MessageSink& msg_sink) override + { + size_t count_stored = 0; + auto ref = make_feedref(request, ""); + std::string package_description = "Cached package for " + ref.id; + for (auto&& write_src : m_sources) + { + auto res = m_azure_tool.publish( + write_src, ref.id, ref.version, request.package_dir, package_description, msg_sink); + if (res) + { + count_stored++; + } + else + { + msg::println(res.error()); + } + } + + return count_stored; + } + + bool needs_nuspec_data() const override { return false; } + bool needs_zip_file() const override { return false; } + + private: + AzureUpkgTool m_azure_tool; + std::vector m_sources; + }; + + struct AzureUpkgGetBinaryProvider : public IReadBinaryProvider + { + AzureUpkgGetBinaryProvider(const ToolCache& cache, MessageSink& sink, AzureUpkgSource source) + : m_azure_tool(cache, sink), m_sink(sink), m_source(source) + { + } + + // Prechecking doesn't exist with universal packages so it's not implemented + void precheck(View, Span) const override { } + + LocalizedString restored_message(size_t count, + std::chrono::high_resolution_clock::duration elapsed) const override + { + return msg::format(msgRestoredPackagesFromAZUPKG, msg::count = count, msg::elapsed = ElapsedTime(elapsed)); + } + + void fetch(View actions, Span out_status) const override + { + for (size_t i = 0; i < actions.size(); ++i) + { + auto info = BinaryPackageReadInfo{*actions[i]}; + auto ref = make_feedref(info, ""); + auto res = m_azure_tool.download(m_source, ref.id, ref.version, info.package_dir, m_sink); + + if (res) + { + out_status[i] = RestoreResult::restored; + } + else + { + out_status[i] = RestoreResult::unavailable; + } + } + } + + private: + AzureUpkgTool m_azure_tool; + MessageSink& m_sink; + AzureUpkgSource m_source; + }; + ExpectedL default_cache_path_impl() { auto maybe_cachepath = get_environment_variable(EnvironmentVariableVcpkgDefaultBinaryCache); @@ -1701,6 +1857,24 @@ namespace state->url_templates_to_get, state->url_templates_to_put, std::move(url_template), segments, 2); state->binary_cache_providers.insert("http"); } + else if (segments[0].second == "x-az-universal") + { + // Scheme: x-az-universal,,,[,] + if (segments.size() < 4 || segments.size() > 5) + { + return add_error(msg::format(msgInvalidArgumentRequiresFourOrFiveArguments, + msg::binary_source = "Universal Packages")); + } + AzureUpkgSource upkg_template{ + segments[1].second, + segments[2].second, + segments[3].second, + }; + + state->binary_cache_providers.insert("upkg"); + handle_readwrite( + state->upkg_templates_to_get, state->upkg_templates_to_put, std::move(upkg_template), segments, 4); + } else { return add_error(msg::format(msgUnknownBinaryProviderType), segments[0].first); @@ -1954,6 +2128,7 @@ namespace vcpkg {"gcs", DefineMetric::BinaryCachingGcs}, {"http", DefineMetric::BinaryCachingHttp}, {"nuget", DefineMetric::BinaryCachingNuget}, + {"upkg", DefineMetric::BinaryCachingUpkg}, }; MetricsSubmission metrics; @@ -2098,6 +2273,19 @@ namespace vcpkg nuget_base, std::move(s.sources_to_write), std::move(s.configs_to_write))); } } + + if (!s.upkg_templates_to_get.empty()) + { + for (auto&& src : s.upkg_templates_to_get) + { + ret.read.push_back(std::make_unique(tools, out_sink, std::move(src))); + } + } + if (!s.upkg_templates_to_put.empty()) + { + ret.write.push_back( + std::make_unique(tools, out_sink, std::move(s.upkg_templates_to_put))); + } } return std::move(ret); } @@ -2470,7 +2658,7 @@ ExpectedL vcpkg::parse_binary_provider_configs(const st return s; } -std::string vcpkg::format_version_for_nugetref(StringView version_text, StringView abi_tag) +std::string vcpkg::format_version_for_feedref(StringView version_text, StringView abi_tag) { // this cannot use DotVersion::try_parse or DateVersion::try_parse, // since this is a subtly different algorithm @@ -2608,6 +2796,7 @@ LocalizedString vcpkg::format_help_topic_binary_caching() table.format("x-azblob,,[,]", msg::format(msgHelpBinaryCachingAzBlob)); table.format("x-gcs,[,]", msg::format(msgHelpBinaryCachingGcs)); table.format("x-cos,[,]", msg::format(msgHelpBinaryCachingCos)); + table.format("x-az-universal,,,[,]", msg::format(msgHelpBinaryCachingAzUpkg)); table.blank(); // NuGet sources: @@ -2657,8 +2846,8 @@ std::string vcpkg::generate_nuget_packages_config(const ActionPlan& plan, String return std::move(xml.buf); } -NugetReference vcpkg::make_nugetref(const InstallPlanAction& action, StringView prefix) +FeedReference vcpkg::make_nugetref(const InstallPlanAction& action, StringView prefix) { - return ::make_nugetref( + return ::make_feedref( action.spec, action.version(), action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi, prefix); } diff --git a/src/vcpkg/metrics.cpp b/src/vcpkg/metrics.cpp index c9ba1e212d..008b9f0f34 100644 --- a/src/vcpkg/metrics.cpp +++ b/src/vcpkg/metrics.cpp @@ -98,6 +98,7 @@ namespace vcpkg {DefineMetric::BinaryCachingHttp, "binarycaching_http"}, {DefineMetric::BinaryCachingNuget, "binarycaching_nuget"}, {DefineMetric::BinaryCachingSource, "binarycaching-source"}, + {DefineMetric::BinaryCachingUpkg, "binarycaching_upkg"}, {DefineMetric::ErrorVersioningDisabled, "error-versioning-disabled"}, {DefineMetric::ErrorVersioningNoBaseline, "error-versioning-no-baseline"}, {DefineMetric::GitHubRepository, "GITHUB_REPOSITORY"}, diff --git a/src/vcpkg/tools.cpp b/src/vcpkg/tools.cpp index cc956da923..c9095dc663 100644 --- a/src/vcpkg/tools.cpp +++ b/src/vcpkg/tools.cpp @@ -157,6 +157,35 @@ namespace vcpkg }); } + // set target to the subrange [begin_idx, end_idx) + static void set_string_to_subrange(std::string& target, size_t begin_idx, size_t end_idx) + { + if (end_idx != std::string::npos) + { + target.resize(end_idx); + } + target.erase(0, begin_idx); + } + + ExpectedL extract_prefixed_nonquote(StringLiteral prefix, + StringLiteral tool_name, + std::string&& output, + const Path& exe_path) + { + auto idx = output.find(prefix.data(), 0, prefix.size()); + if (idx != std::string::npos) + { + idx += prefix.size(); + const auto end_idx = output.find('"', idx); + set_string_to_subrange(output, idx, end_idx); + return {std::move(output), expected_left_tag}; + } + + return std::move(msg::format_error(msgUnexpectedToolOutput, msg::tool_name = tool_name, msg::path = exe_path) + .append_raw('\n') + .append_raw(std::move(output))); + } + ExpectedL extract_prefixed_nonwhitespace(StringLiteral prefix, StringLiteral tool_name, std::string&& output, @@ -167,12 +196,7 @@ namespace vcpkg { idx += prefix.size(); const auto end_idx = output.find_first_of(" \r\n", idx, 3); - if (end_idx != std::string::npos) - { - output.resize(end_idx); - } - - output.erase(0, idx); + set_string_to_subrange(output, idx, end_idx); return {std::move(output), expected_left_tag}; } @@ -463,6 +487,31 @@ namespace vcpkg } }; + struct AzCliProvider : ToolProvider + { + virtual bool is_abi_sensitive() const override { return false; } + virtual StringView tool_data_name() const override { return Tools::AZCLI; } + virtual std::vector system_exe_stems() const override { return {Tools::AZCLI}; } + virtual std::array default_min_version() const override { return {2, 64, 0}; } + + virtual ExpectedL get_version(const ToolCache&, MessageSink&, const Path& exe_path) const override + { + return run_to_extract_version( + Tools::AZCLI, + exe_path, + Command(exe_path).string_arg("version").string_arg("--output").string_arg("json")) + .then([&](std::string&& output) { + // { + // ... + // "azure-cli": "2.64.0", + // ... + // } + + return extract_prefixed_nonquote("\"azure-cli\": \"", Tools::AZCLI, std::move(output), exe_path); + }); + } + }; + struct CosCliProvider : ToolProvider { virtual bool is_abi_sensitive() const override { return false; } @@ -864,6 +913,7 @@ namespace vcpkg if (tool == Tools::MONO) return get_path(MonoProvider(), status_sink); if (tool == Tools::GSUTIL) return get_path(GsutilProvider(), status_sink); if (tool == Tools::AWSCLI) return get_path(AwsCliProvider(), status_sink); + if (tool == Tools::AZCLI) return get_path(AzCliProvider(), status_sink); if (tool == Tools::COSCLI) return get_path(CosCliProvider(), status_sink); if (tool == Tools::PYTHON3) return get_path(Python3Provider(), status_sink); if (tool == Tools::PYTHON3_WITH_VENV) return get_path(Python3WithVEnvProvider(), status_sink);