/// <summary> /// Walks through the set of tasks for this target and processes them by handing them off to the TaskBuilder. /// </summary> /// <returns> /// The result of the tasks, based on the last task which ran. /// </returns> private async Task <WorkUnitResult> ProcessBucket(ITaskBuilder taskBuilder, TargetLoggingContext targetLoggingContext, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution) { WorkUnitResultCode aggregatedTaskResult = WorkUnitResultCode.Success; WorkUnitActionCode finalActionCode = WorkUnitActionCode.Continue; WorkUnitResult lastResult = new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null); try { // Grab the task builder so if cancel is called it will have something to operate on. _currentTaskBuilder = taskBuilder; int currentTask = 0; // Walk through all of the tasks and execute them in order. for (; (currentTask < _target.Children.Count) && !_cancellationToken.IsCancellationRequested; ++currentTask) { ProjectTargetInstanceChild targetChildInstance = _target.Children[currentTask]; // Execute the task. lastResult = await taskBuilder.ExecuteTask(targetLoggingContext, _requestEntry, _targetBuilderCallback, targetChildInstance, mode, lookupForInference, lookupForExecution, _cancellationToken); if (lastResult.ResultCode == WorkUnitResultCode.Failed) { aggregatedTaskResult = WorkUnitResultCode.Failed; } else if (lastResult.ResultCode == WorkUnitResultCode.Success && aggregatedTaskResult != WorkUnitResultCode.Failed) { aggregatedTaskResult = WorkUnitResultCode.Success; } if (lastResult.ActionCode == WorkUnitActionCode.Stop) { finalActionCode = WorkUnitActionCode.Stop; break; } } if (_cancellationToken.IsCancellationRequested) { aggregatedTaskResult = WorkUnitResultCode.Canceled; finalActionCode = WorkUnitActionCode.Stop; } } finally { _currentTaskBuilder = null; } return(new WorkUnitResult(aggregatedTaskResult, finalActionCode, lastResult.Exception)); }
/// <summary> /// Executes the task. /// </summary> public Task <WorkUnitResult> ExecuteTask(TargetLoggingContext targetLoggingContext, BuildRequestEntry requestEntry, ITargetBuilderCallback targetBuilderCallback, ProjectTargetInstanceChild task, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Canceled, WorkUnitActionCode.Stop, null))); } ProjectOnErrorInstance errorTask = task as ProjectOnErrorInstance; if (null != errorTask) { ErrorTasks.Add(errorTask); } else { ProjectTaskInstance taskInstance = task as ProjectTaskInstance; ExecutedTasks.Add(taskInstance); if ((mode & TaskExecutionMode.InferOutputsOnly) == TaskExecutionMode.InferOutputsOnly) { lookupForInference.AddNewItem(new ProjectItemInstance(requestEntry.RequestConfiguration.Project, taskInstance.Name + "_Item", "Item", task.Location.File)); } else if ((mode & TaskExecutionMode.ExecuteTaskAndGatherOutputs) == TaskExecutionMode.ExecuteTaskAndGatherOutputs) { lookupForExecution.AddNewItem(new ProjectItemInstance(requestEntry.RequestConfiguration.Project, taskInstance.Name + "_Item", "Item", task.Location.File)); } if (String.Equals(taskInstance.Name, "CallTarget", StringComparison.OrdinalIgnoreCase)) { taskInstance.GetParameter("Targets"); targetBuilderCallback.LegacyCallTarget(taskInstance.GetParameter("Targets").Split(MSBuildConstants.SemicolonChar), false, taskInstance.Location); } _taskNumber++; if (FailTaskNumber == _taskNumber) { if (taskInstance.ContinueOnError == "True") { return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Continue, null))); } return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null))); } } return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null))); }
/// <summary> /// Create a new PropertyGroup task. /// </summary> /// <param name="taskInstance">The task instance data</param> /// <param name="loggingContext">The logging context</param> /// <param name="projectInstance">The project instance</param> /// <param name="logTaskInputs">Flag to determine whether or not to log task inputs.</param> public PropertyGroupIntrinsicTask(ProjectPropertyGroupTaskInstance taskInstance, TargetLoggingContext loggingContext, ProjectInstance projectInstance, bool logTaskInputs) : base(loggingContext, projectInstance, logTaskInputs) { _taskInstance = taskInstance; }
/// <summary> /// Runs all of the tasks for this target, batched as necessary. /// </summary> internal async Task ExecuteTarget(ITaskBuilder taskBuilder, BuildRequestEntry requestEntry, ProjectLoggingContext projectLoggingContext, CancellationToken cancellationToken) { #if MSBUILDENABLEVSPROFILING try { string beginTargetBuild = String.Format(CultureInfo.CurrentCulture, "Build Target {0} in Project {1} - Start", this.Name, projectFullPath); DataCollection.CommentMarkProfile(8800, beginTargetBuild); #endif try { VerifyState(_state, TargetEntryState.Execution); ErrorUtilities.VerifyThrow(!_isExecuting, "Target {0} is already executing", _target.Name); _cancellationToken = cancellationToken; _isExecuting = true; // Generate the batching buckets. Note that each bucket will get a lookup based on the baseLookup. This lookup will be in its // own scope, which we will collapse back down into the baseLookup at the bottom of the function. List <ItemBucket> buckets = BatchingEngine.PrepareBatchingBuckets(GetBatchableParametersForTarget(), _baseLookup, _target.Location); WorkUnitResult aggregateResult = new WorkUnitResult(); TargetLoggingContext targetLoggingContext = null; bool targetSuccess = false; int numberOfBuckets = buckets.Count; string projectFullPath = requestEntry.RequestConfiguration.ProjectFullPath; string parentTargetName = null; if (ParentEntry != null && ParentEntry.Target != null) { parentTargetName = ParentEntry.Target.Name; } for (int i = 0; i < numberOfBuckets; i++) { ItemBucket bucket = buckets[i]; // If one of the buckets failed, stop building. if (aggregateResult.ActionCode == WorkUnitActionCode.Stop) { break; } targetLoggingContext = projectLoggingContext.LogTargetBatchStarted(projectFullPath, _target, parentTargetName, _buildReason); WorkUnitResult bucketResult = null; targetSuccess = false; Lookup.Scope entryForInference = null; Lookup.Scope entryForExecution = null; try { // This isn't really dependency analysis. This is up-to-date checking. Based on this we will be able to determine if we should // run tasks in inference or execution mode (or both) or just skip them altogether. ItemDictionary <ProjectItemInstance> changedTargetInputs; ItemDictionary <ProjectItemInstance> upToDateTargetInputs; Lookup lookupForInference; Lookup lookupForExecution; // UNDONE: (Refactor) Refactor TargetUpToDateChecker to take a logging context, not a logging service. TargetUpToDateChecker dependencyAnalyzer = new TargetUpToDateChecker(requestEntry.RequestConfiguration.Project, _target, targetLoggingContext.LoggingService, targetLoggingContext.BuildEventContext); DependencyAnalysisResult dependencyResult = dependencyAnalyzer.PerformDependencyAnalysis(bucket, out changedTargetInputs, out upToDateTargetInputs); switch (dependencyResult) { // UNDONE: Need to enter/leave debugger scope properly for the <Target> element. case DependencyAnalysisResult.FullBuild: case DependencyAnalysisResult.IncrementalBuild: case DependencyAnalysisResult.SkipUpToDate: // Create the lookups used to hold the current set of properties and items lookupForInference = bucket.Lookup; lookupForExecution = bucket.Lookup.Clone(); // Push the lookup stack up one so that we are only modifying items and properties in that scope. entryForInference = lookupForInference.EnterScope("ExecuteTarget() Inference"); entryForExecution = lookupForExecution.EnterScope("ExecuteTarget() Execution"); // if we're doing an incremental build, we need to effectively run the task twice -- once // to infer the outputs for up-to-date input items, and once to actually execute the task; // as a result we need separate sets of item and property collections to track changes if (dependencyResult == DependencyAnalysisResult.IncrementalBuild) { // subset the relevant items to those that are up-to-date foreach (string itemType in upToDateTargetInputs.ItemTypes) { lookupForInference.PopulateWithItems(itemType, upToDateTargetInputs[itemType]); } // subset the relevant items to those that have changed foreach (string itemType in changedTargetInputs.ItemTypes) { lookupForExecution.PopulateWithItems(itemType, changedTargetInputs[itemType]); } } // We either have some work to do or at least we need to infer outputs from inputs. bucketResult = await ProcessBucket(taskBuilder, targetLoggingContext, GetTaskExecutionMode(dependencyResult), lookupForInference, lookupForExecution); // Now aggregate the result with the existing known results. There are four rules, assuming the target was not // skipped due to being up-to-date: // 1. If this bucket failed or was cancelled, the aggregate result is failure. // 2. If this bucket Succeeded and we have not previously failed, the aggregate result is a success. // 3. Otherwise, the bucket was skipped, which has no effect on the aggregate result. // 4. If the bucket's action code says to stop, then we stop, regardless of the success or failure state. if (dependencyResult != DependencyAnalysisResult.SkipUpToDate) { aggregateResult = aggregateResult.AggregateResult(bucketResult); } else { if (aggregateResult.ResultCode == WorkUnitResultCode.Skipped) { aggregateResult = aggregateResult.AggregateResult(new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)); } } // Pop the lookup scopes, causing them to collapse their values back down into the // bucket's lookup. // NOTE: this order is important because when we infer outputs, we are trying // to produce the same results as would be produced from a full build; as such // if we're doing both the infer and execute steps, we want the outputs from // the execute step to override the outputs of the infer step -- this models // the full build scenario more correctly than if the steps were reversed entryForInference.LeaveScope(); entryForInference = null; entryForExecution.LeaveScope(); entryForExecution = null; targetSuccess = (bucketResult != null) && (bucketResult.ResultCode == WorkUnitResultCode.Success); break; case DependencyAnalysisResult.SkipNoInputs: case DependencyAnalysisResult.SkipNoOutputs: // We have either no inputs or no outputs, so there is nothing to do. targetSuccess = true; break; } } catch (InvalidProjectFileException e) { // Make sure the Invalid Project error gets logged *before* TargetFinished. Otherwise, // the log is confusing. targetLoggingContext.LogInvalidProjectFileError(e); if (null != entryForInference) { entryForInference.LeaveScope(); } if (null != entryForExecution) { entryForExecution.LeaveScope(); } aggregateResult = aggregateResult.AggregateResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null)); } finally { // Don't log the last target finished event until we can process the target outputs as we want to attach them to the // last target batch. if (targetLoggingContext != null && i < numberOfBuckets - 1) { targetLoggingContext.LogTargetBatchFinished(projectFullPath, targetSuccess, null); targetLoggingContext = null; } } } // Produce the final results. List <TaskItem> targetOutputItems = new List <TaskItem>(); try { // If any legacy CallTarget operations took place, integrate them back in to the main lookup now. LeaveLegacyCallTargetScopes(); // Publish the items for each bucket back into the baseLookup. Note that EnterScope() was actually called on each // bucket inside of the ItemBucket constructor, which is why you don't see it anywhere around here. foreach (ItemBucket bucket in buckets) { bucket.LeaveScope(); } string targetReturns = _target.Returns; ElementLocation targetReturnsLocation = _target.ReturnsLocation; // If there are no targets in the project file that use the "Returns" attribute, that means that we // revert to the legacy behavior in the case where Returns is not specified (null, rather // than the empty string, which indicates no returns). Legacy behavior is for all // of the target's Outputs to be returned. // On the other hand, if there is at least one target in the file that uses the Returns attribute, // then all targets in the file are run according to the new behaviour (return nothing unless otherwise // specified by the Returns attribute). if (targetReturns == null) { if (!_target.ParentProjectSupportsReturnsAttribute) { targetReturns = _target.Outputs; targetReturnsLocation = _target.OutputsLocation; } } if (!String.IsNullOrEmpty(targetReturns)) { // Determine if we should keep duplicates. bool keepDupes = ConditionEvaluator.EvaluateCondition ( _target.KeepDuplicateOutputs, ParserOptions.AllowPropertiesAndItemLists, _expander, ExpanderOptions.ExpandPropertiesAndItems, requestEntry.ProjectRootDirectory, _target.KeepDuplicateOutputsLocation, projectLoggingContext.LoggingService, projectLoggingContext.BuildEventContext, FileSystems.Default); // NOTE: we need to gather the outputs in batches, because the output specification may reference item metadata // Also, we are using the baseLookup, which has possibly had changes made to it since the project started. Because of this, the // set of outputs calculated here may differ from those which would have been calculated at the beginning of the target. It is // assumed the user intended this. List <ItemBucket> batchingBuckets = BatchingEngine.PrepareBatchingBuckets(GetBatchableParametersForTarget(), _baseLookup, _target.Location); if (keepDupes) { foreach (ItemBucket bucket in batchingBuckets) { targetOutputItems.AddRange(bucket.Expander.ExpandIntoTaskItemsLeaveEscaped(targetReturns, ExpanderOptions.ExpandAll, targetReturnsLocation)); } } else { HashSet <TaskItem> addedItems = new HashSet <TaskItem>(); foreach (ItemBucket bucket in batchingBuckets) { IList <TaskItem> itemsToAdd = bucket.Expander.ExpandIntoTaskItemsLeaveEscaped(targetReturns, ExpanderOptions.ExpandAll, targetReturnsLocation); foreach (TaskItem item in itemsToAdd) { if (!addedItems.Contains(item)) { targetOutputItems.Add(item); addedItems.Add(item); } } } } } } finally { if (targetLoggingContext != null) { // log the last target finished since we now have the target outputs. targetLoggingContext.LogTargetBatchFinished(projectFullPath, targetSuccess, targetOutputItems != null && targetOutputItems.Count > 0 ? targetOutputItems : null); } } _targetResult = new TargetResult(targetOutputItems.ToArray(), aggregateResult); if (aggregateResult.ResultCode == WorkUnitResultCode.Failed && aggregateResult.ActionCode == WorkUnitActionCode.Stop) { _state = TargetEntryState.ErrorExecution; } else { _state = TargetEntryState.Completed; } } finally { _isExecuting = false; } #if MSBUILDENABLEVSPROFILING } finally { string endTargetBuild = String.Format(CultureInfo.CurrentCulture, "Build Target {0} in Project {1} - End", this.Name, projectFullPath); DataCollection.CommentMarkProfile(8801, endTargetBuild); } #endif }
/// <summary> /// Is the given task name able to be created by the task factory. In the case of an assembly task factory /// this question is answered by checking the assembly wrapped by the task factory to see if it exists. /// </summary> internal bool TaskNameCreatableByFactory(string taskName, IDictionary <string, string> taskIdentityParameters, string taskProjectFile, TargetLoggingContext targetLoggingContext, ElementLocation elementLocation) { if (!TaskIdentityParametersMatchFactory(_factoryIdentityParameters, taskIdentityParameters)) { return(false); } // Parameters match, so now we check to see if the task exists. LoadedType taskClass = null; try { ErrorUtilities.VerifyThrowArgumentLength(taskName, "TaskName"); taskClass = _typeLoader.ReflectionOnlyLoad(taskName, _loadedType.Assembly); if (taskClass != null) { return(true); } else { return(false); } } catch (TargetInvocationException e) { // Exception thrown by the called code itself // Log the stack, so the task vendor can fix their code ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, Environment.NewLine + e.InnerException.ToString()); } catch (ReflectionTypeLoadException e) { // ReflectionTypeLoadException.LoaderExceptions may contain nulls foreach (Exception exception in e.LoaderExceptions) { if (exception != null) { targetLoggingContext.LogError(new BuildEventFileInfo(taskProjectFile), "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, exception.Message); } } ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message); } catch (ArgumentNullException e) { // taskName may be null ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message); } catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. { if (ExceptionHandling.NotExpectedReflectionException(e)) { throw; } ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, _loadedType.Assembly.AssemblyLocation, e.Message); } return(false); }
/// <summary> /// Initialize the factory from the task registry /// </summary> internal LoadedType InitializeFactory ( AssemblyLoadInfo loadInfo, string taskName, IDictionary <string, TaskPropertyInfo> taskParameters, string taskElementContents, IDictionary <string, string> taskFactoryIdentityParameters, bool taskHostFactoryExplicitlyRequested, TargetLoggingContext targetLoggingContext, ElementLocation elementLocation, string taskProjectFile ) { ErrorUtilities.VerifyThrowArgumentNull(loadInfo, "loadInfo"); VerifyThrowIdentityParametersValid(taskFactoryIdentityParameters, elementLocation, taskName, "Runtime", "Architecture"); if (taskFactoryIdentityParameters != null) { _factoryIdentityParameters = new Dictionary <string, string>(taskFactoryIdentityParameters, StringComparer.OrdinalIgnoreCase); } _taskHostFactoryExplicitlyRequested = taskHostFactoryExplicitlyRequested; try { ErrorUtilities.VerifyThrowArgumentLength(taskName, "taskName"); _taskName = taskName; _loadedType = _typeLoader.Load(taskName, loadInfo); ProjectErrorUtilities.VerifyThrowInvalidProject(_loadedType != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty); } catch (TargetInvocationException e) { // Exception thrown by the called code itself // Log the stack, so the task vendor can fix their code ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, Environment.NewLine + e.InnerException.ToString()); } catch (ReflectionTypeLoadException e) { // ReflectionTypeLoadException.LoaderExceptions may contain nulls foreach (Exception exception in e.LoaderExceptions) { if (exception != null) { targetLoggingContext.LogError(new BuildEventFileInfo(taskProjectFile), "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, exception.Message); } } ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } catch (ArgumentNullException e) { // taskName may be null ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. { if (ExceptionHandling.NotExpectedReflectionException(e)) { throw; } ProjectErrorUtilities.VerifyThrowInvalidProject(false, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } return(_loadedType); }
/// <summary> /// Instantiates an ItemGroup task /// </summary> /// <param name="taskInstance">The original task instance data</param> /// <param name="loggingContext">The logging context</param> /// <param name="projectInstance">The project instance</param> /// <param name="logTaskInputs">Flag to determine whether or not to log task inputs.</param> public ItemGroupIntrinsicTask(ProjectItemGroupTaskInstance taskInstance, TargetLoggingContext loggingContext, ProjectInstance projectInstance, bool logTaskInputs) : base(loggingContext, projectInstance, logTaskInputs) { _taskInstance = taskInstance; _engineFileUtilities = EngineFileUtilities.Default; }