Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ A **.NET CLI tool** (`cswinrtprojectionrefgen.exe`) published as a **Native AOT*

The tool is wired through the `RunCsWinRTProjectionRefGenerator` MSBuild task (in `WinRT.Generator.Tasks`).

**Debug repro support**: when `--debug-repro-directory` is provided, captures the expanded set of input `.winmd` files (resolving any `local` / `sdk` / `sdk+` / version / directory tokens to concrete files) and a faithful `.rsp` into a self-contained `ref-projection-debug-repro.zip`. The tool also accepts a `.zip` as input and replays the captured run.

### 5. Impl generator (`src/WinRT.Impl.Generator/`)

A **.NET CLI tool** (`cswinrtimplgen.exe`) published as a **Native AOT** binary. Generates **forwarder/impl assemblies** that contain only type forwards (no actual code).
Expand Down Expand Up @@ -435,6 +437,8 @@ A **.NET CLI tool** (`cswinrtimplgen.exe`) published as a **Native AOT** binary.
4. Emits `[TypeForwarder]` entries for all public top-level types, routing to the appropriate projection assembly
5. Optionally signs with a strong-name key

**Debug repro support**: when `--debug-repro-directory` is provided, captures the output assembly and all reference assemblies along with a faithful `.rsp` into a self-contained `impl-debug-repro.zip`. The tool also accepts a `.zip` as input and replays the captured run.

### 6. Projection generator (`src/WinRT.Projection.Generator/`)

A **.NET CLI tool** (`cswinrtprojectiongen.exe`) published as a **Native AOT** binary. Runs at **app build / publish time** to produce a single projection `.dll` for the Windows SDK, the UWP XAML SDK, or all third-party Windows Runtime components referenced by the app. The tool drives the projection writer in-process and then compiles the resulting C# sources with Roslyn.
Expand All @@ -459,6 +463,8 @@ A **.NET CLI tool** (`cswinrtprojectiongen.exe`) published as a **Native AOT** b
2. **Generate Sources**: invoke `ProjectionWriter.Run(options)` in-process to produce C# files.
3. **Emit Assembly**: parse the generated `.cs` files with Roslyn, compile to `.dll` with `CSharpCompilation`, emit with embedded debug info.

**Debug repro support**: when `--debug-repro-directory` is provided, captures all reference `.dll`-s, all input `.winmd` files, and the expanded Windows metadata files (bundled into a separate `windows-metadata/` subfolder) along with a faithful `.rsp` into a self-contained `projection-debug-repro.zip`. The tool also accepts a `.zip` as input and replays the captured run.

### 7. Interop generator (`src/WinRT.Interop.Generator/`)

A **.NET CLI tool** (`cswinrtinteropgen.exe`) published as a **Native AOT** binary. This is the most complex build tool — it analyzes all application assemblies and produces the `WinRT.Interop.dll` sidecar containing all marshalling code.
Expand Down Expand Up @@ -493,7 +499,7 @@ There's two reasons for this:
1. **Discover phase**: loads all input assemblies in parallel, scans for Windows Runtime types, generic instantiations, user-defined types implementing Windows Runtime interfaces. Uses visitor pattern (`AllGenericTypesVisitor`, `AllSzArrayTypesVisitor`).
2. **Emit phase**: creates `WinRT.Interop.dll` via AsmResolver. Uses a two-pass IL generation approach (stub creation → rewriting via `InteropMethodRewriter`), then applies IL fixups.

**Debug repro support**: can capture all inputs into a `.zip` file for reproducible debugging.
**Debug repro support**: when `--debug-repro-directory` is provided, captures all reference and implementation `.dll`-s, the output assembly, and the resolved private implementation assemblies along with a faithful `.rsp` into a self-contained `interop-debug-repro.zip`. The tool also accepts a `.zip` as input and replays the captured run.

### 8. WinMD generator (`src/WinRT.WinMD.Generator/`)

