From 35d8855033e29e7025c3794c73093df24f506640 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 31 Mar 2026 15:12:51 -0700 Subject: [PATCH 01/11] Add -RunMode flag, VS detection via vswhere, and multi-install selection - Add -RunMode parameter (Auto/WDK/NuGet/EWDK/Github) defaulting to Auto - Auto-detection priority order: NuGet -> EWDK -> WDK - Github mode: same as NuGet but suppresses opening the HTML report - Add Get-VsInstallationsWithWdk: uses vswhere.exe from its fixed ProgramFiles path to find VS installs with the Microsoft.Windows.DriverKit component - Add Select-VsInstallation: errors if none found, verbose-logs if one found, presents a numbered interactive menu if multiple found - Replace Initialize-DevShell glob-based blind pick with the new helpers - Remove GITHUB_REPOSITORY auto-detect branch; replace HTML open guard with buildEnv.IsGithubMode - Update comment-based help with -RunMode parameter docs and examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Build-Samples.ps1 | 159 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 131 insertions(+), 28 deletions(-) diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 index 7eafb54d4..9cefe67eb 100644 --- a/Build-Samples.ps1 +++ b/Build-Samples.ps1 @@ -7,7 +7,7 @@ 1. Ensures a developer build environment (VS DevShell or EWDK) is active 2. Discovers samples via ListAllSamples.ps1 (or uses the -Samples parameter if provided) - 3. Detects the build environment (GitHub CI, NuGet, EWDK, or WDK) and build number + 3. Resolves the build environment (auto-detected or forced via -RunMode) and build number 4. Loads exclusions from exclusions.csv (supports wildcard paths) 5. Builds all non-excluded sample/configuration/platform combinations in parallel 6. Generates CSV and HTML overview reports @@ -38,6 +38,11 @@ Additional InfVerif options (e.g. '/samples', '/msft'). If not provided, determined automatically based on the WDK build number. +.PARAMETER RunMode + Selects the build environment mode. Valid values: Auto, WDK, NuGet, EWDK, Github. + Defaults to 'Auto', which detects the environment automatically (EWDK → WDK → NuGet). + 'Github' behaves identically to 'NuGet' but suppresses opening the HTML report at the end. + .PARAMETER ThrottleLimit Maximum parallel build jobs. Defaults to 5 x logical processors. @@ -60,6 +65,16 @@ .\Build-Samples -ThrottleLimit 8 Discovers all samples and builds them with limited parallelism. + +.EXAMPLE + .\Build-Samples -RunMode WDK + + Forces WDK mode regardless of environment variables. + +.EXAMPLE + .\Build-Samples -RunMode Github + + Runs as NuGet mode (reads packages\) without opening the HTML report — intended for CI. #> #Requires -Version 7.0 @@ -72,6 +87,8 @@ param( [string]$LogFilesDirectory = (Join-Path (Get-Location) "_logs"), [string]$ReportFileName = $(if ([string]::IsNullOrEmpty($env:WDS_ReportFileName)) { "_overview" } else { $env:WDS_ReportFileName }), [string]$InfOptions, + [ValidateSet('Auto', 'WDK', 'NuGet', 'EWDK', 'Github')] + [string]$RunMode = 'Auto', [int]$ThrottleLimit = 0 ) @@ -79,6 +96,69 @@ param( # Helper Functions # ============================================================================= +function Get-VsInstallationsWithWdk { + <# + .SYNOPSIS + Returns all Visual Studio installations that have the WDK component installed. + .DESCRIPTION + Uses vswhere.exe (from its fixed install location under Program Files (x86)) to + enumerate VS installations that carry the Microsoft.Windows.DriverKit component. + If vswhere.exe is not found at the expected path the installed VS version is too + old to be supported and the script exits with an error. + #> + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (-not (Test-Path $vswhere)) { + Write-Error "vswhere.exe was not found at '$vswhere'. Visual Studio 2017 or later is required." + exit 1 + } + + $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit 2>$null + $installations = $json | ConvertFrom-Json + return $installations | ForEach-Object { + [PSCustomObject]@{ + DisplayName = $_.displayName + InstallationPath = $_.installationPath + } + } +} + +function Select-VsInstallation { + <# + .SYNOPSIS + Chooses a Visual Studio installation from the list returned by Get-VsInstallationsWithWdk. + .DESCRIPTION + - 0 found : error + exit + - 1 found : verbose log, return it + - 2+ found : display a numbered menu and prompt the user to choose + #> + param([object[]]$Installations) + + if (-not $Installations -or $Installations.Count -eq 0) { + Write-Error "No Visual Studio installation with the required WDK media was found. Ensure the WDK Visual Studio component is installed." + exit 1 + } + + if ($Installations.Count -eq 1) { + Write-Verbose "Found Visual Studio installation with required WDK media: $($Installations[0].DisplayName) at $($Installations[0].InstallationPath)" + return $Installations[0] + } + + # Multiple installations — let the user choose + Write-Output "" + Write-Output "The following Visual Studio installations were found with the required WDK media:" + for ($i = 0; $i -lt $Installations.Count; $i++) { + Write-Output " [$($i + 1)] $($Installations[$i].DisplayName) — $($Installations[$i].InstallationPath)" + } + Write-Output "" + + do { + $choice = Read-Host "Select the installation to use [1-$($Installations.Count)]" + $index = [int]$choice - 1 + } while ($index -lt 0 -or $index -ge $Installations.Count) + + return $Installations[$index] +} + function Initialize-DevShell { <# .SYNOPSIS Imports the Visual Studio Developer PowerShell if not already active. @@ -90,16 +170,16 @@ function Initialize-DevShell { return } - $devShellDll = Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" ` - -ErrorAction SilentlyContinue | Select-Object -First 1 - if (-not $devShellDll) { - Write-Error "Visual Studio Developer Shell module not found." + $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) + + $devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll' + if (-not (Test-Path $devShellDll)) { + Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'." exit 1 } - Import-Module $devShellDll.Path - $vsInstall = Resolve-Path "$env:ProgramFiles\Microsoft Visual Studio\*\*" | Select-Object -First 1 - Enter-VsDevShell -VsInstallPath $vsInstall.Path + Import-Module $devShellDll + Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath Set-Location $ReturnToDirectory } @@ -124,38 +204,61 @@ function Assert-MsBuildAvailable { function Resolve-BuildEnvironment { <# .SYNOPSIS - Detects the active build environment and returns metadata. + Detects or resolves the active build environment and returns metadata. .DESCRIPTION - Checks (in priority order) for GitHub CI, local NuGet packages, EWDK, or WDK. - Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion. + When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. + When RunMode is 'Github', behaves identically to 'NuGet' and sets IsGithubMode. + When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. + Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion, IsGithubMode. #> - param([string]$RepoRoot) + param( + [string]$RepoRoot, + [string]$RunMode = 'Auto' + ) $result = @{ Name = '' BuildNumber = [int]0 NuGetVersion = '' WdkVsComponentVersion = '' + IsGithubMode = $false } - if ($env:GITHUB_REPOSITORY) { - $result.Name = 'GitHub' - $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue - $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value - $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) - } - elseif (Test-Path "$RepoRoot\packages\*") { + # Resolve effective mode for Github (same path as NuGet) + $effectiveMode = if ($RunMode -eq 'Github') { 'NuGet' } else { $RunMode } + if ($RunMode -eq 'Github') { $result.IsGithubMode = $true } + + # --- Resolve build environment --- + if ($effectiveMode -eq 'NuGet' -or + ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { + if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { + Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." + exit 1 + } $result.Name = 'NuGet' $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value - $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) - } - elseif ($env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { - $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + } + elseif ($effectiveMode -eq 'EWDK' -or + ($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { + if ($effectiveMode -eq 'EWDK') { + # Forced EWDK: require BuildLab to be set + if ($env:BuildLab -notmatch '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { + Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised." + exit 1 + } + } + $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" $result.BuildNumber = [int]$Matches.build } - elseif ($env:UCRTVersion -match '10\.0\.(?\d+)\.0') { - $result.Name = 'WDK' + elseif ($effectiveMode -eq 'WDK' -or + ($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?\d+)\.0')) { + if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { + 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." + exit 1 + } + $result.Name = 'WDK' $result.BuildNumber = [int]$Matches.build } else { @@ -470,7 +573,7 @@ if ($sampleSet.Count -eq 0) { # Step 5 - Detect Build Environment # ============================================================================= -$buildEnv = Resolve-BuildEnvironment -RepoRoot $root +$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode $buildNumber = $buildEnv.BuildNumber # ============================================================================= @@ -749,7 +852,7 @@ $sortedResults = $buildState.Results | Sort-Object { $_.Sample } $sortedResults | ConvertTo-Csv | Out-File $reportCsvPath $sortedResults | ConvertTo-Html -Title "WDK Sample Build Overview" | Out-File $reportHtmlPath -# Only open the HTML report interactively (not in CI/automation) -if (-not $env:GITHUB_REPOSITORY -and -not $env:BUILD_BUILDID -and [Environment]::UserInteractive) { +# Only open the HTML report interactively (not in CI/automation or Github mode) +if (-not $buildEnv.IsGithubMode -and -not $env:BUILD_BUILDID -and [Environment]::UserInteractive) { Invoke-Item $reportHtmlPath } From e021472d548bb26ff390b6b90b42caf21def7024 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 31 Mar 2026 15:14:04 -0700 Subject: [PATCH 02/11] Extract environment helper functions into BuildEnvironment.ps1 Move Get-VsInstallationsWithWdk, Select-VsInstallation, Initialize-DevShell, Assert-MsBuildAvailable, and Resolve-BuildEnvironment out of Build-Samples.ps1 into a new BuildEnvironment.ps1 helper file. Build-Samples.ps1 dot-sources it via PSScriptRoot, consistent with the existing ListAllSamples.ps1 pattern. Reduces Build-Samples.ps1 from 733 to 565 lines with no behaviour change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Build-Samples.ps1 | 189 +----------------------------------------- BuildEnvironment.ps1 | 192 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 188 deletions(-) create mode 100644 BuildEnvironment.ps1 diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 index 9cefe67eb..3dcbaa94b 100644 --- a/Build-Samples.ps1 +++ b/Build-Samples.ps1 @@ -96,194 +96,7 @@ param( # Helper Functions # ============================================================================= -function Get-VsInstallationsWithWdk { - <# - .SYNOPSIS - Returns all Visual Studio installations that have the WDK component installed. - .DESCRIPTION - Uses vswhere.exe (from its fixed install location under Program Files (x86)) to - enumerate VS installations that carry the Microsoft.Windows.DriverKit component. - If vswhere.exe is not found at the expected path the installed VS version is too - old to be supported and the script exits with an error. - #> - $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - if (-not (Test-Path $vswhere)) { - Write-Error "vswhere.exe was not found at '$vswhere'. Visual Studio 2017 or later is required." - exit 1 - } - - $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit 2>$null - $installations = $json | ConvertFrom-Json - return $installations | ForEach-Object { - [PSCustomObject]@{ - DisplayName = $_.displayName - InstallationPath = $_.installationPath - } - } -} - -function Select-VsInstallation { - <# - .SYNOPSIS - Chooses a Visual Studio installation from the list returned by Get-VsInstallationsWithWdk. - .DESCRIPTION - - 0 found : error + exit - - 1 found : verbose log, return it - - 2+ found : display a numbered menu and prompt the user to choose - #> - param([object[]]$Installations) - - if (-not $Installations -or $Installations.Count -eq 0) { - Write-Error "No Visual Studio installation with the required WDK media was found. Ensure the WDK Visual Studio component is installed." - exit 1 - } - - if ($Installations.Count -eq 1) { - Write-Verbose "Found Visual Studio installation with required WDK media: $($Installations[0].DisplayName) at $($Installations[0].InstallationPath)" - return $Installations[0] - } - - # Multiple installations — let the user choose - Write-Output "" - Write-Output "The following Visual Studio installations were found with the required WDK media:" - for ($i = 0; $i -lt $Installations.Count; $i++) { - Write-Output " [$($i + 1)] $($Installations[$i].DisplayName) — $($Installations[$i].InstallationPath)" - } - Write-Output "" - - do { - $choice = Read-Host "Select the installation to use [1-$($Installations.Count)]" - $index = [int]$choice - 1 - } while ($index -lt 0 -or $index -ge $Installations.Count) - - return $Installations[$index] -} - -function Initialize-DevShell { - <# - .SYNOPSIS Imports the Visual Studio Developer PowerShell if not already active. - #> - param([string]$ReturnToDirectory) - - if ($env:VSCMD_VER) { - Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." - return - } - - $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) - - $devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll' - if (-not (Test-Path $devShellDll)) { - Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'." - exit 1 - } - - Import-Module $devShellDll - Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath - Set-Location $ReturnToDirectory -} - -function Assert-MsBuildAvailable { - <# - .SYNOPSIS Verifies msbuild.exe is on PATH. Exits with error if not found. - #> - $savedPref = $ErrorActionPreference - $ErrorActionPreference = 'Stop' - try { - Get-Command 'msbuild' | Out-Null - } - catch { - Write-Error "msbuild cannot be called from current environment. Ensure it is on PATH (run from VS Developer Command Prompt or EWDK)." - exit 1 - } - finally { - $ErrorActionPreference = $savedPref - } -} - -function Resolve-BuildEnvironment { - <# - .SYNOPSIS - Detects or resolves the active build environment and returns metadata. - .DESCRIPTION - When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. - When RunMode is 'Github', behaves identically to 'NuGet' and sets IsGithubMode. - When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. - Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion, IsGithubMode. - #> - param( - [string]$RepoRoot, - [string]$RunMode = 'Auto' - ) - - $result = @{ - Name = '' - BuildNumber = [int]0 - NuGetVersion = '' - WdkVsComponentVersion = '' - IsGithubMode = $false - } - - # Resolve effective mode for Github (same path as NuGet) - $effectiveMode = if ($RunMode -eq 'Github') { 'NuGet' } else { $RunMode } - if ($RunMode -eq 'Github') { $result.IsGithubMode = $true } - - # --- Resolve build environment --- - if ($effectiveMode -eq 'NuGet' -or - ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { - if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { - Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." - exit 1 - } - $result.Name = 'NuGet' - $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue - $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value - $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) - } - elseif ($effectiveMode -eq 'EWDK' -or - ($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { - if ($effectiveMode -eq 'EWDK') { - # Forced EWDK: require BuildLab to be set - if ($env:BuildLab -notmatch '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { - Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised." - exit 1 - } - } - $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" - $result.BuildNumber = [int]$Matches.build - } - elseif ($effectiveMode -eq 'WDK' -or - ($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?\d+)\.0')) { - if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { - 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." - exit 1 - } - $result.Name = 'WDK' - $result.BuildNumber = [int]$Matches.build - } - else { - Write-Output "Environment variables {" - Get-ChildItem env:* | Sort-Object Name - Write-Output "Environment variables }" - Write-Error "Could not determine build environment. Ensure EWDK, WDK, or NuGet packages are configured." - exit 1 - } - - # WDK VS component version (EWDK does not ship this metadata) - if ($result.Name -match '^EWDK') { - $result.WdkVsComponentVersion = '(not available for EWDK builds)' - } - else { - $vsComponent = Get-ChildItem "${env:ProgramData}\Microsoft\VisualStudio\Packages\Microsoft.Windows.DriverKit,version=*" -ErrorAction SilentlyContinue - if (-not $vsComponent) { - Write-Error "WDK Visual Studio Component not found. Ensure the WDK Component is installed." - exit 1 - } - $result.WdkVsComponentVersion = [regex]::Match($vsComponent.Name, '(\d+\.){3}\d+').Value - } - - return $result -} +. (Join-Path $PSScriptRoot 'BuildEnvironment.ps1') function Import-SampleExclusions { <# diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 new file mode 100644 index 000000000..b0b03ee12 --- /dev/null +++ b/BuildEnvironment.ps1 @@ -0,0 +1,192 @@ +# BuildEnvironment.ps1 +# Helper functions for detecting and initialising the build environment. +# Dot-sourced by Build-Samples.ps1. + +function Get-VsInstallationsWithWdk { + <# + .SYNOPSIS + Returns all Visual Studio installations that have the WDK component installed. + .DESCRIPTION + Uses vswhere.exe (from its fixed install location under Program Files (x86)) to + enumerate VS installations that carry the Microsoft.Windows.DriverKit component. + If vswhere.exe is not found at the expected path the installed VS version is too + old to be supported and the script exits with an error. + #> + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (-not (Test-Path $vswhere)) { + Write-Error "vswhere.exe was not found at '$vswhere'. Visual Studio 2017 or later is required." + exit 1 + } + + $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit 2>$null + $installations = $json | ConvertFrom-Json + return $installations | ForEach-Object { + [PSCustomObject]@{ + DisplayName = $_.displayName + InstallationPath = $_.installationPath + } + } +} + +function Select-VsInstallation { + <# + .SYNOPSIS + Chooses a Visual Studio installation from the list returned by Get-VsInstallationsWithWdk. + .DESCRIPTION + - 0 found : error + exit + - 1 found : verbose log, return it + - 2+ found : display a numbered menu and prompt the user to choose + #> + param([object[]]$Installations) + + if (-not $Installations -or $Installations.Count -eq 0) { + Write-Error "No Visual Studio installation with the required WDK media was found. Ensure the WDK Visual Studio component is installed." + exit 1 + } + + if ($Installations.Count -eq 1) { + Write-Verbose "Found Visual Studio installation with required WDK media: $($Installations[0].DisplayName) at $($Installations[0].InstallationPath)" + return $Installations[0] + } + + # Multiple installations — let the user choose + Write-Output "" + Write-Output "The following Visual Studio installations were found with the required WDK media:" + for ($i = 0; $i -lt $Installations.Count; $i++) { + Write-Output " [$($i + 1)] $($Installations[$i].DisplayName) — $($Installations[$i].InstallationPath)" + } + Write-Output "" + + do { + $choice = Read-Host "Select the installation to use [1-$($Installations.Count)]" + $index = [int]$choice - 1 + } while ($index -lt 0 -or $index -ge $Installations.Count) + + return $Installations[$index] +} + +function Initialize-DevShell { + <# + .SYNOPSIS Imports the Visual Studio Developer PowerShell if not already active. + #> + param([string]$ReturnToDirectory) + + if ($env:VSCMD_VER) { + Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." + return + } + + $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) + + $devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll' + if (-not (Test-Path $devShellDll)) { + Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'." + exit 1 + } + + Import-Module $devShellDll + Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath + Set-Location $ReturnToDirectory +} + +function Assert-MsBuildAvailable { + <# + .SYNOPSIS Verifies msbuild.exe is on PATH. Exits with error if not found. + #> + $savedPref = $ErrorActionPreference + $ErrorActionPreference = 'Stop' + try { + Get-Command 'msbuild' | Out-Null + } + catch { + Write-Error "msbuild cannot be called from current environment. Ensure it is on PATH (run from VS Developer Command Prompt or EWDK)." + exit 1 + } + finally { + $ErrorActionPreference = $savedPref + } +} + +function Resolve-BuildEnvironment { + <# + .SYNOPSIS + Detects or resolves the active build environment and returns metadata. + .DESCRIPTION + When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. + When RunMode is 'Github', behaves identically to 'NuGet' and sets IsGithubMode. + When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. + Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion, IsGithubMode. + #> + param( + [string]$RepoRoot, + [string]$RunMode = 'Auto' + ) + + $result = @{ + Name = '' + BuildNumber = [int]0 + NuGetVersion = '' + WdkVsComponentVersion = '' + IsGithubMode = $false + } + + # Resolve effective mode for Github (same path as NuGet) + $effectiveMode = if ($RunMode -eq 'Github') { 'NuGet' } else { $RunMode } + if ($RunMode -eq 'Github') { $result.IsGithubMode = $true } + + # --- Resolve build environment --- + if ($effectiveMode -eq 'NuGet' -or + ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { + if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { + Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." + exit 1 + } + $result.Name = 'NuGet' + $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue + $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + } + elseif ($effectiveMode -eq 'EWDK' -or + ($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { + if ($effectiveMode -eq 'EWDK') { + # Forced EWDK: require BuildLab to be set + if ($env:BuildLab -notmatch '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { + Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised." + exit 1 + } + } + $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" + $result.BuildNumber = [int]$Matches.build + } + elseif ($effectiveMode -eq 'WDK' -or + ($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?\d+)\.0')) { + if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { + 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." + exit 1 + } + $result.Name = 'WDK' + $result.BuildNumber = [int]$Matches.build + } + else { + Write-Output "Environment variables {" + Get-ChildItem env:* | Sort-Object Name + Write-Output "Environment variables }" + Write-Error "Could not determine build environment. Ensure EWDK, WDK, or NuGet packages are configured." + exit 1 + } + + # WDK VS component version (EWDK does not ship this metadata) + if ($result.Name -match '^EWDK') { + $result.WdkVsComponentVersion = '(not available for EWDK builds)' + } + else { + $vsComponent = Get-ChildItem "${env:ProgramData}\Microsoft\VisualStudio\Packages\Microsoft.Windows.DriverKit,version=*" -ErrorAction SilentlyContinue + if (-not $vsComponent) { + Write-Error "WDK Visual Studio Component not found. Ensure the WDK Component is installed." + exit 1 + } + $result.WdkVsComponentVersion = [regex]::Match($vsComponent.Name, '(\d+\.){3}\d+').Value + } + + return $result +} From 12f94c8573a5ef79abf7842d3ea0faf458c3f733 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 31 Mar 2026 15:58:46 -0700 Subject: [PATCH 03/11] fix: detect WDK component version from the selected VS installation Previously, WdkVsComponentVersion was read from the global ProgramData package cache, which is shared across all VS installations and could return the wrong version when multiple installs are present. - Add -include packages to the vswhere call in Get-VsInstallationsWithWdk so each returned installation includes its WdkVsComponentVersion - Initialize-DevShell now returns the selected installation object - Resolve-BuildEnvironment accepts an optional -VsInstallation parameter; when provided it reuses the already-selected install (no second prompt); when absent (Dev Shell was pre-active) it calls Select-VsInstallation once - Build-Samples.ps1 threads the result of Initialize-DevShell through to Resolve-BuildEnvironment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Build-Samples.ps1 | 4 ++-- BuildEnvironment.ps1 | 34 ++++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 index 3dcbaa94b..50f48949a 100644 --- a/Build-Samples.ps1 +++ b/Build-Samples.ps1 @@ -300,7 +300,7 @@ function Build-SingleSample { $root = (Get-Location).Path -Initialize-DevShell -ReturnToDirectory $root +$selectedVsInstall = Initialize-DevShell -ReturnToDirectory $root Assert-MsBuildAvailable # ============================================================================= @@ -386,7 +386,7 @@ if ($sampleSet.Count -eq 0) { # Step 5 - Detect Build Environment # ============================================================================= -$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode +$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode -VsInstallation $selectedVsInstall $buildNumber = $buildEnv.BuildNumber # ============================================================================= diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index b0b03ee12..285f3d7ec 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -18,12 +18,14 @@ function Get-VsInstallationsWithWdk { exit 1 } - $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit 2>$null + $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit -include packages 2>$null $installations = $json | ConvertFrom-Json return $installations | ForEach-Object { + $wdkPackage = $_.packages | Where-Object { $_.id -eq 'Microsoft.Windows.DriverKit' } | Select-Object -First 1 [PSCustomObject]@{ - DisplayName = $_.displayName - InstallationPath = $_.installationPath + DisplayName = $_.displayName + InstallationPath = $_.installationPath + WdkVsComponentVersion = $wdkPackage.version } } } @@ -67,13 +69,19 @@ function Select-VsInstallation { function Initialize-DevShell { <# - .SYNOPSIS Imports the Visual Studio Developer PowerShell if not already active. + .SYNOPSIS + Imports the Visual Studio Developer PowerShell if not already active. + .OUTPUTS + Returns the selected VS installation object ({ DisplayName, InstallationPath, + WdkVsComponentVersion }), or $null if the shell was already active. + Callers should pass the returned object to Resolve-BuildEnvironment so VS + selection happens exactly once. #> param([string]$ReturnToDirectory) if ($env:VSCMD_VER) { Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." - return + return $null } $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) @@ -87,6 +95,7 @@ function Initialize-DevShell { Import-Module $devShellDll Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath Set-Location $ReturnToDirectory + return $vsInstall } function Assert-MsBuildAvailable { @@ -115,11 +124,14 @@ function Resolve-BuildEnvironment { When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. When RunMode is 'Github', behaves identically to 'NuGet' and sets IsGithubMode. When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. + Pass the VsInstallation returned by Initialize-DevShell to avoid prompting the user + a second time when multiple VS installations are present. Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion, IsGithubMode. #> param( [string]$RepoRoot, - [string]$RunMode = 'Auto' + [string]$RunMode = 'Auto', + [object]$VsInstallation = $null ) $result = @{ @@ -180,12 +192,14 @@ function Resolve-BuildEnvironment { $result.WdkVsComponentVersion = '(not available for EWDK builds)' } else { - $vsComponent = Get-ChildItem "${env:ProgramData}\Microsoft\VisualStudio\Packages\Microsoft.Windows.DriverKit,version=*" -ErrorAction SilentlyContinue - if (-not $vsComponent) { - Write-Error "WDK Visual Studio Component not found. Ensure the WDK Component is installed." + # Re-use the installation selected during Initialize-DevShell if available, + # otherwise query vswhere again (e.g. when the Dev Shell was already active). + $vsInstall = if ($VsInstallation) { $VsInstallation } else { Select-VsInstallation (Get-VsInstallationsWithWdk) } + if (-not $vsInstall.WdkVsComponentVersion) { + Write-Error "Could not determine WDK component version for '$($vsInstall.DisplayName)'. Ensure the WDK Visual Studio component is installed." exit 1 } - $result.WdkVsComponentVersion = [regex]::Match($vsComponent.Name, '(\d+\.){3}\d+').Value + $result.WdkVsComponentVersion = $vsInstall.WdkVsComponentVersion } return $result From 8508ffaaf97a200fe6e8588333c6ab1383687d4f Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 31 Mar 2026 16:02:08 -0700 Subject: [PATCH 04/11] refactor: remove Github RunMode NuGet mode works correctly for GitHub Actions (the HTML open is already guarded by [Environment]::UserInteractive which is false on CI runners). The Github mode was redundant and is removed to keep the interface clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Build-Samples.ps1 | 16 +++++----------- BuildEnvironment.ps1 | 8 ++------ 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 index 50f48949a..a479177b7 100644 --- a/Build-Samples.ps1 +++ b/Build-Samples.ps1 @@ -39,9 +39,8 @@ automatically based on the WDK build number. .PARAMETER RunMode - Selects the build environment mode. Valid values: Auto, WDK, NuGet, EWDK, Github. - Defaults to 'Auto', which detects the environment automatically (EWDK → WDK → NuGet). - 'Github' behaves identically to 'NuGet' but suppresses opening the HTML report at the end. + Selects the build environment mode. Valid values: Auto, WDK, NuGet, EWDK. + Defaults to 'Auto', which detects the environment automatically (NuGet → EWDK → WDK). .PARAMETER ThrottleLimit Maximum parallel build jobs. Defaults to 5 x logical processors. @@ -70,11 +69,6 @@ .\Build-Samples -RunMode WDK Forces WDK mode regardless of environment variables. - -.EXAMPLE - .\Build-Samples -RunMode Github - - Runs as NuGet mode (reads packages\) without opening the HTML report — intended for CI. #> #Requires -Version 7.0 @@ -87,7 +81,7 @@ param( [string]$LogFilesDirectory = (Join-Path (Get-Location) "_logs"), [string]$ReportFileName = $(if ([string]::IsNullOrEmpty($env:WDS_ReportFileName)) { "_overview" } else { $env:WDS_ReportFileName }), [string]$InfOptions, - [ValidateSet('Auto', 'WDK', 'NuGet', 'EWDK', 'Github')] + [ValidateSet('Auto', 'WDK', 'NuGet', 'EWDK')] [string]$RunMode = 'Auto', [int]$ThrottleLimit = 0 ) @@ -665,7 +659,7 @@ $sortedResults = $buildState.Results | Sort-Object { $_.Sample } $sortedResults | ConvertTo-Csv | Out-File $reportCsvPath $sortedResults | ConvertTo-Html -Title "WDK Sample Build Overview" | Out-File $reportHtmlPath -# Only open the HTML report interactively (not in CI/automation or Github mode) -if (-not $buildEnv.IsGithubMode -and -not $env:BUILD_BUILDID -and [Environment]::UserInteractive) { +# Only open the HTML report interactively (not in CI/automation) +if (-not $env:BUILD_BUILDID -and [Environment]::UserInteractive) { Invoke-Item $reportHtmlPath } diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index 285f3d7ec..08f0469e0 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -122,11 +122,10 @@ function Resolve-BuildEnvironment { Detects or resolves the active build environment and returns metadata. .DESCRIPTION When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. - When RunMode is 'Github', behaves identically to 'NuGet' and sets IsGithubMode. When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. Pass the VsInstallation returned by Initialize-DevShell to avoid prompting the user a second time when multiple VS installations are present. - Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion, IsGithubMode. + Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion. #> param( [string]$RepoRoot, @@ -139,12 +138,9 @@ function Resolve-BuildEnvironment { BuildNumber = [int]0 NuGetVersion = '' WdkVsComponentVersion = '' - IsGithubMode = $false } - # Resolve effective mode for Github (same path as NuGet) - $effectiveMode = if ($RunMode -eq 'Github') { 'NuGet' } else { $RunMode } - if ($RunMode -eq 'Github') { $result.IsGithubMode = $true } + $effectiveMode = $RunMode # --- Resolve build environment --- if ($effectiveMode -eq 'NuGet' -or From f9545d33190ad51e13d4bdf2942e83d00fa8e379 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Thu, 2 Apr 2026 10:00:58 -0700 Subject: [PATCH 05/11] fix: prioritize EWDK over NuGet in auto-detection The packages\ folder is a passive disk artifact that persists between builds. If someone runs the script from inside an EWDK command prompt while packages\ exists from a previous NuGet restore, Auto mode would incorrectly select NuGet and then fail trying to find a VS installation that may not be present in an EWDK-only setup. EWDK is now checked first because \ being set is an active, explicit signal that the user is intentionally in the EWDK environment. Auto detection order is now: EWDK -> NuGet -> WDK Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- BuildEnvironment.ps1 | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index 08f0469e0..0b0520da7 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -121,7 +121,9 @@ function Resolve-BuildEnvironment { .SYNOPSIS Detects or resolves the active build environment and returns metadata. .DESCRIPTION - When RunMode is 'Auto', checks in priority order: NuGet, EWDK, WDK. + When RunMode is 'Auto', checks in priority order: EWDK, NuGet, WDK. + EWDK is checked first because $env:BuildLab is an active, explicit signal + whereas the packages\ folder is a passive disk artifact that may linger. When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. Pass the VsInstallation returned by Initialize-DevShell to avoid prompting the user a second time when multiple VS installations are present. @@ -143,18 +145,7 @@ function Resolve-BuildEnvironment { $effectiveMode = $RunMode # --- Resolve build environment --- - if ($effectiveMode -eq 'NuGet' -or - ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { - if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { - Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." - exit 1 - } - $result.Name = 'NuGet' - $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue - $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value - $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) - } - elseif ($effectiveMode -eq 'EWDK' -or + if ($effectiveMode -eq 'EWDK' -or ($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { if ($effectiveMode -eq 'EWDK') { # Forced EWDK: require BuildLab to be set @@ -166,6 +157,17 @@ function Resolve-BuildEnvironment { $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" $result.BuildNumber = [int]$Matches.build } + elseif ($effectiveMode -eq 'NuGet' -or + ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { + if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { + Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." + exit 1 + } + $result.Name = 'NuGet' + $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue + $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + } elseif ($effectiveMode -eq 'WDK' -or ($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?\d+)\.0')) { if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { From c4c1704e398b8ca96bf25f519802ae86182c140f Mon Sep 17 00:00:00 2001 From: 5an7y Date: Thu, 2 Apr 2026 14:45:56 -0700 Subject: [PATCH 06/11] docs: rewrite Building-Locally.md for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the document into a logical flow: 1. Prerequisites — tools (winget), WDK download link, repo clone 2. Building the Samples — NuGet, EWDK, WDK MSI/winget subsections with -RunMode intro 3. Expected Output — build plan and completion summary 4. Ways to Run — common Build-Samples.ps1 invocations 5. Additional Notes — pre-release WDK, usbview .NET packs, NuGet version pinning Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Building-Locally.md | 238 +++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 137 deletions(-) diff --git a/Building-Locally.md b/Building-Locally.md index 645e9a615..290d59545 100644 --- a/Building-Locally.md +++ b/Building-Locally.md @@ -1,165 +1,84 @@ -# How to build locally +# Building Driver Samples Locally -## Step 1: Install Tools +## Prerequisites -```powershell -winget install --id Microsoft.Powershell --source winget -winget install --id Git.Git --source winget -``` +### Required tools -For using WDK NuGet feed based build additionally: +Install PowerShell and Git if you don't have them already: ```powershell -winget install --id Microsoft.NuGet --source winget -``` - ---- - -## Step 2: Optional: Disable Strong Name Validation - -When: This step is only required if you will be using pre-release versions of the WDK. - -As per https://learn.microsoft.com/en-us/windows-hardware/drivers/installing-preview-versions-wdk : - -Run the following commands from an elevated command prompt to disable strong name validation: - -``` -reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\*,31bf3856ad364e35 /v TestPublicKey /t REG_SZ /d 00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0 /f - -reg add HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,31bf3856ad364e35 /v TestPublicKey /t REG_SZ /d 00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0 /f +winget install --id Microsoft.Powershell --source winget +winget install --id Git.Git --source winget ``` ---- - -## Step 3: Optional: Install Microsoft .NET Framework 4.7.2 Targeting Pack and Microsoft .NET Framework 4.8.1 SDK +### Install a supported version of the WDK -When: This step is only required to build sample usb\usbview . +See [Download the Windows Driver Kit (WDK)](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) for all available installation options (NuGet packages, MSI installer, EWDK ISO). -### Option A: Install VS Components - -Easy: If you will install Visual Studio (see later) you may at that point select to add both of following individual components: -* .NET Framework 4.7.2 targeting pack -* .NET Framework 4.8.1 SDK - -### Option B: Use EWDK - -Easy: If you use EWDK, then all necessary prequisites are included. - -### Option C: Install Developer Pack - -Hardest: Install from https://aka.ms/msbuild/developerpacks -> '.NET Framework' -> 'Supported versions' both of following packages: -* .NET Framework 4.7.2 -> Developer Pack -* .NET Framework 4.8.1 -> Developer Pack - -This will install following Apps: -* Microsoft .NET Framework 4.7.2 SDK -* Microsoft .NET Framework 4.7.2 Targeting Pack -* Microsoft .NET Framework 4.7.2 Targeting Pack (ENU) -* Microsoft .NET Framework 4.8.1 SDK -* Microsoft .NET Framework 4.8.1 Targeting Pack -* Microsoft .NET Framework 4.8.1 Targeting Pack (ENU) - ---- - -## Step 4: Clone Windows Driver Samples and checkout relevant branch +### Clone the repository ```powershell -cd "path\to\your\repos" git clone --recurse-submodules "https://github.com/microsoft/Windows-driver-samples.git" cd ".\Windows-driver-samples" ``` -If you are planning to use in-market WDK, then you would typically want to use the 'main' branch: - -``` -git checkout main -``` - -If you are planning to use a WDK Preview or WDK EEAP release, then you would typically want to use the 'develop' branch: +Use `main` for in-market WDK releases, `develop` for WDK Preview / EEAP builds: -``` -git checkout develop +```powershell +git checkout main # stable / in-market +git checkout develop # preview / EEAP ``` --- -## Step 5: Create a "driver build environment" +## Building the Samples -To build the Windows Driver Samples you need a "driver build environment". In essence an environment that consist of following prerequisites: -* Visual Studio Build Tools including tools such as for example cl.exe and link.exe . -* The Windows Software Development Kit. -* The Windows Driver Kit. - -### Option A: Use WDK NuGet Packages - -* See [Download the Windows Driver Kit (WDK)](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) for instructions on how to install Visual Studio, but only complete `Step 1`. You do not need to install the SDK or the WDK. -* Launch a "Developer Command Prompt for VS 2022". -* Restore WDK packages from feed : +The script auto-detects which WDK environment is active. Use `-RunMode` to force a specific one if needed: ```powershell -cd "path\to\your\repos\Windows-driver-samples" -nuget restore -PackagesDirectory ".\packages" +.\Build-Samples.ps1 -RunMode NuGet # force NuGet +.\Build-Samples.ps1 -RunMode EWDK # force EWDK +.\Build-Samples.ps1 -RunMode WDK # force WDK ``` -* When this is done you should have a .\packages folder that looks like example below: +### NuGet Package + +Install NuGet if you don't have it: ```powershell -cd "path\to\your\repos\Windows-driver-samples" -dir /b packages -Microsoft.Windows.SDK.CPP.10.0.26000.1 -Microsoft.Windows.SDK.CPP.x64.10.0.26000.1 -Microsoft.Windows.SDK.CPP.arm64.10.0.26000.1 -Microsoft.Windows.WDK.x64.10.0.26000.1 -Microsoft.Windows.WDK.arm64.10.0.26000.1 +winget install --id Microsoft.NuGet --source winget ``` -### Option B: Use the Windows Driver Kit - -* Here you will install each of above prerequisites one at a time. -* See [Download the Windows Driver Kit (WDK)](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) for instructions on how to install Visual Studio, SDK, and WDK. -* Launch a "Developer Command Prompt for VS 2022". - -### Option C: Use an Enterprise WDK - -* You can also simply use the Enterprise WDK (EWDK), a standalone, self-contained command-line environment for building drivers that contains all prerequisites in one combined ISO. -* See [Download the Windows Driver Kit (WDK)](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) for instructions on how to download the EWDK. -* Mount ISO image -* Open a terminal -* `.\LaunchBuildEnv` - ---- - -## Step 6: Build all samples +Restore the WDK packages and build from Powershell: ```powershell +nuget restore -PackagesDirectory ".\packages" pwsh -.\Build-Samples +.\Build-Samples.ps1 ``` -Above builds all samples for all configurations and platforms. - -You can refine what exact samples to build, what configurations, and platforms to build. Here are a few examples: -```powershell -# Get Help: -Get-Help .\Build-Samples +### EWDK -# Build all solutions for all flavors with builds running in parallel: -.\Build-Samples +Mount the EWDK ISO, open a terminal in the mounted drive, launch the build environment, then build: -# Build with Verbose output (print start and finish of each sample): -.\Build-Samples -Verbose +```powershell +.\LaunchBuildEnv +pwsh +.\Build-Samples.ps1 +``` -# Build without massive parallelism (slow, but good for debugging): -.\Build-Samples -ThrottleLimit 1 +### WDK MSI/winget -# Build all samples inside the 'tools' folder using wildcards: -.\Build-Samples -Samples 'tools.*' +Build the samples from PowerShell: -# Build specific samples for only 'Debug|x64': -.\Build-Samples -Samples 'tools.sdv.samples.sampledriver' -Configurations 'Debug' -Platforms 'x64' +```powershell +pwsh +.\Build-Samples.ps1 ``` -Example of expected output: +--- + +## Expected Output ``` --- WDK Sample Build Plan ------------------------------------------ @@ -209,31 +128,76 @@ Building all combinations... --- -## 7: NuGet - Additional Notes +## Ways to Run -To restore a specific version of our WDK NuGet packages: +```powershell +# Show full parameter reference: +Get-Help .\Build-Samples.ps1 -Detailed + +# Build everything (all samples, configurations, platforms): +.\Build-Samples.ps1 + +# Verbose output — prints start/finish of each sample: +.\Build-Samples.ps1 -Verbose -Follow these steps before running "nuget restore" command: -* Open the .\packages.config file and update the full version (including the branch if required) in all three entries. -* Open the .\Directory.build.props file and update the version and build of the package with the same values as in previous step. -* Open .\Build-Samples.ps1 and check the NuGet build number logic (used by .\exclusions.csv and for determining infverif flags) -* Now you can run "nuget restore" +# Limit parallelism (useful for debugging build failures): +.\Build-Samples.ps1 -ThrottleLimit 1 -A few examples of how to interact with nuget: +# Build only samples inside the 'tools' folder: +.\Build-Samples.ps1 -Samples 'tools.*' + +# Build a specific sample for Debug|x64 only: +.\Build-Samples.ps1 -Samples 'tools.sdv.samples.sampledriver' -Configurations 'Debug' -Platforms 'x64' +``` + +--- + +## Additional Notes + +### Pre-release WDK: disable strong name validation + +Required only when using pre-release WDK versions. Run from an elevated command prompt: + +``` +reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\*,31bf3856ad364e35 /v TestPublicKey /t REG_SZ /d 00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0 /f + +reg add HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,31bf3856ad364e35 /v TestPublicKey /t REG_SZ /d 00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0 /f +``` + +See [Installing preview versions of the WDK](https://learn.microsoft.com/en-us/windows-hardware/drivers/installing-preview-versions-wdk) for more details. + +### Building `usb\usbview`: .NET Framework targeting packs + +The `usb\usbview` sample requires .NET Framework 4.7.2 and 4.8.1. Choose one option: + +- **VS installer** — add the *.NET Framework 4.7.2 targeting pack* and *.NET Framework 4.8.1 SDK* individual components when installing Visual Studio. +- **EWDK** — all required prerequisites are already included. +- **Manual** — download both Developer Packs from https://aka.ms/msbuild/developerpacks. + +### NuGet: restoring a specific WDK version + +To pin a specific WDK NuGet version before running `nuget restore`: + +1. Open `.\packages.config` and update the version in all entries. +2. Open `.\Directory.build.props` and set the same version. +3. Run `nuget restore -PackagesDirectory ".\packages"`. + +Useful NuGet commands: ```powershell -# To add an alternative online NuGet source: -nuget sources add -Name "MyNuGetFeed" -Source "https://nugetserver.com/_packaging/feedname/nuget/v3/index.json" +# Add an online feed: +nuget sources add -Name "MyFeed" -Source "https://nugetserver.com/_packaging/feedname/nuget/v3/index.json" -# To add an alternative local NuGet source: -nuget sources add -Name "MyNuGetFeed" -Source "\\path\to\mylocalrepo" +# Add a local feed: +nuget sources add -Name "MyFeed" -Source "\\path\to\mylocalrepo" -# To remove an alternative NuGet source: -nuget sources remove -Name "MyNuGetFeed" +# Remove a feed: +nuget sources remove -Name "MyFeed" -# To enumerate NuGet locals: +# List local caches: nuget locals all -list -# To clear NuGet locals: +# Clear local caches: nuget locals all -clear ``` + From 1a672e37d6773fa8324d358d0191f4e6fce19d47 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Thu, 2 Apr 2026 15:36:34 -0700 Subject: [PATCH 07/11] 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> --- Build-Samples.ps1 | 27 +++---- BuildEnvironment.ps1 | 169 ++++++++++++++++++++++--------------------- 2 files changed, 97 insertions(+), 99 deletions(-) diff --git a/Build-Samples.ps1 b/Build-Samples.ps1 index a479177b7..a73a2afa1 100644 --- a/Build-Samples.ps1 +++ b/Build-Samples.ps1 @@ -292,9 +292,9 @@ function Build-SingleSample { # Step 1 - Prepare Build Environment # ============================================================================= -$root = (Get-Location).Path - -$selectedVsInstall = Initialize-DevShell -ReturnToDirectory $root +$root = (Get-Location).Path +$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode +$buildNumber = $buildEnv.BuildNumber Assert-MsBuildAvailable # ============================================================================= @@ -377,14 +377,7 @@ if ($sampleSet.Count -eq 0) { } # ============================================================================= -# Step 5 - Detect Build Environment -# ============================================================================= - -$buildEnv = Resolve-BuildEnvironment -RepoRoot $root -RunMode $RunMode -VsInstallation $selectedVsInstall -$buildNumber = $buildEnv.BuildNumber - -# ============================================================================= -# Step 6 - Determine InfVerif Options +# Step 5 - Determine InfVerif Options # ============================================================================= # # Samples must build cleanly, but certain InfVerif warnings are acceptable because @@ -400,13 +393,13 @@ else { } # ============================================================================= -# Step 7 - Load Exclusions +# Step 6 - Load Exclusions # ============================================================================= $exclusions = Import-SampleExclusions -CsvPath (Join-Path $root 'exclusions.csv') -BuildNumber $buildNumber # ============================================================================= -# Step 8 - Print Build Plan +# Step 7 - Print Build Plan # ============================================================================= $combinationsTotal = $sampleSet.Count * $Configurations.Count * $Platforms.Count @@ -439,7 +432,7 @@ Write-Output "" Write-Output "Building all combinations..." # ============================================================================= -# Step 9 - Execute Parallel Builds +# Step 8 - Execute Parallel Builds # ============================================================================= # Shared mutable state protected by a Mutex. This is required because @@ -587,7 +580,7 @@ $stopwatch.Stop() Write-Host "" # ============================================================================= -# Step 10 - Report Failures +# Step 9 - Report Failures # ============================================================================= Write-Output "" @@ -626,7 +619,7 @@ if ($buildState.SporadicSet.Count -gt 0) { } # ============================================================================= -# Step 11 - Final Summary +# Step 10 - Final Summary # ============================================================================= $elapsed = $stopwatch.Elapsed @@ -652,7 +645,7 @@ Write-Output " HTML report: $reportHtmlPath" Write-Output "--------------------------------------------------------------------" # ============================================================================= -# Step 12 - Generate Reports +# Step 11 - Generate Reports # ============================================================================= $sortedResults = $buildState.Results | Sort-Object { $_.Sample } diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index 0b0520da7..c4e3cffaa 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -67,37 +67,6 @@ function Select-VsInstallation { return $Installations[$index] } -function Initialize-DevShell { - <# - .SYNOPSIS - Imports the Visual Studio Developer PowerShell if not already active. - .OUTPUTS - Returns the selected VS installation object ({ DisplayName, InstallationPath, - WdkVsComponentVersion }), or $null if the shell was already active. - Callers should pass the returned object to Resolve-BuildEnvironment so VS - selection happens exactly once. - #> - param([string]$ReturnToDirectory) - - if ($env:VSCMD_VER) { - Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." - return $null - } - - $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) - - $devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll' - if (-not (Test-Path $devShellDll)) { - Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'." - exit 1 - } - - Import-Module $devShellDll - Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath - Set-Location $ReturnToDirectory - return $vsInstall -} - function Assert-MsBuildAvailable { <# .SYNOPSIS Verifies msbuild.exe is on PATH. Exits with error if not found. @@ -119,20 +88,21 @@ function Assert-MsBuildAvailable { function Resolve-BuildEnvironment { <# .SYNOPSIS - Detects or resolves the active build environment and returns metadata. + Detects the active build environment, opens a VS Developer Shell when needed, + and returns metadata about the environment. .DESCRIPTION - When RunMode is 'Auto', checks in priority order: EWDK, NuGet, WDK. - EWDK is checked first because $env:BuildLab is an active, explicit signal - whereas the packages\ folder is a passive disk artifact that may linger. - When RunMode is explicitly set to WDK/NuGet/EWDK, skips detection and uses that mode. - Pass the VsInstallation returned by Initialize-DevShell to avoid prompting the user - a second time when multiple VS installations are present. + Handles the full setup sequence in one place: + 1. Detect mode: EWDK → NuGet → WDK (Auto), or use the explicitly supplied RunMode. + 2. For NuGet / WDK: open a VS Developer Shell if one is not already active, + prompting the user to choose if multiple VS installations with the required + WDK media are found. If the shell is already active, the matching installation + is located via $env:VSINSTALLDIR. + 3. For EWDK: skip VS detection entirely ($env:BuildLab is the authoritative signal). Returns a hashtable: Name, BuildNumber (int), NuGetVersion, WdkVsComponentVersion. #> param( [string]$RepoRoot, - [string]$RunMode = 'Auto', - [object]$VsInstallation = $null + [string]$RunMode = 'Auto' ) $result = @{ @@ -142,63 +112,98 @@ function Resolve-BuildEnvironment { WdkVsComponentVersion = '' } - $effectiveMode = $RunMode + # ------------------------------------------------------------------------- + # Step 1 – Detect / validate build mode + # ------------------------------------------------------------------------- - # --- Resolve build environment --- - if ($effectiveMode -eq 'EWDK' -or - ($effectiveMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { - if ($effectiveMode -eq 'EWDK') { - # Forced EWDK: require BuildLab to be set - if ($env:BuildLab -notmatch '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { - Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised." - exit 1 - } - } - $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" - $result.BuildNumber = [int]$Matches.build - } - elseif ($effectiveMode -eq 'NuGet' -or - ($effectiveMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*"))) { - if ($effectiveMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { - Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." + # EWDK: checked first. $env:BuildLab is an active, explicit signal that + # disappears when you close the EWDK prompt, unlike the packages\ folder. + if ($RunMode -eq 'EWDK' -or + ($RunMode -eq 'Auto' -and $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$')) { + + if ($RunMode -eq 'EWDK' -and + $env:BuildLab -notmatch '^(?[^.]+)\.(?\d+)\.(?[^.]+)$') { + Write-Error "RunMode is 'EWDK' but the EWDK environment variable BuildLab is not set. Ensure the EWDK is mounted and the environment is initialised." exit 1 } - $result.Name = 'NuGet' - $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue - $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value - $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) + # Re-run the match to populate $Matches (the Auto branch already matched above; + # the forced-EWDK branch needs an explicit match after the validation guard). + $null = $env:BuildLab -match '^(?[^.]+)\.(?\d+)\.(?[^.]+)$' + $result.Name = "EWDK.$($Matches.branch).$($Matches.build).$($Matches.qfe)" + $result.BuildNumber = [int]$Matches.build + $result.WdkVsComponentVersion = '(not available for EWDK builds)' + return $result } - elseif ($effectiveMode -eq 'WDK' -or - ($effectiveMode -eq 'Auto' -and $env:UCRTVersion -match '10\.0\.(?\d+)\.0')) { - if ($effectiveMode -eq 'WDK' -and $env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { - 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." + + $isNuGet = ($RunMode -eq 'NuGet') -or + ($RunMode -eq 'Auto' -and (Test-Path "$RepoRoot\packages\*")) + + if ($RunMode -eq 'NuGet' -and -not (Test-Path "$RepoRoot\packages\*")) { + Write-Error "RunMode is 'NuGet' but no packages were found under '$RepoRoot\packages\'. Ensure NuGet restore has been run." + exit 1 + } + + # If not EWDK and not NuGet, assume WDK. VS Dev Shell setup below will validate + # the environment; if no VS with WDK media is found, Select-VsInstallation errors out. + + # ------------------------------------------------------------------------- + # Step 2 – Set up VS Developer Shell + # ------------------------------------------------------------------------- + + $vsInstall = $null + + if (-not $env:VSCMD_VER) { + # Dev Shell not active – open one now. + $vsInstall = Select-VsInstallation (Get-VsInstallationsWithWdk) + $devShellDll = Join-Path $vsInstall.InstallationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll' + if (-not (Test-Path $devShellDll)) { + Write-Error "Visual Studio Developer Shell module not found at '$devShellDll'." exit 1 } - $result.Name = 'WDK' - $result.BuildNumber = [int]$Matches.build + Import-Module $devShellDll + Enter-VsDevShell -VsInstallPath $vsInstall.InstallationPath + Set-Location $RepoRoot } else { - Write-Output "Environment variables {" - Get-ChildItem env:* | Sort-Object Name - Write-Output "Environment variables }" - Write-Error "Could not determine build environment. Ensure EWDK, WDK, or NuGet packages are configured." - exit 1 + Write-Verbose "VS Developer Shell already active (VSCMD_VER=$env:VSCMD_VER)." + # Locate the matching installation via VSINSTALLDIR so we can read its + # WdkVsComponentVersion without prompting the user again. + # Normalize trailing backslash: VSINSTALLDIR ends with '\', vswhere paths do not. + $normalizedVsInstallDir = $env:VSINSTALLDIR.TrimEnd('\') + $vsInstall = Get-VsInstallationsWithWdk | + Where-Object { $_.InstallationPath.TrimEnd('\') -eq $normalizedVsInstallDir } | + Select-Object -First 1 + if (-not $vsInstall) { + 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." + exit 1 + } } - # WDK VS component version (EWDK does not ship this metadata) - if ($result.Name -match '^EWDK') { - $result.WdkVsComponentVersion = '(not available for EWDK builds)' + # ------------------------------------------------------------------------- + # Step 3 – Fill mode-specific fields (Dev Shell is now guaranteed active) + # ------------------------------------------------------------------------- + + if ($isNuGet) { + $result.Name = 'NuGet' + $wdkPackage = Get-ChildItem "$RepoRoot\packages\*WDK.x64*" -Name -ErrorAction SilentlyContinue + $result.NuGetVersion = ([regex]'(?<=x64\.)(\d+\.){3}\d+').Match($wdkPackage).Value + $result.BuildNumber = [int]($result.NuGetVersion.Split('.')[2]) } else { - # Re-use the installation selected during Initialize-DevShell if available, - # otherwise query vswhere again (e.g. when the Dev Shell was already active). - $vsInstall = if ($VsInstallation) { $VsInstallation } else { Select-VsInstallation (Get-VsInstallationsWithWdk) } - if (-not $vsInstall.WdkVsComponentVersion) { - Write-Error "Could not determine WDK component version for '$($vsInstall.DisplayName)'. Ensure the WDK Visual Studio component is installed." + # WDK – Dev Shell is now active, UCRTVersion must be set. + if ($env:UCRTVersion -notmatch '10\.0\.(?\d+)\.0') { + Write-Error "UCRTVersion ('$env:UCRTVersion') is not set or does not match the expected format. Ensure the VS Developer Shell is active." exit 1 } - $result.WdkVsComponentVersion = $vsInstall.WdkVsComponentVersion + $result.Name = 'WDK' + $result.BuildNumber = [int]$Matches.build + } + + if (-not $vsInstall.WdkVsComponentVersion) { + Write-Error "Could not determine WDK component version for '$($vsInstall.DisplayName)'. Ensure the WDK Visual Studio component is installed." + exit 1 } + $result.WdkVsComponentVersion = $vsInstall.WdkVsComponentVersion return $result } From 084c783c5f4ae563520b4446e4b3eeae72b8d9e8 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Fri, 3 Apr 2026 13:06:55 -0700 Subject: [PATCH 08/11] fix: use Write-Host for VS selection menu so it displays to console Write-Output goes to stdout and gets captured by the pipeline, making the menu invisible. Write-Host writes directly to the console, ensuring users see the numbered options. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- BuildEnvironment.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index c4e3cffaa..211b25a49 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -52,12 +52,12 @@ function Select-VsInstallation { } # Multiple installations — let the user choose - Write-Output "" - Write-Output "The following Visual Studio installations were found with the required WDK media:" + Write-Host "" + Write-Host "The following Visual Studio installations were found with the required WDK media:" for ($i = 0; $i -lt $Installations.Count; $i++) { - Write-Output " [$($i + 1)] $($Installations[$i].DisplayName) — $($Installations[$i].InstallationPath)" + Write-Host " [$($i + 1)] $($Installations[$i].DisplayName) — $($Installations[$i].InstallationPath)" } - Write-Output "" + Write-Host "" do { $choice = Read-Host "Select the installation to use [1-$($Installations.Count)]" From 05135c4ec0a93d50e84deaf6a758ca956b1cb6eb Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 7 Apr 2026 14:48:52 -0700 Subject: [PATCH 09/11] fix: include Build Tools in vswhere product search vswhere's default product filter only includes Community, Professional, and Enterprise editions. Build Tools (Microsoft.VisualStudio.Product.BuildTools) requires '-products *' to be discovered. Without this, machines with only VS Build Tools installed would fail detection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- BuildEnvironment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index 211b25a49..d05456c34 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -18,7 +18,7 @@ function Get-VsInstallationsWithWdk { exit 1 } - $json = & $vswhere -all -format json -requires Microsoft.Windows.DriverKit -include packages 2>$null + $json = & $vswhere -all -products * -format json -requires Microsoft.Windows.DriverKit -include packages 2>$null $installations = $json | ConvertFrom-Json return $installations | ForEach-Object { $wdkPackage = $_.packages | Where-Object { $_.id -eq 'Microsoft.Windows.DriverKit' } | Select-Object -First 1 From 6b271bfe71e1994f9103e5988cf3ee1db0b7d0e6 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 7 Apr 2026 14:55:32 -0700 Subject: [PATCH 10/11] fix: detect WDK component in both full VS and Build Tools Full VS editions register the WDK as 'Microsoft.Windows.DriverKit', while Build Tools uses 'Component.Microsoft.Windows.DriverKit.BuildTools'. Query vswhere for both component IDs and deduplicate by install path so machines with only VS Build Tools are properly detected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- BuildEnvironment.ps1 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/BuildEnvironment.ps1 b/BuildEnvironment.ps1 index d05456c34..bdb90a2eb 100644 --- a/BuildEnvironment.ps1 +++ b/BuildEnvironment.ps1 @@ -18,10 +18,21 @@ function Get-VsInstallationsWithWdk { exit 1 } - $json = & $vswhere -all -products * -format json -requires Microsoft.Windows.DriverKit -include packages 2>$null - $installations = $json | ConvertFrom-Json + # Full VS editions install the WDK component as 'Microsoft.Windows.DriverKit', + # while Build Tools uses 'Component.Microsoft.Windows.DriverKit.BuildTools'. + # Query for either so both product types are discovered. + $wdkComponentIds = @('Microsoft.Windows.DriverKit', 'Component.Microsoft.Windows.DriverKit.BuildTools') + $allInstallations = @() + foreach ($componentId in $wdkComponentIds) { + $json = & $vswhere -all -products * -format json -requires $componentId -include packages 2>$null + if ($json) { + $allInstallations += ($json | ConvertFrom-Json) + } + } + # Deduplicate by installationPath in case both components are present + $installations = $allInstallations | Sort-Object -Property installationPath -Unique return $installations | ForEach-Object { - $wdkPackage = $_.packages | Where-Object { $_.id -eq 'Microsoft.Windows.DriverKit' } | Select-Object -First 1 + $wdkPackage = $_.packages | Where-Object { $_.id -in $wdkComponentIds } | Select-Object -First 1 [PSCustomObject]@{ DisplayName = $_.displayName InstallationPath = $_.installationPath From 3a10b3c1ed4a2e611311e9ea3e931fc65fe1a9e8 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Tue, 7 Apr 2026 15:29:58 -0700 Subject: [PATCH 11/11] docs: streamline Building-Locally.md and fix typos Simplify prerequisites with environment-specific requisites section, remove redundant build commands per WDK type since the script auto-detects. Fix typo (Enviroment), casing (powershell), formatting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Building-Locally.md | 44 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/Building-Locally.md b/Building-Locally.md index 290d59545..9d683a86a 100644 --- a/Building-Locally.md +++ b/Building-Locally.md @@ -22,57 +22,29 @@ git clone --recurse-submodules "https://github.com/microsoft/Windows-driver-samp cd ".\Windows-driver-samples" ``` -Use `main` for in-market WDK releases, `develop` for WDK Preview / EEAP builds: +### Environment specific requisites -```powershell -git checkout main # stable / in-market -git checkout develop # preview / EEAP -``` - ---- - -## Building the Samples - -The script auto-detects which WDK environment is active. Use `-RunMode` to force a specific one if needed: - -```powershell -.\Build-Samples.ps1 -RunMode NuGet # force NuGet -.\Build-Samples.ps1 -RunMode EWDK # force EWDK -.\Build-Samples.ps1 -RunMode WDK # force WDK -``` - -### NuGet Package - -Install NuGet if you don't have it: +- If you are using the WDK via **NuGet**: install NuGet and restore the packages: ```powershell winget install --id Microsoft.NuGet --source winget -``` - -Restore the WDK packages and build from Powershell: - -```powershell nuget restore -PackagesDirectory ".\packages" -pwsh -.\Build-Samples.ps1 ``` -### EWDK - -Mount the EWDK ISO, open a terminal in the mounted drive, launch the build environment, then build: +- If you are using the WDK via **EWDK**: mount the EWDK ISO, open a terminal in the mounted drive, and launch the build environment: ```powershell .\LaunchBuildEnv -pwsh -.\Build-Samples.ps1 ``` -### WDK MSI/winget -Build the samples from PowerShell: +--- + +## Building the Samples + +The `Build-Samples.ps1` script auto-detects which WDK environment is active and will build all the samples with all the configurations by default. Just run the following command from **PowerShell**: ```powershell -pwsh .\Build-Samples.ps1 ```