You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Workaround WindowsAppSDK#6394: copy project .pri into AOT publish output (#360)
* Workaround WindowsAppSDK#6394: copy project .pri into AOT publish output
Root cause: WindowsAppSDK's NativeAOT publish pipeline does not copy the
project .pri (generated by _GenerateProjectPriFile) into the publish output
for unpackaged apps (WindowsPackageType=None). The MakePRI item-group is
gated on AppxPackage=true, so unpackaged apps fall through. Same applies
to .xbf files emitted by the XAML compiler. Upstream tracking:
microsoft/WindowsAppSDK#6394 (OPEN against 1.8 and 2.0).
Symptoms previously attributed to multiple unrelated Reactor framework
bugs were all manifestations of this one missing file:
- TabView ctor throws FileNotFoundException from
ResourceAccessor::GetLocalizedStringResource.
- NavigationView template-apply throws the same; Reactor's error boundary
renders an error TextBlock instead of the requested content.
- Application.Current.Resources loads empty (0 merged / 0 themed / 0 keys)
because Application.LoadComponent cascades through MRT for type lookups.
- Every ThemeRef.Resolve returns null; .Foreground(Theme.X) falls back to
defaults.
Fix: add a small post-Publish MSBuild target that copies
$(OutputPath)$(AssemblyName).pri and any .xbf files into $(PublishDir).
Scoped to Reactor.AppTests.Host.csproj since that is currently the only
AOT-published exe in the repo; copy verbatim into any other Reactor app
that uses PublishAot=true with WindowsPackageType=None. Remove once
WindowsAppSDK#6394 ships.
Skip-list impact (tests/Reactor.AppTests.Host/SelfTest/SelfTestRunner.cs):
- 108 skips -> 40 skips (net 68 newly-passing AOT fixtures).
- All 60 NATIVE_CRASH entries eliminated.
- Remaining 40 are all ASSERT_FAIL in pre-acknowledged reflection-heavy
subsystems: Devtools/MCP (27), PropertyGrid auto-discovery (9),
ControlUpdate_Collections, CoreCov2_UseObservableTreeHook, and the
two Issue142 third-party XAML metadata fixtures.
Verification:
- JIT selftest: 735/735 fixtures, 2614 ok, 0 not_ok, 0 bail (unchanged).
- AOT selftest: 735 plan, 695 ran, 40 skipped, 0 not_ok, 0 bail.
- Reactor.Tests unit suite: Passed 8381, Failed 0, Skipped 46 (Yoga
generated, pre-existing).
- Reactor.csproj builds 0 errors (no library reflection added; library
remains AOT-clean).
Documentation: docs/aot-support.md gets a new "Required publish-time
workarounds" section explaining #6394, plus updates to the works /
does-not-work tables reflecting that built-in WinUI controls and
ThemeRef.Resolve now work under AOT with the workaround in place.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(tests): serialize Use*ThreadingTests via [Collection] to avoid global TaskScheduler.UnobservedTaskException race
The three Use*ThreadingTests classes (UseInfiniteResource, UseMutation,
UseResource) each subscribe to the process-wide
TaskScheduler.UnobservedTaskException event in their constructor and assert
that no unobserved exceptions occurred at the end of every test. With xUnit's
default parallel test scheduling, the three classes race each other: an
unobserved exception raised by a Task originating in one test class can fire
the event handler in any of the others, breaking the AssertNoUnobserved()
invariant.
Symptom in CI: random failures across the family, e.g.
- UseResourceThreadingTests.Fetcher_Observed_Cancellation_Is_Silent_Not_Error (main 3c3bed9)
- UseInfiniteResourceThreadingTests.Refresh_During_InFlight_Cancels_And_Restarts (PR #360)
- UseMutationThreadingTests.Unmount_Cancels_Pending_OnError_Does_Not_Fire (PR #360 rerun)
Fix: tag all three classes with [Collection("UnobservedTaskException")].
xUnit serializes classes that share a collection name, so they no longer run
concurrently and the global event handlers no longer see each other's tasks.
Same convention as the existing [Collection("ConsoleTests")] grouping for
Console.Out/Error-mutating tests.
Verified: 10x stress run of all 19 threading tests, 0 failures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
-**Theming** — `ThemeRef` tokens, style caching. Brushes resolved through `XamlControlsResources` work under AOT once the WindowsAppSDK#6394 publish workaround is in place (see [Required publish-time workarounds](#required-publish-time-workarounds) below).
20
+
-**Built-in WinUI controls** — `NavigationView`, `TabView`, `Pivot`, `TreeView`, `Expander`, `InfoBar`, `MenuBar`, `BreadcrumbBar`, `RefreshContainer`, `TeachingTip`, `ColorPicker`, `NumberBox`, `RatingControl`, `SplitButton`, etc. all mount and update under AOT once WindowsAppSDK#6394 is mitigated. `{TemplateBinding}` against built-in DPs works; private DPs declared in a separate third-party assembly without an `IXamlMetadataProvider` are still broken (Issue #142).
20
21
-**Markdown / Localization (read path)** — `IStringLoc`/`Loc.X.Y` lookups generated by `Reactor.Localization.Generator` are AOT-clean. Source-generated, no runtime reflection.
21
22
22
23
## What does *not* work under AOT (today)
@@ -33,15 +34,48 @@ These subsystems compile cleanly with `IsAotCompatible=true` (the warnings are s
33
34
|**Form validation**|`FormField`'s default editor resolution goes through `TypeRegistry`. Same caveat as PropertyGrid. | Issue #70|
34
35
|**Navigation state JSON**|`NavigationHandle` serializes deep-link state via `JsonSerializer` without a source-generated context. Custom types that ride through navigation state will fail to serialize under AOT. | Issue #70|
35
36
|**Component discovery (`ReactorApp.Run<TApp>` reflection paths)**| The instantiation of `TApp` itself is annotated and works. The devtools-only `--list-components` enumeration scans `Assembly.GetTypes`; that path is gated to non-AOT builds. | Issue #70|
36
-
|**Theme resource lookup (`Theme.X`, `ThemeRef.Resolve`)**|`ThemeRef.Resolve` walks `Application.Current.Resources` + its merged/theme dictionaries. The token records (`Theme.Accent`, `Theme.PrimaryText`, …) construct fine, but at runtime the `XamlControlsResources` entries that `ReactorApplication.xaml` brings in aren't fully populated under AOT — `Resolve` returns `null` for keys that exist under the JIT. Brushes applied via `.Foreground(Theme.X)` will fall back to control defaults. | Issue #70|
37
-
|**XAML-metadata-dependent controls (`NavigationView`, `TabView`, `TemplateBinding`)**|A subset of WinUI controls and the XAML-template parser need richer `IXamlMetadataProvider` data than what's reachable through trimmed AOT publish. `NavigationView` / `TabView` won't even reach `Mount` in this state, and `{TemplateBinding}` against custom DPs can't resolve the DP descriptor (see issue #142 reproductions). Use simpler containers under AOT. | Issue #70|
37
+
|**Theme resource lookup (`Theme.X`, `ThemeRef.Resolve`)**|Works once the WindowsAppSDK#6394 workaround target ships the project `.pri` into the publish output (see [Required publish-time workarounds](#required-publish-time-workarounds)). Reactor's library itself is AOT-clean here. |WindowsAppSDK#6394|
38
+
|**Third-party-assembly XAML metadata (Issue #142 fixtures)**|Built-in WinUI controls work under AOT now (see [Required publish-time workarounds](#required-publish-time-workarounds)). What remains is `{TemplateBinding}` against private DPs in a third-party assembly that ships *no*`.xaml` file: the XAML compiler only emits an `IXamlMetadataProvider` for projects that have at least one `.xaml`, and AOT trims any implicit metadata path. Affected fixtures: `Issue142_CustomControlPrivateDp_Renders`, `Issue142_ThirdPartyControlPrivateDp_Renders`. The library-author workaround is to register a hand-written `IXamlMetadataProvider` via `RegisterControlAssembly`. | Issue #142|
38
39
39
40
## Conventions
40
41
41
42
-**The library compiles AOT-clean.** Builds of `Reactor.csproj` produce zero IL2*/IL3* warnings. New code that reaches for reflection must either be source-generated, annotated with `DynamicallyAccessedMembers`, or — as a last resort — gated behind `[RequiresUnreferencedCode]` / `[RequiresDynamicCode]` so consumers see the warning at the call site.
42
43
-**Suppressions are temporary.** Every `[UnconditionalSuppressMessage("Trimming", ...)]` or `("AOT", ...)` in this repo is a TODO. The justification field names the reflection use; tracking is folded into issue #70.
43
44
-**The benchmark canary.**`tests/stress_perf/StressPerf.Reactor` (and the `StressPerf.Direct`/`ReactorGrid` siblings) set `PublishAot=true`. If they stop publishing, an AOT regression has landed in the framework.
When publishing an unpackaged WinUI 3 app (`WindowsPackageType=None`) with `PublishAot=true`, the WindowsAppSDK build pipeline generates `$(AssemblyName).pri` into the intermediate output but does *not* copy it into the publish directory. The MakePRI step is conditioned on `AppxPackage=true`, so unpackaged apps fall through. Same applies to `.xbf` files generated by the XAML compiler.
51
+
52
+
The missing `.pri` has cascading runtime symptoms that look like separate bugs but all trace back to the same cause:
53
+
54
+
-`TabView`'s constructor throws `FileNotFoundException` from `ResourceAccessor::GetLocalizedStringResource(SR_TabViewCloseButtonTooltipWithKA)`.
55
+
-`NavigationView` mounts but its template-apply throws the same `FileNotFoundException` (Reactor's error boundary catches it and renders an error `TextBlock`).
56
+
-`Application.Current.Resources` loads as an empty dictionary (`0` merged, `0` themed, `0` keys) because `Application.LoadComponent` cascades through MRT for type-info lookups.
57
+
- Every `ThemeRef.Resolve(...)` returns `null`; `.Foreground(Theme.X)` falls back to control defaults.
58
+
59
+
Upstream tracking: [microsoft/WindowsAppSDK#6394](https://github.com/microsoft/WindowsAppSDK/issues/6394) — still OPEN against 1.8 and 2.0.
60
+
61
+
**Workaround** (in `tests/Reactor.AppTests.Host/Reactor.AppTests.Host.csproj`):
Any Reactor app that publishes with `PublishAot=true` and `WindowsPackageType=None` should copy this target verbatim until WindowsAppSDK ships a fix. Remove the target once #6394 closes.
78
+
45
79
## Debugging an AOT selftest hang
46
80
47
81
`tests/Reactor.AppTests.Host` maintains an explicit allow-list of fixtures that hang, crash, or assert-fail under NativeAOT (`SelfTestRunner.DefaultAotSkipPatterns`). When you remove an entry from that list and the published Host hangs, crashes, or asserts instead of producing output, use the following workflow.
0 commit comments