Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1244,7 +1244,7 @@ public void TaskFactoryWithNullTaskTypeLogsError()

TaskRegistry registry = CreateTaskRegistryAndRegisterTasks(elementList);

InvalidProjectFileException exception = Should.Throw<InvalidProjectFileException>(() => registry.GetRegisteredTask("Task1", "none", null, false, new TargetLoggingContext(_loggingService, new BuildEventContext(1, 1, BuildEventContext.InvalidProjectContextId, 1)), ElementLocation.Create("none", 1, 2)));
InvalidProjectFileException exception = Should.Throw<InvalidProjectFileException>(() => registry.GetRegisteredTask("Task1", "none", null, false, new TargetLoggingContext(_loggingService, new BuildEventContext(1, 1, BuildEventContext.InvalidProjectContextId, 1)), ElementLocation.Create("none", 1, 2), false));

exception.ErrorCode.ShouldBe("MSB4175");

Expand Down Expand Up @@ -2008,7 +2008,7 @@ private void RetrieveAndValidateRegisteredTaskRecord(
string expectedArchitecture)
{
bool retrievedFromCache;
var record = registry.GetTaskRegistrationRecord(TestTaskName, null, taskParameters, exactMatchRequired, _targetLoggingContext, _elementLocation, out retrievedFromCache);
var record = registry.GetTaskRegistrationRecord(TestTaskName, null, taskParameters, exactMatchRequired, _targetLoggingContext, _elementLocation, out retrievedFromCache, false);

if (shouldBeRetrieved)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Build/BackEnd/BuildManager/BuildManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ public void EndBuild()
Reset();
_buildManagerState = BuildManagerState.Idle;

if (Traits.Instance.ForceTaskFactoryOutOfProc)
if (Traits.Instance.ForceTaskFactoryOutOfProc || _buildParameters.MultiThreaded)
{
TaskFactoryUtilities.CleanCurrentProcessInlineTaskDirectory();
}
Expand Down
37 changes: 21 additions & 16 deletions src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -890,18 +890,18 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary<string, string> taskId
{
if (!_intrinsicTasks.TryGetValue(_taskName, out TaskFactoryWrapper returnClass))
{
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, true /* exact match */, _targetLoggingContext, _taskLocation);
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, true /* exact match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);
if (returnClass == null)
{
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, false /* fuzzy match */, _targetLoggingContext, _taskLocation);
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, false /* fuzzy match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);

if (returnClass == null)
{
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, true /* exact match */, _targetLoggingContext, _taskLocation);
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, true /* exact match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);

if (returnClass == null)
{
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, false /* fuzzy match */, _targetLoggingContext, _taskLocation);
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, false /* fuzzy match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);

if (returnClass == null)
{
Expand Down Expand Up @@ -980,13 +980,17 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
}
else
{
TaskFactoryLoggingHost loggingHost = new TaskFactoryLoggingHost(_buildEngine.IsRunningMultipleNodes, _taskLocation, _taskLoggingContext);
TaskFactoryEngineContext taskFactoryEngineContext = new TaskFactoryEngineContext(_buildEngine.IsRunningMultipleNodes, _taskLocation, _taskLoggingContext, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false, Traits.Instance.ForceTaskFactoryOutOfProc);
bool isTaskHost = false;
try
{
// Check if we should force out-of-process execution for non-AssemblyTaskFactory instances
// This happens when: 1) Environment variable is set, OR 2) MultiThreaded build is enabled
// IntrinsicTaskFactory tasks run in proc always
if (Traits.Instance.ForceTaskFactoryOutOfProc && _taskFactoryWrapper.TaskFactory is not IntrinsicTaskFactory)
bool shouldRunOutOfProc = TaskFactoryUtilities.ShouldCompileForOutOfProcess(taskFactoryEngineContext)
&& _taskFactoryWrapper.TaskFactory is not IntrinsicTaskFactory;

if (shouldRunOutOfProc)
{
// Custom Task factories are not supported, internal TaskFactories implement this marker interface
if (_taskFactoryWrapper.TaskFactory is not IOutOfProcTaskFactory outOfProcTaskFactory)
Expand All @@ -999,15 +1003,15 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
return null;
}

task = CreateTaskHostTaskForOutOfProcFactory(taskIdentityParameters, loggingHost, outOfProcTaskFactory);
task = CreateTaskHostTaskForOutOfProcFactory(taskIdentityParameters, taskFactoryEngineContext, outOfProcTaskFactory);
isTaskHost = true;
}
else
{
// Normal in-process execution for custom task factories
task = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ?
taskFactory2.CreateTask(loggingHost, taskIdentityParameters) :
_taskFactoryWrapper.TaskFactory.CreateTask(loggingHost);
taskFactory2.CreateTask(taskFactoryEngineContext, taskIdentityParameters) :
_taskFactoryWrapper.TaskFactory.CreateTask(taskFactoryEngineContext);
}

// Track telemetry for non-AssemblyTaskFactory task factories
Expand All @@ -1016,7 +1020,7 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
finally
{
#if FEATURE_APPDOMAIN
loggingHost.MarkAsInactive();
taskFactoryEngineContext.MarkAsInactive();
#endif
}
}
Expand Down Expand Up @@ -1730,20 +1734,20 @@ private void DisplayCancelWaitMessage()

/// <summary>
/// Creates a <see cref="TaskHostTask"/> wrapper to run a non-AssemblyTaskFactory task out of process.
/// This is used when Traits.Instance.ForceTaskFactoryOutOfProc is true to ensure
/// This is used when Traits.Instance.ForceTaskFactoryOutOfProc=1 is true or the multi-threaded mode is active to ensure
/// non-AssemblyTaskFactory tasks run in isolation.
/// </summary>
/// <param name="taskIdentityParameters">Task identity parameters.</param>
/// <param name="loggingHost">The logging host to use for the task.</param>
/// <param name="taskFactoryEngineContext">The engine context to use for the task.</param>
/// <param name="outOfProcTaskFactory">The out-of-process task factory instance.</param>
/// <returns>A TaskHostTask that will execute the inner task out of process, or <code>null</code> if task creation fails.</returns>
private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string> taskIdentityParameters, TaskFactoryLoggingHost loggingHost, IOutOfProcTaskFactory outOfProcTaskFactory)
private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string> taskIdentityParameters, TaskFactoryEngineContext taskFactoryEngineContext, IOutOfProcTaskFactory outOfProcTaskFactory)
{
ITask innerTask;

innerTask = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ?
taskFactory2.CreateTask(loggingHost, taskIdentityParameters) :
_taskFactoryWrapper.TaskFactory.CreateTask(loggingHost);
taskFactory2.CreateTask(taskFactoryEngineContext, taskIdentityParameters) :
_taskFactoryWrapper.TaskFactory.CreateTask(taskFactoryEngineContext);

if (innerTask == null)
{
Expand All @@ -1758,7 +1762,8 @@ private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string>
string resolvedAssemblyLocation = outOfProcTaskFactory.GetAssemblyPath();

// This should never happen - if the factory can create a task, it should know where the assembly is
ErrorUtilities.VerifyThrow(!string.IsNullOrEmpty(resolvedAssemblyLocation),
ErrorUtilities.VerifyThrow(
!string.IsNullOrEmpty(resolvedAssemblyLocation),
$"IOutOfProcTaskFactory {_taskFactoryWrapper.TaskFactory.FactoryName} created a task but returned null/empty assembly path");

LoadedType taskLoadedType = new LoadedType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ namespace Microsoft.Build.BackEnd
/// <summary>
/// The host allows task factories access to method to allow them to log message during the construction of the task factories.
/// </summary>
internal class TaskFactoryLoggingHost :
internal class TaskFactoryEngineContext :
#if FEATURE_APPDOMAIN
MarshalByRefObject,
#endif
IBuildEngine
IBuildEngine,
ITaskFactoryBuildParameterProvider
{
/// <summary>
/// Location of the task node in the original file
Expand All @@ -36,6 +37,16 @@ internal class TaskFactoryLoggingHost :
/// </summary>
private BuildLoggingContext _loggingContext;

/// <summary>
/// Whether the build is running in multi-threaded mode.
/// </summary>
private readonly bool _isMultiThreadedBuild;

/// <summary>
/// Whether task factories should be forced to compile for out-of-process execution.
/// </summary>
private readonly bool _forceOutOfProcessExecution;

/// <summary>
/// Is the system running in multi-process mode and requires events to be serializable
/// </summary>
Expand All @@ -58,7 +69,7 @@ internal class TaskFactoryLoggingHost :
/// <summary>
/// Constructor
/// </summary>
public TaskFactoryLoggingHost(bool isRunningWithMultipleNodes, ElementLocation elementLocation, BuildLoggingContext loggingContext)
public TaskFactoryEngineContext(bool isRunningWithMultipleNodes, ElementLocation elementLocation, BuildLoggingContext loggingContext, bool isMultiThreadedBuild, bool forceOutOfProcessExecution)
{
ErrorUtilities.VerifyThrowArgumentNull(loggingContext);
ErrorUtilities.VerifyThrowInternalNull(elementLocation);
Expand All @@ -67,6 +78,8 @@ public TaskFactoryLoggingHost(bool isRunningWithMultipleNodes, ElementLocation e
_isRunningWithMultipleNodes = isRunningWithMultipleNodes;
_loggingContext = loggingContext;
_elementLocation = elementLocation;
_isMultiThreadedBuild = isMultiThreadedBuild;
_forceOutOfProcessExecution = forceOutOfProcessExecution;
}

/// <summary>
Expand Down Expand Up @@ -146,6 +159,37 @@ internal BuildLoggingContext LoggingContext
{ return _loggingContext; }
}

/// <summary>
/// Gets a value indicating whether the build is running in multi-threaded mode (/mt flag).
/// </summary>
/// <remarks>
/// This property implements ITaskFactoryBuildParameterProvider to allow task factories to determine
/// if they should compile for out-of-process execution during their Initialize() method.
/// </remarks>
public bool IsMultiThreadedBuild
{
get
{
VerifyActiveProxy();
return _isMultiThreadedBuild;
}
}

/// <summary>
/// Gets a value indicating whether task factories should be forced to compile for out-of-process execution.
/// </summary>
/// <remarks>
/// This is controlled by the MSBUILDFORCEINLINETASKFACTORIESOUTOFPROC environment variable.
/// </remarks>
public bool ForceOutOfProcessExecution
{
get
{
VerifyActiveProxy();
return _forceOutOfProcessExecution;
}
}

#region IBuildEngine Members

/// <summary>
Expand Down
Loading