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

Fix TFM precedence issues between netX.0 and netX.0-windows* in NuGet packages #238

Open
1 of 7 tasks
Sergio0694 opened this issue Dec 21, 2024 · 10 comments
Open
1 of 7 tasks
Assignees
Labels
bug 🐛 Something isn't working

Comments

@Sergio0694
Copy link
Member

Sergio0694 commented Dec 21, 2024

Describe the bug

I was testing the new builds from main and noticed all UWP packages are broken on .NET 9:

Image

The core of the issue is that we have a net9.0 TFM pulling in Uno dependencies. We should not do this. The official guidance for packages that have UI framework dependencies is to have different packages for different UI frameworks. Uno should have their own packages as well. We need 4:

  • *.Uwp: UWP .NET Native and .NET 9
  • *.Uwp.Uno: UWP XAML Uno flavor
  • *.WinUI: WinUI 3
  • *.WinUI.Uno: WinUI 3 Uno flavor

As a short term solution, we can add a net9.0-windows10.0.26100.0 TFM to work around the issue for UWP .NET 9 consumers.

Code Platform

  • UWP
  • WinAppSDK / WinUI 3
  • Web Assembly (WASM)
  • Android
  • iOS
  • MacOS
  • Linux / GTK
@Arlodotexe
Copy link
Member

Arlodotexe commented Dec 27, 2024

Background

Packaging was first introduced in CommunityToolkit/Labs-Windows#28 (see here). At the time, our infrastructure was capable of multi-targeting but not packaging, primarily because we didn't have a way to distinguish between the two variants of Uno (Uno.UI/Uwp and Uno.WinUI/Wasdk) either while building or while packaging, since they used the same TargetFramework on both.

If not for Uno here, we likely would have put Uwp and Wasdk into one package, and we wouldn't have been able to ship .NET 9 in UWP alongside Wasdk like we just did, since the Windows App SDK and modern .NET on UWP use the same TFM.

@michael-hawker and I were under the impression that NuGet and the .NET SDK would be enough to handle what we were doing, and that we'd run into total blockers if we did something we shouldn't. We never got that signal beyond the Uwp/WinUI package split needed to support Uno in the first place, which happened to work in our favor in regard to the official guidance mentioned in the OP.

We also encountered this issue in the past on Wasdk via #125. We had found a tool that allowed us to check TFM compatibility and precedence:

Using this tool, you can see that:

  • For tfm compatibility (see here), net7.0-windows10.0.19041.0 projects can use net6.0-windows10.0.19041.0 packages
  • For tfm precedence (see here), the very next candidate after net7.0-windows10.0.19041.0 is net7.0.

Problem

The problem here seems to be that TFM precedence is affecting our ability to use the net8.0-windows* TFM from UWP projects targeting net9.0-windows*, because the package contains a net9.0 TFM which dotnet selects instead of selecting net8.0-windows* like we might expect.

Solution

Revisit official guidance.

@Sergio0694 had stated:

The official guidance for packages that have UI framework dependencies is to have different packages for different UI frameworks.

Checking around the web, I've found this, but I'm unable to find any published guidance about packaging and UI frameworks.

@Arlodotexe Arlodotexe changed the title All UWP packages are broken on .NET 9 because of Uno references Fix TFM precedence issues between netX.0 and netX.0-windows* in NuGet packages Dec 27, 2024
@Sergio0694
Copy link
Member Author

This general issue came up while we were evaluating what TFM/TPM we should use for UWP support for .NET 9. We eventually settled on just using netX.0-windows10... like all other UI frameworks (Windows Forms, WPF, WinUI, etc.) are also doing (it was UWP that was the odd one out). While doing this, the question of how to handle packages targeting multiple UI frameworks was also raised. And once again the official answer is just: have different packages for each specific UI framework.

Also worth mentioning that the whole TFM <=> UI framework association would never be correct anyway. It's completely valid to mix and match multiple UI frameworks in the same project too. That's another reason why having multiple packages is the only way to give developers proper control over what they're pulling in.

@Arlodotexe
Copy link
Member

Arlodotexe commented Dec 27, 2024

@Sergio0694 Just to clarify:

  • UWP XAML and WinUI 3 are separate UI frameworks
  • Uno.UI is a 1:1 match of the APIs in UWP XAML
  • Uno.WinUI is a 1:1 match of many APIs in the Windows App SDK.

To understand this issue properly, we need to recognize that Uno isn't designed to be a 'separate UI framework', rather it's specifically design to polyfill UWP XAML and WinUI 3 as transparently as possible. If it runs natively, it should 'just work' on the other platforms.

If we were instead using some hypothetical official implementations of WinUI on those platforms instead of using Uno, it would still have this TFM precedence issue.

That's important to note, because the issue can be solved a different way: by using more specific TFMs instead of simply net9.0, such as net9.0-wasm, just as MAUI would do to work around the issue.

We should consider this as well, especially since we still can't find published guidance on this.

@Arlodotexe
Copy link
Member

If we were instead using some hypothetical official implementations of WinUI on those platforms instead of using Uno, it would still have this TFM precedence issue.

That's important to note, because the issue can be solved a different way: by using more specific TFMs instead of simply net9.0, such as net9.0-wasm, just as MAUI would do to work around the issue.

@jeromelaban Is this something that's been raised with the Uno Platform team? Do you have any insights to add from your end?

@Arlodotexe Arlodotexe moved this to 🔖 Ready in Toolkit 8.x Dec 27, 2024
@Sergio0694
Copy link
Member Author

"If we were instead using some hypothetical official implementations of WinUI on those platforms instead of using Uno, it would still have this TFM precedence issue."

Yeah, the problem isn't specific to Uno. We're just only hitting it with Uno because UWP and WinAppSDK are already split.