Expand Down Expand Up @@ -532,6 +538,7 @@ WinRT.WinMD.Generator/
| `--output-winmd-path` | Output `.winmd` file path |
| `--assembly-version` | Assembly version stamped into the generated WinMD |
| `--use-windows-ui-xaml-projections` | Use UWP XAML (`Windows.UI.Xaml`) instead of WinUI |
| `--debug-repro-directory` | Optional directory to write a self-contained debug repro `.zip` to |

**How it integrates with the build:**

Expand All @@ -540,6 +547,8 @@ WinRT.WinMD.Generator/
- Runs after `CoreCompile` (it needs the compiled .dll), gated on `CsWinRTComponent == true` and `DesignTimeBuild != true`
- Output is `$(IntermediateOutputPath)$(AssemblyName).winmd`, then copied to `$(TargetDir)` by the authoring targets and packaged into the component's NuGet

**Debug repro support**: when `--debug-repro-directory` is provided, captures the input component `.dll` and all reference assemblies along with a faithful `.rsp` into a self-contained `winmd-debug-repro.zip`. The tool also accepts a `.zip` as input and replays the captured run.

### 9. Generator tasks (`src/WinRT.Generator.Tasks/`)

MSBuild task wrappers that bridge the MSBuild build system with the CLI tools above.
Expand Down Expand Up @@ -662,19 +671,20 @@ All five .NET build tools (`cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `c
- `WellKnown*Exception` for expected errors (with error IDs like `CSWINRTIMPLGEN0001`)
- `Unhandled*Exception` for unexpected errors (suggests opening a GitHub issue)
- `CommandLineArgumentNameAttribute` maps properties to CLI flag names
- Support a **debug repro** mode: when `--debug-repro-directory` is provided, each tool packages all of its input files (with hashed names to avoid collisions) and a faithful `.rsp` into a self-contained `.zip` (`impl-debug-repro.zip`, `interop-debug-repro.zip`, `projection-debug-repro.zip`, `ref-projection-debug-repro.zip`, `winmd-debug-repro.zip`). Each tool also accepts a `.zip` as input and replays the captured run from a temporary unpack directory, with original file names restored. The MSBuild task wrappers in `WinRT.Generator.Tasks` expose this via a `DebugReproDirectory` parameter, plumbed from the `$(CsWinRTGeneratorDebugReproDirectory)` MSBuild property.
- Security hardening: Control Flow Guard, `IlcResilient = false` (fail on unresolved assemblies)

### Error ID ranges

| Project | Error ID Pattern | Range |
|---------|-----------------|-------|
| Source Generator | `CSWINRT2xxx` | `CSWINRT2000`–`CSWINRT2009` |
| Reference Projection Generator | `CSWINRTPROJECTIONREFGENxxxx` | `0001`–`0005`, `9999` |
| Projection Generator (host) | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` |
| Reference Projection Generator | `CSWINRTPROJECTIONREFGENxxxx` | `0001`–`0008`, `9999` |
| Projection Generator (host) | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0011`, `9999` |
| Projection Writer (library) | `CSWINRTPROJECTIONGEN5xxx` | `5003`–`5021`, `9999` (shares the `CSWINRTPROJECTIONGEN` prefix with the host; the writer reserves the 5000+ range so the two never collide) |
| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0014`, `9999` |
| Interop Generator | `CSWINRTINTEROPGENxxxx` | `0001`–`0097`, `9999` |
| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0007`, `9999` |
| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0010`, `9999` |
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` |

---
Expand Down
6 changes: 6 additions & 0 deletions .github/skills/update-copilot-instructions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,40 @@ Launch parallel explore agents for each of the 11 CsWinRT 3.0 projects listed in
- Error ID range (`CSWINRTPROJECTIONREFGENxxxx`) in `Errors/WellKnownReferenceProjectionGeneratorExceptions.cs` is accurate
- Project settings (Native AOT, dependencies) are current
- MSBuild integration via `nuget/Microsoft.Windows.CsWinRT.targets` (CsWinRTGenerateProjection target → `RunCsWinRTProjectionRefGenerator`) is wired
- Debug repro support is documented (mentions `--debug-repro-directory`, `ref-projection-debug-repro.zip`, expansion of input tokens to concrete `.winmd` files)

5. **Impl generator (`src/WinRT.Impl.Generator/`)**
- Type forward routing logic is current
- Project settings and dependencies are current
- CLI parameters are current
- Debug repro support is documented (mentions `--debug-repro-directory`, `impl-debug-repro.zip`, output assembly + reference assemblies bundled)

6. **Projection generator (`src/WinRT.Projection.Generator/`)**
- Three projection modes are accurately described
- Namespace filter logic is current
- Project settings and dependencies (project reference to `WinRT.Projection.Writer`) are current
- The pipeline is documented as in-process (the projection writer is invoked as a library)
- `ProjectionGeneratorArgs` no longer contains any leftover `CsWinRTExePath` field
- Debug repro support is documented (mentions `--debug-repro-directory`, `projection-debug-repro.zip`, expanded Windows metadata bundled into a `windows-metadata/` subfolder)

7. **Interop generator (`src/WinRT.Interop.Generator/`)**
- Generated content categories are current
- Directory structure and key types are accurate
- Project settings and dependencies are current
- Debug repro support is documented (mentions `--debug-repro-directory`, `interop-debug-repro.zip`, reference + implementation `.dll`-s bundled into separate subfolders)

8. **WinMD generator (`src/WinRT.WinMD.Generator/`)**
- CLI parameters on `WinMDGeneratorArgs` are current
- Error ID range (`CSWINRTWINMDGENxxxx`) in `Errors/WellKnownWinMDExceptions.cs` is accurate
- Project settings and dependencies are current
- MSBuild integration via `nuget/Microsoft.Windows.CsWinRT.Authoring.WinMD.targets` is wired (gated on `CsWinRTComponent`)
- Debug repro support is documented (mentions `--debug-repro-directory`, `winmd-debug-repro.zip`, input component `.dll` + reference assemblies bundled)

9. **Generator tasks (`src/WinRT.Generator.Tasks/`)**
- MSBuild task classes are accurately listed (including `RunCsWinRTProjectionRefGenerator` and `RunCsWinRTWinMDGenerator`)
- Task-to-tool mappings are current
- No leftover `CsWinRTExePath` parameter on `RunCsWinRTMergedProjectionGenerator`
- All five tasks expose a `DebugReproDirectory` parameter plumbed from `$(CsWinRTGeneratorDebugReproDirectory)`

10. **SDK projection builds (`src/WinRT.Sdk.Projection/`)**
- Assembly name logic (base vs XAML) is current
Expand Down
1 change: 1 addition & 0 deletions nuget/Microsoft.Windows.CsWinRT.Authoring.WinMD.targets
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
OutputWinmdPath="$(_CsWinRTWinMDOutputPath)"
AssemblyVersion="$(AssemblyVersion)"
UseWindowsUIXamlProjections="$(CsWinRTUseWindowsUIXamlProjections)"
DebugReproDirectory="$(CsWinRTGeneratorDebugReproDirectory)"
CsWinRTToolsDirectory="$(CsWinRTWinMDGenEffectiveToolsDirectory)"
CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)"
StandardOutputImportance="$(CsWinRTGeneratorStandardOutputImportance)"
Expand Down
1 change: 1 addition & 0 deletions nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
WinMDPaths="@(_WinMDPathsList)"
TargetFramework="$(CsWinRTExeTFM)"
WindowsMetadata="$(CsWinRTWindowsMetadata)"
DebugReproDirectory="$(CsWinRTGeneratorDebugReproDirectory)"
CsWinRTToolsDirectory="$(CsWinRTMergedProjectionEffectiveToolsDirectory)"
CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)"
AdditionalArguments="@(CsWinRTGeneratorAdditionalArgument)"
Expand Down
1 change: 1 addition & 0 deletions nuget/Microsoft.Windows.CsWinRT.targets
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
PublicExclusiveTo="$(CsWinRTPublicExclusiveToInterfaces)"
IdicExclusiveTo="$(CsWinRTDynamicallyInterfaceCastableExclusiveTo)"
ReferenceProjection="$(CsWinRTGenerateReferenceProjection)"
DebugReproDirectory="$(CsWinRTGeneratorDebugReproDirectory)"
CsWinRTToolsDirectory="$(CsWinRTRefGenEffectiveToolsDirectory)"
CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)"
ContinueOnError="$(CsWinRTContinueOnError)" />
Expand Down
29 changes: 29 additions & 0 deletions src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public sealed class RunCsWinRTMergedProjectionGenerator : ToolTask
/// <remarks>If not set, the default will match the number of available processor cores.</remarks>
public int MaxDegreesOfParallelism { get; set; } = -1;

