Browse Source

Merge pull request #9760 from AvaloniaUI/better-trimming

Remove IRuntimePlatform.OperatingSystem API and replace with trimmable OperatingSystem API
pull/9803/head
Max Katz 3 years ago
committed by GitHub
parent
commit
2bf384c751
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs
  2. 4
      samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs
  3. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  4. 26
      src/Avalonia.Base/Compatibility/OperatingSystem.cs
  5. 5
      src/Avalonia.Base/Logging/LogArea.cs
  6. 19
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  7. 44
      src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
  8. 13
      src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs
  9. 17
      src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs
  10. 6
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  11. 7
      src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
  12. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs
  13. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
  14. 43
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs
  15. 8
      src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs
  16. 75
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs
  17. 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs

5
samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs

@ -1,5 +1,8 @@
using Avalonia.Controls;
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media.Immutable;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages

4
samples/ControlCatalog/ViewModels/PlatformInformationViewModel.cs

@ -1,3 +1,5 @@
using System;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Platform;
using MiniMvvm;
@ -13,7 +15,7 @@ public class PlatformInformationViewModel : ViewModelBase
if (runtimeInfo is { } info)
{
if (info.IsBrowser)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")))
{
if (info.IsDesktop)
{

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -28,10 +28,13 @@
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Desktop, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.OpenGL, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Skia, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />

26
src/Avalonia.Base/Compatibility/OperatingSystem.cs

@ -0,0 +1,26 @@
using System;
using System.Runtime.InteropServices;
namespace Avalonia.Compatibility
{
internal sealed class OperatingSystemEx
{
#if NET6_0_OR_GREATER
public static bool IsWindows() => OperatingSystem.IsWindows();
public static bool IsMacOS() => OperatingSystem.IsMacOS();
public static bool IsLinux() => OperatingSystem.IsLinux();
public static bool IsAndroid() => OperatingSystem.IsAndroid();
public static bool IsIOS() => OperatingSystem.IsIOS();
public static bool IsBrowser() => OperatingSystem.IsBrowser();
public static bool IsOSPlatform(string platform) => OperatingSystem.IsOSPlatform(platform);
#else
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
public static bool IsAndroid() => IsOSPlatform("ANDROID");
public static bool IsIOS() => IsOSPlatform("IOS");
public static bool IsBrowser() => IsOSPlatform("BROWSER");
public static bool IsOSPlatform(string platform) => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform));
#endif
}
}

5
src/Avalonia.Base/Logging/LogArea.cs

@ -35,6 +35,11 @@ namespace Avalonia.Logging
/// </summary>
public const string Control = nameof(Control);
/// <summary>
/// The log event comes from Win32 Platform.
/// </summary>
public const string Platform = nameof(Platform);
/// <summary>
/// The log event comes from Win32 Platform.
/// </summary>

19
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@ -23,29 +23,10 @@ namespace Avalonia.Platform
[Unstable]
public record struct RuntimePlatformInfo
{
public OperatingSystemType OperatingSystem { get; set; }
public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop :
IsMobile ? FormFactorType.Mobile : FormFactorType.Unknown;
public bool IsDesktop { get; set; }
public bool IsMobile { get; set; }
public bool IsBrowser { get; set; }
public bool IsCoreClr { get; set; }
public bool IsMono { get; set; }
public bool IsDotNetFramework { get; set; }
public bool IsUnix { get; set; }
}
[Unstable]
public enum OperatingSystemType
{
Unknown,
WinNT,
Linux,
OSX,
Android,
iOS,
Browser
}
[Unstable]

44
src/Avalonia.Base/Platform/StandardRuntimePlatform.cs

@ -1,6 +1,6 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Compatibility;
using Avalonia.Platform.Internal;
namespace Avalonia.Platform
@ -14,45 +14,13 @@ namespace Avalonia.Platform
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size);
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
private static readonly RuntimePlatformInfo s_info = new()
{
OperatingSystemType os;
IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(),
IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS()
};
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
os = OperatingSystemType.OSX;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
os = OperatingSystemType.Linux;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
os = OperatingSystemType.WinNT;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android")))
os = OperatingSystemType.Android;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS")))
os = OperatingSystemType.iOS;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser")))
os = OperatingSystemType.Browser;
else
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription);
// Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase);
var isMonoRuntime = Type.GetType("Mono.Runtime") != null;
var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase);
return new RuntimePlatformInfo
{
IsCoreClr = isCoreClr,
IsDotNetFramework = isFramework,
IsMono = isMonoRuntime,
IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT,
IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS,
IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android,
IsBrowser = os == OperatingSystemType.Browser,
OperatingSystem = os,
};
});
public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value;
public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info;
}
}

13
src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs

@ -1,4 +1,5 @@
using System.Reflection;
using Avalonia.Compatibility;
using Avalonia.Platform.Internal;
using Avalonia.Platform.Interop;
@ -18,15 +19,9 @@ namespace Avalonia.Platform
#if NET6_0_OR_GREATER
new Net6Loader()
#else
standardPlatform.GetRuntimeInfo().OperatingSystem switch
{
OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(),
OperatingSystemType.OSX => new UnixLoader(),
OperatingSystemType.Linux => new UnixLoader(),
OperatingSystemType.Android => new UnixLoader(),
// iOS, WASM, ...
_ => new NotSupportedLoader()
}
OperatingSystemEx.IsWindows() ? (IDynamicLibraryLoader)new Win32Loader()
: OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsAndroid() ? new UnixLoader()
: new NotSupportedLoader()
#endif
);
}

17
src/Avalonia.Desktop/AppBuilderDesktopExtensions.cs

@ -1,5 +1,6 @@
using Avalonia.Compatibility;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Logging;
namespace Avalonia
{
@ -7,8 +8,6 @@ namespace Avalonia
{
public static AppBuilder UsePlatformDetect(this AppBuilder builder)
{
var os = builder.RuntimePlatform.GetRuntimeInfo().OperatingSystem;
// We don't have the ability to load every assembly right now, so we are
// stuck with manual configuration here
// Helpers are extracted to separate methods to take the advantage of the fact
@ -16,21 +15,27 @@ namespace Avalonia
// Additionally, by having a hard reference to each assembly,
// we verify that the assemblies are in the final .deps.json file
// so .NET Core knows where to load the assemblies from,.
if (os == OperatingSystemType.WinNT)
if (OperatingSystemEx.IsWindows())
{
LoadWin32(builder);
LoadSkia(builder);
}
else if(os==OperatingSystemType.OSX)
else if(OperatingSystemEx.IsMacOS())
{
LoadAvaloniaNative(builder);
LoadSkia(builder);
}
else
else if (OperatingSystemEx.IsLinux())
{
LoadX11(builder);
LoadSkia(builder);
}
else
{
Logger.TryGet(LogEventLevel.Warning, LogArea.Platform)?.Log(builder,
"Avalonia.Desktop package was referenced on non-desktop platform or it isn't supported");
}
return builder;
}

6
src/Avalonia.OpenGL/Egl/EglInterface.cs

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Compatibility;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
using Avalonia.SourceGenerator;
@ -24,10 +25,9 @@ namespace Avalonia.OpenGL.Egl
static Func<string, IntPtr> Load()
{
var os = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem;
if(os == OperatingSystemType.Linux)
if(OperatingSystemEx.IsLinux())
return Load("libEGL.so.1");
if (os == OperatingSystemType.Android)
if (OperatingSystemEx.IsAndroid())
return Load("libEGL.so");
throw new PlatformNotSupportedException();

7
src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs

@ -11,12 +11,11 @@ internal class BrowserRuntimePlatform : StandardRuntimePlatform
{
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
{
var isMobile = AvaloniaModule.IsMobile();
var result = new RuntimePlatformInfo
{
IsCoreClr = true, // WASM browser is always CoreCLR
IsBrowser = true, // BrowserRuntimePlatform only runs on Browser.
OperatingSystem = OperatingSystemType.Browser,
IsMobile = AvaloniaModule.IsMobile()
IsMobile = isMobile,
IsDesktop = !isMobile
};
return result;

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs

@ -366,6 +366,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
foreach (var branch in ExtensionNodeContainer.Branches)
{
var next = codeGen.DefineLabel();
codeGen.Emit(OpCodes.Nop);
if (branch.HasContext)
{
codeGen.Ldloc(context.ContextLocal);

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
public class OnFormFactorExtension : OnFormFactorExtensionBase<object, On>
public sealed class OnFormFactorExtension : OnFormFactorExtensionBase<object, On>
{
public OnFormFactorExtension()
{
@ -24,7 +24,7 @@ public class OnFormFactorExtension : OnFormFactorExtensionBase<object, On>
}
}
public class OnFormFactorExtension<TReturn> : OnFormFactorExtensionBase<TReturn, On<TReturn>>
public sealed class OnFormFactorExtension<TReturn> : OnFormFactorExtensionBase<TReturn, On<TReturn>>
{
public OnFormFactorExtension()
{

43
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs

@ -1,11 +1,11 @@
#nullable enable
using System;
using Avalonia.Compatibility;
using Avalonia.Metadata;
using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
public class OnPlatformExtension : OnPlatformExtensionBase<object, On>
public sealed class OnPlatformExtension : OnPlatformExtensionBase<object, On>
{
public OnPlatformExtension()
{
@ -17,13 +17,13 @@ public class OnPlatformExtension : OnPlatformExtensionBase<object, On>
Default = defaultValue;
}
public static bool ShouldProvideOption(IServiceProvider serviceProvider, OperatingSystemType option)
public static bool ShouldProvideOption(string option)
{
return serviceProvider.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem == option;
return ShouldProvideOptionInternal(option);
}
}
public class OnPlatformExtension<TReturn> : OnPlatformExtensionBase<TReturn, On<TReturn>>
public sealed class OnPlatformExtension<TReturn> : OnPlatformExtensionBase<TReturn, On<TReturn>>
{
public OnPlatformExtension()
{
@ -35,9 +35,9 @@ public class OnPlatformExtension<TReturn> : OnPlatformExtensionBase<TReturn, On<
Default = defaultValue;
}
public static bool ShouldProvideOption(IServiceProvider serviceProvider, OperatingSystemType option)
public static bool ShouldProvideOption(string option)
{
return serviceProvider.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem == option;
return ShouldProvideOptionInternal(option);
}
}
@ -47,27 +47,44 @@ public abstract class OnPlatformExtensionBase<TReturn, TOn> : IAddChild<TOn>
[MarkupExtensionDefaultOption]
public TReturn? Default { get; set; }
[MarkupExtensionOption(OperatingSystemType.WinNT)]
[MarkupExtensionOption("WINDOWS")]
public TReturn? Windows { get; set; }
[MarkupExtensionOption(OperatingSystemType.OSX)]
[MarkupExtensionOption("OSX")]
// ReSharper disable once InconsistentNaming
public TReturn? macOS { get; set; }
[MarkupExtensionOption(OperatingSystemType.Linux)]
[MarkupExtensionOption("LINUX")]
public TReturn? Linux { get; set; }
[MarkupExtensionOption(OperatingSystemType.Android)]
[MarkupExtensionOption("ANDROID")]
public TReturn? Android { get; set; }
[MarkupExtensionOption(OperatingSystemType.iOS)]
[MarkupExtensionOption("IOS")]
// ReSharper disable once InconsistentNaming
public TReturn? iOS { get; set; }
[MarkupExtensionOption(OperatingSystemType.Browser)]
[MarkupExtensionOption("BROWSER")]
public TReturn? Browser { get; set; }
// Required for the compiler, will be replaced with actual method compile time.
public object ProvideValue() { return this; }
void IAddChild<TOn>.AddChild(TOn child) {}
private protected static bool ShouldProvideOptionInternal(string option)
{
// Instead of using OperatingSystem.IsOSPlatform(string) we use specific "Is***" methods so whole method can be trimmed by the mono linked.
// Keep in mind it works only with const "option" parameter.
// IsOSPlatform might work better with trimming in the future, so it should be re-visited after .NET 8/9.
return option switch
{
"WINDOWS" => OperatingSystemEx.IsWindows(),
"OSX" => OperatingSystemEx.IsMacOS(),
"LINUX" => OperatingSystemEx.IsLinux(),
"ANDROID" => OperatingSystemEx.IsAndroid(),
"IOS" => OperatingSystemEx.IsIOS(),
"BROWSER" => OperatingSystemEx.IsBrowser(),
_ => OperatingSystemEx.IsOSPlatform(option)
};
}
}

8
src/Skia/Avalonia.Skia/Helpers/PixelFormatHelper.cs

@ -1,4 +1,5 @@
using Avalonia.Platform;
using Avalonia.Compatibility;
using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia.Helpers
@ -18,10 +19,7 @@ namespace Avalonia.Skia.Helpers
var colorType = format?.ToSkColorType() ?? SKImageInfo.PlatformColorType;
// TODO: This looks like some leftover hack
var runtimePlatform = AvaloniaLocator.Current?.GetService<IRuntimePlatform>();
var runtime = runtimePlatform?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime.Value.OperatingSystem == OperatingSystemType.Linux)
if (OperatingSystemEx.IsLinux())
{
colorType = SKColorType.Bgra8888;
}

75
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs

@ -1,75 +0,0 @@
using Avalonia.Controls;
using Avalonia.Platform;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;
public class OnPlatformExtensionTests : XamlTestBase
{
[Fact]
public void Should_Resolve_Default_Value()
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>()
.ToConstant(new TestRuntimePlatform(OperatingSystemType.Unknown));
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<TextBlock Text='{OnPlatform Default=""Hello World""}'/>
</UserControl>";
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = (TextBlock)userControl.Content!;
Assert.Equal("Hello World", textBlock.Text);
}
}
[Theory]
[InlineData(OperatingSystemType.WinNT, "Im Windows")]
[InlineData(OperatingSystemType.OSX, "Im macOS")]
[InlineData(OperatingSystemType.Linux, "Im Linux")]
[InlineData(OperatingSystemType.Android, "Im Android")]
[InlineData(OperatingSystemType.iOS, "Im iOS")]
[InlineData(OperatingSystemType.Browser, "Im Browser")]
[InlineData(OperatingSystemType.Unknown, "Default value")]
public void Should_Resolve_Expected_Value_Per_Platform(OperatingSystemType currentPlatform, string expectedResult)
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>()
.ToConstant(new TestRuntimePlatform(currentPlatform));
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<TextBlock Text='{OnPlatform ""Default value"",
Windows=""Im Windows"", macOS=""Im macOS"",
Linux=""Im Linux"", Android=""Im Android"",
iOS=""Im iOS"", Browser=""Im Browser""}'/>
</UserControl>";
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = (TextBlock)userControl.Content!;
Assert.Equal(expectedResult, textBlock.Text);
}
}
private class TestRuntimePlatform : StandardRuntimePlatform
{
private readonly OperatingSystemType _operatingSystemType;
public TestRuntimePlatform(OperatingSystemType operatingSystemType)
{
_operatingSystemType = operatingSystemType;
}
public override RuntimePlatformInfo GetRuntimeInfo()
{
return new RuntimePlatformInfo() { OperatingSystem = _operatingSystemType };
}
}
}

2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OptionsMarkupExtensionTests.cs

@ -434,7 +434,7 @@ public class OptionsMarkupExtensionTests : XamlTestBase
<TextBlock xmlns='https://github.com/avaloniaui'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Text='{local:OptionsMarkupExtensionNoServiceProvider OptionB=""Im Option 2""}' />";
Text='{local:OptionsMarkupExtensionNoServiceProvider OptionB=""Im Option 2"", OptionA=""Im Option 1""}' />";
var textBlock = (TextBlock)AvaloniaRuntimeXamlLoader.Load(xaml);

Loading…
Cancel
Save