From e576ec178c95ccbd8a5772754ad1c30d87465d26 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 6 Jun 2018 21:06:05 +0200 Subject: [PATCH 01/53] Initial --- src/Avalonia.Controls/AppBuilderBase.cs | 45 +++--- src/Avalonia.Controls/Application.cs | 118 ++++++++++++++- src/Avalonia.Controls/ExitMode.cs | 12 ++ src/Avalonia.Controls/Window.cs | 51 ++++--- src/Avalonia.Controls/WindowCollection.cs | 134 ++++++++++++++++++ .../ApplicationTests.cs | 107 ++++++++++++++ .../WindowTests.cs | 10 +- 7 files changed, 428 insertions(+), 49 deletions(-) create mode 100644 src/Avalonia.Controls/ExitMode.cs create mode 100644 src/Avalonia.Controls/WindowCollection.cs create mode 100644 tests/Avalonia.Controls.UnitTests/ApplicationTests.cs diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 7af3deef34..875f5263c2 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new() { private static bool s_setupWasAlreadyCalled; - + /// /// Gets or sets the instance. /// @@ -92,7 +92,7 @@ namespace Avalonia.Controls }; } - protected TAppBuilder Self => (TAppBuilder) this; + protected TAppBuilder Self => (TAppBuilder)this; /// /// Registers a callback to call before Start is called on the . @@ -125,7 +125,6 @@ namespace Avalonia.Controls var window = new TMainWindow(); if (dataContextProvider != null) window.DataContext = dataContextProvider(); - window.Show(); Instance.Run(window); } @@ -143,7 +142,6 @@ namespace Avalonia.Controls if (dataContextProvider != null) mainWindow.DataContext = dataContextProvider(); - mainWindow.Show(); Instance.Run(mainWindow); } @@ -209,6 +207,17 @@ namespace Avalonia.Controls public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); + /// + /// Sets the shutdown mode of the application. + /// + /// The shutdown mode. + /// + public TAppBuilder SetExitMode(ExitMode exitMode) + { + Instance.ExitMode = exitMode; + return Self; + } + private bool CheckSetup { get; set; } = true; /// @@ -223,20 +232,20 @@ namespace Avalonia.Controls private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies() - from attribute in assembly.GetCustomAttributes() - where attribute.ForWindowingSubsystem == "" - || attribute.ForWindowingSubsystem == WindowingSubsystemName - where attribute.ForRenderingSubsystem == "" - || attribute.ForRenderingSubsystem == RenderingSubsystemName - group attribute by attribute.Name into exports - select (from export in exports - orderby export.ForWindowingSubsystem.Length descending - orderby export.ForRenderingSubsystem.Length descending - select export).First().ModuleType into moduleType - select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors - where constructor.GetParameters().Length == 0 && !constructor.IsStatic - select constructor).Single() into constructor - select (Action)(() => constructor.Invoke(new object[0])); + from attribute in assembly.GetCustomAttributes() + where attribute.ForWindowingSubsystem == "" + || attribute.ForWindowingSubsystem == WindowingSubsystemName + where attribute.ForRenderingSubsystem == "" + || attribute.ForRenderingSubsystem == RenderingSubsystemName + group attribute by attribute.Name into exports + select (from export in exports + orderby export.ForWindowingSubsystem.Length descending + orderby export.ForRenderingSubsystem.Length descending + select export).First().ModuleType into moduleType + select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors + where constructor.GetParameters().Length == 0 && !constructor.IsStatic + select constructor).Single() into constructor + select (Action)(() => constructor.Invoke(new object[0])); Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 6fdca557eb..ffe4a9c513 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -43,11 +43,15 @@ namespace Avalonia private Styles _styles; private IResourceDictionary _resources; + private CancellationTokenSource _mainLoopCancellationTokenSource; + /// /// Initializes a new instance of the class. /// public Application() { + Windows = new WindowCollection(this); + OnExit += OnExiting; } @@ -158,6 +162,40 @@ namespace Avalonia /// IResourceNode IResourceNode.ResourceParent => null; + /// + /// Gets or sets the . This property indicates whether the application exits explicitly or implicitly. + /// If is set to OnExplicitExit the application is only closes if Exit is called. + /// The default is OnLastWindowClose + /// + /// + /// The shutdown mode. + /// + public ExitMode ExitMode { get; set; } + + /// + /// Gets or sets the main window of the application. + /// + /// + /// The main window. + /// + public Window MainWindow { get; set; } + + /// + /// Gets the open windows of the application. + /// + /// + /// The windows. + /// + public WindowCollection Windows { get; } + + /// + /// Gets or sets a value indicating whether this instance is existing. + /// + /// + /// true if this instance is existing; otherwise, false. + /// + internal bool IsExiting { get; set; } + /// /// Initializes the application by loading XAML etc. /// @@ -171,19 +209,81 @@ namespace Avalonia /// The closable to track public void Run(ICloseable closable) { - var source = new CancellationTokenSource(); - closable.Closed += OnExiting; - closable.Closed += (s, e) => source.Cancel(); - Dispatcher.UIThread.MainLoop(source.Token); + if (_mainLoopCancellationTokenSource != null) + { + throw new Exception("Run should only called once"); + } + + closable.Closed += (s, e) => Exit(); + + _mainLoopCancellationTokenSource = new CancellationTokenSource(); + + Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Runs the application's main loop until some condition occurs that is specified by ExitMode. + /// + /// The main window + public void Run(Window mainWindow) + { + if (_mainLoopCancellationTokenSource != null) + { + throw new Exception("Run should only called once"); + } + + _mainLoopCancellationTokenSource = new CancellationTokenSource(); + + Dispatcher.UIThread.InvokeAsync( + () => + { + if (mainWindow == null) + { + return; + } + + if (MainWindow != null) + { + return; + } + + if (!mainWindow.IsVisible) + { + mainWindow.Show(); + } + + MainWindow = mainWindow; + }, + DispatcherPriority.Send); + + Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } } - + /// - /// Runs the application's main loop until the is cancelled. + /// Runs the application's main loop until the is canceled. /// /// The token to track public void Run(CancellationToken token) { Dispatcher.UIThread.MainLoop(token); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } } /// @@ -191,7 +291,13 @@ namespace Avalonia /// public void Exit() { + IsExiting = true; + + Windows.Clear(); + OnExit?.Invoke(this, EventArgs.Empty); + + _mainLoopCancellationTokenSource?.Cancel(); } /// diff --git a/src/Avalonia.Controls/ExitMode.cs b/src/Avalonia.Controls/ExitMode.cs new file mode 100644 index 0000000000..0c5ecd7171 --- /dev/null +++ b/src/Avalonia.Controls/ExitMode.cs @@ -0,0 +1,12 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +namespace Avalonia +{ + public enum ExitMode + { + OnLastWindowClose, + OnMainWindowClose, + OnExplicitExit + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3cbfdbd657..c19c69ce73 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -49,14 +49,6 @@ namespace Avalonia.Controls /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { - private static List s_windows = new List(); - - /// - /// Retrieves an enumeration of all Windows in the currently running application. - /// - public static IReadOnlyList OpenWindows => s_windows; - - /// /// Defines the property. /// public static readonly StyledProperty SizeToContentProperty = @@ -75,7 +67,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(ShowInTaskbar), true); /// - /// Enables or disables the taskbar icon + /// Represents the current window state (normal, minimized, maximized) /// public static readonly StyledProperty WindowStateProperty = AvaloniaProperty.Register(nameof(WindowState)); @@ -117,7 +109,7 @@ namespace Avalonia.Controls BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool)e.NewValue)); ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); @@ -149,7 +141,7 @@ namespace Avalonia.Controls _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); Screens = new Screens(PlatformImpl?.Screen); } - + /// event EventHandler INameScope.Registered { @@ -199,7 +191,7 @@ namespace Avalonia.Controls get { return GetValue(HasSystemDecorationsProperty); } set { SetValue(HasSystemDecorationsProperty, value); } } - + /// /// Enables or disables the taskbar icon /// @@ -259,6 +251,26 @@ namespace Avalonia.Controls /// public event EventHandler Closing; + private static void AddWindow(Window window) + { + if (Application.Current == null) + { + return; + } + + Application.Current.Windows.Add(window); + } + + private static void RemoveWindow(Window window) + { + if (Application.Current == null) + { + return; + } + + Application.Current.Windows.Remove(window); + } + /// /// Closes the window. /// @@ -298,10 +310,9 @@ namespace Avalonia.Controls finally { if (ignoreCancel || !cancelClosing) - { - s_windows.Remove(this); + { PlatformImpl?.Dispose(); - IsVisible = false; + HandleClosed(); } } } @@ -359,7 +370,7 @@ namespace Avalonia.Controls return; } - s_windows.Add(this); + AddWindow(this); EnsureInitialized(); SetWindowStartupLocation(); @@ -400,7 +411,7 @@ namespace Avalonia.Controls throw new InvalidOperationException("The window is already being shown."); } - s_windows.Add(this); + AddWindow(this); EnsureInitialized(); SetWindowStartupLocation(); @@ -409,7 +420,7 @@ namespace Avalonia.Controls using (BeginAutoSizing()) { - var affectedWindows = s_windows.Where(w => w.IsEnabled && w != this).ToList(); + var affectedWindows = Application.Current.Windows.Where(w => w.IsEnabled && w != this).ToList(); var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); @@ -513,8 +524,8 @@ namespace Avalonia.Controls protected override void HandleClosed() { - IsVisible = false; - s_windows.Remove(this); + RemoveWindow(this); + base.HandleClosed(); } diff --git a/src/Avalonia.Controls/WindowCollection.cs b/src/Avalonia.Controls/WindowCollection.cs new file mode 100644 index 0000000000..c21a12f05b --- /dev/null +++ b/src/Avalonia.Controls/WindowCollection.cs @@ -0,0 +1,134 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Collections; +using System.Collections.Generic; + +using Avalonia.Controls; + +namespace Avalonia +{ + public class WindowCollection : IReadOnlyList + { + private readonly Application _application; + private readonly List _windows = new List(); + + public WindowCollection(Application application) + { + _application = application; + } + + /// + /// + /// Gets the number of elements in the collection. + /// + public int Count => _windows.Count; + + /// + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// + public Window this[int index] => _windows[index]; + + /// + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _windows.GetEnumerator(); + } + + /// + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Adds the specified window. + /// + /// The window. + internal void Add(Window window) + { + if (window == null) + { + return; + } + + _windows.Add(window); + } + + /// + /// Removes the specified window. + /// + /// The window. + internal void Remove(Window window) + { + if (window == null) + { + return; + } + + _windows.Remove(window); + + OnRemoveWindow(window); + } + + /// + /// Closes all windows and removes them from the underlying collection. + /// + internal void Clear() + { + while (_windows.Count > 0) + { + _windows[0].Close(); + } + } + + private void OnRemoveWindow(Window window) + { + if (window == null) + { + return; + } + + if (_application.IsExiting) + { + return; + } + + switch (_application.ExitMode) + { + case ExitMode.OnLastWindowClose: + if (Count == 0) + { + _application.Exit(); + } + + break; + case ExitMode.OnMainWindowClose: + if (window == _application.MainWindow) + { + _application.Exit(); + } + + break; + } + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs new file mode 100644 index 0000000000..85f95b2b5c --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -0,0 +1,107 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Collections.Generic; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ApplicationTests + { + [Fact] + public void Should_Exit_After_MainWindow_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnMainWindowClose; + + var mainWindow = new Window(); + + mainWindow.Show(); + + Application.Current.MainWindow = mainWindow; + + var window = new Window(); + + window.Show(); + + mainWindow.Close(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Exit_After_Last_Window_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnLastWindowClose; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(Application.Current.IsExiting); + + windowB.Close(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Only_Exit_On_Explicit_Exit() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnExplicitExit; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(Application.Current.IsExiting); + + windowB.Close(); + + Assert.False(Application.Current.IsExiting); + + Application.Current.Exit(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var windows = new List { new Window(), new Window(), new Window(), new Window() }; + + foreach (var window in windows) + { + window.Show(); + } + + Application.Current.Exit(); + + Assert.Empty(Application.Current.Windows); + } + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index a85c4df8af..e80ffd97cd 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -129,7 +129,7 @@ namespace Avalonia.Controls.UnitTests window.Show(); - Assert.Equal(new[] { window }, Window.OpenWindows); + Assert.Equal(new[] { window }, Application.Current.Windows); } } @@ -145,7 +145,7 @@ namespace Avalonia.Controls.UnitTests window.Show(); window.IsVisible = true; - Assert.Equal(new[] { window }, Window.OpenWindows); + Assert.Equal(new[] { window }, Application.Current.Windows); window.Close(); } @@ -162,7 +162,7 @@ namespace Avalonia.Controls.UnitTests window.Show(); window.Close(); - Assert.Empty(Window.OpenWindows); + Assert.Empty(Application.Current.Windows); } } @@ -184,7 +184,7 @@ namespace Avalonia.Controls.UnitTests window.Show(); windowImpl.Object.Closed(); - Assert.Empty(Window.OpenWindows); + Assert.Empty(Application.Current.Windows); } } @@ -339,7 +339,7 @@ namespace Avalonia.Controls.UnitTests { // HACK: We really need a decent way to have "statics" that can be scoped to // AvaloniaLocator scopes. - ((IList)Window.OpenWindows).Clear(); + Application.Current.Windows.Clear(); } } } From fca102655ea12961e18e03e616adc3bdff6c0817 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sun, 17 Jun 2018 19:34:46 +0200 Subject: [PATCH 02/53] Add ExitMode comments --- src/Avalonia.Controls/ExitMode.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Avalonia.Controls/ExitMode.cs b/src/Avalonia.Controls/ExitMode.cs index 0c5ecd7171..b73fe4a963 100644 --- a/src/Avalonia.Controls/ExitMode.cs +++ b/src/Avalonia.Controls/ExitMode.cs @@ -3,10 +3,24 @@ namespace Avalonia { + /// + /// Enum for ExitMode + /// public enum ExitMode { + /// + /// Indicates an implicit call to Application.Exit when the last window closes. + /// OnLastWindowClose, + + /// + /// Indicates an implicit call to Application.Exit when the main window closes. + /// OnMainWindowClose, + + /// + /// Indicates that the application only exits on an explicit call to Application.Exit. + /// OnExplicitExit } } \ No newline at end of file From 42dadae3ecdfeedb6fdde2d5aea59bd76bd6d3c0 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sun, 17 Jun 2018 22:46:40 +0200 Subject: [PATCH 03/53] Merge fix --- src/Avalonia.Controls/AppBuilderBase.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 814c662341..83763c0836 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -218,17 +218,6 @@ namespace Avalonia.Controls return Self; } - private bool CheckSetup { get; set; } = true; - - /// - /// Set this AppBuilder to ignore the setup check. Used for testing purposes. - /// - internal TAppBuilder IgnoreSetupCheck() - { - CheckSetup = false; - return Self; - } - protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() From 7e8f9fbf619313389065950244924c9e46cf0658 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 19 Jun 2018 21:15:22 +0200 Subject: [PATCH 04/53] Add NullArgumentException for Run with main window --- src/Avalonia.Controls/Application.cs | 8 ++++---- .../Avalonia.Controls.UnitTests/ApplicationTests.cs | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index ffe4a9c513..99575771da 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -243,14 +243,14 @@ namespace Avalonia Dispatcher.UIThread.InvokeAsync( () => { - if (mainWindow == null) + if (MainWindow != null) { return; } - if (MainWindow != null) + if (mainWindow == null) { - return; + throw new ArgumentNullException(nameof(mainWindow)); } if (!mainWindow.IsVisible) @@ -283,7 +283,7 @@ namespace Avalonia if (!IsExiting) { OnExit?.Invoke(this, EventArgs.Empty); - } + } } /// diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 85f95b2b5c..694a6d2278 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Collections.Generic; using Avalonia.UnitTests; using Xunit; @@ -103,5 +104,14 @@ namespace Avalonia.Controls.UnitTests Assert.Empty(Application.Current.Windows); } } + + [Fact] + public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Assert.Throws(() => { Application.Current.Run(null); }); + } + } } -} +} \ No newline at end of file From 0214939fd3bf865655b50ab9e0c5e592cfb8ef5c Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 19 Jun 2018 21:56:13 +0200 Subject: [PATCH 05/53] Add unit test --- tests/Avalonia.Controls.UnitTests/ApplicationTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 694a6d2278..9602bb0460 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -105,6 +105,17 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Should_Show_MainWindow_After_Run() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var mainWindow = new Window(); + Application.Current.Run(mainWindow); + Assert.True(mainWindow.IsVisible); + } + } + [Fact] public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() { From 90fdfae3bd93667abe9bdc92094688c1a8244029 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 20 Jun 2018 00:01:57 +0200 Subject: [PATCH 06/53] Remove invoke call on Application.Run --- src/Avalonia.Controls/Application.cs | 35 ++++++++----------- .../ApplicationTests.cs | 11 ------ 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 99575771da..de27aa94f8 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -240,27 +240,20 @@ namespace Avalonia _mainLoopCancellationTokenSource = new CancellationTokenSource(); - Dispatcher.UIThread.InvokeAsync( - () => - { - if (MainWindow != null) - { - return; - } - - if (mainWindow == null) - { - throw new ArgumentNullException(nameof(mainWindow)); - } - - if (!mainWindow.IsVisible) - { - mainWindow.Show(); - } - - MainWindow = mainWindow; - }, - DispatcherPriority.Send); + if (MainWindow == null) + { + if (mainWindow == null) + { + throw new ArgumentNullException(nameof(mainWindow)); + } + + if (!mainWindow.IsVisible) + { + mainWindow.Show(); + } + + MainWindow = mainWindow; + } Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 9602bb0460..694a6d2278 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -105,17 +105,6 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Should_Show_MainWindow_After_Run() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var mainWindow = new Window(); - Application.Current.Run(mainWindow); - Assert.True(mainWindow.IsVisible); - } - } - [Fact] public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() { From 7f8dc7766ea500a1046763e73e0640d62973660c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 12:46:29 -0500 Subject: [PATCH 07/53] Span-ified PathMarkupParser v1. --- build/System.Memory.props | 5 + src/Avalonia.Visuals/Avalonia.Visuals.csproj | 1 + .../Media/PathMarkupParser.cs | 106 +++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 build/System.Memory.props diff --git a/build/System.Memory.props b/build/System.Memory.props new file mode 100644 index 0000000000..f3253f8882 --- /dev/null +++ b/build/System.Memory.props @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index 0f003a4018..c34752a3ef 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -8,4 +8,5 @@ + \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index e8824753b5..7ef07d7ec8 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -82,7 +82,7 @@ namespace Avalonia.Media /// The path data. public void Parse(string pathData) { - var tokens = ParseTokens(pathData); + var tokens = ParseTokens2(pathData); CreateGeometry(tokens); } @@ -121,6 +121,17 @@ namespace Avalonia.Media return @"(?=[" + stringBuilder + "])"; } + private static IEnumerable ParseTokens2(string s) + { + var commands = new List(); + var span = s.AsSpan(); + while (!span.IsEmpty) + { + commands.Add(CommandToken.Parse(ref span)); + } + return commands; + } + private static IEnumerable ParseTokens(string s) { return Regex.Split(s, s_separatorPattern).Where(t => !string.IsNullOrEmpty(t)).Select(CommandToken.Parse); @@ -494,6 +505,27 @@ namespace Avalonia.Media return new CommandToken(command, isRelative, arguments); } } + + public static CommandToken Parse(ref ReadOnlySpan span) + { + if (!ReadCommand(ref span, out var command, out var isRelative)) + { + throw new InvalidDataException("No path command declared."); + } + + span = span.Slice(1); + span = SkipWhitespace(span); + + var arguments = new List(); + + while (ReadArgument(ref span, out var argument)) + { + arguments.Add(argument.ToString()); + span = ReadSeparator(span); + } + + return new CommandToken(command, isRelative, arguments); + } public FillRule ReadFillRule() { @@ -592,6 +624,28 @@ namespace Avalonia.Media return new Point(origin.X + x, origin.Y + y); } + private static bool ReadCommand(ref ReadOnlySpan span, out Command command, out bool relative) + { + span = SkipWhitespace(span); + if (span.IsEmpty) + { + command = default; + relative = false; + return false; + } + + var c = span[0]; + + if (!s_commands.TryGetValue(char.ToUpperInvariant(c), out command)) + { + throw new InvalidDataException("Unexpected path command '" + c + "'."); + } + + relative = char.IsLower(c); + + return true; + } + private static bool ReadCommand(TextReader reader, ref Command command, ref bool relative) { ReadWhitespace(reader); @@ -619,6 +673,56 @@ namespace Avalonia.Media return true; } + private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument) + { + if (remaining.IsEmpty) + { + argument = ReadOnlySpan.Empty; + return false; + } + + var valid = false; + int i = 0; + if (remaining[i] == '-') + { + i++; + } + for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true ; + + if (i < remaining.Length && remaining[i] == '.') + { + valid = false; + i++; + } + for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true ; + + if (!valid) + { + argument = ReadOnlySpan.Empty; + return false; + } + argument = remaining.Slice(0, i); + remaining = remaining.Slice(i); + return true; + } + + private static ReadOnlySpan ReadSeparator(ReadOnlySpan span) + { + span = SkipWhitespace(span); + if (!span.IsEmpty && span[0] == ',') + { + span = span.Slice(1); + } + return SkipWhitespace(span); + } + + private static ReadOnlySpan SkipWhitespace(ReadOnlySpan span) + { + int i = 0; + for (; i < span.Length && char.IsWhiteSpace(span[i]); i++) ; + return span.Slice(i); + } + private static void ReadWhitespace(TextReader reader) { int i; From dc9fb565fd35b4feed137f11cacd62b7854c7f47 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 12:50:06 -0500 Subject: [PATCH 08/53] Remove old regex-based parser --- .../Media/PathMarkupParser.cs | 107 +----------------- 1 file changed, 5 insertions(+), 102 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 7ef07d7ec8..c71aed8b66 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -17,7 +17,6 @@ namespace Avalonia.Media /// public class PathMarkupParser : IDisposable { - private static readonly string s_separatorPattern; private static readonly Dictionary s_commands = new Dictionary { @@ -42,7 +41,6 @@ namespace Avalonia.Media static PathMarkupParser() { - s_separatorPattern = CreatesSeparatorPattern(); } /// @@ -82,7 +80,7 @@ namespace Avalonia.Media /// The path data. public void Parse(string pathData) { - var tokens = ParseTokens2(pathData); + var tokens = ParseTokens(pathData); CreateGeometry(tokens); } @@ -107,21 +105,7 @@ namespace Avalonia.Media _isDisposed = true; } - private static string CreatesSeparatorPattern() - { - var stringBuilder = new StringBuilder(); - - foreach (var command in s_commands.Keys) - { - stringBuilder.Append(command); - - stringBuilder.Append(char.ToLower(command)); - } - - return @"(?=[" + stringBuilder + "])"; - } - - private static IEnumerable ParseTokens2(string s) + private static IEnumerable ParseTokens(string s) { var commands = new List(); var span = s.AsSpan(); @@ -132,11 +116,6 @@ namespace Avalonia.Media return commands; } - private static IEnumerable ParseTokens(string s) - { - return Regex.Split(s, s_separatorPattern).Where(t => !string.IsNullOrEmpty(t)).Select(CommandToken.Parse); - } - private static Point MirrorControlPoint(Point controlPoint, Point center) { var dir = controlPoint - center; @@ -146,7 +125,7 @@ namespace Avalonia.Media private void CreateGeometry(IEnumerable commandTokens) { - _currentPoint = new Point(); + _currentPoint = new Point(); foreach (var commandToken in commandTokens) { @@ -446,15 +425,13 @@ namespace Avalonia.Media private class CommandToken { - private const string ArgumentExpression = @"-?[0-9]*\.?\d+"; - - private CommandToken(Command command, bool isRelative, IEnumerable arguments) + private CommandToken(Command command, bool isRelative, List arguments) { Command = command; IsRelative = isRelative; - Arguments = new List(arguments); + Arguments = arguments; } public Command Command { get; } @@ -477,34 +454,6 @@ namespace Avalonia.Media private int CurrentPosition { get; set; } private List Arguments { get; } - - public static CommandToken Parse(string s) - { - using (var reader = new StringReader(s)) - { - var command = Command.None; - - var isRelative = false; - - if (!ReadCommand(reader, ref command, ref isRelative)) - { - throw new InvalidDataException("No path command declared."); - } - - var commandArguments = reader.ReadToEnd(); - - var argumentMatches = Regex.Matches(commandArguments, ArgumentExpression); - - var arguments = new List(); - - foreach (Match match in argumentMatches) - { - arguments.Add(match.Value); - } - - return new CommandToken(command, isRelative, arguments); - } - } public static CommandToken Parse(ref ReadOnlySpan span) { @@ -646,33 +595,6 @@ namespace Avalonia.Media return true; } - private static bool ReadCommand(TextReader reader, ref Command command, ref bool relative) - { - ReadWhitespace(reader); - - var i = reader.Peek(); - - if (i == -1) - { - return false; - } - - var c = (char)i; - - if (!s_commands.TryGetValue(char.ToUpperInvariant(c), out var next)) - { - throw new InvalidDataException("Unexpected path command '" + c + "'."); - } - - command = next; - - relative = char.IsLower(c); - - reader.Read(); - - return true; - } - private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument) { if (remaining.IsEmpty) @@ -722,25 +644,6 @@ namespace Avalonia.Media for (; i < span.Length && char.IsWhiteSpace(span[i]); i++) ; return span.Slice(i); } - - private static void ReadWhitespace(TextReader reader) - { - int i; - - while ((i = reader.Peek()) != -1) - { - var c = (char)i; - - if (char.IsWhiteSpace(c)) - { - reader.Read(); - } - else - { - break; - } - } - } } } } \ No newline at end of file From 6c6f1b48cf62048c9b94c671020558cb1b742ba9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 15:08:23 -0500 Subject: [PATCH 09/53] Remove unused static constructor. --- src/Avalonia.Visuals/Media/PathMarkupParser.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index c71aed8b66..e17aafeda3 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -39,10 +39,6 @@ namespace Avalonia.Media private bool? _isOpen; private bool _isDisposed; - static PathMarkupParser() - { - } - /// /// Initializes a new instance of the class. /// From 82802621ead1ff9440b2bb3060697388906b560d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 15:54:48 -0500 Subject: [PATCH 10/53] Update gitignore and add System.Memory package dependency. --- .gitignore | 8 ++++++-- packages.cake | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a9a8fd36b4..583a2b8a2b 100644 --- a/.gitignore +++ b/.gitignore @@ -176,5 +176,9 @@ nuget Avalonia.XBuild.sln project.lock.json .idea/* -**/obj-Skia/* -**/obj-Direct2D1/* + + +################## +## BenchmarkDotNet +################## +BenchmarkDotNet.Artifacts/ diff --git a/packages.cake b/packages.cake index 7e7e722c82..79147b5f99 100644 --- a/packages.cake +++ b/packages.cake @@ -253,7 +253,7 @@ public class Packages } .Deps(new string[]{null, "netcoreapp2.0"}, "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", - "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter") + "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter", "System.Memory") .ToArray(), Files = coreLibrariesNuSpecContent .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) From 63ec705748fd13ad3fa33aad762eebd3bf315d18 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 16:37:16 -0500 Subject: [PATCH 11/53] Remove extraneous whitespace in tests. --- tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs index 35ec38789e..5074d306fd 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs @@ -69,7 +69,7 @@ namespace Avalonia.Visuals.UnitTests.Media using (var context = new PathGeometryContext(pathGeometry)) using (var parser = new PathMarkupParser(context)) { - parser.Parse("F 1M0,0"); + parser.Parse("F 1M0,0"); Assert.Equal(FillRule.NonZero, pathGeometry.FillRule); } From e2f54537982df5136da906478807a71e5e4fe96d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 16:53:00 -0500 Subject: [PATCH 12/53] Remove LINQ usage. --- .../Media/PathMarkupParser.cs | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index e17aafeda3..41cd95d319 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -5,9 +5,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using Avalonia.Platform; namespace Avalonia.Media @@ -70,17 +67,6 @@ namespace Avalonia.Media Close } - /// - /// Parses the specified path data and writes the result to the geometryContext of this instance. - /// - /// The path data. - public void Parse(string pathData) - { - var tokens = ParseTokens(pathData); - - CreateGeometry(tokens); - } - void IDisposable.Dispose() { Dispose(true); @@ -101,17 +87,6 @@ namespace Avalonia.Media _isDisposed = true; } - private static IEnumerable ParseTokens(string s) - { - var commands = new List(); - var span = s.AsSpan(); - while (!span.IsEmpty) - { - commands.Add(CommandToken.Parse(ref span)); - } - return commands; - } - private static Point MirrorControlPoint(Point controlPoint, Point center) { var dir = controlPoint - center; @@ -119,15 +94,21 @@ namespace Avalonia.Media return center + -dir; } - private void CreateGeometry(IEnumerable commandTokens) + /// + /// Parses the specified path data and writes the result to the geometryContext of this instance. + /// + /// The path data. + public void Parse(string pathData) { + var span = pathData.AsSpan(); _currentPoint = new Point(); - foreach (var commandToken in commandTokens) + while(!span.IsEmpty) { + var commandToken = CommandToken.Parse(ref span); try { - while (true) + do { switch (commandToken.Command) { @@ -169,14 +150,7 @@ namespace Avalonia.Media default: throw new NotSupportedException("Unsupported command"); } - - if (commandToken.HasImplicitCommands) - { - continue; - } - - break; - } + } while (commandToken.HasImplicitCommands); } catch (InvalidDataException) { @@ -235,11 +209,6 @@ namespace Avalonia.Media CreateFigure(); - if (!commandToken.HasImplicitCommands) - { - return; - } - while (commandToken.HasImplicitCommands) { AddLine(commandToken); From c31edafb553c70ea64087248fa84addac77fb1b8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 17:05:33 -0500 Subject: [PATCH 13/53] Clear up code logic. --- src/Avalonia.Visuals/Media/PathMarkupParser.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 41cd95d319..b0c7ed7e95 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -213,14 +213,11 @@ namespace Avalonia.Media { AddLine(commandToken); - if (commandToken.IsRelative) + if (!commandToken.IsRelative) { - continue; + _currentPoint = currentPoint; + CreateFigure(); } - - _currentPoint = currentPoint; - - CreateFigure(); } } From 11d3e7f7e3cd26f8c92fb09bc4449b40dcac935f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 17:10:56 -0500 Subject: [PATCH 14/53] Make _isOpen non-nullable since the only two values that were used were true and null. --- .../Media/PathMarkupParser.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index b0c7ed7e95..8f1d8ebc71 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -33,7 +33,7 @@ namespace Avalonia.Media private IGeometryContext _geometryContext; private Point _currentPoint; private Point? _previousControlPoint; - private bool? _isOpen; + private bool _isOpen; private bool _isDisposed; /// @@ -162,7 +162,7 @@ namespace Avalonia.Media } } - if (_isOpen != null) + if (_isOpen) { _geometryContext.EndFigure(false); } @@ -170,7 +170,7 @@ namespace Avalonia.Media private void CreateFigure() { - if (_isOpen != null) + if (_isOpen) { _geometryContext.EndFigure(false); } @@ -189,14 +189,14 @@ namespace Avalonia.Media private void CloseFigure() { - if (_isOpen == true) + if (_isOpen) { _geometryContext.EndFigure(true); } _previousControlPoint = null; - _isOpen = null; + _isOpen = false; } private void AddMove(CommandToken commandToken) @@ -227,7 +227,7 @@ namespace Avalonia.Media ? commandToken.ReadRelativePoint(_currentPoint) : commandToken.ReadPoint(); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -241,7 +241,7 @@ namespace Avalonia.Media ? new Point(_currentPoint.X + commandToken.ReadDouble(), _currentPoint.Y) : _currentPoint.WithX(commandToken.ReadDouble()); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -255,7 +255,7 @@ namespace Avalonia.Media ? new Point(_currentPoint.X, _currentPoint.Y + commandToken.ReadDouble()) : _currentPoint.WithY(commandToken.ReadDouble()); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -279,7 +279,7 @@ namespace Avalonia.Media ? commandToken.ReadRelativePoint(_currentPoint) : commandToken.ReadPoint(); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -301,7 +301,7 @@ namespace Avalonia.Media ? commandToken.ReadRelativePoint(_currentPoint) : commandToken.ReadPoint(); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -326,7 +326,7 @@ namespace Avalonia.Media _previousControlPoint = MirrorControlPoint((Point)_previousControlPoint, _currentPoint); } - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -349,7 +349,7 @@ namespace Avalonia.Media _previousControlPoint = MirrorControlPoint((Point)_previousControlPoint, _currentPoint); } - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } @@ -373,7 +373,7 @@ namespace Avalonia.Media ? commandToken.ReadRelativePoint(_currentPoint) : commandToken.ReadPoint(); - if (_isOpen == null) + if (!_isOpen) { CreateFigure(); } From 85159c069c08df23983fa0163c78bf851f46a515 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Jun 2018 18:52:04 -0500 Subject: [PATCH 15/53] Span-ification v2. Remove CommandToken and just operate directly on the single span. Memory allocation is now down to less than 5KB. --- .../Media/PathMarkupParser.cs | 534 ++++++++---------- 1 file changed, 238 insertions(+), 296 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 8f1d8ebc71..384c96a7d6 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -105,61 +105,64 @@ namespace Avalonia.Media while(!span.IsEmpty) { - var commandToken = CommandToken.Parse(ref span); - try + if(!ReadCommand(ref span, out var command, out var relative)) { - do - { - switch (commandToken.Command) - { - case Command.None: - break; - case Command.FillRule: - SetFillRule(commandToken); - break; - case Command.Move: - AddMove(commandToken); - break; - case Command.Line: - AddLine(commandToken); - break; - case Command.HorizontalLine: - AddHorizontalLine(commandToken); - break; - case Command.VerticalLine: - AddVerticalLine(commandToken); - break; - case Command.CubicBezierCurve: - AddCubicBezierCurve(commandToken); - break; - case Command.QuadraticBezierCurve: - AddQuadraticBezierCurve(commandToken); - break; - case Command.SmoothCubicBezierCurve: - AddSmoothCubicBezierCurve(commandToken); - break; - case Command.SmoothQuadraticBezierCurve: - AddSmoothQuadraticBezierCurve(commandToken); - break; - case Command.Arc: - AddArc(commandToken); - break; - case Command.Close: - CloseFigure(); - break; - default: - throw new NotSupportedException("Unsupported command"); - } - } while (commandToken.HasImplicitCommands); - } - catch (InvalidDataException) - { - break; + return; } - catch (NotSupportedException) + + bool initialCommand = true; + + do { - break; - } + if (!initialCommand) + { + span = ReadSeparator(span); + } + + switch (command) + { + case Command.None: + break; + case Command.FillRule: + SetFillRule(ref span); + break; + case Command.Move: + AddMove(ref span, relative); + break; + case Command.Line: + AddLine(ref span, relative); + break; + case Command.HorizontalLine: + AddHorizontalLine(ref span, relative); + break; + case Command.VerticalLine: + AddVerticalLine(ref span, relative); + break; + case Command.CubicBezierCurve: + AddCubicBezierCurve(ref span, relative); + break; + case Command.QuadraticBezierCurve: + AddQuadraticBezierCurve(ref span, relative); + break; + case Command.SmoothCubicBezierCurve: + AddSmoothCubicBezierCurve(ref span, relative); + break; + case Command.SmoothQuadraticBezierCurve: + AddSmoothQuadraticBezierCurve(ref span, relative); + break; + case Command.Arc: + AddArc(ref span, relative); + break; + case Command.Close: + CloseFigure(); + break; + default: + throw new NotSupportedException("Unsupported command"); + } + + initialCommand = false; + } while (PeekArgument(span)); + } if (_isOpen) @@ -180,11 +183,28 @@ namespace Avalonia.Media _isOpen = true; } - private void SetFillRule(CommandToken commandToken) + private void SetFillRule(ref ReadOnlySpan span) { - var fillRule = commandToken.ReadFillRule(); + if (!ReadArgument(ref span, out var fillRule) || fillRule.Length != 1) + { + throw new InvalidDataException("Invalid fill rule."); + } + + FillRule rule; - _geometryContext.SetFillRule(fillRule); + switch (fillRule[0]) + { + case '0': + rule = FillRule.EvenOdd; + break; + case '1': + rule = FillRule.NonZero; + break; + default: + throw new InvalidDataException("Invalid fill rule"); + } + + _geometryContext.SetFillRule(rule); } private void CloseFigure() @@ -199,21 +219,22 @@ namespace Avalonia.Media _isOpen = false; } - private void AddMove(CommandToken commandToken) + private void AddMove(ref ReadOnlySpan span, bool relative) { - var currentPoint = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var currentPoint = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); _currentPoint = currentPoint; CreateFigure(); - while (commandToken.HasImplicitCommands) + while (PeekArgument(span)) { - AddLine(commandToken); + span = ReadSeparator(span); + AddLine(ref span, relative); - if (!commandToken.IsRelative) + if (!relative) { _currentPoint = currentPoint; CreateFigure(); @@ -221,11 +242,11 @@ namespace Avalonia.Media } } - private void AddLine(CommandToken commandToken) + private void AddLine(ref ReadOnlySpan span, bool relative) { - _currentPoint = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + _currentPoint = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (!_isOpen) { @@ -235,11 +256,11 @@ namespace Avalonia.Media _geometryContext.LineTo(_currentPoint); } - private void AddHorizontalLine(CommandToken commandToken) + private void AddHorizontalLine(ref ReadOnlySpan span, bool relative) { - _currentPoint = commandToken.IsRelative - ? new Point(_currentPoint.X + commandToken.ReadDouble(), _currentPoint.Y) - : _currentPoint.WithX(commandToken.ReadDouble()); + _currentPoint = relative + ? new Point(_currentPoint.X + ReadDouble(ref span), _currentPoint.Y) + : _currentPoint.WithX(ReadDouble(ref span)); if (!_isOpen) { @@ -249,11 +270,11 @@ namespace Avalonia.Media _geometryContext.LineTo(_currentPoint); } - private void AddVerticalLine(CommandToken commandToken) + private void AddVerticalLine(ref ReadOnlySpan span, bool relative) { - _currentPoint = commandToken.IsRelative - ? new Point(_currentPoint.X, _currentPoint.Y + commandToken.ReadDouble()) - : _currentPoint.WithY(commandToken.ReadDouble()); + _currentPoint = relative + ? new Point(_currentPoint.X, _currentPoint.Y + ReadDouble(ref span)) + : _currentPoint.WithY(ReadDouble(ref span)); if (!_isOpen) { @@ -263,21 +284,25 @@ namespace Avalonia.Media _geometryContext.LineTo(_currentPoint); } - private void AddCubicBezierCurve(CommandToken commandToken) + private void AddCubicBezierCurve(ref ReadOnlySpan span, bool relative) { - var point1 = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var point1 = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); - var point2 = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + span = ReadSeparator(span); + + var point2 = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); _previousControlPoint = point2; - var point3 = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + span = ReadSeparator(span); + + var point3 = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (!_isOpen) { @@ -289,17 +314,19 @@ namespace Avalonia.Media _currentPoint = point3; } - private void AddQuadraticBezierCurve(CommandToken commandToken) + private void AddQuadraticBezierCurve(ref ReadOnlySpan span, bool relative) { - var start = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var start = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); _previousControlPoint = start; - var end = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + span = ReadSeparator(span); + + var end = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (!_isOpen) { @@ -311,15 +338,17 @@ namespace Avalonia.Media _currentPoint = end; } - private void AddSmoothCubicBezierCurve(CommandToken commandToken) + private void AddSmoothCubicBezierCurve(ref ReadOnlySpan span, bool relative) { - var point2 = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var point2 = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); + + span = ReadSeparator(span); - var end = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var end = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (_previousControlPoint != null) { @@ -338,11 +367,11 @@ namespace Avalonia.Media _currentPoint = end; } - private void AddSmoothQuadraticBezierCurve(CommandToken commandToken) + private void AddSmoothQuadraticBezierCurve(ref ReadOnlySpan span, bool relative) { - var end = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var end = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (_previousControlPoint != null) { @@ -359,19 +388,25 @@ namespace Avalonia.Media _currentPoint = end; } - private void AddArc(CommandToken commandToken) + private void AddArc(ref ReadOnlySpan span, bool relative) { - var size = commandToken.ReadSize(); + var size = ReadSize(ref span); - var rotationAngle = commandToken.ReadDouble(); + span = ReadSeparator(span); - var isLargeArc = commandToken.ReadBool(); + var rotationAngle = ReadDouble(ref span); + span = ReadSeparator(span); + var isLargeArc = ReadBool(ref span); - var sweepDirection = commandToken.ReadBool() ? SweepDirection.Clockwise : SweepDirection.CounterClockwise; + span = ReadSeparator(span); + + var sweepDirection = ReadBool(ref span) ? SweepDirection.Clockwise : SweepDirection.CounterClockwise; + + span = ReadSeparator(span); - var end = commandToken.IsRelative - ? commandToken.ReadRelativePoint(_currentPoint) - : commandToken.ReadPoint(); + var end = relative + ? ReadRelativePoint(ref span, _currentPoint) + : ReadPoint(ref span); if (!_isOpen) { @@ -385,227 +420,134 @@ namespace Avalonia.Media _previousControlPoint = null; } - private class CommandToken + private static bool PeekArgument(ReadOnlySpan span) { - private CommandToken(Command command, bool isRelative, List arguments) - { - Command = command; - - IsRelative = isRelative; - - Arguments = arguments; - } - - public Command Command { get; } + span = SkipWhitespace(span); - public bool IsRelative { get; } + return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || char.IsDigit(span[0])); + } - public bool HasImplicitCommands + private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument) + { + remaining = SkipWhitespace(remaining); + if (remaining.IsEmpty) { - get - { - if (CurrentPosition == 0 && Arguments.Count > 0) - { - return true; - } - - return CurrentPosition < Arguments.Count - 1; - } + argument = ReadOnlySpan.Empty; + return false; } - private int CurrentPosition { get; set; } - - private List Arguments { get; } - - public static CommandToken Parse(ref ReadOnlySpan span) + var valid = false; + int i = 0; + if (remaining[i] == '-') { - if (!ReadCommand(ref span, out var command, out var isRelative)) - { - throw new InvalidDataException("No path command declared."); - } - - span = span.Slice(1); - span = SkipWhitespace(span); - - var arguments = new List(); - - while (ReadArgument(ref span, out var argument)) - { - arguments.Add(argument.ToString()); - span = ReadSeparator(span); - } - - return new CommandToken(command, isRelative, arguments); + i++; } + for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true; - public FillRule ReadFillRule() + if (i < remaining.Length && remaining[i] == '.') { - if (CurrentPosition == Arguments.Count) - { - throw new InvalidDataException("Invalid fill rule"); - } - - var value = Arguments[CurrentPosition]; - - CurrentPosition++; - - switch (value) - { - case "0": - { - return FillRule.EvenOdd; - } - - case "1": - { - return FillRule.NonZero; - } - - default: - throw new InvalidDataException("Invalid fill rule"); - } + valid = false; + i++; } + for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true; - public bool ReadBool() + if (!valid) { - if (CurrentPosition == Arguments.Count) - { - throw new InvalidDataException("Invalid boolean value"); - } - - var value = Arguments[CurrentPosition]; - - CurrentPosition++; - - switch (value) - { - case "1": - { - return true; - } - - case "0": - { - return false; - } - - default: - throw new InvalidDataException("Invalid boolean value"); - } + argument = ReadOnlySpan.Empty; + return false; } + argument = remaining.Slice(0, i); + remaining = remaining.Slice(i); + return true; + } - public double ReadDouble() - { - if (CurrentPosition == Arguments.Count) - { - throw new InvalidDataException("Invalid double value"); - } - - var value = Arguments[CurrentPosition]; - - CurrentPosition++; - - return double.Parse(value, CultureInfo.InvariantCulture); - } - public Size ReadSize() + private static ReadOnlySpan ReadSeparator(ReadOnlySpan span) + { + span = SkipWhitespace(span); + if (!span.IsEmpty && span[0] == ',') { - var width = ReadDouble(); - - var height = ReadDouble(); - - return new Size(width, height); + span = span.Slice(1); } + return span; + } - public Point ReadPoint() - { - var x = ReadDouble(); - - var y = ReadDouble(); - - return new Point(x, y); - } + private static ReadOnlySpan SkipWhitespace(ReadOnlySpan span) + { + int i = 0; + for (; i < span.Length && char.IsWhiteSpace(span[i]); i++) ; + return span.Slice(i); + } - public Point ReadRelativePoint(Point origin) + private bool ReadBool(ref ReadOnlySpan span) + { + if (!ReadArgument(ref span, out var boolValue) || boolValue.Length != 1) { - var x = ReadDouble(); - - var y = ReadDouble(); - - return new Point(origin.X + x, origin.Y + y); + throw new InvalidDataException("Invalid bool rule."); } - - private static bool ReadCommand(ref ReadOnlySpan span, out Command command, out bool relative) + + switch (boolValue[0]) { - span = SkipWhitespace(span); - if (span.IsEmpty) - { - command = default; - relative = false; + case '0': return false; - } - - var c = span[0]; - - if (!s_commands.TryGetValue(char.ToUpperInvariant(c), out command)) - { - throw new InvalidDataException("Unexpected path command '" + c + "'."); - } - - relative = char.IsLower(c); - - return true; + case '1': + return true; + default: + throw new InvalidDataException("Invalid bool rule"); } + } - private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument) + private double ReadDouble(ref ReadOnlySpan span) + { + if (!ReadArgument(ref span, out var doubleValue)) { - if (remaining.IsEmpty) - { - argument = ReadOnlySpan.Empty; - return false; - } + throw new InvalidDataException("Invalid double value"); + } - var valid = false; - int i = 0; - if (remaining[i] == '-') - { - i++; - } - for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true ; + return double.Parse(doubleValue.ToString(), CultureInfo.InvariantCulture); + } - if (i < remaining.Length && remaining[i] == '.') - { - valid = false; - i++; - } - for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true ; + private Size ReadSize(ref ReadOnlySpan span) + { + var width = ReadDouble(ref span); + span = ReadSeparator(span); + var height = ReadDouble(ref span); + return new Size(width, height); + } - if (!valid) - { - argument = ReadOnlySpan.Empty; - return false; - } - argument = remaining.Slice(0, i); - remaining = remaining.Slice(i); - return true; - } + private Point ReadPoint(ref ReadOnlySpan span) + { + var x = ReadDouble(ref span); + span = ReadSeparator(span); + var y = ReadDouble(ref span); + return new Point(x, y); + } - private static ReadOnlySpan ReadSeparator(ReadOnlySpan span) + private Point ReadRelativePoint(ref ReadOnlySpan span, Point origin) + { + var x = ReadDouble(ref span); + span = ReadSeparator(span); + var y = ReadDouble(ref span); + return new Point(origin.X + x, origin.Y + y); + } + + private bool ReadCommand(ref ReadOnlySpan span, out Command command, out bool relative) + { + span = SkipWhitespace(span); + if (span.IsEmpty) { - span = SkipWhitespace(span); - if (!span.IsEmpty && span[0] == ',') - { - span = span.Slice(1); - } - return SkipWhitespace(span); + command = default; + relative = false; + return false; } - - private static ReadOnlySpan SkipWhitespace(ReadOnlySpan span) + var c = span[0]; + if (!s_commands.TryGetValue(char.ToUpperInvariant(c), out command)) { - int i = 0; - for (; i < span.Length && char.IsWhiteSpace(span[i]); i++) ; - return span.Slice(i); + throw new InvalidDataException("Unexpected path command '" + c + "'."); } + relative = char.IsLower(c); + span = span.Slice(1); + return true; } } } \ No newline at end of file From 4f0764ea6bee6d4f24f445491b3169ba4830fcd9 Mon Sep 17 00:00:00 2001 From: Robert Hencke Date: Sat, 23 Jun 2018 19:29:49 -0400 Subject: [PATCH 16/53] Make Delete on macOS delete correctly. On a Mac keyboard, the Delete key functions as the Backspace key does on a PC keyboard. Map VK_Delete to Key.Back (VK_ForwardDelete correctly maps to Key.Delete) --- src/OSX/Avalonia.MonoMac/KeyTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OSX/Avalonia.MonoMac/KeyTransform.cs b/src/OSX/Avalonia.MonoMac/KeyTransform.cs index 6d4b58031e..4abd5a24f1 100644 --- a/src/OSX/Avalonia.MonoMac/KeyTransform.cs +++ b/src/OSX/Avalonia.MonoMac/KeyTransform.cs @@ -200,7 +200,7 @@ namespace Avalonia.MonoMac [kVK_Return] = Key.Return, [kVK_Tab] = Key.Tab, [kVK_Space] = Key.Space, - [kVK_Delete] = Key.Delete, + [kVK_Delete] = Key.Back, [kVK_Escape] = Key.Escape, [kVK_Command] = Key.LWin, [kVK_Shift] = Key.LeftShift, From 6d0e46134919956ba20a115218b5ba7f43fa933c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 25 Jun 2018 10:46:50 +0200 Subject: [PATCH 17/53] Throw if no matching property accessor found. This shouldn't happen normally as `InpcPropertyAcessorPlugin` matches everything. --- src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index 2565a34322..e9831eb047 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -53,6 +53,12 @@ namespace Avalonia.Data.Core } } + if (accessor == null) + { + throw new NotSupportedException( + $"Could not find a matching property accessor for {PropertyName}."); + } + accessor.Subscribe(ValueChanged); _accessor = accessor; } From af7e139ed4cb7e06850ccef07d6cc8e934ca061d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Jun 2018 00:33:24 +0200 Subject: [PATCH 18/53] Added failing test for #1698. --- .../SelectorTests_Class.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs index b41c21fbf4..75599925b7 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; @@ -8,6 +9,7 @@ using Moq; using Avalonia.Controls; using Avalonia.Styling; using Xunit; +using System.Collections.Generic; namespace Avalonia.Styling.UnitTests { @@ -117,6 +119,28 @@ namespace Avalonia.Styling.UnitTests Assert.False(await activator.Take(1)); } + [Fact] + public void Only_Notifies_When_Result_Changes() + { + // Test for #1698 + var control = new Control1 + { + Classes = new Classes { "foo" }, + }; + + var target = default(Selector).Class("foo"); + var activator = target.Match(control).ObservableResult; + var result = new List(); + + using (activator.Subscribe(x => result.Add(x))) + { + control.Classes.Add("bar"); + control.Classes.Remove("foo"); + } + + Assert.Equal(new[] { true, false }, result); + } + public class Control1 : TestControlBase { } From 114b393813d38683ebc4ea013555ed5f7fa6956b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Jun 2018 00:33:55 +0200 Subject: [PATCH 19/53] Only publish distinct values from ClassObserver. Fixes #1698. --- .../Styling/TypeNameAndClassSelector.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs index 93d4e14f27..94c0b75c6e 100644 --- a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs @@ -202,6 +202,7 @@ namespace Avalonia.Styling { readonly IList _match; IAvaloniaReadOnlyList _classes; + bool _value; public ClassObserver(IAvaloniaReadOnlyList classes, IList match) { @@ -210,18 +211,29 @@ namespace Avalonia.Styling } protected override void Deinitialize() => _classes.CollectionChanged -= ClassesChanged; - protected override void Initialize() => _classes.CollectionChanged += ClassesChanged; + + protected override void Initialize() + { + _value = GetResult(); + _classes.CollectionChanged += ClassesChanged; + } protected override void Subscribed(IObserver observer, bool first) { - observer.OnNext(GetResult()); + observer.OnNext(_value); } private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action != NotifyCollectionChangedAction.Move) { - PublishNext(GetResult()); + var value = GetResult(); + + if (value != _value) + { + PublishNext(GetResult()); + _value = value; + } } } From 617dd6a5ab9718043440e4cccbece7ae8e566a01 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 26 Jun 2018 15:43:33 +0100 Subject: [PATCH 20/53] fix win32 clipboard settext async. --- src/Windows/Avalonia.Win32/ClipboardImpl.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index 3dae8e37d9..a908c9e1e2 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -57,6 +57,9 @@ namespace Avalonia.Win32 } await OpenClipboard(); + + UnmanagedMethods.EmptyClipboard(); + try { var hGlobal = Marshal.StringToHGlobalUni(text); From b3cfc22e27cb18e9c758ab9f3fc551c3577ec80e Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 28 Jun 2018 19:55:25 +0200 Subject: [PATCH 21/53] Add scientific notation support for double values --- src/Avalonia.Visuals/Media/PathMarkupParser.cs | 15 +++++++++++++++ .../Media/PathMarkupParserTests.cs | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 384c96a7d6..bfb6ec6450 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -451,6 +451,21 @@ namespace Avalonia.Media } for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true; + if (i < remaining.Length) + { + // scientific notation + if (remaining[i] == 'E') + { + valid = false; + i++; + if (remaining[i] == '-' || remaining[i] == '+') + { + i++; + for (; i < remaining.Length && char.IsNumber(remaining[i]); i++) valid = true; + } + } + } + if (!valid) { argument = ReadOnlySpan.Empty; diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs index 5074d306fd..6c456a9321 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs @@ -142,6 +142,10 @@ namespace Avalonia.Visuals.UnitTests.Media } [Theory] + [InlineData("F1M9.0771,11C9.1161,10.701,9.1801,10.352,9.3031,10L9.0001,10 9.0001,6.166 3.0001,9.767 3.0001,10 " + + "9.99999999997669E-05,10 9.99999999997669E-05,0 3.0001,0 3.0001,0.234 9.0001,3.834 9.0001,0 " + + "12.0001,0 12.0001,8.062C12.1861,8.043 12.3821,8.031 12.5941,8.031 15.3481,8.031 15.7961,9.826 " + + "15.9201,11L16.0001,16 9.0001,16 9.0001,12.562 9.0001,11z")] // issue #1708 [InlineData(" M0 0")] [InlineData("F1 M24,14 A2,2,0,1,1,20,14 A2,2,0,1,1,24,14 z")] // issue #1107 [InlineData("M0 0L10 10z")] From 9ccf63d51bd4fe548d920219f256a0df2620766d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 28 Jun 2018 23:45:10 +0200 Subject: [PATCH 22/53] Add failing test for clearing templated child's parent. --- .../Primitives/TemplatedControlTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs index cd71717619..166586ace1 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs @@ -160,6 +160,24 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(target, child.GetLogicalParent()); } + [Fact] + public void Changing_Template_Should_Clear_Old_Templated_Childs_Parent() + { + var target = new TemplatedControl + { + Template = new FuncControlTemplate(_ => new Decorator()) + }; + + target.ApplyTemplate(); + + var child = (Decorator)target.GetVisualChildren().Single(); + + target.Template = new FuncControlTemplate(_ => new Canvas()); + target.ApplyTemplate(); + + Assert.Null(child.Parent); + } + [Fact] public void Nested_Templated_Control_Should_Not_Have_Template_Applied() { From 95fe6f4cdfa9ca67be64037f956ad0a70c3c567c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 28 Jun 2018 23:46:03 +0200 Subject: [PATCH 23/53] Clear templated child's parent when template detached. --- src/Avalonia.Controls/Primitives/TemplatedControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 8514104c91..296134ca48 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -247,6 +247,7 @@ namespace Avalonia.Controls.Primitives foreach (var child in this.GetTemplateChildren()) { child.SetValue(TemplatedParentProperty, null); + ((ISetLogicalParent)child).SetParent(null); } VisualChildren.Clear(); From 9c1c8749ddd3e11a717901726f7e61119a8c9e64 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 29 Jun 2018 00:50:15 +0200 Subject: [PATCH 24/53] Added failing test for #1709. --- .../ItemsControlTests.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 4da803353e..9ef1e9f0d2 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -315,6 +315,26 @@ namespace Avalonia.Controls.UnitTests Assert.Same(before, after); } + [Fact] + public void Should_Clear_Containers_When_ItemsPresenter_Changes() + { + var target = new ItemsControl + { + Items = new[] { "foo", "bar" }, + Template = GetTemplate(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + Assert.Equal(2, target.ItemContainerGenerator.Containers.Count()); + + target.Template = GetTemplate(); + target.ApplyTemplate(); + + Assert.Empty(target.ItemContainerGenerator.Containers); + } + [Fact] public void Empty_Class_Should_Initially_Be_Applied() { From cf14976dcce4ce011a664a83d9ea57184706f183 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 29 Jun 2018 00:50:28 +0200 Subject: [PATCH 25/53] Fix #1709. --- src/Avalonia.Controls/ItemsControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 5119096965..3cb997f615 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -155,6 +155,7 @@ namespace Avalonia.Controls void IItemsPresenterHost.RegisterItemsPresenter(IItemsPresenter presenter) { Presenter = presenter; + ItemContainerGenerator.Clear(); } /// From 2f7a578c38eddc108028ea2aff34e7e4e9d68932 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 29 Jun 2018 00:51:03 +0200 Subject: [PATCH 26/53] Added null check for panel. Problem reared its head when #1709 was fixed. --- .../Primitives/SelectingItemsControl.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 2e668fda95..a7b8981583 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -408,12 +408,15 @@ namespace Avalonia.Controls.Primitives var panel = (InputElement)Presenter.Panel; - foreach (var container in e.Containers) + if (panel != null) { - if (KeyboardNavigation.GetTabOnceActiveElement(panel) == container.ContainerControl) + foreach (var container in e.Containers) { - KeyboardNavigation.SetTabOnceActiveElement(panel, null); - break; + if (KeyboardNavigation.GetTabOnceActiveElement(panel) == container.ContainerControl) + { + KeyboardNavigation.SetTabOnceActiveElement(panel, null); + break; + } } } } From ac8342ebf89d35f1b0030edc566325b40668e057 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 29 Jun 2018 06:34:54 +0200 Subject: [PATCH 27/53] Allow lower case e for scientific notation --- .../Media/PathMarkupParser.cs | 2 +- .../Media/PathMarkupParserTests.cs | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index bfb6ec6450..b6232c6803 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -454,7 +454,7 @@ namespace Avalonia.Media if (i < remaining.Length) { // scientific notation - if (remaining[i] == 'E') + if (remaining[i] == 'E' || remaining[i] == 'e') { valid = false; i++; diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs index 6c456a9321..4d3de034d8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs @@ -7,6 +7,7 @@ using Xunit; namespace Avalonia.Visuals.UnitTests.Media { + using System.Globalization; using System.IO; public class PathMarkupParserTests @@ -139,7 +140,26 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.Equal(new Point(30, 30), lineSegment.Point); } - } + } + + [Fact] + public void Parses_Scientific_Notation_Double() + { + var pathGeometry = new PathGeometry(); + using (var context = new PathGeometryContext(pathGeometry)) + using (var parser = new PathMarkupParser(context)) + { + parser.Parse("M -1.01725E-005 -1.01725e-005"); + + var figure = pathGeometry.Figures[0]; + + Assert.Equal( + new Point( + double.Parse("-1.01725E-005", NumberStyles.Float, CultureInfo.InvariantCulture), + double.Parse("-1.01725E-005", NumberStyles.Float, CultureInfo.InvariantCulture)), + figure.StartPoint); + } + } [Theory] [InlineData("F1M9.0771,11C9.1161,10.701,9.1801,10.352,9.3031,10L9.0001,10 9.0001,6.166 3.0001,9.767 3.0001,10 " From 370bfe3c82b7e467a68ba44c74cb54679e597d34 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 29 Jun 2018 12:31:53 +0200 Subject: [PATCH 28/53] Support double values that start with a dot for implicit commands --- src/Avalonia.Visuals/Media/PathMarkupParser.cs | 2 +- tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index b6232c6803..656526890a 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -424,7 +424,7 @@ namespace Avalonia.Media { span = SkipWhitespace(span); - return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || char.IsDigit(span[0])); + return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || span[0] == '.' || char.IsDigit(span[0])); } private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument) diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs index 4d3de034d8..8dca52e6a7 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs @@ -162,6 +162,7 @@ namespace Avalonia.Visuals.UnitTests.Media } [Theory] + [InlineData("M5.5.5 5.5.5 5.5.5")] [InlineData("F1M9.0771,11C9.1161,10.701,9.1801,10.352,9.3031,10L9.0001,10 9.0001,6.166 3.0001,9.767 3.0001,10 " + "9.99999999997669E-05,10 9.99999999997669E-05,0 3.0001,0 3.0001,0.234 9.0001,3.834 9.0001,0 " + "12.0001,0 12.0001,8.062C12.1861,8.043 12.3821,8.031 12.5941,8.031 15.3481,8.031 15.7961,9.826 " From 2a14f4d6a55ef4ab4f49f39d8b7837cbd226bbcd Mon Sep 17 00:00:00 2001 From: Sannan Khan Date: Fri, 29 Jun 2018 16:33:33 +0500 Subject: [PATCH 29/53] Moved SetWindowStartupLocation() to end of Show() function --- src/Avalonia.Controls/Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index c19c69ce73..e0fefef0b7 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -373,7 +373,6 @@ namespace Avalonia.Controls AddWindow(this); EnsureInitialized(); - SetWindowStartupLocation(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); @@ -382,6 +381,7 @@ namespace Avalonia.Controls PlatformImpl?.Show(); Renderer?.Start(); } + SetWindowStartupLocation(); } /// From 064c89bd42583b3d6103be1b2fee4db12e29f6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 15:17:42 +0200 Subject: [PATCH 30/53] Refactored Cake script to enable running Impl tasks without dependencies --- build.cake | 140 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 49 deletions(-) diff --git a/build.cake b/build.cake index 561a33186a..e739d9375e 100644 --- a/build.cake +++ b/build.cake @@ -95,10 +95,10 @@ Teardown((context, buildContext) => }); /////////////////////////////////////////////////////////////////////////////// -// TASKS +// TASKS IMPLEMENTATIONS /////////////////////////////////////////////////////////////////////////////// -Task("Clean") +Task("Clean-Impl") .Does(data => { CleanDirectories(data.Parameters.BuildDirs); @@ -108,9 +108,7 @@ Task("Clean") CleanDirectory(data.Parameters.BinRoot); }); -Task("Restore-NuGet-Packages") - .IsDependentOn("Clean") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) +Task("Restore-NuGet-Packages-Impl") .Does(data => { var maxRetryCount = 5; @@ -148,8 +146,7 @@ void DotNetCoreBuild(Parameters parameters) DotNetCoreBuild(parameters.MSBuildSolution, settings); } -Task("Build") - .IsDependentOn("Restore-NuGet-Packages") +Task("Build-Impl") .Does(data => { if(data.Parameters.IsRunningOnWindows) @@ -171,7 +168,6 @@ Task("Build") } }); - void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) { if(!project.EndsWith(".csproj")) @@ -194,9 +190,7 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) } } -Task("Run-Unit-Tests") - .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests) +Task("Run-Unit-Tests-Impl") .Does(data => { RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); @@ -214,24 +208,18 @@ Task("Run-Unit-Tests") } }); -Task("Run-Designer-Tests") - .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests) +Task("Run-Designer-Tests-Impl") .Does(data => { RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); }); -Task("Run-Render-Tests") - .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) +Task("Run-Render-Tests-Impl") .Does(data => { RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); }); -Task("Run-Leak-Tests") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) - .IsDependentOn("Build") +Task("Run-Leak-Tests-Impl") .Does(() => { var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); @@ -251,21 +239,13 @@ Task("Run-Leak-Tests") } }); -Task("Run-Tests") - .IsDependentOn("Run-Unit-Tests") - .IsDependentOn("Run-Render-Tests") - .IsDependentOn("Run-Designer-Tests") - .IsDependentOn("Run-Leak-Tests"); - -Task("Copy-Files") - .IsDependentOn("Run-Tests") +Task("Copy-Files-Impl") .Does(data => { CopyFiles(data.Packages.BinFiles, data.Parameters.BinRoot); }); -Task("Zip-Files") - .IsDependentOn("Copy-Files") +Task("Zip-Files-Impl") .Does(data => { Zip(data.Parameters.BinRoot, data.Parameters.ZipCoreArtifacts); @@ -279,9 +259,7 @@ Task("Zip-Files") GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); }); -Task("Create-NuGet-Packages") - .IsDependentOn("Run-Tests") - .IsDependentOn("Inspect") +Task("Create-NuGet-Packages-Impl") .Does(data => { foreach(var nuspec in data.Packages.NuspecNuGetSettings) @@ -290,13 +268,7 @@ Task("Create-NuGet-Packages") } }); -Task("Publish-MyGet") - .IsDependentOn("Create-NuGet-Packages") - .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) - .WithCriteria((context, data) => !data.Parameters.IsPullRequest) - .WithCriteria((context, data) => data.Parameters.IsMainRepo) - .WithCriteria((context, data) => data.Parameters.IsMasterBranch) - .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) +Task("Publish-MyGet-Impl") .Does(data => { var apiKey = EnvironmentVariable("MYGET_API_KEY"); @@ -324,12 +296,7 @@ Task("Publish-MyGet") Information("Publish-MyGet Task failed, but continuing with next Task..."); }); -Task("Publish-NuGet") - .IsDependentOn("Create-NuGet-Packages") - .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) - .WithCriteria((context, data) => !data.Parameters.IsPullRequest) - .WithCriteria((context, data) => data.Parameters.IsMainRepo) - .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) +Task("Publish-NuGet-Impl") .Does(data => { var apiKey = EnvironmentVariable("NUGET_API_KEY"); @@ -357,9 +324,7 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); -Task("Inspect") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) - .IsDependentOn("Restore-NuGet-Packages") +Task("Inspect-Impl") .Does(() => { var badIssues = new []{"PossibleNullReferenceException"}; @@ -394,6 +359,83 @@ Task("Inspect") throw new Exception("Issues found"); }); +/////////////////////////////////////////////////////////////////////////////// +// TASKS DEPENDENCY TREE +/////////////////////////////////////////////////////////////////////////////// + +Task("Clean"); + .IsDependentOn("Clean-Impl"); + +Task("Restore-NuGet-Packages") + .IsDependentOn("Restore-NuGet-Packages-Impl"); + .IsDependentOn("Clean") + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows); + +Task("Build") + .IsDependentOn("Build-Impl"); + .IsDependentOn("Restore-NuGet-Packages"); + +Task("Run-Unit-Tests") + .IsDependentOn("Run-Unit-Tests-Impl"); + .IsDependentOn("Build") + .WithCriteria((context, data) => !data.Parameters.SkipTests); + +Task("Run-Designer-Tests") + .IsDependentOn("Run-Designer-Tests-Impl"); + .IsDependentOn("Build") + .WithCriteria((context, data) => !data.Parameters.SkipTests); + +Task("Run-Render-Tests") + .IsDependentOn("Run-Render-Tests-Impl"); + .IsDependentOn("Build") + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows); + +Task("Run-Leak-Tests") + .IsDependentOn("Run-Leak-Tests-Impl"); + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .IsDependentOn("Build"); + +Task("Run-Tests") + .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Render-Tests") + .IsDependentOn("Run-Designer-Tests") + .IsDependentOn("Run-Leak-Tests"); + +Task("Copy-Files") + .IsDependentOn("Copy-Files-Impl"); + .IsDependentOn("Run-Tests"); + +Task("Zip-Files") + .IsDependentOn("Zip-Files-Impl"); + .IsDependentOn("Copy-Files"); + +Task("Create-NuGet-Packages") + .IsDependentOn("Create-NuGet-Packages-Impl"); + .IsDependentOn("Run-Tests"); + .IsDependentOn("Inspect"); + +Task("Publish-MyGet") + .IsDependentOn("Publish-MyGet-Impl"); + .IsDependentOn("Create-NuGet-Packages"); + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsMasterBranch) + .WithCriteria((context, data) => data.Parameters.IsMyGetRelease); + +Task("Publish-NuGet") + .IsDependentOn("Publish-NuGet-Impl"); + .IsDependentOn("Create-NuGet-Packages") + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsNuGetRelease); + +Task("Inspect") + .IsDependentOn("Inspect-Impl"); + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .IsDependentOn("Restore-NuGet-Packages"); + /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// From 4de1d156bfbe2599377f52e37efa952dba50abd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 15:26:16 +0200 Subject: [PATCH 31/53] Fix errors --- build.cake | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/build.cake b/build.cake index e739d9375e..d60e358184 100644 --- a/build.cake +++ b/build.cake @@ -363,35 +363,35 @@ Task("Inspect-Impl") // TASKS DEPENDENCY TREE /////////////////////////////////////////////////////////////////////////////// -Task("Clean"); +Task("Clean") .IsDependentOn("Clean-Impl"); Task("Restore-NuGet-Packages") - .IsDependentOn("Restore-NuGet-Packages-Impl"); + .IsDependentOn("Restore-NuGet-Packages-Impl") .IsDependentOn("Clean") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows); Task("Build") - .IsDependentOn("Build-Impl"); + .IsDependentOn("Build-Impl") .IsDependentOn("Restore-NuGet-Packages"); Task("Run-Unit-Tests") - .IsDependentOn("Run-Unit-Tests-Impl"); + .IsDependentOn("Run-Unit-Tests-Impl") .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests); Task("Run-Designer-Tests") - .IsDependentOn("Run-Designer-Tests-Impl"); + .IsDependentOn("Run-Designer-Tests-Impl") .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests); Task("Run-Render-Tests") - .IsDependentOn("Run-Render-Tests-Impl"); + .IsDependentOn("Run-Render-Tests-Impl") .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows); Task("Run-Leak-Tests") - .IsDependentOn("Run-Leak-Tests-Impl"); + .IsDependentOn("Run-Leak-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .IsDependentOn("Build"); @@ -402,21 +402,21 @@ Task("Run-Tests") .IsDependentOn("Run-Leak-Tests"); Task("Copy-Files") - .IsDependentOn("Copy-Files-Impl"); + .IsDependentOn("Copy-Files-Impl") .IsDependentOn("Run-Tests"); Task("Zip-Files") - .IsDependentOn("Zip-Files-Impl"); + .IsDependentOn("Zip-Files-Impl") .IsDependentOn("Copy-Files"); Task("Create-NuGet-Packages") - .IsDependentOn("Create-NuGet-Packages-Impl"); - .IsDependentOn("Run-Tests"); + .IsDependentOn("Create-NuGet-Packages-Impl") + .IsDependentOn("Run-Tests") .IsDependentOn("Inspect"); Task("Publish-MyGet") - .IsDependentOn("Publish-MyGet-Impl"); - .IsDependentOn("Create-NuGet-Packages"); + .IsDependentOn("Publish-MyGet-Impl") + .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) .WithCriteria((context, data) => data.Parameters.IsMainRepo) @@ -424,7 +424,7 @@ Task("Publish-MyGet") .WithCriteria((context, data) => data.Parameters.IsMyGetRelease); Task("Publish-NuGet") - .IsDependentOn("Publish-NuGet-Impl"); + .IsDependentOn("Publish-NuGet-Impl") .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) @@ -432,7 +432,7 @@ Task("Publish-NuGet") .WithCriteria((context, data) => data.Parameters.IsNuGetRelease); Task("Inspect") - .IsDependentOn("Inspect-Impl"); + .IsDependentOn("Inspect-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages"); From c9fa15fce82edc376617cb853f9ea92eeefc71a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 15:27:21 +0200 Subject: [PATCH 32/53] Fix dependency order --- build.cake | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/build.cake b/build.cake index d60e358184..7bac795633 100644 --- a/build.cake +++ b/build.cake @@ -367,33 +367,33 @@ Task("Clean") .IsDependentOn("Clean-Impl"); Task("Restore-NuGet-Packages") - .IsDependentOn("Restore-NuGet-Packages-Impl") .IsDependentOn("Clean") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows); + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .IsDependentOn("Restore-NuGet-Packages-Impl"); Task("Build") - .IsDependentOn("Build-Impl") - .IsDependentOn("Restore-NuGet-Packages"); + .IsDependentOn("Restore-NuGet-Packages") + .IsDependentOn("Build-Impl"); Task("Run-Unit-Tests") - .IsDependentOn("Run-Unit-Tests-Impl") .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests); + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .IsDependentOn("Run-Unit-Tests-Impl"); Task("Run-Designer-Tests") - .IsDependentOn("Run-Designer-Tests-Impl") .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests); + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .IsDependentOn("Run-Designer-Tests-Impl"); Task("Run-Render-Tests") - .IsDependentOn("Run-Render-Tests-Impl") .IsDependentOn("Build") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows); + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .IsDependentOn("Run-Render-Tests-Impl"); Task("Run-Leak-Tests") - .IsDependentOn("Run-Leak-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) - .IsDependentOn("Build"); + .IsDependentOn("Build") + .IsDependentOn("Run-Leak-Tests-Impl"); Task("Run-Tests") .IsDependentOn("Run-Unit-Tests") @@ -402,39 +402,39 @@ Task("Run-Tests") .IsDependentOn("Run-Leak-Tests"); Task("Copy-Files") - .IsDependentOn("Copy-Files-Impl") - .IsDependentOn("Run-Tests"); + .IsDependentOn("Run-Tests") + .IsDependentOn("Copy-Files-Impl"); Task("Zip-Files") - .IsDependentOn("Zip-Files-Impl") - .IsDependentOn("Copy-Files"); + .IsDependentOn("Copy-Files") + .IsDependentOn("Zip-Files-Impl"); Task("Create-NuGet-Packages") - .IsDependentOn("Create-NuGet-Packages-Impl") .IsDependentOn("Run-Tests") - .IsDependentOn("Inspect"); + .IsDependentOn("Inspect") + .IsDependentOn("Create-NuGet-Packages-Impl"); Task("Publish-MyGet") - .IsDependentOn("Publish-MyGet-Impl") .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) .WithCriteria((context, data) => data.Parameters.IsMainRepo) .WithCriteria((context, data) => data.Parameters.IsMasterBranch) - .WithCriteria((context, data) => data.Parameters.IsMyGetRelease); + .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) + .IsDependentOn("Publish-MyGet-Impl"); Task("Publish-NuGet") - .IsDependentOn("Publish-NuGet-Impl") .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) .WithCriteria((context, data) => data.Parameters.IsMainRepo) - .WithCriteria((context, data) => data.Parameters.IsNuGetRelease); + .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) + .IsDependentOn("Publish-NuGet-Impl"); Task("Inspect") - .IsDependentOn("Inspect-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) - .IsDependentOn("Restore-NuGet-Packages"); + .IsDependentOn("Restore-NuGet-Packages") + .IsDependentOn("Inspect-Impl"); /////////////////////////////////////////////////////////////////////////////// // TARGETS From 9da1c65eec57af931cd41a3403e5a2503938cdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 15:53:31 +0200 Subject: [PATCH 33/53] Check first for criteria --- build.cake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build.cake b/build.cake index 7bac795633..0007e83c61 100644 --- a/build.cake +++ b/build.cake @@ -367,8 +367,8 @@ Task("Clean") .IsDependentOn("Clean-Impl"); Task("Restore-NuGet-Packages") - .IsDependentOn("Clean") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .IsDependentOn("Clean") .IsDependentOn("Restore-NuGet-Packages-Impl"); Task("Build") @@ -376,18 +376,18 @@ Task("Build") .IsDependentOn("Build-Impl"); Task("Run-Unit-Tests") - .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests) + .IsDependentOn("Build") .IsDependentOn("Run-Unit-Tests-Impl"); Task("Run-Designer-Tests") - .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests) + .IsDependentOn("Build") .IsDependentOn("Run-Designer-Tests-Impl"); Task("Run-Render-Tests") - .IsDependentOn("Build") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .IsDependentOn("Build") .IsDependentOn("Run-Render-Tests-Impl"); Task("Run-Leak-Tests") @@ -415,20 +415,20 @@ Task("Create-NuGet-Packages") .IsDependentOn("Create-NuGet-Packages-Impl"); Task("Publish-MyGet") - .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) .WithCriteria((context, data) => data.Parameters.IsMainRepo) .WithCriteria((context, data) => data.Parameters.IsMasterBranch) .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) + .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-MyGet-Impl"); Task("Publish-NuGet") - .IsDependentOn("Create-NuGet-Packages") .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) .WithCriteria((context, data) => !data.Parameters.IsPullRequest) .WithCriteria((context, data) => data.Parameters.IsMainRepo) .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) + .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-NuGet-Impl"); Task("Inspect") From 3bcaa2996aa9576691263ab20afc4d859764f125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:18:15 +0200 Subject: [PATCH 34/53] Moved criteria to Impl tasks --- build.cake | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/build.cake b/build.cake index 0007e83c61..dca58e54b3 100644 --- a/build.cake +++ b/build.cake @@ -109,6 +109,7 @@ Task("Clean-Impl") }); Task("Restore-NuGet-Packages-Impl") + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .Does(data => { var maxRetryCount = 5; @@ -191,6 +192,7 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) } Task("Run-Unit-Tests-Impl") + .WithCriteria((context, data) => !data.Parameters.SkipTests) .Does(data => { RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); @@ -209,17 +211,20 @@ Task("Run-Unit-Tests-Impl") }); Task("Run-Designer-Tests-Impl") + .WithCriteria((context, data) => !data.Parameters.SkipTests) .Does(data => { RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); }); Task("Run-Render-Tests-Impl") + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .Does(data => { RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); }); Task("Run-Leak-Tests-Impl") + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .Does(() => { var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); @@ -269,6 +274,11 @@ Task("Create-NuGet-Packages-Impl") }); Task("Publish-MyGet-Impl") + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsMasterBranch) + .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) .Does(data => { var apiKey = EnvironmentVariable("MYGET_API_KEY"); @@ -297,6 +307,10 @@ Task("Publish-MyGet-Impl") }); Task("Publish-NuGet-Impl") + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) .Does(data => { var apiKey = EnvironmentVariable("NUGET_API_KEY"); @@ -325,6 +339,7 @@ Task("Publish-NuGet-Impl") }); Task("Inspect-Impl") + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .Does(() => { var badIssues = new []{"PossibleNullReferenceException"}; @@ -367,7 +382,6 @@ Task("Clean") .IsDependentOn("Clean-Impl"); Task("Restore-NuGet-Packages") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Clean") .IsDependentOn("Restore-NuGet-Packages-Impl"); @@ -376,22 +390,18 @@ Task("Build") .IsDependentOn("Build-Impl"); Task("Run-Unit-Tests") - .WithCriteria((context, data) => !data.Parameters.SkipTests) .IsDependentOn("Build") .IsDependentOn("Run-Unit-Tests-Impl"); Task("Run-Designer-Tests") - .WithCriteria((context, data) => !data.Parameters.SkipTests) .IsDependentOn("Build") .IsDependentOn("Run-Designer-Tests-Impl"); Task("Run-Render-Tests") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .IsDependentOn("Build") .IsDependentOn("Run-Render-Tests-Impl"); Task("Run-Leak-Tests") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .IsDependentOn("Build") .IsDependentOn("Run-Leak-Tests-Impl"); @@ -415,24 +425,14 @@ Task("Create-NuGet-Packages") .IsDependentOn("Create-NuGet-Packages-Impl"); Task("Publish-MyGet") - .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) - .WithCriteria((context, data) => !data.Parameters.IsPullRequest) - .WithCriteria((context, data) => data.Parameters.IsMainRepo) - .WithCriteria((context, data) => data.Parameters.IsMasterBranch) - .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-MyGet-Impl"); Task("Publish-NuGet") - .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) - .WithCriteria((context, data) => !data.Parameters.IsPullRequest) - .WithCriteria((context, data) => data.Parameters.IsMainRepo) - .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-NuGet-Impl"); Task("Inspect") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages") .IsDependentOn("Inspect-Impl"); From 84be6a7d35a676f3fdaffb20f90a07078a217a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:32:02 +0200 Subject: [PATCH 35/53] Check for NetCoreOnly platform --- build.cake | 3 ++- packages.cake | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index dca58e54b3..839f91b135 100644 --- a/build.cake +++ b/build.cake @@ -110,6 +110,7 @@ Task("Clean-Impl") Task("Restore-NuGet-Packages-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(data => { var maxRetryCount = 5; @@ -150,7 +151,7 @@ void DotNetCoreBuild(Parameters parameters) Task("Build-Impl") .Does(data => { - if(data.Parameters.IsRunningOnWindows) + if(data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") { MSBuild(data.Parameters.MSBuildSolution, settings => { settings.SetConfiguration(data.Parameters.Configuration); diff --git a/packages.cake b/packages.cake index 79147b5f99..ffc6e7d257 100644 --- a/packages.cake +++ b/packages.cake @@ -491,7 +491,10 @@ public class Packages NuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop); - NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile); + + if (parameters.Platform != "NetCoreOnly") { + NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile); + } NuspecNuGetSettings.ForEach((nuspec) => SetNuGetNuspecCommonProperties(nuspec)); From 81bd1c1acc01e763077c7218a025d8ea33db9096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:34:46 +0200 Subject: [PATCH 36/53] More NetCoreOnly checks --- build.cake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 839f91b135..8e58eaf4b1 100644 --- a/build.cake +++ b/build.cake @@ -205,7 +205,7 @@ Task("Run-Unit-Tests-Impl") RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); - if (data.Parameters.IsRunningOnWindows) + if (data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") { RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); } @@ -219,6 +219,7 @@ Task("Run-Designer-Tests-Impl") Task("Run-Render-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(data => { RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); @@ -226,6 +227,7 @@ Task("Run-Render-Tests-Impl") Task("Run-Leak-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(() => { var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); @@ -341,6 +343,7 @@ Task("Publish-NuGet-Impl") Task("Inspect-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(() => { var badIssues = new []{"PossibleNullReferenceException"}; From 40eb37c9777d20bb642d466204bb753d6a62ed19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:42:19 +0200 Subject: [PATCH 37/53] Filter core files --- packages.cake | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/packages.cake b/packages.cake index ffc6e7d257..525ba539f9 100644 --- a/packages.cake +++ b/packages.cake @@ -1,3 +1,6 @@ +using System; +using System.Collections; +using System.Collections.Generic; using System.Xml.Linq; public class Packages @@ -9,9 +12,7 @@ public class Packages public string SkiaSharpVersion {get; private set; } public string SkiaSharpLinuxVersion {get; private set; } public Dictionary>> PackageVersions{get; private set;} - - - + class DependencyBuilder : List { Packages _parent; @@ -223,6 +224,21 @@ public class Packages } }; + IList coreFiles; + + if (parameters.Platform != "NetCoreOnly") { + coreFiles = coreLibrariesNuSpecContent + .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) + .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) + .Concat(toolsContent) + .ToList(); + } else { + coreFiles = coreLibrariesNuSpecContent + .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) + .Concat(toolsContent) + .ToList(); + } + var nuspecNuGetSettingsCore = new [] { /////////////////////////////////////////////////////////////////////////////// @@ -255,11 +271,7 @@ public class Packages "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter", "System.Memory") .ToArray(), - Files = coreLibrariesNuSpecContent - .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) - .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) - .Concat(toolsContent) - .ToList(), + Files = coreFiles, BasePath = context.Directory("./"), OutputDirectory = parameters.NugetRoot }, From a0196a3ddc130ca95831b9ebe827d62f86d0a9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:46:15 +0200 Subject: [PATCH 38/53] Filter tools content --- packages.cake | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages.cake b/packages.cake index 525ba539f9..3071ddf2a0 100644 --- a/packages.cake +++ b/packages.cake @@ -213,26 +213,27 @@ public class Packages }; }); - var toolsContent = new[] { - new NuSpecContent{ - Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.Designer.HostApp.dll")).FullPath, - Target = "tools/netcoreapp2.0/previewer" - }, - new NuSpecContent{ - Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp.NetFx/bin/" + parameters.DirSuffix + "/Avalonia.Designer.HostApp.exe")).FullPath, - Target = "tools/net461/previewer" - } + var toolHostApp = new NuSpecContent{ + Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.Designer.HostApp.dll")).FullPath, + Target = "tools/netcoreapp2.0/previewer" + }; + + var toolHostAppNetFx = new NuSpecContent{ + Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp.NetFx/bin/" + parameters.DirSuffix + "/Avalonia.Designer.HostApp.exe")).FullPath, + Target = "tools/net461/previewer" }; IList coreFiles; if (parameters.Platform != "NetCoreOnly") { + var toolsContent = new[] { toolHostApp, toolHostAppNetFx }; coreFiles = coreLibrariesNuSpecContent .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) .Concat(toolsContent) .ToList(); } else { + var toolsContent = new[] { toolHostApp }; coreFiles = coreLibrariesNuSpecContent .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) .Concat(toolsContent) From 8661dcc48f6e99659d90a97d316814ea493f3ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:57:26 +0200 Subject: [PATCH 39/53] Enable Direct2D1 for NetCoreOnly platform --- Avalonia.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia.sln b/Avalonia.sln index 39396f3ab8..75bca0bca8 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -398,6 +398,7 @@ Global {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|NetCoreOnly.ActiveCfg = Debug|Any CPU + {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|NetCoreOnly.Build.0 = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|x86.ActiveCfg = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|x86.Build.0 = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -407,6 +408,7 @@ Global {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|NetCoreOnly.ActiveCfg = Release|Any CPU + {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|NetCoreOnly.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|x86.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|x86.Build.0 = Release|Any CPU {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU From 21ab8c36662f96f8dd26e57c3e4663304ce4066f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 16:57:45 +0200 Subject: [PATCH 40/53] Do not include interop for NetCoreOnly --- packages.cake | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages.cake b/packages.cake index 3071ddf2a0..f45e53aeaf 100644 --- a/packages.cake +++ b/packages.cake @@ -464,22 +464,6 @@ public class Packages BasePath = context.Directory("./"), OutputDirectory = parameters.NugetRoot }, - new NuGetPackSettings() - { - Id = "Avalonia.Win32.Interoperability", - Dependencies = new [] - { - new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, - new NuSpecDependency() { Id = "SharpDX.Direct3D9", Version = SharpDXDirect3D9Version }, - }, - Files = new [] - { - new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } - }, - BasePath = context.Directory("./src/Windows"), - OutputDirectory = parameters.NugetRoot - }, /////////////////////////////////////////////////////////////////////////////// // Avalonia.LinuxFramebuffer /////////////////////////////////////////////////////////////////////////////// @@ -500,12 +484,30 @@ public class Packages } }; + var nuspecNuGetSettingInterop = new NuGetPackSettings() + { + Id = "Avalonia.Win32.Interoperability", + Dependencies = new [] + { + new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, + new NuSpecDependency() { Id = "SharpDX.Direct3D9", Version = SharpDXDirect3D9Version }, + }, + Files = new [] + { + new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } + }, + BasePath = context.Directory("./src/Windows"), + OutputDirectory = parameters.NugetRoot + }; + NuspecNuGetSettings = new List(); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop); if (parameters.Platform != "NetCoreOnly") { + NuspecNuGetSettings.Add(nuspecNuGetSettingInterop); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile); } From e787ff5a5a8b96e64e52e562fb9744830ecbac1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 17:01:39 +0200 Subject: [PATCH 41/53] Zip nupkg --- build.cake | 2 ++ parameters.cake | 2 ++ 2 files changed, 4 insertions(+) diff --git a/build.cake b/build.cake index 8e58eaf4b1..941f640544 100644 --- a/build.cake +++ b/build.cake @@ -258,6 +258,8 @@ Task("Zip-Files-Impl") { Zip(data.Parameters.BinRoot, data.Parameters.ZipCoreArtifacts); + Zip(data.Parameters.NugetRoot, data.Parameters.ZipNuGetArtifacts); + Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, data.Parameters.ZipTargetControlCatalogDesktopDirs, GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + diff --git a/parameters.cake b/parameters.cake index ffd472cbd4..939ac6227e 100644 --- a/parameters.cake +++ b/parameters.cake @@ -34,6 +34,7 @@ public class Parameters public DirectoryPathCollection BuildDirs { get; private set; } public string FileZipSuffix { get; private set; } public FilePath ZipCoreArtifacts { get; private set; } + public FilePath ZipNuGetArtifacts { get; private set; } public DirectoryPath ZipSourceControlCatalogDesktopDirs { get; private set; } public FilePath ZipTargetControlCatalogDesktopDirs { get; private set; } @@ -111,6 +112,7 @@ public class Parameters FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix); + ZipNuGetArtifacts = ZipRoot.CombineWithFilePath("Avalonia-NuGet-" + FileZipSuffix); ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461"); ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix); } From 5cd9ede2893235cca9f1ec8e7ed27b7247200dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 17:29:42 +0200 Subject: [PATCH 42/53] Fix Zip-Files task --- build.cake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index 941f640544..34ad461819 100644 --- a/build.cake +++ b/build.cake @@ -423,6 +423,7 @@ Task("Copy-Files") Task("Zip-Files") .IsDependentOn("Copy-Files") + .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Zip-Files-Impl"); Task("Create-NuGet-Packages") @@ -450,9 +451,9 @@ Task("Package") .IsDependentOn("Create-NuGet-Packages"); Task("AppVeyor") - .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") - .IsDependentOn("Publish-NuGet"); + .IsDependentOn("Publish-NuGet") + .IsDependentOn("Zip-Files"); Task("Travis") .IsDependentOn("Run-Tests"); From 03628cc0bb10f0d3505eb581829b1a930b6cf060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 29 Jun 2018 17:37:45 +0200 Subject: [PATCH 43/53] Do not copy when building NetCoreOnly platform --- build.cake | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/build.cake b/build.cake index 34ad461819..ab20f7fc7f 100644 --- a/build.cake +++ b/build.cake @@ -260,13 +260,15 @@ Task("Zip-Files-Impl") Zip(data.Parameters.NugetRoot, data.Parameters.ZipNuGetArtifacts); - Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, - data.Parameters.ZipTargetControlCatalogDesktopDirs, - GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + - GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + - GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + - GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + - GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); + if (data.Parameters.Platform != "NetCoreOnly") { + Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, + data.Parameters.ZipTargetControlCatalogDesktopDirs, + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); + } }); Task("Create-NuGet-Packages-Impl") From 30c9ef8dd46dc6000b2fc414d333de01179e8ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:38:32 +0200 Subject: [PATCH 44/53] Simplified task graph --- build.cake | 49 ++++++++++--------------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/build.cake b/build.cake index ab20f7fc7f..66a30edb59 100644 --- a/build.cake +++ b/build.cake @@ -386,53 +386,28 @@ Task("Inspect-Impl") // TASKS DEPENDENCY TREE /////////////////////////////////////////////////////////////////////////////// -Task("Clean") - .IsDependentOn("Clean-Impl"); - -Task("Restore-NuGet-Packages") - .IsDependentOn("Clean") - .IsDependentOn("Restore-NuGet-Packages-Impl"); - Task("Build") - .IsDependentOn("Restore-NuGet-Packages") + .IsDependentOn("Clean-Impl") + .IsDependentOn("Restore-NuGet-Packages-Impl") .IsDependentOn("Build-Impl"); -Task("Run-Unit-Tests") - .IsDependentOn("Build") - .IsDependentOn("Run-Unit-Tests-Impl"); - -Task("Run-Designer-Tests") - .IsDependentOn("Build") - .IsDependentOn("Run-Designer-Tests-Impl"); - -Task("Run-Render-Tests") - .IsDependentOn("Build") - .IsDependentOn("Run-Render-Tests-Impl"); - -Task("Run-Leak-Tests") +Task("Run-Tests") .IsDependentOn("Build") + .IsDependentOn("Run-Unit-Tests-Impl") + .IsDependentOn("Run-Render-Tests-Impl") + .IsDependentOn("Run-Designer-Tests-Impl") .IsDependentOn("Run-Leak-Tests-Impl"); -Task("Run-Tests") - .IsDependentOn("Run-Unit-Tests") - .IsDependentOn("Run-Render-Tests") - .IsDependentOn("Run-Designer-Tests") - .IsDependentOn("Run-Leak-Tests"); - -Task("Copy-Files") +Task("Create-NuGet-Packages") .IsDependentOn("Run-Tests") - .IsDependentOn("Copy-Files-Impl"); + .IsDependentOn("Inspect-Impl") + .IsDependentOn("Create-NuGet-Packages-Impl"); Task("Zip-Files") - .IsDependentOn("Copy-Files") .IsDependentOn("Create-NuGet-Packages") + .IsDependentOn("Copy-Files-Impl") .IsDependentOn("Zip-Files-Impl"); -Task("Create-NuGet-Packages") - .IsDependentOn("Run-Tests") - .IsDependentOn("Inspect") - .IsDependentOn("Create-NuGet-Packages-Impl"); - Task("Publish-MyGet") .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-MyGet-Impl"); @@ -441,10 +416,6 @@ Task("Publish-NuGet") .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-NuGet-Impl"); -Task("Inspect") - .IsDependentOn("Restore-NuGet-Packages") - .IsDependentOn("Inspect-Impl"); - /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// From 98e08fa63bdbb6449cb76d0d3abe7f550f9689d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:42:02 +0200 Subject: [PATCH 45/53] Removed unused task graph --- build.cake | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/build.cake b/build.cake index 66a30edb59..2b40a0e301 100644 --- a/build.cake +++ b/build.cake @@ -386,13 +386,10 @@ Task("Inspect-Impl") // TASKS DEPENDENCY TREE /////////////////////////////////////////////////////////////////////////////// -Task("Build") +Task("Run-Tests") .IsDependentOn("Clean-Impl") .IsDependentOn("Restore-NuGet-Packages-Impl") - .IsDependentOn("Build-Impl"); - -Task("Run-Tests") - .IsDependentOn("Build") + .IsDependentOn("Build-Impl") .IsDependentOn("Run-Unit-Tests-Impl") .IsDependentOn("Run-Render-Tests-Impl") .IsDependentOn("Run-Designer-Tests-Impl") From 7bfe322cddff7e9300989a864a0fa96dc453fbc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:47:29 +0200 Subject: [PATCH 46/53] Formatted code to be consistent --- build.cake | 138 +++++++++++++++++++++++++----------------------- packages.cake | 4 +- parameters.cake | 8 +-- 3 files changed, 74 insertions(+), 76 deletions(-) diff --git a/build.cake b/build.cake index 2b40a0e301..092334b9f8 100644 --- a/build.cake +++ b/build.cake @@ -6,6 +6,7 @@ #addin "nuget:?package=NuGet.Core&version=2.14.0" #tool "nuget:?package=NuGet.CommandLine&version=4.3.0" #tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2017.1.20170613.162720" + /////////////////////////////////////////////////////////////////////////////// // TOOLS /////////////////////////////////////////////////////////////////////////////// @@ -194,59 +195,62 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) Task("Run-Unit-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests) - .Does(data => { - RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Input.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Layout.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Markup.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); - RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); - if (data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") - { - RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); - } - }); + .Does(data => +{ + RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Input.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Layout.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); + if (data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") + { + RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); + } +}); Task("Run-Designer-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests) - .Does(data => { - RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); - }); + .Does(data => +{ + RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); +}); Task("Run-Render-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") - .Does(data => { - RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); - RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); - }); + .Does(data => +{ + RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); + RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); +}); Task("Run-Leak-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(() => +{ + var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); + var leakTestsExitCode = StartProcess(dotMemoryUnit, new ProcessSettings { - var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); - var leakTestsExitCode = StartProcess(dotMemoryUnit, new ProcessSettings - { - Arguments = new ProcessArgumentBuilder() - .Append(Context.Tools.Resolve("xunit.console.x86.exe").FullPath) - .Append("--propagate-exit-code") - .Append("--") - .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"), - Timeout = 120000 - }); - - if (leakTestsExitCode != 0) - { - throw new Exception("Leak Tests failed"); - } + Arguments = new ProcessArgumentBuilder() + .Append(Context.Tools.Resolve("xunit.console.x86.exe").FullPath) + .Append("--propagate-exit-code") + .Append("--") + .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"), + Timeout = 120000 }); + if (leakTestsExitCode != 0) + { + throw new Exception("Leak Tests failed"); + } +}); + Task("Copy-Files-Impl") .Does(data => { @@ -349,38 +353,38 @@ Task("Inspect-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") .Does(() => - { - var badIssues = new []{"PossibleNullReferenceException"}; - var whitelist = new []{"tests", "src\\android", "src\\ios", - "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; - Information("Running code inspections"); - - var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"), - new ProcessSettings - { - Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln", - RedirectStandardOutput = true - }); +{ + var badIssues = new []{"PossibleNullReferenceException"}; + var whitelist = new []{"tests", "src\\android", "src\\ios", + "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; + Information("Running code inspections"); + + var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"), + new ProcessSettings + { + Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln", + RedirectStandardOutput = true + }); - Information("Analyzing report"); - var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); - var failBuild = false; - foreach(var xml in doc.Descendants("Issue")) + Information("Analyzing report"); + var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); + var failBuild = false; + foreach(var xml in doc.Descendants("Issue")) + { + var typeId = xml.Attribute("TypeId").Value.ToString(); + if(badIssues.Contains(typeId)) { - var typeId = xml.Attribute("TypeId").Value.ToString(); - if(badIssues.Contains(typeId)) - { - var file = xml.Attribute("File").Value.ToString().ToLower(); - if(whitelist.Any(wh => file.StartsWith(wh))) - continue; - var line = xml.Attribute("Line").Value.ToString(); - Error(typeId + " - " + file + " on line " + line); - failBuild = true; - } + var file = xml.Attribute("File").Value.ToString().ToLower(); + if(whitelist.Any(wh => file.StartsWith(wh))) + continue; + var line = xml.Attribute("Line").Value.ToString(); + Error(typeId + " - " + file + " on line " + line); + failBuild = true; } - if(failBuild) - throw new Exception("Issues found"); - }); + } + if(failBuild) + throw new Exception("Issues found"); +}); /////////////////////////////////////////////////////////////////////////////// // TASKS DEPENDENCY TREE diff --git a/packages.cake b/packages.cake index f45e53aeaf..8a1cf8453c 100644 --- a/packages.cake +++ b/packages.cake @@ -16,6 +16,7 @@ public class Packages class DependencyBuilder : List { Packages _parent; + public DependencyBuilder(Packages parent) { _parent = parent; @@ -25,8 +26,7 @@ public class Packages { return _parent.PackageVersions[name].First().Item1; } - - + public DependencyBuilder Dep(string name, params string[] fws) { if(fws.Length == 0) diff --git a/parameters.cake b/parameters.cake index 939ac6227e..328eed81cf 100644 --- a/parameters.cake +++ b/parameters.cake @@ -8,8 +8,7 @@ public class Parameters public string AssemblyInfoPath { get; private set; } public string ReleasePlatform { get; private set; } public string ReleaseConfiguration { get; private set; } - public string MSBuildSolution { get; private set; } - public string XBuildSolution { get; private set; } + public string MSBuildSolution { get; private set; } public bool IsPlatformAnyCPU { get; private set; } public bool IsPlatformX86 { get; private set; } public bool IsPlatformX64 { get; private set; } @@ -54,7 +53,6 @@ public class Parameters ReleasePlatform = "Any CPU"; ReleaseConfiguration = "Release"; MSBuildSolution = "./Avalonia.sln"; - XBuildSolution = "./Avalonia.XBuild.sln"; // PARAMETERS IsPlatformAnyCPU = StringComparer.OrdinalIgnoreCase.Equals(Platform, "Any CPU"); @@ -72,7 +70,6 @@ public class Parameters IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); IsMyGetRelease = !IsTagged && IsReleasable; - // VERSION Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); @@ -104,12 +101,9 @@ public class Parameters NugetRoot = ArtifactsDir.Combine("nuget"); ZipRoot = ArtifactsDir.Combine("zip"); BinRoot = ArtifactsDir.Combine("bin"); - BuildDirs = context.GetDirectories("**/bin") + context.GetDirectories("**/obj"); - DirSuffix = Configuration; DirSuffixIOS = "iPhone" + "/" + Configuration; - FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot.CombineWithFilePath("Avalonia-NuGet-" + FileZipSuffix); From 3cd1e567ed0865b0610ef8597f6bbd206bdcb3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:53:05 +0200 Subject: [PATCH 47/53] Added IsPlatformNetCoreOnly parameter --- build.cake | 14 +++++++------- parameters.cake | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build.cake b/build.cake index 092334b9f8..3d2dfe6e51 100644 --- a/build.cake +++ b/build.cake @@ -111,7 +111,7 @@ Task("Clean-Impl") Task("Restore-NuGet-Packages-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) - .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") + .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(data => { var maxRetryCount = 5; @@ -152,7 +152,7 @@ void DotNetCoreBuild(Parameters parameters) Task("Build-Impl") .Does(data => { - if(data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") + if(data.Parameters.IsRunningOnWindows && !data.Parameters.IsPlatformNetCoreOnly) { MSBuild(data.Parameters.MSBuildSolution, settings => { settings.SetConfiguration(data.Parameters.Configuration); @@ -207,7 +207,7 @@ Task("Run-Unit-Tests-Impl") RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); - if (data.Parameters.IsRunningOnWindows && data.Parameters.Platform != "NetCoreOnly") + if (data.Parameters.IsRunningOnWindows && !data.Parameters.IsPlatformNetCoreOnly) { RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); } @@ -222,7 +222,7 @@ Task("Run-Designer-Tests-Impl") Task("Run-Render-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) - .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") + .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(data => { RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); @@ -231,7 +231,7 @@ Task("Run-Render-Tests-Impl") Task("Run-Leak-Tests-Impl") .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) - .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") + .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(() => { var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); @@ -264,7 +264,7 @@ Task("Zip-Files-Impl") Zip(data.Parameters.NugetRoot, data.Parameters.ZipNuGetArtifacts); - if (data.Parameters.Platform != "NetCoreOnly") { + if (!data.Parameters.IsPlatformNetCoreOnly) { Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, data.Parameters.ZipTargetControlCatalogDesktopDirs, GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + @@ -351,7 +351,7 @@ Task("Publish-NuGet-Impl") Task("Inspect-Impl") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) - .WithCriteria((context, data) => data.Parameters.Platform != "NetCoreOnly") + .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(() => { var badIssues = new []{"PossibleNullReferenceException"}; diff --git a/parameters.cake b/parameters.cake index 328eed81cf..e595b8159f 100644 --- a/parameters.cake +++ b/parameters.cake @@ -12,6 +12,7 @@ public class Parameters public bool IsPlatformAnyCPU { get; private set; } public bool IsPlatformX86 { get; private set; } public bool IsPlatformX64 { get; private set; } + public bool IsPlatformNetCoreOnly { get; private set; } public bool IsLocalBuild { get; private set; } public bool IsRunningOnUnix { get; private set; } public bool IsRunningOnWindows { get; private set; } @@ -58,6 +59,7 @@ public class Parameters IsPlatformAnyCPU = StringComparer.OrdinalIgnoreCase.Equals(Platform, "Any CPU"); IsPlatformX86 = StringComparer.OrdinalIgnoreCase.Equals(Platform, "x86"); IsPlatformX64 = StringComparer.OrdinalIgnoreCase.Equals(Platform, "x64"); + IsPlatformNetCoreOnly = StringComparer.OrdinalIgnoreCase.Equals(Platform, "NetCoreOnly"); IsLocalBuild = buildSystem.IsLocalBuild; IsRunningOnUnix = context.IsRunningOnUnix(); IsRunningOnWindows = context.IsRunningOnWindows(); From 608194cb1418c6519533d1d3fc878f050af373ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:53:40 +0200 Subject: [PATCH 48/53] Changed comments --- build.cake | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/build.cake b/build.cake index 3d2dfe6e51..6585da1e72 100644 --- a/build.cake +++ b/build.cake @@ -96,7 +96,7 @@ Teardown((context, buildContext) => }); /////////////////////////////////////////////////////////////////////////////// -// TASKS IMPLEMENTATIONS +// TASKS /////////////////////////////////////////////////////////////////////////////// Task("Clean-Impl") @@ -387,7 +387,7 @@ Task("Inspect-Impl") }); /////////////////////////////////////////////////////////////////////////////// -// TASKS DEPENDENCY TREE +// TARGETS /////////////////////////////////////////////////////////////////////////////// Task("Run-Tests") @@ -417,10 +417,6 @@ Task("Publish-NuGet") .IsDependentOn("Create-NuGet-Packages") .IsDependentOn("Publish-NuGet-Impl"); -/////////////////////////////////////////////////////////////////////////////// -// TARGETS -/////////////////////////////////////////////////////////////////////////////// - Task("Package") .IsDependentOn("Create-NuGet-Packages"); From efd119e6d4f55488dd80a87cce95f1ba60bc3b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 12:56:11 +0200 Subject: [PATCH 49/53] Unified tasks with criteria --- build.cake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index 6585da1e72..6b03f9fe52 100644 --- a/build.cake +++ b/build.cake @@ -221,7 +221,8 @@ Task("Run-Designer-Tests-Impl") }); Task("Run-Render-Tests-Impl") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(data => { @@ -230,7 +231,8 @@ Task("Run-Render-Tests-Impl") }); Task("Run-Leak-Tests-Impl") - .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria((context, data) => !data.Parameters.IsPlatformNetCoreOnly) .Does(() => { From fcf54c4939cf39a94105d2b7437db98905c48b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 13:07:48 +0200 Subject: [PATCH 50/53] Even more simplification of tasks graph --- build.cake | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/build.cake b/build.cake index 6b03f9fe52..28dc503e35 100644 --- a/build.cake +++ b/build.cake @@ -401,31 +401,17 @@ Task("Run-Tests") .IsDependentOn("Run-Designer-Tests-Impl") .IsDependentOn("Run-Leak-Tests-Impl"); -Task("Create-NuGet-Packages") +Task("Package") .IsDependentOn("Run-Tests") .IsDependentOn("Inspect-Impl") - .IsDependentOn("Create-NuGet-Packages-Impl"); - -Task("Zip-Files") - .IsDependentOn("Create-NuGet-Packages") - .IsDependentOn("Copy-Files-Impl") - .IsDependentOn("Zip-Files-Impl"); - -Task("Publish-MyGet") - .IsDependentOn("Create-NuGet-Packages") - .IsDependentOn("Publish-MyGet-Impl"); - -Task("Publish-NuGet") - .IsDependentOn("Create-NuGet-Packages") - .IsDependentOn("Publish-NuGet-Impl"); - -Task("Package") - .IsDependentOn("Create-NuGet-Packages"); + .IsDependentOn("Create-NuGet-Packages-Impl") Task("AppVeyor") - .IsDependentOn("Publish-MyGet") - .IsDependentOn("Publish-NuGet") - .IsDependentOn("Zip-Files"); + .IsDependentOn("Package") + .IsDependentOn("Publish-MyGet-Impl") + .IsDependentOn("Publish-NuGet-Impl") + .IsDependentOn("Copy-Files-Impl") + .IsDependentOn("Zip-Files-Impl"); Task("Travis") .IsDependentOn("Run-Tests"); From 98642a97b717b3afa62630dcbdc7a29629126b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 13:09:57 +0200 Subject: [PATCH 51/53] Changed task dependency order for AppVeyor task --- build.cake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.cake b/build.cake index 28dc503e35..deac33de09 100644 --- a/build.cake +++ b/build.cake @@ -408,10 +408,10 @@ Task("Package") Task("AppVeyor") .IsDependentOn("Package") - .IsDependentOn("Publish-MyGet-Impl") - .IsDependentOn("Publish-NuGet-Impl") .IsDependentOn("Copy-Files-Impl") - .IsDependentOn("Zip-Files-Impl"); + .IsDependentOn("Zip-Files-Impl") + .IsDependentOn("Publish-MyGet-Impl") + .IsDependentOn("Publish-NuGet-Impl"); Task("Travis") .IsDependentOn("Run-Tests"); From ff6676a2b0e1e027a990bfd6295c44c45184464c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 13:11:37 +0200 Subject: [PATCH 52/53] Fixed error --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index deac33de09..78c166a3bc 100644 --- a/build.cake +++ b/build.cake @@ -404,7 +404,7 @@ Task("Run-Tests") Task("Package") .IsDependentOn("Run-Tests") .IsDependentOn("Inspect-Impl") - .IsDependentOn("Create-NuGet-Packages-Impl") + .IsDependentOn("Create-NuGet-Packages-Impl"); Task("AppVeyor") .IsDependentOn("Package") From 213ec8ce07c806f8cd7d1a0ea16474ccaa55da90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Sat, 30 Jun 2018 14:46:39 +0200 Subject: [PATCH 53/53] Use IsPlatformNetCoreOnly property --- packages.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages.cake b/packages.cake index 8a1cf8453c..51d9c48159 100644 --- a/packages.cake +++ b/packages.cake @@ -225,7 +225,7 @@ public class Packages IList coreFiles; - if (parameters.Platform != "NetCoreOnly") { + if (!parameters.IsPlatformNetCoreOnly) { var toolsContent = new[] { toolHostApp, toolHostAppNetFx }; coreFiles = coreLibrariesNuSpecContent .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) @@ -506,7 +506,7 @@ public class Packages NuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop); - if (parameters.Platform != "NetCoreOnly") { + if (!parameters.IsPlatformNetCoreOnly) { NuspecNuGetSettings.Add(nuspecNuGetSettingInterop); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile); }