/// <summary>
/// Gets or sets the directory where the debug repro will be produced.
/// </summary>
/// <remarks>If not set, no debug repro will be produced.</remarks>
public string? DebugReproDirectory { get; set; }

/// <inheritdoc/>
protected override string ToolName => "cswinrtprojectiongen.exe";

Expand Down Expand Up @@ -134,6 +140,13 @@ protected override bool ValidateParameters()
return false;
}

if (DebugReproDirectory is not null && !Directory.Exists(DebugReproDirectory))
{
Log.LogWarning("Debug repro directory '{0}' is invalid or does not exist.", DebugReproDirectory);

return false;
}

if (CsWinRTToolsArchitecture is not null &&
!CsWinRTToolsArchitecture.Equals("x86", StringComparison.OrdinalIgnoreCase) &&
!CsWinRTToolsArchitecture.Equals("x64", StringComparison.OrdinalIgnoreCase) &&
Expand Down Expand Up @@ -208,6 +221,7 @@ protected override string GenerateResponseFileCommands()
}

AppendResponseFileCommand(args, "--max-degrees-of-parallelism", MaxDegreesOfParallelism.ToString());
AppendResponseFileOptionalCommand(args, "--debug-repro-directory", DebugReproDirectory);

// Add any additional arguments that are not statically known
foreach (ITaskItem additionalArgument in AdditionalArguments ?? [])
Expand All @@ -228,4 +242,19 @@ private static void AppendResponseFileCommand(StringBuilder args, string command
{
_ = args.Append($"{commandName} ").AppendLine(commandValue);
}

/// <summary>
/// Appends an optional command line argument to the response file arguments, with the right format.
/// </summary>
/// <param name="args">The command line arguments being built.</param>
/// <param name="commandName">The command name to append.</param>
/// <param name="commandValue">The optional command value to append.</param>
/// <remarks>This method will not append the command if <paramref name="commandValue"/> is <see langword="null"/>.</remarks>
private static void AppendResponseFileOptionalCommand(StringBuilder args, string commandName, string? commandValue)
{
if (commandValue is not null)
{
AppendResponseFileCommand(args, commandName, commandValue);
}
}
}
30 changes: 30 additions & 0 deletions src/WinRT.Generator.Tasks/RunCsWinRTProjectionRefGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ public sealed class RunCsWinRTProjectionRefGenerator : ToolTask
/// </summary>
public bool ReferenceProjection { get; set; }

