Skip to content

Commit 0ab8c3e

Browse files
committed
chore: Introductory support for Native AOT builds
Context: unoplatform/uno@559b1f7 Context: unoplatform/uno.toolkit.ui@3ef6d6c Context: unoplatform/ShowMeTheXAML#30 What do we want? To build and run with Native AOT! Which in turn requires Uno.Sdk 6.6.0 or later: sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.6.0-dev.15"/g' global.json dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Uno.Gallery/Uno.Gallery.csproj -p:UseSkiaRendering=true -p:SelfContained=true \ -p:PublishAot=true …which promptly fails to build: …Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(120,5): error NETSDK1207: Ahead-of-time compilation is not supported for the target framework. ~~ Build Support for Native AOT ~~ This specifically happens because `Uno.Gallery.csproj` has a `@(ProjectReference)` to `Uno.Gallery.SourceGenerators.csproj`, which is a .NET Standard 2.0 project, which doesn't support Native AOT. To fix this, we need an "indirection": a property which we can set on the command-line which causes `$(PublishAot)` to be set. The "conventional" indirection used in unoplatform/uno SamplesApp and in unoplatform/uno.chefs is `$(SkiaPublishAot)`. This allows: dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Uno.Gallery/Uno.Gallery.csproj -p:UseSkiaRendering=true -p:SelfContained=true \ -p:SkiaPublishAot=true \ -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen which *builds* successfully. (It doesn't *run* successfully yet; that's for later.) ~~ Build Support for Android + Native AOT ~~ Remove `$(RunAOTCompilation)` from the Android build, as it's presence prevents use of Native AOT on Android: /usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/36.1.12/targets/Microsoft.Android.Sdk.Aot.targets(123,5): error Precompiling failed for …/Uno.Gallery/obj/Release/net10.0-android/android-x64/aot-in/System.Collections.dll with exit code 134. Runtime critical type System.RuntimeMethodHandle not found # and 24 more similar errors ~~ Build Support for Mobile Side-by-Side Installation ~~ Additionally, to "reasonably easily" support side-by-side execution of Uno Gallery builds on e.g. iOS or Android, introduce support for the following MSBuild properties (also done in unoplatform/uno.chefs): * `$(ApplicationIdVendorSuffix)`: Value to append to `$(ApplicationId)`, so that separate builds have separate names. * `$(ApplicationTitleVendorSuffix)`: Value to append to `$(ApplicationTitle)`, so that the different builds are distinguishable on app launchers. On iOS, this allows side-by-side installs, but the iOS build doesn't use the `$(ApplicationTitle)` value. This is because `Uno.Gallery/Platforms/iOS/Info.plist` already had a `CFBundleDisplayName` entry, which overrides `$(ApplicationTitle)`. Remove the `CFBundleDisplayName` entry so that `$(ApplicationTitle)` can be used to control `CFBundleDisplayName`. ~~ Desktop Startup Timing ~~ Introduce a trivial "hack" to support app starting time comparisons: add a `--exit` command-line argument which causes the app to `Exit()` at the end of `App.OnLaunched()`. This allows using **time**(1) to capture "app startup time": # CoreCLR: % dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Uno.Gallery/Uno.Gallery.csproj -p:UseSkiaRendering=true -p:SelfContained=true % time Uno.Gallery/bin/Release/net10.0-desktop/osx-x64/publish/Uno.Gallery --exit Uno.Gallery/bin/Release/net10.0-desktop/osx-x64/publish/Uno.Gallery --exit 1.08s user 0.32s system 70% cpu 1.982 total # Native AOT: % dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \ -bl Uno.Gallery/Uno.Gallery.csproj -p:UseSkiaRendering=true -p:SelfContained=true -p:SkiaPublishAot=true % time Uno.Gallery/bin/Release/net10.0-desktop/osx-x64/publish/Uno.Gallery --exit Uno.Gallery/bin/Release/net10.0-desktop/osx-x64/publish/Uno.Gallery --exit 0.27s user 0.15s system 51% cpu 0.811 total Or on Windows: Measure-Command { .\Uno.Gallery\bin\Release\net10.0-desktop\win-x64\publish\Uno.Gallery.exe --exit | Out-Default } | Select TotalMilliseconds ~~ iOS Startup Timing ~~ No changes are needed; to capture startup time on iOS: 1. Install the `.ipa` onto your iOS device. 2. Open **Instruments** (launch Xcode, then click Xcode > Open Developer Tool > Instruments) 3. In Instruments, click File > New ⌘N. 4. In the **Choose a Template…** window, select **App Launch**. 5. In the Window which appears, in the central area is a **Target** section; from the **Device** dropdown, select your connected iOS device, and from the **Target** dropdowns, select **Launch** and the Uno Gallery app from the dropdown. Note: the Target dropdown shows `CFBundleExecutable` -- which is identical regardless of `$(ApplicationIdVendorSuffix)` or `$(ApplicationTitleVendorSuffix)` -- so this is easiest to do if there is only *one* Uno.Gallery app installed. 6. Click File > Record Trace ⌘R to start recording, which will launch the app on the device. 7. When the app has finished launching, force-quit the app. Instruments will now start processing the data. 8. Type ⌘4 to switch to **App Lifecycle**. This shows a table of events with Start, Duration, and Narrative columns. Look for the *Launching - Initial Frame Rendering* Narrative entry. The Start value is the time taken to launch the app. | Start | Duration | Narrative | | ------------: | --------: | :------------------------------------------------ | | 00:00.000.000 | 480.77 ms | The system took 480.77 ms to create the process. | | … | … | … | | 00:01.306.446 | 4.41 ms | Launching – Initial Frame Rendering | In this run, app startup time was 1.306 seconds. ~~ Native AOT Runtime Fixes ~~ Begin fixing some of the runtime issues with Native AOT builds. The following messages would appear on the Console during startup: fail: Uno.Gallery.Sample[0] Failed to initialize data for `AutoSuggestBoxSamplePage`. dataType: Uno.Gallery.Views.Samples.AutoSuggestBoxSamplePageViewModel. Exception: System.MissingMethodException: No parameterless constructor defined for type 'Uno.Gallery.Views.Samples.AutoSuggestBoxSamplePageViewModel'. at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0x180 at Uno.Gallery.Sample.CreateData(Type) + 0x3a fail: Uno.Gallery.Sample[0] Failed to initialize data for `BarometerSamplePage`. dataType: BarometerSamplePageViewModel. Exception: System.MissingMethodException: No parameterless constructor defined for type 'BarometerSamplePageViewModel'. at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0x180 at Uno.Gallery.Sample.CreateData(Type) + 0x3a Plus 19 more similar messages. These `fail` messages appear because of missing "reflection metadata" (unoplatform/uno@559b1f78), and we can fix these by using the `[DynamicallyAccessedMembers]` and `[DynamicDependency]` attributes. In order to use `[DynamicallyAccessedMembers]`, we need either a generic type parameter or a `typeof()` expression. Searching around, there are useful places `typeof()` is already used; from `Uno.Gallery.SourceGenerators` generated output, in `$(CompilerGeneratedFilesOutputPath)`, we have: // …/App.Samples.g.cs public partial class App { public static Sample[] GetSamples() { return new[] { // … new global::Uno.Gallery.Sample(new global::Uno.Gallery.SamplePageAttribute(category: (global::Uno.Gallery.SampleCategory)(2), title: "AutoSuggestBox", source: (global::Uno.Gallery.SourceSdk)(0), glyph: "") { Description = @"Represents a text control that makes suggestions to users as they enter text.", DocumentationLink = "https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.autosuggestbox?view=windows-app-sdk-1.3", DataType = typeof(global::Uno.Gallery.Views.Samples.AutoSuggestBoxSamplePageViewModel), SortOrder = 2147483647 }, typeof(Uno.Gallery.Views.Samples.AutoSuggestBoxSamplePage)), // … }; } } Which has the handy benefit of containing *both* `AutoSuggestBoxSamplePage` and `AutoSuggestBoxSamplePageViewModel`, which were mentioned in the `fail` message. This in turn makes the fix straightforward: update the `Sample` and `SamplePageAttribute` types to use `[DynamicallyAccessedMembers]` on their respective `Type` parameters and properties to preserve constructors (from the original message) and properties, which almost certainly will be needed anyway. (Property preservation is reflexive at this point.) If you select **UI components > AutoSuggestBox** in the left-side tree view, then click the `<>` "Show XAML" button, nothing is shown *and* the following messages are written to the Console: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ShowMeTheXAML:XamlDisplayExtensions.Header] property getter does not exist on type [ShowMeTheXAML.XamlDisplay] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ShowMeTheXAML:XamlDisplayExtensions.Description] property getter does not exist on type [ShowMeTheXAML.XamlDisplay] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ShowMeTheXAML:XamlDisplayExtensions.Options] property getter does not exist on type [ShowMeTheXAML.XamlDisplay] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ShowMeTheXAML:XamlDisplayExtensions.PrettyXaml] property getter does not exist on type [ShowMeTheXAML.XamlDisplay] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ShowMeTheXAML:XamlDisplayExtensions.ShowXaml] property getter does not exist on type [ShowMeTheXAML.XamlDisplay] This is because WPF Attached Properties require use of `[DynamicDependency]` to ensure that the required methods are accessible via Reflection; see also unoplatform/uno.toolkit.ui@3ef6d6ce. Update all use of `DependencyProperty.RegisterAttached()` so that required methods are retained. Unfortunately, while this fixes the above `fail` messages, clicking the `<>` "Show XAML" button still does nothing. Fixing the `<>` "Show XAML" button requires unoplatform/ShowMeTheXAML#30 and replacing the existing `XamlDisplay.Init()` invocation with `XamlDictionary.Init()`. Bump to Uno.ShowMeTheXAML 2.0.0-dev0026 to get this fix. Unfortunately, this breaks the build: D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Warning As Error: Detected package downgrade: Microsoft.Windows.SDK.BuildTools from 10.0.28000.1-RTM to 10.0.26100.7463. Reference the package directly from the project to select a different version. D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Uno.Gallery -> Uno.ShowMeTheXAML 2.0.0-dev0026 -> Microsoft.Windows.SDK.BuildTools (>= 10.0.28000.1-RTM) D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Uno.Gallery -> Microsoft.Windows.SDK.BuildTools (>= 10.0.26100.7463) D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Warning As Error: Detected package downgrade: Microsoft.WindowsAppSDK from 1.8.260101001 to 1.7.250909003. Reference the package directly from the project to select a different version. D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Uno.Gallery -> Uno.ShowMeTheXAML 2.0.0-dev0026 -> Microsoft.WindowsAppSDK (>= 1.8.260101001) D:\a\1\s\Uno.Gallery\Uno.Gallery.csproj : error NU1605: Uno.Gallery -> Microsoft.WindowsAppSDK (>= 1.7.250909003) Fix this error by explicitly adding `Microsoft.WindowsAppSDK` and `Microsoft.Windows.SDK.BuildTools` NuGet package references which match the versions used by `Uno.ShowMeTheXAML`. If you select **UI components > BreadcrumbBar** in the left-side tree view, then the **BreadcrumbBar with Custom DataTemplate** section on the right-hand page doesn't show anything, and Console output contains: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Name] property getter does not exist on type [Uno.Gallery.Entities.Data.Folder] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [ItemCount] property getter does not exist on type [Uno.Gallery.Entities.Data.Folder] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Description] property getter does not exist on type [Uno.Gallery.Entities.Data.Folder] Fix this by adding `[Microsoft.UI.Xaml.Data.Bindable]` to `Uno.Gallery.Entities.Data.Folder`. The use of `[Bindable]` causes the `Uno.UI.SourceGenerators.BindableTypeProvider` source generator to emit `BindableType(…, typeof(Folder))` to `BindableMetadata.g.cs`, which cause all public properties to be added to reflection metadata. Add `[Bindable]` to all other types containing properties within `Uno.Gallery.Entities.Data`. This fixes a number of other samples and messages such as: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Color] property getter does not exist on type [Uno.Gallery.Entities.Data.Record] If you select **UI features > Binding** in the left-side tree view, the right-hand panel would be subtly different between CoreCLR and Native AOT: * CoreCLR: `The length is: [empty]` * Native AOT: `The length is:` Worse, if you enter text into "myTextBox", the bound fields properly update under CoreCLR. For example, enter "123" into "myTextBox", and CoreCLR would show: > The text of myTextBox is: 123 > The length is: 3 Under Native AOT, *nothing* updates. Console output contains: fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [Text] property getter does not exist on type [Uno.Gallery.Views.Samples.BindingSamplePageViewModel] fail: Uno.UI.DataBinding.BindingPropertyHelper[0] The [TextLength] property getter does not exist on type [Uno.Gallery.Views.Samples.BindingSamplePageViewModel] Fix this by adding `[Bindable]` to `BindingSamplePageViewModel`. (Which means "just add `[Bindable]`!" is quickly becoming my favorite answer to everything. Unless the type has `required` or `init`-only properties; see also unoplatform/uno.chefs@06f4f804.) Continue this pattern: update all types which implement `INotifyPropertyChanged` or inherit from a type which implements `INotifyPropertyChanged` to have `[Bindable]`. This fixes the following `fail` messages, among others: The [AccelerationX] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [AccelerationY] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [AccelerationZ] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [AccelerometerAvailable] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [ButtonContent] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [ButtonContent] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [GeolocatedAccuracy] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [GeolocatedAltitude] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [GeolocatedLatitude] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [GeolocatedLongitude] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [GeolocatedTimestamp] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel] The [LastReadTimestamp] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [LastShakeTimestamp] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [ListenButtonContent] property getter does not exist on type [Uno.Gallery.Views.Samples.ClipboardSamplePageViewModel] The [Message] property getter does not exist on type [Uno.Gallery.Views.Samples.ClipboardSamplePageViewModel] The [ReportInterval] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [ShakeCount] property getter does not exist on type [Uno.Gallery.Views.Samples.AccelerometerSamplePageViewModel] The [ToggleButtonContent] property getter does not exist on type [Uno.Gallery.Views.Samples.GeolocatorSamplePageViewModel]
1 parent 224b77d commit 0ab8c3e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+141
-22
lines changed

Directory.Packages.props

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
<PackageVersion Include="Uno.UITest.Helpers" Version="1.1.0-dev.70" />
1616
<PackageVersion Include="Xamarin.UITest" Version="4.4.1" />
1717

18-
<PackageVersion Include="Uno.ShowMeTheXAML" Version="2.0.0-dev0015" />
19-
<PackageVersion Include="Uno.ShowMeTheXAML.MSBuild" Version="2.0.0-dev0015" />
18+
<PackageVersion Include="Uno.ShowMeTheXAML" Version="2.0.0-dev0026" />
19+
<PackageVersion Include="Uno.ShowMeTheXAML.MSBuild" Version="2.0.0-dev0026" />
2020

2121
<PackageVersion Include="Uno.CommunityToolkit.WinUI.UI.Controls" Version="7.1.200" />
2222
<PackageVersion Include="Uno.CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.200" />
@@ -32,6 +32,8 @@
3232
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
3333
<PackageVersion Include="Microsoft.TypeScript.Compiler" Version="3.1.5" />
3434
<PackageVersion Include="Microsoft.TypeScript.MSBuild" Version="4.6.4" />
35+
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.260101001" />
36+
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1-RTM" />
3537
<PackageVersion Include="nventive.Nimue.Application.Packaging" Version="0.1.0-alpha.58" />
3638
<PackageVersion Include="Xamarin.TestCloud.Agent" Version="0.23.2" />
3739
</ItemGroup>

