* Initial implementation of pseudo-lockfiles

* Migrate e2e test collateral

* Add git registry parsing unit tests

* Increase lockfile value validation (40 hex)

* Fix MSVC Analyze

* Introduce vcpkg-lock.json e2e testing

Add redirection for the registries cache directory via X_VCPKG_REGISTRIES_CACHE

* Fix unit tests

* Handle JSON escaping in lock file test

* Rename VTPResult to VersionsTreePathResult

* Format all version-files collateral

Co-authored-by: Robert Schumacher <ras0219@outlook.com>
This commit is contained in:
Robert Schumacher 2021-05-04 12:36:05 -07:00 коммит произвёл GitHub
Родитель b2c742187d
Коммит ee93e8f487
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
52 изменённых файлов: 803 добавлений и 250 удалений

Просмотреть файл

@ -0,0 +1,8 @@
{
"name": "default-baseline-test",
"version-string": "0",
"builtin-baseline": "fca18ba3572f8aebe3b8158c359db62a7e26134e",
"dependencies": [
"zlib"
]
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"name": "default-baseline-test-2",
"version-string": "0",
"builtin-baseline": "d5cd6b8c74ee548cfc9ff83cefdac4843cc1503f",
"dependencies": [
"zlib"
]
}

Просмотреть файл

