feat: add .NET 11 support and drop .NET 8 and .NET 9 - #5529
feat: add .NET 11 support and drop .NET 8 and .NET 9#5529jamescrosswell wants to merge 18 commits into
Conversation
Shifts the centralized TFM properties up one release: net11.0 becomes the latest TFM and net10.0 the previous, dropping net8.0 and net9.0 (both lose LTS when .NET 11 ships in November). Platform TFMs follow the preview 7 workload set (11.0.100-preview.7.26410.2): android 37.0.0-preview.7.2131, iOS/MacCatalyst/macOS 26.5.11997-net11-p7. Note iOS and MacCatalyst only ship a net11.0_26.5 ref pack, so those TFMs need the minor version, unlike the current net10.0-ios26. Samples are bumped by hand rather than moved onto the central properties - they are documentation for SDK users and stay conventional and literal.
Pins global.json to the preview 7 SDK and its workload set, and updates the projects that don't consume the centralized TFM properties: - Sentry.Maui.CommunityToolkit.Mvvm (+ tests) and Sentry.MauiTrimTest were pinned to exactly the Previous* TFM set, so they now use those properties and will track future bumps on their own. - SingleFileTestApp and Sentry.TrimTest move to net11.0, so single-file bundle reading and trim/AOT analysis are exercised against the new runtime. - The TFM switches in SingleFileAppTests and AndroidAssemblyReaderTests, and the APK build matrix in Sentry.Android.AssemblyReader.Tests, gain net11.0 and drop net9.0/net8.0.
Works through what building against .NET 11 preview 7 surfaced: - Android raised its minimum deployment target to API 24 and MacCatalyst to 17.0. Both are applied only to the net11.0 TFM, so net10.0 consumers keep API 21 / 15.0. Raising Android's floor is user-facing: it drops Android 5.0-6.0 for net11.0. - Seven ItemGroups keyed on TargetFrameworkVersion v10.0 had no v11.0 sibling, leaving the new TFM with no packages at all. This is what produced the Sentry.Bindings.Android XA4242 Java dependency failures - the AndroidX and Kotlin references simply weren't there for net11.0-android37.0. - The Cocoa bindings are now nullable-annotated. ObjectForInfoDictionary and NSObject.FromObject return nullable values, which exposed a latent NRE when an Info.plist key is missing, and two spots that could store null in a non-nullable NSObject dictionary. The marshal-exception delegates also declare a nullable sender now. - .NET 11 Android defaults to CoreCLR, and RunAOTCompilation is Mono-only, so the APK build matrix now asks for Mono only on the AOT cases. - The synchronous HttpClient.Send is annotated unsupported on iOS/MacCatalyst; suppressed at the test helper, whose callers all use a substitute handler. - integration-test/net9-maui is renamed to maui-app - it's a generic MAUI template, and pinning a .NET version in the name has aged badly once. Sentry.Samples.OpenTelemetry.AzureFunctions stays on net10.0: the Azure Functions Worker SDK has no .NET 11 support yet (2.0.7 and 2.1.0 both reject the TFM). Sentry.Compiler.Extensions.Tests compiles its analyzer snippets against the .NET 10 reference assemblies, as Microsoft.CodeAnalysis.Testing ships nothing newer than Net100.
- .NET 11 removes the Mono runtime for Android (NETSDK1242), so Mono AOT cannot be produced at all. The AOT APK variants are now built only for net10.0-android. No coverage is lost: AndroidAssemblyReaderTests already skips every AOT case, so those APKs were built but never read. - MAUI 11's MSIX PRI expansion shells out to the Windows-only makepri.exe, which breaks cross-building net11.0-windows from macOS. net10.0-windows is unaffected, so PRI expansion is skipped when the host isn't Windows. - MauiEventsBinderTests had no live branch left once net9 went, leaving navigatedToEventArgs undeclared on every TFM; the conditional is gone. - LocalDbFixture gains its net11.0 name, and drops arms for TFMs we no longer build. Its #else is a hard #error - each TFM needs a distinct database name. - The MAUI sample carried unused Frame and ListView styles from the project template, both obsolete in MAUI 11, and one EndAndExpand layout option. - BL0012 is suppressed in the Blazor sample: the analyzer treats StateHasChanged as redundant after an event handler, but that handler always throws, so the automatic re-render never runs. Drops the net8.0 and net9.0 verified snapshots. The net11.0 ones regenerate themselves on a test run, and CI's Windows job produces the net48 files.
…ader tests - ReferenceAssembliesExtensions declares a real net11.0 entry. Falling back to Net100 did not work: the analyzer snippets reference a net11.0-built Sentry.dll, so CS1705 fires against .NET 10 reference assemblies. Microsoft.CodeAnalysis.Testing has its own PackageIdentity, so this needs no extra package reference. - DebugStackTraceTests asserted the top frame's declaring type was exactly System.Convert. .NET 11 routes Convert.FromBase64String through System.Buffers.Text.Base64. That was only the test's sanity check - what it actually asserts is keyed off the namespace, so it now checks that instead. - AndroidAssemblyReaderTests skips the AOT permutations on net11.0. The existing Skip.If(isAot) is inside #if ANDROID, so on host runs every permutation executes and needs an APK - and .NET 11 cannot produce Mono-AOT APKs at all. - StoreReader documents that .NET 11 emits assembly store v4, which changes the header/index layout and not just the version number. Bumping the constant alone gets past IsSupported() and then fails in Prepare().
Generated by a local test run. Content is byte-identical to the net10.0 snapshots in every case, so .NET 11 introduces no behavioural difference here - Verify just keys these files by target framework. The net48 snapshots can only be produced on Windows, so CI's verify-api job will commit those.
Sentry.MauiTrimTest ships empty Directory.Build.props and .targets stubs to deliberately isolate itself from the repo's build customization, so it is a realistic standalone consumer app for trim analysis. That means the centralized $(Previous*Tfm) properties are not available to it - switching it onto them left TargetFrameworks evaluating to ";", and MSBuild then treated it as a cross-targeting project with no frameworks and recursed: Microsoft.NET.Sdk.Workloads.CrossTargeting.targets(25,5): error MSB4006: There is a circular dependency in the target dependency graph involving target "_GetRequiredWorkloads". The TFMs go back to literals, with a comment so the next TFM bump doesn't repeat it. Sentry.TrimTest and AndroidTestApp use the same isolation stubs but were already left on literals. This project is not in Sentry-CI-Build-macOS.slnf, which is why a local build and test run of that filter didn't catch it - CI's workload restore covers every project.
| // Each .NET release bumps the assembly store format: v2 in .NET 9, v3 in .NET 10, v4 in | ||
| // .NET 11. v4 changes the header/index layout as well as the version number, so it needs | ||
| // the upstream reader changes ported, not just a new constant - bumping the version alone | ||
| // gets past IsSupported() and then fails with EndOfStreamException in Prepare(). | ||
| // Until that port lands, .NET 11 stores are reported as unsupported, which | ||
| // AndroidHelpers.GetAndroidAssemblyReader handles by logging and returning null. |
There was a problem hiding this comment.
MAUI 11's XAML compilation produced CS0618 warning for these two styles:
CS0618: 'Frame' is obsolete: 'Frame is obsolete as of .NET 9. Please use Border instead.'
The MAUI 11 template already includes styles for Border so no need to include them in our sample.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d640105. Configure here.
Skip the assembly store v4 tests on Android device runs too. The guard was NET11_0_OR_GREATER && !ANDROID, so the net11.0 leg of the Android device-test matrix still ran them. On device GetSut uses AndroidHelpers.GetAndroidAssemblyReader, which returns null for a v4 store, and the tests then dereference it - the null-forgiving ! turns the known limitation into a NullReferenceException. The v4 gap applies to any .NET 11 Android APK, host or device. Revert the Android JNI constructor the format bot added to the iOS, MacCatalyst and Tizen Program types in Sentry.MauiTrimTest (d640105). Those types have no Android base class and Android.Runtime.JniHandleOwnership doesn't exist on those platforms, so the TFMs failed to compile. MainApplication.cs already had the correct constructor, so nothing was missing - the code fix was misapplied. The format job only reached this project once MSB4006 was fixed in 85c3ce2, which is why it appeared now.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## version7 #5529 +/- ##
============================================
- Coverage 74.67% 74.05% -0.62%
============================================
Files 515 500 -15
Lines 18909 18465 -444
Branches 3691 3598 -93
============================================
- Hits 14120 13674 -446
- Misses 3904 3908 +4
+ Partials 885 883 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Four independent failures, all consequences of the preview: - NU5104 (most jobs, at pack time): supporting a .NET 11 preview means depending on preview Microsoft.Extensions / Microsoft.AspNetCore packages, and pack refuses a stable version with prerelease dependencies. Demoted to a warning rather than silenced, and it disappears at GA when those references become stable. The .NET 10 work took the same approach of pinning rc packages, so this is inherent to tracking a preview rather than new. - NETSDK1094/NETSDK1096 (Linux and Android device tests): .NET 11 Android defaults PublishReadyToRun to true - net10.0-android leaves it unset - and R2R can't be produced for the Android RIDs here. Turned off in the two app projects that build a net11.0 Android TFM, matching Sentry.Samples.Maui. - NETSDK1242 (Android device tests): the integration test still ran its mono cases on net11.0, where the Mono runtime no longer exists. Mono cases now apply only below net11.0; coreclr runs everywhere. - NETSDK1005 (ios-tests): device-test.ps1 defaulted to net10.0, so it asked for net10.0-ios, which the device test app no longer targets. The Android workflow passes -Tfm per matrix leg; the iOS one relies on the default.
- Install the .NET 10 runtime explicitly. global.json now pins the .NET 11 SDK, so the .NET 10 runtime no longer arrives with it, but net10.0 is still a target framework we build and test. Most runners have it preinstalled; the Alpine containers don't, so their net10.0 test hosts failed to start. - Install the Android API 37 platform, which net11.0-android37.0 needs (XA5207). - Pin ContainerBaseImage for the integration test. The SDK derives the tag from the runtime version, giving 11.0.0-preview.7-noble-chiseled, which doesn't exist: .NET 11 images moved from noble to resolute and previews only ship under floating 11.0-preview tags. The .NET 10 previews needed the same treatment - see the commented-out block just below. - .NET 11 removed the Mono runtime for iOS as well as Android, so the iOS integration test now uses coreclr (NETSDK1242). - The Android integration test app pinned its own deployment target of API 21, overriding Directory.Build.props, so it still tripped XA4216.
Mirrors what the net10.0-rc.1 work did (9e2f5f2): bump to the upcoming major and mark the branch prerelease while it tracks a .NET preview. This also removes the need for the NU5104 workaround added in the previous commit. That warning fires because pack refuses a stable version with prerelease dependencies, and supporting a .NET 11 preview means depending on preview Microsoft.Extensions / Microsoft.AspNetCore packages. A prerelease package may depend on prerelease packages, so the warning no longer occurs at all rather than being demoted.
The format job adds an Android JNI constructor to the iOS, MacCatalyst and Tizen Program types, which have no Android base type - so those TFMs stop compiling. Reverting alone wasn't enough: the job re-applied it on the next run (2e052b1), so the exclusion is the fix. MAUI projects list every platform folder in every TFM's Compile items - the SDK filters them later, during the build - so the tool analyses the iOS sources in an Android context and misapplies the code fix. Sentry.Samples.Maui shows the same item list, so this isn't specific to the trim test; it just happens to be where the fix lands.
Reverts the Android JNI constructors the format job re-added to the iOS, MacCatalyst and Tizen Program types. The run that pushed them (34082068453) was triggered by b7b358e, before the exclusion added in 9f173f7, and ran the old command without ./test/Sentry.MauiTrimTest/Platforms. It rebased onto the current tip when pushing, which is why the commit appears on top of the exclusion. The next format run picks up the exclusion.

