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;
}