Skip to content

Commit c911129

Browse files
review feedback (Copilot): pin runtime vcpkg clones, share install dir across configs, fail fast, fix stale comments
Addresses 6 of 7 review comments from #48039 (review) (the 7th — global VcpkgEnabled=true — is a deliberate trade-off; see the expanded Cpp.Build.props comment for why per-project opt-in via spdlog.props proved unworkable on this codebase's import order). What changes ------------ Cpp.Build.props - VcpkgInstalledDir: dropped $(Configuration) from the path (now `vcpkg_installed\$(Platform)\`). vcpkg already separates debug/ release artifacts inside the same triplet directory ($triplet/lib vs $triplet/debug/lib), so a single install dir per platform is correct and avoids redundant restores on config switches. - Added a fail-fast <Target>: when VcpkgEnabled=true but vcpkg.props cannot be found at the resolved VcpkgRoot, abort with a clear error listing the three resolution paths (VS component, VCPKG_ROOT, or build-essentials.cmd). Previously the import was silently skipped and surfaced as opaque C1083/LNK2019 errors much later in the build. - Expanded the leading comment to call out the per-project-opt-in trade-off explicitly so future readers don't repeat the attempt. .pipelines/v2/templates/steps-install-vcpkg.yml - When falling back to a fresh vcpkg clone, check out the commit listed in vcpkg.json's `builtin-baseline` after cloning, before bootstrapping. Previously the clone tracked microsoft/vcpkg HEAD, which makes CI non-reproducible (a new vcpkg HEAD can change tool behavior or break restores even when the manifest baseline is pinned). The baseline commit is read from vcpkg.json at runtime so the two cannot drift. tools/build/build-essentials.ps1 - Same baseline-pinning behavior as the CI template. - Handles three states correctly: (a) no clone yet -> clone+checkout baseline, (b) existing clone at the wrong revision -> fetch+checkout baseline, (c) existing clone at the right revision -> no-op. Avoids unnecessary network round-trips on subsequent runs. - Updated the leading comment: vcpkg integration is wired in Cpp.Build.props (with vcpkg.targets in Cpp.Build.targets), not deps/spdlog.props. .pipelines/v2/templates/job-build-project.yml - Updated the leading comment for the same reason. Verification ------------ Local Release|x64 build of src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj green (~87 s) with the new VcpkgInstalledDir layout. /preprocess output confirms the fail-fast <Target> is properly merged into every .vcxproj. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 765e0de commit c911129

4 files changed

Lines changed: 84 additions & 20 deletions

File tree

.pipelines/v2/templates/job-build-project.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,11 @@ jobs:
276276
# Visual Studio (Microsoft.VisualStudio.Component.Vcpkg) and falls back to a
277277
# fresh clone of microsoft/vcpkg into deps/vcpkg if VS doesn't have it.
278278
# Either way it sets the VCPKG_ROOT pipeline variable; MSBuild integration
279-
# picks it up from deps/spdlog.props (three-tier VcpkgRoot fallback).
279+
# is wired globally from Cpp.Build.props (with vcpkg.targets in
280+
# Cpp.Build.targets) using the three-tier VcpkgRoot fallback
281+
# (env var > VS-shipped > deps/vcpkg runtime clone).
280282
#
281-
# Vcpkg's MSBuild integration runs `vcpkg install` once per build, so the
283+
# Vcpkg's MSBuild integration runs `vcpkg install` once per project, so the
282284
# binary cache below saves ~3-5 minutes per triplet on cache hits.
283285
- template: .\steps-install-vcpkg.yml
284286

