Skip to content

Commit 1a672e3

Browse files
5an7yCopilot
andcommitted
refactor: consolidate VS detection into Resolve-BuildEnvironment
- Remove Initialize-DevShell as a separate step; VS Dev Shell setup is now fully owned by Resolve-BuildEnvironment alongside mode detection. This eliminates the ordering problem where vswhere ran unconditionally before knowing whether the environment was EWDK (which needs no VS). - Fix EWDK regression: EWDK mode now returns early before any vswhere or Dev Shell logic is invoked. - Fix Auto+WDK from plain terminal: the old isWdk guard required UCRTVersion to be set before the Dev Shell was opened, making Auto detection fail on a clean terminal. Removed the guard; the code now falls through to Dev Shell setup and validates UCRTVersion afterward. - Fix VSINSTALLDIR trailing-backslash mismatch: when the Dev Shell is already active, VSINSTALLDIR ends with '\' while vswhere installationPath does not. Added TrimEnd('\') on both sides before comparing. - Renumber Build-Samples.ps1 step comments (old Step 5 'Detect Build Environment' merged into Step 1; remaining steps renumbered). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c4c1704 commit 1a672e3

2 files changed

Lines changed: 97 additions & 99 deletions

File tree

Build-Samples.ps1

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,9 @@ function Build-SingleSample {
292292
# Step 1 - Prepare Build Environment
293293
# =============================================================================
294294

295-
$root = (Get-Location).Path
296-
297-
$selectedVsInstall = Initialize-DevShell -ReturnToDirectory $root
295+
$root = (Get-Location).Path
296+
$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode
297+
$buildNumber = $buildEnv.BuildNumber
298298
Assert-MsBuildAvailable
299299

300300
# =============================================================================
@@ -377,14 +377,7 @@ if ($sampleSet.Count -eq 0) {
377377
}
378378

379379
# =============================================================================
380-
# Step 5 - Detect Build Environment
381-
# =============================================================================
382-
383-
$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode -VsInstallation $selectedVsInstall
384-
$buildNumber = $buildEnv.BuildNumber
385-
386-
# =============================================================================
387-
# Step 6 - Determine InfVerif Options
380+
# Step 5 - Determine InfVerif Options
388381
# =============================================================================
389382
#
390383
# Samples must build cleanly, but certain InfVerif warnings are acceptable because
@@ -400,13 +393,13 @@ else {
400393
}
401394

402395
# =============================================================================
403-
# Step 7 - Load Exclusions
396+
# Step 6 - Load Exclusions
404397
# =============================================================================
405398

406399
$exclusions = Import-SampleExclusions -CsvPath (Join-Path $root 'exclusions.csv') -BuildNumber $buildNumber
407400

408401
# =============================================================================
409-
# Step 8 - Print Build Plan
402+
# Step 7 - Print Build Plan
410403
# =============================================================================
411404

412405
$combinationsTotal = $sampleSet.Count * $Configurations.Count * $Platforms.Count
@@ -439,7 +432,7 @@ Write-Output ""
439432
Write-Output "Building all combinations..."
440433

441434
# =============================================================================
442-
# Step 9 - Execute Parallel Builds
435+
# Step 8 - Execute Parallel Builds
443436
# =============================================================================
444437

445438
# Shared mutable state protected by a Mutex. This is required because
@@ -587,7 +580,7 @@ $stopwatch.Stop()
587580
Write-Host ""
588581

589582
# =============================================================================
590-
# Step 10 - Report Failures
583+
# Step 9 - Report Failures
591584
# =============================================================================
592585

593586
Write-Output ""
@@ -626,7 +619,7 @@ if ($buildState.SporadicSet.Count -gt 0) {
626619
}
627620

628621
# =============================================================================
629-
# Step 11 - Final Summary
622+
# Step 10 - Final Summary
630623
# =============================================================================
631624

632625
$elapsed = $stopwatch.Elapsed
@@ -652,7 +645,7 @@ Write-Output " HTML report: $reportHtmlPath"
652645
Write-Output "--------------------------------------------------------------------"
653646

654647
# =============================================================================
655-
# Step 12 - Generate Reports
648+
# Step 11 - Generate Reports
656649
# =============================================================================
657650

658651
$sortedResults = $buildState.Results | Sort-Object { $_.Sample }

BuildEnvironment.ps1

Lines changed: 87 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -67,37 +67,6 @@ function Select-VsInstallation {
6767
return $Installations[$index]
6868
}
6969

70-
function Initialize-DevShell {
71-
<#
72-
.SYNOPSIS
73-
Imports the Visual Studio Developer PowerShell if not already active.
74-
.OUTPUTS
75-
Returns the selected VS installation object ({ DisplayName, InstallationPath,
76-
WdkVsComponentVersion }), or $null if the shell was already active.
77-
Callers should pass the returned object to Resolve-BuildEnvironment so VS
78-
selection happens exactly once.
79-
#>
80-
param([string]$ReturnToDirectory)
81-
82-
if ($env:VSCMD_VER) {
83-
Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)."
84-
return $null
85-
}
86-
87-
$vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk)
88-
89-
$devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
90-
if (-not (Test-Path $devShellDll)) {
91-
Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'."
92-
exit 1
93-
}
94-
95-
Import-Module $devShellDll
96-
Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath
97-
Set-Location $ReturnToDirectory
98-
return $vsInstall
99-
}
100-
10170
function Assert-MsBuildAvailable {
10271
<#
10372
.SYNOPSIS Verifies msbuild.exe is on PATH. Exits with error if not found.
@@ -119,20 +88,21 @@ function Assert-MsBuildAvailable {
11988
function Resolve-BuildEnvironment {
12089
<#
12190
.SYNOPSIS
122-
Detects or resolves the active build environment and returns metadata.
91+
Detects the active build environment, opens a VS Developer Shell when needed,
92+
and returns metadata about the environment.
12393
.DESCRIPTION
124-
When RunMode is 'Auto', checks in priority order: EWDK, NuGet, WDK.
125-
EWDK is checked first because $env:BuildLab is an active, explicit signal
126-
whereas the packages\ folder is a passive disk artifact that may linger.
127-
When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode.
128-
Pass the VsInstallation returned by Initialize-DevShell to avoid prompting the user
129-
a second time when multiple VS installations are present.
94+
Handles the full setup sequence in one place:
95+
1. Detect mode: EWDK → NuGet → WDK (Auto), or use the explicitly supplied RunMode.
96+
2. For NuGet / WDK: open a VS Developer Shell if one is not already active,
97+
prompting the user to choose if multiple VS installations with the required
98+
WDK media are found. If the shell is already active, the matching installation
99+
is located via $env:VSINSTALLDIR.
100+
3. For EWDK: skip VS detection entirely ($env:BuildLab is the authoritative signal).
130101
Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion.
131102
#>
132103
param(
133104
[string]$RepoRoot,
134-
[string]$RunMode = 'Auto',
135-
[object]$VsInstallation = $null
105+
[string]$RunMode = 'Auto'
136106
)
137107

138108
$result = @{
@@ -142,63 +112,98 @@ function Resolve-BuildEnvironment {
142112
WdkVsComponentVersion = ''
143113
}
144114

145-
$effectiveMode = $RunMode
115+
# -------------------------------------------------------------------------
116+
# Step 1 – Detect / validate build mode
117+
# -------------------------------------------------------------------------
146118

147-
# --- Resolve build environment ---
148-
if ($effectiveMode -eq 'EWDK' -or
149-
($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?<branch>[^.]+)\.(?<build>\d+)\.(?<qfe>[^.]+)$')) {
150-
if ($effectiveMode -eq 'EWDK') {
151-
# Forced EWDK: require BuildLab to be set
152-
if ($env:BuildLab -notmatch '^(?<branch>[^.]+)\.(?<build>\d+)\.(?<qfe>[^.]+)$') {
153-
Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised."
154-
exit 1
155-
}
156-
}
157-
$result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)"
158-
$result.BuildNumber = [int]$Matches.build
159-
}
160-
elseif ($effectiveMode -eq 'NuGet' -or
161-
($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) {
162-
if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) {
163-
Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run."
119+
# EWDK: checked first. $env:BuildLab is an active, explicit signal that
120+
# disappears when you close the EWDK prompt, unlike the packages\ folder.
121+
if ($RunMode -eq 'EWDK' -or
122+
($RunMode -eq 'Auto' -and $env:BuildLab -match '^(?<branch>[^.]+)\.(?<build>\d+)\.(?<qfe>[^.]+)$')) {
123+
124+
if ($RunMode -eq 'EWDK' -and
125+
$env:BuildLab -notmatch '^(?<branch>[^.]+)\.(?<build>\d+)\.(?<qfe>[^.]+)$') {
126+
Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised."
164127
exit 1
165128
}
166-
$result.Name = 'NuGet'
167-
$wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue
168-
$result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value
169-
$result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2])
129+
# Re-run the match to populate $Matches (the Auto branch already matched above;
130+
# the forced-EWDK branch needs an explicit match after the validation guard).
131+
$null = $env:BuildLab -match '^(?<branch>[^.]+)\.(?<build>\d+)\.(?<qfe>[^.]+)$'
132+
$result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)"
133+
$result.BuildNumber = [int]$Matches.build
134+
$result.WdkVsComponentVersion = '(not available for EWDK builds)'
135+
return $result
170136
}
171-
elseif ($effectiveMode -eq 'WDK' -or
172-
($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?<build>\d+)\.0')) {
173-
if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?<build>\d+)\.0') {
174-
Write-Error "RunMode is 'WDK' but UCRTVersion ('$env:UCRTVersion') is not set or does not match the expected format. Ensure the VS Developer Shell is active."
137+
138+
$isNuGet = ($RunMode -eq 'NuGet') -or
139+
($RunMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))
140+
141+
if ($RunMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) {
142+
Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run."
143+
exit 1
144+
}
145+
146+
# If not EWDK and not NuGet, assume WDK. VS Dev Shell setup below will validate
147+
# the environment; if no VS with WDK media is found, Select-VsInstallation errors out.
148+
149+
# -------------------------------------------------------------------------
150+
# Step 2 – Set up VS Developer Shell
151+
# -------------------------------------------------------------------------
152+
153+
$vsInstall = $null
154+
155+
if (-not $env:VSCMD_VER) {
156+
# Dev Shell not active – open one now.
157+
$vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk)
158+
$devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
159+
if (-not (Test-Path $devShellDll)) {
160+
Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'."
175161
exit 1
176162
}
177-
$result.Name = 'WDK'
178-
$result.BuildNumber = [int]$Matches.build
163+
Import-Module $devShellDll
164+
Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath
165+
Set-Location $RepoRoot
179166
}
180167
else {
181-
Write-Output "Environment variables {"
182-
Get-ChildItem env:* | Sort-Object Name
183-
Write-Output "Environment variables }"
184-
Write-Error "Could not determine build environment. Ensure EWDK, WDK, or NuGet packages are configured."
185-
exit 1
168+
Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)."
169+
# Locate the matching installation via VSINSTALLDIR so we can read its
170+
# WdkVsComponentVersion without prompting the user again.
171+
# Normalize trailing backslash: VSINSTALLDIR ends with '\', vswhere paths do not.
172+
$normalizedVsInstallDir = $env:VSINSTALLDIR.TrimEnd('\')
173+
$vsInstall = Get-VsInstallationsWithWdk |
174+
Where-Object { $_.InstallationPath.TrimEnd('\') -eq $normalizedVsInstallDir } |
175+
Select-Object -First 1
176+
if (-not $vsInstall) {
177+
Write-Error "The active Visual Studio Developer Shell ('$env:VSINSTALLDIR') does not have the required WDK media installed. Ensure the WDK Visual Studio component is installed."
178+
exit 1
179+
}
186180
}
187181

188-
# WDK VS component version (EWDK does not ship this metadata)
189-
if ($result.Name -match '^EWDK') {
190-
$result.WdkVsComponentVersion = '(not available for EWDK builds)'
182+
# -------------------------------------------------------------------------
183+
# Step 3 – Fill mode-specific fields (Dev Shell is now guaranteed active)
184+
# -------------------------------------------------------------------------
185+
186+
if ($isNuGet) {
187+
$result.Name = 'NuGet'
188+
$wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue
189+
$result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value
190+
$result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2])
191191
}
192192
else {
193-
# Re-use the installation selected during Initialize-DevShell if available,
194-
# otherwise query vswhere again (e.g. when the Dev Shell was already active).
195-
$vsInstall = if ($VsInstallation) { $VsInstallation } else { Select-VsInstallation (Get-VsInstallationsWithWdk) }
196-
if (-not $vsInstall.WdkVsComponentVersion) {
197-
Write-Error "Could not determine WDK component version for '$($vsInstall.DisplayName)'. Ensure the WDK Visual Studio component is installed."
193+
# WDK – Dev Shell is now active, UCRTVersion must be set.
194+
if ($env:UCRTVersion -notmatch '10\.0\.(?<build>\d+)\.0') {
195+
Write-Error "UCRTVersion ('$env:UCRTVersion') is not set or does not match the expected format. Ensure the VS Developer Shell is active."
198196
exit 1
199197
}
200-
$result.WdkVsComponentVersion = $vsInstall.WdkVsComponentVersion
198+
$result.Name = 'WDK'
199+
$result.BuildNumber = [int]$Matches.build
200+
}
201+
202+
if (-not $vsInstall.WdkVsComponentVersion) {
203+
Write-Error "Could not determine WDK component version for '$($vsInstall.DisplayName)'. Ensure the WDK Visual Studio component is installed."
204+
exit 1
201205
}
206+
$result.WdkVsComponentVersion = $vsInstall.WdkVsComponentVersion
202207

203208
return $result
204209
}

0 commit comments

Comments
 (0)