@ -0,0 +1,14 @@
{
"versions": [
{
"git-tree": "7bb2b2f3783303a4dd41163553fe4cc103dc9262",
"version-string": "1.2.11",
"port-version": 9
},
{
"git-tree": "4927735fa9baca564ebddf6e6880de344b20d7a8",
"version-string": "1.2.11",
"port-version": 8
}
]
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "cat",
"version": "1.0"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "dog",
"version-date": "2001-01-01"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "duck",
"version-string": "mallard"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "mouse",
"version-semver": "1.0.0"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "cat",
"version": "1.0"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,5 @@
{
"name": "dog",
"version-date": "2001-01-01",
"port-version": 1
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "duck",
"version-string": "mallard"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "ferret",
"version": "1"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,5 @@
{
"name": "fish",
"version-string": "1.0.0",
"description": "This description causes an intentional discrepancy between the local SHA and the SHA in fish.json for version 1.0.0"
}

Просмотреть файл

@ -0,0 +1 @@
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Просмотреть файл

@ -0,0 +1,4 @@
{
"name": "mouse",
"version-semver": "1.0.0"
}

Просмотреть файл

@ -0,0 +1,20 @@
{
"default": {
"cat": {
"baseline": "1.0",
"port-version": 0
},
"dog": {
"baseline": "2001-01-01",
"port-version": 0
},
"duck": {
"baseline": "mallard",
"port-version": 0
},
"mouse": {
"baseline": "1.0.0",
"port-version": 0
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "e635ee8b3277303dfc7231d526e04f1102b56605",
"version": "1.0",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "d6c3e716b6fbd5f8a0690376b6fdadf8a69204d8",
"version-date": "2001-01-01",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "e2974a551846257708d1e0dbdf81cfd76abf8e97",
"version-string": "mallard",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "f8882feb032d2aacd83340decb0966c2dacc3fd6",
"version-semver": "1.0.0",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,20 @@
{
"default": {
"cat": {
"baseline": "1.0",
"port-version": 0
},
"dog": {
"baseline": "2001-01-01",
"port-version": 0
},
"duck": {
"baseline": "mallard",
"port-version": 0
},
"fish": {
"baseline": "1.0.0",
"port-version": 0
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "e635ee8b3277303dfc7231d526e04f1102b56605",
"version": "1.0",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "e170a2ed0da7ba5d434c4a0a98ffd7a3159e3200",
"version-date": "2001-01-01",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "cf3be634f203c1b4152b65ec7700d5695a1fca5c",
"version-string": "1.0.0",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"versions": [
{
"git-tree": "f8882feb032d2aacd83340decb0966c2dacc3fd6",
"version-semver": "1.0.0",
"port-version": 0
}
]
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"name": "without-default-baseline-test-2",
"version-string": "0",
"dependencies": [
"zlib"
]
}

Просмотреть файл

@ -0,0 +1,14 @@
{
"versions": [
{
"git-tree": "7bb2b2f3783303a4dd41163553fe4cc103dc9262",
"version-string": "1.2.11",
"port-version": 9
},
{
"git-tree": "4927735fa9baca564ebddf6e6880de344b20d7a8",
"version-string": "1.2.11",
"port-version": 8
}
]
}

Просмотреть файл

@ -3,7 +3,7 @@
# Test vcpkg create
$Script:CurrentTest = "create zlib"
Write-Host $Script:CurrentTest
./vcpkg --x-builtin-ports-root=$TestingRoot/ports create zlib https://github.com/madler/zlib/archive/v1.2.11.tar.gz zlib-1.2.11.tar.gz
Run-Vcpkg --x-builtin-ports-root=$TestingRoot/ports create zlib https://github.com/madler/zlib/archive/v1.2.11.tar.gz zlib-1.2.11.tar.gz
Throw-IfFailed
Require-FileExists "$TestingRoot/ports/zlib/portfile.cmake"

Просмотреть файл

@ -107,6 +107,9 @@ try
$CurrentTest = 'git commit'
git @gitConfigOptions commit --amend --no-edit
Throw-IfFailed
$gitBaselineCommit = git rev-parse HEAD
$gitRefVersionsObject = git rev-parse HEAD:versions
}
finally
{
@ -176,6 +179,7 @@ try
@{
"kind" = "git";
"repository" = $gitRegistryUpstream;
"baseline" = $gitBaselineCommit;
"packages" = @( "vcpkg-internal-e2e-test-port" )
}
)
@ -183,6 +187,37 @@ try
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
# This is both the selected baseline as well as the current HEAD
Require-FileExists $env:X_VCPKG_REGISTRIES_CACHE/git-trees/$gitRefVersionsObject
# 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"
# Using the lock file means we can reinstall without pulling from the upstream registry
$vcpkgConfigurationJson = @{
"default-registry" = $null;
"registries" = @(
@{
"kind" = "git";
"repository" = "/"; # An invalid repository
"baseline" = $gitBaselineCommit;
"packages" = @( "vcpkg-internal-e2e-test-port" )
}
)
}
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"
Run-Vcpkg install @builtinRegistryArgs '--feature-flags=registries,manifests'
Throw-IfFailed
}

Просмотреть файл

@ -3,7 +3,7 @@
##### Test spaces in the path
$Script:CurrentTest = "zlib with spaces in path"
Write-Host $Script:CurrentTest
./vcpkg install zlib "--triplet" $Triplet `
Run-Vcpkg install zlib "--triplet" $Triplet `
"--no-binarycaching" `
"--x-buildtrees-root=$TestingRoot/build Trees" `
"--x-install-root=$TestingRoot/instalL ed" `

Просмотреть файл

@ -1,9 +1,9 @@
. $PSScriptRoot/../end-to-end-tests-prelude.ps1
$versionFilesPath = "$env:VCPKG_ROOT/scripts/testing/version-files"
$versionFilesPath = "$PSScriptRoot/../e2e_ports/version-files"
# Test verify versions
mkdir $VersionFilesRoot
mkdir $VersionFilesRoot | Out-Null
Copy-Item -Recurse "$versionFilesPath/versions_incomplete" $VersionFilesRoot
$portsRedirectArgsOK = @(
"--feature-flags=versions",
@ -17,51 +17,51 @@ $portsRedirectArgsIncomplete = @(
)
$CurrentTest = "x-verify-ci-versions (All files OK)"
Write-Host $CurrentTest
./vcpkg $portsRedirectArgsOK x-ci-verify-versions --verbose
Run-Vcpkg @portsRedirectArgsOK x-ci-verify-versions --verbose --debug
Throw-IfFailed
$CurrentTest = "x-verify-ci-versions (Incomplete)"
./vcpkg $portsRedirectArgsIncomplete x-ci-verify-versions --verbose
Run-Vcpkg @portsRedirectArgsIncomplete x-ci-verify-versions --verbose
Throw-IfNotFailed
$CurrentTest = "x-add-version cat"
# Do not fail if there's nothing to update
./vcpkg $portsRedirectArgsIncomplete x-add-version cat --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version cat
Throw-IfFailed
$CurrentTest = "x-add-version dog"
# Local version is not in baseline and versions file
./vcpkg $portsRedirectArgsIncomplete x-add-version dog --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version dog
Throw-IfFailed
$CurrentTest = "x-add-version duck"
# Missing versions file
./vcpkg $portsRedirectArgsIncomplete x-add-version duck --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version duck
Throw-IfFailed
$CurrentTest = "x-add-version ferret"
# Missing versions file and missing baseline entry
./vcpkg $portsRedirectArgsIncomplete x-add-version ferret --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version ferret
Throw-IfFailed
$CurrentTest = "x-add-version fish (must fail)"
# Discrepancy between local SHA and SHA in fish.json. Requires --overwrite-version.
$out = ./vcpkg $portsRedirectArgsIncomplete x-add-version fish --skip-formatting-check
$out = Run-Vcpkg @portsRedirectArgsIncomplete x-add-version fish
Throw-IfNotFailed
$CurrentTest = "x-add-version fish --overwrite-version"
./vcpkg $portsRedirectArgsIncomplete x-add-version fish --overwrite-version --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version fish --overwrite-version
Throw-IfFailed
$CurrentTest = "x-add-version mouse"
# Missing baseline entry
./vcpkg $portsRedirectArgsIncomplete x-add-version mouse --skip-formatting-check
Run-Vcpkg @portsRedirectArgsIncomplete x-add-version mouse
Throw-IfFailed
# Validate changes
./vcpkg $portsRedirectArgsIncomplete x-ci-verify-versions --verbose
Run-Vcpkg @portsRedirectArgsIncomplete x-ci-verify-versions --verbose
Throw-IfFailed
$CurrentTest = "default baseline"
$out = ./vcpkg $commonArgs "--feature-flags=versions" install --x-manifest-root=$versionFilesPath/default-baseline-1 2>&1 | Out-String
$out = Run-Vcpkg @commonArgs "--feature-flags=versions" install --x-manifest-root=$versionFilesPath/default-baseline-1 2>&1 | Out-String
Throw-IfNotFailed
if ($out -notmatch ".*Error: while checking out baseline.*")
{
@ -76,7 +76,7 @@ foreach ($opt_registries in @("",",registries"))
Refresh-TestRoot
$CurrentTest = "without default baseline 2 -- enabling versions should not change behavior"
Remove-Item -Recurse $buildtreesRoot/versioning -ErrorAction SilentlyContinue
./vcpkg $commonArgs "--feature-flags=versions$opt_registries" install `
Run-Vcpkg @commonArgs "--feature-flags=versions$opt_registries" install `
"--dry-run" `
"--x-manifest-root=$versionFilesPath/without-default-baseline-2" `
"--x-builtin-registry-versions-dir=$versionFilesPath/default-baseline-2/versions"
@ -84,7 +84,7 @@ foreach ($opt_registries in @("",",registries"))
Require-FileNotExists $buildtreesRoot/versioning
$CurrentTest = "default baseline 2"
./vcpkg $commonArgs "--feature-flags=versions$opt_registries" install `
Run-Vcpkg @commonArgs "--feature-flags=versions$opt_registries" install `
"--dry-run" `
"--x-manifest-root=$versionFilesPath/default-baseline-2" `
"--x-builtin-registry-versions-dir=$versionFilesPath/default-baseline-2/versions"
@ -92,7 +92,7 @@ foreach ($opt_registries in @("",",registries"))
Require-FileExists $buildtreesRoot/versioning
$CurrentTest = "using version features fails without flag"
./vcpkg $commonArgs "--feature-flags=-versions$opt_registries" install `
Run-Vcpkg @commonArgs "--feature-flags=-versions$opt_registries" install `
"--dry-run" `
"--x-manifest-root=$versionFilesPath/default-baseline-2" `
"--x-builtin-registry-versions-dir=$versionFilesPath/default-baseline-2/versions"

Просмотреть файл

@ -5,7 +5,7 @@ $packagesRoot = Join-Path $TestingRoot 'packages'
$NuGetRoot = Join-Path $TestingRoot 'nuget'
$NuGetRoot2 = Join-Path $TestingRoot 'nuget2'
$ArchiveRoot = Join-Path $TestingRoot 'archives'
$VersionFilesRoot = Join-Path $env:VCPKG_ROOT 'version-test'
$VersionFilesRoot = Join-Path $TestingRoot 'version-test'
$commonArgs = @(
"--triplet",
$Triplet,
@ -16,41 +16,66 @@ $commonArgs = @(
"--overlay-triplets=$PSScriptRoot/e2e_ports/triplets"
)
$Script:CurrentTest = 'unassigned'
$env:X_VCPKG_REGISTRIES_CACHE = Join-Path $TestingRoot 'registries'
function Refresh-TestRoot {
Remove-Item -Recurse -Force $TestingRoot -ErrorAction SilentlyContinue
mkdir $TestingRoot | Out-Null
mkdir $env:X_VCPKG_REGISTRIES_CACHE | Out-Null
mkdir $NuGetRoot | Out-Null
}
function Write-Stack {
Get-PSCallStack | % {
Write-Host "$($_.ScriptName):$($_.ScriptLineNumber): $($_.FunctionName)"
}
}
function Require-FileExists {
[CmdletBinding()]
Param(
[string]$File
)
if (-Not (Test-Path $File)) {
Write-Stack
throw "'$Script:CurrentTest' failed to create file '$File'"
}
}
function Require-FileEquals {
[CmdletBinding()]
Param(
[string]$File,
[string]$Content
)
Require-FileExists $File
if ((Get-Content $File -Raw) -ne $Content) {
Write-Stack
throw "'$Script:CurrentTest' file '$File' did not have the correct contents"
}
}
function Require-FileNotExists {
[CmdletBinding()]
Param(
[string]$File
)
if (Test-Path $File) {
Write-Stack
throw "'$Script:CurrentTest' should not have created file '$File'"
}
}
function Throw-IfFailed {
if ($LASTEXITCODE -ne 0) {
Write-Stack
throw "'$Script:CurrentTest' had a step with a nonzero exit code"
}
}
function Throw-IfNotFailed {
if ($LASTEXITCODE -eq 0) {
Write-Stack
throw "'$Script:CurrentTest' had a step with an unexpectedly zero exit code"
}
}

Просмотреть файл

@ -71,7 +71,8 @@ $envvars_clear = @(
"VCPKG_KEEP_ENV_VARS",
"VCPKG_ROOT",
"VCPKG_FEATURE_FLAGS",
"VCPKG_DISABLE_METRICS"
"VCPKG_DISABLE_METRICS",
"X_VCPKG_REGISTRIES_CACHE"
)
$envvars = $envvars_clear + @("VCPKG_DOWNLOADS")

Просмотреть файл

@ -182,6 +182,10 @@ namespace vcpkg::Files
std::error_code& ec) = 0;
void write_contents(const fs::path& path, const std::string& data, LineInfo linfo);
virtual void write_contents(const fs::path& file_path, const std::string& data, std::error_code& ec) = 0;
void write_rename_contents(const fs::path& path,
const fs::path& tmpext,
const std::string& data,
LineInfo linfo);
void write_contents_and_dirs(const fs::path& path, const std::string& data, LineInfo linfo);
virtual void write_contents_and_dirs(const fs::path& file_path,
const std::string& data,

Просмотреть файл

@ -6,4 +6,5 @@ namespace vcpkg
struct RegistryImplementation;
struct Registry;
struct RegistrySet;
struct LockFile;
}

Просмотреть файл

@ -20,6 +20,31 @@
namespace vcpkg
{
struct LockFile
{
struct EntryData
{
std::string value;
bool stale;
};
struct Entry
{
LockFile* lockfile;
std::map<std::string, EntryData, std::less<>>::iterator data;
const std::string& value() const { return data->second.value; }
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);
std::map<std::string, EntryData, std::less<>> lockdata;
bool modified = false;
};
struct RegistryEntry
{
virtual View<VersionT> get_port_versions() const = 0;
@ -109,4 +134,6 @@ namespace vcpkg
StringView port_name);
ExpectedS<std::map<std::string, VersionT, std::less<>>> get_builtin_baseline(const VcpkgPaths& paths);
bool is_git_commit_sha(StringView sv);
}

Просмотреть файл

@ -86,6 +86,9 @@ namespace vcpkg
StringView get_ports_cmake_hash() const;
const fs::path get_triplet_file_path(Triplet triplet) const;
LockFile& get_installed_lockfile() const;
void flush_lockfile() const;
fs::path original_cwd;
fs::path root;
fs::path manifest_root_dir;
@ -141,8 +144,10 @@ namespace vcpkg
// Git manipulation for remote registries
// runs `git fetch {uri} {treeish}`, and returns the hash of FETCH_HEAD.
// If `treeish` is empty, then just runs `git fetch {uri}`
ExpectedS<std::string> git_fetch_from_remote_registry(StringView uri, StringView treeish = {}) const;
// Use {treeish} of "HEAD" for the default branch
ExpectedS<std::string> git_fetch_from_remote_registry(StringView uri, StringView treeish) const;
// runs `git fetch {uri} {treeish}`
Optional<std::string> git_fetch(StringView uri, StringView treeish) const;
ExpectedS<std::string> git_show_from_remote_registry(StringView hash,
const fs::path& relative_path_to_file) const;
ExpectedS<std::string> git_find_object_id_for_remote_registry_path(StringView hash,
@ -177,13 +182,5 @@ namespace vcpkg
private:
std::unique_ptr<details::VcpkgPathsImpl> m_pimpl;
static void git_checkout_subpath(const VcpkgPaths& paths,
StringView commit_sha,
const fs::path& subpath,
const fs::path& local_repo,
const fs::path& destination,
const fs::path& dot_git_dir,
const fs::path& work_tree);
};
}

Просмотреть файл

@ -76,59 +76,109 @@ TEST_CASE ("registry_set_selects_registry", "[registries]")
TEST_CASE ("registry_parsing", "[registries]")
{
Json::Reader r;
auto registry_impl_des = get_registry_implementation_deserializer({});
{
Json::Reader r;
auto test_json = parse_json(R"json(
auto test_json = parse_json(R"json(
{
"kind": "builtin"
}
)json");
auto registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
auto registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
test_json = parse_json(R"json(
test_json = parse_json(R"json(
{
"kind": "builtin",
"baseline": "hi"
}
)json");
registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
test_json = parse_json(R"json(
test_json = parse_json(R"json(
{
"kind": "builtin",
"path": "a/b"
}
)json");
registry_impl = r.visit(test_json, *registry_impl_des);
CHECK_FALSE(r.errors().empty());
r.errors().clear();
registry_impl = r.visit(test_json, *registry_impl_des);
CHECK_FALSE(r.errors().empty());
r.errors().clear();
test_json = parse_json(R"json(
test_json = parse_json(R"json(
{
"kind": "filesystem",
"path": "a/b/c"
}
)json");
registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
test_json = parse_json(R"json(
test_json = parse_json(R"json(
{
"kind": "filesystem",
"path": "/a/b/c"
}
)json");
registry_impl = r.visit(test_json, *registry_impl_des);
registry_impl = r.visit(test_json, *registry_impl_des);
REQUIRE(registry_impl);
CHECK(*registry_impl.get());
CHECK(r.errors().empty());
}
auto test_json = parse_json(R"json(
{
"kind": "git"
}
)json");
{
Json::Reader r;
r.visit(test_json, *registry_impl_des);
CHECK(!r.errors().empty());
}
test_json = parse_json(R"json(
{
"kind": "git",
"repository": "abc"
}
)json");
{
Json::Reader r;
r.visit(test_json, *registry_impl_des);
CHECK(!r.errors().empty());
}
test_json = parse_json(R"json(
{
"kind": "git",
"baseline": "123"
}
)json");
{
Json::Reader r;
r.visit(test_json, *registry_impl_des);
CHECK(!r.errors().empty());
}
test_json = parse_json(R"json(
{
"kind": "git",
"repository": "abc",
"baseline": "123"
}
)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());
}

Просмотреть файл

@ -487,6 +487,16 @@ namespace vcpkg::Files
Checks::exit_with_message(linfo, "error writing file: %s: %s", fs::u8string(path), ec.message());
}
}
void Filesystem::write_rename_contents(const fs::path& path,
const fs::path& tmpname,
const std::string& data,
LineInfo linfo)
{
auto tmp_path = path;
tmp_path.replace_filename(tmpname);
this->write_contents(tmp_path, data, linfo);
this->rename(tmp_path, path, linfo);
}
void Filesystem::write_contents_and_dirs(const fs::path& path, const std::string& data, LineInfo linfo)
{
std::error_code ec;

Просмотреть файл

@ -236,8 +236,8 @@ namespace vcpkg::Commands::CIVerifyVersions
port_name,
fs::u8string(versions_file_path),
top_entry.first.versiont,
local_git_tree,
top_entry.second,
local_git_tree,
port_name),
expected_right_tag,
};

Просмотреть файл

@ -104,6 +104,8 @@ namespace vcpkg::Commands::SetInstalled
Checks::exit_success(VCPKG_LINE_INFO);
}
paths.flush_lockfile();
Install::track_install_plan(action_plan);
const auto summary = Install::perform(args,

Просмотреть файл

@ -1043,6 +1043,8 @@ namespace vcpkg::Install
Checks::exit_success(VCPKG_LINE_INFO);
}
paths.flush_lockfile();
track_install_plan(action_plan);
const InstallSummary summary =

Просмотреть файл

@ -22,21 +22,29 @@ namespace
static const fs::path registry_versions_dir_name = fs::u8path("versions");
// this class is an implementation detail of `BuiltinRegistryEntry`;
// when `BuiltinRegistryEntry` is using a port versions file for a port,
// it uses this as it's underlying type;
// when `BuiltinRegistryEntry` is using a port tree, it uses the scfl
struct GitRegistry;
struct GitRegistryEntry final : RegistryEntry
{
View<VersionT> get_port_versions() const override { return port_versions; }
GitRegistryEntry(const GitRegistry& reg, StringView name);
View<VersionT> get_port_versions() const override;
ExpectedS<fs::path> get_path_to_version(const VcpkgPaths&, const VersionT& version) const override;
private:
void fill_data_from_path(const Files::Filesystem& fs, const fs::path& port_versions_path) const;
std::string port_name;
const GitRegistry& parent;
// Indicates whether port_versions and git_trees were filled in with stale (i.e. lock) data.
mutable bool stale;
// these two map port versions to git trees
// these shall have the same size, and git_trees[i] shall be the git tree for port_versions[i]
std::vector<VersionT> port_versions;
std::vector<std::string> git_trees;
mutable std::vector<VersionT> port_versions;
mutable std::vector<std::string> git_trees;
};
struct GitRegistry final : RegistryImplementation
@ -54,26 +62,22 @@ namespace
Optional<VersionT> get_baseline_version(const VcpkgPaths&, StringView) const override;
StringView get_commit_of_repo(const VcpkgPaths& paths) const
private:
friend struct GitRegistryEntry;
LockFile::Entry get_lock_entry(const VcpkgPaths& paths) const
{
return m_commit.get([this, &paths]() -> std::string {
auto maybe_hash = paths.git_fetch_from_remote_registry(m_repo);
if (!maybe_hash.has_value())
{
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Failed to fetch from remote registry `%s`: %s",
m_repo,
maybe_hash.error());
}
return std::move(*maybe_hash.get());
});
return m_lock_entry.get(
[this, &paths]() { return paths.get_installed_lockfile().get_or_fetch(paths, m_repo); });
}
fs::path get_versions_tree_path(const VcpkgPaths& paths) const
{
return m_versions_tree.get([this, &paths]() -> fs::path {
auto maybe_tree = paths.git_find_object_id_for_remote_registry_path(get_commit_of_repo(paths),
registry_versions_dir_name);
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);
if (!maybe_tree)
{
Metrics::g_metrics.lock()->track_property("registries-error-no-versions-at-commit", "defined");
@ -81,7 +85,7 @@ namespace
VCPKG_LINE_INFO,
"Error: could not find the git tree for `versions` in repo `%s` at commit `%s`: %s",
m_repo,
get_commit_of_repo(paths),
e.value(),
maybe_tree.error());
}
auto maybe_path = paths.git_checkout_object_from_remote_registry(*maybe_tree.get());
@ -96,11 +100,48 @@ namespace
});
}
struct VersionsTreePathResult
{
fs::path path;
bool stale;
};
VersionsTreePathResult get_stale_versions_tree_path(const VcpkgPaths& paths) const
{
auto e = get_lock_entry(paths);
if (!e.stale())
{
return {get_versions_tree_path(paths), false};
}
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);
if (!maybe_tree)
{
// This could be caused by git gc or otherwise -- fall back to full fetch
return {get_versions_tree_path(paths), false};
}
auto maybe_path = paths.git_checkout_object_from_remote_registry(*maybe_tree.get());
if (!maybe_path)
{
// This could be caused by git gc or otherwise -- fall back to full fetch
return {get_versions_tree_path(paths), false};
}
m_stale_versions_tree = std::move(*maybe_path.get());
}
return {*m_stale_versions_tree.get(), true};
}
std::string m_repo;
DelayedInit<std::string> m_commit; // TODO: eventually this should end up in the vcpkg-lock.json file
DelayedInit<fs::path> m_versions_tree;
std::string m_baseline_identifier;
DelayedInit<LockFile::Entry> m_lock_entry;
mutable Optional<fs::path> m_stale_versions_tree;
DelayedInit<fs::path> m_versions_tree;
DelayedInit<Baseline> m_baseline;
// Shared by all child GitRegistryEntry's
mutable const VcpkgPaths* m_paths = nullptr;
};
struct BuiltinPortTreeRegistryEntry final : RegistryEntry
@ -491,101 +532,95 @@ namespace
// { GitRegistry::RegistryImplementation
std::unique_ptr<RegistryEntry> GitRegistry::get_port_entry(const VcpkgPaths& paths, StringView port_name) const
{
auto port_versions = get_versions_tree_path(paths);
auto maybe_version_entries =
load_versions_file(paths.get_filesystem(), VersionDbType::Git, port_versions, port_name);
Checks::check_maybe_upgrade(
VCPKG_LINE_INFO, maybe_version_entries.has_value(), "Error: " + maybe_version_entries.error());
auto version_entries = std::move(maybe_version_entries).value_or_exit(VCPKG_LINE_INFO);
if (!m_paths) m_paths = &paths;
return std::make_unique<GitRegistryEntry>(*this, port_name);
}
auto res = std::make_unique<GitRegistryEntry>();
res->port_name = port_name.to_string();
for (auto&& version_entry : version_entries)
{
res->port_versions.push_back(version_entry.version);
res->git_trees.push_back(version_entry.git_tree);
}
return res;
GitRegistryEntry::GitRegistryEntry(const GitRegistry& reg, StringView name)
: port_name(name.to_string()), parent(reg)
{
// This guarantees that parent.m_paths has been filled before any entries are constructed.
// The entries may then rely on (bool)parent.m_paths during their operation
Checks::check_exit(VCPKG_LINE_INFO, parent.m_paths);
#if defined(_MSC_VER)
__assume(parent.m_paths);
#endif
auto vtp = parent.get_stale_versions_tree_path(*parent.m_paths);
stale = vtp.stale;
fill_data_from_path(parent.m_paths->get_filesystem(), vtp.path);
}
Optional<VersionT> GitRegistry::get_baseline_version(const VcpkgPaths& paths, StringView port_name) const
{
const auto& baseline = m_baseline.get([this, &paths]() -> Baseline {
auto baseline_file = get_versions_tree_path(paths) / fs::u8path("baseline.json");
auto res_baseline = load_baseline_versions(paths, baseline_file, m_baseline_identifier);
if (!res_baseline.has_value())
// We delay baseline validation until here to give better error messages and suggestions
if (!is_git_commit_sha(m_baseline_identifier))
{
Checks::exit_maybe_upgrade(VCPKG_LINE_INFO, res_baseline.error());
}
auto opt_baseline = res_baseline.get();
if (auto p = opt_baseline->get())
{
return std::move(*p);
}
if (m_baseline_identifier.empty())
{
return {};
}
if (m_baseline_identifier == "default")
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_with_message(
auto e = get_lock_entry(paths);
e.ensure_up_to_date(paths);
Checks::exit_maybe_upgrade(
VCPKG_LINE_INFO,
"Couldn't find explicitly specified baseline `\"default\"` in the baseline file.",
m_baseline_identifier);
}
// attempt to check out the baseline:
auto explicit_hash = paths.git_fetch_from_remote_registry(m_repo, m_baseline_identifier);
if (!explicit_hash.has_value())
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Error: Couldn't find explicitly specified baseline `\"%s\"` in the baseline file for repo %s, "
"and that commit doesn't exist.\n%s",
m_baseline_identifier,
"Error: the git registry entry for \"%s\" must have a \"baseline\" field that is a valid git "
"commit SHA (40 lowercase hexadecimal characters).\n"
"The current HEAD of that repo is \"%s\".\n",
m_repo,
explicit_hash.error());
e.value());
}
auto path_to_baseline = registry_versions_dir_name / fs::u8path("baseline.json");
auto maybe_contents = paths.git_show_from_remote_registry(*explicit_hash.get(), path_to_baseline);
auto maybe_contents = paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline);
if (!maybe_contents.has_value())
{
if (auto err = paths.git_fetch(m_repo, m_baseline_identifier))
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Error: Couldn't find baseline `\"%s\"` for repo %s:\n%s\nError: Failed to fetch %s:\n%s",
m_baseline_identifier,
m_repo,
maybe_contents.error(),
m_repo,
*err.get());
}
maybe_contents = paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline);
}
if (!maybe_contents.has_value())
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Error: Couldn't find explicitly specified baseline `\"%s\"` in the baseline file for repo %s, "
"and the baseline file doesn't exist at that commit.\n%s\n",
m_baseline_identifier,
m_repo,
maybe_contents.error());
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Couldn't find baseline in commit `\"%s\"` from repo %s:\n%s\n",
m_baseline_identifier,
m_repo,
maybe_contents.error());
}
auto contents = maybe_contents.get();
res_baseline = parse_baseline_versions(*contents, "default", fs::u8string(path_to_baseline));
if (!res_baseline.has_value())
auto res_baseline = parse_baseline_versions(*contents, "default", fs::u8string(path_to_baseline));
if (auto opt_baseline = res_baseline.get())
{
Checks::exit_with_message(VCPKG_LINE_INFO, res_baseline.error());
}
opt_baseline = res_baseline.get();
if (auto p = opt_baseline->get())
{
return std::move(*p);
if (auto p = opt_baseline->get())
{
return std::move(*p);
}
else
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_maybe_upgrade(
VCPKG_LINE_INFO,
"The baseline.json from commit `\"%s\"` in the repo %s did not contain a \"default\" field.",
m_baseline_identifier,
m_repo);
}
}
else
{
Metrics::g_metrics.lock()->track_property("registries-error-could-not-find-baseline", "defined");
Checks::exit_maybe_upgrade(
VCPKG_LINE_INFO,
"Couldn't find explicitly specified baseline `\"%s\"` in the baseline file for repo %s, "
"and the `\"default\"` baseline does not exist at that commit.",
m_baseline_identifier,
m_repo);
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error while fetching baseline `\"%s\"` from repo %s:\n%s",
m_baseline_identifier,
m_repo,
res_baseline.error());
}
});
@ -647,19 +682,31 @@ namespace
// } FilesystemRegistryEntry::RegistryEntry
// { GitRegistryEntry::RegistryEntry
View<VersionT> GitRegistryEntry::get_port_versions() const
{
if (stale)
{
fill_data_from_path(parent.m_paths->get_filesystem(), parent.get_versions_tree_path(*parent.m_paths));
stale = false;
}
return port_versions;
}
ExpectedS<fs::path> GitRegistryEntry::get_path_to_version(const VcpkgPaths& paths, const VersionT& version) const
{
auto it = std::find(port_versions.begin(), port_versions.end(), version);
if (it == port_versions.end() && stale)
{
fill_data_from_path(parent.m_paths->get_filesystem(), parent.get_versions_tree_path(*parent.m_paths));
stale = false;
it = std::find(port_versions.begin(), port_versions.end(), version);
}
if (it == port_versions.end())
{
// This message suggests that the user updates vcpkg -- this is appropriate for the builtin registry for now
// but needs tweaking for external git registries
return {Strings::concat("Error: No version entry for ",
port_name,
" at version ",
version,
". This may be fixed by updating vcpkg to the latest master via `git "
"pull`.\nAvailable versions:\n",
".\nAvailable versions:\n",
Strings::join("",
port_versions,
[](const VersionT& v) { return Strings::concat(" ", v, "\n"); }),
@ -670,6 +717,21 @@ namespace
const auto& git_tree = git_trees[it - port_versions.begin()];
return paths.git_checkout_object_from_remote_registry(git_tree);
}
void GitRegistryEntry::fill_data_from_path(const Files::Filesystem& fs, const fs::path& port_versions_path) const
{
auto maybe_version_entries = load_versions_file(fs, VersionDbType::Git, port_versions_path, port_name);
Checks::check_maybe_upgrade(
VCPKG_LINE_INFO, maybe_version_entries.has_value(), "Error: " + maybe_version_entries.error());
auto version_entries = std::move(maybe_version_entries).value_or_exit(VCPKG_LINE_INFO);
for (auto&& version_entry : version_entries)
{
port_versions.push_back(version_entry.version);
git_trees.push_back(version_entry.git_tree);
}
}
// } GitRegistryEntry::RegistryEntry
// } RegistryEntry
@ -896,20 +958,22 @@ namespace
static Json::StringDeserializer kind_deserializer{"a registry implementation kind"};
static Json::StringDeserializer baseline_deserializer{"a baseline"};
std::string kind;
std::string baseline;
r.required_object_field(type_name(), obj, KIND, kind, kind_deserializer);
r.optional_object_field(obj, BASELINE, baseline, baseline_deserializer);
std::unique_ptr<RegistryImplementation> res;
if (kind == KIND_BUILTIN)
{
std::string baseline;
r.optional_object_field(obj, BASELINE, baseline, baseline_deserializer);
r.check_for_unexpected_fields(obj, valid_builtin_fields(), "a builtin registry");
res = std::make_unique<BuiltinRegistry>(std::move(baseline));
}
else if (kind == KIND_FILESYSTEM)
{
std::string baseline;
r.optional_object_field(obj, BASELINE, baseline, baseline_deserializer);
r.check_for_unexpected_fields(obj, valid_filesystem_fields(), "a filesystem registry");
fs::path path;
@ -925,6 +989,9 @@ namespace
Json::StringDeserializer repo_des{"a git repository URL"};
r.required_object_field("a git registry", obj, REPO, repo, repo_des);
std::string baseline;
r.required_object_field("a git registry", obj, BASELINE, baseline, baseline_deserializer);
res = std::make_unique<GitRegistry>(std::move(repo), std::move(baseline));
}
else
@ -1100,6 +1167,28 @@ namespace
namespace vcpkg
{
LockFile::Entry LockFile::get_or_fetch(const VcpkgPaths& paths, StringView key)
{
auto it = lockdata.find(key);
if (it == lockdata.end())
{
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;
modified = true;
}
return {this, it};
}
void LockFile::Entry::ensure_up_to_date(const VcpkgPaths& paths) const
{
if (data->second.stale)
{
data->second.value =
paths.git_fetch_from_remote_registry(data->first, "HEAD").value_or_exit(VCPKG_LINE_INFO);
data->second.stale = false;
lockfile->modified = true;
}
}
std::unique_ptr<Json::IDeserializer<std::unique_ptr<RegistryImplementation>>>
get_registry_implementation_deserializer(const fs::path& configuration_directory)
{
@ -1217,4 +1306,14 @@ namespace vcpkg
{
return try_parse_builtin_baseline(paths, "default");
}
bool is_git_commit_sha(StringView sv)
{
static constexpr struct
{
bool operator()(char ch) { return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'); }
} is_lcase_ascii_hex;
return sv.size() == 40 && std::all_of(sv.begin(), sv.end(), is_lcase_ascii_hex);
}
}

Просмотреть файл

@ -186,6 +186,55 @@ namespace vcpkg
namespace details
{
namespace
{
const ExpectedS<fs::path>& default_registries_cache_path()
{
static auto cachepath = System::get_platform_cache_home().then([](fs::path p) -> ExpectedS<fs::path> {
auto maybe_cachepath = System::get_environment_variable("X_VCPKG_REGISTRIES_CACHE");
if (auto p_str = maybe_cachepath.get())
{
Metrics::g_metrics.lock()->track_property("X_VCPKG_REGISTRIES_CACHE", "defined");
auto path = fs::u8path(*p_str);
path.make_preferred();
const auto status = fs::stdfs::status(path);
if (!fs::stdfs::exists(status))
{
return {"Path to X_VCPKG_REGISTRIES_CACHE does not exist: " + fs::u8string(path),
expected_right_tag};
}
if (!fs::stdfs::is_directory(status))
{
return {"Value of environment variable X_VCPKG_REGISTRIES_CACHE is not a directory: " +
fs::u8string(path),
expected_right_tag};
}
if (!path.is_absolute())
{
return {"Value of environment variable X_VCPKG_REGISTRIES_CACHE is not absolute: " +
fs::u8string(path),
expected_right_tag};
}
return {std::move(path), expected_left_tag};
}
p /= fs::u8path("vcpkg/registries");
p.make_preferred();
if (p.is_absolute())
{
return {std::move(p), expected_left_tag};
}
else
{
return {"default path was not absolute: " + fs::u8string(p), expected_right_tag};
}
});
return cachepath;
}
}
struct VcpkgPathsImpl
{
VcpkgPathsImpl(Files::Filesystem& fs, FeatureFlagSettings ff_settings)
@ -194,11 +243,10 @@ namespace vcpkg
, m_env_cache(ff_settings.compiler_tracking)
, m_ff_settings(ff_settings)
{
const auto& cache_root =
System::get_platform_cache_home().value_or_exit(VCPKG_LINE_INFO) / fs::u8path("vcpkg");
registries_work_tree_dir = cache_root / fs::u8path("registries") / fs::u8path("git");
const auto& cache_root = default_registries_cache_path().value_or_exit(VCPKG_LINE_INFO);
registries_work_tree_dir = cache_root / fs::u8path("git");
registries_dot_git_dir = registries_work_tree_dir / fs::u8path(".git");
registries_git_trees = cache_root / fs::u8path("registries") / fs::u8path("git-trees");
registries_git_trees = cache_root / fs::u8path("git-trees");
}
Lazy<std::vector<VcpkgPaths::TripletFile>> available_triplets;
@ -228,9 +276,13 @@ namespace vcpkg
fs::path registries_work_tree_dir;
fs::path registries_dot_git_dir;
fs::path registries_git_trees;
Optional<LockFile> m_installed_lock;
};
}
static fs::path lockfile_path(const VcpkgPaths& p) { return p.vcpkg_dir / fs::u8path("vcpkg-lock.json"); }
VcpkgPaths::VcpkgPaths(Files::Filesystem& filesystem, const VcpkgCmdArguments& args)
: m_pimpl(std::make_unique<details::VcpkgPathsImpl>(filesystem, args.feature_flag_settings()))
{
@ -494,6 +546,72 @@ If you wish to silence this error and use classic mode, you can:
});
}
static LockFile load_lockfile(const Files::Filesystem& fs, const fs::path& p)
{
LockFile ret;
std::error_code ec;
auto maybe_lock_contents = Json::parse_file(fs, p, ec);
if (ec)
{
Debug::print("Failed to load lockfile: ", ec.message(), "\n");
return ret;
}
else if (auto lock_contents = maybe_lock_contents.get())
{
auto& doc = lock_contents->first;
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});
}
return ret;
}
Debug::print("Lockfile was not an object\n");
return ret;
}
else
{
Debug::print("Failed to load lockfile:\n", maybe_lock_contents.error()->format());
return ret;
}
}
LockFile& VcpkgPaths::get_installed_lockfile() const
{
if (!m_pimpl->m_installed_lock.has_value())
{
m_pimpl->m_installed_lock = load_lockfile(get_filesystem(), lockfile_path(*this));
}
return *m_pimpl->m_installed_lock.get();
}
void VcpkgPaths::flush_lockfile() const
{
// If the lock file was not loaded, no need to flush it.
if (!m_pimpl->m_installed_lock.has_value()) return;
// 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));
}
get_filesystem().write_rename_contents(
lockfile_path(*this), fs::u8path("vcpkg-lock.json.tmp"), Json::stringify(obj, {}), VCPKG_LINE_INFO);
}
const fs::path VcpkgPaths::get_triplet_file_path(Triplet triplet) const
{
return m_pimpl->m_triplets_cache.get_lazy(
@ -523,75 +641,17 @@ If you wish to silence this error and use classic mode, you can:
System::Command VcpkgPaths::git_cmd_builder(const fs::path& dot_git_dir, const fs::path& work_tree) const
{
return System::Command(get_tool_exe(Tools::GIT))
.string_arg(Strings::concat("--git-dir=", fs::u8string(dot_git_dir)))
.string_arg(Strings::concat("--work-tree=", fs::u8string(work_tree)))
.string_arg("-c")
.string_arg("core.autocrlf=false");
}
void VcpkgPaths::git_checkout_subpath(const VcpkgPaths& paths,
StringView commit_sha,
const fs::path& subpath,
const fs::path& local_repo,
const fs::path& destination,
const fs::path& dot_git_dir,
const fs::path& work_tree)
{
Files::Filesystem& fs = paths.get_filesystem();
fs.remove_all(work_tree, VCPKG_LINE_INFO);
fs.remove_all(destination, VCPKG_LINE_INFO);
fs.remove_all(dot_git_dir, VCPKG_LINE_INFO);
// All git commands are run with: --git-dir={dot_git_dir} --work-tree={work_tree_temp}
// git clone --no-checkout --local --no-hardlinks {vcpkg_root} {dot_git_dir}
// note that `--no-hardlinks` is added because otherwise, git fails to clone in some cases
System::Command clone_cmd_builder = paths.git_cmd_builder(dot_git_dir, work_tree)
.string_arg("clone")
.string_arg("--no-checkout")
.string_arg("--local")
.string_arg("--no-hardlinks")
.path_arg(local_repo)
.path_arg(dot_git_dir);
const auto clone_output = System::cmd_execute_and_capture_output(clone_cmd_builder);
Checks::check_exit(VCPKG_LINE_INFO,
clone_output.exit_code == 0,
"Failed to clone temporary vcpkg instance.\n%s\n",
clone_output.output);
// git checkout {commit-sha} -- {subpath}
System::Command checkout_cmd_builder = paths.git_cmd_builder(dot_git_dir, work_tree)
.string_arg("checkout")
.string_arg(commit_sha)
.string_arg("--")
.path_arg(subpath);
const auto checkout_output = System::cmd_execute_and_capture_output(checkout_cmd_builder);
Checks::check_exit(VCPKG_LINE_INFO,
checkout_output.exit_code == 0,
"Error: Failed to checkout %s:%s\n%s\n",
commit_sha,
fs::u8string(subpath),
checkout_output.output);
const fs::path checked_out_path = work_tree / subpath;
const auto& containing_folder = destination.parent_path();
if (!fs.exists(containing_folder))
System::Command ret(get_tool_exe(Tools::GIT));
if (!dot_git_dir.empty())
{
fs.create_directories(containing_folder, VCPKG_LINE_INFO);
ret.string_arg(Strings::concat("--git-dir=", fs::u8string(dot_git_dir)));
}
std::error_code ec;
fs.rename_or_copy(checked_out_path, destination, ".tmp", ec);
fs.remove_all(work_tree, VCPKG_LINE_INFO);
fs.remove_all(dot_git_dir, VCPKG_LINE_INFO);
if (ec)
if (!work_tree.empty())
{
System::printf(System::Color::error,
"Error: Couldn't move checked out files from %s to destination %s",
fs::u8string(checked_out_path),
fs::u8string(destination));
Checks::exit_fail(VCPKG_LINE_INFO);
ret.string_arg(Strings::concat("--work-tree=", fs::u8string(work_tree)));
}
ret.string_arg("-c").string_arg("core.autocrlf=false");
return ret;
}
ExpectedS<std::string> VcpkgPaths::get_current_git_sha() const
@ -641,14 +701,13 @@ If you wish to silence this error and use classic mode, you can:
ExpectedS<std::map<std::string, std::string, std::less<>>> VcpkgPaths::git_get_local_port_treeish_map() const
{
const auto local_repo = this->root / fs::u8path(".git");
const auto path_with_separator =
Strings::concat(fs::u8string(this->builtin_ports_directory()), Files::preferred_separator);
const auto git_cmd = git_cmd_builder(local_repo, this->root)
const auto git_cmd = git_cmd_builder({}, {})
.string_arg("-C")
.path_arg(this->builtin_ports_directory())
.string_arg("ls-tree")
.string_arg("-d")
.string_arg("HEAD")
.string_arg("--")
.path_arg(path_with_separator);
.string_arg("--");
auto output = System::cmd_execute_and_capture_output(git_cmd);
if (output.exit_code != 0)
@ -673,15 +732,7 @@ If you wish to silence this error and use classic mode, you can:
git_cmd.command_line(),
line);
const auto index = split_line[1].find_last_of('/');
if (index == std::string::npos)
{
return Strings::format("Error: Unexpected output from command `%s`. Couldn't split by `/`.\n%s",
git_cmd.command_line(),
line);
}
ret.emplace(split_line[1].substr(index + 1), file_info_section.back());
ret.emplace(split_line[1], file_info_section.back());
}
return ret;
}
@ -856,20 +907,14 @@ If you wish to silence this error and use classic mode, you can:
.string_arg("fetch")
.string_arg("--update-shallow")
.string_arg("--")
.string_arg(repo);
if (treeish.size() != 0)
{
fetch_git_ref.string_arg(treeish);
}
.string_arg(repo)
.string_arg(treeish);
auto fetch_output = System::cmd_execute_and_capture_output(fetch_git_ref);
if (fetch_output.exit_code != 0)
{
return {Strings::format("Error: Failed to fetch %s%s from repository %s.\n%s\n",
treeish.size() != 0 ? "ref " : "",
treeish,
repo,
fetch_output.output),
return {Strings::format(
"Error: Failed to fetch ref %s from repository %s.\n%s\n", treeish, repo, fetch_output.output),
expected_right_tag};
}
@ -883,6 +928,44 @@ If you wish to silence this error and use classic mode, you can:
}
return {Strings::trim(fetch_head_output.output).to_string(), expected_left_tag};
}
Optional<std::string> VcpkgPaths::git_fetch(StringView repo, StringView treeish) const
{
auto& fs = get_filesystem();
auto work_tree = m_pimpl->registries_work_tree_dir;
fs.create_directories(work_tree, VCPKG_LINE_INFO);
auto lock_file = work_tree / fs::u8path(".vcpkg-lock");
std::error_code ec;
Files::ExclusiveFileLock guard(Files::ExclusiveFileLock::Wait::Yes, fs, lock_file, ec);
auto dot_git_dir = m_pimpl->registries_dot_git_dir;
System::Command init_registries_git_dir = git_cmd_builder(dot_git_dir, work_tree).string_arg("init");
auto init_output = System::cmd_execute_and_capture_output(init_registries_git_dir);
if (init_output.exit_code != 0)
{
return Strings::format(
"Error: Failed to initialize local repository %s.\n%s\n", fs::u8string(work_tree), init_output.output);
}
System::Command fetch_git_ref = git_cmd_builder(dot_git_dir, work_tree)
.string_arg("fetch")
.string_arg("--update-shallow")
.string_arg("--")
.string_arg(repo)
.string_arg(treeish);
auto fetch_output = System::cmd_execute_and_capture_output(fetch_git_ref);
if (fetch_output.exit_code != 0)
{
return Strings::format(
"Error: Failed to fetch ref %s from repository %s.\n%s\n", treeish, repo, fetch_output.output);
}
return nullopt;
}
// returns an error if there was an unexpected error; returns nullopt if the file doesn't exist at the specified
// hash
ExpectedS<std::string> VcpkgPaths::git_show_from_remote_registry(StringView hash,
@ -954,6 +1037,8 @@ If you wish to silence this error and use classic mode, you can:
git_tree_temp_tar);
auto untar_output = System::cmd_execute_and_capture_output(untar, System::InWorkingDirectory{git_tree_temp});
// Attempt to remove temporary files, though non-critical.
fs.remove(git_tree_temp_tar, ignore_errors);
if (untar_output.exit_code != 0)
{
return {Strings::format("cmake's untar failed with message:\n%s", untar_output.output), expected_right_tag};