diff --git a/src/Build.UnitTests/BackEnd/ProcessIdTaskSidecar.cs b/src/Build.UnitTests/BackEnd/ProcessIdTaskSidecar.cs index 486c644ee07..37c08e5e665 100644 --- a/src/Build.UnitTests/BackEnd/ProcessIdTaskSidecar.cs +++ b/src/Build.UnitTests/BackEnd/ProcessIdTaskSidecar.cs @@ -9,7 +9,7 @@ namespace Microsoft.Build.UnitTests { /// - /// This task was created for https://github.com/dotnet/msbuild/issues/3141 + /// This task was created for https://github.com/dotnet/msbuild/issues/3141. /// public class ProcessIdTaskSidecar : Task { diff --git a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs index 3e2ddc21569..6d9f78de3aa 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs @@ -115,7 +115,7 @@ public void TaskNodesDieAfterBuild(bool taskHostFactorySpecified, bool envVariab /// can coexist in the same build and operate independently. /// [Fact] - public void TransientandSidecarNodeCanCoexist() + public void TransientAndSidecarNodeCanCoexist() { using (TestEnvironment env = TestEnvironment.Create(_output)) { diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index 1e8febcc1d8..30af960b986 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -647,8 +647,6 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor // if runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments. string commandLineArgsPlaceholder = "{0} /nologo /nodemode:2 /nodereuse:{1} /low:{2} "; - bool enableNodeReuse = ComponentHost.BuildParameters.EnableNodeReuse && Handshake.IsHandshakeOptionEnabled(hostContext, HandshakeOptions.NodeReuse); - IList nodeContexts; int nodeId = (int)hostContext; @@ -665,7 +663,7 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor // There is always one task host per host context so we always create just 1 one task host node here. nodeContexts = GetNodes( runtimeHostPath, - string.Format(commandLineArgsPlaceholder, Path.Combine(msbuildAssemblyPath, Constants.MSBuildAssemblyName), enableNodeReuse, ComponentHost.BuildParameters.LowPriority), + string.Format(commandLineArgsPlaceholder, Path.Combine(msbuildAssemblyPath, Constants.MSBuildAssemblyName), NodeReuseIsEnabled(hostContext), ComponentHost.BuildParameters.LowPriority), nodeId, this, handshake, @@ -689,7 +687,7 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor nodeContexts = GetNodes( msbuildLocation, - string.Format(commandLineArgsPlaceholder, string.Empty, enableNodeReuse, ComponentHost.BuildParameters.LowPriority), + string.Format(commandLineArgsPlaceholder, string.Empty, NodeReuseIsEnabled(hostContext), ComponentHost.BuildParameters.LowPriority), nodeId, this, new Handshake(hostContext), @@ -698,6 +696,17 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor 1); return nodeContexts.Count == 1; + + // Determines whether node reuse should be enabled for the given host context. + // Node reuse allows MSBuild to reuse existing task host processes for better performance, + // but is disabled for CLR2 because it uses legacy MSBuildTaskHost. + bool NodeReuseIsEnabled(HandshakeOptions hostContext) + { + bool isCLR2 = Handshake.IsHandshakeOptionEnabled(hostContext, HandshakeOptions.CLR2); + + return Handshake.IsHandshakeOptionEnabled(hostContext, HandshakeOptions.NodeReuse) + && !isCLR2; + } } /// diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 1daf0873048..056bb45e954 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -393,7 +393,7 @@ internal ITask CreateTaskInstance( buildComponentHost, mergedParameters, _loadedType, - _isTaskHostFactory + taskHostFactoryExplicitlyRequested: _isTaskHostFactory #if FEATURE_APPDOMAIN , appDomainSetup #endif diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 4f6927a1d65..6d90fa71380 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -124,7 +124,6 @@ internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactor /// private bool _taskExecutionSucceeded = false; - /// /// This separates the cause where we force all tasks to run in a task host via environment variables and TaskHostFactory /// The difference is that TaskHostFactory requires the TaskHost to be transient i.e. to expire after build. @@ -132,9 +131,8 @@ internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactor private bool _taskHostFactoryExplicitlyRequested = false; /// - /// Constructor + /// Constructor. /// - /// #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter public TaskHostTask( IElementLocation taskLocation, @@ -301,7 +299,13 @@ public bool Execute() { lock (_taskHostLock) { - _requiredContext = CommunicationsUtilities.GetHandshakeOptions(taskHost: true, nodeReuse: !_taskHostFactoryExplicitlyRequested, taskHostParameters: _taskHostParameters); + _requiredContext = CommunicationsUtilities.GetHandshakeOptions( + taskHost: true, + + // Determine if we should use node reuse based on build parameters or user preferences (comes from UsingTask element). + // If the user explicitly requested the task host factory, then we always disable node reuse due to the transient nature of task host factory hosts. + nodeReuse: _buildComponentHost.BuildParameters.EnableNodeReuse && !_taskHostFactoryExplicitlyRequested, + taskHostParameters: _taskHostParameters); _connectedToTaskHost = _taskHostProvider.AcquireAndSetUpHost(_requiredContext, this, this, hostConfiguration, _taskHostParameters); } diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index d76fff1454e..b633d8c810b 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -716,6 +716,9 @@ public static ExitType Execute( #endif GatherAllSwitches(commandLine, out var switchesFromAutoResponseFile, out var switchesNotFromAutoResponseFile, out _); + + CommunicationsUtilities.Trace($"Command line parameters: {commandLine}"); + bool buildCanBeInvoked = ProcessCommandLineSwitches( switchesFromAutoResponseFile, switchesNotFromAutoResponseFile, diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index f657468c2b4..aded7e1d3c0 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -185,7 +185,7 @@ internal Handshake(HandshakeOptions nodeType, string predefinedToolsDirectory = { } - // Helper method to validate handshake option presense. + // Helper method to validate handshake option presence internal static bool IsHandshakeOptionEnabled(HandshakeOptions hostContext, HandshakeOptions option) => (hostContext & option) == option; // Source options of the handshake. @@ -920,7 +920,8 @@ internal static HandshakeOptions GetHandshakeOptions( break; } - if (nodeReuse) + // Node reuse is not supported in CLR2 because it's a legacy runtime. + if (nodeReuse && clrVersion != 2) { context |= HandshakeOptions.NodeReuse; }