Skip to content

Remove IsTrimmable=false from assemblies that are already AotCompatible#34573

Merged
PureWeen merged 14 commits into
dotnet:net11.0from
kotlarmilos:dev/miloskotlar/remove-istrimmable-false
Apr 30, 2026
Merged

Remove IsTrimmable=false from assemblies that are already AotCompatible#34573
PureWeen merged 14 commits into
dotnet:net11.0from
kotlarmilos:dev/miloskotlar/remove-istrimmable-false

Conversation

@kotlarmilos

@kotlarmilos kotlarmilos commented Mar 19, 2026

Copy link
Copy Markdown
Member

Description

These four assemblies already set IsAotCompatible=true (which implies IsTrimmable=true via the SDK's Microsoft.NET.Publish.targets), but the explicit IsTrimmable=false overrides that and prevents them from being trimmed in SdkOnly link mode.

The IsTrimmable=false was added in PR #10647 to lock down the value, but it was never updated when these assemblies were made AOT-compatible in PRs #21076 and #21505.

Removing IsTrimmable=false lets IsAotCompatible=true take effect, allowing these assemblies to be trimmed in SdkOnly link mode.

Affected assemblies:

  • Microsoft.Maui (Core.csproj)
  • Microsoft.Maui.Controls (Controls.Core.csproj)
  • Microsoft.Maui.Graphics (Graphics.csproj)
  • Microsoft.Maui.Controls.Xaml (Controls.Xaml.csproj)

Context

While working on reducing Debug bundle size for CoreCLR R2R builds in dotnet/macios (dotnet/macios#24909), we found these assemblies are the main ones not being trimmed in SdkOnly mode.

These assemblies already set IsAotCompatible=true (which implies
IsTrimmable=true via the SDK), but the explicit IsTrimmable=false
overrides that and prevents them from being trimmed in SdkOnly mode.

The IsTrimmable=false was added in PR dotnet#10647 (Oct 2022) to lock down
the value, but it was never updated when these assemblies were made
AOT-compatible in PRs dotnet#21076 and dotnet#21505 (2024).

Removing IsTrimmable=false lets IsAotCompatible=true take effect,
allowing these assemblies to be trimmed in SdkOnly link mode. This
reduces bundle size for both Debug and Release builds.

Affected assemblies:
- Microsoft.Maui (Core.csproj)
- Microsoft.Maui.Controls (Controls.Core.csproj)
- Microsoft.Maui.Graphics (Graphics.csproj)
- Microsoft.Maui.Controls.Xaml (Controls.Xaml.csproj)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 19, 2026 15:49
@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34573

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34573"

@kotlarmilos kotlarmilos changed the base branch from main to net11.0 March 19, 2026 15:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns trimming behavior with existing AOT-compatibility declarations by removing explicit IsTrimmable=false overrides from key MAUI assemblies, so IsAotCompatible=true can cause them to be treated as trimmable (notably impacting SdkOnly link mode scenarios).

Changes:

  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui (Core).
  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui.Controls and Microsoft.Maui.Controls.Xaml.
  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui.Graphics.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Core/src/Core.csproj Removes the explicit IsTrimmable=false override so the existing non-netstandard IsAotCompatible=true can drive trimming.
src/Controls/src/Core/Controls.Core.csproj Same: drops the override to allow trimming in configurations where IsAotCompatible=true is already set.
src/Controls/src/Xaml/Controls.Xaml.csproj Same: removes IsTrimmable=false to let IsAotCompatible=true imply trimming for applicable TFMs.
src/Graphics/src/Graphics/Graphics.csproj Same: removes IsTrimmable=false so trimming can apply when IsAotCompatible=true is set.

You can also share your feedback on Copilot code review. Take the survey.

@rolfbjarne

Copy link
Copy Markdown
Member

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@kotlarmilos

kotlarmilos commented Mar 24, 2026

Copy link
Copy Markdown
Member Author

CI failures (ui tests timeouts, mobile device test crashes) are pre-existing net11.0 baseline issues unrelated to this PR

@PureWeen

Copy link
Copy Markdown
Member

Shell MenuItem Trimming Regression Analysis

Investigation of the 34 VerifyShellFlyout_* Android test failures across all 6 Shell test runs.

Root Cause

Removing IsTrimmable=false makes MAUI assemblies eligible for ILLink trimming in Android Release builds (SdkOnly link mode). This causes Shell MenuItem elements to silently disappear from the flyout drawer — no runtime exception, no MissingMethodException, the items simply never render.

Evidence from ADO Test Attachments (Build 1344675)

Logcat analysis of Order 1 test (VerifyShellFlyout_Header, 1.3MB logcat):

Step Result
Navigate to Shell Feature Matrix page ✅ Found GoToTestButton, SearchBar
Tap ShellFlyoutButton → Shell page loads
Tap Options → FlyoutHeader → Apply ✅ All elements found and tapped
Open flyout drawer (hamburger icon) "Open navigation drawer" found
WaitForElement("Header") in flyout ✅ Found — drawer is open
WaitForElement("OpenFlyout") MenuItem 0 matches by ID and XPath

Page source XML at failure shows what is rendered vs what is not:

✅ Renders in flyout ❌ Missing from flyout
Header (FlyoutHeader) OpenFlyout (MenuItem)
FlyoutItems label FlyoutBehavior (MenuItem)
Home, Reports, Options (ShellContent tabs) Any MenuItem-type elements

No managed exceptions in logcat — no TypeLoadException, MissingMethodException, or MissingMemberException. The app runs fine, Shell navigation works, 152 other Shell tests pass. Only the MenuItem rendering path is broken.

Failure Pattern

  • 34 tests fail — all reference OpenFlyout AutomationId (a <MenuItem>)
  • 5 tests pass — the only ones that never touch OpenFlyout
  • 100% reproducible across all 6 Android Shell runs
  • Order 1 fails with NullReferenceException at Tap(OpenFlyout); Orders 2-34 cascade with TimeoutException

Suspected Trimmed Code Paths

The linker likely trims code in the MenuItem→flyout rendering pipeline:

  • Shell.cs line 694: if (bo is IMenuItemController) — conditional template selection for MenuItems
  • Shell.cs line 884: case MenuItem m: — MenuItem activation handler
  • ShellFlyoutItemsManager.AddMenuItems() — adds MenuItem items to the flyout collection
  • MenuShellItem (internal) — wrapper created via implicit operator
  • BaseShellItem.CreateDefaultFlyoutItemCell() — DataTemplate with Grid/Label/Image used for flyout item rendering

Shell navigation items (ShellContent, ShellSection) render through a different code path than MenuItem, which is why tabs work but MenuItems don't.

Recommendation

The PR may need [DynamicDependency] or [DynamicallyAccessedMembers] annotations on the MenuItem rendering paths to preserve them through ILLink, or the trimming scope needs to be narrowed for Shell-related assemblies.

kotlarmilos and others added 2 commits March 25, 2026 10:06
MenuShellItem is an internal type that wraps MenuItem for flyout rendering.
When MAUI assemblies become trimmable (IsTrimmable=true), the ILLink trimmer
can strip MenuShellItem and related code paths that are only reached through
event-driven and interface-dispatch patterns the linker cannot statically trace.

Add [DynamicDependency] on Shell constructor and ShellItem implicit operator
to preserve MenuShellItem and its members through trimming.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos

Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

Add [UnconditionalSuppressMessage] for IL2026, IL2111, and IL3050 on
Shell() constructor and ShellItem.op_Implicit(MenuItem). The warnings
originate from the TypeConverter base class hierarchy
(ShellItemConverter : TypeConverter) whose inherited members like
GetProperties/GetEditor carry RequiresUnreferencedCode. These base
methods are never called by MAUI - ShellItemConverter only overrides
CanConvertFrom/To and ConvertFrom/To.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…:kotlarmilos/maui into dev/miloskotlar/remove-istrimmable-false
@kotlarmilos

Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@PureWeen

Copy link
Copy Markdown
Member

/rebase

@PureWeen

Copy link
Copy Markdown
Member

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@kotlarmilos

Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@kotlarmilos

Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

Two regressions vs net11.0 baseline were observed on PR dotnet#34573:

1. Android Core CoreCLR: ImageSourceServiceTests.ReleasingImageSourceReturnsDifferentBitmap
   asserts that a bitmap is collectible after Dispose. Trim/AOT codegen can keep locals
   rooted across the GC fence, causing Glide's MemoryCache to retain the bitmap.
   Fix: hoist the load+dispose into a [MethodImpl(NoInlining)] helper so the
   IImageSourceServiceResult/Drawable locals leave scope before TryCollectFile runs.
   This mirrors the established AOT-friendly memory test pattern in MAUI
   (see UIViewSubclassTests / CALayerAutosizeToSuperLayerBehaviorTests).

2. Windows Controls.DeviceTests-packaged hang (2h timeout, exit -3): cannot be
   reproduced or root-caused without a Windows machine. Conservatively keep
   IsTrimmable=false on Microsoft.Maui.Controls.Xaml until the hang is rooted
   out; Core, Controls.Core and Graphics still get their trim opt-in from this PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos

Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@kotlarmilos

Copy link
Copy Markdown
Member Author

@PureWeen Ready for review, the remaining maui-pr-devicetests and maui-pr-uitests failures are BlazorWebView Android tests hitting "Failed to fetch", and infrastructure osx.26.arm64.open queue issues.

@PureWeen PureWeen merged commit 464599f into dotnet:net11.0 Apr 30, 2026
198 of 216 checks passed
@github-actions github-actions Bot added this to the .NET 11.0-preview4 milestone Apr 30, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators May 31, 2026
@PureWeen PureWeen added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label Jun 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants