示例#1
0
        /// <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];

                    if (DebuggerManager.DebuggingEnabled)
                    {
                        DebuggerManager.EnterState(targetChildInstance.Location, lookupForExecution.GlobalsForDebugging /* does not matter which lookup we get this from */);
                    }

                    // Execute the task.
                    lastResult = await taskBuilder.ExecuteTask(targetLoggingContext, _requestEntry, _targetBuilderCallback, targetChildInstance, mode, lookupForInference, lookupForExecution, _cancellationToken);

                    if (DebuggerManager.DebuggingEnabled)
                    {
                        DebuggerManager.LeaveState(targetChildInstance.Location);
                    }

                    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));
        }
示例#2
0
        /// <summary>
        /// Aggregates the specified result with this result and returns the aggregation.
        /// </summary>
        /// <remarks>
        /// The rules are:
        /// 1. Errors take precedence over success.
        /// 2. Success takes precedence over skipped.
        /// 3. Stop takes precedence over continue.
        /// 4. The first exception in the result wins.
        /// </remarks>
        internal WorkUnitResult AggregateResult(WorkUnitResult result)
        {
            WorkUnitResultCode aggregateResult    = _resultCode;
            WorkUnitActionCode aggregateAction    = _actionCode;
            Exception          aggregateException = _exception;

            if (result._resultCode == WorkUnitResultCode.Canceled || result.ResultCode == WorkUnitResultCode.Failed)
            {
                // Failed and canceled take priority
                aggregateResult = result._resultCode;
            }
            else if (result._resultCode == WorkUnitResultCode.Success && aggregateResult == WorkUnitResultCode.Skipped)
            {
                // Success only counts if we were previously in the skipped category.
                aggregateResult = result._resultCode;
            }

            if (result._actionCode == WorkUnitActionCode.Stop)
            {
                aggregateAction = result.ActionCode;
            }

            if (aggregateException == null)
            {
                aggregateException = result._exception;
            }

            return(new WorkUnitResult(aggregateResult, aggregateAction, aggregateException));
        }
示例#3
0
 /// <summary>
 /// Initializes the results with specified items and result.
 /// </summary>
 /// <param name="items">The items produced by the target.</param>
 /// <param name="result">The overall result for the target.</param>
 internal TargetResult(TaskItem[] items, WorkUnitResult result)
 {
     ErrorUtilities.VerifyThrowArgumentNull(items, "items");
     ErrorUtilities.VerifyThrowArgumentNull(result, "result");
     _itemsStore = new ItemsStore(items);
     _result = result;
 }
示例#4
0
        /// <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);
                    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
                                         );

                        // 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
        }
示例#5
0
 /// <summary>
 /// Creates a build result for a request
 /// </summary>
 private BuildResult CreateBuildResult(BuildRequest request, string target, WorkUnitResult workUnitResult)
 {
     BuildResult result = new BuildResult(request);
     result.AddResultsForTarget(target, new TargetResult(new TaskItem[] { }, workUnitResult));
     return result;
 }
示例#6
0
 /// <summary>
 /// Creates and caches a built result.
 /// </summary>
 private BuildResult CacheBuildResult(BuildRequest request, string target, WorkUnitResult workUnitResult)
 {
     BuildResult result = CreateBuildResult(request, target, workUnitResult);
     IResultsCache resultsCache = _host.GetComponent(BuildComponentType.ResultsCache) as IResultsCache;
     resultsCache.AddResult(result);
     return result;
 }
示例#7
0
 /// <summary>
 /// Constructor allows you to set all the data
 /// </summary>
 public TaskDefinition(string name, string condition, bool continueOnError, XmlDocument projectXmlDocument, WorkUnitResult expectedResult)
 {
     this.name = name;
     this.condition = condition;
     this.continueOnError = continueOnError;
     this.taskElement = projectXmlDocument.CreateElement(this.name, @"http://schemas.microsoft.com/developer/msbuild/2003");
     this.parentXmlDocument = projectXmlDocument;
     this.expectedResult = expectedResult;
     this.finalTaskParameters = new Dictionary<string, string>();
     this.taskExecuted = new AutoResetEvent(false);
     this.taskStarted = new AutoResetEvent(false);
     GenerateTaskElement();
 }
示例#8
0
 /// <summary>
 /// Basic constructor which takes the task name and the task expected status
 /// </summary>
 public TaskDefinition(string name, XmlDocument projectXmlDocument, WorkUnitResult expectedResult)
     : this(name, null, false, projectXmlDocument, expectedResult)
 {
 }
示例#9
0
        /// <summary>
        /// Aggregates the specified result with this result and returns the aggregation.
        /// </summary>
        /// <remarks>
        /// The rules are:
        /// 1. Errors take precedence over success.
        /// 2. Success takes precedence over skipped.
        /// 3. Stop takes precedence over continue.
        /// 4. The first exception in the result wins.
        /// </remarks>
        internal WorkUnitResult AggregateResult(WorkUnitResult result)
        {
            WorkUnitResultCode aggregateResult = _resultCode;
            WorkUnitActionCode aggregateAction = _actionCode;
            Exception aggregateException = _exception;

            if (result._resultCode == WorkUnitResultCode.Canceled || result.ResultCode == WorkUnitResultCode.Failed)
            {
                // Failed and canceled take priority
                aggregateResult = result._resultCode;
            }
            else if (result._resultCode == WorkUnitResultCode.Success && aggregateResult == WorkUnitResultCode.Skipped)
            {
                // Success only counts if we were previously in the skipped category.
                aggregateResult = result._resultCode;
            }

            if (result._actionCode == WorkUnitActionCode.Stop)
            {
                aggregateAction = result.ActionCode;
            }

            if (aggregateException == null)
            {
                aggregateException = result._exception;
            }

            return new WorkUnitResult(aggregateResult, aggregateAction, aggregateException);
        }
示例#10
0
        /// <summary>
        /// Execute a task object for a given bucket.
        /// </summary>
        /// <param name="taskExecutionHost">The host used to execute the task.</param>
        /// <param name="taskLoggingContext">The logging context.</param>
        /// <param name="taskHost">The task host for the task.</param>
        /// <param name="bucket">The batching bucket</param>
        /// <param name="howToExecuteTask">The task execution mode</param>
        /// <returns>The result of running the task.</returns>
        private async Task<WorkUnitResult> ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask)
        {
            UpdateContinueOnError(bucket, taskHost);

            bool taskResult = false;

            WorkUnitResultCode resultCode = WorkUnitResultCode.Success;
            WorkUnitActionCode actionCode = WorkUnitActionCode.Continue;

            if (!taskExecutionHost.SetTaskParameters(_taskNode.ParametersForBuild))
            {
                // The task cannot be initialized.
                ProjectErrorUtilities.VerifyThrowInvalidProject(false, _targetChildInstance.Location, "TaskParametersError", _taskNode.Name, String.Empty);
            }
            else
            {
                bool taskReturned = false;
                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))
                    {
                        MSBuild msbuildTask = host.TaskInstance as MSBuild;
                        ErrorUtilities.VerifyThrow(msbuildTask != null, "Unexpected MSBuild internal task.");
                        _targetBuilderCallback.EnterMSBuildCallbackState();

                        try
                        {
                            taskResult = await msbuildTask.ExecuteInternal();
                        }
                        finally
                        {
                            _targetBuilderCallback.ExitMSBuildCallbackState();
                        }
                    }
                    else if (taskType == typeof(CallTarget))
                    {
                        CallTarget callTargetTask = host.TaskInstance as CallTarget;
                        taskResult = await callTargetTask.ExecuteInternal();
                    }
                    else
                    {
                        using (FullTracking.Track(taskLoggingContext.TargetLoggingContext.Target.Name, _taskNode.Name, _buildRequestEntry.ProjectRootDirectory, _buildRequestEntry.RequestConfiguration.Project.PropertiesToBuildWith))
                        {
                            taskResult = taskExecutionHost.Execute();
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (ExceptionHandling.IsCriticalException(ex) || (Environment.GetEnvironmentVariable("MSBUILDDONOTCATCHTASKEXCEPTIONS") == "1"))
                    {
                        throw;
                    }

                    taskException = ex;
                }

                if (taskException == null)
                {
                    taskReturned = true;

                    // Set the property "MSBuildLastTaskResult" to reflect whether the task succeeded or not.
                    // The main use of this is if ContinueOnError is true -- so that the next task can consult the result.
                    // So we want it to be "false" even if ContinueOnError is true. 
                    // The constants "true" and "false" should NOT be localized. They become property values.
                    bucket.Lookup.SetProperty(ProjectPropertyInstance.Create(ReservedPropertyNames.lastTaskResult, taskResult ? "true" : "false", true/* may be reserved */, _buildRequestEntry.RequestConfiguration.Project.IsImmutable));
                }
                else
                {
                    Type type = taskException.GetType();

                    if (type == typeof(LoggerException))
                    {
                        // if a logger has failed, abort immediately
                        // Polite logger failure
                        _continueOnError = ContinueOnError.ErrorAndStop;

                        // Rethrow wrapped in order to avoid losing the callstack
                        throw new LoggerException(taskException.Message, taskException);
                    }
                    else if (type == typeof(InternalLoggerException))
                    {
                        // Logger threw arbitrary exception
                        _continueOnError = ContinueOnError.ErrorAndStop;
                        InternalLoggerException ex = taskException as InternalLoggerException;

                        // Rethrow wrapped in order to avoid losing the callstack
                        throw new InternalLoggerException(taskException.Message, taskException, ex.BuildEventArgs, ex.ErrorCode, ex.HelpKeyword, ex.InitializationException);
                    }
                    else if (type == typeof(ThreadAbortException))
                    {
                        Thread.ResetAbort();
                        _continueOnError = ContinueOnError.ErrorAndStop;

                        // Cannot rethrow wrapped as ThreadAbortException is sealed and has no appropriate constructor
                        // Stack will be lost
                        throw taskException;
                    }
                    else if (type == typeof(BuildAbortedException))
                    {
                        _continueOnError = ContinueOnError.ErrorAndStop;

                        // Rethrow wrapped in order to avoid losing the callstack
                        throw new BuildAbortedException(taskException.Message, ((BuildAbortedException)taskException));
                    }
                    else if (type == typeof(CircularDependencyException))
                    {
                        _continueOnError = ContinueOnError.ErrorAndStop;
                        ProjectErrorUtilities.ThrowInvalidProject(taskLoggingContext.Task.Location, "CircularDependency", taskLoggingContext.TargetLoggingContext.Target.Name);
                    }
                    else if (type == typeof(InvalidProjectFileException))
                    {
                        // Just in case this came out of a task, make sure it's not
                        // marked as having been logged.
                        InvalidProjectFileException ipex = (InvalidProjectFileException)taskException;
                        ipex.HasBeenLogged = false;

                        if (_continueOnError != ContinueOnError.ErrorAndStop)
                        {
                            taskLoggingContext.LogInvalidProjectFileError(ipex);
                            taskLoggingContext.LogComment(MessageImportance.Normal, "ErrorConvertedIntoWarning");
                        }
                        else
                        {
                            // Rethrow wrapped in order to avoid losing the callstack
                            throw new InvalidProjectFileException(ipex.Message, ipex);
                        }
                    }
                    else if (type == typeof(Exception) || type.IsSubclassOf(typeof(Exception)))
                    {
                        // Occasionally, when debugging a very uncommon task exception, it is useful to loop the build with 
                        // a debugger attached to break on 2nd chance exceptions.
                        // That requires that there needs to be a way to not catch here, by setting an environment variable.
                        if (ExceptionHandling.IsCriticalException(taskException) || (Environment.GetEnvironmentVariable("MSBUILDDONOTCATCHTASKEXCEPTIONS") == "1"))
                        {
                            // Wrapping in an Exception will unfortunately mean that this exception would fly through any IsCriticalException above.
                            // However, we should not have any, also we should not have stashed such an exception anyway.
                            throw new Exception(taskException.Message, taskException);
                        }

                        Exception exceptionToLog = taskException;

                        if (exceptionToLog is TargetInvocationException)
                        {
                            exceptionToLog = exceptionToLog.InnerException;
                        }

                        // handle any exception thrown by the task during execution
                        // NOTE: We catch ALL exceptions here, to attempt to completely isolate the Engine
                        // from failures in the task.
                        if (_continueOnError == ContinueOnError.WarnAndContinue)
                        {
                            taskLoggingContext.LogTaskWarningFromException
                            (
                                new BuildEventFileInfo(_targetChildInstance.Location),
                                exceptionToLog,
                                _taskNode.Name
                            );

                            // Log a message explaining why we converted the previous error into a warning.
                            taskLoggingContext.LogComment(MessageImportance.Normal, "ErrorConvertedIntoWarning");
                        }
                        else
                        {
                            taskLoggingContext.LogFatalTaskError
                            (
                                new BuildEventFileInfo(_targetChildInstance.Location),
                                exceptionToLog,
                                _taskNode.Name
                            );
                        }
                    }
                    else
                    {
                        ErrorUtilities.ThrowInternalErrorUnreachable();
                    }
                }

                // If the task returned attempt to gather its outputs.  If gathering outputs fails set the taskResults
                // to false
                if (taskReturned)
                {
                    taskResult = GatherTaskOutputs(taskExecutionHost, howToExecuteTask, bucket) && taskResult;
                }

                // If the taskResults are false look at ContinueOnError.  If ContinueOnError=false (default)
                // mark the taskExecutedSuccessfully=false.  Otherwise let the task succeed but log a normal
                // pri message that says this task is continuing because ContinueOnError=true
                resultCode = taskResult ? WorkUnitResultCode.Success : WorkUnitResultCode.Failed;
                actionCode = WorkUnitActionCode.Continue;
                if (resultCode == WorkUnitResultCode.Failed)
                {
                    if (_continueOnError == ContinueOnError.ErrorAndStop)
                    {
                        actionCode = WorkUnitActionCode.Stop;
                    }
                    else
                    {
                        // This is the ErrorAndContinue or WarnAndContinue case...
                        string settingString = "true";
                        if (_taskNode.ContinueOnErrorLocation != null)
                        {
                            settingString = bucket.Expander.ExpandIntoStringAndUnescape(_taskNode.ContinueOnError, ExpanderOptions.ExpandAll, _taskNode.ContinueOnErrorLocation); // expand embedded item vectors after expanding properties and item metadata
                        }

                        taskLoggingContext.LogComment
                        (
                            MessageImportance.Normal,
                            "TaskContinuedDueToContinueOnError",
                            "ContinueOnError",
                            _taskNode.Name,
                            settingString
                        );

                        actionCode = WorkUnitActionCode.Continue;
                    }
                }
            }

            WorkUnitResult result = new WorkUnitResult(resultCode, actionCode, null);

            return result;
        }
示例#11
0
        private WorkUnitResult ExecuteTaskInSTAThread(ItemBucket bucket, TaskLoggingContext taskLoggingContext, IDictionary<string, string> taskIdentityParameters, TaskHost taskHost, TaskExecutionMode howToExecuteTask)
        {
            WorkUnitResult taskResult = new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null);
            Thread staThread = null;
            Exception exceptionFromExecution = null;
            ManualResetEvent taskRunnerFinished = new ManualResetEvent(false);
            try
            {
                ThreadStart taskRunnerDelegate = delegate ()
                {
                    Lookup.Scope scope = bucket.Lookup.EnterScope("STA Thread for Task");
                    try
                    {
                        taskResult = InitializeAndExecuteTask(taskLoggingContext, bucket, taskIdentityParameters, taskHost, howToExecuteTask).Result;
                    }
                    catch (Exception e)
                    {
                        if (ExceptionHandling.IsCriticalException(e))
                        {
                            throw;
                        }

                        exceptionFromExecution = e;
                    }
                    finally
                    {
                        scope.LeaveScope();
                        taskRunnerFinished.Set();
                    }
                };

                staThread = new Thread(taskRunnerDelegate);
                staThread.SetApartmentState(ApartmentState.STA);
                staThread.Name = "MSBuild STA task runner thread";
                staThread.CurrentCulture = _componentHost.BuildParameters.Culture;
                staThread.CurrentUICulture = _componentHost.BuildParameters.UICulture;
                staThread.Start();

                // TODO: Why not just Join on the thread???
                taskRunnerFinished.WaitOne();
            }
            finally
            {
                taskRunnerFinished.Close();
                taskRunnerFinished = null;
            }

            if (exceptionFromExecution != null)
            {
                // Unfortunately this will reset the callstack
                throw exceptionFromExecution;
            }

            return taskResult;
        }
示例#12
0
        /// <summary>
        /// Execute a single bucket
        /// </summary>
        /// <returns>true if execution succeeded</returns>        
        private async Task<WorkUnitResult> ExecuteBucket(TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Dictionary<string, string> lookupHash)
        {
            // On Intrinsic tasks, we do not allow batchable params, therefore metadata is excluded.
            ParserOptions parserOptions = (_taskNode == null) ? ParserOptions.AllowPropertiesAndItemLists : ParserOptions.AllowAll;
            WorkUnitResult taskResult = new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null);

            bool condition = ConditionEvaluator.EvaluateCondition
                (
                _targetChildInstance.Condition,
                parserOptions,
                bucket.Expander,
                ExpanderOptions.ExpandAll,
                _buildRequestEntry.ProjectRootDirectory,
                _targetChildInstance.ConditionLocation,
                _targetLoggingContext.LoggingService,
                _targetLoggingContext.BuildEventContext
                );

            if (!condition)
            {
                LogSkippedTask(bucket, howToExecuteTask);
                taskResult = new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null);

                return taskResult;
            }

            // If this is an Intrinsic task, it gets handled in a special fashion.
            if (_taskNode == null)
            {
                ExecuteIntrinsicTask(bucket);
                taskResult = new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null);
            }
            else
            {
                if (_componentHost.BuildParameters.SaveOperatingEnvironment)
                {
                    // Change to the project root directory.
                    // If that directory does not exist, do nothing. (Do not check first as it is almost always there and it is slow)
                    // This is because if the project has not been saved, this directory may not exist, yet it is often useful to still be able to build the project. 
                    // No errors are masked by doing this: errors loading the project from disk are reported at load time, if necessary.
                    NativeMethodsShared.SetCurrentDirectory(_buildRequestEntry.ProjectRootDirectory);
                }

                if (howToExecuteTask == TaskExecutionMode.ExecuteTaskAndGatherOutputs)
                {
                    // 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<string, string> taskIdentityParameters = GatherTaskIdentityParameters(bucket.Expander);
                    TaskRequirements? requirements = _taskExecutionHost.FindTask(taskIdentityParameters);
                    if (requirements != null)
                    {
                        TaskLoggingContext taskLoggingContext = _targetLoggingContext.LogTaskBatchStarted(_projectFullPath, _targetChildInstance);
                        try
                        {
                            if (
                                ((requirements.Value & TaskRequirements.RequireSTAThread) == TaskRequirements.RequireSTAThread) &&
                                (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
                                )
                            {
                                taskResult = ExecuteTaskInSTAThread(bucket, taskLoggingContext, taskIdentityParameters, taskHost, howToExecuteTask);
                            }
                            else
                            {
                                taskResult = await InitializeAndExecuteTask(taskLoggingContext, bucket, taskIdentityParameters, taskHost, howToExecuteTask);
                            }

                            if (lookupHash != null)
                            {
                                List<string> overrideMessages = bucket.Lookup.GetPropertyOverrideMessages(lookupHash);
                                if (overrideMessages != null)
                                {
                                    foreach (string s in overrideMessages)
                                    {
                                        taskLoggingContext.LogCommentFromText(MessageImportance.Low, s);
                                    }
                                }
                            }
                        }
                        catch (InvalidProjectFileException e)
                        {
                            // Make sure the Invalid Project error gets logged *before* TaskFinished.  Otherwise,
                            // the log is confusing.
                            taskLoggingContext.LogInvalidProjectFileError(e);
                            _continueOnError = ContinueOnError.ErrorAndStop;
                        }
                        finally
                        {
                            // Flag the completion of the task.
                            taskLoggingContext.LogTaskBatchFinished(_projectFullPath, taskResult.ResultCode == WorkUnitResultCode.Success || taskResult.ResultCode == WorkUnitResultCode.Skipped);

                            if (taskResult.ResultCode == WorkUnitResultCode.Failed && _continueOnError == ContinueOnError.WarnAndContinue)
                            {
                                // We coerce the failing result to a successful result.
                                taskResult = new WorkUnitResult(WorkUnitResultCode.Success, taskResult.ActionCode, taskResult.Exception);
                            }
                        }
                    }
                }
                else
                {
                    ErrorUtilities.VerifyThrow(howToExecuteTask == TaskExecutionMode.InferOutputsOnly, "should be inferring");

                    ErrorUtilities.VerifyThrow
                        (
                        GatherTaskOutputs(null, howToExecuteTask, bucket),
                        "The method GatherTaskOutputs() should never fail when inferring task outputs."
                        );

                    if (lookupHash != null)
                    {
                        List<string> overrideMessages = bucket.Lookup.GetPropertyOverrideMessages(lookupHash);
                        if (overrideMessages != null)
                        {
                            foreach (string s in overrideMessages)
                            {
                                _targetLoggingContext.LogCommentFromText(MessageImportance.Low, s);
                            }
                        }
                    }

                    taskResult = new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null);
                }
            }

            return taskResult;
        }
