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

Update Gallery for NativeAOT Compatibility #336

Merged
merged 8 commits into from
Nov 27, 2024

Conversation

dme-compunet
Copy link
Contributor

@dme-compunet dme-compunet commented Nov 23, 2024

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

Key Changes:

  • Replaced the original ViewLocator with one that resolves views using a pre-defined list.
  • Changed the search strategy in SideMenuBar (unsure why, but SideMenuSearchToVisibilityConverter throws InvalidCastException in NativeAOT).
  • Removed reliance on the AutoGenerateColumns property in DataGrid, manually adding columns instead.
  • Refactored icon page to support compiled bindings.

Issues/Incompatibilities:

  • PropertyGrid is broken due to its reliance on reflection, requiring a larger fix.
  • The Playground is unavailable, showing an error message instead (might consider removing it entirely in NativeAOT).
  • On the Collections page, the CheckBox in DataGrid is uneditable (this behavior needs investigation).

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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

Exception details:
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!<BaseAddress>+0x1129b1a

@kikipoulet
Copy link
Owner

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
Copy link
Contributor

Insire commented Nov 24, 2024

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
Copy link
Collaborator

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
Copy link
Owner

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
Copy link
Contributor

Insire commented Nov 25, 2024

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
Copy link
Collaborator

sirdoombox commented Nov 25, 2024

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
Copy link
Contributor Author

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

@kikipoulet kikipoulet merged commit cfd6486 into kikipoulet:main Nov 27, 2024
1 check passed
@dme-compunet dme-compunet deleted the native-aot-gallery branch November 27, 2024 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants