diff --git a/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/portfile.cmake b/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/portfile.cmake new file mode 100644 index 0000000000..065116c276 --- /dev/null +++ b/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/portfile.cmake @@ -0,0 +1 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) diff --git a/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/vcpkg.json b/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/vcpkg.json new file mode 100644 index 0000000000..b3806dacc1 --- /dev/null +++ b/azure-pipelines/e2e_ports/vcpkg-internal-e2e-test-port2/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "vcpkg-internal-e2e-test-port2", + "version-string": "1.0.0" +} diff --git a/azure-pipelines/end-to-end-tests-dir/registries.ps1 b/azure-pipelines/end-to-end-tests-dir/registries.ps1 index 0a1f39e35c..2c16c44fdc 100644 --- a/azure-pipelines/end-to-end-tests-dir/registries.ps1 +++ b/azure-pipelines/end-to-end-tests-dir/registries.ps1 @@ -64,17 +64,26 @@ try '-c', 'core.autocrlf=false' ) + $gitMainBranch = 'main' + $gitSecondaryBranch = 'secondary' + $CurrentTest = 'git init .' git @gitConfigOptions init . Throw-IfFailed + + # Create git registry with vcpkg-internal-e2e-test-port in the main branch + $CurrentTest = 'git switch --orphan' + git @gitConfigOptions switch --orphan $gitMainBranch + Throw-IfFailed + Copy-Item -Recurse -LiteralPath "$PSScriptRoot/../e2e_ports/vcpkg-internal-e2e-test-port" -Destination . New-Item -Path './vcpkg-internal-e2e-test-port/foobar' -Value 'this is just to get a distinct git tree' $CurrentTest = 'git add -A' git @gitConfigOptions add -A Throw-IfFailed - $CurrentTest = 'git commit' - git @gitConfigOptions commit -m 'initial commit' + $CurrentTest = 'git commit -m' + git @gitConfigOptions commit -m 'add vcpkg-internal-e2e-test-port' Throw-IfFailed $vcpkgInternalE2eTestPortGitTree = git rev-parse 'HEAD:vcpkg-internal-e2e-test-port' @@ -103,12 +112,65 @@ try $CurrentTest = 'git add -A' git @gitConfigOptions add -A Throw-IfFailed - $CurrentTest = 'git commit' + $CurrentTest = 'git commit --amend --no-edit' + git @gitConfigOptions commit --amend --no-edit + Throw-IfFailed + + $gitMainBaselineCommit = git rev-parse HEAD + $gitMainRefVersionsObject = git rev-parse HEAD:versions + + # Create git registry with vcpkg-internal-e2e-test-port2 in the secondary branch + + $CurrentTest = 'git switch --orphan' + git @gitConfigOptions switch --orphan $gitSecondaryBranch + Throw-IfFailed + + Copy-Item -Recurse -LiteralPath "$PSScriptRoot/../e2e_ports/vcpkg-internal-e2e-test-port2" -Destination . + New-Item -Path './vcpkg-internal-e2e-test-port2/foobaz' -Value 'this is just to get a distinct git tree' + + $CurrentTest = 'git add -A' + git @gitConfigOptions add -A + Throw-IfFailed + $CurrentTest = 'git commit -m' + git @gitConfigOptions commit -m 'add vcpkg-internal-e2e-test-port2' + Throw-IfFailed + + $vcpkgInternalE2eTestPort2GitTree = git rev-parse 'HEAD:vcpkg-internal-e2e-test-port2' + $vcpkgInternalE2eTestPortVersionsJson = @{ + "versions" = @( + @{ + "version-string" = "1.0.0"; + "git-tree" = $vcpkgInternalE2eTestPort2GitTree + } + ) + } + $vcpkgBaseline = @{ + "default" = @{ + "vcpkg-internal-e2e-test-port2" = @{ + "baseline" = "1.0.0" + } + } + } + + New-Item -Path './versions' -ItemType Directory + New-Item -Path './versions/v-' -ItemType Directory + + New-Item -Path './versions/baseline.json' -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgBaseline) + New-Item -Path './versions/v-/vcpkg-internal-e2e-test-port2.json' -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgInternalE2eTestPortVersionsJson) + + $CurrentTest = 'git add -A' + git @gitConfigOptions add -A + Throw-IfFailed + $CurrentTest = 'git commit --amend --no-edit' git @gitConfigOptions commit --amend --no-edit Throw-IfFailed - $gitBaselineCommit = git rev-parse HEAD - $gitRefVersionsObject = git rev-parse HEAD:versions + $gitSecondaryBaselineCommit = git rev-parse HEAD + $gitSecondaryRefVersionsObject = git rev-parse HEAD:versions + + $CurrentTest = 'git switch' + git @gitConfigOptions switch $gitMainBranch + Throw-IfFailed } finally { @@ -117,18 +179,6 @@ finally # actually test the registries Write-Trace "actually test the registries" -$vcpkgJson = @{ - "name" = "manifest-test"; - "version-string" = "1.0.0"; - "dependencies" = @( - "vcpkg-internal-e2e-test-port" - ); - # Use versioning features without a builtin-baseline - "overrides" = @(@{ - "name" = "unused"; - "version" = "0"; - }) -} # test the filesystem registry Write-Trace "test the filesystem registry" @@ -140,9 +190,18 @@ $manifestDir = (Get-Item $manifestDir).FullName Push-Location $manifestDir try { - New-Item -Path 'vcpkg.json' -ItemType File ` - -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) - + $vcpkgJson = @{ + "name" = "manifest-test"; + "version-string" = "1.0.0"; + "dependencies" = @( + "vcpkg-internal-e2e-test-port" + ); + # Use versioning features without a builtin-baseline + "overrides" = @(@{ + "name" = "unused"; + "version" = "0"; + }) + } $vcpkgConfigurationJson = @{ "default-registry" = $null; "registries" = @( @@ -153,6 +212,8 @@ try } ) } + New-Item -Path 'vcpkg.json' -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) New-Item -Path 'vcpkg-configuration.json' -ItemType File ` -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgConfigurationJson) @@ -174,8 +235,19 @@ $manifestDir = (Get-Item $manifestDir).FullName Push-Location $manifestDir try { - New-Item -Path 'vcpkg.json' -ItemType File ` - -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) + $vcpkgJson = @{ + "name" = "manifest-test"; + "version-string" = "1.0.0"; + "dependencies" = @( + "vcpkg-internal-e2e-test-port", + "vcpkg-internal-e2e-test-port2" + ); + # Use versioning features without a builtin-baseline + "overrides" = @(@{ + "name" = "unused"; + "version" = "0"; + }) + } $vcpkgConfigurationJson = @{ "default-registry" = $null; @@ -183,25 +255,44 @@ try @{ "kind" = "git"; "repository" = $gitRegistryUpstream; - "baseline" = $gitBaselineCommit; - "packages" = @( "vcpkg-internal-e2e-test-port" ) + "baseline" = $gitMainBaselineCommit; + "packages" = @( "vcpkg-internal-e2e-test-port" ) + }, + @{ + "kind" = "git"; + "repository" = $gitRegistryUpstream; + "reference" = $gitSecondaryBranch; + "baseline" = $gitSecondaryBaselineCommit; + "packages" = @( "vcpkg-internal-e2e-test-port2" ) } ) } + + New-Item -Path 'vcpkg.json' -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) + New-Item -Path 'vcpkg-configuration.json' -ItemType File ` -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgConfigurationJson) Run-Vcpkg install @builtinRegistryArgs '--feature-flags=registries,manifests' --dry-run Throw-IfFailed Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$vcpkgInternalE2eTestPortGitTree + Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$vcpkgInternalE2eTestPort2GitTree # This is both the selected baseline as well as the current HEAD - Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$gitRefVersionsObject + Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$gitMainRefVersionsObject + Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$gitSecondaryRefVersionsObject # Dry run does not create a lockfile Require-FileNotExists $installRoot/vcpkg/vcpkg-lock.json Run-Vcpkg install @builtinRegistryArgs '--feature-flags=registries,manifests' Throw-IfFailed - Require-FileEquals $installRoot/vcpkg/vcpkg-lock.json "{`n $(ConvertTo-Json $gitRegistryUpstream): `"$gitBaselineCommit`"`n}`n" + + $expectedVcpkgLockJson = "{$(ConvertTo-Json $gitRegistryUpstream):{ + `"HEAD`" : `"$gitMainBaselineCommit`", + `"$gitSecondaryBranch`" : `"$gitSecondaryBaselineCommit`" + }}" + + Require-JsonFileEquals $installRoot/vcpkg/vcpkg-lock.json $expectedVcpkgLockJson # Using the lock file means we can reinstall without pulling from the upstream registry $vcpkgConfigurationJson = @{ @@ -210,18 +301,34 @@ try @{ "kind" = "git"; "repository" = "/"; # An invalid repository - "baseline" = $gitBaselineCommit; - "packages" = @( "vcpkg-internal-e2e-test-port" ) + "baseline" = $gitMainBaselineCommit; + "packages" = @( "vcpkg-internal-e2e-test-port" ) + }, + @{ + "kind" = "git"; + "repository" = "/"; # An invalid repository + "reference" = $gitSecondaryBranch; + "baseline" = $gitSecondaryBaselineCommit; + "packages" = @( "vcpkg-internal-e2e-test-port2" ) } ) } + Set-Content -Path 'vcpkg-configuration.json' ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgConfigurationJson) + Remove-Item -Recurse -Force $installRoot -ErrorAction SilentlyContinue Require-FileNotExists $installRoot New-Item -Path $installRoot/vcpkg -ItemType Directory # We pre-seed the install root with a lockfile for the invalid repository, so it isn't actually fetched from - New-Item -Path $installRoot/vcpkg/vcpkg-lock.json -ItemType File ` - -Value "{`n `"/`": `"$gitBaselineCommit`"`n}`n" + $vcpkgLockJson = @{ + "/" = @{ + "HEAD" = $gitMainBaselineCommit; + $gitSecondaryBranch = $gitSecondaryBaselineCommit + } + } + New-Item -Path $installRoot/vcpkg/vcpkg-lock.json -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgLockJson) Run-Vcpkg install @builtinRegistryArgs '--feature-flags=registries,manifests' Throw-IfFailed } diff --git a/azure-pipelines/end-to-end-tests-prelude.ps1 b/azure-pipelines/end-to-end-tests-prelude.ps1 index 4d7cf033d0..bdae1232a1 100644 --- a/azure-pipelines/end-to-end-tests-prelude.ps1 +++ b/azure-pipelines/end-to-end-tests-prelude.ps1 @@ -42,16 +42,21 @@ function Require-FileExists { } } -function Require-FileEquals { +function Require-JsonFileEquals { [CmdletBinding()] Param( [string]$File, - [string]$Content + [string]$Json ) Require-FileExists $File - if ((Get-Content $File -Raw) -ne $Content) { + $ExpectedJson = $Json | ConvertFrom-Json | ConvertTo-Json -Compress + $ActualJson = Get-Content $File | ConvertFrom-Json | ConvertTo-Json -Compress + + if ($ActualJson -ne $ExpectedJson) { Write-Stack - throw "'$Script:CurrentTest' file '$File' did not have the correct contents" + throw "'$Script:CurrentTest' file '$File' did not have the correct contents`n + Expected: $ExpectedJson`n + Actual: $ActualJson" } } diff --git a/azure-pipelines/end-to-end-tests.ps1 b/azure-pipelines/end-to-end-tests.ps1 old mode 100644 new mode 100755 diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index d4b91755c2..4a478b8f3c 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -24,24 +24,28 @@ namespace vcpkg { struct EntryData { - std::string value; + std::string reference; + std::string commit_id; bool stale; }; + + using LockDataType = std::multimap>; struct Entry { LockFile* lockfile; - std::map>::iterator data; + LockDataType::iterator data; - const std::string& value() const { return data->second.value; } + const std::string& reference() const { return data->second.reference; } + const std::string& commit_id() const { return data->second.commit_id; } bool stale() const { return data->second.stale; } const std::string& uri() const { return data->first; } void ensure_up_to_date(const VcpkgPaths& paths) const; }; - Entry get_or_fetch(const VcpkgPaths& paths, StringView key); + Entry get_or_fetch(const VcpkgPaths& paths, StringView repo, StringView reference); - std::map> lockdata; + LockDataType lockdata; bool modified = false; }; diff --git a/src/vcpkg-test/registries.cpp b/src/vcpkg-test/registries.cpp index 71b0537829..4e0b9f2c49 100644 --- a/src/vcpkg-test/registries.cpp +++ b/src/vcpkg-test/registries.cpp @@ -182,6 +182,23 @@ TEST_CASE ("registry_parsing", "[registries]") CHECK(!r.errors().empty()); } + test_json = parse_json(R"json( +{ + "kind": "git", + "repository": "abc", + "baseline": "123", + "reference": "abc/def" +} + )json"); + { + Json::Reader r; + auto registry_impl = r.visit(test_json, *registry_impl_des); + REQUIRE(registry_impl); + CHECK(*registry_impl.get()); + INFO(Strings::join("\n", r.errors())); + CHECK(r.errors().empty()); + } + test_json = parse_json(R"json( { "kind": "git", diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index bc376b5d00..412f5efa0d 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -50,8 +50,8 @@ namespace struct GitRegistry final : RegistryImplementation { - GitRegistry(std::string&& repo, std::string&& baseline) - : m_repo(std::move(repo)), m_baseline_identifier(std::move(baseline)) + GitRegistry(std::string&& repo, std::string&& reference, std::string&& baseline) + : m_repo(std::move(repo)), m_reference(std::move(reference)), m_baseline_identifier(std::move(baseline)) { } @@ -69,7 +69,7 @@ namespace LockFile::Entry get_lock_entry(const VcpkgPaths& paths) const { return m_lock_entry.get( - [this, &paths]() { return paths.get_installed_lockfile().get_or_fetch(paths, m_repo); }); + [this, &paths]() { return paths.get_installed_lockfile().get_or_fetch(paths, m_repo, m_reference); }); } Path get_versions_tree_path(const VcpkgPaths& paths) const @@ -78,7 +78,7 @@ namespace auto e = get_lock_entry(paths); e.ensure_up_to_date(paths); auto maybe_tree = - paths.git_find_object_id_for_remote_registry_path(e.value(), registry_versions_dir_name); + paths.git_find_object_id_for_remote_registry_path(e.commit_id(), registry_versions_dir_name); if (!maybe_tree) { LockGuardPtr(g_metrics)->track_property("registries-error-no-versions-at-commit", @@ -87,7 +87,7 @@ namespace VCPKG_LINE_INFO, "Error: could not find the git tree for `versions` in repo `%s` at commit `%s`: %s", m_repo, - e.value(), + e.commit_id(), maybe_tree.error()); } auto maybe_path = paths.git_checkout_object_from_remote_registry(*maybe_tree.get()); @@ -118,7 +118,7 @@ namespace if (!m_stale_versions_tree.has_value()) { auto maybe_tree = - paths.git_find_object_id_for_remote_registry_path(e.value(), registry_versions_dir_name); + paths.git_find_object_id_for_remote_registry_path(e.commit_id(), registry_versions_dir_name); if (!maybe_tree) { // This could be caused by git gc or otherwise -- fall back to full fetch @@ -136,6 +136,7 @@ namespace } std::string m_repo; + std::string m_reference; std::string m_baseline_identifier; DelayedInit m_lock_entry; mutable Optional m_stale_versions_tree; @@ -507,7 +508,7 @@ namespace "commit SHA (40 lowercase hexadecimal characters).\n" "The current HEAD of that repo is \"%s\".\n", m_repo, - e.value()); + e.commit_id()); } auto path_to_baseline = Path(registry_versions_dir_name) / "baseline.json"; @@ -717,6 +718,7 @@ namespace constexpr static StringLiteral BASELINE = "baseline"; constexpr static StringLiteral PATH = "path"; constexpr static StringLiteral REPO = "repository"; + constexpr static StringLiteral REFERENCE = "reference"; constexpr static StringLiteral KIND_BUILTIN = "builtin"; constexpr static StringLiteral KIND_FILESYSTEM = "filesystem"; @@ -737,6 +739,7 @@ namespace constexpr StringLiteral RegistryImplDeserializer::BASELINE; constexpr StringLiteral RegistryImplDeserializer::PATH; constexpr StringLiteral RegistryImplDeserializer::REPO; + constexpr StringLiteral RegistryImplDeserializer::REFERENCE; constexpr StringLiteral RegistryImplDeserializer::KIND_BUILTIN; constexpr StringLiteral RegistryImplDeserializer::KIND_FILESYSTEM; constexpr StringLiteral RegistryImplDeserializer::KIND_GIT; @@ -758,7 +761,7 @@ namespace View RegistryImplDeserializer::valid_fields() const { - static const StringView t[] = {KIND, BASELINE, PATH, REPO}; + static const StringView t[] = {KIND, BASELINE, PATH, REPO, REFERENCE}; return t; } View valid_builtin_fields() @@ -786,6 +789,7 @@ namespace RegistryImplDeserializer::KIND, RegistryImplDeserializer::BASELINE, RegistryImplDeserializer::REPO, + RegistryImplDeserializer::REFERENCE, RegistryDeserializer::PACKAGES, }; return t; @@ -839,10 +843,17 @@ namespace Json::StringDeserializer repo_des{"a git repository URL"}; r.required_object_field("a git registry", obj, REPO, repo, repo_des); + std::string ref; + Json::StringDeserializer ref_des{"a git reference (for example, a branch)"}; + if (!r.optional_object_field(obj, REFERENCE, ref, ref_des)) + { + ref = "HEAD"; + } + std::string baseline; r.required_object_field("a git registry", obj, BASELINE, baseline, baseline_deserializer); - res = std::make_unique(std::move(repo), std::move(baseline)); + res = std::make_unique(std::move(repo), std::move(ref), std::move(baseline)); } else { @@ -866,6 +877,7 @@ namespace RegistryImplDeserializer::BASELINE, RegistryImplDeserializer::PATH, RegistryImplDeserializer::REPO, + RegistryImplDeserializer::REFERENCE, PACKAGES, }; return t; @@ -1130,25 +1142,34 @@ namespace vcpkg return r.array_elements(arr, underlying); } - LockFile::Entry LockFile::get_or_fetch(const VcpkgPaths& paths, StringView key) + LockFile::Entry LockFile::get_or_fetch(const VcpkgPaths& paths, StringView repo, StringView reference) { - auto it = lockdata.find(key); - if (it == lockdata.end()) + auto range = lockdata.equal_range(repo); + auto it = std::find_if(range.first, range.second, [&reference](const LockDataType::value_type& repo2entry) { + return repo2entry.second.reference == reference; + }); + + if (it == range.second) { - print2("Fetching registry information from ", key, "...\n"); - auto x = paths.git_fetch_from_remote_registry(key, "HEAD"); - it = lockdata.emplace(key.to_string(), EntryData{x.value_or_exit(VCPKG_LINE_INFO), false}).first; + print2("Fetching registry information from ", repo, " (", reference, ")...\n"); + auto x = paths.git_fetch_from_remote_registry(repo, reference); + it = lockdata.emplace(repo.to_string(), + EntryData{reference.to_string(), x.value_or_exit(VCPKG_LINE_INFO), false}); modified = true; } + return {this, it}; } void LockFile::Entry::ensure_up_to_date(const VcpkgPaths& paths) const { if (data->second.stale) { - print2("Fetching registry information from ", data->first, "...\n"); - data->second.value = - paths.git_fetch_from_remote_registry(data->first, "HEAD").value_or_exit(VCPKG_LINE_INFO); + StringView repo(data->first); + StringView reference(data->second.reference); + print2("Fetching registry information from ", repo, " (", reference, ")...\n"); + + data->second.commit_id = + paths.git_fetch_from_remote_registry(repo, reference).value_or_exit(VCPKG_LINE_INFO); data->second.stale = false; lockfile->modified = true; } diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index fb7db74bcf..8bb1049906 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -509,6 +509,63 @@ namespace vcpkg }); } + static LockFile::LockDataType lockdata_from_json_object(const Json::Object& obj) + { + LockFile::LockDataType ret; + for (auto&& repo_to_ref_info_value : obj) + { + auto repo = repo_to_ref_info_value.first; + const auto& ref_info_value = repo_to_ref_info_value.second; + + if (!ref_info_value.is_object()) + { + Debug::print("Lockfile value for key '", repo, "' was not an object\n"); + return ret; + } + + for (auto&& reference_to_commit : ref_info_value.object()) + { + auto reference = reference_to_commit.first; + const auto& commit = reference_to_commit.second; + + if (!commit.is_string()) + { + Debug::print("Lockfile value for key '", reference, "' was not a string\n"); + return ret; + } + auto sv = commit.string(); + if (!is_git_commit_sha(sv)) + { + Debug::print("Lockfile value for key '", reference, "' was not a git commit sha\n"); + return ret; + } + ret.emplace(repo.to_string(), LockFile::EntryData{reference.to_string(), sv.to_string(), true}); + } + } + return ret; + } + + static Json::Object lockdata_to_json_object(const LockFile::LockDataType& lockdata) + { + Json::Object obj; + for (auto it = lockdata.begin(); it != lockdata.end();) + { + const auto& repo = it->first; + auto repo_info_range = lockdata.equal_range(repo); + + Json::Object repo_info; + for (auto repo_it = repo_info_range.first; repo_it != repo_info_range.second; ++repo_it) + { + repo_info.insert(repo_it->second.reference, Json::Value::string(repo_it->second.commit_id)); + } + repo_info.sort_keys(); + obj.insert(repo, std::move(repo_info)); + it = repo_info_range.second; + } + + return obj; + } + static LockFile load_lockfile(const Filesystem& fs, const Path& p) { LockFile ret; @@ -522,26 +579,14 @@ namespace vcpkg else if (auto lock_contents = maybe_lock_contents.get()) { auto& doc = lock_contents->first; - if (doc.is_object()) + if (!doc.is_object()) { - for (auto&& x : doc.object()) - { - if (!x.second.is_string()) - { - Debug::print("Lockfile value for key '", x.first, "' was not a string\n"); - return ret; - } - auto sv = x.second.string(); - if (!is_git_commit_sha(sv)) - { - Debug::print("Lockfile value for key '", x.first, "' was not a git commit sha\n"); - return ret; - } - ret.lockdata.emplace(x.first.to_string(), LockFile::EntryData{sv.to_string(), true}); - } + Debug::print("Lockfile was not an object\n"); return ret; } - Debug::print("Lockfile was not an object\n"); + + ret.lockdata = lockdata_from_json_object(doc.object()); + return ret; } else @@ -559,6 +604,7 @@ namespace vcpkg } return *m_pimpl->m_installed_lock.get(); } + void VcpkgPaths::flush_lockfile() const { // If the lock file was not loaded, no need to flush it. @@ -566,11 +612,9 @@ namespace vcpkg // lockfile was not modified, no need to write anything to disk. const auto& lockfile = *m_pimpl->m_installed_lock.get(); if (!lockfile.modified) return; - Json::Object obj; - for (auto&& data : lockfile.lockdata) - { - obj.insert(data.first, Json::Value::string(data.second.value)); - } + + auto obj = lockdata_to_json_object(lockfile.lockdata); + get_filesystem().write_rename_contents( lockfile_path(*this), "vcpkg-lock.json.tmp", Json::stringify(obj, {}), VCPKG_LINE_INFO); }