Gets the SDK building and testing against .NET 11 preview 7, targeting
version7.Note
Lots of changes in this PR but it's mostly mechanical. The goal of this PR is just to get things building with the new TFMs.
I've left a few review comments myself to explain a few things that aren't obvious.
Closes #5527
TFMs
net11.0becomes the latest TFM andnet10.0the previous;net8.0andnet9.0are dropped (both lose LTS when .NET 11 ships). Platform TFMs follow the preview 7 workload set (11.0.100-preview.7.26410.2):net11.0net10.0net11.0-android37.0net10.0-android36.0net11.0-ios26.5net10.0-ios26net11.0-maccatalyst26.5net10.0-maccatalyst26net11.0-windows10.0.19041.0net10.0-windows10.0.19041.0iOS and MacCatalyst only publish a
net11.0_26.5ref pack, so those need the minor version — unlike the currentnet10.0-ios26.Samples are bumped by hand rather than moved onto the central properties: they're documentation, and
$(LatestTfm)doesn't resolve outside this repo.What .NET 11 changed
NETSDK1242).RunAOTCompilationis Mono-only, so Mono-AOT APKs cannot be produced at all. The APK matrix inSentry.Android.AssemblyReader.Testsbuilds the AOT variants fornet10.0-androidonly.GetBundleValuedidNSBundle.MainBundle.ObjectForInfoDictionary(key).ToString(), so a missingInfo.plistkey would have thrown while building the release/distribution string.NSObject.FromObjectis also nullable now, and two conversions were storing its result straight into aDictionary<NSString, NSObject>.net11.0TFM only, sonet10.0consumers are unaffected. Android API 24 is a user-facing breaking change for .NET 11 — it drops Android 5.0, 5.1 and 6.0 — and wants a line in the 7.0.0 breaking changes.HttpClient.Sendis annotated unsupported on iOS/MacCatalyst. Suppressed narrowly in the test helper, whose callers all supply a substitute inner handler.makepri.exe, so cross-buildingnet11.0-windowsfrom macOS/Linux fails.net10.0-windowsis fine, so this is new. PRI expansion is skipped when the host isn't Windows; Windows CI still runs the full path.Convert.FromBase64Stringnow routes throughSystem.Buffers.Text.Base64, changing the declaring type of the top stack frame.DebugStackTraceTestswas asserting one specific type in a sanity check; it now checks the namespace, which is whatInAppactually keys off.Known gaps
0x80030004), and v4 changed the header/index layout as well as the version number — adding the version alone gets pastIsSupported()and then fails inPrepare(). This is already tracked in Port upstream AssemblyStore reader changes: v4/CoreCLR format, _assembly_store symbol, index-entry sizing (next major) #5454, which identifies the same root cause (a newcontent_idheader field when the format number is >= 4). It degrades gracefully:AndroidHelpers.GetAndroidAssemblyReadercatches, logs and returns null. 20 tests inAndroidAssemblyReaderTestsare skipped onnet11.0with a reason pointing at Port upstream AssemblyStore reader changes: v4/CoreCLR format, _assembly_store symbol, index-entry sizing (next major) #5454, and must be re-enabled when that port lands.net10.0still runs all 23.Sentry.Samples.OpenTelemetry.AzureFunctionsstays onnet10.0. The Azure Functions Worker SDK has no .NET 11 support — 2.0.7 and the current 2.1.0 both reject the TFM with "Invalid combination of TargetFramework and AzureFunctionsVersion is set."Microsoft.CodeAnalysis.Testingships noReferenceAssemblies.Net.Net110(1.1.4 stops atNet100), soSentry.Compiler.Extensions.Testsdeclares one locally, pinned toMicrosoft.NETCore.App.Ref11.0.0-preview.7.26381.103. Keep it in step withglobal.json.