Uno.Gallery/App.xaml.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
7171

7272
this.Log().Debug("Launched app.");
7373
OnLaunchedOrActivated();
74+
75+
if (e.Arguments?.Contains("--exit") ?? false)
76+
{
77+
Exit();
78+
}
7479
}
7580

7681
private void OnLaunchedOrActivated()
@@ -486,7 +491,7 @@ internal static void InitializeLogging()
486491

487492
private void ConfigureXamlDisplay()
488493
{
489-
XamlDisplay.Init(GetType().Assembly);
494+
XamlDictionary.Init();
490495
}
491496

492497
private void ConfigureFeatureFlags()

Uno.Gallery/Entities/Data/DividerItem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace Uno.Gallery.Entities.Data
77
{
8+
[Microsoft.UI.Xaml.Data.Bindable]
89
public class DividerItem
910
{
1011
public DividerItem(int i)

Uno.Gallery/Entities/Data/Folder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace Uno.Gallery.Entities.Data
55
{
6+
[Microsoft.UI.Xaml.Data.Bindable]
67
public class Folder
78
{
89
public Folder(string name, int itemCount, string description)

Uno.Gallery/Entities/Data/Plant.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace Uno.Gallery.Entities.Data;
77

8+
[Microsoft.UI.Xaml.Data.Bindable]
89
public class Plant : IComparable
910
{
1011
public Plant(

Uno.Gallery/Entities/Data/Record.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Uno.Gallery.Entities.Data
66
{
7+
[Microsoft.UI.Xaml.Data.Bindable]
78
public class Record
89
{
910
public Record(string compositionName, string artistName, string color)

Uno.Gallery/Entities/Data/TestCollections.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public void ResetChipItems()
4242
}
4343
}
4444

45+
[Microsoft.UI.Xaml.Data.Bindable]
4546
public class SelectableData : InpcObject
4647
{
4748
public int Index { get => GetProperty<int>(); set => SetProperty(value); }
@@ -51,6 +52,7 @@ public class SelectableData : InpcObject
5152
public override string ToString() => $"Item #{Index}";
5253
}
5354

55+
[Microsoft.UI.Xaml.Data.Bindable]
5456
public class InpcObject : INotifyPropertyChanged
5557
{
5658
public event PropertyChangedEventHandler PropertyChanged;

Uno.Gallery/Entities/Data/TreeItem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace Uno.Gallery.Entities.Data
55
{
6+
[Microsoft.UI.Xaml.Data.Bindable]
67
public class TreeItem
78
{
89
public TreeItem(string name)

Uno.Gallery/Entities/Sample.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using System.Text;
56
using System.Threading.Tasks;
@@ -13,7 +14,11 @@ namespace Uno.Gallery
1314
[Bindable]
1415
public class Sample
1516
{
16-
public Sample(SamplePageAttribute attribute, Type viewType)
17+
internal const DynamicallyAccessedMemberTypes ViewRequirements =
18+
DynamicallyAccessedMemberTypes.PublicConstructors
19+
| DynamicallyAccessedMemberTypes.PublicProperties;
20+
21+
public Sample(SamplePageAttribute attribute, [DynamicallyAccessedMembers(ViewRequirements)] Type viewType)
1722
{
1823
Category = attribute.Category;
1924
Title = attribute.Title;

Uno.Gallery/Entities/SamplePageAttribute.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.ComponentModel;
45
using System.Linq;
56
using System.Text;
@@ -30,6 +31,7 @@ public SamplePageAttribute(SampleCategory category, string title, SourceSdk sour
3031

3132
public string DocumentationLink { get; set; }
3233

34+
[DynamicallyAccessedMembers(Sample.ViewRequirements)]
3335
public Type DataType { get; set; }
3436

3537
public SourceSdk Source { get; }

0 commit comments

Comments
 (0)