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
{{ message }}
Repository navigation
[Android] Hosting: Trim disabled MAUI Aspire support - #38616
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Dependency
This PR is stacked on #38630, which separately preserves handler constructors referenced through ElementHandlerAttribute<THandler> when trimming is enabled.
Root Cause
Microsoft.Maui.RuntimeFeature.EnableMauiAspire uses the runtime's [FeatureSwitchDefinition] pattern, but normal optimized Android builds previously left _EnableMauiAspire unset. As a result, the linker did not substitute the disabled feature and the guarded environment-variable/dev-tunnel implementation—including the source-generated LocalhostRegex—survived publishing.
Description of Change
Resolve _EnableMauiAspire and add its RuntimeHostConfigurationOption globally from the Core package during MSBuild evaluation, so both Controls and UseMauiCore consumers receive the switch without target-order dependencies.
Keep Aspire enabled for non-optimized builds and substitute it away from optimized builds.
Preserve validation of explicitly configured _EnableMauiAspire values.
Leave Aspire-enabled builds unchanged.
Add focused MSBuild coverage for default disabled/enabled behavior, explicit optimized-build warnings, and the runtime option's Value/Trim metadata.
Add URL rewrite coverage for dev-tunnel behavior.
Update the feature-switch documentation for regular optimized builds and the validation opt-out.
Release Targeting
This stack targets net11.0. If it cannot be accepted after the .NET 11 RC2 snap, it may need to move to .NET 12.
Android Release Measurements
A fresh RC2 full-package build and stock .NET 11 Android arm64 Release partial-trim comparison of baseline 2eed04f873 against the complete stack (00267ae424 + 3bde0d5ec3) plus the XAML/Shadow changes produced:
Output
Baseline
Patched stack
Delta
Signed APK
21,428,303 bytes
21,354,575 bytes
-73,728 bytes
linked Microsoft.Maui.dll
828,416 bytes
820,224 bytes
-8,192 bytes
linked Microsoft.Maui.Controls.dll
1,533,952 bytes
1,513,472 bytes
-20,480 bytes
linked Microsoft.Maui.Controls.Xaml.dll
136,704 bytes
27,648 bytes
-109,056 bytes
In the non-Aspire output, ConfigureEnvironmentVariables is reduced to a return-only stub and LocalhostRegex is absent. An identical Aspire-enabled publish retains the implementation and regex.
System.Text.RegularExpressions.dll remains because the sample still reaches regex through Microsoft.Maui.Controls.ShadowTypeConverter; System.Private.Xml.dll also contains a generated LanguageRegex. This PR removes the MAUI Aspire regex root but does not eliminate the framework assembly entirely.
Validation
DOTNET_ROLL_FORWARD=Major dotnet test src/Core/tests/UnitTests/Core.UnitTests.csproj --no-restore --filter FullyQualifiedName~HostBuilderAppConfigurationTests --logger 'console;verbosity=minimal' -m:1 -p:PublicApiType=Validate — 7 passed on net11.0.
DOTNET_ROLL_FORWARD=Major dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --no-restore --filter 'FullyQualifiedName~MauiAspireFeatureSwitchMatchesOptimization|FullyQualifiedName~ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild' --logger 'console;verbosity=minimal' -m:1 -p:PublicApiType=Validate — 5 passed on net11.0.
Fresh RC2 package builds and arm64 partial publishes succeeded for baseline and the final stack.
The complete two-PR stack passed the combined preview7 XAML + Shadow + Aspire contract validation (8/8).
Linked CheckBoxHandler retains its public parameterless constructor, platform creation, connect, and disconnect methods.
A clean physical-device cold launch remained alive with PID 7613 and no TypeLoadException.
Reusable validation artifacts are at /Users/simon/.copilot/session-state/6e13df7f-1302-47fa-b0fa-6d104a1dc0a8/files/final-stack-validation.
The test currently formats @(RuntimeHostConfigurationOption) as item identities only, so it verifies presence but not the Value or Trim="true" metadata that makes feature-switch substitution work. A regression that emits the wrong value or omits trimming would still pass. Include %(Value) and %(Trim) in the diagnostic message and assert the expected metadata.
The s/agent-review-in-progress label stays on this PR while the run is active. The final recommendation and outcome labels are posted only after Gate, expert review, and Deep UI tests finish.
The s/agent-review-in-progress label stays on this PR while the run is active. The final recommendation and outcome labels are posted only after Gate, expert review, and Deep UI tests finish.
✅ Verified (new API / feature) — this PR adds new API and a test that references it in the same project, so reverting the fix un-compiles the test: there is no valid "fails without the fix" baseline to establish (a compile-coupled baseline). The gate instead verified the fix by a clean build + pass with the fix, so this is a real PASS rather than a non-committal INCONCLUSIVE.
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs(77,52): error CS0117: 'AppHostBuilderExtensions' does not contain a definition for 'ReplaceLocalhost' [/home/vsts/work/1/s/src/Core/tests/UnitTests/Core.UnitTests.csproj]
🟢 With fix — 🧪 HostBuilderAppConfigurationTests: PASS ✅ · 60s
Error-relevant lines (filtered from the build log):
at Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.MauiAspireFeatureSwitchMatchesOptimization(Boolean optimize, String explicitValue, String expected) in /_/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:line 461
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.MauiAspireFeatureSwitchMatchesOptimization(Boolean optimize, String explicitValue, String expected) in /_/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:line 462
at InvokeStub_MSBuildTests.MauiAspireFeatureSwitchMatchesOptimization(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
[xUnit.net 00:00:30.09] 1>/home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
[xUnit.net 00:00:30.09] Build FAILED.
[xUnit.net 00:00:30.09] /home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
at Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.Build(String projectFile, String target, String verbosity, String additionalArgs, Boolean shouldSucceed, String command) in /_/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:line 265
at Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild(String explicitValue) in /_/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:line 483
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
1>/home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
Build FAILED.
/home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
[xUnit.net 00:00:33.64] 1>/home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
[xUnit.net 00:00:33.64] Build FAILED.
[xUnit.net 00:00:33.64] /home/vsts/work/1/s/artifacts/bin/Controls.Xaml.UnitTests/Debug/net11.0/temp/ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild/test.csproj : error MSB4057: The target "_MauiValidateAspireConfiguration" does not exist in the project.
at InvokeStub_MSBuildTests.ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
🛠️ HostBuilderAppConfigurationTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/Core/tests/UnitTests/Hosting/HostBuilderAppConfigurationTests.cs(77,52): error CS0117: 'AppHostBuilderExtensions' does not contain a definition for 'ReplaceLocalhost' [/home/vs...
Issue: N/A - no linked issue PR:#38616 - [Android] Hosting: Trim disabled MAUI Aspire support Platforms Affected: Android is the requested validation platform; the Core build-transitive switch configuration applies to MAUI Core and Controls consumers on all platforms Files Changed: 3 implementation, 2 test, 1 documentation
Key Findings
Optimized builds previously left _EnableMauiAspire unset outside the Controls full-trim/AOT property group, so the FeatureSwitchDefinition could not substitute RuntimeFeature.EnableMauiAspire=false and trim the guarded environment-variable/dev-tunnel path.
The PR moves property resolution, runtime-host option emission, and explicit-override validation to Microsoft.Maui.Core.Before.targets, preserving non-optimized and explicitly enabled behavior while covering UseMauiCore consumers.
The PR is stacked on #38630, but its six-file diff is independently focused on Aspire feature-switch configuration, URL-rewrite coverage, and documentation.
Gate verification already passed the focused HostBuilderAppConfigurationTests and MSBuildTests; no gate verification was rerun.
What this changes: The PR moves _EnableMauiAspire defaulting and RuntimeHostConfigurationOption emission from the Controls-only linker-preparation target into Microsoft.Maui.Core.Before.targets. This gives both Controls and UseMauiCore consumers an explicit feature-switch value during evaluation: true when Optimize is not true, false when Optimize is true, while preserving an explicitly supplied value. It also relocates MA002 validation, exposes ReplaceLocalhost internally for focused tests, and updates documentation.
Inferred motivation: Optimized Android builds that do not use full trimming or AOT previously left the runtime feature switch unset, so the linker could not substitute RuntimeFeature.EnableMauiAspire=false and remove the guarded environment-variable and dev-tunnel code.
Approach Assessment
The approach is sound. Core build-transitive evaluation is the appropriate shared producer because it does not depend on Controls target execution and reaches Core-only consumers. Capturing whether _EnableMauiAspire was already populated before applying defaults preserves the distinction needed by MA002. No simpler change in the former Controls target would provide equivalent consumer coverage without retaining the ordering dependency.
Trim/AOT Evidence Chain
Link
Source evidence
Reachability implication
Build-time value
Microsoft.Maui.Core.Before.targets resolves _EnableMauiAspire and emits Microsoft.Maui.RuntimeFeature.EnableMauiAspire with Trim="true".
Optimized builds provide the linker an explicit false substitution; non-optimized or explicitly enabled builds retain true.
Runtime switch
RuntimeFeature.EnableMauiAspire carries FeatureSwitchDefinition and reads the matching AppContext key.
The runtime property is the recognized substitution point rather than merely relying on its ordinary default of true.
Guarded consumer
ConfigureEnvironmentVariables returns before environment propagation and dev-tunnel rewriting when the switch is false.
The Aspire-specific branch, including LocalhostRegex, becomes unreachable in the disabled trim configuration.
Tests
MauiAspireFeatureSwitchMatchesOptimization checks the effective property plus runtime option Value and Trim; ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild checks explicit-value validation.
The focused tests cover default disabled/enabled behavior, explicit override, metadata, and warning behavior.
MSBuild Evaluation-Order Assessment
The relocation is ordering-correct for the tested package import contract: configuration and item emission occur together in Core's .Before.targets, before downstream targets consume RuntimeHostConfigurationOption. The explicitness marker is captured before either default assignment mutates _EnableMauiAspire, and the target-time warning reads that immutable marker. No actionable ordering defect was identified.
Findings
No actionable inline findings. inline-findings.json contains an empty JSON array.
Failure-Mode Probing
Core-only consumer: Core's build-transitive import now emits the switch without requiring Controls targets.
Regular optimized build (TrimMode=partial): The value resolves to false, and the runtime option remains present with Trim=true.
Explicit true or false in an optimized build: The authored value is preserved and MA002 is emitted unless validation is intentionally disabled.
Non-optimized build: The default remains true, preserving Aspire environment propagation.
No Aspire usage at runtime: The change only supplies a configuration value; disabled builds return at the existing runtime guard and introduce no new startup state or subscriptions.
External Output Contract
Not applicable.
Verdict: NEEDS_DISCUSSION
Confidence: low Summary: The expert review found the submitted implementation correct and found no code-level error, warning, or discussion item. Its formal verdict is capped only because current required-check state was unavailable/undetermined to the reviewer; the trusted workflow Gate supplied for this comparison independently passed.
🛠️ Try-Fix — Analysis & Comparison
Fix Candidates
#
Source
Approach
Test Result
Files Changed
Notes
1
gpt-5.3-codex try-fix
Compute consumer-local Aspire defaults at execution time instead of snapshotting explicitness during evaluation
⚠️ BLOCKED
0 files
Baseline protocol rejected unrelated pre-existing tracked changes; no implementation or tests ran
2
gpt-5.6-sol try-fix
Use an atomic <Choose> decision record containing the effective Aspire value and whether it was explicit
✅ PASS — 5/5 primary, 7/7 regression
3 files
One pre-test MSBuild ordering flaw was corrected; final inline self-review was clean
Configure and validate the Aspire feature switch globally from Core build-transitive evaluation
✅ PASSED (Gate)
3 implementation files
Original PR
Candidate Details
Candidate 1: Proposed consumer-local, execution-time defaulting and warning validation after optimization properties settle. It intentionally avoided the PR's Core evaluation-time explicitness snapshot, but EstablishBrokenBaseline.ps1 rejected the primary worktree's unrelated pre-existing tracked changes. No files were edited and neither focused test command ran. Full record: try-fix-1/content.md.
Candidate 2: Used an evaluation-time <Choose> to atomically select _MauiAspireConfigurationValue and _MauiAspireConfigurationSource, then projected the value into _EnableMauiAspire and RuntimeHostConfigurationOption. This avoids both the PR's mutable-property snapshot and Candidate 1's target-time consumer mutation. The focused Aspire MSBuild tests passed 5/5 and HostBuilderAppConfigurationTests passed 7/7 after one permitted correction for the clean detached worktree's missing build-task link. The final candidate had zero inline self-review findings and restoration reported Restored True. Full record and diff: try-fix-2/content.md.
Cross-Pollination
Model
Round
New Ideas?
Details
N/A
N/A
N/A
Skipped by STEP 5a execution contract
Exhausted: Yes — the two-candidate STEP 5a cap was reached; no cross-pollination or follow-up agents were permitted. Selected Fix: PR's fix — Candidate 2 is a valid passing alternative, but the PR achieves the same tested defaults, explicit-override provenance, warning behavior, and Core/Controls coverage with a smaller and more conventional conditional-property implementation than the 38-line <Choose> decision record.
🏁 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Winning candidate
Winner:pr
The submitted PR is the strongest candidate. It fixes the missing Aspire feature-switch substitution at the shared Core build-transitive layer, preserves explicit overrides and MA002 provenance, and does so with a smaller conventional conditional-property implementation than the passing alternative. The trusted Gate passed, the expert reviewer found no actionable code findings, and the regression cross-reference was clean.
Comparative ranking
Rank
Candidate
Implementation
Validation
Expert assessment
Result
1
pr
Resolve _EnableMauiAspire in Microsoft.Maui.Core.Before.targets, emit the runtime option globally, and retain explicit-value validation.
✅ Trusted Gate passed: the focused behavior failed without the fix and passed with it; submitted Core and MSBuild coverage passed.
No errors, warnings, suggestions, or code-level discussion items.
Winner
2
pr-plus-reviewer
Byte-for-byte equivalent to pr; the expert supplied no actionable feedback, so reviewer.patch is empty.
✅ No separate run required because no source changed; inherits the same trusted Gate evidence as pr.
No improvement over the submitted implementation.
Valid tie, ranked below the already-submitted candidate
3
try-fix-2
Use a <Choose> decision record to select an effective value and Explicit/Default source, then project those into the compatibility property and runtime item.
✅ 5/5 primary Aspire MSBuild cases and 7/7 Core regression cases passed after the documented one-time harness correction.
Correct final implementation with zero self-review findings.
Valid alternative, but more verbose with no demonstrated correctness advantage
4
try-fix-1
Proposed consumer-local target-time defaulting after optimization properties settle.
⚠️ Blocked before implementation or testing by the baseline protocol; produced no code diff.
No implementation available to assess empirically.
Lowest-ranked because regression behavior was not validated
Why pr wins
Correct shared scope: Core's build-transitive .Before.targets reaches both Controls and UseMauiCore consumers and removes the former Controls-target ordering dependency.
Sound trim contract:RuntimeFeature.EnableMauiAspire is a FeatureSwitchDefinition; the emitted RuntimeHostConfigurationOption carries the resolved value with Trim="true", and the existing runtime guard isolates the removable environment/dev-tunnel path.
Preserved behavior: Non-optimized builds remain enabled, optimized builds default disabled, explicit values are retained, and explicitly configured optimized builds still receive MA002 unless validation is intentionally disabled.
Least complexity:try-fix-2 reaches the same tested contract through a 38-line <Choose> record and two extra intermediate properties. That separation is defensible, but no failure in the PR's capture-before-default pattern was found, so the additional mechanism is not justified.
Expert review reconciliation
The expert review's formal NEEDS_DISCUSSION/low-confidence label was caused solely by its inability to determine current required-check status. It identified no code defect or unresolved design question and wrote an empty inline-findings.json. For this comparison, the caller supplied a trusted Gate result of PASSED, satisfying the approval gate for the raw PR candidate.
Candidate artifacts
pr-plus-reviewer/reviewer.patch is empty because no reviewer change was warranted.
pr-plus-reviewer/candidate.patch contains the submitted six-file candidate diff against the pipeline PR base and excludes the trusted infrastructure overlay.
No candidate failed an executed regression test. The blocked, untested try-fix-1 is ranked below all passing candidates as required.
📱 UI Tests — Button,Label,Layout
Detected UI test categories:Button,Label,Layout
✅ Deep UI tests — 422 passed, 0 failed, 7 skipped across 3 categories on platform-pool agent (replaces in-process counts above).
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
## Summary
- create XML marshalling metadata references lazily in generated `[Export]` dispatch assemblies
- avoid adding `System.Xml.ReaderWriter` to a generated type-map assembly when exported signatures do not use XML parser mappings
- preserve the existing XML marshalling references when `XmlPullParser` or `XmlResourceParser` mappings are used
## APK size and combined MAUI validation
The PR is draft because the stock MAUI `sample-content` application does not currently demonstrate an APK-size or packaged-assembly reduction from this change.
The first stock .NET 11 Android CoreCLR Release comparison produced an identical signed APK before and after this change:
| Build | Signed APK size |
|---|---:|
| Baseline | 23,300,175 bytes |
| Patched | 23,300,175 bytes |
| Difference | **0 bytes** |
Three independent MAUI trimming fixes were then applied together:
- dotnet/maui#38613 trims unused runtime XAML loader/parser code
- dotnet/maui#38615 removes `ShadowTypeConverter`'s regex dependency
- dotnet/maui#38616 trims disabled MAUI Aspire support and its generated localhost regex
The combined MAUI changes reduced the sample-content signed APK by 81,920 bytes, removed `XamlLoader`, `XamlParser`, and `LocalhostRegex`, and reduced the retained XML/regex implementations. However, `System.Xml.ReaderWriter` remains rooted directly by source-generated XAML line-info calls in the application, and `System.Private.Xml` remains transitively and through additional framework/control references.
To exercise this PR's actual code path, the identical combined-MAUI input was then published twice with the trimmable Android type map explicitly enabled:
```sh
dotnet publish CombinedTrimApp.csproj \
-c Release -f net11.0-android -r android-arm64 \
-p:AndroidKeyStore=false \
-p:PublishReadyToRun=false \
-p:TrimMode=partial \
-p:_AndroidTypeMapImplementation=trimmable \
-p:AndroidPackageFormats=apk
```
| Output | Baseline generator | Patched generator | Difference |
|---|---:|---:|---:|
| Signed APK | 21,248,079 B | 21,248,079 B | **0 B** |
| Unsigned APK | 21,118,994 B | 21,119,021 B | +27 B packaging noise |
| Generated type-map DLLs | 11,133,952 B | 11,133,952 B | **0 B** |
| `System.Xml.ReaderWriter.dll` | 5,120 B | 5,120 B | **0 B** |
| `System.Private.Xml.dll` | 381,440 B | 381,440 B | **0 B** |
All 86 generated type-map DLLs were byte-identical, and none had a `System.Xml.ReaderWriter` assembly reference. The sample-content app and its dependencies do not contain a non-XML `[Export]` dispatcher that triggers the bug fixed here. Its remaining `System.Xml.ReaderWriter` reference comes from generated XAML `InitializeComponent` line-info services (`System.Xml.IXmlLineInfo`), outside the Android type-map generator.
Therefore this change is valid generator cleanup and is covered by focused tests, but it does **not** currently reduce the stock MAUI sample-content APK or remove its XML assemblies. The benefit applies only to an app/library that generates a non-XML `[Export]` dispatcher and has no independent XML roots.
## Tests
- `dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj --no-restore --nologo -v:minimal`
- targeted `TypeMapAssemblyGeneratorTests.Generate_Export*` coverage
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Description of Change
Fix the missing Helix unit-test fixture and the independent device-test
failures uncovered while validating this PR on `net11.0`.
#### Helix correlation payload
The Aspire feature-switch tests added by #38616 import
`src/Core/src/nuget/buildTransitive/Microsoft.Maui.Core.Before.targets`,
but `PrepareMauiHelixPayload` did not stage that file. Both test methods
fail before their assertions run: three cases of
`MauiAspireFeatureSwitchMatchesOptimization` and two cases of
`ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild`, in Windows/macOS
Debug and Release.
- Copy the required Core targets file into the existing source
correlation payload, preserving its source-relative path.
- Add Pester regression coverage that executes the actual staging target
in an isolated temporary directory, checks the correlation payload
destination, and compares staged file hashes with their sources.
- Add `eng/helix.proj` to the existing PowerShell test workflow's path
triggers.
#### Device-test trimming and synchronization
- Move the handler constructor API-shape check from trimmed device
applications to untrimmed Core unit tests. Unused constructor overloads
may legitimately disappear from a deployed application. The new theory
checks public concrete Core view handlers, excluding
`SwipeItemViewHandler`, whose mapper constructors are intentionally
protected.
- Preserve the `AbsoluteLayout` parameterless constructor used
indirectly by `HandlerDoesNotLeak`. The CI-linked Mono assembly
contained the type but no constructor, so the test failed before its
leak assertions.
- Preserve the protected `RequestManager.Glide` property used by
`GlideStaticEqualsGlideGet`, look it up on its declaring type, and
assert that reflection found it before invoking it. The CI APK had
trimmed the property. The singleton comparison is unchanged.
- Wait for the iOS flyout header to reach its minimum height using the
bounded `AssertEventually` helper. The previous assertion assumed UIKit
completed the header layout within the scroll helper's 10 ms delay. The
expected height (100 px) and tolerance (0.3 px) are unchanged; no
product layout code changes.
- Declare the existing Windows
`MapHandler.DisconnectHandler(FrameworkElement)` override in
`PublicAPI.Unshipped.txt`. While this PR was being validated, #38844
restored the Windows map implementation on `net11.0` but omitted that
declaration, causing RS0016 in both target-branch build 1611088 and this
PR's build 1611131. The updated base is incorporated by a normal merge,
and **both Windows map lifecycle tests remain enabled**. The earlier
temporary skips have been removed.
- Wait for the native Windows Map's embedded document to create its
layers and pin before the lifecycle tests close their temporary window.
The exact crash dump identifies `MapControl::OnLayerAdded` awaiting
`addSymbolLayer()` after `InitializeWebMap`; `NavigationCompleted` fires
before those continuations finish. The bounded waits observe local
script state, not authenticated map tiles, and preserve the existing
native-layer ownership assertions.
#### Android intent isolation and Windows execution reporting
- Scope Android explicit-component navigation fixtures to the test
package and a custom, unhandled action. After production correctly
removes the explicit component, the old broad implicit intent could open
the system resolver or Google Backup settings and leave the test
activity stopped. Subsequent layout and lifecycle-bound back-callback
tests then failed. Preserve the explicit-target launch monitor and
assert that the original `ComponentActivity` is `RESUMED` before and
after navigation, on the UI thread. Do not use `HasWindowFocus` as a
lifecycle check: system overlays can take focus without pausing the
activity.
- Select Windows MSIX dependencies by their actual `Dependencies/x64`
directory rather than matching `x64` anywhere in the full path. The
parent package directory already contains `x64`, so the old filter also
selected arm64, win32, and x86 dependencies. Install the app with the
selected dependency paths and emit deployment logs on failure.
- Fail Windows work items for abnormal packaged/unpackaged process exits
or empty/malformed xUnit files. The old Map category crashed with an
empty XML file, but the runner ignored it and reported a false green.
Valid category results are still merged and uploaded; ordinary WinUI
exit codes `0` and `-1` remain supported.
- Retain the packaged process handle before waiting and read its exit
code through the explicit getter, then dispose the handle. On Windows
PowerShell's .NET Framework, `WaitForExit` alone does not retain the
handle that `ExitCode` requires. The initial reporting change therefore
read an empty property and incorrectly failed otherwise passing packaged
suites. Unreadable exit codes still fail explicitly rather than being
treated as success.
The final diff introduces **no test skips or product behavior changes**.
Trimming remains enabled; only the exact indirectly used members are
rooted. The Windows API entry records an implementation already present
on the target branch rather than introducing a new API. No new pipeline
tests were added for these follow-ups; runner changes use syntax checks
and actual CI.
### Issues Fixed
Resolves the Helix payload failure observed on #38806 in [build
1608731](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1608731).
The same missing-file failures occur on the `net11.0` baseline in [build
1607883](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1607883).
Also addresses the independent failures in this PR's [device build
1609895](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1609895),
which also affected target-branch [build
1609516](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1609516).
The recurring Glide failure is tracked in #38240.
The subsequent Windows compile error is reproduced in target-branch
[build
1611088](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611088)
and PR [build
1611131](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611131).
Android isolation, Windows execution-reporting problems, and the native
Map initialization race were uncovered in [device build
1611162](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611162).
### Validation
- Original staging regression failed before the payload fix; all **109**
Pester tests passed afterward. Debug and Release staging produced
byte-identical Core targets files.
- Main [build
1609883](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1609883)
succeeded. Actual XAML results confirmed all **20 affected executions**
passed across Windows/macOS and Debug/Release.
- Pinned RC2 SDK: **41 focused Core unit tests passed**, including the
migrated constructor contracts. Earlier main build 1611161 also confirms
all **32 constructor theory cases** in the inspected macOS unit leg.
- Android 13, CoreCLR Release with partial trimming: **49 image-source
tests passed**, including `GlideStaticEqualsGlideGet`. Linked metadata
confirms the protected property/getter is preserved. Device build
1611162 confirms the Glide case passes on **both Mono and CoreCLR**.
- iOS 18.5, Mono Release with partial trimming: the exact CI app and the
pre-change local app reproduced collapsed-header failures. With the
condition-based assertion, **442 Shell tests passed in each of three
consecutive local runs**. Device build 1611162 confirms **442/442 Shell
cases on each of Mono and CoreCLR iOS 18**, including all flyout-header
cases.
- The same trimmed local iOS app passed **190 memory tests**, with 3
existing skips. All inherited `AbsoluteLayout` leak cases passed; linked
metadata confirms the constructor is preserved. Device build 1611162
also confirms all three inherited cases in Mono iOS general tests.
- The earlier Android window-focus guard failed both old broad-intent
cases. With the isolated fixtures, the complete Release Mono API 33
Blazor suite passed **62 tests with 4 existing skips**, including all
seven cases that failed in CI. The subsequent lifecycle-check correction
and its additional positive/negative evidence are described below.
- Windows [device build
1611496](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611496),
Helix job `073cf090-5d99-4ee4-acf8-afaf7066d8bb`, confirms **both Map
facts pass in both packaged and unpackaged execution**: 2/2 in each
mode, no skips. The disconnect test takes 1.89s/2.01s and the
remove/re-add test 2.31s/2.46s. All five unpackaged applications pass.
- The same Windows run successfully installs and executes all five
packaged applications. Complete result XML contains **3,249 passed, 160
existing skips, zero failed tests, and zero errors**. The packaged work
items were incorrectly red only because the launcher could not read
`ExitCode`; commit `8768da7c59` fixes the retained-handle requirement.
- Packaged runner and embedded PowerShell syntax checks pass. Changed C#
files are formatted; `git diff --check` passes. The Map follow-up also
compiles against the 245 managed assemblies extracted from the exact
failing CI payload.
- Before the latest push, [device build
1611589](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611589),
head `8768da7c59`, produced **42 complete Apple XML files**, correlated
through its own submission logs and fully audited: **26,480 passed, 720
existing skips, zero failures/errors**, across Mono/CoreCLR iOS 18/26
and CoreCLR Mac 15/26.
- All **10 Android XML files** from that run were also correlated and
audited. Core, Controls, Essentials and Graphics passed on both
runtimes. Blazor Mono was **62 passed, 4 skipped**. Blazor CoreCLR was
**60 passed, 2 failed, 4 skipped**: only the old window-focus guards
failed; all seven original cascading cases passed.
- Commit `065fe7c97a` replaces those false-positive guards with
lifecycle assertions. Opening the notification shade locally reproduces
exactly the two old failures: the activity lacks window focus but
remains `RESUMED` before and after navigation. With the correction, the
complete Release API 33 suite passes with the shade open on **both Mono
and CoreCLR: 62 passed, 4 existing skips each**. A separate controlled
run opens Settings only after the navigation request; the corrected
guard rejects real backgrounding (`RESUMED` expected, `CREATED` actual),
and all seven original cascading failures return. A final normal CoreCLR
run again passes **62 tests with 4 existing skips**.
- Current-head [device build
1611714](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611714),
own submission log 149, confirms Windows job
`f6501433-8b24-401b-ae86-311a339062ab`: **all ten real
packaged/unpackaged application work items passed**. All ten complete
XML files total **6,498 passed, 320 existing skips, zero
failures/errors** (3,249 passed/160 skipped per mode). Both Map facts
pass in each mode. All **52 packaged process launches** correctly report
native exit code `-1` and launcher exit code `0`, providing runtime
proof of the retained-handle correction.
- The same current-head build's full Apple matrix also passed: **42
complete XML files, 26,480 passed, 720 existing skips, zero
failures/errors**. All six jobs are correlated through this build's own
submission logs 125, 133 and 141.
- Current-head Android CI also passes on **both Mono and CoreCLR**. Each
full Blazor suite is **62 passed, 4 existing skips**, including both
formerly failing explicit-component theory rows and all seven original
cascading cases. All ten Android result files are complete and total
**9,238 passed, 176 existing skips, zero failures/errors**, correlated
through this build's own submission logs 157 and 165.
**Device verification complete:** [Device build
1611714](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611714)
succeeded on published head `065fe7c97a` at 2026-09-25 03:25:54 UTC. All
**62 real work items** across nine Helix jobs passed. All **62 complete
XML files** were audited: **42,216 passed, 1,216 existing skips, zero
failures and zero assembly errors**. Counts are test executions across
lanes, not unique tests. The successful run was requested in [comment
5825614265](#38827 (comment)).
**Main verification complete:** [Main build
1611713](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1611713)
succeeded on the same published head at 2026-09-25 04:47:55 UTC. All
four platform/configuration build legs and the integration matrix
passed. Its four unit-test Helix jobs are correlated through own
submission logs 200 and 208; all **44 real test work items** and four
controllers passed. The final Windows Build integration leg reports
**108 passed, 2 existing skips, zero failures**. The PowerShell Script
Tests workflow, Build Analysis and Build Insights also succeeded. Both
pipelines are now verified green; no further rerun is needed on this
unchanged head.
The previous run's ten Windows work items remained unstarted for more
than 90 minutes and were canceled after the push; their artificial
`-1234` results are not application crashes or test execution. All
completed results were preserved before publishing the Android
correction. The replacement run above now provides actual Windows proof;
no queue outage is claimed for the earlier wait.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
### Root Cause
`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.
### Broader goal and related work
This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.
Related work toward that goal includes:
- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.
Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.
### Description of Change
Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.
The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.
Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.
The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.
### Performance considerations
Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.
### Tests
- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.
### Android size measurement
Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.
| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |
`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.
### Issues Fixed
None.
---------
Co-authored-by: Copilot App <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
s/agent-changes-requestedAI agent recommends changes - found a better alternative or issuess/agent-fix-pr-pickedAI could not beat the PR fix - PR is the best among all candidatess/agent-gate-passedAI verified tests catch the bug (fail without fix, pass with fix)s/agent-reviewedPR was reviewed by AI agent workflow (full 4-phase review)
4 participants
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.
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Dependency
This PR is stacked on #38630, which separately preserves handler constructors referenced through
ElementHandlerAttribute<THandler>when trimming is enabled.Root Cause
Microsoft.Maui.RuntimeFeature.EnableMauiAspireuses the runtime's[FeatureSwitchDefinition]pattern, but normal optimized Android builds previously left_EnableMauiAspireunset. As a result, the linker did not substitute the disabled feature and the guarded environment-variable/dev-tunnel implementation—including the source-generatedLocalhostRegex—survived publishing.Description of Change
_EnableMauiAspireand add itsRuntimeHostConfigurationOptionglobally from the Core package during MSBuild evaluation, so both Controls andUseMauiCoreconsumers receive the switch without target-order dependencies._EnableMauiAspirevalues.Value/Trimmetadata.Release Targeting
This stack targets
net11.0. If it cannot be accepted after the .NET 11 RC2 snap, it may need to move to .NET 12.Android Release Measurements
A fresh RC2 full-package build and stock .NET 11 Android arm64 Release partial-trim comparison of baseline
2eed04f873against the complete stack (00267ae424+3bde0d5ec3) plus the XAML/Shadow changes produced:Microsoft.Maui.dllMicrosoft.Maui.Controls.dllMicrosoft.Maui.Controls.Xaml.dllIn the non-Aspire output,
ConfigureEnvironmentVariablesis reduced to a return-only stub andLocalhostRegexis absent. An identical Aspire-enabled publish retains the implementation and regex.System.Text.RegularExpressions.dllremains because the sample still reaches regex throughMicrosoft.Maui.Controls.ShadowTypeConverter;System.Private.Xml.dllalso contains a generatedLanguageRegex. This PR removes the MAUI Aspire regex root but does not eliminate the framework assembly entirely.Validation
DOTNET_ROLL_FORWARD=Major dotnet test src/Core/tests/UnitTests/Core.UnitTests.csproj --no-restore --filter FullyQualifiedName~HostBuilderAppConfigurationTests --logger 'console;verbosity=minimal' -m:1 -p:PublicApiType=Validate— 7 passed onnet11.0.DOTNET_ROLL_FORWARD=Major dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --no-restore --filter 'FullyQualifiedName~MauiAspireFeatureSwitchMatchesOptimization|FullyQualifiedName~ExplicitMauiAspireFeatureSwitchWarnsInOptimizedBuild' --logger 'console;verbosity=minimal' -m:1 -p:PublicApiType=Validate— 5 passed onnet11.0.CheckBoxHandlerretains its public parameterless constructor, platform creation, connect, and disconnect methods.TypeLoadException.Reusable validation artifacts are at
/Users/simon/.copilot/session-state/6e13df7f-1302-47fa-b0fa-6d104a1dc0a8/files/final-stack-validation.Issues Fixed
N/A