diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index 7c119ce2929..f795a3eceae 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -9,6 +9,7 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; using Microsoft.Build.Internal; +using System.Threading.Tasks; namespace Microsoft.Build.Execution { @@ -19,6 +20,8 @@ public sealed class OutOfProcServerNode : INode, INodePacketFactory, INodePacket { private readonly Func _buildFunction; + private readonly Action _onCancel; + /// /// The endpoint used to talk to the host. /// @@ -59,11 +62,14 @@ public sealed class OutOfProcServerNode : INode, INodePacketFactory, INodePacket /// private readonly bool _debugCommunications; + private Task? _buildTask; + private string _serverBusyMutexName = default!; - public OutOfProcServerNode(Func buildFunction) + public OutOfProcServerNode(Func buildFunction, Action onCancel) { _buildFunction = buildFunction; + _onCancel = onCancel; new Dictionary(); _debugCommunications = (Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM") == "1"); @@ -74,6 +80,7 @@ public OutOfProcServerNode(Func buildFu (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.ServerNodeBuildCommand, ServerNodeBuildCommand.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.NodeBuildComplete, NodeBuildComplete.FactoryForDeserialization, this); + (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.ServerNodeBuildCancel, ServerNodeBuildCancel.FactoryForDeserialization, this); } #region INode Members @@ -105,7 +112,7 @@ public NodeEngineShutdownReason Run(out Exception? shutdownException) _nodeEndpoint.Listen(this); var waitHandles = new WaitHandle[] { _shutdownEvent, _packetReceivedEvent }; - + // Get the current directory before doing any work. We need this so we can restore the directory when the node shutsdown. while (true) { @@ -269,11 +276,35 @@ private void HandlePacket(INodePacket packet) switch (packet.Type) { case NodePacketType.ServerNodeBuildCommand: - HandleServerNodeBuildCommand((ServerNodeBuildCommand)packet); + HandleServerNodeBuildCommandAsync((ServerNodeBuildCommand)packet); + break; + case NodePacketType.ServerNodeBuildCancel: + _onCancel(); break; } } + private void HandleServerNodeBuildCommandAsync(ServerNodeBuildCommand command) + { + _buildTask = Task.Run(() => + { + try + { + HandleServerNodeBuildCommand(command); + } + catch(Exception e) + { + _shutdownException = e; + _shutdownReason = NodeEngineShutdownReason.Error; + _shutdownEvent.Set(); + } + finally + { + _buildTask = null; + } + }); + } + private void HandleServerNodeBuildCommand(ServerNodeBuildCommand command) { CommunicationsUtilities.Trace("Building with MSBuild server with command line {0}", command.CommandLine); @@ -285,6 +316,8 @@ private void HandleServerNodeBuildCommand(ServerNodeBuildCommand command) _shutdownException = new InvalidOperationException("Client requested build while server is busy processing previous client build request."); _shutdownReason = NodeEngineShutdownReason.Error; _shutdownEvent.Set(); + + return; } // set build process context diff --git a/src/Build/BackEnd/Node/ServerNodeBuildCancel.cs b/src/Build/BackEnd/Node/ServerNodeBuildCancel.cs new file mode 100644 index 00000000000..349c1b8170d --- /dev/null +++ b/src/Build/BackEnd/Node/ServerNodeBuildCancel.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +namespace Microsoft.Build.BackEnd +{ + internal sealed class ServerNodeBuildCancel : INodePacket + { + public NodePacketType Type => NodePacketType.ServerNodeBuildCancel; + + public void Translate(ITranslator translator) + { + } + + internal static INodePacket FactoryForDeserialization(ITranslator translator) + { + return new ServerNodeBuildCancel(); + } + } +} diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index d14821f787f..9c4bbac8ee4 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -155,6 +155,7 @@ + diff --git a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt index b7e25f06956..349a8e57aac 100644 --- a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt @@ -15,5 +15,5 @@ Microsoft.Build.Execution.MSBuildClientExitType.ServerBusy = 1 -> Microsoft.Buil Microsoft.Build.Execution.MSBuildClientExitType.Success = 0 -> Microsoft.Build.Execution.MSBuildClientExitType Microsoft.Build.Execution.MSBuildClientExitType.Unexpected = 4 -> Microsoft.Build.Execution.MSBuildClientExitType Microsoft.Build.Execution.OutOfProcServerNode -Microsoft.Build.Execution.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction) -> void -Microsoft.Build.Execution.OutOfProcServerNode.Run(out System.Exception shutdownException) -> Microsoft.Build.Execution.NodeEngineShutdownReason \ No newline at end of file +Microsoft.Build.Execution.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction, System.Action onCancel) -> void +Microsoft.Build.Execution.OutOfProcServerNode.Run(out System.Exception shutdownException) -> Microsoft.Build.Execution.NodeEngineShutdownReason diff --git a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt index b7e25f06956..39c901f1b5c 100644 --- a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -15,5 +15,5 @@ Microsoft.Build.Execution.MSBuildClientExitType.ServerBusy = 1 -> Microsoft.Buil Microsoft.Build.Execution.MSBuildClientExitType.Success = 0 -> Microsoft.Build.Execution.MSBuildClientExitType Microsoft.Build.Execution.MSBuildClientExitType.Unexpected = 4 -> Microsoft.Build.Execution.MSBuildClientExitType Microsoft.Build.Execution.OutOfProcServerNode -Microsoft.Build.Execution.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction) -> void +Microsoft.Build.Execution.OutOfProcServerNode.OutOfProcServerNode(System.Func buildFunction, System.Action onCancel) -> void Microsoft.Build.Execution.OutOfProcServerNode.Run(out System.Exception shutdownException) -> Microsoft.Build.Execution.NodeEngineShutdownReason \ No newline at end of file diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index f3665f0c80b..b7d98c179d5 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -2684,7 +2684,14 @@ private static void StartLocalNode(CommandLineSwitches commandLineSwitches, bool return (exitCode, exitType.ToString()); }; - OutOfProcServerNode node = new(buildFunction); + Action onCancel = () => + { + Console.WriteLine(ResourceUtilities.GetResourceString("AbortingBuild")); + + BuildManager.DefaultBuildManager.CancelAllSubmissions(); + }; + + OutOfProcServerNode node = new(buildFunction, onCancel); s_isServerNode = true; shutdownReason = node.Run(out nodeException); diff --git a/src/Shared/INodePacket.cs b/src/Shared/INodePacket.cs index b0ec1f1f6c5..0ddbf49a0d7 100644 --- a/src/Shared/INodePacket.cs +++ b/src/Shared/INodePacket.cs @@ -207,6 +207,12 @@ internal enum NodePacketType : byte /// Keep this enum value constant intact as this is part of contract with dotnet CLI /// ServerNodeConsoleWrite = 0xF2, + + /// + /// Command to cancel ongoing build. + /// Keep this enum value constant intact as this is part of contract with dotnet CLI + /// + ServerNodeBuildCancel = 0xF3, } #endregion