diff --git a/scripts/azure-pipelines/end-to-end-tests.ps1 b/scripts/azure-pipelines/end-to-end-tests.ps1 new file mode 100644 index 0000000000..b0642df824 --- /dev/null +++ b/scripts/azure-pipelines/end-to-end-tests.ps1 @@ -0,0 +1,134 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: MIT +# +<# +.SYNOPSIS +End-to-End tests for the vcpkg executable. + +.DESCRIPTION +These tests cover the command line interface and broad functions of vcpkg, including `install`, `remove` and certain +binary caching scenarios. They use the vcpkg executable in the current directory. + +.PARAMETER Triplet +The triplet to use for testing purposes. + +.PARAMETER WorkingRoot +The location used as scratch space for testing. + +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$Triplet, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$WorkingRoot +) + +$ErrorActionPreference = "Stop" + +$TestingRoot = Join-Path $WorkingRoot 'testing' +$buildtreesRoot = Join-Path $TestingRoot 'buildtrees' +$installRoot = Join-Path $TestingRoot 'installed' +$packagesRoot = Join-Path $TestingRoot 'packages' +$NuGetRoot = Join-Path $TestingRoot 'nuget' +$NuGetRoot2 = Join-Path $TestingRoot 'nuget2' +$ArchiveRoot = Join-Path $TestingRoot 'archives' +$commonArgs = @( + "--triplet", + $Triplet, + "--x-buildtrees-root=$buildtreesRoot", + "--x-install-root=$installRoot", + "--x-packages-root=$packagesRoot" +) + +Remove-Item -Recurse -Force $TestingRoot -ErrorAction SilentlyContinue +mkdir $TestingRoot +mkdir $NuGetRoot + +function Require-FileExists { + [CmdletBinding()] + Param( + [string]$File + ) + if (-Not (Test-Path $File)) { + throw "'$CurrentTest' failed to create file '$File'" + } +} +function Require-FileNotExists { + [CmdletBinding()] + Param( + [string]$File + ) + if (Test-Path $File) { + throw "'$CurrentTest' should not have created file '$File'" + } +} + +# Test simple installation +$args = $commonArgs + @("install","rapidjson","--binarycaching","--x-binarysource=clear;files,$ArchiveRoot,write;nuget,$NuGetRoot,upload") +$CurrentTest = "./vcpkg $($args -join ' ')" +Write-Host $CurrentTest +./vcpkg @args + +Require-FileExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" + +# Test simple removal +$args = $commonArgs + @("remove", "rapidjson") +$CurrentTest = "./vcpkg $($args -join ' ')" +Write-Host $CurrentTest +./vcpkg @args + +Require-FileNotExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" + +# Test restoring from files archive +$args = $commonArgs + @("install","rapidjson","--binarycaching","--x-binarysource=clear;files,$ArchiveRoot,read") +$CurrentTest = "./vcpkg $($args -join ' ')" +Remove-Item -Recurse -Force $installRoot +Remove-Item -Recurse -Force $buildtreesRoot +Write-Host $CurrentTest +./vcpkg @args + +Require-FileExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" +Require-FileNotExists "$buildtreesRoot/rapidjson/src" + +# Test restoring from nuget +$args = $commonArgs + @("install","rapidjson","--binarycaching","--x-binarysource=clear;nuget,$NuGetRoot") +$CurrentTest = "./vcpkg $($args -join ' ')" +Remove-Item -Recurse -Force $installRoot +Remove-Item -Recurse -Force $buildtreesRoot +Write-Host $CurrentTest +./vcpkg @args + +Require-FileExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" +Require-FileNotExists "$buildtreesRoot/rapidjson/src" + +# Test four-phase flow +$args = $commonArgs + @("install","rapidjson","--dry-run","--x-write-nuget-packages-config=$TestingRoot/packages.config") +$CurrentTest = "./vcpkg $($args -join ' ')" +Remove-Item -Recurse -Force $installRoot -ErrorAction SilentlyContinue +Write-Host $CurrentTest +./vcpkg @args +Require-FileNotExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" +Require-FileNotExists "$buildtreesRoot/rapidjson/src" +Require-FileExists "$TestingRoot/packages.config" + +& $(./vcpkg fetch nuget) restore $TestingRoot/packages.config -OutputDirectory "$NuGetRoot2" -Source "$NuGetRoot" + +Remove-Item -Recurse -Force $NuGetRoot -ErrorAction SilentlyContinue +mkdir $NuGetRoot + +$args = $commonArgs + @("install","rapidjson","tinyxml","--binarycaching","--x-binarysource=clear;nuget,$NuGetRoot2;nuget,$NuGetRoot,upload") +$CurrentTest = "./vcpkg $($args -join ' ')" +Write-Host $CurrentTest +./vcpkg @args +Require-FileExists "$installRoot/$Triplet/include/rapidjson/rapidjson.h" +Require-FileExists "$installRoot/$Triplet/include/tinyxml.h" +Require-FileNotExists "$buildtreesRoot/rapidjson/src" +Require-FileExists "$buildtreesRoot/tinyxml/src" + +if ((Get-ChildItem $NuGetRoot -Filter '*.nupkg' | Measure-Object).Count -ne 1) { + throw "In '$CurrentTest': did not create exactly 1 NuGet package" +} diff --git a/scripts/azure-pipelines/windows/azure-pipelines.yml b/scripts/azure-pipelines/windows/azure-pipelines.yml index 69ea089d6c..a20ee23bb1 100644 --- a/scripts/azure-pipelines/windows/azure-pipelines.yml +++ b/scripts/azure-pipelines/windows/azure-pipelines.yml @@ -42,6 +42,7 @@ jobs: cmake.exe -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DVCPKG_DEVELOPMENT_WARNINGS=ON -DVCPKG_WARNINGS_AS_ERRORS=ON -DVCPKG_BUILD_FUZZING=ON -B build.x86.debug -S toolsrc ninja.exe -C build.x86.debug build.x86.debug\vcpkg-test.exe + powershell.exe -NoProfile -ExecutionPolicy Bypass "scripts\azure-pipelines\end-to-end-tests.ps1 -WorkingRoot \"%cd%\testing\" -triplet x86-windows" failOnStderr: true - task: PowerShell@2 displayName: '*** Test Modified Ports and Prepare Test Logs ***' diff --git a/toolsrc/include/vcpkg/base/files.h b/toolsrc/include/vcpkg/base/files.h index dce9584fa4..7676b49a03 100644 --- a/toolsrc/include/vcpkg/base/files.h +++ b/toolsrc/include/vcpkg/base/files.h @@ -201,4 +201,8 @@ namespace vcpkg::Files bool has_invalid_chars_for_filesystem(const std::string& s); void print_paths(const std::vector<fs::path>& paths); + + /// Performs "lhs / rhs" according to the C++17 Filesystem Library Specification. + /// This function exists as a workaround for TS implementations. + fs::path combine(const fs::path& lhs, const fs::path& rhs); } diff --git a/toolsrc/include/vcpkg/binarycaching.h b/toolsrc/include/vcpkg/binarycaching.h index 61af79a3fe..c1db1f1692 100644 --- a/toolsrc/include/vcpkg/binarycaching.h +++ b/toolsrc/include/vcpkg/binarycaching.h @@ -39,8 +39,7 @@ namespace vcpkg 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) = 0; + virtual RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; }; IBinaryProvider& null_binary_provider(); @@ -50,5 +49,7 @@ namespace vcpkg ExpectedS<std::unique_ptr<IBinaryProvider>> create_binary_provider_from_configs_pure(const std::string& env_string, View<std::string> args); + std::string generate_nuget_packages_config(const Dependencies::ActionPlan& action); + void help_topic_binary_caching(const VcpkgPaths& paths); } diff --git a/toolsrc/include/vcpkg/binarycaching.private.h b/toolsrc/include/vcpkg/binarycaching.private.h index f1fd046de3..f20a0db121 100644 --- a/toolsrc/include/vcpkg/binarycaching.private.h +++ b/toolsrc/include/vcpkg/binarycaching.private.h @@ -36,9 +36,6 @@ namespace vcpkg struct XmlSerializer { - std::string buf; - int indent = 0; - XmlSerializer& emit_declaration(); XmlSerializer& open_tag(StringLiteral sl); XmlSerializer& start_complex_open_tag(StringLiteral sl); @@ -49,6 +46,14 @@ namespace vcpkg XmlSerializer& text(StringView sv); XmlSerializer& simple_tag(StringLiteral tag, StringView content); XmlSerializer& line_break(); + + std::string buf; + + private: + XmlSerializer& emit_pending_indent(); + + int m_indent = 0; + bool m_pending_indent = false; }; } \ No newline at end of file diff --git a/toolsrc/include/vcpkg/commands.h b/toolsrc/include/vcpkg/commands.h index b73e91fe76..cec9237f57 100644 --- a/toolsrc/include/vcpkg/commands.h +++ b/toolsrc/include/vcpkg/commands.h @@ -163,7 +163,8 @@ namespace vcpkg::Commands const CMakeVars::CMakeVarProvider& cmake_vars, const std::vector<FullPackageSpec>& specs, const Build::BuildPackageOptions& install_plan_options, - DryRun dry_run); + DryRun dry_run, + const Optional<fs::path>& pkgsconfig_path); void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet); } diff --git a/toolsrc/src/vcpkg-test/binarycaching.cpp b/toolsrc/src/vcpkg-test/binarycaching.cpp index 817b85e03d..b220b5ccba 100644 --- a/toolsrc/src/vcpkg-test/binarycaching.cpp +++ b/toolsrc/src/vcpkg-test/binarycaching.cpp @@ -1,10 +1,11 @@ #include <catch2/catch.hpp> #include <vcpkg/binarycaching.private.h> +#include <vcpkg/binarycaching.h> #include <vcpkg/base/files.h> +#include <vcpkg/dependencies.h> #include <vcpkg/vcpkgcmdarguments.h> #include <vcpkg/sourceparagraph.h> #include <vcpkg/paragraphs.h> -#include <vcpkg/dependencies.h> #include <string> using namespace vcpkg; @@ -96,9 +97,9 @@ Features: a, b Dependencies: </description> <packageTypes><packageType name="vcpkg"/></packageTypes> - </metadata> + </metadata> <files><file src=")" PKGPATH R"(" target=""/></files> - </package> +</package> )"; auto expected_lines = Strings::split(expected, '\n'); auto nuspec_lines = Strings::split(nuspec, '\n'); @@ -123,8 +124,14 @@ TEST_CASE ("XmlSerializer", "[XmlSerializer]") 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 version="1.0" encoding="utf-8"?><a b="<" c=" "/>)"); + xml.start_complex_open_tag("a") + .text_attr("b", "<") + .text_attr("c", " ") + .finish_self_closing_complex_tag() + .line_break(); + xml.simple_tag("d", "e"); + REQUIRE(xml.buf == R"(<?xml version="1.0" encoding="utf-8"?><a b="<" c=" "/>)" + "\n<d>e</d>"); xml = XmlSerializer(); xml.start_complex_open_tag("a").finish_complex_open_tag(); @@ -134,5 +141,72 @@ TEST_CASE ("XmlSerializer", "[XmlSerializer]") xml.line_break(); xml.open_tag("a").line_break().line_break(); xml.close_tag("a").line_break().line_break(); - REQUIRE(xml.buf == "\n<a>\n \n </a>\n\n"); -} \ No newline at end of file + REQUIRE(xml.buf == "\n<a>\n\n</a>\n\n"); + + xml = XmlSerializer(); + xml.start_complex_open_tag("a") + .text_attr("b", "<") + .line_break() + .text_attr("c", " ") + .finish_complex_open_tag() + .line_break(); + xml.simple_tag("d", "e").line_break(); + REQUIRE(xml.buf == "<a b=\"<\"\n c=\" \">\n <d>e</d>\n"); +} + +TEST_CASE ("generate_nuget_packages_config", "[generate_nuget_packages_config]") +{ + Dependencies::ActionPlan plan; + auto packageconfig = generate_nuget_packages_config(plan); + REQUIRE(packageconfig == R"(<?xml version="1.0" encoding="utf-8"?> +<packages> +</packages> +)"); + + auto pghs = Paragraphs::parse_paragraphs(R"( +Source: zlib +Version: 1.5 +Description: a spiffy compression library wrapper +)", + "<testdata>"); + 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()}; + plan.install_actions.push_back(Dependencies::InstallPlanAction()); + plan.install_actions[0].spec = PackageSpec("zlib", Triplet::X64_ANDROID); + plan.install_actions[0].source_control_file_location = scfl; + plan.install_actions[0].abi_info = Build::AbiInfo{}; + plan.install_actions[0].abi_info.get()->package_abi = "packageabi"; + + packageconfig = generate_nuget_packages_config(plan); + REQUIRE(packageconfig == R"(<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="zlib_x64-android" version="1.5.0-packageabi"/> +</packages> +)"); + + auto pghs2 = Paragraphs::parse_paragraphs(R"( +Source: zlib2 +Version: 1.52 +Description: a spiffy compression library wrapper +)", + "<testdata>"); + REQUIRE(pghs2.has_value()); + auto maybe_scf2 = SourceControlFile::parse_control_file(fs::path(), std::move(*pghs2.get())); + REQUIRE(maybe_scf2.has_value()); + SourceControlFileLocation scfl2{std::move(*maybe_scf2.get()), fs::path()}; + plan.install_actions.push_back(Dependencies::InstallPlanAction()); + plan.install_actions[1].spec = PackageSpec("zlib2", Triplet::X64_ANDROID); + plan.install_actions[1].source_control_file_location = scfl2; + plan.install_actions[1].abi_info = Build::AbiInfo{}; + plan.install_actions[1].abi_info.get()->package_abi = "packageabi2"; + + packageconfig = generate_nuget_packages_config(plan); + REQUIRE(packageconfig == R"(<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="zlib_x64-android" version="1.5.0-packageabi"/> + <package id="zlib2_x64-android" version="1.52.0-packageabi2"/> +</packages> +)"); +} diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 9d9aecb696..2e39073cea 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -989,4 +989,20 @@ namespace vcpkg::Files message.push_back('\n'); System::print2(message); } + + fs::path combine(const fs::path& lhs, const fs::path& rhs) + { +#if VCPKG_USE_STD_FILESYSTEM + return lhs / rhs; +#else // ^^^ VCPKG_USE_STD_FILESYSTEM // !VCPKG_USE_STD_FILESYSTEM vvv + if (rhs.is_absolute()) + { + return rhs; + } + else + { + return lhs / rhs; + } +#endif + } } diff --git a/toolsrc/src/vcpkg/binarycaching.cpp b/toolsrc/src/vcpkg/binarycaching.cpp index 5bebc4fb86..532e07032b 100644 --- a/toolsrc/src/vcpkg/binarycaching.cpp +++ b/toolsrc/src/vcpkg/binarycaching.cpp @@ -285,7 +285,7 @@ namespace for (auto&& action : plan.install_actions) { auto& spec = action.spec; - fs.remove_all_inside(paths.package_dir(spec), VCPKG_LINE_INFO); + fs.remove_all(paths.package_dir(spec), VCPKG_LINE_INFO); nuget_refs.emplace_back(spec, NugetReference(action)); } @@ -615,42 +615,57 @@ XmlSerializer& XmlSerializer::emit_declaration() } XmlSerializer& XmlSerializer::open_tag(StringLiteral sl) { + emit_pending_indent(); Strings::append(buf, '<', sl, '>'); - indent += 2; + m_indent += 2; return *this; } XmlSerializer& XmlSerializer::start_complex_open_tag(StringLiteral sl) { + emit_pending_indent(); Strings::append(buf, '<', sl); - indent += 2; + m_indent += 2; return *this; } XmlSerializer& XmlSerializer::text_attr(StringLiteral name, StringView content) { - Strings::append(buf, ' ', name, "=\""); + if (m_pending_indent) + { + m_pending_indent = false; + buf.append(m_indent, ' '); + } + else + { + buf.push_back(' '); + } + Strings::append(buf, name, "=\""); text(content); Strings::append(buf, '"'); return *this; } XmlSerializer& XmlSerializer::finish_complex_open_tag() { + emit_pending_indent(); Strings::append(buf, '>'); return *this; } XmlSerializer& XmlSerializer::finish_self_closing_complex_tag() { + emit_pending_indent(); Strings::append(buf, "/>"); - indent -= 2; + m_indent -= 2; return *this; } XmlSerializer& XmlSerializer::close_tag(StringLiteral sl) { + m_indent -= 2; + emit_pending_indent(); Strings::append(buf, "</", sl, '>'); - indent -= 2; return *this; } XmlSerializer& XmlSerializer::text(StringView sv) { + emit_pending_indent(); for (auto ch : sv) { if (ch == '&') @@ -682,12 +697,21 @@ XmlSerializer& XmlSerializer::text(StringView sv) } XmlSerializer& XmlSerializer::simple_tag(StringLiteral tag, StringView content) { - return open_tag(tag).text(content).close_tag(tag); + return emit_pending_indent().open_tag(tag).text(content).close_tag(tag); } XmlSerializer& XmlSerializer::line_break() { buf.push_back('\n'); - buf.append(indent, ' '); + m_pending_indent = true; + return *this; +} +XmlSerializer& XmlSerializer::emit_pending_indent() +{ + if (m_pending_indent) + { + m_pending_indent = false; + buf.append(m_indent, ' '); + } return *this; } @@ -1118,3 +1142,22 @@ void vcpkg::help_topic_binary_caching(const VcpkgPaths&) System::print2(tbl.m_str); } + +std::string vcpkg::generate_nuget_packages_config(const Dependencies::ActionPlan& action) +{ + auto refs = Util::fmap(action.install_actions, + [&](const Dependencies::InstallPlanAction& ipa) { return NugetReference(ipa); }); + XmlSerializer xml; + xml.emit_declaration().line_break(); + xml.open_tag("packages").line_break(); + for (auto&& ref : refs) + { + xml.start_complex_open_tag("package") + .text_attr("id", ref.id) + .text_attr("version", ref.version) + .finish_self_closing_complex_tag() + .line_break(); + } + xml.close_tag("packages").line_break(); + return std::move(xml.buf); +} diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 5e2f8778b7..7d29761658 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -484,8 +484,9 @@ namespace vcpkg::Build env); out_file.close(); - Checks::check_exit( - VCPKG_LINE_INFO, !compiler_hash.empty(), "Error occured while detecting compiler information"); + Checks::check_exit(VCPKG_LINE_INFO, + !compiler_hash.empty(), + "Error occured while detecting compiler information. Pass `--debug` for more information."); Debug::print("Detecting compiler hash for triplet ", triplet, ": ", compiler_hash, "\n"); return compiler_hash; @@ -866,6 +867,8 @@ namespace vcpkg::Build for (auto it = action_plan.install_actions.begin(); it != action_plan.install_actions.end(); ++it) { auto& action = *it; + if (action.abi_info.has_value()) continue; + std::vector<AbiEntry> dependency_abis; if (!Util::Enum::to_bool(action.build_options.only_downloads)) { diff --git a/toolsrc/src/vcpkg/commands.setinstalled.cpp b/toolsrc/src/vcpkg/commands.setinstalled.cpp index f52e4942b7..df191ee3e6 100644 --- a/toolsrc/src/vcpkg/commands.setinstalled.cpp +++ b/toolsrc/src/vcpkg/commands.setinstalled.cpp @@ -14,16 +14,22 @@ namespace vcpkg::Commands::SetInstalled { static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run"; + static constexpr StringLiteral OPTION_WRITE_PACKAGES_CONFIG = "--x-write-nuget-packages-config"; static constexpr CommandSwitch INSTALL_SWITCHES[] = { {OPTION_DRY_RUN, "Do not actually build or install"}, }; + static constexpr CommandSetting INSTALL_SETTINGS[] = { + {OPTION_WRITE_PACKAGES_CONFIG, + "Writes out a NuGet packages.config-formatted file for use with external binary caching.\n" + "See `vcpkg help binarycaching` for more information."}, + }; const CommandStructure COMMAND_STRUCTURE = { create_example_string(R"(x-set-installed <package>...)"), 0, SIZE_MAX, - {INSTALL_SWITCHES}, + {INSTALL_SWITCHES, INSTALL_SETTINGS}, nullptr, }; @@ -34,7 +40,8 @@ namespace vcpkg::Commands::SetInstalled const CMakeVars::CMakeVarProvider& cmake_vars, const std::vector<FullPackageSpec>& specs, const Build::BuildPackageOptions& install_plan_options, - DryRun dry_run) + DryRun dry_run, + const Optional<fs::path>& maybe_pkgsconfig) { // We have a set of user-requested specs. // We need to know all the specs which are required to fulfill dependencies for those specs. @@ -91,6 +98,16 @@ namespace vcpkg::Commands::SetInstalled Dependencies::print_plan(action_plan, true, paths.ports); + if (auto p_pkgsconfig = maybe_pkgsconfig.get()) + { + Build::compute_all_abis(paths, action_plan, cmake_vars, status_db); + auto& fs = paths.get_filesystem(); + auto pkgsconfig_path = Files::combine(paths.original_cwd, *p_pkgsconfig); + auto pkgsconfig_contents = generate_nuget_packages_config(action_plan); + fs.write_contents(pkgsconfig_path, pkgsconfig_contents, VCPKG_LINE_INFO); + System::print2("Wrote NuGet packages config information to ", pkgsconfig_path.u8string(), "\n"); + } + if (dry_run == DryRun::Yes) { Checks::exit_success(VCPKG_LINE_INFO); @@ -142,6 +159,12 @@ namespace vcpkg::Commands::SetInstalled PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports); auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths); + Optional<fs::path> pkgsconfig; + auto it_pkgsconfig = options.settings.find(OPTION_WRITE_PACKAGES_CONFIG); + if (it_pkgsconfig != options.settings.end()) + { + pkgsconfig = it_pkgsconfig->second; + } perform_and_exit_ex(args, paths, provider, @@ -149,6 +172,7 @@ namespace vcpkg::Commands::SetInstalled *cmake_vars, specs, install_plan_options, - dry_run ? DryRun::Yes : DryRun::No); + dry_run ? DryRun::Yes : DryRun::No, + pkgsconfig); } } diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 5750469add..aa79bb1f29 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -500,6 +500,7 @@ namespace vcpkg::Install static constexpr StringLiteral OPTION_XUNIT = "--x-xunit"; static constexpr StringLiteral OPTION_USE_ARIA2 = "--x-use-aria2"; static constexpr StringLiteral OPTION_CLEAN_AFTER_BUILD = "--clean-after-build"; + static constexpr StringLiteral OPTION_WRITE_PACKAGES_CONFIG = "--x-write-nuget-packages-config"; static constexpr std::array<CommandSwitch, 8> INSTALL_SWITCHES = {{ {OPTION_DRY_RUN, "Do not actually build or install"}, @@ -511,8 +512,11 @@ namespace vcpkg::Install {OPTION_USE_ARIA2, "Use aria2 to perform download tasks"}, {OPTION_CLEAN_AFTER_BUILD, "Clean buildtrees, packages and downloads after building each package"}, }}; - static constexpr std::array<CommandSetting, 1> INSTALL_SETTINGS = {{ + static constexpr std::array<CommandSetting, 2> INSTALL_SETTINGS = {{ {OPTION_XUNIT, "File to output results in XUnit format (Internal use)"}, + {OPTION_WRITE_PACKAGES_CONFIG, + "Writes out a NuGet packages.config-formatted file for use with external binary caching.\nSee `vcpkg help " + "binarycaching` for more information."}, }}; std::vector<std::string> get_all_port_names(const VcpkgPaths& paths) @@ -654,7 +658,8 @@ namespace vcpkg::Install void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) { // input sanitization - const ParsedArguments options = args.parse_arguments(paths.manifest_mode_enabled() ? MANIFEST_COMMAND_STRUCTURE : COMMAND_STRUCTURE); + const ParsedArguments options = + args.parse_arguments(paths.manifest_mode_enabled() ? MANIFEST_COMMAND_STRUCTURE : COMMAND_STRUCTURE); auto binaryprovider = create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO); @@ -697,8 +702,10 @@ namespace vcpkg::Install if (ec) { - Checks::exit_with_message(VCPKG_LINE_INFO, "Failed to load manifest file (%s): %s\n", - path_to_manifest.u8string(), ec.message()); + Checks::exit_with_message(VCPKG_LINE_INFO, + "Failed to load manifest file (%s): %s\n", + path_to_manifest.u8string(), + ec.message()); } std::vector<FullPackageSpec> specs; @@ -716,7 +723,21 @@ namespace vcpkg::Install Checks::exit_fail(VCPKG_LINE_INFO); } - Commands::SetInstalled::perform_and_exit_ex(args, paths, provider, *binaryprovider, var_provider, specs, install_plan_options, dry_run ? Commands::DryRun::Yes : Commands::DryRun::No); + Optional<fs::path> pkgsconfig; + auto it_pkgsconfig = options.settings.find(OPTION_WRITE_PACKAGES_CONFIG); + if (it_pkgsconfig != options.settings.end()) + { + pkgsconfig = fs::u8path(it_pkgsconfig->second); + } + Commands::SetInstalled::perform_and_exit_ex(args, + paths, + provider, + *binaryprovider, + var_provider, + specs, + install_plan_options, + dry_run ? Commands::DryRun::Yes : Commands::DryRun::No, + pkgsconfig); } const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { @@ -799,6 +820,17 @@ namespace vcpkg::Install Dependencies::print_plan(action_plan, is_recursive, paths.ports); + auto it_pkgsconfig = options.settings.find(OPTION_WRITE_PACKAGES_CONFIG); + if (it_pkgsconfig != options.settings.end()) + { + Build::compute_all_abis(paths, action_plan, var_provider, status_db); + + auto pkgsconfig_path = Files::combine(paths.original_cwd, fs::u8path(it_pkgsconfig->second)); + auto pkgsconfig_contents = generate_nuget_packages_config(action_plan); + fs.write_contents(pkgsconfig_path, pkgsconfig_contents, VCPKG_LINE_INFO); + System::print2("Wrote NuGet packages config information to ", pkgsconfig_path.u8string(), "\n"); + } + if (dry_run) { Checks::exit_success(VCPKG_LINE_INFO);