diff --git a/docs/sdk_patches.md b/docs/sdk_patches.md new file mode 100644 index 00000000..30ffad0a --- /dev/null +++ b/docs/sdk_patches.md @@ -0,0 +1,74 @@ +# SDK Header Patches + +Patches modify SDK headers during ingestion to add metadata annotations (conditional enums, SAL attributes, etc.) until those changes ship in the official SDK. + +## How It Works + +Patches are unified diffs in `generation/WinSDK/patches/`, organized by phase: + +``` +patches/ + pre-midl/ ← Applied after SDK copy, before MIDL recompilation + post-midl/ ← Applied after MIDL recompilation and header restores +``` + +**Pre-MIDL** patches target `.idl` files (or headers included during MIDL compilation). Changes flow through `ConvertMidlAttributesToSalAnnotations.ps1` → MIDL → generated `.h`. + +**Post-MIDL** patches target `.h` files directly. Use this for headers not generated by MIDL (e.g., `Uxtheme.h`). + +Both phases run automatically during `UpdateSDK.ps1` / `RecompileIdlFilesForScraping.ps1`. If a patch fails to apply, the script errors out — this signals the SDK changed and the patch needs regeneration. + +## Naming Convention + +``` +..patch +``` + +Each patch is a single logical change. Multiple patches per file are supported: + +``` +patches/post-midl/ + Uxtheme.h.set-theme-app-properties-enum.patch + Uxtheme.h.some-other-fix.patch +``` + +Patches are applied in sorted filename order within each phase. + +## Creating a Patch + +1. Run `UpdateSDK.ps1` (or `RecompileIdlFilesForScraping.ps1`) to get a pristine SDK copy with existing patches applied. +2. Edit the target file in `generation/WinSDK/RecompiledIdlHeaders/`. +3. Generate the patch: + ```powershell + git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/MyHeader.h" ` + > "generation/WinSDK/patches/post-midl/MyHeader.h.my-reason.patch" + ``` +4. Commit both the patch file and the modified header. + +For IDL files, use `pre-midl/`: +```powershell +git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/myfile.idl" ` + > "generation/WinSDK/patches/pre-midl/myfile.idl.my-reason.patch" +``` + +## Updating a Patch After an SDK Update + +When `UpdateSDK.ps1` is run with a new SDK version: +1. Fresh SDK headers are copied (overwriting all previous content). +2. Patches are re-applied automatically. +3. **If a patch fails**: the SDK incorporated the fix (or changed the surrounding code). Regenerate or remove the patch. + +To regenerate a failing patch against the new SDK: +1. Comment out the failing patch temporarily (or move it aside). +2. Re-run `RecompileIdlFilesForScraping.ps1` to get the new pristine baseline. +3. Make the edit, generate the new patch, commit. + +## Removing a Patch + +When the official SDK ships a fix, delete the `.patch` file and re-run `UpdateSDK.ps1`. The header will now contain the fix natively. + +## Examples + +**Header patch** (`post-midl/Uxtheme.h.set-theme-app-properties-enum.patch`): Would add a conditional `SET_THEME_APP_PROPERTIES_FLAGS` enum under `#ifdef _WIN32METADATA_` and update function signatures. + +**IDL patch** (`pre-midl/myfile.idl.my-reason.patch`): Would add a `cpp_quote` block to an IDL file. MIDL compiles it and the change appears in the generated `.h`. diff --git a/generation/WinSDK/patches/post-midl/.gitkeep b/generation/WinSDK/patches/post-midl/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/generation/WinSDK/patches/pre-midl/.gitkeep b/generation/WinSDK/patches/pre-midl/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/scripts/ApplySDKPatches.ps1 b/scripts/ApplySDKPatches.ps1 new file mode 100644 index 00000000..bd38550c --- /dev/null +++ b/scripts/ApplySDKPatches.ps1 @@ -0,0 +1,78 @@ +# ApplySDKPatches.ps1 +# Applies SDK header patches from generation/WinSDK/patches/{phase}/ directories. +# +# Patches are unified diffs (generated by `git diff`) with paths relative to the repo root. +# They are organized by phase: +# patches/pre-midl/ — Applied after SDK copy, before MIDL recompilation. +# Use for .idl files and headers included during MIDL compilation. +# patches/post-midl/ — Applied after MIDL recompilation and header restores. +# Use for .h files not generated by MIDL, or direct header patches. +# +# Naming convention: ..patch +# e.g. Uxtheme.h.set-theme-app-properties-enum.patch +# amstream.idl.output-state-metadata-marker.patch +# Multiple patches per file are supported. Each should be a single logical change. +# Patches are applied in sorted filename order within each phase. +# +# Creating a new patch: +# 1. Run UpdateSDK.ps1 (or RecompileIdlFilesForScraping.ps1) to get a pristine SDK copy +# 2. Edit the target file in RecompiledIdlHeaders/ +# 3. From the repo root: +# git diff -- "generation/WinSDK/RecompiledIdlHeaders/um/" ` +# > "generation/WinSDK/patches//..patch" +# 4. The patched file in RecompiledIdlHeaders/ stays as-is (it's the committed result) +# +# IMPORTANT: When updating an existing patch after an SDK update, the fresh SDK copy is already +# the correct baseline. Edit the file, re-run `git diff`, and overwrite the patch file. +# +# Usage: +# . .\ApplySDKPatches.ps1 -Phase pre-midl # Apply pre-MIDL patches (IDL files, etc.) +# . .\ApplySDKPatches.ps1 -Phase post-midl # Apply post-MIDL patches (header files) + +[CmdletBinding()] +Param( + [Parameter(Mandatory=$true)] + [ValidateSet("pre-midl", "post-midl")] + [string]$Phase +) + +. "$PSScriptRoot\CommonUtils.ps1" + +$patchDir = Join-Path $windowsWin32ProjectRoot "patches\$Phase" + +if (!(Test-Path $patchDir)) { + Write-Host "No $Phase patches directory found. Skipping." + return +} + +$patchFiles = Get-ChildItem -Path $patchDir -Filter "*.patch" -Recurse | Sort-Object FullName +if ($patchFiles.Count -eq 0) { + Write-Host "No $Phase patch files found. Skipping." + return +} + +$appliedCount = 0 +$failedCount = 0 + +foreach ($patchFile in $patchFiles) { + $relativeName = $patchFile.FullName.Substring($patchDir.Length + 1) + Write-Host "Applying $Phase patch: $relativeName..." + + # Anchor git to repo root so patch paths (generation/WinSDK/...) resolve correctly + git -C $rootDir apply $patchFile.FullName 2>&1 | ForEach-Object { Write-Host " $_" } + if ($LASTEXITCODE -eq 0) { + Write-Host " Applied successfully" -ForegroundColor Green + $appliedCount++ + } else { + Write-Warning " FAILED to apply patch: $relativeName" + Write-Warning " The SDK headers may have changed. Regenerate this patch against the new SDK." + $failedCount++ + } +} + +Write-Host "SDK $Phase patches: $appliedCount applied, $failedCount failed (of $($patchFiles.Count))." + +if ($failedCount -gt 0) { + Write-Error "$Phase patches failed. Regenerate failing patches against the new SDK headers." + exit 1 +} diff --git a/scripts/RecompileIdlFilesForScraping.ps1 b/scripts/RecompileIdlFilesForScraping.ps1 index 5e54df3c..c0e66e88 100644 --- a/scripts/RecompileIdlFilesForScraping.ps1 +++ b/scripts/RecompileIdlFilesForScraping.ps1 @@ -44,6 +44,10 @@ Copy-Item "$d3dIncludeDir\dxgiformat.*" "$recompiledIdlHeadersDir\shared" -Recur Write-Host "Copying additional headers from $windowsWin32ProjectRoot\AdditionalHeaders to $recompiledIdlHeadersDir\um..." Copy-Item "$windowsWin32ProjectRoot\AdditionalHeaders\*" "$recompiledIdlHeadersDir\um" -Recurse +Write-Host "Applying SDK patches to IDL files (pre-MIDL)..." +& $PSScriptRoot\ApplySDKPatches.ps1 -Phase pre-midl +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + Write-Host "Converting MIDL attributes to SAL annotations..." $idlFilesToRecompile = [System.Collections.ArrayList]@() $idlFilesToExclude = "cellularapi_oem", "certbcli", "dxgicommon", "dxgitype", "microsoft.diagnostics.appanalysis", "PortableDeviceConnectImports", "wincrypt" | ForEach-Object { "$_.idl" } @@ -183,4 +187,8 @@ foreach ($winHvHeader in $winHvHeadersToRestore) { git checkout origin/main $fullPath } +Write-Host "Applying SDK patches to header files (post-MIDL)..." +& $PSScriptRoot\ApplySDKPatches.ps1 -Phase post-midl +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $ErrorActionPreference = "Stop"