/// <summary>
/// Gets or sets the directory where the debug repro will be produced.
/// </summary>
/// <remarks>If not set, no debug repro will be produced.</remarks>
public string? DebugReproDirectory { get; set; }

/// <summary>
/// Gets or sets the tools directory where the 'cswinrtprojectionrefgen' tool is located.
/// </summary>
Expand Down Expand Up @@ -148,6 +154,13 @@ protected override bool ValidateParameters()
return false;
}

if (DebugReproDirectory is not null && !Directory.Exists(DebugReproDirectory))
{
Log.LogWarning("Debug repro directory '{0}' is invalid or does not exist.", DebugReproDirectory);

return false;
}

if (CsWinRTToolsArchitecture is not null &&
!CsWinRTToolsArchitecture.Equals("x86", StringComparison.OrdinalIgnoreCase) &&
!CsWinRTToolsArchitecture.Equals("x64", StringComparison.OrdinalIgnoreCase) &&
Expand Down Expand Up @@ -247,6 +260,8 @@ protected override string GenerateResponseFileCommands()
AppendResponseFileCommand(args, "--reference-projection", "true");
}

AppendResponseFileOptionalCommand(args, "--debug-repro-directory", DebugReproDirectory);

// Add any additional arguments that are not statically known
foreach (ITaskItem additionalArgument in AdditionalArguments ?? [])
{
Expand All @@ -266,4 +281,19 @@ private static void AppendResponseFileCommand(StringBuilder args, string command
{
_ = args.Append($"{commandName} ").AppendLine(commandValue);
}

/// <summary>
/// Appends an optional command line argument to the response file arguments, with the right format.
/// </summary>
/// <param name="args">The command line arguments being built.</param>
/// <param name="commandName">The command name to append.</param>
/// <param name="commandValue">The optional command value to append.</param>
/// <remarks>This method will not append the command if <paramref name="commandValue"/> is <see langword="null"/>.</remarks>
private static void AppendResponseFileOptionalCommand(StringBuilder args, string commandName, string? commandValue)
{
if (commandValue is not null)
{
AppendResponseFileCommand(args, commandName, commandValue);
}
}
}
25 changes: 25 additions & 0 deletions src/WinRT.Generator.Tasks/RunCsWinRTWinMDGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public sealed class RunCsWinRTWinMDGenerator : ToolTask
/// </summary>
public bool UseWindowsUIXamlProjections { get; set; } = false;

/// <summary>
/// Gets or sets the directory where the debug repro will be produced.
/// </summary>
/// <remarks>If not set, no debug repro will be produced.</remarks>
public string? DebugReproDirectory { get; set; }

/// <summary>
/// Gets or sets the tools directory where the 'cswinrtwinmdgen' tool is located.
/// </summary>
Expand Down Expand Up @@ -104,6 +110,12 @@ protected override bool ValidateParameters()
return false;
}

