committed by
GitHub
31 changed files with 314 additions and 1834 deletions
File diff suppressed because it is too large
@ -1,4 +1,4 @@ |
|||||
namespace Avalonia.PlatformSupport.Internal; |
namespace Avalonia.Platform.Internal; |
||||
|
|
||||
internal static class Constants |
internal static class Constants |
||||
{ |
{ |
||||
@ -1,7 +1,7 @@ |
|||||
using System; |
using System; |
||||
using System.IO; |
using System.IO; |
||||
|
|
||||
namespace Avalonia.PlatformSupport.Internal; |
namespace Avalonia.Platform.Internal; |
||||
|
|
||||
internal class SlicedStream : Stream |
internal class SlicedStream : Stream |
||||
{ |
{ |
||||
@ -0,0 +1,155 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace Avalonia.Platform.Internal; |
||||
|
|
||||
|
internal class UnmanagedBlob : IUnmanagedBlob |
||||
|
{ |
||||
|
private IntPtr _address; |
||||
|
private readonly object _lock = new object(); |
||||
|
#if DEBUG
|
||||
|
private static readonly List<string> Backtraces = new List<string>(); |
||||
|
private static Thread? GCThread; |
||||
|
private readonly string _backtrace; |
||||
|
private static readonly object _btlock = new object(); |
||||
|
|
||||
|
class GCThreadDetector |
||||
|
{ |
||||
|
~GCThreadDetector() |
||||
|
{ |
||||
|
GCThread = Thread.CurrentThread; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
static void Spawn() => new GCThreadDetector(); |
||||
|
|
||||
|
static UnmanagedBlob() |
||||
|
{ |
||||
|
Spawn(); |
||||
|
GC.WaitForPendingFinalizers(); |
||||
|
} |
||||
|
#endif
|
||||
|
|
||||
|
public UnmanagedBlob(int size) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (size <= 0) |
||||
|
throw new ArgumentException("Positive number required", nameof(size)); |
||||
|
_address = Alloc(size); |
||||
|
GC.AddMemoryPressure(size); |
||||
|
Size = size; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
GC.SuppressFinalize(this); |
||||
|
throw; |
||||
|
} |
||||
|
#if DEBUG
|
||||
|
_backtrace = Environment.StackTrace; |
||||
|
lock (_btlock) |
||||
|
Backtraces.Add(_backtrace); |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
void DoDispose() |
||||
|
{ |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
if (!IsDisposed) |
||||
|
{ |
||||
|
#if DEBUG
|
||||
|
lock (_btlock) |
||||
|
Backtraces.Remove(_backtrace); |
||||
|
#endif
|
||||
|
Free(_address, Size); |
||||
|
GC.RemoveMemoryPressure(Size); |
||||
|
IsDisposed = true; |
||||
|
_address = IntPtr.Zero; |
||||
|
Size = 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
#if DEBUG
|
||||
|
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) |
||||
|
{ |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
if (!IsDisposed) |
||||
|
{ |
||||
|
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " |
||||
|
+ Environment.StackTrace |
||||
|
+ "\n\nBlob created by " + _backtrace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
|
DoDispose(); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
~UnmanagedBlob() |
||||
|
{ |
||||
|
#if DEBUG
|
||||
|
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); |
||||
|
#endif
|
||||
|
DoDispose(); |
||||
|
} |
||||
|
|
||||
|
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; |
||||
|
public int Size { get; private set; } |
||||
|
public bool IsDisposed { get; private set; } |
||||
|
|
||||
|
[DllImport("libc", SetLastError = true)] |
||||
|
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); |
||||
|
[DllImport("libc", SetLastError = true)] |
||||
|
private static extern int munmap(IntPtr addr, IntPtr length); |
||||
|
[DllImport("libc", SetLastError = true)] |
||||
|
private static extern long sysconf(int name); |
||||
|
|
||||
|
private bool? _useMmap; |
||||
|
private bool UseMmap |
||||
|
=> _useMmap ?? ((_useMmap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value); |
||||
|
|
||||
|
// Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available.
|
||||
|
private IntPtr Alloc(int size) |
||||
|
{ |
||||
|
if (!UseMmap) |
||||
|
{ |
||||
|
return Marshal.AllocHGlobal(size); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); |
||||
|
if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) |
||||
|
{ |
||||
|
var errno = Marshal.GetLastWin32Error(); |
||||
|
throw new Exception("Unable to allocate memory: " + errno); |
||||
|
} |
||||
|
return rv; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void Free(IntPtr ptr, int len) |
||||
|
{ |
||||
|
if (!UseMmap) |
||||
|
{ |
||||
|
Marshal.FreeHGlobal(ptr); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (munmap(ptr, new IntPtr(len)) == -1) |
||||
|
{ |
||||
|
var errno = Marshal.GetLastWin32Error(); |
||||
|
throw new Exception("Unable to free memory: " + errno); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Threading; |
||||
|
using Avalonia.Platform.Internal; |
||||
|
|
||||
|
namespace Avalonia.Platform |
||||
|
{ |
||||
|
public class StandardRuntimePlatform : IRuntimePlatform |
||||
|
{ |
||||
|
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
||||
|
{ |
||||
|
return new Timer(_ => tick(), null, interval, interval); |
||||
|
} |
||||
|
|
||||
|
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); |
||||
|
|
||||
|
private static readonly Lazy<RuntimePlatformInfo> Info = new(() => |
||||
|
{ |
||||
|
OperatingSystemType os; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
} |
||||
@ -1,30 +1,33 @@ |
|||||
using System.Reflection; |
using System.Reflection; |
||||
using Avalonia.Platform; |
using Avalonia.Platform.Internal; |
||||
using Avalonia.Platform.Interop; |
using Avalonia.Platform.Interop; |
||||
|
|
||||
namespace Avalonia.PlatformSupport |
namespace Avalonia.Platform |
||||
{ |
{ |
||||
public static class StandardRuntimePlatformServices |
public static class StandardRuntimePlatformServices |
||||
{ |
{ |
||||
public static void Register(Assembly? assembly = null) |
public static void Register(Assembly? assembly = null) |
||||
{ |
{ |
||||
var standardPlatform = new StandardRuntimePlatform(); |
var standardPlatform = new StandardRuntimePlatform(); |
||||
var os = standardPlatform.GetRuntimeInfo().OperatingSystem; |
|
||||
|
|
||||
AssetLoader.RegisterResUriParsers(); |
AssetLoader.RegisterResUriParsers(); |
||||
AvaloniaLocator.CurrentMutable |
AvaloniaLocator.CurrentMutable |
||||
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
||||
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
||||
.Bind<IDynamicLibraryLoader>().ToConstant( |
.Bind<IDynamicLibraryLoader>().ToConstant( |
||||
os switch |
#if NET6_0_OR_GREATER
|
||||
|
new Net6Loader() |
||||
|
#else
|
||||
|
standardPlatform.GetRuntimeInfo().OperatingSystem switch |
||||
{ |
{ |
||||
OperatingSystemType.WinNT => new Win32Loader(), |
OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(), |
||||
OperatingSystemType.OSX => new UnixLoader(), |
OperatingSystemType.OSX => new UnixLoader(), |
||||
OperatingSystemType.Linux => new UnixLoader(), |
OperatingSystemType.Linux => new UnixLoader(), |
||||
OperatingSystemType.Android => new UnixLoader(), |
OperatingSystemType.Android => new UnixLoader(), |
||||
// iOS, WASM, ...
|
// iOS, WASM, ...
|
||||
_ => (IDynamicLibraryLoader)new NotSupportedLoader() |
_ => new NotSupportedLoader() |
||||
} |
} |
||||
|
#endif
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,5 +1,5 @@ |
|||||
using Avalonia.Controls; |
using Avalonia.Controls; |
||||
using Avalonia.PlatformSupport; |
using Avalonia.Platform; |
||||
|
|
||||
namespace Avalonia |
namespace Avalonia |
||||
{ |
{ |
||||
@ -1,24 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFrameworks>net6.0;net461;netstandard2.0</TargetFrameworks> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="../Avalonia.Base/Avalonia.Base.csproj" /> |
|
||||
<ProjectReference Include="../Avalonia.Controls/Avalonia.Controls.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" Condition="'$(TargetFramework)' == 'net461'" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<Import Project="..\..\build\NetCore.props" /> |
|
||||
<Import Project="..\..\build\NetFX.props" /> |
|
||||
<Import Project="..\..\build\NullableEnable.props" /> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<InternalsVisibleTo Include="$(AssemblyName).UnitTests, PublicKey=$(AvaloniaPublicKey)" /> |
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" /> |
|
||||
</ItemGroup> |
|
||||
</Project> |
|
||||
@ -1,218 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Threading; |
|
||||
using Avalonia.Platform; |
|
||||
|
|
||||
namespace Avalonia.PlatformSupport |
|
||||
{ |
|
||||
public class StandardRuntimePlatform : IRuntimePlatform |
|
||||
{ |
|
||||
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
|
||||
{ |
|
||||
return new Timer(_ => tick(), null, interval, interval); |
|
||||
} |
|
||||
|
|
||||
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size); |
|
||||
|
|
||||
private class UnmanagedBlob : IUnmanagedBlob |
|
||||
{ |
|
||||
private readonly StandardRuntimePlatform _plat; |
|
||||
private IntPtr _address; |
|
||||
private readonly object _lock = new object(); |
|
||||
#if DEBUG
|
|
||||
private static readonly List<string> Backtraces = new List<string>(); |
|
||||
private static Thread? GCThread; |
|
||||
private readonly string _backtrace; |
|
||||
private static readonly object _btlock = new object(); |
|
||||
|
|
||||
class GCThreadDetector |
|
||||
{ |
|
||||
~GCThreadDetector() |
|
||||
{ |
|
||||
GCThread = Thread.CurrentThread; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
|
||||
static void Spawn() => new GCThreadDetector(); |
|
||||
|
|
||||
static UnmanagedBlob() |
|
||||
{ |
|
||||
Spawn(); |
|
||||
GC.WaitForPendingFinalizers(); |
|
||||
} |
|
||||
#endif
|
|
||||
|
|
||||
public UnmanagedBlob(StandardRuntimePlatform plat, int size) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
if (size <= 0) |
|
||||
throw new ArgumentException("Positive number required", nameof(size)); |
|
||||
_plat = plat; |
|
||||
_address = plat.Alloc(size); |
|
||||
GC.AddMemoryPressure(size); |
|
||||
Size = size; |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
GC.SuppressFinalize(this); |
|
||||
throw; |
|
||||
} |
|
||||
#if DEBUG
|
|
||||
_backtrace = Environment.StackTrace; |
|
||||
lock (_btlock) |
|
||||
Backtraces.Add(_backtrace); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
void DoDispose() |
|
||||
{ |
|
||||
lock (_lock) |
|
||||
{ |
|
||||
if (!IsDisposed) |
|
||||
{ |
|
||||
#if DEBUG
|
|
||||
lock (_btlock) |
|
||||
Backtraces.Remove(_backtrace); |
|
||||
#endif
|
|
||||
_plat?.Free(_address, Size); |
|
||||
GC.RemoveMemoryPressure(Size); |
|
||||
IsDisposed = true; |
|
||||
_address = IntPtr.Zero; |
|
||||
Size = 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
#if DEBUG
|
|
||||
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) |
|
||||
{ |
|
||||
lock (_lock) |
|
||||
{ |
|
||||
if (!IsDisposed) |
|
||||
{ |
|
||||
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " |
|
||||
+ Environment.StackTrace |
|
||||
+ "\n\nBlob created by " + _backtrace); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
DoDispose(); |
|
||||
GC.SuppressFinalize(this); |
|
||||
} |
|
||||
|
|
||||
~UnmanagedBlob() |
|
||||
{ |
|
||||
#if DEBUG
|
|
||||
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); |
|
||||
#endif
|
|
||||
DoDispose(); |
|
||||
} |
|
||||
|
|
||||
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; |
|
||||
public int Size { get; private set; } |
|
||||
public bool IsDisposed { get; private set; } |
|
||||
} |
|
||||
|
|
||||
#if NET461 || NETCOREAPP2_0_OR_GREATER
|
|
||||
[DllImport("libc", SetLastError = true)] |
|
||||
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); |
|
||||
[DllImport("libc", SetLastError = true)] |
|
||||
private static extern int munmap(IntPtr addr, IntPtr length); |
|
||||
[DllImport("libc", SetLastError = true)] |
|
||||
private static extern long sysconf(int name); |
|
||||
|
|
||||
private bool? _useMmap; |
|
||||
private bool UseMmap |
|
||||
=> _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value; |
|
||||
|
|
||||
IntPtr Alloc(int size) |
|
||||
{ |
|
||||
if (UseMmap) |
|
||||
{ |
|
||||
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); |
|
||||
if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) |
|
||||
{ |
|
||||
var errno = Marshal.GetLastWin32Error(); |
|
||||
throw new Exception("Unable to allocate memory: " + errno); |
|
||||
} |
|
||||
return rv; |
|
||||
} |
|
||||
else |
|
||||
return Marshal.AllocHGlobal(size); |
|
||||
} |
|
||||
|
|
||||
void Free(IntPtr ptr, int len) |
|
||||
{ |
|
||||
if (UseMmap) |
|
||||
{ |
|
||||
if (munmap(ptr, new IntPtr(len)) == -1) |
|
||||
{ |
|
||||
var errno = Marshal.GetLastWin32Error(); |
|
||||
throw new Exception("Unable to free memory: " + errno); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
Marshal.FreeHGlobal(ptr); |
|
||||
} |
|
||||
#else
|
|
||||
IntPtr Alloc(int size) => Marshal.AllocHGlobal(size); |
|
||||
void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr); |
|
||||
#endif
|
|
||||
|
|
||||
private static readonly Lazy<RuntimePlatformInfo> Info = new Lazy<RuntimePlatformInfo>(() => |
|
||||
{ |
|
||||
OperatingSystemType os; |
|
||||
|
|
||||
#if NET5_0_OR_GREATER
|
|
||||
if (OperatingSystem.IsWindows()) |
|
||||
os = OperatingSystemType.WinNT; |
|
||||
else if (OperatingSystem.IsMacOS()) |
|
||||
os = OperatingSystemType.OSX; |
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) |
|
||||
os = OperatingSystemType.Linux; |
|
||||
else if (OperatingSystem.IsAndroid()) |
|
||||
os = OperatingSystemType.Android; |
|
||||
else if (OperatingSystem.IsIOS()) |
|
||||
os = OperatingSystemType.iOS; |
|
||||
else if (OperatingSystem.IsBrowser()) |
|
||||
os = OperatingSystemType.Browser; |
|
||||
else |
|
||||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|
||||
#else
|
|
||||
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 |
|
||||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|
||||
#endif
|
|
||||
|
|
||||
return new RuntimePlatformInfo |
|
||||
{ |
|
||||
#if NETCOREAPP
|
|
||||
IsCoreClr = true, |
|
||||
#elif NETFRAMEWORK
|
|
||||
IsDotNetFramework = true, |
|
||||
#endif
|
|
||||
IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT, |
|
||||
IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser, |
|
||||
IsMobile = os == OperatingSystemType.Android || os == OperatingSystemType.iOS, |
|
||||
IsUnix = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.Android, |
|
||||
IsBrowser = os == OperatingSystemType.Browser, |
|
||||
OperatingSystem = os, |
|
||||
}; |
|
||||
}); |
|
||||
|
|
||||
|
|
||||
public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; |
|
||||
} |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>net6.0</TargetFramework> |
|
||||
<Nullable>enable</Nullable> |
|
||||
<OutputType>Library</OutputType> |
|
||||
<IsTestProject>true</IsTestProject> |
|
||||
<LangVersion>latest</LangVersion> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<Import Project="..\..\build\UnitTests.NetCore.targets" /> |
|
||||
<Import Project="..\..\build\UnitTests.NetFX.props" /> |
|
||||
<Import Project="..\..\build\Moq.props" /> |
|
||||
<Import Project="..\..\build\XUnit.props" /> |
|
||||
<Import Project="..\..\build\Rx.props" /> |
|
||||
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> |
|
||||
<Import Project="..\..\build\SharedVersion.props" /> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj" /> |
|
||||
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
Loading…
Reference in new issue