diff --git a/azure-pipelines/end-to-end-tests-dir/update-baseline.ps1 b/azure-pipelines/end-to-end-tests-dir/update-baseline.ps1 new file mode 100644 index 0000000000..4a7a989456 --- /dev/null +++ b/azure-pipelines/end-to-end-tests-dir/update-baseline.ps1 @@ -0,0 +1,299 @@ +. $PSScriptRoot/../end-to-end-tests-prelude.ps1 + +$env:X_VCPKG_REGISTRIES_CACHE = Join-Path $TestingRoot 'registries' +New-Item -ItemType Directory -Force $env:X_VCPKG_REGISTRIES_CACHE | Out-Null + +function New-TestRegistry { + Param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + New-Item -Path $Path -ItemType Directory -Force | Out-Null + $Path = (Get-Item $Path).FullName + + git -C $Path @gitConfigOptions init . | Out-Null + Throw-IfFailed + + return [pscustomobject]@{ + Path = $Path + Current = @{} + Versions = @{} + } +} + +function Set-TestRegistryPort { + Param( + [Parameter(Mandatory = $true)] + [string]$RegistryPath, + [Parameter(Mandatory = $true)] + [string]$Name, + [Parameter(Mandatory = $true)] + [string]$Version, + [string[]]$Dependencies = @() + ) + + $portDir = Join-Path $RegistryPath "ports/$Name" + New-Item -Path $portDir -ItemType Directory -Force | Out-Null + Set-Content -LiteralPath (Join-Path $portDir 'portfile.cmake') ` + -Value 'set(VCPKG_POLICY_EMPTY_PACKAGE enabled)' ` + -Encoding Ascii + + $manifest = [ordered]@{ + name = $Name + version = $Version + } + + if ($Dependencies.Count -ne 0) { + $manifest.dependencies = $Dependencies + } + + Set-Content -LiteralPath (Join-Path $portDir 'vcpkg.json') ` + -Value (ConvertTo-Json -Depth 10 -InputObject $manifest) ` + -Encoding Ascii ` + -NoNewline +} + +function Add-TestRegistryCommit { + Param( + [Parameter(Mandatory = $true)] + $Registry, + [Parameter(Mandatory = $true)] + [hashtable[]]$Ports, + [Parameter(Mandatory = $true)] + [string]$Message + ) + + foreach ($port in $Ports) { + $previousVersion = $null + if ($Registry.Current.ContainsKey($port.Name)) { + $previousVersion = $Registry.Current[$port.Name] + } + + if ($previousVersion -ne $port.Version) { + if ($port.ContainsKey('Dependencies')) { + Set-TestRegistryPort -RegistryPath $Registry.Path -Name $port.Name -Version $port.Version -Dependencies $port.Dependencies + } else { + Set-TestRegistryPort -RegistryPath $Registry.Path -Name $port.Name -Version $port.Version + } + } + + $Registry.Current[$port.Name] = $port.Version + } + + git -C $Registry.Path @gitConfigOptions add ports | Out-Null + Throw-IfFailed + git -C $Registry.Path @gitConfigOptions commit --allow-empty -m $Message | Out-Null + Throw-IfFailed + + foreach ($port in $Ports) { + if (@($Registry.Versions[$port.Name])[0].version -eq $port.Version) { + continue + } + + $gitTree = git -C $Registry.Path rev-parse "HEAD:ports/$($port.Name)" + Throw-IfFailed + + $versionEntry = [ordered]@{ + 'git-tree' = $gitTree + version = $port.Version + } + + $previousVersions = @() + if ($Registry.Versions.ContainsKey($port.Name)) { + $previousVersions = @($Registry.Versions[$port.Name]) + } + + $Registry.Versions[$port.Name] = @($versionEntry) + $previousVersions + } + + $baseline = [ordered]@{ default = [ordered]@{} } + foreach ($portName in ($Registry.Current.Keys | Sort-Object)) { + $baseline.default[$portName] = [ordered]@{ baseline = $Registry.Current[$portName] } + } + + New-Item -Path (Join-Path $Registry.Path 'versions') -ItemType Directory -Force | Out-Null + Set-Content -LiteralPath (Join-Path $Registry.Path 'versions/baseline.json') ` + -Value (ConvertTo-Json -Depth 10 -InputObject $baseline) ` + -Encoding Ascii ` + -NoNewline + + foreach ($portName in $Registry.Versions.Keys) { + $versionDir = Join-Path $Registry.Path "versions/$($portName[0])-" + New-Item -Path $versionDir -ItemType Directory -Force | Out-Null + $versionFile = [ordered]@{ versions = @($Registry.Versions[$portName]) } + Set-Content -LiteralPath (Join-Path $versionDir "$portName.json") ` + -Value (ConvertTo-Json -Depth 10 -InputObject $versionFile) ` + -Encoding Ascii ` + -NoNewline + } + + git -C $Registry.Path @gitConfigOptions add -A | Out-Null + Throw-IfFailed + git -C $Registry.Path @gitConfigOptions commit --amend --no-edit --allow-empty | Out-Null + Throw-IfFailed + + $baselineCommit = git -C $Registry.Path rev-parse HEAD + Throw-IfFailed + return $baselineCommit +} + +function New-TestManifest { + Param( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $true)] + [string[]]$Dependencies, + [Parameter(Mandatory = $true)] + [hashtable]$Configuration + ) + + New-Item -Path $Path -ItemType Directory -Force | Out-Null + Set-Content -LiteralPath (Join-Path $Path 'vcpkg.json') ` + -Value (ConvertTo-Json -Depth 10 -InputObject ([ordered]@{ + name = 'update-baseline-test' + version = '1.0.0' + dependencies = $Dependencies + })) ` + -Encoding Ascii ` + -NoNewline + Set-Content -LiteralPath (Join-Path $Path 'vcpkg-configuration.json') ` + -Value (ConvertTo-Json -Depth 10 -InputObject $Configuration) ` + -Encoding Ascii ` + -NoNewline +} + +function Invoke-UpdateBaselineDryRun { + Param( + [Parameter(Mandatory = $true)] + [string]$ManifestRoot, + [switch]$Quiet + ) + + $args = @('x-update-baseline') + $commonArgs + @('--dry-run', "--x-manifest-root=$ManifestRoot") + if ($Quiet) { + $args += '--quiet' + } + + $out = Run-VcpkgAndCaptureOutput @args + Throw-IfFailed + return $out +} + +function New-GitRegistryConfiguration { + Param( + [Parameter(Mandatory = $true)] + [string]$Repository, + [Parameter(Mandatory = $true)] + [string]$Baseline, + [Parameter(Mandatory = $true)] + [string[]]$Packages + ) + + return [ordered]@{ + kind = 'git' + repository = $Repository + baseline = $Baseline + packages = $Packages + } +} + +function New-DefaultGitRegistryConfiguration { + Param( + [Parameter(Mandatory = $true)] + [string]$Repository, + [Parameter(Mandatory = $true)] + [string]$Baseline + ) + + return [ordered]@{ + kind = 'git' + repository = $Repository + baseline = $Baseline + } +} + +Write-Trace 'build update-baseline registries' +$primaryRegistry = New-TestRegistry (Join-Path $TestingRoot 'primary-registry') +$primaryOldBaseline = Add-TestRegistryCommit -Registry $primaryRegistry -Message 'primary 1.0.0' -Ports @( + @{ Name = 'direct-port'; Version = '1.0.0'; Dependencies = @('transitive-port') }, + @{ Name = 'transitive-port'; Version = '1.0.0' } +) +$primaryNewBaseline = Add-TestRegistryCommit -Registry $primaryRegistry -Message 'primary 2.0.0' -Ports @( + @{ Name = 'direct-port'; Version = '2.0.0'; Dependencies = @('transitive-port') }, + @{ Name = 'transitive-port'; Version = '1.1.0' } +) + +$unchangedSharedRegistry = New-TestRegistry (Join-Path $TestingRoot 'unchanged-shared-registry') +$unchangedSharedOldBaseline = Add-TestRegistryCommit -Registry $unchangedSharedRegistry -Message 'shared unchanged 1.0.0' -Ports @( + @{ Name = 'shared-port'; Version = '1.0.0' } +) +$unchangedSharedNewBaseline = Add-TestRegistryCommit -Registry $unchangedSharedRegistry -Message 'shared unchanged still 1.0.0' -Ports @( + @{ Name = 'shared-port'; Version = '1.0.0' } +) + +$updatedSharedRegistry = New-TestRegistry (Join-Path $TestingRoot 'updated-shared-registry') +$updatedSharedOldBaseline = Add-TestRegistryCommit -Registry $updatedSharedRegistry -Message 'shared updated 1.0.0' -Ports @( + @{ Name = 'shared-port'; Version = '1.0.0' } +) +$updatedSharedNewBaseline = Add-TestRegistryCommit -Registry $updatedSharedRegistry -Message 'shared updated 2.0.0' -Ports @( + @{ Name = 'shared-port'; Version = '2.0.0' } +) + +Write-Trace 'test direct and transitive dependency diff output' +$manifestDir = Join-Path $TestingRoot 'direct-and-transitive' +New-TestManifest -Path $manifestDir -Dependencies @('direct-port') -Configuration ([ordered]@{ + 'default-registry' = New-DefaultGitRegistryConfiguration -Repository $primaryRegistry.Path -Baseline $primaryOldBaseline +}) + +$out = Invoke-UpdateBaselineDryRun -ManifestRoot $manifestDir +Throw-IfNonContains -Actual $out -Expected @" +Updating baselines has resulted in the following version updates: + +Direct dependencies: +direct-port: 1.0.0 -> 2.0.0 + +Transitive dependencies: +transitive-port: 1.0.0 -> 1.1.0 +"@ + +Write-Trace 'test quiet suppresses dependency diff output' +$out = Invoke-UpdateBaselineDryRun -ManifestRoot $manifestDir -Quiet +Throw-IfContains -Actual $out -Expected 'Updating baselines has resulted in the following version updates:' +Throw-IfContains -Actual $out -Expected 'direct-port: 1.0.0 -> 2.0.0' +Throw-IfContains -Actual $out -Expected 'transitive-port: 1.0.0 -> 1.1.0' + +Write-Trace 'test no dependency diff output when baseline is already current' +$manifestDir = Join-Path $TestingRoot 'no-port-changes' +New-TestManifest -Path $manifestDir -Dependencies @('direct-port') -Configuration ([ordered]@{ + 'default-registry' = New-DefaultGitRegistryConfiguration -Repository $primaryRegistry.Path -Baseline $primaryNewBaseline +}) + +$out = Invoke-UpdateBaselineDryRun -ManifestRoot $manifestDir +Throw-IfNonContains -Actual $out -Expected 'There were no changes in the ports.' +Throw-IfContains -Actual $out -Expected 'direct-port: 1.0.0 -> 2.0.0' + +Write-Trace 'test shared port selected from updated registry is included in diff' +$manifestDir = Join-Path $TestingRoot 'shared-port-updated-registry-selected' +New-TestManifest -Path $manifestDir -Dependencies @('shared-port') -Configuration ([ordered]@{ + 'default-registry' = New-DefaultGitRegistryConfiguration -Repository $unchangedSharedRegistry.Path -Baseline $unchangedSharedOldBaseline + registries = @( + (New-GitRegistryConfiguration -Repository $updatedSharedRegistry.Path -Baseline $updatedSharedOldBaseline -Packages @('shared-port')) + ) +}) + +$out = Invoke-UpdateBaselineDryRun -ManifestRoot $manifestDir +Throw-IfNonContains -Actual $out -Expected 'shared-port: 1.0.0 -> 2.0.0' + +Write-Trace 'test shared port selected from unchanged registry is excluded from diff' +$manifestDir = Join-Path $TestingRoot 'shared-port-unchanged-registry-selected' +New-TestManifest -Path $manifestDir -Dependencies @('shared-port') -Configuration ([ordered]@{ + 'default-registry' = New-DefaultGitRegistryConfiguration -Repository $updatedSharedRegistry.Path -Baseline $updatedSharedOldBaseline + registries = @( + (New-GitRegistryConfiguration -Repository $unchangedSharedRegistry.Path -Baseline $unchangedSharedOldBaseline -Packages @('shared-port')) + ) +}) + +$out = Invoke-UpdateBaselineDryRun -ManifestRoot $manifestDir +Throw-IfContains -Actual $out -Expected 'shared-port: 1.0.0 -> 2.0.0' +Throw-IfNonContains -Actual $out -Expected 'There were no changes in the ports.' diff --git a/include/vcpkg/base/contractual-constants.h b/include/vcpkg/base/contractual-constants.h index b14658590d..600acbeddf 100644 --- a/include/vcpkg/base/contractual-constants.h +++ b/include/vcpkg/base/contractual-constants.h @@ -266,6 +266,7 @@ namespace vcpkg inline constexpr StringLiteral SwitchPrintmetrics = "printmetrics"; inline constexpr StringLiteral SwitchPurge = "purge"; inline constexpr StringLiteral SwitchPython = "python"; + inline constexpr StringLiteral SwitchQuiet = "quiet"; inline constexpr StringLiteral SwitchRaw = "raw"; inline constexpr StringLiteral SwitchRecurse = "recurse"; inline constexpr StringLiteral SwitchRegistriesCache = "registries-cache"; diff --git a/include/vcpkg/base/message-data.inc.h b/include/vcpkg/base/message-data.inc.h index 1ff76720e5..0814523cc1 100644 --- a/include/vcpkg/base/message-data.inc.h +++ b/include/vcpkg/base/message-data.inc.h @@ -874,6 +874,7 @@ DECLARE_MESSAGE(CmdUpdateBaselineOptInitial, (), "", "Adds a `builtin-baseline` to a vcpkg.json that doesn't already have it") +DECLARE_MESSAGE(CmdUpdateBaselineOptQuiet, (), "", "Does not print the port version diff after updating baselines") DECLARE_MESSAGE(CmdUpdateBaselineSynopsis, (), "", @@ -1056,6 +1057,7 @@ DECLARE_MESSAGE(DependencyWillFail, "'cascade' is a keyword and should not be translated", "Dependency {feature_spec} will not build => cascade") DECLARE_MESSAGE(DetectCompilerHash, (msg::triplet), "", "Detecting compiler hash for triplet {triplet}...") +DECLARE_MESSAGE(DirectDependencies, (), "", "Direct dependencies") DECLARE_MESSAGE(DirectoriesRelativeToThePackageDirectoryHere, (), "", @@ -2666,7 +2668,7 @@ DECLARE_MESSAGE(PortMissingManifest2, "", "{package_name} port manifest missing (no vcpkg.json or CONTROL file)") DECLARE_MESSAGE(PortNotSupported, (msg::package_name, msg::triplet), "", "{package_name} is not supported on {triplet}") -DECLARE_MESSAGE(PortsNoDiff, (), "", "There were no changes in the ports between the two commits.") +DECLARE_MESSAGE(PortsNoDiff, (), "", "There were no changes in the ports.") DECLARE_MESSAGE(PortsRemoved, (msg::count), "", "The following {count} ports were removed:") DECLARE_MESSAGE(PortsUpdated, (msg::count), "", "The following {count} ports were updated:") DECLARE_MESSAGE(PortSupportsField, (msg::supports_expression), "", "(supports: \"{supports_expression}\")") @@ -2867,6 +2869,7 @@ DECLARE_MESSAGE(ToUpdatePackages, "To update these packages and all dependencies, run\n{command_name} upgrade'") DECLARE_MESSAGE(TrailingCommaInArray, (), "", "Trailing comma in array") DECLARE_MESSAGE(TrailingCommaInObj, (), "", "Trailing comma in an object") +DECLARE_MESSAGE(TransitiveDependencies, (), "", "Transitive dependencies") DECLARE_MESSAGE(TripletLabel, (), "", "Triplet:") DECLARE_MESSAGE(TripletFileNotFound, (msg::triplet), "", "Triplet file {triplet}.cmake not found") DECLARE_MESSAGE(TwoFeatureFlagsSpecified, @@ -3114,15 +3117,26 @@ DECLARE_MESSAGE(UpdateBaselineNoExistingBuiltinBaseline, "", "the manifest file currently does not contain a `builtin-baseline` field; in order to " "add one, pass the --{option} switch.") +DECLARE_MESSAGE(UpdateBaselineNewDependencyVersion, + (msg::package_name, msg::version), + "example of {package_name} is zlib. example of {version} is 1.3.2#1", + "{package_name}: new: {version}") DECLARE_MESSAGE(UpdateBaselineNoUpdate, (msg::url, msg::value), "example of {value} is '5507daa796359fe8d45418e694328e878ac2b82f'", - "registry '{url}' not updated: '{value}'") -DECLARE_MESSAGE(UpdateBaselineRemoteGitError, (msg::url), "", "git failed to fetch remote repository '{url}'") + "registry '{url}' not updated: {value}") +DECLARE_MESSAGE(UpdateBaselineRemovedDependencyVersion, + (msg::package_name, msg::version), + "example of {package_name} is zlib. example of {version} is 1.3.2#1", + "{package_name}: removed: {version}") +DECLARE_MESSAGE(UpdateBaselineVersionUpdates, + (), + "", + "Updating baselines has resulted in the following version updates:") DECLARE_MESSAGE(UpdateBaselineUpdatedBaseline, (msg::url, msg::old_value, msg::new_value), "example of {old_value}, {new_value} is '5507daa796359fe8d45418e694328e878ac2b82f'", - "updated registry '{url}': baseline '{old_value}' -> '{new_value}'") + "updated registry '{url}': {old_value} -> {new_value}") DECLARE_MESSAGE( UpgradeInManifest, (), diff --git a/include/vcpkg/commands.update-baseline.h b/include/vcpkg/commands.update-baseline.h index 632307b258..65f5f36e34 100644 --- a/include/vcpkg/commands.update-baseline.h +++ b/include/vcpkg/commands.update-baseline.h @@ -1,10 +1,14 @@ #pragma once +#include #include #include namespace vcpkg { extern const CommandMetadata CommandUpdateBaselineMetadata; - void command_update_baseline_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); + void command_update_baseline_and_exit(const VcpkgCmdArguments& args, + const VcpkgPaths& paths, + Triplet default_triplet, + Triplet host_triplet); } diff --git a/locales/messages.json b/locales/messages.json index b2db8c8c8f..22c28af3b2 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -504,6 +504,7 @@ "CmdTestFeaturesSynopsis": "Tests the features of a port", "CmdUpdateBaselineOptDryRun": "Prints out plan without execution", "CmdUpdateBaselineOptInitial": "Adds a `builtin-baseline` to a vcpkg.json that doesn't already have it", + "CmdUpdateBaselineOptQuiet": "Does not print the port version diff after updating baselines", "CmdUpdateBaselineSynopsis": "Updates baselines of git registries in a manifest to those registries' HEAD commit", "CmdUpdateRegistryAll": "Updates all known artifact registries", "CmdUpdateRegistryAllExcludesTargets": "Update registry --all cannot be used with a list of artifact registries", @@ -609,6 +610,7 @@ "_DependencyWillFail.comment": "'cascade' is a keyword and should not be translated An example of {feature_spec} is zlib[featurea,featureb].", "DetectCompilerHash": "Detecting compiler hash for triplet {triplet}...", "_DetectCompilerHash.comment": "An example of {triplet} is x64-windows.", + "DirectDependencies": "Direct dependencies", "DirectoriesRelativeToThePackageDirectoryHere": "the directories are relative to ${{CURRENT_PACKAGES_DIR}} here", "DllsRelativeToThePackageDirectoryHere": "the DLLs are relative to ${{CURRENT_PACKAGES_DIR}} here", "DocumentedFieldsSuggestUpdate": "If these are documented fields that should be recognized try updating the vcpkg tool.", @@ -1377,7 +1379,7 @@ "PortVersionMultipleSpecification": "\"port_version\" cannot be combined with an embedded '#' in the version", "PortsAdded": "The following {count} ports were added:", "_PortsAdded.comment": "An example of {count} is 42.", - "PortsNoDiff": "There were no changes in the ports between the two commits.", + "PortsNoDiff": "There were no changes in the ports.", "PortsRemoved": "The following {count} ports were removed:", "_PortsRemoved.comment": "An example of {count} is 42.", "PortsUpdated": "The following {count} ports were updated:", @@ -1491,6 +1493,7 @@ "_TotalInstallTimeSuccess.comment": "An example of {elapsed} is 3.532 min.", "TrailingCommaInArray": "Trailing comma in array", "TrailingCommaInObj": "Trailing comma in an object", + "TransitiveDependencies": "Transitive dependencies", "TripletFileNotFound": "Triplet file {triplet}.cmake not found", "_TripletFileNotFound.comment": "An example of {triplet} is x64-windows.", "TripletLabel": "Triplet:", @@ -1613,15 +1616,18 @@ "_UpdateBaselineAddBaselineNoManifest.comment": "An example of {option} is editable.", "UpdateBaselineLocalGitError": "git failed to parse HEAD for the local vcpkg registry at \"{path}\"", "_UpdateBaselineLocalGitError.comment": "An example of {path} is /foo/bar.", + "UpdateBaselineNewDependencyVersion": "{package_name}: new: {version}", + "_UpdateBaselineNewDependencyVersion.comment": "example of {package_name} is zlib. example of {version} is 1.3.2#1 An example of {package_name} is zlib. An example of {version} is 1.3.8.", "UpdateBaselineNoConfiguration": "neither `vcpkg.json` nor `vcpkg-configuration.json` exist to update.", "UpdateBaselineNoExistingBuiltinBaseline": "the manifest file currently does not contain a `builtin-baseline` field; in order to add one, pass the --{option} switch.", "_UpdateBaselineNoExistingBuiltinBaseline.comment": "An example of {option} is editable.", - "UpdateBaselineNoUpdate": "registry '{url}' not updated: '{value}'", + "UpdateBaselineNoUpdate": "registry '{url}' not updated: {value}", "_UpdateBaselineNoUpdate.comment": "example of {value} is '5507daa796359fe8d45418e694328e878ac2b82f' An example of {url} is https://github.com/microsoft/vcpkg.", - "UpdateBaselineRemoteGitError": "git failed to fetch remote repository '{url}'", - "_UpdateBaselineRemoteGitError.comment": "An example of {url} is https://github.com/microsoft/vcpkg.", - "UpdateBaselineUpdatedBaseline": "updated registry '{url}': baseline '{old_value}' -> '{new_value}'", + "UpdateBaselineRemovedDependencyVersion": "{package_name}: removed: {version}", + "_UpdateBaselineRemovedDependencyVersion.comment": "example of {package_name} is zlib. example of {version} is 1.3.2#1 An example of {package_name} is zlib. An example of {version} is 1.3.8.", + "UpdateBaselineUpdatedBaseline": "updated registry '{url}': {old_value} -> {new_value}", "_UpdateBaselineUpdatedBaseline.comment": "example of {old_value}, {new_value} is '5507daa796359fe8d45418e694328e878ac2b82f' An example of {url} is https://github.com/microsoft/vcpkg.", + "UpdateBaselineVersionUpdates": "Updating baselines has resulted in the following version updates:", "UpgradeInManifest": "Upgrade upgrades a classic mode installation and thus does not support manifest mode. Consider updating your dependencies by updating your baseline to a current value with vcpkg x-update-baseline and running vcpkg install.", "_UpgradeInManifest.comment": "'vcpkg x-update-baseline' and 'vcpkg install' are command lines and should not be localized.", "UpgradeRunWithNoDryRun": "If you are sure you want to rebuild the above packages, run this command with the --no-dry-run option.", diff --git a/src/vcpkg/base/files.cpp b/src/vcpkg/base/files.cpp index 5ad9445114..597386f45c 100644 --- a/src/vcpkg/base/files.cpp +++ b/src/vcpkg/base/files.cpp @@ -4190,11 +4190,11 @@ namespace vcpkg { Checks::check_exit(VCPKG_LINE_INFO, handle == INVALID_HANDLE_VALUE); handle = CreateFileW(native.c_str(), - GENERIC_READ, + FILE_GENERIC_READ | DELETE, 0 /* no sharing */, nullptr /* no security attributes */, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr /* no template file */); if (handle != INVALID_HANDLE_VALUE) { @@ -4260,6 +4260,8 @@ namespace vcpkg { if (locked) { + // Do not unlink this file before unlocking. Another process may already have this file open while + // waiting on flock(); unlinking here would let later processes create and lock a different inode. Checks::check_exit(VCPKG_LINE_INFO, fd && fd.flock(LOCK_UN) == 0); } } diff --git a/src/vcpkg/commands.cpp b/src/vcpkg/commands.cpp index 325b3830a5..d41db492e4 100644 --- a/src/vcpkg/commands.cpp +++ b/src/vcpkg/commands.cpp @@ -105,7 +105,6 @@ namespace vcpkg {CommandRegenerateMetadata, command_regenerate_and_exit}, {CommandSearchMetadata, command_search_and_exit}, {CommandUpdateMetadata, command_update_and_exit}, - {CommandUpdateBaselineMetadata, command_update_baseline_and_exit}, {CommandUpdateRegistryMetadata, command_update_registry_and_exit}, {CommandUseMetadata, command_use_and_exit}, {CommandVsInstancesMetadata, command_vs_instances_and_exit}, @@ -128,6 +127,7 @@ namespace vcpkg {CommandRemoveMetadata, command_remove_and_exit}, {CommandTestFeaturesMetadata, command_test_features_and_exit}, {CommandSetInstalledMetadata, command_set_installed_and_exit}, + {CommandUpdateBaselineMetadata, command_update_baseline_and_exit}, {CommandUpgradeMetadata, command_upgrade_and_exit}, {CommandZPrintConfigMetadata, command_z_print_config_and_exit}, }; diff --git a/src/vcpkg/commands.update-baseline.cpp b/src/vcpkg/commands.update-baseline.cpp index 8170237a86..ca1fba854b 100644 --- a/src/vcpkg/commands.update-baseline.cpp +++ b/src/vcpkg/commands.update-baseline.cpp @@ -3,9 +3,15 @@ #include #include +#include +#include #include #include +#include +#include +#include #include +#include #include #include @@ -13,6 +19,160 @@ using namespace vcpkg; namespace { + struct ManifestVersionSnapshotEntry + { + Version version; + RequestType request_type; + }; + + using ManifestVersionSnapshot = std::map>; + + ManifestVersionSnapshot create_manifest_version_snapshot(const VcpkgPaths& paths, + const ManifestAndPath& manifest, + const ConfigurationAndSource& configuration, + const ParsedArguments& options, + CMakeVars::CMakeVarProvider& var_provider, + Triplet default_triplet, + Triplet host_triplet) + { + auto registry_set = configuration.instantiate_registry_set(paths); + auto manifest_scf = parse_manifest_scf_or_exit(manifest, paths, registry_set->is_default_builtin_registry()); + const auto& manifest_core = *manifest_scf->core_paragraph; + PackageSpec toplevel{manifest_core.name, default_triplet}; + + const auto features = get_manifest_features(options, manifest_core, var_provider, toplevel, host_triplet); + const auto dependencies = get_manifest_dependencies(*manifest_scf, features); + + const bool add_builtin_ports_directory_as_overlay = + registry_set->is_default_builtin_registry() && !paths.use_git_default_registry(); + auto extended_overlay_port_directories = paths.overlay_ports; + if (add_builtin_ports_directory_as_overlay) + { + extended_overlay_port_directories.builtin_overlay_port_dir.emplace(paths.builtin_ports_directory()); + } + + auto verprovider = make_versioned_portfile_provider(*registry_set); + auto baseprovider = make_baseline_provider(*registry_set); + auto oprovider = make_manifest_provider( + paths.get_filesystem(), extended_overlay_port_directories, manifest.path, std::move(manifest_scf)); + PackagesDirAssigner packages_dir_assigner{paths.packages()}; + ActionPlan install_plan = + create_versioned_install_plan( + *verprovider, + *baseprovider, + *oprovider, + var_provider, + dependencies, + manifest_core.overrides, + toplevel, + packages_dir_assigner, + {nullptr, host_triplet, UnsupportedPortAction::Error, UseHeadVersion::No, Editable::No}) + .value_or_exit(VCPKG_LINE_INFO); + + Util::erase_remove_if(install_plan.install_actions, + [&toplevel](auto&& action) { return action.spec == toplevel; }); + + ManifestVersionSnapshot versions; + for (const auto& action : install_plan.install_actions) + { + versions.emplace(action.spec.name(), ManifestVersionSnapshotEntry{action.version, action.request_type}); + } + + return versions; + } + + void add_version_snapshot_diff_line(std::vector& direct_dependencies, + std::vector& transitive_dependencies, + const ManifestVersionSnapshotEntry& entry, + std::string&& line) + { + if (entry.request_type == RequestType::USER_REQUESTED) + { + direct_dependencies.push_back(std::move(line)); + return; + } + + transitive_dependencies.push_back(std::move(line)); + } + + bool print_version_snapshot_diff_lines(const msg::MessageT<>& header, std::vector&& lines) + { + if (lines.empty()) + { + return false; + } + + Util::sort_unique_erase(lines); + msg::print(msg::format(header).append_raw(":\n")); + for (const auto& line : lines) + { + msg::write_unlocalized_text(Color::none, line); + msg::write_unlocalized_text(Color::none, "\n"); + } + + msg::write_unlocalized_text(Color::none, "\n"); + return true; + } + + void print_version_snapshot_diff(const ManifestVersionSnapshot& previous, const ManifestVersionSnapshot& current) + { + std::vector direct_dependencies; + std::vector transitive_dependencies; + + for (const auto& current_entry : current) + { + const auto previous_entry = previous.find(current_entry.first); + if (previous_entry == previous.end()) + { + add_version_snapshot_diff_line(direct_dependencies, + transitive_dependencies, + current_entry.second, + msg::format(msgUpdateBaselineNewDependencyVersion, + msg::package_name = current_entry.first, + msg::version = current_entry.second.version) + .extract_data()); + } + else if (previous_entry->second.version != current_entry.second.version) + { + add_version_snapshot_diff_line(direct_dependencies, + transitive_dependencies, + current_entry.second, + fmt::format("{}: {} -> {}", + current_entry.first, + previous_entry->second.version, + current_entry.second.version)); + } + } + + for (const auto& previous_entry : previous) + { + if (!Util::Maps::contains(current, previous_entry.first)) + { + add_version_snapshot_diff_line(direct_dependencies, + transitive_dependencies, + previous_entry.second, + msg::format(msgUpdateBaselineRemovedDependencyVersion, + msg::package_name = previous_entry.first, + msg::version = previous_entry.second.version) + .extract_data()); + } + } + + const bool has_changes = !direct_dependencies.empty() || !transitive_dependencies.empty(); + if (has_changes) + { + msg::println(msgUpdateBaselineVersionUpdates); + msg::write_unlocalized_text(Color::none, "\n"); + } + + print_version_snapshot_diff_lines(msgDirectDependencies, std::move(direct_dependencies)); + print_version_snapshot_diff_lines(msgTransitiveDependencies, std::move(transitive_dependencies)); + if (!has_changes) + { + msg::println(msgPortsNoDiff); + } + } + void update_baseline_in_config(const VcpkgPaths& paths, RegistryConfig& reg) { auto url = reg.pretty_location(); @@ -47,6 +207,7 @@ namespace constexpr CommandSwitch switches[] = { {SwitchAddInitialBaseline, msgCmdUpdateBaselineOptInitial}, {SwitchDryRun, msgCmdUpdateBaselineOptDryRun}, + {SwitchQuiet, msgCmdUpdateBaselineOptQuiet}, }; } // unnamed namespace @@ -65,18 +226,24 @@ namespace vcpkg nullptr, }; - void command_update_baseline_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + void command_update_baseline_and_exit(const VcpkgCmdArguments& args, + const VcpkgPaths& paths, + Triplet default_triplet, + Triplet host_triplet) { auto options = args.parse_arguments(CommandUpdateBaselineMetadata); const bool add_builtin_baseline = Util::Sets::contains(options.switches, SwitchAddInitialBaseline); const bool dry_run = Util::Sets::contains(options.switches, SwitchDryRun); + const bool quiet = Util::Sets::contains(options.switches, SwitchQuiet); auto configuration = paths.get_configuration(); const auto* manifest_ptr = paths.get_manifest(); const bool has_manifest = manifest_ptr != nullptr; auto manifest = has_manifest ? *manifest_ptr : ManifestAndPath{}; + const auto old_configuration = configuration; + const auto old_manifest = manifest; if (configuration.source == ConfigurationSource::None && !has_manifest) { @@ -154,6 +321,23 @@ namespace vcpkg paths.get_filesystem().write_contents(manifest.path, Json::stringify(manifest.manifest), VCPKG_LINE_INFO); } + if (has_manifest && !quiet) + { + InstallAndBuildDatabaseLock installed_lock{paths.get_filesystem(), + paths.installed(), + paths.buildtrees(), + paths.packages(), + args.wait_for_lock, + args.ignore_lock_failures}; + auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths, installed_lock); + auto& var_provider = *var_provider_storage; + const auto previous_snapshot = create_manifest_version_snapshot( + paths, old_manifest, old_configuration, options, var_provider, default_triplet, host_triplet); + const auto current_snapshot = create_manifest_version_snapshot( + paths, manifest, configuration, options, var_provider, default_triplet, host_triplet); + print_version_snapshot_diff(previous_snapshot, current_snapshot); + } + Checks::exit_success(VCPKG_LINE_INFO); } } // namespace vcpkg diff --git a/src/vcpkg/configuration.cpp b/src/vcpkg/configuration.cpp index 6d75943108..62249314c7 100644 --- a/src/vcpkg/configuration.cpp +++ b/src/vcpkg/configuration.cpp @@ -677,17 +677,12 @@ namespace vcpkg StringView url, std::string reference) { - auto res = paths.git_fetch_from_remote_registry(url, reference); - if (auto p = res.get()) - { - return Optional(std::move(*p)); - } - else - { - return msg::format(msgUpdateBaselineRemoteGitError, msg::url = url) - .append_raw('\n') - .append_raw(Strings::trim(res.error())); - } + return paths.get_installed_lockfile() + .get_or_fetch(paths, url, reference) + .then([&](const LockFile::Entry& lock_entry) -> ExpectedL> { + return lock_entry.ensure_up_to_date(paths).map( + [&](Unit) { return Optional(lock_entry.commit_id()); }); + }); } ExpectedL> RegistryConfig::get_latest_baseline(const VcpkgPaths& paths) const