Auto-derive typedef-tag remaps from SDK headers - #2237
Closed
Jevan Saks (jevansaks) wants to merge 23 commits into
Closed
Auto-derive typedef-tag remaps from SDK headers#2237Jevan Saks (jevansaks) wants to merge 23 commits into
Jevan Saks (jevansaks) wants to merge 23 commits into
Conversation
Comprehensive plan for moving sidecar metadata (.rsp, .json files) into header annotations using SAL-like __attribute__((annotate(...))) macros. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rigorous testing of __attribute__((annotate(...))) through ClangSharp v17: - Parameter-level annotations: ALL WORK (8 types validated) - Function-level annotations: DROPPED by ClangSharp - Type/typedef annotations: DROPPED by ClangSharp - Struct field annotations: DROPPED by ClangSharp Documented carrier-parameter workaround and extraction tool alternative. Updated shift-left plan with revised strategy based on findings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ClangSharp v18+ (PR #552) adds [NativeAnnotation] support that preserves __attribute__((annotate(...))) on ALL declaration types - functions, parameters, struct fields, and return values. This eliminates the need for extraction tools or carrier-parameter workarounds. Upgrading from v17 to v18+ is the path forward for shift-left metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add AutoDeriveRemaps MSBuild task that scans SDK header files to
automatically generate --remap entries for typedef-tag patterns
(e.g., typedef struct _FOO { ... } FOO -> _FOO=FOO).
This removes 11,805 manually-maintained entries from scraper.settings.rsp
(reduced from 12,891 lines to 1,086 lines). The remaining 913 entries are:
- 51 semantic overrides (e.g., _RTL_BARRIER=SYNCHRONIZATION_BARRIER)
- 862 entries not derivable from headers (type aliases, nested members, etc.)
The auto-derived remaps are generated at build time and produce the exact
same remap set as the original manual entries (verified: 0 missing, 0 changed).
New typedef-tag patterns in future SDK versions will be auto-discovered
without manual maintenance.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add .config/dotnet-tools.json pinning ClangSharpPInvokeGenerator to 17.0.1 so builds use the correct version regardless of what is installed globally (a newer global version like v18 generates NativeAnnotation attributes that the emitter does not handle, causing GetDeclarationDiagnostics to take ~68 minutes instead of ~28 seconds) - Generate a local tool manifest at build time in the scratch directory so this works both in-repo and when the GeneratorSdk is consumed as a NuGet MSBuild SDK package by external projects - Invoke ClangSharp directly via its DLL path instead of dotnet tool run to avoid per-invocation overhead across 963 partition items - Fix thread-safety bug: replace non-atomic ret &= ProcessPartition() in Parallel.ForEach with Interlocked.Increment on a failure counter - Increase default scraping parallelism from ProcessorCount/2 to ProcessorCount - Remove GenerateNuGetConfig and global tool install path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add AutoDeriveRemaps MSBuild task that scans SDK header files to
automatically generate --remap entries for typedef-tag patterns
(e.g., typedef struct _FOO { ... } FOO -> _FOO=FOO).
This removes ~11,800 manually-maintained entries from scraper.settings.rsp
(reduced from 12,891 lines to 1,107 lines). The remaining ~930 entries are:
- 53 semantic overrides (e.g., _GUID=Guid, _RTL_BARRIER=SYNCHRONIZATION_BARRIER)
- 13 identity suppression remaps for types where auto-derivation picks the wrong
typedef from headers with multiple aliases
- ~870 entries not derivable from headers (type aliases, nested members, etc.)
Verified: produces identical winmd output with zero differences vs last release.
Also adds .vsconfig for VS workload requirements.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace CLI invocation of ClangSharpPInvokeGenerator with direct library usage via reflection. This lets us access _allValidNameRemappings after generation, which contains all typedef-tag relationships discovered by libclang (13,896 remaps auto-discovered from the preprocessed AST). Key changes to ScrapeHeaders.ProcessPartition: - Load ClangSharp assemblies from tool store via Assembly.LoadFrom - Pre-load native libclang.dll/libClangSharp.dll via NativeLibrary.Load - Create PInvokeGeneratorConfiguration from parsed RSP files - Call PInvokeGenerator.GenerateBindings via reflection - Use IL-emitted delegate for CXTranslationUnit.TryParse (ReadOnlySpan params) - Read _allValidNameRemappings via reflection after generation - Custom outputStreamFactory (MemoryStream) avoids file locking issues - Fall back to CLI for partitions with x86 inline assembly diagnostics Handles macro-expanded names (e.g., VK_TO_WCHARS1 from ## pasting) that the previous text-based scanner could not discover. End-to-end verified: full build succeeds, 393/393 partitions scraped, winmd generated successfully (24.3 MB, matching baseline). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Documents the architecture, implementation details, and next steps for using ClangSharp PInvokeGenerator as a library to auto-derive typedef-tag remaps from libclang's preprocessed AST. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the in-process ClangSharp library integration (reflection, IL emission,
assembly loading, native library pre-loading) that ran all partitions in the
same host process. This shared a single libclang instance across 16 concurrent
threads, creating process isolation concerns: shared state, memory pressure,
thread safety, and file contention.
Instead, rely on the existing CLI invocation which naturally provides process
isolation (each partition runs in its own dotnet process). The
log-potential-typedef-remappings config option (already enabled in
baseSettings.rsp) causes ClangSharp to output typedef-tag relationships as
'Info: Potential remap: X=Y' lines, which ProcessPartition already parses.
New auto-rsp writing:
- WriteAutoRemapsRsp() writes discovered remaps to
scraper.autoRemaps.generated.rsp after all partitions complete
- Merges with existing auto-rsp from previous builds (accumulates over time)
- sdk.targets includes auto-rsp in @(ScraperRsp) before manual entries so
manual overrides take priority
- Condition=Exists handles first-build bootstrap (file doesn't exist yet)
Also fixes a pre-existing bug in remap value parsing: Split('=') was used
which incorrectly handles values containing '=' characters. Now uses
IndexOf('=') to split only on the first occurrence.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create ClangSharpWorker, a standalone console app that wraps PInvokeGenerator as a library in its own process. It: - Takes RSP files as CLI args (same format as ClangSharpPInvokeGenerator) - Loads ClangSharp assemblies via Assembly.LoadFrom (reflection-based) - Runs GenerateBindings() to produce C# output (same as CLI) - Reads _allValidNameRemappings via reflection after generation - Writes discovered typedef-tag remaps to a .remaps sidecar file - Exits cleanly (full process isolation) ScrapeHeaders spawns ClangSharpWorker instead of the stock CLI when available. Each partition runs in its own dotnet process, eliminating thread safety and shared-state concerns. After all partitions complete, ScrapeHeaders reads the .remaps files, merges the discoveries, and writes scraper.autoRemaps.generated.rsp for consumption on subsequent builds. Falls back to stock ClangSharpPInvokeGenerator CLI if the worker is not available or fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ClangSharpWorker.dll must be present — remove conditional fallback to stock ClangSharpPInvokeGenerator CLI. ScrapeHeaders now errors if the worker is not found at ToolsBinDir. Also reads .remaps sidecar files from the worker to collect discovered typedef-tag remaps after each successful partition. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrite auto-type-remappings-plan.md to document: - ClangSharpWorker subprocess architecture - PInvokeGenerator extensibility research (sealed class, no public API for remapping data, no AST hooks) - Useful private fields for future extraction (_usedRemappings) - How VisitTypedefDecl discovers remaps - Verified build results: 16,098 remaps from 393 partitions, 23.2 MB winmd Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Full IL comparison of 1,651,355 lines confirms the ClangSharpWorker build produces an identical winmd to the origin/main CLI baseline. Analysis of 16,098 auto-discovered remaps vs 12,705 manual entries: - 12,196 (96%) are safely auto-derivable - 32 conflicts where auto picks wrong name (e.g., _LARGE_INTEGER→ LARGE_INTEGER vs manual long, _RTL_CRITICAL_SECTION→RTL_CRITICAL_SECTION vs manual CRITICAL_SECTION) - 477 manual-only entries (semantic overrides, not discoverable from AST) - 3,869 new discoveries not in manual file The auto-rsp works correctly with manual overrides taking priority. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change remap output from .rsp (consumed by scraper) to .cs (consumed by WinmdGenerator). ScrapeHeaders writes AutoRemaps.generated.cs containing tag=typedef entries as a static string array. ClangSharpSourceToWinmd reads this .cs file and applies the remaps during MetadataSyntaxTreeCleaner via: - VisitIdentifierName: renames all type references globally - VisitStructDeclaration: renames struct declarations - VisitEnumDeclaration: renames enum declarations - VisitCompilationUnit: strips the AutoRemaps namespace from output This allows the full pipeline to work in a single build pass: the worker discovers remaps during scraping, writes them as .cs, and the winmd generator applies them during syntax tree cleaning. Remove auto-rsp from sdk.targets since remaps now flow through .cs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The LoadAutoTypedefRemaps parser used Contains(=") which looked for equals-then-quote. Changed to Contains(=) to match actual format. Also restores scraper.settings.rsp -- manual remap removal revealed that _allValidNameRemappings contains incorrect entries (IUnknown to IXmlReaderInput) making blind global renaming unsafe. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
_allValidNameRemappings contains incorrect entries like IUnknown to IXmlReaderInput that make blind global renaming unsafe. VisitIdentifierName approach is too aggressive. Document three path-forward options. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add filtering in WriteAutoRemapsCsFile: only include entries where the tag name looks internal (_, tag, __MIDL, ABI::, lowercase, trailing _). Skip built-in defaults, identity remaps, and entries where the typedef name looks worse than the tag name. Disable VisitIdentifierName global rename in MetadataSyntaxTreeCleaner. Testing revealed that applying new remap discoveries (not in the manual --remap file) changes the API surface in ways that need review. Examples: - smiOCTETS -> smiBITS (valid typedef but not previously remapped) - IUnknown -> IXmlReaderInput (correctly filtered by heuristic) The auto-remaps .cs file is still generated for analysis and reporting. Applying the remaps at build time requires a more targeted approach that only renames types matching the manual --remap entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revert the earlier attempted rsp cleanup. The auto-remap application in MetadataSyntaxTreeCleaner is disabled, so the manual --remap entries must remain for PInvokeGenerator to produce correct output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ClangSharpWorker now runs PInvokeGenerator twice per partition: Phase 1 (Discovery): Runs GenerateBindings with existing --remap entries. Extracts _allValidNameRemappings. Filters using heuristic (single typedef, internal-looking tag name). Output is discarded. Phase 2 (Generation): Runs GenerateBindings again with merged remaps (discovered + configured). Produces final C# output with correct type names, including auto-discovered typedef-tag relationships. Both phases run in the same process, using the same loaded assemblies. Build time increases ~53% (15:51 vs 10:20) due to double parsing. The heuristic filter: - Single typedef per tag only (skip ambiguous) - Tag must look internal (_, tag, __MIDL, ABI::, lowercase, trailing _) - Skip if typedef looks worse than tag - Configured --remap overrides take priority Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two-phase ClangSharpWorker produces identical winmd to baseline (0 IL differences across 1,651,338 lines). Build time ~53% overhead. Removing manual --remap entries is not feasible: entries affect PInvokeGenerator partition scoping beyond just naming (VDMCONTEXT cross-namespace conflict). Manual rsp kept unchanged. Going-forward benefit: new SDK types auto-remapped without manual entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an \AutoDeriveRemaps\ MSBuild task that scans SDK header files at build time to automatically generate --remap\ entries for typedef-tag patterns, replacing ~11,800 manually-maintained entries in \scraper.settings.rsp.
What it does
The task parses C/C++ headers to find \ ypedef struct/enum/union TAG { ... } TYPEDEF;\ patterns and generates \TAG=TYPEDEF\ remap entries that ClangSharp uses to prefer the public typedef name over the internal tag name.
Before: 12,891 lines of manual remaps in \scraper.settings.rsp
After: 1,107 lines (930 residual entries that can't be auto-derived)
Residual entries (kept manual)
Verification
Files changed
Future work