.pipelines/v2/templates/steps-install-vcpkg.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,34 @@
44
# 1. The Visual Studio installation (Microsoft.VisualStudio.Component.Vcpkg).
55
# 2. A local clone at deps/vcpkg, cloned and bootstrapped on demand.
66
#
7-
# Sets the pipeline-scoped VCPKG_ROOT variable so the rest of the build (and
8-
# MSBuild's vcpkg integration imported from deps/spdlog.props) can find vcpkg
9-
# without the repo having to carry a submodule.
7+
# When falling back to a local clone, the clone is pinned to the same commit
8+
# as vcpkg.json's `builtin-baseline` so CI builds are reproducible (vcpkg
9+
# HEAD changes can otherwise break restores even though the baseline is
10+
# pinned). The baseline commit is read from vcpkg.json at runtime so the
11+
# two never drift.
12+
#
13+
# Sets the pipeline-scoped VCPKG_ROOT variable; the rest of the build
14+
# resolves vcpkg through it (see the three-tier VcpkgRoot fallback in
15+
# Cpp.Build.props). No repo-level vcpkg submodule required.
1016
steps:
1117
- pwsh: |-
1218
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -prerelease -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath
1319
If ([String]::IsNullOrEmpty($VsInstallRoot)) {
20+
$manifest = Get-Content "$(build.sourcesdirectory)\vcpkg.json" -Raw | ConvertFrom-Json
21+
$baseline = $manifest.'builtin-baseline'
22+
if (-not $baseline) { throw "vcpkg.json is missing 'builtin-baseline'." }
23+
Write-Host "Cloning microsoft/vcpkg pinned to builtin-baseline $baseline"
1424
Remove-Item -Recurse -Force deps/vcpkg -ErrorAction:Ignore
1525
git clone https://github.com/microsoft/vcpkg deps/vcpkg
1626
if ($LASTEXITCODE -ne 0) { throw "git clone vcpkg failed (exit $LASTEXITCODE)" }
1727
Push-Location deps/vcpkg
28+
git checkout --quiet $baseline
29+
if ($LASTEXITCODE -ne 0) { Pop-Location; throw "git checkout $baseline in vcpkg failed (exit $LASTEXITCODE)" }
1830
& ./bootstrap-vcpkg.bat -disableMetrics
1931
if ($LASTEXITCODE -ne 0) { Pop-Location; throw "bootstrap-vcpkg failed (exit $LASTEXITCODE)" }
2032
$VcpkgRoot = $PWD
2133
Pop-Location
22-
Write-Host "Using vcpkg from local checkout ($VcpkgRoot)"
34+
Write-Host "Using vcpkg from local checkout ($VcpkgRoot) at $baseline"
2335
} Else {
2436
$VcpkgRoot = Join-Path $VsInstallRoot 'VC\vcpkg'
2537
Write-Host "Using vcpkg from Visual Studio installation ($VcpkgRoot)"

Cpp.Build.props

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,29 @@
124124

125125
<!--
126126
vcpkg integration. Set globally (not per-project) so that import order is
127-
guaranteed to be BEFORE Microsoft.Cpp.targets — this matters because
128-
vcpkg's MSBuild integration registers a target (VcpkgInstallManifestDependencies)
129-
that ClCompile must depend on, and that hook only takes effect when
130-
vcpkg.props is loaded before the C++ targets. PowerToys has projects
131-
that import deps/spdlog.props after Microsoft.Cpp.targets, so putting
132-
vcpkg setup in spdlog.props loaded that way failed to wire include paths.
127+
guaranteed to be BEFORE Microsoft.Cpp.targets — vcpkg.props registers a
128+
target (VcpkgInstallManifestDependencies) that ClCompile must depend on,
129+
and that hook only takes effect when vcpkg.props is loaded before the
130+
C++ targets. PowerToys has projects that import deps/spdlog.props after
131+
Microsoft.Cpp.targets, so a per-project opt-in via spdlog.props (the
132+
earlier attempt) silently broke include-path wiring for those projects.
133+
134+
Trade-off: every C++ project now invokes `vcpkg install` once at build
135+
time, even if it doesn't consume spdlog. With binary cache enabled this
136+
is ~0.5 s/project on cache hits. The manifest declares only spdlog, so
137+
the cost is fixed and bounded; non-spdlog projects just carry an unused
138+
include path.
133139
134140
Three-tier VcpkgRoot fallback matching microsoft/terminal's pattern:
135141
1. $(VCPKG_ROOT) env var (CI sets via .pipelines/v2/templates/steps-install-vcpkg.yml;
136142
local dev sets via tools/build/build-essentials.ps1).
137143
2. $(VsInstallRoot)\VC\vcpkg (vcpkg shipped with Visual Studio).
138144
3. $(RepoRoot)deps\vcpkg (gitignored runtime-cloned fallback).
139145
140-
The vcpkg manifest at the repo root declares only spdlog, so non-spdlog
141-
C++ projects pay only the vcpkg install latency (~0.5 s on cache hits)
142-
and an unused include path. With binary cache enabled this is negligible.
146+
VcpkgInstalledDir is platform-scoped (not configuration-scoped); vcpkg
147+
already separates debug/release artifacts inside the same triplet
148+
directory ($triplet/lib vs $triplet/debug/lib). Splitting per-platform
149+
keeps parallel x64/arm64 builds from racing on the same install lock.
143150
-->
144151
<PropertyGroup Label="vcpkg">
145152
<VcpkgEnabled>true</VcpkgEnabled>
@@ -151,13 +158,30 @@
151158
<!-- vcpkg validates triplets case-sensitively; PowerToys uses ARM64 capital-case. -->
152159
<VcpkgPlatformTarget Condition="'$(Platform)' == 'ARM64'">arm64</VcpkgPlatformTarget>
153160
<VcpkgApplocalDeps>false</VcpkgApplocalDeps>
154-
<VcpkgInstalledDir>$(MSBuildThisFileDirectory)$(Platform)\$(Configuration)\vcpkg_installed\</VcpkgInstalledDir>
161+
<VcpkgInstalledDir>$(MSBuildThisFileDirectory)vcpkg_installed\$(Platform)\</VcpkgInstalledDir>
155162
<VcpkgRoot Condition="'$(VcpkgRoot)' == ''">$(VCPKG_ROOT)</VcpkgRoot>
156163
<VcpkgRoot Condition="'$(VcpkgRoot)' == '' and '$(VsInstallRoot)' != ''">$(VsInstallRoot)\VC\vcpkg</VcpkgRoot>
157164
<VcpkgRoot Condition="'$(VcpkgRoot)' == '' or !Exists('$(VcpkgRoot)\vcpkg.exe')">$(MSBuildThisFileDirectory)deps\vcpkg</VcpkgRoot>
158165
<CAExcludePath>$(CAExcludePath);$(VcpkgInstalledDir)</CAExcludePath>
159166
<VCPkgLocalAppDataDisabled>true</VCPkgLocalAppDataDisabled>
160167
</PropertyGroup>
168+
<!--
169+
Fail fast with a clear message if vcpkg is enabled but vcpkg.props
170+
cannot be found at the resolved VcpkgRoot. Without this the build
171+
silently skips vcpkg integration and surfaces later as missing
172+
<spdlog/*> includes or unresolved spdlog::* link errors, which are
173+
much harder to root-cause.
174+
-->
175+
<Target Name="PowerToysEnsureVcpkgAvailable"
176+
BeforeTargets="PrepareForBuild"
177+
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(VcpkgEnabled)' == 'true' and !Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props')">
178+
<Error Text="vcpkg is required to build this project but vcpkg.props was not found at '$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props'.
179+
180+
Resolve via one of:
181+
1) Install the Visual Studio component 'Microsoft.VisualStudio.Component.Vcpkg' (in the VS Installer, under Desktop development with C++), or
182+
2) Set the VCPKG_ROOT environment variable to an existing bootstrapped vcpkg checkout, or
183+
3) Run tools\build\build-essentials.cmd at least once to clone+bootstrap a local vcpkg into deps\vcpkg." />
184+
</Target>
161185
<Import Project="$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props"
162186
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props')" />
163187

tools/build/build-essentials.ps1

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ if (-not (Ensure-VsDevEnvironment)) { exit 1 }
5454
# Detect vcpkg using the Windows Terminal pattern: prefer VS-shipped vcpkg
5555
# (Microsoft.VisualStudio.Component.Vcpkg), fall back to a runtime clone at
5656
# deps/vcpkg if VS doesn't have it. Either way, set VCPKG_ROOT so MSBuild
57-
# picks it up via deps/spdlog.props' three-tier fallback. Idempotent.
57+
# picks it up via the three-tier VcpkgRoot fallback in Cpp.Build.props
58+
# (env var > VS-shipped > local clone). Idempotent.
59+
#
60+
# The fallback clone is pinned to vcpkg.json's `builtin-baseline` so local
61+
# builds match what CI uses and don't drift as vcpkg HEAD changes. If an
62+
# existing deps/vcpkg clone is at a different commit, it's checked out to
63+
# the baseline before bootstrap (the manifest baseline is the source of
64+
# truth; the clone is just a delivery mechanism).
5865
if (-not $env:VCPKG_ROOT) {
5966
$vswhere = 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
6067
$vsInstallRoot = $null
@@ -65,20 +72,39 @@ if (-not $env:VCPKG_ROOT) {
6572
$env:VCPKG_ROOT = Join-Path $vsInstallRoot 'VC\vcpkg'
6673
Write-Host "[BUILD-ESSENTIALS] Using vcpkg from Visual Studio installation ($env:VCPKG_ROOT)"
6774
} else {
75+
$manifest = Get-Content (Join-Path $repoRoot 'vcpkg.json') -Raw | ConvertFrom-Json
76+
$baseline = $manifest.'builtin-baseline'
77+
if (-not $baseline) { Write-Error "vcpkg.json is missing 'builtin-baseline'."; exit 1 }
6878
$localVcpkg = Join-Path $repoRoot 'deps\vcpkg'
69-
if (-not (Test-Path (Join-Path $localVcpkg 'vcpkg.exe'))) {
70-
Write-Host "[BUILD-ESSENTIALS] VS-shipped vcpkg not found; cloning microsoft/vcpkg to $localVcpkg"
79+
$needClone = -not (Test-Path (Join-Path $localVcpkg '.git'))
80+
if (-not $needClone) {
81+
$currentHead = (& git -C $localVcpkg rev-parse HEAD 2>$null).Trim()
82+
if ($currentHead -ne $baseline) {
83+
Write-Host "[BUILD-ESSENTIALS] Existing deps/vcpkg at $currentHead; checking out baseline $baseline"
84+
& git -C $localVcpkg fetch --quiet origin $baseline
85+
if ($LASTEXITCODE -ne 0) { Write-Warning "[BUILD-ESSENTIALS] fetch failed, re-cloning"; $needClone = $true } else {
86+
& git -C $localVcpkg checkout --quiet $baseline
87+
if ($LASTEXITCODE -ne 0) { Write-Error "git checkout $baseline in vcpkg failed (exit $LASTEXITCODE)"; exit 1 }
88+
}
89+
}
90+
}
91+
if ($needClone) {
92+
Write-Host "[BUILD-ESSENTIALS] Cloning microsoft/vcpkg to $localVcpkg pinned to baseline $baseline"
7193
if (Test-Path $localVcpkg) { Remove-Item -Recurse -Force $localVcpkg }
7294
& git clone https://github.com/microsoft/vcpkg $localVcpkg
7395
if ($LASTEXITCODE -ne 0) { Write-Error "git clone vcpkg failed (exit $LASTEXITCODE)"; exit 1 }
96+
& git -C $localVcpkg checkout --quiet $baseline
97+
if ($LASTEXITCODE -ne 0) { Write-Error "git checkout $baseline in vcpkg failed (exit $LASTEXITCODE)"; exit 1 }
98+
}
99+
if (-not (Test-Path (Join-Path $localVcpkg 'vcpkg.exe'))) {
74100
Push-Location $localVcpkg
75101
try {
76102
& .\bootstrap-vcpkg.bat -disableMetrics
77103
if ($LASTEXITCODE -ne 0) { Write-Error "bootstrap-vcpkg failed (exit $LASTEXITCODE)"; exit 1 }
78104
} finally { Pop-Location }
79105
}
80106
$env:VCPKG_ROOT = $localVcpkg
81-
Write-Host "[BUILD-ESSENTIALS] Using vcpkg from local checkout ($env:VCPKG_ROOT)"
107+
Write-Host "[BUILD-ESSENTIALS] Using vcpkg from local checkout ($env:VCPKG_ROOT) at $baseline"
82108
}
83109
}
84110

0 commit comments

Comments
 (0)