示例#13
0
        /// <summary>
        /// Called to execute a task within a target. This method instantiates the task, sets its parameters, and executes it. 
        /// </summary>
        /// <returns>true, if successful</returns>
        private async Task<WorkUnitResult> ExecuteTask(TaskExecutionMode mode, Lookup lookup)
        {
            ErrorUtilities.VerifyThrowArgumentNull(lookup, "lookup");

            WorkUnitResult taskResult = new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null);
            TaskHost taskHost = null;

            List<ItemBucket> buckets = null;

            try
            {
                if (_taskNode != null)
                {
                    taskHost = new TaskHost(_componentHost, _buildRequestEntry, _targetChildInstance.Location, _targetBuilderCallback);
                    _taskExecutionHost.InitializeForTask(taskHost, _targetLoggingContext, _buildRequestEntry.RequestConfiguration.Project, _taskNode.Name, _taskNode.Location, _taskHostObject, _continueOnError != ContinueOnError.ErrorAndStop, taskHost.AppDomainSetup, taskHost.IsOutOfProc, _cancellationToken);
                }

                List<string> taskParameterValues = CreateListOfParameterValues();
                buckets = BatchingEngine.PrepareBatchingBuckets(taskParameterValues, lookup, _targetChildInstance.Location);

                Dictionary<string, string> lookupHash = null;

                // Only create a hash table if there are more than one bucket as this is the only time a property can be overridden
                if (buckets.Count > 1)
                {
                    lookupHash = new Dictionary<string, string>(MSBuildNameIgnoreCaseComparer.Default);
                }

                WorkUnitResult aggregateResult = new WorkUnitResult();

                // Loop through each of the batch buckets and execute them one at a time
                for (int i = 0; i < buckets.Count; i++)
                {
                    // Execute the batch bucket, pass in which bucket we are executing so that we know when to get a new taskId for the bucket.
                    taskResult = await ExecuteBucket(taskHost, (ItemBucket)buckets[i], mode, lookupHash);

                    aggregateResult = aggregateResult.AggregateResult(taskResult);

                    if (aggregateResult.ActionCode == WorkUnitActionCode.Stop)
                    {
                        break;
                    }
                }

                taskResult = aggregateResult;
            }
            finally
            {
                _taskExecutionHost.CleanupForTask();

                if (taskHost != null)
                {
                    taskHost.MarkAsInactive();
                }

                // Now all task batches are done, apply all item adds to the outer 
                // target batch; we do this even if the task wasn't found (in that case,
                // no items or properties will have been added to the scope)
                if (buckets != null)
                {
                    foreach (ItemBucket bucket in buckets)
                    {
                        bucket.LeaveScope();
                    }
                }
            }

            return taskResult;
        }
示例#14
0
        /// <summary>
        /// Builds the task specified by the XML.
        /// </summary>
        /// <param name="loggingContext">The logging context of the target</param>
        /// <param name="requestEntry">The build request entry being built</param>
        /// <param name="targetBuilderCallback">The target builder callback.</param>
        /// <param name="taskInstance">The task instance.</param>
        /// <param name="mode">The mode in which to execute tasks.</param>
        /// <param name="inferLookup">The lookup to be used for inference.</param>
        /// <param name="executeLookup">The lookup to be used during execution.</param>
        /// <returns>The result of running the task batch.</returns>
        /// <remarks>
        /// The ExecuteTask method takes a task as specified by XML and executes it.  This procedure is comprised 
        /// of the following steps:
        /// 1. Loading the Task from its containing assembly by looking it up in the task registry
        /// 2. Determining if the task is batched.  If it is, create the batches and execute each as if it were a non-batched task
        /// 3. If the task is not batched, execute it.
        /// 4. If the task was batched, hold on to its Lookup until all of the natches are done, then merge them.
        /// </remarks>
        public async Task<WorkUnitResult> ExecuteTask(TargetLoggingContext loggingContext, BuildRequestEntry requestEntry, ITargetBuilderCallback targetBuilderCallback, ProjectTargetInstanceChild taskInstance, TaskExecutionMode mode, Lookup inferLookup, Lookup executeLookup, CancellationToken cancellationToken)
        {
            ErrorUtilities.VerifyThrow(taskInstance != null, "Need to specify the task instance.");

            _buildRequestEntry = requestEntry;
            _targetBuilderCallback = targetBuilderCallback;
            _cancellationToken = cancellationToken;
            _targetChildInstance = taskInstance;

            // In the case of Intrinsic tasks, taskNode will end up null.  Currently this is how we distinguish
            // intrinsic from extrinsic tasks.
            _taskNode = taskInstance as ProjectTaskInstance;

            if (_taskNode != null && requestEntry.Request.HostServices != null)
            {
                _taskHostObject = requestEntry.Request.HostServices.GetHostObject(requestEntry.RequestConfiguration.Project.FullPath, loggingContext.Target.Name, _taskNode.Name);
            }

            _projectFullPath = requestEntry.RequestConfiguration.Project.FullPath;

            // this.handleId = handleId; No handles
            // this.parentModule = parentModule; No task execution module
            _continueOnError = ContinueOnError.ErrorAndStop;

            _targetLoggingContext = loggingContext;

            WorkUnitResult taskResult = new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null);
            if ((mode & TaskExecutionMode.InferOutputsOnly) == TaskExecutionMode.InferOutputsOnly)
            {
                taskResult = await ExecuteTask(TaskExecutionMode.InferOutputsOnly, inferLookup);
            }

            if ((mode & TaskExecutionMode.ExecuteTaskAndGatherOutputs) == TaskExecutionMode.ExecuteTaskAndGatherOutputs)
            {
                taskResult = await ExecuteTask(TaskExecutionMode.ExecuteTaskAndGatherOutputs, executeLookup);
            }

            return taskResult;
        }
示例#15
0
        /// <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);

            List <StaticTarget.Task> staticTasks = new List <StaticTarget.Task>();

            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, staticTasks);

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

            StaticTarget staticTarget = null;

            if (staticTasks.Count > 0)
            {
                staticTarget = new StaticTarget()
                {
                    Location = _target.Location, Tasks = staticTasks, Name = Name
                };
            }

            return(new WorkUnitResult(aggregatedTaskResult, finalActionCode, lastResult.Exception)
            {
                GeneratedStaticTarget = staticTarget,
            });
        }