if (DebugReproDirectory is not null && !Directory.Exists(DebugReproDirectory))
{
Log.LogWarning("Debug repro directory '{0}' is invalid or does not exist.", DebugReproDirectory);
return false;
}

return true;
}

Expand Down Expand Up @@ -145,6 +157,7 @@ protected override string GenerateResponseFileCommands()
AppendResponseFileCommand(args, "--output-winmd-path", OutputWinmdPath!);
AppendResponseFileCommand(args, "--assembly-version", AssemblyVersion!);
AppendResponseFileCommand(args, "--use-windows-ui-xaml-projections", UseWindowsUIXamlProjections.ToString());
AppendResponseFileOptionalCommand(args, "--debug-repro-directory", DebugReproDirectory);

return args.ToString();
}
Expand All @@ -156,4 +169,16 @@ private static void AppendResponseFileCommand(StringBuilder args, string command
{
_ = args.Append($"{commandName} ").AppendLine(commandValue);
}

/// <summary>
/// Appends an optional command line argument to the response file arguments, with the right format.
/// </summary>
/// <remarks>This method will not append the command if <paramref name="commandValue"/> is <see langword="null"/>.</remarks>
private static void AppendResponseFileOptionalCommand(StringBuilder args, string commandName, string? commandValue)
{
if (commandValue is not null)
{
AppendResponseFileCommand(args, commandName, commandValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ public static Exception CsWinRTProcessError(int exitCode, Exception exception)
return Exception(8, $"The projection writer failed during source generation (exit code {exitCode}).", exception);
}

/// <summary>
/// The debug repro directory does not exist.
/// </summary>
public static Exception DebugReproDirectoryDoesNotExist(string path)
{
return Exception(9, $"The debug repro directory '{path}' does not exist.");
}

/// <summary>
/// The debug repro contains a file entry that has no mapping.
/// </summary>
public static Exception DebugReproMissingFileEntryMapping(string path)
{
return Exception(10, $"The debug repro file entry with path '{path}' is missing its assembly path mapping.");
}

/// <summary>
/// The debug repro contains a file entry that was not recognized.
/// </summary>
public static Exception DebugReproUnrecognizedFileEntry(string path)
{
return Exception(11, $"The debug repro file entry with path '{path}' was not recognized.");
}

/// <summary>
/// Creates a new exception with the specified id and message.
/// </summary>
Expand Down
Loading