"especially since we still can't find published guidance on this"

Like I said, the official guidance is to split libraries into separate packages for different UI frameworks (WPF, UWP, WinUI, etc.). I agree there doesn't seem to be a dedicated docs page specifically for this. We talked about adding a couple bullet points for this to this docs page, but we haven't done so just yet (the guidance is however what I've mentioned).

I'll make a PR into the docs repo to fix this 🙂

"by using more specific TFMs instead of simply net9.0"

Right, the issue would also go away if you removed all -windows TFMs for Uno. But I don't think that's viable?

@Arlodotexe
Copy link
Member

Arlodotexe commented Dec 28, 2024

Yeah, the problem isn't specific to Uno. We're just only hitting it with Uno because UWP and WinAppSDK are already split.

Close, but not quite. We would have hit the same problem if we had gotten our "unified package" like we initially wanted, but in a different way.

If we had supported all four of Uwp, Wasdk, and Uwp/Uno and Wasdk/Uno (e.g., if Uno used different TFMs per platform from the start), then the split could have occurred either down Uwp/Wasdk or Uno/Non-Uno first. One split, regardless of which we did first, wouldn't have been enough. The existing UWP and WinAppSDK package split was only ever done because of Uno, the remaining split hasn't been discussed until now.

Right, the issue would also go away if you removed all -windows TFMs for Uno. But I don't think that's viable?

Not suggesting this, nor am I sure what you mean. Uno isn't using any -windows TFMs. The issue runs the other way around: we need a more specific TFM for each platform than net9.0 to avoid the TFM precedence issues.

To illustrate what I mean, see the notes gathered from the Wasdk encounter with this issue:

We also encountered this issue in the past on Wasdk via #125. We had found a tool that allowed us to check TFM compatibility and precedence:

Using this tool, you can see that:

  • For tfm compatibility (see here), net7.0-windows10.0.19041.0 projects can use net6.0-windows10.0.19041.0 packages
  • For tfm precedence (see here), the very next candidate after net7.0-windows10.0.19041.0 is net7.0.

If Uno didn't use net9.0 but instead used net9.0-wasm or something similar for wpf, linux and the rest, then NuGet wouldn't have that net9.0 TFM to incorrectly select when running a higher and more platforms-specific TFM than our package supports, such as net8.0-windows* in the package and net9.0-windows* on the consuming project.

I'll make a PR into the docs repo to fix this

That sounds good. It would be great to understand the rationale behind it, what problems this can cause and why, instead of flying blind making decisions.

@Sergio0694
Copy link
Member Author

"instead of flying blind making decisions"

Just to clarify, we're not flying blind here. I'm telling you what the official guidance is 🙂
And that's from several internal conversations we've had with folks from both .NET, NuGet, and Windows. I agree that we should also have this guidance being written down somewhere in the docs (and I'll help with that), but we're not flying blind here.

"TFM to incorrectly select when running a higher and more platforms-specific TFM than our package supports"

Note that having more specific TFMs wouldn't fully solve the problem either. The fundamental issue is that you cannot (by design) rely on TFMs to know which UI framework someone is consuming. Nothing stops people from referencing more than a single UI framework in a given project (eg. you could have a WindowsAppSDK app with a UWP XAML island, or you could have a WPF app that also uses WindowsAppSDK stuff, and so on). So having separate packages is also the only way to let people properly control which dependencies they're pulling in, based on what they need and want to do.

@Arlodotexe
Copy link
Member

Arlodotexe commented Dec 28, 2024

Just to clarify, we're not flying blind here. I'm telling you what the official guidance is 🙂

Right, to clarify: You're not flying blind, but we are. You're telling me the 'what' but none of the 'why', which doesn't allow us to inspect all of our options or even identify possible alternatives. That's why having guidance and underlying rationale documented would be nice.

The fundamental issue is that you cannot (by design) rely on TFMs to know which UI framework someone is consuming.

Right... as I laid out, Uno isn't a different UI framework, it's a re-implementation of UWP XAML and WinUI 3 on other TFMs to run on other platforms.

The problem is that TFM precedence will prioritize a non-platform-specific TFM if the version is closer: net9.0 gets picked instead of net8.0-windows* by a project running net9.0-windows*.

This wouldn't happen if it used net9.0-wasm instead of net9.0, because that TFM isn't compatible with netX.0-windows*, so net9.0-windows* projects would have grabbed net8.0-windows* from the package instead.

It also doesn't happen if you just target net9.0-windows* in the package and match the project, which is the workaround we used.


All that said, you're saying something slightly different @Sergio0694. Both of our additions are worth inspecting.

To me, it sounds like you're saying that each UI implementation (including polyfills like Uno) should be package-specific, not just the targeted UI framework itself (e.g. UWP XAML vs WinUI 3). Is that right?

That is, something like MAUI which uses the same underlying UI implementation for all targets would be able to package together in the same NuGet package, but something like Uno that uses a different implementation from canonical 'native' UI targets should be in a separate package?

@Sergio0694
Copy link
Member Author

"To me, it sounds like you're saying that each UI implementation (including polyfills like Uno) should be package-specific, not just the targeted UI framework itself (e.g. UWP XAML vs WinUI 3). Is that right?"

Yes, that's what I'm saying and why I feel there's some disconnect. The fact that Uno is technically a reimplementation of whatever UI framework and not a UI framework per se doesn't matter. The point is that you should not use TFMs to select which technology to pull in. You should have different NuGet packages per technology, and then each will just have TFMs for whichever target framework they can support.

@Arlodotexe
Copy link
Member

@Sergio0694 Perfect, that makes sense! Glad we could get it cleared up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working
Projects
Status: 🔖 Ready
Development

No branches or pull requests

2 participants