Closed HavenDV closed 7 months ago
Thanks for the details!
At this time, the main difference between MAUI and Uno is the fact that the Windows head is separate from the mobile head. it was chosen to keep it this way because many of the msbuild properties needed to have a single project were mostly private, and were causing issues at the time we tested it.
There are multiple considerations for this topic:
net7.0-webassembly
is set?net8.0-webassembly
resolve to? Does it need to be specified in a library as well? You mention that msbuild extras handles it, yet nuget knows about those -android
and -ios
variants explicitly, and that would not be the case for a custom implementation for Uno.net7.0-webassembly
could conflict with an upcoming target defined by the .NET SDK.Points 1 and 2 are particularly important to answer before any change is done to the way generators or internal structure, as it may invalidate the solution altogether.
Good questions and they all require an answer. Writing a custom sdk is not the most common thing, so at the moment I only know the answers to some questions, which I will answer below. For now, I'm envisioning doing this as a separate project, and clearing up as early as possible any issues that might cause it to fail. At the same time, start small - first add Mobile as the basis of the project, then add WebAssembly here and validate all the questions.
- How does Visual studio handle such a change? Adding a target framework platform variant could break the default behavior, and we have no way of altering this. For instance, what does the debugger drop down do when net7.0-webassembly is set?
- How does nuget handle dependencies? In such a context what does a net8.0-webassembly resolve to? Does it need to be specified in a library as well? You mention that msbuild extras handles it, yet nuget knows about those -android and -ios variants explicitly, and that would not be the case for a custom implementation for Uno.
The goal is to provide standard behavior across all platforms, as if a regular TFM were specified. For custom TFM we will need to find a way to correlate it with what we want to mean in any interaction, for example for net7.0-webassembly it will be meant to be net7.0 Since Sdk is just syntactic sugar, I don't see any global problems with this
- Defining a custom target such as net7.0-webassembly could conflict with an upcoming target defined by the .NET SDK.
Didn't know about this. We can immediately use the names net7.0-browser/net8.0-browser, but with the condition that it is not provided by the standard sdk. Thus, the transition will be smooth, but it will require additional time to figure out how to detect the presence of net8.0-browser. Or we can use net7.0-webassembly/net8.0-webassembly and later upgrade to net8.0-browser. It will be cheaper in time.
- How does a specialized SDK work when not locally available? msbuild extras caused all sorts of issues (VS deadlocking) when the SDK was not originally present in the nuget cache folder.
I have consistently used MSBuildExtras in my projects within VS and have not noticed such problems. I'm guessing this is restored when calling dotnet restore. I'm not sure at this point we should be thinking about situations where for some reason there was no restored/cached files were deleted. In any case, this would cause problems with building the project even within the current solution. But in general I'm not sure about this and I may just not have much experience.
- How do other tooling handle such a new structure, for instance our vs code extension (not something that can be publicly done yet).
I think that just like WebAssembly/Skia.Gtk projects that expect files in a chosen location, extensions will also need some work to support this. But this is most likely just a question of what project structure the extension expects instead of some specific knowledge about the Sdk (as I already said, this is just syntactic sugar)
- How does Rider handle it?
- How does vsmac handle it?
Rider definitely supports this, as does vsmac. Regarding the latter, Microsoft seems to have suspended its development, so I don't think we'll have to test it much.
In addition, we also need to provide testing in both the msbuild build
and the dotnet build
build to ensure that everything works correctly in different situations.
At the same time, start small - first add Mobile as the basis of the project, then add WebAssembly here and validate all the questions.
I don't see mobile targets as posing any kind of issues, as the target frameworks won't change and they're already in a single project. Merging skia and wasm targets into the same project is the main challenge.
The goal is to provide standard behavior across all platforms, as if a regular TFM were specified. For custom TFM we will need to find a way to correlate it with what we want to mean in any interaction, for example for net7.0-webassembly it will be meant to be net7.0
Let's find out early in the discovery process. We cannot modify nuget, so we cannot rely on a non-out-of-the-box behavior.
I have consistently used MSBuildExtras in my projects within VS and have not noticed such problems. I'm guessing this is restored when calling dotnet restore.
This particular behavior is specific to VS 2022 for Windows, where dotnet restore
is not explicitly used. We must be thinking about such issues because it impacts the first-timer experience.
Current progress: I managed to combine Windows/Mobile/WebAssembly/Skia.Gtk/Skia.Linux.Framebuffer/Skia.Wpf within one project. Now it looks like this:
<Project Sdk="H.Uno.Sdk/0.18.0">
<PropertyGroup>
<TargetFrameworks>net7.0-maccatalyst;net7.0-android;net7.0-ios;net7.0-linux;net7.0-gtk;net7.0-webassembly;net7.0-windows</TargetFramework>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
</Project>
Tested:
msbuild
builds)Here are two ways to add a custom TargetFramework:
SupportedPlatform
(and other things) without installing the corresponding workload. This will work, but when building the project, dotnet
will complain about the lack of installed workloads. Therefore, here we are redefining this Target so that it works on all platforms except fake ones.
Pros: This will work without requiring additional steps (if we use the NuGet sdk approach via <Project Sdk="H.Uno.Sdk/0.9.8">
).
Cons: Possible side effects in VisualStudio because it also detects this. And also problems if the dotnet sdk changes something in this Target.curl -sSL https://raw.githubusercontent.com/HavenDV/Uno.Sdk/main/scripts/workload-install.sh | sudo bash
Invoke-WebRequest 'https://raw.githubusercontent.com/HavenDV/Uno.Sdk/main/scripts/workload-install.ps1' -OutFile 'workload-install.ps1';
./workload-install.ps1
You can remove this after testing with the following commands:
dotnet workload uninstall uno
Sdk Repo: https://github.com/HavenDV/Uno.Sdk/
Usage Example Repo(does not require installation of workloads): https://github.com/HavenDV/Uno.Sdk.Example
Templates: dotnet new install H.Uno.Templates
and dotnet new uno
Other links:
Current issues:
How does nuget handle dependencies? In such a context what does a net8.0-webassembly resolve to?
This supports explicit setting of fallback, which is used when reference does not support it. In this case, net8.0
will always be used
Does it need to be specified in a library as well?
This is possible, but it is not necessary because net8.0 will be used if an exact match is not found
This particular behavior is specific to VS 2022 for Windows, where dotnet restore is not explicitly used. We must be thinking about such issues because it impacts the first-timer experience.
I'm guessing that to make things easier, there will also be a Uno workload here that will implicitly install everything that is required as dependencies and will contain the given SDK, so there won't be any restore issues here. I also think this approach will allow us in the future to have a separate visual studio workload in Visual Studio Installer that the user can choose to install.
Copy from Discord conversation:
Technical details: I allow 3 possible applications of Sdk that will work (simultaneously):
<Project Sdk="Uno.Sdk/5.0.0">
and NuGet - NuGet restore is required<Project Sdk="Uno.Sdk">
because Uno.Sdk will be located locally<UseUno>true</UseUno>
in the project to activate the Workload Sdk. The only disadvantage of this approach is that we will have to use <Project Sdk="Microsoft.NET.Sdk.Web">
as the basis of the project (due to the presence of WebAssembly) and bypass/disable its Targets depending on the platformWe can install uno workload within uno-check
, it’s just running a script from the repository
At the same time, installing workload allows us to specify dependencies (like android/ios/maccatalyst), and we can replace uno-check
with installing workload. This will also manage the removal of this via dotnet workload uninstall uno
.
We can also include uno-check
as a tool for workload, and after installing uno workload there will be an option to run uno-check
through dotnet uno-check
. This will give us a single point for installation (install-workload scripts) and updating (via dotnet workload update
) Uno.
Official documentation regarding the design of Workloads and Sdks:
I have studied this documentation in detail and I can guarantee that this is the best way to implement a single project solution and in the future for uno development.
I also want to point out that I've already done the bulk of it and there is a repository here with a project that just works. - https://github.com/HavenDV/Uno.Sdk.Example Of course, there are a series of problems that need to be fixed and solutions found for them, but this already runs in the IDE and builds the project correctly
HavenDV — 09/29/2023 8:55 PM It might also be worth creating a separate Uno.Sdk repository(or Uno.Workload, or just sdk/workload) in which I would work. It will include current Uno.Templates repo(or this repo can be just renamed). I will need access to configure GitHub Actions and secrets. This could be integrated into the main repository in the future, but for now it's much faster to do it in an external project. Later we may also release this as separate NuGet packages Uno.Sdk/Uno.Sdk.Templates/Uno.Sdk.Manifest, but for now I'm using the prefix.
This also allows Uno to be used as a dependency on a single package (which I find is much easier for users) - https://www.nuget.org/packages/H.Uno/5.0.41#dependencies-body-tab
MAUI has switched to explicit package dependency since version 8, and this is what it might look like in Uno Single project.
I also noticed that some users complain about the difficulty of creating a project (https://discord.com/channels/372137812037730304/1172384337883304046/1175617324762546176) The SDK approach will allow them to provide implicit default settings that are suitable for any project and then customize many features already within the existing project by activating MSBuild properties that will be listed in the documentation
This feature is now available as part of the 5.2 release. Thanks @HavenDV for the source of inspiration!
What would you like to be added
For me, the most difficult thing about returning to Uno is the structure of the project and the number of different files. After MAUI, it feels very complicated and superfluous
To implement this, I propose to implement the approach with our own SDK, while trying to do a minimum of customization of the original SDK. The SDK approach will get around the problem that WebAssembly uses
Microsoft.NET.Sdk.Web
when other platforms useMicrosoft.NET.Sdk
. This will also allow us to add a custom TargetFrameworks and get around the problem thatnet7.0
is used by different projects.What it will look like for the user:
The SDK will include all required PackageReferences and versions for each platform. This can cause problems with
ManagePackageVersionsCentrally
, so we need to be careful here. This can be ruled out at the initial stage. Relevant MAUI issue: https://github.com/dotnet/maui/issues/12953Project Structure like MAUI:
Problems
Possible solutions: The user will be able to specify a list of generators that must be started before the others are started. After this, a test pass will be run using only these generators to obtain their code. We could potentially use
EmitCompilerGeneratedFiles
and two build runs to do this. We can also write a custom task using Roslyn. Or a similar solution, but without user participation - simply generate the code of all available generators except Uno, and then run the usual assembly with the generated files and the Uno generator.Relevant discussions:
https://github.com/unoplatform/uno/discussions/6539 https://github.com/unoplatform/uno/discussions/6096 https://github.com/unoplatform/uno/discussions/7339 https://github.com/unoplatform/uno/discussions/4886
Relevant projects and docs:
Why is this needed
Users are asking for it. This is much easier to use than the current approach. This is easier to maintain in the long run because Uno.Sdk will hide all the details (but can be overridden).
For which platform
WebAssembly, Android, iOS, macOS (AppKit), Mac Catalyst, Skia (WPF), Skia (GTK on Linux/macOS/Windows), Skia (Linux Framebuffer), Windows, Build tasks