[vcpkg] Implement --x-write-nuget-packages-config= setting for install and x-set-installed (#12138)

* [vcpkg] Implement --x-write-nuget-packages-config= setting for `install` and `x-set-installed`.

* [vcpkg] Add end-to-end testing suite for install, remove, and binary caching

* [vcpkg] Define `$TestingRoot in end-to-end-tests.ps1

* [vcpkg] Address CR comments

Co-authored-by: Robert Schumacher <roschuma@microsoft.com>
This commit is contained in:
ras0219 2020-07-01 11:36:09 -07:00 committed by GitHub
parent 42f70a4276
commit 135f91de1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 369 additions and 31 deletions

View File

@ -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"
}

View File

@ -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 ***'

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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);
}

View File

@ -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="&lt;" 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="&lt;" 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");
}
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=\"&lt;\"\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>
)");
}

View File

@ -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
}
}

View File

@ -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);
}

View File

@ -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))
{

View File

@ -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);
}
}

View File

@ -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);