Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to support .NET MAUI on .NET 10? #1084

Open
jonathanpeppers opened this issue Jan 29, 2025 · 7 comments
Open

How to support .NET MAUI on .NET 10? #1084

jonathanpeppers opened this issue Jan 29, 2025 · 7 comments

Comments

@jonathanpeppers
Copy link
Member

jonathanpeppers commented Jan 29, 2025

Android framework version

Other

Affected platform version

.NET 10, NativeAOT

Description

For NativeAOT-friendly support, there are some generator changes to emit methods like:

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static void n_OnCreate_Landroid_os_Bundle_Landroid_os_PersistableBundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState, IntPtr native_persistentState)
{
 if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
  return;
 try {
  var __this = global::Java.Lang.Object.GetObject<Android.App.Activity> (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
  var savedInstanceState = global::Java.Lang.Object.GetObject<Android.OS.Bundle> (native_savedInstanceState, JniHandleOwnership.DoNotTransfer);
  var persistentState = global::Java.Lang.Object.GetObject<Android.OS.PersistableBundle> (native_persistentState, JniHandleOwnership.DoNotTransfer);
  __this.OnCreate (savedInstanceState, persistentState);
 } catch (global::System.Exception __e) {
  __r.OnUserUnhandledException (ref __envp, __e);
 } finally {
  global::Java.Interop.JniEnvironment.EndMarshalMethod (ref __envp);
 }
}

These use:

  • New Java.Interop.dll methods that are only present in .NET 10
  • OnUserUnhandledException() relies on a feature that only works in ICorDebug (not Mono debugger)

I have a PR here that builds this repo with a net10.0-android tfm:

So, how do we build .NET MAUI for .NET 10 and rely on new AndroidX binding packages?

Idea 1

Multi-target for .NET 8, .NET 9, .NET 10.

This would require:

  • .NET 10 Android workload to support .NET 8 for a period of time

Potential blocker:

  • We would build all frameworks with either a preview or nightly Roslyn (C# compiler). If you have a .NET 10 SDK, you get the .NET 10 C# compiler for the other target frameworks.
  • Alternatively build the repo twice w/ stable and preview .NET SDKs, and somehow "merge" the .NET 10 assemblies inside.

Idea 2

  • Build a "preview" set of AndroidX and GPS NuGets with a version number like -preview-net10 or similar.

  • Publish these to NuGet.org.

  • .NET MAUI relies on these for .NET 10.

Potential blocker:

  • It appears that "binderator" (unsure of exact details) emits supported ranges of dependent versions between the various AndroidX packages.
  • This would need to be removed, to only depend on the .NET 10 preview versions.
  • Customers could see the -preview-net10 packages and try to use them.
  • Customers might have trouble trying out .NET 10, updating from .NET 9 to 10?

Conclusion

I am somewhat leaning towards no. 2, but @jpobst or @Redth open to other ideas?

@jpobst
Copy link
Contributor

jpobst commented Jan 29, 2025

To be precise, .NET MAUI 10 will run on today's packages. However the NativeAOT feature (when it exists) will not work without net10.0-android AndroidX packages.

My hunch is that (2) isn't going to work in practice.

Let's say we have:

  • Xamarin.AndroidX.Core 1.0.0
  • Xamarin.AndroidX.Core 1.0.0-preview-net10

NuGet considers 1.0.0 as "higher" than 1.0.0-preview and will likely choose the stable version whenever it can. Given the massive amount of transitive packages in the AndroidX ecosystem, I think it's very likely a stable one will get chosen somewhere.

Additional issues:

  • MAUI requires Xamarin.AndroidX.Security.SecurityCrypto 1.1.0.2-alpha06, not sure how we could force a dependency on 1.1.0.2-alpha06-preview-net10 that doesn't get broken when we release new unstable versions.

I think (1) is probably the better way to go. We would need to decide if we're concerned about using a .NET 10 preview CSC to compile our net8.0-android libraries. If we are, then likely a significant amount of work would be required to build the ecosystem twice and merge them into a single NuGet.

"Idea 3" would be a custom NuGet feed. This would require extra effort on the user's part.

We probably need to decide what timeframe(s) we are talking about. For example, a custom feed is probably good enough for the first several previews as the expectation is that users will have to do quite a bit of extra effort to try it out. (And they will likely just be doing toy "hello world" apps. No one could try to port their existing "real" apps because the ecosystem support won't exist yet.) As .NET previews mature, maybe we are more comfortable with doing (1) with the Roslyn caveat.

We will also likely need build warnings/errors that can detect when you're attempting to use NativeAOT with non-supported binding libraries.

@jonathanpeppers
Copy link
Member Author

NuGet considers 1.0.0 as "higher" than 1.0.0-preview

For this problem, do we normally just bump a .1 across all the packages anyway? It would be 1.0.1-preview?

Is there already something built into this repo for doing this? To "align" all the packages?

We probably need to decide what timeframe(s) we are talking about. For example, a custom feed is probably good enough for the first several previews as the expectation is that users will have to do quite a bit of extra effort to try it out.

I don't think we can require a custom feed for the maui template. dotnet new immediately restores now, and so dotnet new maui would fail.

@jpobst
Copy link
Contributor

jpobst commented Jan 29, 2025

For this problem, do we normally just bump a .1 across all the packages anyway? It would be 1.0.1-preview?

We do not publish preview packages, so it isn't something that has come up. But then you have the same problem when we publish a new 1.0.1 stable version and everyone's local preview MAUI 10 is asking for 1.0.1-preview.

Is there already something built into this repo for doing this? To "align" all the packages?

Yes, you can run dotnet cake -t=bump-config

I don't think we can require a custom feed for the maui template. dotnet new immediately restores now, and so dotnet new maui would fail.

We could add a NuGet.config to the .NET 10 preview maui template, which might not be a horrible idea.

@moljac
Copy link
Contributor

moljac commented Jan 30, 2025

My initial idea (gut feeling) was - Option 1

.NET 10 Android workload to support .NET 8 for a period of time

How big problem would that represent?

I am afraid Option 2 will cause confusion and tons of issues.

Option 3 (nuget feed) would also be feasible solution.

For this problem, do we normally just bump a .1 across all the packages anyway? It would be 1.0.1-preview?

This would be a problem if stable 1.0.1 is published. Usually we bump build number (4th numerical part)

@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented Jan 30, 2025

Option 1

@moljac are we OK to use a preview roslyn for the stable .NET 8 and 9 packs?

Or do you propose to build the repo twice and somehow "merge in" the .NET 10 assemblies?

@moljac
Copy link
Contributor

moljac commented Jan 30, 2025

are we OK to use a preview roslyn for the stable .NET 8 and 9 packs?

I think this would be OK.

Or do you propose to build the repo twice and somehow "merge in" the .NET 10 assemblies?

I think this would be overly complicated and we don't have that much resources.

@jpobst
Copy link
Contributor

jpobst commented Jan 30, 2025

Or do you propose to build the repo twice and somehow "merge in" the .NET 10 assemblies?

I was thinking if we do go down this route, the easiest way may be to:

  • Build <TargetFrameworks>net8.0-android;net10.0-android</TargetFrameworks> NuGet packages with .NET 10 SDK
  • Switch to .NET 8 SDK
  • Build the assemblies again with <TargetFrameworks>net8.0-android</TargetFrameworks> with .NET 8 SDK
  • Use a custom script to replace the net8.0-android .dll in each NuGet

This way the NuGet package would already be correctly built and we just change one file in it.

This feels easier than:

  • Build net10.0-android assemblies
  • Build net8.0-android assemblies
  • Try to author .nuspec files to create a combined package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants