diff --git a/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs index 5cd20e9d57a..6a31254da54 100644 --- a/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; @@ -1089,7 +1090,7 @@ public void TaskStartedNullBuildEventContext() Assert.Throws(() => { ProcessBuildEventHelper service = (ProcessBuildEventHelper)ProcessBuildEventHelper.CreateLoggingService(LoggerMode.Synchronous, 1); - service.LogTaskStarted(null, "MyTask", "ProjectFile", "ProjectFileOfTask"); + service.LogTaskStarted(taskBuildEventContext: null, "MyTask", "ProjectFile", "ProjectFileOfTask", taskAssemblyLocation: null); }); } @@ -1443,14 +1444,15 @@ private void TestProjectFinishedEvent(string projectFile, bool success) private void TestTaskStartedEvent(string taskName, string projectFile, string projectFileOfTask) { string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("TaskStarted", taskName); + string taskAssemblyLocation = Assembly.GetExecutingAssembly().Location; ProcessBuildEventHelper service = (ProcessBuildEventHelper)ProcessBuildEventHelper.CreateLoggingService(LoggerMode.Synchronous, 1); - service.LogTaskStarted(s_buildEventContext, taskName, projectFile, projectFileOfTask); - VerifyTaskStartedEvent(taskName, projectFile, projectFileOfTask, message, service); + service.LogTaskStarted(s_buildEventContext, taskName, projectFile, projectFileOfTask, taskAssemblyLocation); + VerifyTaskStartedEvent(taskName, projectFile, projectFileOfTask, message, service, taskAssemblyLocation); service.ResetProcessedBuildEvent(); service.OnlyLogCriticalEvents = true; - service.LogTaskStarted(s_buildEventContext, taskName, projectFile, projectFileOfTask); + service.LogTaskStarted(s_buildEventContext, taskName, projectFile, projectFileOfTask, taskAssemblyLocation); Assert.Null(service.ProcessedBuildEvent); } @@ -1631,7 +1633,7 @@ private void VerifyTaskFinishedEvent(string taskName, string projectFile, string /// ProjectFileOfTask to create the comparison event with. /// Message to create the comparison event with. /// LoggingService mock object which overrides ProcessBuildEvent and can provide a ProcessedBuildEvent (the event which would have been sent to the loggers) - private void VerifyTaskStartedEvent(string taskName, string projectFile, string projectFileOfTask, string message, ProcessBuildEventHelper service) + private void VerifyTaskStartedEvent(string taskName, string projectFile, string projectFileOfTask, string message, ProcessBuildEventHelper service, string taskAssemblyLocation) { TaskStartedEventArgs taskEvent = new TaskStartedEventArgs( message, @@ -1639,7 +1641,8 @@ private void VerifyTaskStartedEvent(string taskName, string projectFile, string projectFile, projectFileOfTask, taskName, - service.ProcessedBuildEvent.Timestamp); + service.ProcessedBuildEvent.Timestamp, + taskAssemblyLocation); taskEvent.BuildEventContext = s_buildEventContext; Assert.True(((TaskStartedEventArgs)service.ProcessedBuildEvent).IsEquivalent(taskEvent)); } diff --git a/src/Build.UnitTests/BackEnd/MockLoggingService.cs b/src/Build.UnitTests/BackEnd/MockLoggingService.cs index b3ceffe4bd5..a62b03686d4 100644 --- a/src/Build.UnitTests/BackEnd/MockLoggingService.cs +++ b/src/Build.UnitTests/BackEnd/MockLoggingService.cs @@ -573,7 +573,8 @@ public void LogTargetFinished(BuildEventContext targetBuildEventContext, string /// The name of the task /// The project file /// The project file containing the task node. - public void LogTaskStarted(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode) + /// >The location of the assembly containing the implementation of the task. + public void LogTaskStarted(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, string taskAssemblyLocation) { } @@ -584,8 +585,9 @@ public void LogTaskStarted(BuildEventContext targetBuildEventContext, string tas /// The name of the task /// The project file /// The project file containing the task node. + /// >The location of the assembly containing the implementation of the task. /// The task logging context - public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column) + public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column, string taskAssemblyLocation) { return new BuildEventContext(0, 0, 0, 0); } diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs index f4644d4e358..08efa2c9a7f 100644 --- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs @@ -44,7 +44,7 @@ public class TaskExecutionHost_Tests : ITestTaskHost, IBuildEngine2, IDisposable /// /// The task execution host /// - private ITaskExecutionHost _host; + private TaskExecutionHost _host; /// /// The mock logging service @@ -1169,7 +1169,7 @@ private void InitializeHost(bool throwOnExecute) #else AssemblyLoadInfo loadInfo = AssemblyLoadInfo.Create(typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory).GetTypeInfo().FullName, null); #endif - LoadedType loadedType = new LoadedType(typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory), loadInfo, typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory).GetTypeInfo().Assembly, typeof(ITaskItem)); + LoadedType loadedType = new LoadedType(typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory), loadInfo, typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory).Assembly, typeof(ITaskItem)); TaskBuilderTestTask.TaskBuilderTestTaskFactory taskFactory = new TaskBuilderTestTask.TaskBuilderTestTaskFactory(); taskFactory.ThrowOnExecute = throwOnExecute; @@ -1190,7 +1190,7 @@ private void InitializeHost(bool throwOnExecute) CancellationToken.None); ProjectTaskInstance taskInstance = project.Targets["foo"].Tasks.First(); - TaskLoggingContext talc = tlc.LogTaskBatchStarted(".", taskInstance); + TaskLoggingContext talc = tlc.LogTaskBatchStarted(".", taskInstance, typeof(TaskBuilderTestTask.TaskBuilderTestTaskFactory).Assembly.GetName().FullName); ItemDictionary itemsByName = new ItemDictionary(); diff --git a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs index 46170961e81..cb60a9f2d42 100644 --- a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs +++ b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs @@ -191,7 +191,9 @@ public void RoundtripTaskStartedEventArgs() null, projectFile: "C:\\project.proj", taskFile: "C:\\common.targets", - taskName: "Csc"); + taskName: "Csc", + DateTime.Now, + "TaskAssemblyLocation"); args.LineNumber = 42; args.ColumnNumber = 999; @@ -200,7 +202,8 @@ public void RoundtripTaskStartedEventArgs() e => e.TaskFile, e => e.TaskName, e => e.LineNumber.ToString(), - e => e.ColumnNumber.ToString()); + e => e.ColumnNumber.ToString(), + e => e.TaskAssemblyLocation); } [Fact] diff --git a/src/Build/BackEnd/Components/Logging/ILoggingService.cs b/src/Build/BackEnd/Components/Logging/ILoggingService.cs index 6d4973bc223..3e44402a61e 100644 --- a/src/Build/BackEnd/Components/Logging/ILoggingService.cs +++ b/src/Build/BackEnd/Components/Logging/ILoggingService.cs @@ -570,7 +570,8 @@ BuildEventContext LogProjectStarted( /// The name of the task /// The project file which is being built /// The file in which the task is defined - typically a .targets file - void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode); + /// >The location of the assembly containing the implementation of the task. + void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, string taskAssemblyLocation); /// /// Log that a task is about to start @@ -581,8 +582,9 @@ BuildEventContext LogProjectStarted( /// The file in which the task is defined - typically a .targets file /// The line number in the file where the task invocation is located. /// The column number in the file where the task invocation is located. + /// >The location of the assembly containing the implementation of the task. /// The task build event context - BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column); + BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column, string taskAssemblyLocation); /// /// Log that a task has just completed diff --git a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs index 9ef9a58f17b..547554d06d8 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs @@ -688,8 +688,9 @@ public void LogTargetFinished(BuildEventContext targetBuildEventContext, string /// Task Name /// Project file being built /// Project file which contains the task + /// >The location of the assembly containing the implementation of the task. /// BuildEventContext is null - public void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode) + public void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, string taskAssemblyLocation) { ErrorUtilities.VerifyThrow(taskBuildEventContext != null, "targetBuildEventContext is null"); if (!OnlyLogCriticalEvents) @@ -699,7 +700,8 @@ public void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskN helpKeyword: null, projectFile, projectFileOfTaskNode, - taskName); + taskName, + taskAssemblyLocation); buildEvent.BuildEventContext = taskBuildEventContext; ProcessLoggingEvent(buildEvent); } @@ -714,9 +716,10 @@ public void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskN /// Project file which contains the task /// The line number in the file where the task invocation is located. /// The column number in the file where the task invocation is located. + /// >The location of the assembly containing the implementation of the task. /// The build event context for the task. /// BuildEventContext is null - public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column) + public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column, string taskAssemblyLocation) { ErrorUtilities.VerifyThrow(targetBuildEventContext != null, "targetBuildEventContext is null"); BuildEventContext taskBuildEventContext = new BuildEventContext( @@ -734,7 +737,8 @@ public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventConte helpKeyword: null, projectFile, projectFileOfTaskNode, - taskName); + taskName, + taskAssemblyLocation); buildEvent.BuildEventContext = taskBuildEventContext; buildEvent.LineNumber = line; buildEvent.ColumnNumber = column; diff --git a/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs b/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs index 3d9a25df6cc..af37bd0f690 100644 --- a/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs @@ -108,11 +108,11 @@ internal void LogTargetBatchFinished(string projectFullPath, bool success, IEnum /// /// Log that a task is about to start /// - internal TaskLoggingContext LogTaskBatchStarted(string projectFullPath, ProjectTargetInstanceChild task) + internal TaskLoggingContext LogTaskBatchStarted(string projectFullPath, ProjectTargetInstanceChild task, string taskAssemblyLocation) { ErrorUtilities.VerifyThrow(IsValid, "Should be valid"); - return new TaskLoggingContext(this, projectFullPath, task); + return new TaskLoggingContext(this, projectFullPath, task, taskAssemblyLocation); } /// diff --git a/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs b/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs index 2728ed5592e..f962f3da74d 100644 --- a/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs @@ -34,7 +34,7 @@ internal class TaskLoggingContext : BuildLoggingContext /// /// Constructs a task logging context from a parent target context and a task node. /// - internal TaskLoggingContext(TargetLoggingContext targetLoggingContext, string projectFullPath, ProjectTargetInstanceChild task) + internal TaskLoggingContext(TargetLoggingContext targetLoggingContext, string projectFullPath, ProjectTargetInstanceChild task, string taskAssemblyLocation) : base(targetLoggingContext) { _targetLoggingContext = targetLoggingContext; @@ -72,7 +72,8 @@ internal TaskLoggingContext(TargetLoggingContext targetLoggingContext, string pr projectFullPath, task.Location.File, task.Location.Line, - task.Location.Column); + task.Location.Column, + taskAssemblyLocation); this.IsValid = true; } diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs index ee09e5be50f..4e6bc8ef0fd 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs @@ -115,7 +115,7 @@ internal class TaskBuilder : ITaskBuilder, IBuildComponent /// /// The task execution host for in-proc tasks. /// - private ITaskExecutionHost _taskExecutionHost; + private TaskExecutionHost _taskExecutionHost; /// /// The object used to synchronize access to the task execution host. @@ -423,10 +423,12 @@ private async Task ExecuteBucket(TaskHost taskHost, ItemBucket b { // We need to find the task before logging the task started event so that the using task statement comes before the task started event IDictionary taskIdentityParameters = GatherTaskIdentityParameters(bucket.Expander); - TaskRequirements? requirements = _taskExecutionHost.FindTask(taskIdentityParameters); + (TaskRequirements? requirements, TaskFactoryWrapper taskFactoryWrapper) = _taskExecutionHost.FindTask(taskIdentityParameters); + string taskAssemblyLocation = taskFactoryWrapper?.TaskFactoryLoadedType?.Path; + if (requirements != null) { - TaskLoggingContext taskLoggingContext = _targetLoggingContext.LogTaskBatchStarted(_projectFullPath, _targetChildInstance); + TaskLoggingContext taskLoggingContext = _targetLoggingContext.LogTaskBatchStarted(_projectFullPath, _targetChildInstance, taskAssemblyLocation); MSBuildEventSource.Log.ExecuteTaskStart(_taskNode?.Name, taskLoggingContext.BuildEventContext.TaskId); _buildRequestEntry.Request.CurrentTaskContext = taskLoggingContext.BuildEventContext; @@ -652,7 +654,7 @@ private async Task InitializeAndExecuteTask(TaskLoggingContext t ProjectErrorUtilities.ThrowInvalidProject(_targetChildInstance.Location, "TaskDeclarationOrUsageError", _taskNode.Name); } - using var assemblyLoadsTracker = AssemblyLoadsTracker.StartTracking(taskLoggingContext, AssemblyLoadingContext.TaskRun, (_taskExecutionHost as TaskExecutionHost)?.TaskInstance?.GetType()); + using var assemblyLoadsTracker = AssemblyLoadsTracker.StartTracking(taskLoggingContext, AssemblyLoadingContext.TaskRun, _taskExecutionHost?.TaskInstance?.GetType()); try { @@ -734,7 +736,7 @@ private void UpdateContinueOnError(ItemBucket bucket, TaskHost taskHost) /// The batching bucket /// The task execution mode /// The result of running the task. - private async Task ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) + private async Task ExecuteInstantiatedTask(TaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) { UpdateContinueOnError(bucket, taskHost); @@ -754,20 +756,13 @@ private async Task ExecuteInstantiatedTask(ITaskExecutionHost ta Exception taskException = null; // If this is the MSBuild task, we need to execute it's special internal method. - TaskExecutionHost host = taskExecutionHost as TaskExecutionHost; - Type taskType = host.TaskInstance.GetType(); - try { - if (taskType == typeof(MSBuild)) + if (taskExecutionHost.TaskInstance is MSBuild msbuildTask) { - MSBuild msbuildTask = host.TaskInstance as MSBuild; - - ErrorUtilities.VerifyThrow(msbuildTask != null, "Unexpected MSBuild internal task."); - var undeclaredProjects = GetUndeclaredProjects(msbuildTask); - if (undeclaredProjects != null && undeclaredProjects.Count != 0) + if (undeclaredProjects?.Count > 0) { _continueOnError = ContinueOnError.ErrorAndStop; @@ -799,9 +794,8 @@ private async Task ExecuteInstantiatedTask(ITaskExecutionHost ta } } } - else if (taskType == typeof(CallTarget)) + else if (taskExecutionHost.TaskInstance is CallTarget callTargetTask) { - CallTarget callTargetTask = host.TaskInstance as CallTarget; taskResult = await callTargetTask.ExecuteInternal(); } else @@ -941,7 +935,7 @@ private async Task ExecuteInstantiatedTask(ITaskExecutionHost ta // When a task fails it must log an error. If a task fails to do so, // that is logged as an error. MSBuild tasks are an exception because // errors are not logged directly from them, but the tasks spawned by them. - IBuildEngine be = host.TaskInstance.BuildEngine; + IBuildEngine be = taskExecutionHost.TaskInstance.BuildEngine; if (taskReturned // if the task returned && !taskResult // and it returned false && !taskLoggingContext.HasLoggedErrors // and it didn't log any errors @@ -1062,7 +1056,7 @@ private List GetUndeclaredProjects(MSBuild msbuildTask) /// The task execution mode /// The bucket to which the task execution belongs. /// true, if successful - private bool GatherTaskOutputs(ITaskExecutionHost taskExecutionHost, TaskExecutionMode howToExecuteTask, ItemBucket bucket) + private bool GatherTaskOutputs(TaskExecutionHost taskExecutionHost, TaskExecutionMode howToExecuteTask, ItemBucket bucket) { bool gatheredTaskOutputsSuccessfully = true; diff --git a/src/Build/BackEnd/TaskExecutionHost/AddInParts/ITaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/AddInParts/ITaskExecutionHost.cs deleted file mode 100644 index 60dfee13d2c..00000000000 --- a/src/Build/BackEnd/TaskExecutionHost/AddInParts/ITaskExecutionHost.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Threading; -using Microsoft.Build.Execution; -using Microsoft.Build.Framework; -using ElementLocation = Microsoft.Build.Construction.ElementLocation; -using TargetLoggingContext = Microsoft.Build.BackEnd.Logging.TargetLoggingContext; -using TaskLoggingContext = Microsoft.Build.BackEnd.Logging.TaskLoggingContext; - -#nullable disable - -namespace Microsoft.Build.BackEnd -{ - /// - /// Flags requrned by ITaskExecutionHost.FindTask(). - /// - [Flags] - internal enum TaskRequirements - { - /// - /// The task was not found. - /// - None = 0, - - /// - /// The task must be executed on an STA thread. - /// - RequireSTAThread = 0x01, - - /// - /// The task must be executed in a separate AppDomain. - /// - RequireSeparateAppDomain = 0x02 - } - - /// - /// This interface represents the host for task execution. When used in the in-proc scenario, these method calls essentially - /// are pass-throughs to just set some member variables and call methods directly on the task and associated objects. - /// In the out-of-proc/AppDomain-isolated case, the object implementing these methods may break apart the information - /// in the parameters to be consumed by the IContract representing the remote object through MAF. - /// - /// REFACTOR - Eliminate this interface. - /// - internal interface ITaskExecutionHost - { - /// - /// The associated project. - /// - ProjectInstance ProjectInstance - { - get; - } - - /// - /// Flag to determine whether or not to log task inputs. - /// - bool LogTaskInputs { get; } - - /// - /// Initialize the host with the objects required to communicate with the host process. - /// - void InitializeForTask(IBuildEngine2 buildEngine, TargetLoggingContext loggingContext, ProjectInstance projectInstance, string taskName, ElementLocation taskLocation, ITaskHost taskHost, bool continueOnError, -#if FEATURE_APPDOMAIN - AppDomainSetup appDomainSetup, -#endif - bool isOutOfProc, CancellationToken cancellationToken); - - /// - /// Ask the task host to find its task in the registry and get it ready for initializing the batch - /// - /// The task requirements if the task is found, null otherwise. - TaskRequirements? FindTask(IDictionary taskIdentityParameters); - - /// - /// Initializes for running a particular batch - /// - /// True if the task is instantiated, false otherwise. - bool InitializeForBatch(TaskLoggingContext loggingContext, ItemBucket batchBucket, IDictionary taskIdentityParameters); - - /// - /// Sets a task parameter using an unevaluated value, which will be expanded by the batch bucket. - /// - bool SetTaskParameters(IDictionary parameters); - - /// - /// Gets all of the outputs and stores them in the batch bucket. - /// - bool GatherTaskOutputs(string parameterName, ElementLocation parameterLocation, bool outputTargetIsItem, string outputTargetName); - - /// - /// Signal that we are done with this bucket. - /// - void CleanupForBatch(); - - /// - /// Signal that we are done with this task. - /// - void CleanupForTask(); - - /// - /// Executes the task. - /// - /// - /// True if execution succeeded, false otherwise. - /// - bool Execute(); - } -} diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 4418a48a63d..dbfce00a8f6 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -29,12 +29,34 @@ namespace Microsoft.Build.BackEnd { + /// + /// Flags returned by TaskExecutionHost.FindTask(). + /// + [Flags] + internal enum TaskRequirements + { + /// + /// The task was not found. + /// + None = 0, + + /// + /// The task must be executed on an STA thread. + /// + RequireSTAThread = 0x01, + + /// + /// The task must be executed in a separate AppDomain. + /// + RequireSeparateAppDomain = 0x02 + } + /// /// The TaskExecutionHost is responsible for instantiating tasks, setting their parameters and gathering outputs using /// reflection, and executing the task in the appropriate context.The TaskExecutionHost does not deal with any part of the task declaration or /// XML. /// - internal class TaskExecutionHost : ITaskExecutionHost, IDisposable + internal class TaskExecutionHost : IDisposable { /// /// Time interval in miliseconds to wait between receiving a cancelation signal and emitting the first warning that a non-cancelable task has not finished @@ -178,7 +200,7 @@ internal TaskExecutionHost() /// /// The associated project. /// - ProjectInstance ITaskExecutionHost.ProjectInstance => _projectInstance; + public ProjectInstance ProjectInstance => _projectInstance; /// /// Gets the task instance @@ -220,7 +242,7 @@ public virtual void Dispose() /// /// Initialize to run a specific task. /// - void ITaskExecutionHost.InitializeForTask(IBuildEngine2 buildEngine, TargetLoggingContext loggingContext, ProjectInstance projectInstance, string taskName, ElementLocation taskLocation, ITaskHost taskHost, bool continueOnError, + public void InitializeForTask(IBuildEngine2 buildEngine, TargetLoggingContext loggingContext, ProjectInstance projectInstance, string taskName, ElementLocation taskLocation, ITaskHost taskHost, bool continueOnError, #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif @@ -243,17 +265,14 @@ void ITaskExecutionHost.InitializeForTask(IBuildEngine2 buildEngine, TargetLoggi /// /// Ask the task host to find its task in the registry and get it ready for initializing the batch /// - /// True if the task is found in the task registry false if otherwise. - TaskRequirements? ITaskExecutionHost.FindTask(IDictionary taskIdentityParameters) + /// The task requirements and task factory wrapper if the task is found, (null, null) otherwise. + public (TaskRequirements? requirements, TaskFactoryWrapper taskFactoryWrapper) FindTask(IDictionary taskIdentityParameters) { - if (_taskFactoryWrapper == null) - { - _taskFactoryWrapper = FindTaskInRegistry(taskIdentityParameters); - } + _taskFactoryWrapper ??= FindTaskInRegistry(taskIdentityParameters); - if (_taskFactoryWrapper == null) + if (_taskFactoryWrapper is null) { - return null; + return (null, null); } TaskRequirements requirements = TaskRequirements.None; @@ -272,13 +291,13 @@ void ITaskExecutionHost.InitializeForTask(IBuildEngine2 buildEngine, TargetLoggi _remotedTaskItems = new List(); } - return requirements; + return (requirements, _taskFactoryWrapper); } /// /// Initialize to run a specific batch of the current task. /// - bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, ItemBucket batchBucket, IDictionary taskIdentityParameters) + public bool InitializeForBatch(TaskLoggingContext loggingContext, ItemBucket batchBucket, IDictionary taskIdentityParameters) { ErrorUtilities.VerifyThrowArgumentNull(loggingContext, nameof(loggingContext)); ErrorUtilities.VerifyThrowArgumentNull(batchBucket, nameof(batchBucket)); @@ -313,6 +332,13 @@ bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, It return false; } + string realTaskAssemblyLoaction = TaskInstance.GetType().Assembly.Location; + if (!string.IsNullOrWhiteSpace(realTaskAssemblyLoaction) && + realTaskAssemblyLoaction != _taskFactoryWrapper.TaskFactoryLoadedType.Path) + { + _taskLoggingContext.LogComment(MessageImportance.Normal, "TaskAssemblyLocationMismatch", realTaskAssemblyLoaction, _taskFactoryWrapper.TaskFactoryLoadedType.Path); + } + TaskInstance.BuildEngine = _buildEngine; TaskInstance.HostObject = _taskHost; @@ -324,7 +350,7 @@ bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, It /// /// The name/value pairs for the parameters. /// True if the parameters were set correctly, false otherwise. - bool ITaskExecutionHost.SetTaskParameters(IDictionary parameters) + public bool SetTaskParameters(IDictionary parameters) { ErrorUtilities.VerifyThrowArgumentNull(parameters, nameof(parameters)); @@ -395,7 +421,7 @@ bool ITaskExecutionHost.SetTaskParameters(IDictionary /// True of the outputs were gathered successfully, false otherwise. - bool ITaskExecutionHost.GatherTaskOutputs(string parameterName, ElementLocation parameterLocation, bool outputTargetIsItem, string outputTargetName) + public bool GatherTaskOutputs(string parameterName, ElementLocation parameterLocation, bool outputTargetIsItem, string outputTargetName) { ErrorUtilities.VerifyThrow(_taskFactoryWrapper != null, "Need a taskFactoryWrapper to retrieve outputs from."); @@ -500,7 +526,7 @@ bool ITaskExecutionHost.GatherTaskOutputs(string parameterName, ElementLocation /// /// Cleans up after running a batch. /// - void ITaskExecutionHost.CleanupForBatch() + public void CleanupForBatch() { try { @@ -518,7 +544,7 @@ void ITaskExecutionHost.CleanupForBatch() /// /// Cleans up after running the task. /// - void ITaskExecutionHost.CleanupForTask() + public void CleanupForTask() { #if FEATURE_APPDOMAIN if (_resolver != null) @@ -541,7 +567,7 @@ void ITaskExecutionHost.CleanupForTask() /// /// Executes the task. /// - bool ITaskExecutionHost.Execute() + public bool Execute() { // If cancel is called before we get here, we simply don't execute and return failure. If cancel is called after this check // the task needs to be able to handle the possibility that Cancel has been called before the task has done anything meaningful, @@ -923,17 +949,11 @@ private ITask InstantiateTask(IDictionary taskIdentityParameters else { TaskFactoryLoggingHost loggingHost = new TaskFactoryLoggingHost(_buildEngine.IsRunningMultipleNodes, _taskLocation, _taskLoggingContext); - ITaskFactory2 taskFactory2 = _taskFactoryWrapper.TaskFactory as ITaskFactory2; try { - if (taskFactory2 == null) - { - task = _taskFactoryWrapper.TaskFactory.CreateTask(loggingHost); - } - else - { - task = taskFactory2.CreateTask(loggingHost, taskIdentityParameters); - } + task = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ? + taskFactory2.CreateTask(loggingHost, taskIdentityParameters) : + _taskFactoryWrapper.TaskFactory.CreateTask(loggingHost); } finally { diff --git a/src/Build/Logging/BinaryLogger/BinaryLogger.cs b/src/Build/Logging/BinaryLogger/BinaryLogger.cs index 54de65d1d05..8be89caf254 100644 --- a/src/Build/Logging/BinaryLogger/BinaryLogger.cs +++ b/src/Build/Logging/BinaryLogger/BinaryLogger.cs @@ -69,6 +69,8 @@ public sealed class BinaryLogger : ILogger // - Adding serialized events lengths - to support forward compatible reading // version 19: // - GeneratedFileUsedEventArgs exposed for brief period of time (so let's continue with 20) + // version 20: + // - TaskStartedEventArgs: Added TaskAssemblyLocation property // This should be never changed. // The minimum version of the binary log reader that can read log of above version. @@ -76,7 +78,7 @@ public sealed class BinaryLogger : ILogger // The current version of the binary log representation. // Changes with each update of the binary log format. - internal const int FileFormatVersion = 18; + internal const int FileFormatVersion = 20; // The minimum version of the binary log reader that can read log of above version. // This should be changed only when the binary log format is changed in a way that would prevent it from being diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs index 2c49c17c8a7..1f6d638f67c 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs @@ -806,6 +806,7 @@ private BuildEventArgs ReadTaskStartedEventArgs() var taskName = ReadOptionalString(); var projectFile = ReadOptionalString(); var taskFile = ReadOptionalString(); + var taskAssemblyLocation = _fileFormatVersion > 19 ? ReadOptionalString() : null; var e = new TaskStartedEventArgs( fields.Message, @@ -813,7 +814,8 @@ private BuildEventArgs ReadTaskStartedEventArgs() projectFile, taskFile, taskName, - fields.Timestamp); + fields.Timestamp, + taskAssemblyLocation); e.LineNumber = fields.LineNumber; e.ColumnNumber = fields.ColumnNumber; SetCommonFields(e, fields); diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs index a3ce5efa3c5..0c5c82846b3 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs @@ -408,6 +408,7 @@ private BinaryLogRecordKind Write(TaskStartedEventArgs e) WriteDeduplicatedString(e.TaskName); WriteDeduplicatedString(e.ProjectFile); WriteDeduplicatedString(e.TaskFile); + WriteDeduplicatedString(e.TaskAssemblyLocation); return BinaryLogRecordKind.TaskStarted; } diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 6d41abd7891..da392976726 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -354,7 +354,6 @@ - @@ -682,11 +681,7 @@ - + <Namespace Prefix='ns' Uri='urn:schemas-microsoft-com:asm.v1' /> /configuration/runtime/ns:assemblyBinding/* diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index a037905be7e..7453fff8c5e 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -2000,7 +2000,7 @@ Utilization: {0} Average Utilization: {1:###.0} LOCALIZATION: {0} is a file path. {1} is a comma-separated list of target names - + MSB4276: The default SDK resolver failed to resolve SDK "{0}" because directory "{1}" did not exist. @@ -2095,6 +2095,9 @@ Utilization: {0} Average Utilization: {1:###.0} LOCALIZATION: {0} is integer number denoting number of bytes. 'int.MaxValue' should not be translated. + + Task assembly was loaded from '{0}' while the desired location was '{1}'. +