kikipoulet / SukiUI

UI Theme for AvaloniaUI
MIT License
1.52k stars 140 forks source link

Update Gallery for NativeAOT Compatibility #336

Closed dme-compunet closed 1 week ago

dme-compunet commented 1 week ago

This PR makes the SukiUI control gallery compatible with NativeAOT compilation.

Key Changes:

Issues/Incompatibilities:

Additional Notes:

Note: Feel free to close the PR if you don't agree with these changes. I think it would be great if the gallery was compatible with NativeAOT compilation, but the decision is yours.

dme-compunet commented 1 week ago

For example: this is gallery binaries after native compilation created using the current added workflow.

https://github.com/kikipoulet/SukiUI/actions/runs/11990392672/artifacts/2228541595

dme-compunet commented 1 week ago

Now I see that after merging the master, the Dock page is also broken, probably due to a dependency bump.

Exception details: ```txt System.IO.FileNotFoundException: Cannot load assembly 'Avalonia.MarkupExtension'. No metadata found for this assembly. at System.Reflection.Runtime.General.ReflectionCoreCallbacksImplementation.Load(AssemblyName, Boolean) + 0x61 at Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers.XamlTypeResolver.Resolve(String) + 0x184 at Avalonia.Markup.Parsers.ExpressionNodeFactory.LookupType(Func`3, String, String) + 0x3e at Avalonia.Markup.Parsers.ExpressionNodeFactory.LogicalAncestorNode(Func`3, BindingExpressionGrammar.AncestorNode) + 0x27 at Avalonia.Markup.Parsers.ExpressionNodeFactory.CreateFromAst(List`1, Func`3, INameScope, Boolean&) + 0x279 at Avalonia.Data.Binding.InstanceCore(AvaloniaProperty, AvaloniaObject, Object, Boolean) + 0xac at Avalonia.Data.Binding.Instance(AvaloniaObject, AvaloniaProperty, Object) + 0x5d at Avalonia.Styling.Setter.SetBinding(StyleInstance, AvaloniaObject, IBinding2) + 0x2f at Avalonia.Styling.Setter.Instance(IStyleInstance, StyledElement) + 0x82 at Avalonia.Styling.StyleBase.Attach(StyledElement, IStyleActivator, FrameType, Boolean) + 0xc2 at Avalonia.Styling.Style.TryAttach(StyledElement, Object, FrameType) + 0xd0 at Avalonia.StyledElement.ApplyStyle(IStyle, IStyleHost, FrameType) + 0x3a at Avalonia.StyledElement.ApplyControlTheme(ControlTheme, FrameType) + 0x8c at Avalonia.StyledElement.ApplyStyling() + 0x51 at Avalonia.StyledElement.OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs) + 0x5c at Avalonia.StyledElement.OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs) + 0xe6 at Avalonia.StyledElement.OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs) + 0xe6 at Avalonia.StyledElement.Avalonia.Controls.ISetLogicalParent.SetParent(ILogical) + 0x105 at Avalonia.Controls.Primitives.TemplatedControl.ApplyTemplate() + 0x2b9 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x12e at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Border.MeasureOverride(Size) + 0x96 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Controls.ProportionalStackPanel.MeasureOverride(Size) + 0x35d at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Border.MeasureOverride(Size) + 0x96 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Controls.DockPanel.MeasureOverride(Size) + 0x109 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Controls.Grid.MeasureCell(Int32, Boolean) + 0x122 at Avalonia.Controls.Grid.MeasureCellsGroup(Int32, Size, Boolean, Boolean, Boolean&) + 0xba at Avalonia.Controls.Grid.MeasureOverride(Size) + 0x450 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.Layoutable.MeasureOverride(Size) + 0x7f at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable, Size, Thickness, Thickness) + 0x126 at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size) + 0x71 at Avalonia.Layout.Layoutable.MeasureCore(Size) + 0x19f at Avalonia.Layout.Layoutable.Measure(Size) + 0xed at Avalonia.Layout.LayoutManager.Measure(Layoutable) + 0xfb at Avalonia.Layout.LayoutManager.ExecuteMeasurePass() + 0x39 at Avalonia.Layout.LayoutManager.InnerLayoutPass() + 0x13 at Avalonia.Layout.LayoutManager.ExecuteLayoutPass() + 0x1f4 at Avalonia.Media.MediaContext.FireInvokeOnRenderCallbacks() + 0x6d at Avalonia.Media.MediaContext.RenderCore() + 0x58 at Avalonia.Media.MediaContext.Render() + 0x17 at Avalonia.Threading.DispatcherOperation.InvokeCore() + 0x1af at Avalonia.Threading.DispatcherOperation.Execute() + 0x73 at Avalonia.Threading.Dispatcher.ExecuteJob(DispatcherOperation) + 0x5d at Avalonia.Threading.Dispatcher.ExecuteJobsCore(Boolean) + 0xa1 at Avalonia.Threading.Dispatcher.Signaled() + 0x41 at Avalonia.Win32.Win32Platform.WndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam) + 0x58 at SukiUI.Demo!+0x1129b1a ```
kikipoulet commented 1 week ago

I have no computer to test it today but If I understand well, these changes permits the actual code to open the gallery without crash at startup at least, right ? It's a welcomed fix to me, but we need @sirdoombox opinion about the ViewLocator modifications.

Running the gallery in NativeAOT is not a priority at all, but being able to open it until going to a critical page is a great addition.

Insire commented 1 week ago

I doubt anybody cares about the ViewLocator. Thats just code thats copied often between projects. Breaking controls is a larger issue for me. If there is supposed to be support for AOT, then all things in SukiUI should support it.

For the PropertyGrid, that probably means writing a source generator. Same for the DataGrid.AutoGenerateColumns

sirdoombox commented 1 week ago

I'm not too worried about any implementation detail in the demo, it's only for testing and showcasing, the viewlocator in particular only ever used reflection because it was simpler and we could add/remove pages without having to worry about it. The demo is mostly complete at this point, so the additional maintenance burden of a more complex view locator doesn't really matter now.

I think getting NativeAOT working with (at least some of) our custom controls and having a testing environment to work with is a nice idea. As long as this doesn't break the regular build, having NativeAOT builds at least partially functional is great, even if we never release a NativeAOT build, having it for testing internally is a solid get.

I'm also not too concerned with getting every single feature working with NativeAOT, it's inherently a limiting target and just marking some controls as "not NativeAOT compatible" is entirely reasonable I think, generally if you need the NativeAOT target you're going in eyes wide open about the limits you'll be facing.

I won't be in front of my dev machine to test this until some time tomorrow, but as long as the standard build alll works as before I see no problem with these changes.

kikipoulet commented 1 week ago

It seems to me that we have a kind of consensus.

As long as this doesn't break the regular build, having NativeAOT builds at least partially functional is great, even if we never release a NativeAOT build, having it for testing internally is a solid get.

I'm also not too concerned with getting every single feature working with NativeAOT, it's inherently a limiting target and just marking some controls as "not NativeAOT compatible" is entirely reasonable I think, generally if you need the NativeAOT target you're going in eyes wide open about the limits you'll be facing.

This 100%.

I will test it today 👍

Insire commented 1 week ago

We could introduce some compiler directive for the NativeAOT build, which would enable us to include only those pages that we think should work with NativeAOT (or atleast be able to warn users). If thats fine with everyone, i could PR this after this gets merged.

sirdoombox commented 1 week ago

I mean there's no need for the NativeAOT build to work perfectly, and having all our features available to see what does/doesn't work is probably more useful than trying to make the NativeAOT build stable enough to release.

I hold no strong view either way, in a perfect world everything we have would work with NativeAOT. Maybe having a semi working NativeAOT build will make contributing fixes easier?

Tested the regular build locally, other than the splash page missing from the viewlocator and therefore the app, everything seems to be working as intended. The NativeAOT publish succeeds and seems to work as expected. The playground page doesn't work but it doesn't work in a way that is informative and useful, so that's fine.

dme-compunet commented 1 week ago

The Dock page breaks at the transition point between version 11.1.0.2 and 11.1.0.3 of Dock.Avalonia package.