summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author5an7y-Microsoft <[email protected]>2026-07-02 13:27:26 -0700
committerGitHub <[email protected]>2026-07-02 13:27:26 -0700
commit2ee527bfeb0aeb6be11f0a8b6dce4011b358ce89 (patch)
tree22403b0092c85976352e311322abc835c7eb2f09
parent1439bff8606882f4c69cf8b21230b5ee7e5e4eee (diff)
parentd157348f0566d3da5bf00a3f1e414f95443b3a59 (diff)
Merge pull request #1397 from 5an7y-Microsoft/mainHEADmain
Expose the NT_TARGET_VERSION in the build samples script & workflow update
-rw-r--r--.github/scripts/Build-ChangedSamples.ps12
-rw-r--r--.github/scripts/Join-CsvReports.ps1220
-rw-r--r--.github/workflows/ci-pr.yml33
-rw-r--r--.github/workflows/ci.yml35
-rw-r--r--Build-Samples.ps1143
-rw-r--r--Building-Locally.md46
-rw-r--r--Get-NtTargetVersions.ps1100
-rw-r--r--exclusions.csv32
8 files changed, 555 insertions, 56 deletions
diff --git a/.github/scripts/Build-ChangedSamples.ps1 b/.github/scripts/Build-ChangedSamples.ps1
index 3e91eb88..f03f93f3 100644
--- a/.github/scripts/Build-ChangedSamples.ps1
+++ b/.github/scripts/Build-ChangedSamples.ps1
@@ -22,7 +22,7 @@ foreach ($file in $ChangedFiles) {
$filename = Split-Path $file -Leaf
# Files that can affect how every sample is built should trigger a full build
- if ($filename -eq "Build-Samples.ps1" -or $filename -eq "exclusions.csv" -or $filename -eq "Directory.Build.props" -or $filename -eq "packages.config") {
+ if ($filename -eq "Build-Samples.ps1" -or $filename -eq "Get-NtTargetVersions.ps1" -or $filename -eq "exclusions.csv" -or $filename -eq "Directory.Build.props" -or $filename -eq "packages.config") {
$buildAll = $true
}
if ($dir -like "$root\.github\scripts" -or $dir -like "$root\.github\scripts\*") {
diff --git a/.github/scripts/Join-CsvReports.ps1 b/.github/scripts/Join-CsvReports.ps1
index 32cd9d63..43e0c826 100644
--- a/.github/scripts/Join-CsvReports.ps1
+++ b/.github/scripts/Join-CsvReports.ps1
@@ -1,35 +1,201 @@
-$logsPath = Join-Path (Get-Location).Path "_logs"
+<#
+.SYNOPSIS
+ Joins the per-job Build-Samples CSV reports (one per _NT_TARGET_VERSION x configuration x
+ platform) into a single overview, and writes an easy-to-scan summary to the GitHub Actions
+ run page ($GITHUB_STEP_SUMMARY).
+
+.DESCRIPTION
+ Each build job uploads a "_logs" folder containing a report named
+ _overview.<ntTag>.<configuration>.<platform>.csv
+ with columns: Sample, <Configuration|Platform> (one combination per file). This script:
+ * parses the _NT_TARGET_VERSION tag and combination from every report,
+ * collapses each sample's combinations into one status per version,
+ * writes _overview.all.csv / _overview.all.htm (a colour-coded sample x version matrix), and
+ * appends a Markdown summary (per-version totals + a failures table) to $GITHUB_STEP_SUMMARY
+ so failures are obvious from the run page without opening any logs.
+
+ The older 2-part name (_overview.<configuration>.<platform>.csv, no version) is still
+ understood and bucketed under the "latest" column.
+#>
+
+$logsPath = Join-Path (Get-Location).Path "_logs"
$reportFileName = '_overview.all'
-$idProperty = 'Sample'
-$results = $null
-Get-ChildItem -Path $logsPath -Filter '*.csv' | ForEach-Object {
- $csv = Import-Csv -Path $_
- if ($results -eq $null) {
- $results = $csv
+if (-not (Test-Path $logsPath)) {
+ Write-Warning "No _logs directory found at $logsPath - nothing to report."
+ return
+}
+
+# --- Load every per-job CSV ---------------------------------------------------
+# data[sample][tag][combo] = status
+$data = @{}
+$allSamples = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+$tagSet = [System.Collections.Generic.HashSet[string]]::new()
+
+Get-ChildItem -Path $logsPath -Filter '_overview.*.csv' |
+ Where-Object { $_.Name -notlike '_overview.all.*' } |
+ ForEach-Object {
+ # _overview.<tag>.<config>.<platform> -> drop _overview; last two are config/platform.
+ $parts = [IO.Path]::GetFileNameWithoutExtension($_.Name).Split('.')
+ $parts = $parts[1..($parts.Count - 1)] # drop the leading "_overview"
+ if ($parts.Count -ge 3) { $tag = ($parts[0..($parts.Count - 3)] -join '.') }
+ else { $tag = 'latest' }
+ [void]$tagSet.Add($tag)
+
+ Import-Csv -Path $_.FullName | ForEach-Object {
+ $sample = $_.Sample
+ if (-not $sample) { return }
+ [void]$allSamples.Add($sample)
+ if (-not $data.ContainsKey($sample)) { $data[$sample] = @{} }
+ if (-not $data[$sample].ContainsKey($tag)) { $data[$sample][$tag] = @{} }
+ foreach ($col in ($_.PSObject.Properties.Name | Where-Object { $_ -ne 'Sample' })) {
+ $data[$sample][$tag][$col] = "$($_.$col)".Trim()
+ }
+ }
}
- else {
- $results = $csv | ForEach-Object {
- $id = $_.$idProperty
- $match = $results | Where-Object { $_.$idProperty -eq $id }
- if ($match) {
- $properties = $_ | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -ne $idProperty } | Select-Object -ExpandProperty Name
- $newObject = New-Object PSObject
- # Add ID property separately to ensure it appears first
- $newObject | Add-Member -MemberType NoteProperty -Name $idProperty -Value $_.$idProperty
- foreach ($property in $properties) {
- $newObject | Add-Member -MemberType NoteProperty -Name $property -Value $_.$property
- }
- foreach ($property in ($match | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -ne $idProperty } | Select-Object -ExpandProperty Name)) {
- if ($properties -notcontains $property) {
- $newObject | Add-Member -MemberType NoteProperty -Name $property -Value $match.$property
- }
- }
- $newObject
+
+if ($tagSet.Count -eq 0) {
+ Write-Warning "No per-job '_overview.*.csv' reports were found in $logsPath."
+ return
+}
+
+# Order versions newest-first (numeric tags descending; non-numeric last)
+$tags = $tagSet | Sort-Object @{ Expression = { if ($_ -match '^\d+$') { [int]$_ } else { 0 } }; Descending = $true }, @{ Expression = { $_ } }
+
+function Get-Status {
+ # Collapse one sample/version's combinations into a single status object.
+ param([hashtable]$Combos)
+ $c = @{ Succeeded = 0; Failed = 0; Sporadic = 0; Unsupported = 0; Excluded = 0 }
+ $details = @()
+ if ($Combos) {
+ foreach ($k in ($Combos.Keys | Sort-Object)) {
+ switch ($Combos[$k]) {
+ 'Succeeded' { $c.Succeeded++ } 'Failed' { $c.Failed++ } 'Sporadic' { $c.Sporadic++ }
+ 'Unsupported' { $c.Unsupported++ } 'Excluded' { $c.Excluded++ }
+ }
+ $details += "$k = $($Combos[$k])"
+ }
+ }
+ $buildable = $c.Succeeded + $c.Failed + $c.Sporadic
+ if (-not $Combos -or $Combos.Count -eq 0) { $label = 'n/a'; $klass = 'na' }
+ elseif ($buildable -eq 0) { $label = '--'; $klass = 'na' }
+ elseif ($c.Failed -eq 0 -and $c.Sporadic -eq 0) { $label = "PASS ($($c.Succeeded)/$buildable)"; $klass = 'pass' }
+ elseif ($c.Failed -eq 0) { $label = "PASS* ($($c.Succeeded + $c.Sporadic)/$buildable)"; $klass = 'flaky' }
+ elseif ($c.Failed -eq $buildable) { $label = "FAIL ($($c.Failed)/$buildable)"; $klass = 'fail' }
+ else { $label = "PARTIAL ($($c.Failed) failed / $buildable)"; $klass = 'partial' }
+ [pscustomobject]@{ Label = $label; Class = $klass; Tooltip = ($details -join ' | '); Counts = $c; Buildable = $buildable }
+}
+
+# --- Build per-version totals + the matrix ------------------------------------
+$totals = @{}; foreach ($t in $tags) { $totals[$t] = [pscustomobject]@{ S = 0; F = 0; O = 0; U = 0; E = 0; pass = 0; flaky = 0; partial = 0; fail = 0; na = 0 } }
+$failuresList = [System.Collections.ArrayList]::new()
+$csvRows = @()
+$bodyRows = New-Object System.Text.StringBuilder
+
+foreach ($sample in $allSamples) {
+ $csvRow = [ordered]@{ Sample = $sample }
+ $cells = ''
+ foreach ($t in $tags) {
+ $combos = $null
+ if ($data[$sample].ContainsKey($t)) { $combos = $data[$sample][$t] }
+ $st = Get-Status -Combos $combos
+ $tt = $totals[$t]
+ $tt.S += $st.Counts.Succeeded; $tt.F += $st.Counts.Failed; $tt.O += $st.Counts.Sporadic
+ $tt.U += $st.Counts.Unsupported; $tt.E += $st.Counts.Excluded
+ switch ($st.Class) { 'pass' { $tt.pass++ } 'flaky' { $tt.flaky++ } 'partial' { $tt.partial++ } 'fail' { $tt.fail++ } 'na' { $tt.na++ } }
+ $csvRow["$t"] = $st.Label
+ $tip = [System.Web.HttpUtility]::HtmlEncode($st.Tooltip)
+ $cells += "<td class='$($st.Class)' title='$tip'>$($st.Label)</td>"
+ if ($combos) {
+ foreach ($k in ($combos.Keys | Sort-Object)) {
+ if ($combos[$k] -eq 'Failed') { [void]$failuresList.Add([pscustomobject]@{ Sample = $sample; Version = $t; Combo = $k }) }
}
}
}
+ $csvRows += [pscustomobject]$csvRow
+ $enc = [System.Web.HttpUtility]::HtmlEncode($sample)
+ [void]$bodyRows.Append("<tr><td class='sample'>$enc</td>$cells</tr>`n")
}
-$results | ConvertTo-Csv | Out-File (Join-Path $logsPath "$reportFileName.csv")
-$results | ConvertTo-Html -Title "Overview" | Out-File (Join-Path $logsPath "$reportFileName.htm")
+Add-Type -AssemblyName System.Web -ErrorAction SilentlyContinue
+
+# --- CSV ----------------------------------------------------------------------
+$csvRows | Export-Csv -Path (Join-Path $logsPath "$reportFileName.csv") -NoTypeInformation
+
+# --- HTML (colour-coded sample x version matrix) ------------------------------
+$generated = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
+$sumHead = "<tr><th>_NT_TARGET_VERSION</th><th>Pass</th><th>Flaky</th><th>Partial</th><th>Fail</th><th>n/a</th><th>Combos OK</th><th>Sporadic</th><th>Failed</th><th>Excluded</th><th>Pass rate</th></tr>"
+$sumRows = ''
+foreach ($t in $tags) {
+ $x = $totals[$t]; $tot = $x.pass + $x.flaky + $x.partial + $x.fail + $x.na; $elig = $tot - $x.na
+ $rate = if ($elig -gt 0) { '{0:N0}%' -f (100.0 * ($x.pass + $x.flaky) / $elig) } else { 'n/a' }
+ $sumRows += "<tr><td class='sample'>$t</td><td class='pass'>$($x.pass)</td><td class='flaky'>$($x.flaky)</td><td class='partial'>$($x.partial)</td><td class='fail'>$($x.fail)</td><td class='na'>$($x.na)</td><td>$($x.S)</td><td>$($x.O)</td><td>$($x.F)</td><td>$($x.E)</td><td><b>$rate</b></td></tr>`n"
+}
+$matHead = "<tr><th class='sample'>Sample</th>"
+foreach ($t in $tags) { $matHead += "<th>$t</th>" }
+$matHead += "</tr>"
+
+$html = @"
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/>
+<title>WDK Driver Samples - Build Overview</title>
+<style>
+ body{font-family:'Segoe UI',Arial,sans-serif;margin:24px;color:#1b1b1b}
+ h1{font-size:22px;margin-bottom:4px}h2{font-size:17px;margin-top:28px}
+ .meta{color:#555;font-size:13px;margin-bottom:8px}
+ table{border-collapse:collapse;margin-top:8px;font-size:13px}
+ th,td{border:1px solid #cfcfcf;padding:5px 9px;text-align:center}
+ th{background:#f0f3f7;position:sticky;top:0}
+ td.sample,th.sample{text-align:left;font-family:Consolas,monospace;white-space:nowrap}
+ .sub{font-weight:normal;color:#666;font-size:11px}
+ .pass{background:#c8e6c9}.flaky{background:#fff59d}.partial{background:#ffcc80}.fail{background:#ef9a9a}.na{background:#eee;color:#888}
+ .legend span{display:inline-block;padding:3px 9px;margin-right:6px;border:1px solid #cfcfcf;border-radius:3px;font-size:12px}
+</style></head><body>
+<h1>WDK Driver Samples &mdash; Build Overview</h1>
+<div class="meta">Generated: $generated &nbsp;|&nbsp; columns are <b>_NT_TARGET_VERSION</b> (library link version); hover a cell for the per-combination breakdown.</div>
+<div class="legend"><span class="pass">PASS</span><span class="flaky">PASS* (retry)</span><span class="partial">PARTIAL</span><span class="fail">FAIL</span><span class="na">-- n/a</span></div>
+<h2>Summary by _NT_TARGET_VERSION</h2>
+<table>$sumHead
+$sumRows</table>
+<h2>Sample &times; _NT_TARGET_VERSION</h2>
+<table>$matHead
+$($bodyRows.ToString())</table>
+</body></html>
+"@
+$html | Out-File -FilePath (Join-Path $logsPath "$reportFileName.htm") -Encoding UTF8
+
+# --- GitHub Actions run summary (Markdown) ------------------------------------
+if ($env:GITHUB_STEP_SUMMARY) {
+ $totalFailed = ($totals.Values | Measure-Object -Property fail -Sum).Sum + ($totals.Values | Measure-Object -Property partial -Sum).Sum
+ $icon = if ($failuresList.Count -gt 0) { ':x:' } else { ':white_check_mark:' }
+
+ $md = [System.Text.StringBuilder]::new()
+ [void]$md.AppendLine("# $icon WDK Driver Samples &mdash; Build Overview")
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("Columns are **_NT_TARGET_VERSION** (the WDK library version drivers link against). Each version was built for Debug/Release x x64/arm64.")
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("## Summary by _NT_TARGET_VERSION")
+ [void]$md.AppendLine("| _NT_TARGET_VERSION | :white_check_mark: Pass | :warning: Flaky | :large_orange_diamond: Partial | :x: Fail | :heavy_minus_sign: n/a | Pass rate |")
+ [void]$md.AppendLine("|---|---:|---:|---:|---:|---:|---:|")
+ foreach ($t in $tags) {
+ $x = $totals[$t]; $tot = $x.pass + $x.flaky + $x.partial + $x.fail + $x.na; $elig = $tot - $x.na
+ $rate = if ($elig -gt 0) { '{0:N0}%' -f (100.0 * ($x.pass + $x.flaky) / $elig) } else { 'n/a' }
+ [void]$md.AppendLine("| ``$t`` | $($x.pass) | $($x.flaky) | $($x.partial) | $($x.fail) | $($x.na) | **$rate** |")
+ }
+ [void]$md.AppendLine()
+
+ if ($failuresList.Count -gt 0) {
+ [void]$md.AppendLine("## :x: Failures ($($failuresList.Count))")
+ [void]$md.AppendLine("| Sample | _NT_TARGET_VERSION | Config/Platform |")
+ [void]$md.AppendLine("|---|---|---|")
+ foreach ($f in ($failuresList | Sort-Object Sample, Version, Combo)) {
+ [void]$md.AppendLine("| ``$($f.Sample)`` | $($f.Version) | $($f.Combo.Replace('|','/')) |")
+ }
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("> Open the matching **build** job's summary (or the ``logs-*`` artifact) for the exact compiler error.")
+ }
+ else {
+ [void]$md.AppendLine(":tada: **All combinations built successfully.**")
+ }
+
+ $md.ToString() | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
+}
diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml
index 6719270e..8119c94c 100644
--- a/.github/workflows/ci-pr.yml
+++ b/.github/workflows/ci-pr.yml
@@ -8,13 +8,39 @@ on:
- '**.md'
- 'LICENSE'
jobs:
+ # Auto-discover the available _NT_TARGET_VERSION values from the active WDK so the build
+ # matrix never needs a hand-maintained version list. Change -Newest to build more/fewer.
+ discover:
+ name: discover _NT_TARGET_VERSIONs
+ runs-on: windows-2025-vs2026
+ outputs:
+ versions: ${{ steps.nt.outputs.versions }}
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v4
+
+ - name: Install Nuget Packages
+ run: nuget restore .\packages.config -PackagesDirectory .\packages\
+
+ - name: Discover the newest _NT_TARGET_VERSION values
+ id: nt
+ shell: pwsh
+ run: |
+ $json = .\Get-NtTargetVersions.ps1 -Newest 4 -AsMatrixJson
+ "versions=$json" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
+ Write-Host "Discovered _NT_TARGET_VERSION matrix: $json"
+
build:
- name: Build driver samples
+ name: build ${{ matrix.nt.tag }} ${{ matrix.configuration }} ${{ matrix.platform }}
+ needs: discover
strategy:
fail-fast: false
matrix:
configuration: [Debug, Release]
platform: [x64, arm64]
+ # _NT_TARGET_VERSION values are auto-discovered by the 'discover' job from the active
+ # WDK, so there is no version list to maintain here.
+ nt: ${{ fromJSON(needs.discover.outputs.versions) }}
runs-on: windows-2025-vs2026
steps:
- name: Check out repository code
@@ -38,13 +64,14 @@ jobs:
env:
WDS_Configuration: ${{ matrix.configuration }}
WDS_Platform: ${{ matrix.platform }}
- WDS_ReportFileName: _overview.${{ matrix.configuration }}.${{ matrix.platform }}
+ WDS_NtTargetVersion: ${{ matrix.nt.version }}
+ WDS_ReportFileName: _overview.${{ matrix.nt.tag }}.${{ matrix.configuration }}.${{ matrix.platform }}
- name: Archive build logs and overview build reports
uses: actions/upload-artifact@v4
if: always()
with:
- name: logs-${{ matrix.configuration }}-${{ matrix.platform }}
+ name: logs-${{ matrix.nt.tag }}-${{ matrix.configuration }}-${{ matrix.platform }}
path: _logs
report:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ca2696d8..4b263a82 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,7 @@
name: Build all driver samples
on:
+ # Allows the workflow to be triggered manually from the Actions tab ("Run workflow").
+ workflow_dispatch:
push:
branches:
- main
@@ -11,13 +13,39 @@ on:
# Runs every Saturday at 00:00 PST (08:00 UTC)
- cron: '0 8 * * 6'
jobs:
+ # Auto-discover the available _NT_TARGET_VERSION values from the active WDK so the build
+ # matrix never needs a hand-maintained version list. Change -Newest to build more/fewer.
+ discover:
+ name: discover _NT_TARGET_VERSIONs
+ runs-on: windows-2025-vs2026
+ outputs:
+ versions: ${{ steps.nt.outputs.versions }}
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v4
+
+ - name: Install Nuget Packages
+ run: nuget restore .\packages.config -PackagesDirectory .\packages\
+
+ - name: Discover the newest _NT_TARGET_VERSION values
+ id: nt
+ shell: pwsh
+ run: |
+ $json = .\Get-NtTargetVersions.ps1 -Newest 4 -AsMatrixJson
+ "versions=$json" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
+ Write-Host "Discovered _NT_TARGET_VERSION matrix: $json"
+
build:
- name: Build driver samples
+ name: build ${{ matrix.nt.tag }} ${{ matrix.configuration }} ${{ matrix.platform }}
+ needs: discover
strategy:
fail-fast: false
matrix:
configuration: [Debug, Release]
platform: [x64, arm64]
+ # _NT_TARGET_VERSION values are auto-discovered by the 'discover' job from the active
+ # WDK, so there is no version list to maintain here.
+ nt: ${{ fromJSON(needs.discover.outputs.versions) }}
runs-on: windows-2025-vs2026
steps:
- name: Check out repository code
@@ -33,13 +61,14 @@ jobs:
env:
WDS_Configuration: ${{ matrix.configuration }}
WDS_Platform: ${{ matrix.platform }}
- WDS_ReportFileName: _overview.${{ matrix.configuration }}.${{ matrix.platform }}
+ WDS_NtTargetVersion: ${{ matrix.nt.version }}
+ WDS_ReportFileName: _overview.${{ matrix.nt.tag }}.${{ matrix.configuration }}.${{ matrix.platform }}
- name: Archive build logs and overview build reports
uses: actions/upload-artifact@v4
if: always()
with:
- name: logs-${{ matrix.configuration }}-${{ matrix.platform }}
+ name: logs-${{ matrix.nt.tag }}-${{ matrix.configuration }}-${{ matrix.platform }}
path: _logs
report:
diff --git a/Build-Samples.ps1 b/Build-Samples.ps1
index a73a2afa..e0b1a6b3 100644
--- a/Build-Samples.ps1
+++ b/Build-Samples.ps1
@@ -27,6 +27,14 @@
.PARAMETER Platforms
Build platforms (e.g. 'x64','arm64'). Defaults to $env:WDS_Platform or ('x64','arm64').
+.PARAMETER NtTargetVersion
+ The _NT_TARGET_VERSION value - the WDK library version the driver links against
+ ("OS version of libraries"). Accepts the Windows build-number form '10.0.<build>' or the
+ short '<build>' tag (e.g. '10.0.28000' or '28000'). The valid values are auto-discovered
+ from the active WDK's DriverGeneral.xml rule (see Get-NtTargetVersions.ps1), so a new WDK
+ version is picked up with no script change. Defaults to $env:WDS_NtTargetVersion, or the
+ latest discovered version when unset.
+
.PARAMETER LogFilesDirectory
Directory for build log files. Defaults to _logs in the current directory.
@@ -69,6 +77,11 @@
.\Build-Samples -RunMode WDK
Forces WDK mode regardless of environment variables.
+
+.EXAMPLE
+ .\Build-Samples -NtTargetVersion 10.0.22000
+
+ Builds all samples linking against the 10.0.22000 library set instead of the latest.
#>
#Requires -Version 7.0
@@ -78,6 +91,9 @@ param(
[string[]]$Samples,
[string[]]$Configurations = @(if ([string]::IsNullOrEmpty($env:WDS_Configuration)) { ('Debug', 'Release') } else { $env:WDS_Configuration }),
[string[]]$Platforms = @(if ([string]::IsNullOrEmpty($env:WDS_Platform)) { ('x64', 'arm64') } else { $env:WDS_Platform }),
+ # _NT_TARGET_VERSION = the WDK library version the driver links against. Valid values are
+ # auto-discovered from the WDK (Get-NtTargetVersions.ps1); empty = the latest discovered.
+ [string]$NtTargetVersion = $env:WDS_NtTargetVersion,
[string]$LogFilesDirectory = (Join-Path (Get-Location) "_logs"),
[string]$ReportFileName = $(if ([string]::IsNullOrEmpty($env:WDS_ReportFileName)) { "_overview" } else { $env:WDS_ReportFileName }),
[string]$InfOptions,
@@ -102,15 +118,20 @@ function Import-SampleExclusions {
- Configurations: semicolon-separated config|platform patterns (or '*' for all)
- Reason: human-readable explanation
- Only exclusions whose [MinBuild, MaxBuild] range includes the given build number
- are returned. Exclusions outside the range are silently skipped.
+ A row is only returned when ALL of the following match the current build:
+ - its [MinBuild, MaxBuild] range includes the given build number, and
+ - its [MinNtTargetVersion, MaxNtTargetVersion] range includes the current
+ _NT_TARGET_VERSION build number (e.g. 22000 parsed from '10.0.22000').
+ Rows outside either range are skipped; blank range bounds mean unbounded.
.NOTES
- CSV format: Path,Configurations,MinBuild,MaxBuild,Reason
- Example row: network\wlan\wdi,*,,27100,"failure introduced in VS17.14"
+ CSV format: Path,Configurations,MinBuild,MaxBuild,MinNtTargetVersion,MaxNtTargetVersion,Reason
+ Example row: network\wlan\wdi,*,26100,,,,"failure introduced in VS17.14"
+ NT-version-specific: somepath,*,,,,22621,"needs an API newer than the 10.0.22621 library"
#>
param(
[string]$CsvPath,
- [int]$BuildNumber
+ [int]$BuildNumber,
+ [string]$NtTargetVersion
)
if (-not (Test-Path $CsvPath)) {
@@ -118,20 +139,38 @@ function Import-SampleExclusions {
return @()
}
+ # The _NT_TARGET_VERSION param is the friendly build-number form (e.g. '10.0.22000');
+ # take its last dotted component for numeric range comparisons. An empty value (or
+ # 'latest') means the newest libraries, so no NT-version-scoped row applies.
+ $ntBuild = if ([string]::IsNullOrWhiteSpace($NtTargetVersion) -or $NtTargetVersion -eq 'latest') {
+ [int]::MaxValue
+ }
+ else {
+ [int]($NtTargetVersion -replace '.*\.', '')
+ }
+
$exclusions = [System.Collections.ArrayList]::new()
Import-Csv $CsvPath | ForEach-Object {
$pattern = $_.Path.Trim('\').Replace('\', '.').ToLower()
$configs = if ([string]::IsNullOrWhiteSpace($_.Configurations)) { '*' } else { $_.Configurations }
$minBuild = if ([string]::IsNullOrWhiteSpace($_.MinBuild)) { 0 } else { [int]$_.MinBuild }
$maxBuild = if ([string]::IsNullOrWhiteSpace($_.MaxBuild)) { 99999 } else { [int]$_.MaxBuild }
+ # Min/MaxNtTargetVersion columns are optional; blank or missing means "all NT versions".
+ $minNt = if ([string]::IsNullOrWhiteSpace($_.MinNtTargetVersion)) { 0 } else { [int]$_.MinNtTargetVersion }
+ $maxNt = if ([string]::IsNullOrWhiteSpace($_.MaxNtTargetVersion)) { [int]::MaxValue } else { [int]$_.MaxNtTargetVersion }
- if ($minBuild -le $BuildNumber -and $BuildNumber -le $maxBuild) {
+ # _NT_TARGET_VERSION is constant for the whole run, so (like the build number) filter
+ # these rows out here at load time.
+ if ($ntBuild -lt $minNt -or $ntBuild -gt $maxNt) {
+ Write-Verbose "Exclusion skipped: '$pattern' - _NT_TARGET_VERSION $ntBuild outside [$minNt, $maxNt]"
+ }
+ elseif ($minBuild -le $BuildNumber -and $BuildNumber -le $maxBuild) {
[void]$exclusions.Add([PSCustomObject]@{
Pattern = $pattern
Configurations = $configs
Reason = $_.Reason
})
- Write-Verbose "Exclusion applied: '$pattern' configs='$configs' reason='$($_.Reason)'"
+ Write-Verbose "Exclusion applied: '$pattern' configs='$configs' ntRange=[$minNt,$maxNt] reason='$($_.Reason)'"
}
else {
Write-Verbose "Exclusion skipped: '$pattern' - build $BuildNumber outside [$minBuild, $maxBuild]"
@@ -172,6 +211,7 @@ function Build-SingleSample {
[string]$SampleName,
[string]$Configuration = 'Debug',
[string]$Platform = 'x64',
+ [string]$NtTargetVersionCode,
[string]$InfVerif_AdditionalOptions = '/samples',
[string]$LogFilesDirectory = (Get-Location),
[bool]$Verbose = $false
@@ -249,6 +289,7 @@ function Build-SingleSample {
-property:Configuration=$Configuration `
-property:Platform=$Platform `
-p:TargetVersion=Windows10 `
+ -p:_NT_TARGET_VERSION=$NtTargetVersionCode `
-p:InfVerif_AdditionalOptions="$InfVerif_AdditionalOptions" `
-warnaserror `
-binaryLogger:LogFile=$binLogFilePath`;ProjectImports=None `
@@ -393,10 +434,36 @@ else {
}
# =============================================================================
+# Step 5b - Resolve _NT_TARGET_VERSION
+# =============================================================================
+#
+# _NT_TARGET_VERSION selects the WDK library version the driver links against. The valid
+# values (and their NTDDI codes) are auto-discovered from the active WDK by
+# Get-NtTargetVersions.ps1, so nothing here needs updating when a new WDK version ships.
+# msbuild takes the NTDDI code.
+$ntVersions = & (Join-Path $PSScriptRoot 'Get-NtTargetVersions.ps1')
+if (-not $ntVersions) {
+ Write-Error "Could not discover any _NT_TARGET_VERSION values from the active WDK."
+ exit 1
+}
+if ([string]::IsNullOrWhiteSpace($NtTargetVersion) -or $NtTargetVersion -eq 'latest') {
+ $ntSelected = $ntVersions[0] # newest
+}
+else {
+ $ntSelected = $ntVersions | Where-Object { $_.Version -eq $NtTargetVersion -or $_.Tag -eq $NtTargetVersion } | Select-Object -First 1
+ if (-not $ntSelected) {
+ Write-Error "Invalid -NtTargetVersion '$NtTargetVersion'. Valid values: $(($ntVersions.Version) -join ', ')"
+ exit 1
+ }
+}
+$NtTargetVersion = $ntSelected.Version
+$ntTargetVersionCode = $ntSelected.Code
+
+# =============================================================================
# Step 6 - Load Exclusions
# =============================================================================
-$exclusions = Import-SampleExclusions -CsvPath (Join-Path $root 'exclusions.csv') -BuildNumber $buildNumber
+$exclusions = Import-SampleExclusions -CsvPath (Join-Path $root 'exclusions.csv') -BuildNumber $buildNumber -NtTargetVersion $NtTargetVersion
# =============================================================================
# Step 7 - Print Build Plan
@@ -417,6 +484,7 @@ Write-Output ""
Write-Output " Samples: $($sampleSet.Count) ($skippedCount skipped)"
Write-Output " Configurations: $($Configurations -join ', ')"
Write-Output " Platforms: $($Platforms -join ', ')"
+Write-Output " NT Target Ver: $NtTargetVersion ($ntTargetVersionCode)"
Write-Output " Combinations: $combinationsTotal"
Write-Output " Exclusions: $($exclusions.Count)"
Write-Output ""
@@ -463,6 +531,7 @@ $sampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Paral
$configs = $using:Configurations
$platforms = $using:Platforms
$infOpts = $using:infVerifOptions
+ $ntCode = $using:ntTargetVersionCode
$isVerbose = $using:verbose
$state = $using:buildState
$total = $using:combinationsTotal
@@ -513,7 +582,8 @@ $sampleSet.GetEnumerator() | ForEach-Object -ThrottleLimit $ThrottleLimit -Paral
$buildResult = Build-SingleSample `
-Directory $directory -SampleName $sampleName `
-LogFilesDirectory $logDir -Configuration $configuration `
- -Platform $platform -InfVerif_AdditionalOptions $infOpts `
+ -Platform $platform -NtTargetVersionCode $ntCode `
+ -InfVerif_AdditionalOptions $infOpts `
-Verbose:$isVerbose
# Return codes from Build-SingleSample:
@@ -631,6 +701,7 @@ Write-Output ""
Write-Output " Samples: $($sampleSet.Count)"
Write-Output " Configurations: $($Configurations -join ', ')"
Write-Output " Platforms: $($Platforms -join ', ')"
+Write-Output " NT Target Ver: $NtTargetVersion ($ntTargetVersionCode)"
Write-Output " Combinations: $combinationsTotal"
Write-Output ""
Write-Output " Succeeded: $($buildState.Succeeded)"
@@ -650,9 +721,61 @@ Write-Output "------------------------------------------------------------------
$sortedResults = $buildState.Results | Sort-Object { $_.Sample }
$sortedResults | ConvertTo-Csv | Out-File $reportCsvPath
-$sortedResults | ConvertTo-Html -Title "WDK Sample Build Overview" | Out-File $reportHtmlPath
+$sortedResults | ConvertTo-Html -Title "WDK Sample Build Overview - _NT_TARGET_VERSION $NtTargetVersion" | Out-File $reportHtmlPath
# Only open the HTML report interactively (not in CI/automation)
if (-not $env:BUILD_BUILDID -and [Environment]::UserInteractive) {
Invoke-Item $reportHtmlPath
}
+
+# =============================================================================
+# Step 12 - GitHub Actions job summary (CI only; no-op when run locally)
+# =============================================================================
+# When $GITHUB_STEP_SUMMARY is set, emit an easy-to-scan markdown summary for the run
+# page: a status header, a counts table, and (if any) a table of failures with the first
+# compiler/linker error so problems are obvious without opening the logs.
+if ($env:GITHUB_STEP_SUMMARY) {
+ $icon = if ($buildState.Failed -gt 0) { ':x:' } elseif ($buildState.Sporadic -gt 0) { ':warning:' } else { ':white_check_mark:' }
+ $cfgLabel = "$($Configurations -join ',')|$($Platforms -join ',')"
+
+ $md = [System.Text.StringBuilder]::new()
+ [void]$md.AppendLine("## $icon ``$cfgLabel`` &nbsp;&middot;&nbsp; _NT_TARGET_VERSION ``$NtTargetVersion``")
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("Environment **$($buildEnv.Name)** &middot; WDK build **$buildNumber** &middot; **$($sampleSet.Count)** samples &middot; $($elapsed.Minutes)m $($elapsed.Seconds)s")
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("| :white_check_mark: Succeeded | :x: Failed | :warning: Sporadic | :heavy_minus_sign: Excluded | :grey_question: Unsupported |")
+ [void]$md.AppendLine("|---:|---:|---:|---:|---:|")
+ [void]$md.AppendLine("| $($buildState.Succeeded) | $($buildState.Failed) | $($buildState.Sporadic) | $($buildState.Excluded) | $($buildState.Unsupported) |")
+ [void]$md.AppendLine()
+
+ if ($buildState.FailSet.Count -gt 0) {
+ [void]$md.AppendLine("<details open><summary><b>:x: $($buildState.FailSet.Count) failed</b></summary>")
+ [void]$md.AppendLine()
+ [void]$md.AppendLine("| Sample | Config/Platform | First error |")
+ [void]$md.AppendLine("|---|---|---|")
+ foreach ($entry in ($buildState.FailSet | Sort-Object)) {
+ if ($entry -match '^(?<name>.*)\s+(?<config>\w+)\|(?<platform>\w+)$') {
+ $fName = $Matches.name; $fConfig = $Matches.config; $fPlatform = $Matches.platform
+ $errLog = Join-Path $LogFilesDirectory "$fName.$fConfig.$fPlatform.0.err"
+ $msg = ''
+ if (Test-Path $errLog) {
+ $line = Get-Content $errLog | Where-Object { $_ -match ': (error|fatal error) ' } | Select-Object -First 1
+ if ($line -match ':\s*((?:fatal )?error\s.+?)\s*\[[^\[]*\]\s*$') { $msg = $Matches[1] } else { $msg = $line }
+ }
+ $msg = ("$msg" -replace '\|', '\|').Trim()
+ if ($msg.Length -gt 180) { $msg = $msg.Substring(0, 177) + '...' }
+ [void]$md.AppendLine("| ``$fName`` | $fConfig/$fPlatform | $msg |")
+ }
+ }
+ [void]$md.AppendLine("</details>")
+ [void]$md.AppendLine()
+ }
+
+ if ($buildState.SporadicSet.Count -gt 0) {
+ $sp = ($buildState.SporadicSet | Sort-Object | ForEach-Object { "``$_``" }) -join ', '
+ [void]$md.AppendLine(":warning: **Sporadic** (passed on retry): $sp")
+ [void]$md.AppendLine()
+ }
+
+ $md.ToString() | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
+}
diff --git a/Building-Locally.md b/Building-Locally.md
index 9d683a86..ebddbfba 100644
--- a/Building-Locally.md
+++ b/Building-Locally.md
@@ -120,6 +120,52 @@ Get-Help .\Build-Samples.ps1 -Detailed
# Build a specific sample for Debug|x64 only:
.\Build-Samples.ps1 -Samples 'tools.sdv.samples.sampledriver' -Configurations 'Debug' -Platforms 'x64'
+
+# Build every sample linking against an older WDK library set (default is the latest):
+.\Build-Samples.ps1 -NtTargetVersion 10.0.22000
+```
+
+`-NtTargetVersion` selects the WDK **`_NT_TARGET_VERSION`** — the OS version of the libraries
+the driver links against. It accepts the Windows build number (`10.0.<build>`) or the short
+`<build>` tag (e.g. `10.0.22000` or `22000`); when omitted it uses the latest. The valid
+values are **auto-discovered from the active WDK** — `Get-NtTargetVersions.ps1` parses the
+WDK's `DriverGeneral.xml` rule — so a new WDK version is picked up automatically with no edits.
+List what's available with:
+
+```powershell
+.\Get-NtTargetVersions.ps1
+```
+
+---
+
+## Excluding samples from the build
+
+Samples that are known not to build for a given environment are listed in `exclusions.csv`
+at the repo root. Each row excludes a path (with wildcards) for specific
+configuration/platform combinations, an optional WDK build-number range, and an optional
+`_NT_TARGET_VERSION` range:
+
+```
+Path,Configurations,MinBuild,MaxBuild,MinNtTargetVersion,MaxNtTargetVersion,Reason
+```
+
+| Column | Meaning |
+| ---------------- | ---------------------------------------------------------------------------------------- |
+| `Path` | Sample path (backslashes); supports `*`/`?` wildcards. |
+| `Configurations` | `;`-separated `Config\|Platform` patterns, or `*` for all (e.g. `*\|ARM64`, `Debug\|x64`). |
+| `MinBuild`/`MaxBuild` | Inclusive WDK build-number range; blank = unbounded. |
+| `MinNtTargetVersion`/`MaxNtTargetVersion` | Inclusive `-NtTargetVersion` build-number range (e.g. `22621` matches `10.0.22621`); blank = unbounded. Use this for samples that fail only when linking against older libraries. |
+| `Reason` | Human-readable explanation (keep this column last; quote it if it contains commas). |
+
+A row is applied only when every populated condition matches the current run (path,
+configuration/platform, WDK build-number range, and NT target-version range are AND-ed
+together). Leave a column blank to ignore that dimension (the default for most rows).
+
+For example, to exclude a sample (Debug builds only) when linking against the `10.0.22621`
+library set or older, because it uses a newer API:
+
+```
+somepath,Debug|*,,,,22621,uses an API newer than the 10.0.22621 library
```
---
diff --git a/Get-NtTargetVersions.ps1 b/Get-NtTargetVersions.ps1
new file mode 100644
index 00000000..68088277
--- /dev/null
+++ b/Get-NtTargetVersions.ps1
@@ -0,0 +1,100 @@
+<#
+.SYNOPSIS
+ Auto-discovers the valid _NT_TARGET_VERSION values from the active WDK.
+
+.DESCRIPTION
+ The _NT_TARGET_VERSION property (the OS version of the libraries a driver links against)
+ is an enumeration defined by the WDK in its 'DriverGeneral.xml' rule file. This script
+ locates that rule file (from the restored NuGet packages, or the installed WDK) and parses
+ the enumeration so that nothing in the build needs a hard-coded version list: when a new
+ WDK adds a new _NT_TARGET_VERSION it is picked up automatically.
+
+ Returns one object per Windows 10/11 entry, newest-first:
+ Version e.g. 10.0.28000 (use with -NtTargetVersion)
+ Tag e.g. 28000 (short, filename/CI-friendly)
+ Code e.g. 0xA000012 (the NTDDI value passed to msbuild)
+ Build e.g. 28000 (numeric, for sorting/ranges)
+
+.PARAMETER XmlPath
+ Optional explicit path to a DriverGeneral.xml. When omitted the newest available rule file
+ is auto-located.
+
+.PARAMETER Newest
+ Return only the newest N versions (0 = all). Useful for bounding the CI build matrix.
+
+.PARAMETER AsMatrixJson
+ Emit a compact JSON array of { version, tag } objects for a GitHub Actions matrix
+ (consumed via fromJSON). Implies a single-line output.
+
+.EXAMPLE
+ .\Get-NtTargetVersions.ps1 # all discovered versions (objects)
+
+.EXAMPLE
+ .\Get-NtTargetVersions.ps1 -Newest 4 -AsMatrixJson
+#>
+[CmdletBinding()]
+param(
+ [string]$XmlPath,
+ [int]$Newest = 0,
+ [switch]$AsMatrixJson
+)
+
+function Find-DriverGeneralXml {
+ # Prefer the restored NuGet WDK package (matches what the build actually uses), then the
+ # installed WDK. Within each source, pick the highest build version.
+ $candidates = @()
+ $candidates += Get-ChildItem -Path (Join-Path $PSScriptRoot 'packages') -Recurse -Filter 'DriverGeneral.xml' -ErrorAction SilentlyContinue
+ foreach ($kitsRoot in @("${env:ProgramFiles(x86)}\Windows Kits\10\build", "${env:ProgramFiles}\Windows Kits\10\build")) {
+ if ($kitsRoot -and (Test-Path $kitsRoot)) {
+ $candidates += Get-ChildItem -Path $kitsRoot -Recurse -Filter 'DriverGeneral.xml' -ErrorAction SilentlyContinue
+ }
+ }
+ # EWDK / arbitrary build environments expose the build tree via these variables.
+ foreach ($envRoot in @($env:WDKContentRoot, $env:WindowsSdkDir)) {
+ if ($envRoot -and (Test-Path $envRoot)) {
+ $buildDir = Join-Path $envRoot 'build'
+ if (Test-Path $buildDir) {
+ $candidates += Get-ChildItem -Path $buildDir -Recurse -Filter 'DriverGeneral.xml' -ErrorAction SilentlyContinue
+ }
+ }
+ }
+ if (-not $candidates) { return $null }
+ # Order by the build version embedded in the path (e.g. ...\10.0.28000.0\...), highest first.
+ return ($candidates | Sort-Object {
+ if ($_.FullName -match '10\.0\.(\d+)\.\d') { [int]$Matches[1] } else { 0 }
+ } -Descending | Select-Object -First 1).FullName
+}
+
+if (-not $XmlPath) { $XmlPath = Find-DriverGeneralXml }
+if (-not $XmlPath -or -not (Test-Path $XmlPath)) {
+ throw "Could not locate DriverGeneral.xml. Restore the WDK NuGet packages or install the WDK, or pass -XmlPath."
+}
+
+[xml]$xml = Get-Content -Path $XmlPath -Raw
+$enum = $xml.ProjectSchemaDefinitions.Rule.EnumProperty | Where-Object { $_.Name -eq '_NT_TARGET_VERSION' }
+if (-not $enum) { throw "No _NT_TARGET_VERSION enumeration found in '$XmlPath'." }
+
+$versions =
+ $enum.EnumValue |
+ ForEach-Object {
+ # DisplayName is e.g. "Windows 10.0.28000"; Name is the NTDDI code e.g. "0xA000012".
+ if ("$($_.DisplayName)" -match 'Windows\s+(?<v>10\.0\.(?<b>\d+))\s*$') {
+ [pscustomobject]@{
+ Version = $Matches.v
+ Tag = $Matches.b
+ Code = $_.Name
+ Build = [int]$Matches.b
+ }
+ }
+ } |
+ Sort-Object Build -Descending
+
+if ($Newest -gt 0) { $versions = $versions | Select-Object -First $Newest }
+
+if ($AsMatrixJson) {
+ # Compact, single-line JSON for a GitHub Actions matrix: [{ "version": "...", "tag": "..." }, ...]
+ $matrix = @($versions | ForEach-Object { [ordered]@{ version = $_.Version; tag = $_.Tag } })
+ return ($matrix | ConvertTo-Json -Compress -Depth 3)
+}
+
+return $versions
diff --git a/exclusions.csv b/exclusions.csv
index df12335d..0cae0380 100644
--- a/exclusions.csv
+++ b/exclusions.csv
@@ -1,12 +1,20 @@
-Path,Configurations,MinBuild,MaxBuild,Reason
-audio\acx\samples\audiocodec\driver,*,,22621,Only NI: error C1083: Cannot open include file: 'acx.h': No such file or directory
-general\dchu\osrfx2_dchu_extension_loose,*|x64,,22621,Only NI: Only x64: Fails to build
-general\dchu\osrfx2_dchu_extension_tight,*|x64,,22621,Only NI: Only x64: Fails to build
-network\trans\WFPSampler,Debug|ARM64,,22621,Only NI: Only ARM: Fails to build on EWDK 22621 with VS 17.1.5 - CallingConvention=StdCall not supported
-prm,*,,22621,Only NI: Not supported on NI.
-powerlimit\plclient,*,,22621,Only NI: Not supported on NI.
-powerlimit\plpolicy,*,,22621,Only NI: Not supported on NI.
-general\pcidrv,*,26100,,"failure introduced in VS17.14, suppressed until fix"
-serial\serial,*,26100,,"failure introduced in VS17.14, suppressed until fix"
-network\wlan\wdi,*,26100,,"failure introduced in VS17.14, suppressed until fix"
-tools\kasan\samples\kasandemo-wdm,*|x64,26100,,"failure introduced in VS17.14, suppressed until fix"
+Path,Configurations,MinBuild,MaxBuild,MinNtTargetVersion,MaxNtTargetVersion,Reason
+audio\acx\samples\audiocodec\driver,*,,22621,,,Only NI: error C1083: Cannot open include file: 'acx.h': No such file or directory
+general\dchu\osrfx2_dchu_extension_loose,*|x64,,22621,,,Only NI: Only x64: Fails to build
+general\dchu\osrfx2_dchu_extension_tight,*|x64,,22621,,,Only NI: Only x64: Fails to build
+network\trans\WFPSampler,Debug|ARM64,,22621,,,Only NI: Only ARM: Fails to build on EWDK 22621 with VS 17.1.5 - CallingConvention=StdCall not supported
+prm,*,,22621,,,Only NI: Not supported on NI.
+powerlimit\plclient,*,,22621,,,Only NI: Not supported on NI.
+powerlimit\plpolicy,*,,22621,,,Only NI: Not supported on NI.
+general\pcidrv,*,26100,,,,"failure introduced in VS17.14, suppressed until fix"
+serial\serial,*,26100,,,,"failure introduced in VS17.14, suppressed until fix"
+network\wlan\wdi,*,26100,,,,"failure introduced in VS17.14, suppressed until fix"
+tools\kasan\samples\kasandemo-wdm,*|x64,26100,,,,"failure introduced in VS17.14, suppressed until fix"
+audio\sysvad,*,,,,22000,_NT_TARGET_VERSION: KSJACK_DESCRIPTION3 undeclared; audio jack descriptor v3 was added in 22H2 (10.0.22621)
+network\netadaptercx\netvadapter,*,,,,22621,_NT_TARGET_VERSION: requests an NDIS/DDI version newer than the linked library (C1189 wrong NDIS or DDI version)
+network\wlan\wificx,*,,,,22621,_NT_TARGET_VERSION: requests an NDIS/DDI version newer than the linked library (C1189 wrong NDIS or DDI version)
+powerlimit\plclient,*,,,,22621,_NT_TARGET_VERSION: POWER_LIMIT_ATTRIBUTES not declared in the older library (C2061)
+powerlimit\plpolicy,*,,,,22621,_NT_TARGET_VERSION: POWER_LIMIT_ATTRIBUTES not declared in the older library (C2061)
+storage\class\classpnp,Debug|*,,,,22621,_NT_TARGET_VERSION: STOR_ADDRESS_TYPE_NVME undeclared in the older library (C2065); Debug only
+storage\miniports\storahci,Debug|*,,,,22621,_NT_TARGET_VERSION: STOR_ADDRESS_TYPE_NVME undeclared in the older library (C2065); Debug only
+storage\msdsm,Debug|x64,,,,22621,_NT_TARGET_VERSION: STOR_ADDRESS_TYPE_NVME undeclared in the older library (C2065); Debug|x64 only