diff --git a/readme.md b/readme.md index fcc3379f..8830e6ad 100644 --- a/readme.md +++ b/readme.md @@ -147,6 +147,14 @@ Available parameters: *By default*: true +``-m, --mvvm`` + +*Description*: MVVM toolkit to use in the template. + +*Options*: **ReactiveUI**, **CommunityToolkit** + +*By default*: ReactiveUI + ``-av, --avalonia-version`` *Description*: The target version of Avalonia NuGet packages. diff --git a/templates/csharp/xplat/.template.config/dotnetcli.host.json b/templates/csharp/xplat/.template.config/dotnetcli.host.json index d68268f6..b4e57786 100644 --- a/templates/csharp/xplat/.template.config/dotnetcli.host.json +++ b/templates/csharp/xplat/.template.config/dotnetcli.host.json @@ -10,11 +10,14 @@ "UseCompiledBindings": { "longName": "compiled-bindings" }, + "MVVMToolkit": { + "longName": "mvvm" + }, "RemoveViewLocator": { "longName": "remove-view-locator" } }, "usageExamples": [ - "" + "--mvvm communitytoolkit" ] } \ No newline at end of file diff --git a/templates/csharp/xplat/.template.config/ide.host.json b/templates/csharp/xplat/.template.config/ide.host.json index 287ea1de..4774aa44 100644 --- a/templates/csharp/xplat/.template.config/ide.host.json +++ b/templates/csharp/xplat/.template.config/ide.host.json @@ -9,6 +9,13 @@ }, "isVisible": true }, + { + "id": "MVVMToolkit", + "name": { + "text": "MVVM Toolkit" + }, + "isVisible": true + }, { "id": "UseCompiledBindings", "name": { diff --git a/templates/csharp/xplat/.template.config/template.json b/templates/csharp/xplat/.template.config/template.json index bdb47ac3..f3cf24eb 100644 --- a/templates/csharp/xplat/.template.config/template.json +++ b/templates/csharp/xplat/.template.config/template.json @@ -32,6 +32,30 @@ "replaces": "FrameworkParameter", "defaultValue": "net8.0" }, + "MVVMToolkit": { + "type": "parameter", + "description": "MVVM toolkit to use in the template.", + "datatype": "choice", + "choices": [ + { + "choice": "ReactiveUI", + "description": "Choose ReactiveUI as MVVM toolkit in the template." + }, + { + "choice": "CommunityToolkit", + "description": "Choose CommunityToolkit as MVVM toolkit in the template." + } + ], + "defaultValue": "ReactiveUI" + }, + "ReactiveUIToolkitChosen": { + "type": "computed", + "value": "(MVVMToolkit == \"ReactiveUI\")" + }, + "CommunityToolkitChosen": { + "type": "computed", + "value": "(MVVMToolkit == \"CommunityToolkit\")" + }, "AvaloniaVersion": { "type": "parameter", "description": "The target version of Avalonia NuGet packages.", diff --git a/templates/csharp/xplat/AvaloniaTest.Android/MainActivity.cs b/templates/csharp/xplat/AvaloniaTest.Android/MainActivity.cs index 9ac57834..b7f9d82f 100644 --- a/templates/csharp/xplat/AvaloniaTest.Android/MainActivity.cs +++ b/templates/csharp/xplat/AvaloniaTest.Android/MainActivity.cs @@ -2,7 +2,9 @@ using Android.Content.PM; using Avalonia; using Avalonia.Android; +#if (ReactiveUIToolkitChosen) using Avalonia.ReactiveUI; +#endif namespace AvaloniaTest.Android; @@ -17,7 +19,11 @@ public class MainActivity : AvaloniaMainActivity protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) { return base.CustomizeAppBuilder(builder) +#if (CommunityToolkitChosen) + .WithInterFont(); +#else .WithInterFont() .UseReactiveUI(); +#endif } } diff --git a/templates/csharp/xplat/AvaloniaTest.Browser/Program.cs b/templates/csharp/xplat/AvaloniaTest.Browser/Program.cs index e0ab2612..94035fbd 100644 --- a/templates/csharp/xplat/AvaloniaTest.Browser/Program.cs +++ b/templates/csharp/xplat/AvaloniaTest.Browser/Program.cs @@ -2,7 +2,9 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Browser; +#if (ReactiveUIToolkitChosen) using Avalonia.ReactiveUI; +#endif using AvaloniaTest; [assembly: SupportedOSPlatform("browser")] @@ -11,7 +13,9 @@ internal sealed partial class Program { private static Task Main(string[] args) => BuildAvaloniaApp() .WithInterFont() +#if (ReactiveUIToolkitChosen) .UseReactiveUI() +#endif .StartBrowserAppAsync("out"); public static AppBuilder BuildAvaloniaApp() diff --git a/templates/csharp/xplat/AvaloniaTest.Desktop/Program.cs b/templates/csharp/xplat/AvaloniaTest.Desktop/Program.cs index 080b0f61..78e2a12b 100644 --- a/templates/csharp/xplat/AvaloniaTest.Desktop/Program.cs +++ b/templates/csharp/xplat/AvaloniaTest.Desktop/Program.cs @@ -1,6 +1,8 @@ using System; using Avalonia; +#if (ReactiveUIToolkitChosen) using Avalonia.ReactiveUI; +#endif namespace AvaloniaTest.Desktop; @@ -18,6 +20,8 @@ public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .LogToTrace() - .UseReactiveUI(); +#if (ReactiveUIToolkitChosen) + .UseReactiveUI() +#endif + .LogToTrace(); } diff --git a/templates/csharp/xplat/AvaloniaTest.iOS/AppDelegate.cs b/templates/csharp/xplat/AvaloniaTest.iOS/AppDelegate.cs index 6612b3ee..1e0bdff6 100644 --- a/templates/csharp/xplat/AvaloniaTest.iOS/AppDelegate.cs +++ b/templates/csharp/xplat/AvaloniaTest.iOS/AppDelegate.cs @@ -4,7 +4,9 @@ using Avalonia.Controls; using Avalonia.iOS; using Avalonia.Media; +#if (ReactiveUIToolkitChosen) using Avalonia.ReactiveUI; +#endif namespace AvaloniaTest.iOS; @@ -19,7 +21,11 @@ public partial class AppDelegate : AvaloniaAppDelegate protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) { return base.CustomizeAppBuilder(builder) +#if (CommunityToolkitChosen) + .WithInterFont(); +#else .WithInterFont() .UseReactiveUI(); +#endif } } diff --git a/templates/csharp/xplat/AvaloniaTest/App.axaml.cs b/templates/csharp/xplat/AvaloniaTest/App.axaml.cs index 5530a141..7e4f8ebe 100644 --- a/templates/csharp/xplat/AvaloniaTest/App.axaml.cs +++ b/templates/csharp/xplat/AvaloniaTest/App.axaml.cs @@ -1,5 +1,9 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +#if (CommunityToolkitChosen) +using Avalonia.Data.Core; +using Avalonia.Data.Core.Plugins; +#endif using Avalonia.Markup.Xaml; using AvaloniaTest.ViewModels; using AvaloniaTest.Views; @@ -17,6 +21,11 @@ public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { +#if (CommunityToolkitChosen) + // Line below is needed to remove Avalonia data validation. + // Without this line you will get duplicate validations from both Avalonia and CT + BindingPlugins.DataValidators.RemoveAt(0); +#endif desktop.MainWindow = new MainWindow { DataContext = new MainViewModel() diff --git a/templates/csharp/xplat/AvaloniaTest/AvaloniaTest.csproj b/templates/csharp/xplat/AvaloniaTest/AvaloniaTest.csproj index 10934d0e..6a42d303 100644 --- a/templates/csharp/xplat/AvaloniaTest/AvaloniaTest.csproj +++ b/templates/csharp/xplat/AvaloniaTest/AvaloniaTest.csproj @@ -15,9 +15,13 @@ - - + + + + + + diff --git a/templates/csharp/xplat/AvaloniaTest/ViewModels/MainViewModel.cs b/templates/csharp/xplat/AvaloniaTest/ViewModels/MainViewModel.cs index 2daa637d..a48feba4 100644 --- a/templates/csharp/xplat/AvaloniaTest/ViewModels/MainViewModel.cs +++ b/templates/csharp/xplat/AvaloniaTest/ViewModels/MainViewModel.cs @@ -1,8 +1,21 @@ -namespace AvaloniaTest.ViewModels; +#if (CommunityToolkitChosen) +using CommunityToolkit.Mvvm.ComponentModel; +#endif +namespace AvaloniaTest.ViewModels; + +#if (CommunityToolkitChosen) +public partial class MainViewModel : ViewModelBase +#else public class MainViewModel : ViewModelBase +#endif { +#if (CommunityToolkitChosen) + [ObservableProperty] + private string _greeting = "Welcome to Avalonia!"; +#else #pragma warning disable CA1822 // Mark members as static public string Greeting => "Welcome to Avalonia!"; #pragma warning restore CA1822 // Mark members as static +#endif } diff --git a/templates/csharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.cs b/templates/csharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.cs index 3b1a38de..d0e973a9 100644 --- a/templates/csharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.cs +++ b/templates/csharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.cs @@ -1,7 +1,15 @@ -using ReactiveUI; +#if (CommunityToolkitChosen) +using CommunityToolkit.Mvvm.ComponentModel; +#elif (ReactiveUIToolkitChosen) +using ReactiveUI; +#endif namespace AvaloniaTest.ViewModels; -public class ViewModelBase : ReactiveObject +#if (CommunityToolkitChosen) +public abstract class ViewModelBase : ObservableObject +#elif (ReactiveUIToolkitChosen) +public abstract class ViewModelBase : ReactiveObject +#endif { } diff --git a/templates/fsharp/app-mvvm/ViewModels/ViewModelBase.fs b/templates/fsharp/app-mvvm/ViewModels/ViewModelBase.fs index fc704833..cd8ef1a8 100644 --- a/templates/fsharp/app-mvvm/ViewModels/ViewModelBase.fs +++ b/templates/fsharp/app-mvvm/ViewModels/ViewModelBase.fs @@ -6,6 +6,7 @@ open CommunityToolkit.Mvvm.ComponentModel open ReactiveUI #endif +[] type ViewModelBase() = #if (CommunityToolkitChosen) inherit ObservableObject() diff --git a/templates/fsharp/xplat/.template.config/dotnetcli.host.json b/templates/fsharp/xplat/.template.config/dotnetcli.host.json index d68268f6..b4e57786 100644 --- a/templates/fsharp/xplat/.template.config/dotnetcli.host.json +++ b/templates/fsharp/xplat/.template.config/dotnetcli.host.json @@ -10,11 +10,14 @@ "UseCompiledBindings": { "longName": "compiled-bindings" }, + "MVVMToolkit": { + "longName": "mvvm" + }, "RemoveViewLocator": { "longName": "remove-view-locator" } }, "usageExamples": [ - "" + "--mvvm communitytoolkit" ] } \ No newline at end of file diff --git a/templates/fsharp/xplat/.template.config/ide.host.json b/templates/fsharp/xplat/.template.config/ide.host.json index 287ea1de..4774aa44 100644 --- a/templates/fsharp/xplat/.template.config/ide.host.json +++ b/templates/fsharp/xplat/.template.config/ide.host.json @@ -9,6 +9,13 @@ }, "isVisible": true }, + { + "id": "MVVMToolkit", + "name": { + "text": "MVVM Toolkit" + }, + "isVisible": true + }, { "id": "UseCompiledBindings", "name": { diff --git a/templates/fsharp/xplat/.template.config/template.json b/templates/fsharp/xplat/.template.config/template.json index 3aecf274..2d28ea5e 100644 --- a/templates/fsharp/xplat/.template.config/template.json +++ b/templates/fsharp/xplat/.template.config/template.json @@ -32,6 +32,30 @@ "replaces": "FrameworkParameter", "defaultValue": "net8.0" }, + "MVVMToolkit": { + "type": "parameter", + "description": "MVVM toolkit to use in the template.", + "datatype": "choice", + "choices": [ + { + "choice": "ReactiveUI", + "description": "Choose ReactiveUI as MVVM toolkit in the template." + }, + { + "choice": "CommunityToolkit", + "description": "Choose CommunityToolkit as MVVM toolkit in the template." + } + ], + "defaultValue": "ReactiveUI" + }, + "ReactiveUIToolkitChosen": { + "type": "computed", + "value": "(MVVMToolkit == \"ReactiveUI\")" + }, + "CommunityToolkitChosen": { + "type": "computed", + "value": "(MVVMToolkit == \"CommunityToolkit\")" + }, "AvaloniaVersion": { "type": "parameter", "description": "The target version of Avalonia NuGet packages.", diff --git a/templates/fsharp/xplat/AvaloniaTest.Android/Activities.fs b/templates/fsharp/xplat/AvaloniaTest.Android/Activities.fs index b194fca1..0ac84a2d 100644 --- a/templates/fsharp/xplat/AvaloniaTest.Android/Activities.fs +++ b/templates/fsharp/xplat/AvaloniaTest.Android/Activities.fs @@ -3,7 +3,9 @@ namespace AvaloniaTest.Android open Android.App open Android.Content.PM open Avalonia +#if (ReactiveUIToolkitChosen) open Avalonia.ReactiveUI +#endif open Avalonia.Android open AvaloniaTest @@ -19,4 +21,6 @@ type MainActivity() = override _.CustomizeAppBuilder(builder) = base.CustomizeAppBuilder(builder) .WithInterFont() +#if (ReactiveUIToolkitChosen) .UseReactiveUI() +#endif diff --git a/templates/fsharp/xplat/AvaloniaTest.Browser/Program.fs b/templates/fsharp/xplat/AvaloniaTest.Browser/Program.fs index f115d515..66cbee26 100644 --- a/templates/fsharp/xplat/AvaloniaTest.Browser/Program.fs +++ b/templates/fsharp/xplat/AvaloniaTest.Browser/Program.fs @@ -1,8 +1,9 @@ open System.Runtime.Versioning open Avalonia open Avalonia.Browser +#if (ReactiveUIToolkitChosen) open Avalonia.ReactiveUI - +#endif open AvaloniaTest module Program = @@ -19,7 +20,9 @@ module Program = task { do! (buildAvaloniaApp() .WithInterFont() +#if (ReactiveUIToolkitChosen) .UseReactiveUI() +#endif .StartBrowserAppAsync("out")) } |> ignore diff --git a/templates/fsharp/xplat/AvaloniaTest.Desktop/Program.fs b/templates/fsharp/xplat/AvaloniaTest.Desktop/Program.fs index 556fe3f3..5b91bda9 100644 --- a/templates/fsharp/xplat/AvaloniaTest.Desktop/Program.fs +++ b/templates/fsharp/xplat/AvaloniaTest.Desktop/Program.fs @@ -1,7 +1,9 @@ namespace AvaloniaTest.Desktop open System open Avalonia +#if (ReactiveUIToolkitChosen) open Avalonia.ReactiveUI +#endif open AvaloniaTest module Program = @@ -13,7 +15,9 @@ module Program = .UsePlatformDetect() .WithInterFont() .LogToTrace(areas = Array.empty) +#if (ReactiveUIToolkitChosen) .UseReactiveUI() +#endif [] let main argv = diff --git a/templates/fsharp/xplat/AvaloniaTest.iOS/AppDelegate.fs b/templates/fsharp/xplat/AvaloniaTest.iOS/AppDelegate.fs index d956cff2..81101d87 100644 --- a/templates/fsharp/xplat/AvaloniaTest.iOS/AppDelegate.fs +++ b/templates/fsharp/xplat/AvaloniaTest.iOS/AppDelegate.fs @@ -2,7 +2,9 @@ namespace AvaloniaTest.iOS open Foundation open Avalonia open Avalonia.iOS +#if (ReactiveUIToolkitChosen) open Avalonia.ReactiveUI +#endif // The UIApplicationDelegate for the application. This class is responsible for launching the // User Interface of the application, as well as listening (and optionally responding) to @@ -13,4 +15,6 @@ type [] AppDelegate() = override _.CustomizeAppBuilder(builder) = base.CustomizeAppBuilder(builder) .WithInterFont() - .UseReactiveUI() \ No newline at end of file +#if (ReactiveUIToolkitChosen) + .UseReactiveUI() +#endif \ No newline at end of file diff --git a/templates/fsharp/xplat/AvaloniaTest/App.axaml.fs b/templates/fsharp/xplat/AvaloniaTest/App.axaml.fs index 580581d9..411560cc 100644 --- a/templates/fsharp/xplat/AvaloniaTest/App.axaml.fs +++ b/templates/fsharp/xplat/AvaloniaTest/App.axaml.fs @@ -2,6 +2,10 @@ namespace AvaloniaTest open Avalonia open Avalonia.Controls.ApplicationLifetimes +#if (CommunityToolkitChosen) +open Avalonia.Data.Core +open Avalonia.Data.Core.Plugins +#endif open Avalonia.Markup.Xaml open AvaloniaTest.ViewModels open AvaloniaTest.Views @@ -13,6 +17,13 @@ type App() = AvaloniaXamlLoader.Load(this) override this.OnFrameworkInitializationCompleted() = + +#if (CommunityToolkitChosen) + // Line below is needed to remove Avalonia data validation. + // Without this line you will get duplicate validations from both Avalonia and CT + BindingPlugins.DataValidators.RemoveAt(0) +#endif + match this.ApplicationLifetime with | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime -> desktopLifetime.MainWindow <- MainWindow(DataContext = MainViewModel()) diff --git a/templates/fsharp/xplat/AvaloniaTest/AvaloniaTest.fsproj b/templates/fsharp/xplat/AvaloniaTest/AvaloniaTest.fsproj index 4ef74f7e..1e86966f 100644 --- a/templates/fsharp/xplat/AvaloniaTest/AvaloniaTest.fsproj +++ b/templates/fsharp/xplat/AvaloniaTest/AvaloniaTest.fsproj @@ -26,9 +26,13 @@ - - + + + + + + diff --git a/templates/fsharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.fs b/templates/fsharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.fs index c64d6679..ea73aff4 100644 --- a/templates/fsharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.fs +++ b/templates/fsharp/xplat/AvaloniaTest/ViewModels/ViewModelBase.fs @@ -1,6 +1,15 @@ namespace AvaloniaTest.ViewModels +#if (CommunityToolkitChosen) +open CommunityToolkit.Mvvm.ComponentModel +#elif (ReactiveUIToolkitChosen) open ReactiveUI +#endif +[] type ViewModelBase() = +#if (CommunityToolkitChosen) + inherit ObservableObject() +#elif (ReactiveUIToolkitChosen) inherit ReactiveObject() +#endif diff --git a/tests/build-test.ps1 b/tests/build-test.ps1 index 3dbde752..a01fbe47 100644 --- a/tests/build-test.ps1 +++ b/tests/build-test.ps1 @@ -1,3 +1,7 @@ +# Enable common parameters e.g. -Verbose +[CmdletBinding()] +param() + Set-StrictMode -Version latest $ErrorActionPreference = "Stop" @@ -17,8 +21,14 @@ function Exec param( [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd) - ) - & $cmd + ) + + # Convert the ScriptBlock to a string and expand the variables + $expandedCmdString = $ExecutionContext.InvokeCommand.ExpandString($cmd.ToString()) + Write-Verbose "Executing command: $expandedCmdString" + + Invoke-Command -ScriptBlock $cmd + if ($lastexitcode -ne 0) { throw ("Exec: " + $errorMessage) } @@ -80,7 +90,7 @@ function Create-And-Build { # Remove dots and - from folderName because in sln it will cause errors when building project $folderName = $folderName -replace "[.-]" - + # Create the project Exec { dotnet new $template -o output/$lang/$folderName -$parameterName $value -lang $lang } @@ -88,10 +98,21 @@ function Create-And-Build { Exec { dotnet build output/$lang/$folderName -bl:$bl } } -if (Test-Path "output") { - Remove-Item -Recurse output +# Clear file system from possible previous runs +Write-Output "Clearing outputs from possible previous runs" +if (Test-Path "output" -ErrorAction SilentlyContinue) { + Remove-Item -Recurse -Force "output" +} +$outDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "output")) +if (Test-Path $outDir -ErrorAction SilentlyContinue) { + Remove-Item -Recurse -Force $outDir +} +$binLogDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog")) +if (Test-Path $binLogDir -ErrorAction SilentlyContinue) { + Remove-Item -Recurse -Force $binLogDir } +# Use same log file for all executions $binlog = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog", "test.binlog")) Create-And-Build "avalonia.app" "AvaloniaApp" "C#" "f" "net8.0" $binlog @@ -111,18 +132,21 @@ Create-And-Build "avalonia.mvvm" "AvaloniaMvvm" "C#" "rvl" "false" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "f" "net8.0" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "av" "11.1.0" $binlog +Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "m" "ReactiveUI" $binlog +Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "m" "CommunityToolkit" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "cb" "true" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "cb" "false" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "rvl" "true" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "C#" "rvl" "false" $binlog +# Ignore errors when files are still used by another process +Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "output/C#" + Create-And-Build "avalonia.app" "AvaloniaApp" "F#" "f" "net8.0" $binlog Create-And-Build "avalonia.app" "AvaloniaApp" "F#" "av" "11.1.0" $binlog Create-And-Build "avalonia.app" "AvaloniaApp" "F#" "cb" "true" $binlog Create-And-Build "avalonia.app" "AvaloniaApp" "F#" "cb" "false" $binlog -Remove-Item -Recurse "output/C#" - Test-Template "avalonia.mvvm" "AvaloniaMvvm" "F#" "f" "net8.0" $binlog Create-And-Build "avalonia.mvvm" "AvaloniaMvvm" "F#" "av" "11.1.0" $binlog Create-And-Build "avalonia.mvvm" "AvaloniaMvvm" "F#" "m" "ReactiveUI" $binlog @@ -134,7 +158,12 @@ Create-And-Build "avalonia.mvvm" "AvaloniaMvvm" "F#" "rvl" "false" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "f" "net8.0" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "av" "11.1.0" $binlog +Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "m" "ReactiveUI" $binlog +Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "m" "CommunityToolkit" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "cb" "true" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "cb" "false" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "rvl" "true" $binlog Create-And-Build "avalonia.xplat" "AvaloniaXplat" "F#" "rvl" "false" $binlog + +# Ignore errors when files are still used by another process +Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "output/F#"