File: Engine\TaskExecutionState.cs
Web Access
Project: ..\..\..\src\Deprecated\Engine\Microsoft.Build.Engine.csproj (Microsoft.Build.Engine)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
// THE ASSEMBLY BUILT FROM THIS SOURCE FILE HAS BEEN DEPRECATED FOR YEARS. IT IS BUILT ONLY TO PROVIDE
// BACKWARD COMPATIBILITY FOR API USERS WHO HAVE NOT YET MOVED TO UPDATED APIS. PLEASE DO NOT SEND PULL
// REQUESTS THAT CHANGE THIS FILE WITHOUT FIRST CHECKING WITH THE MAINTAINERS THAT THE FIX IS REQUIRED.
 
using System;
using System.IO;
using System.Text;
using System.Xml;
 
using Microsoft.Build.Framework;
using Microsoft.Build.BuildEngine.Shared;
 
namespace Microsoft.Build.BuildEngine
{
    /// <summary>
    /// This class is a wrapper used to contain the data needed to execute a task. This class
    /// is initially instantiated on the engine side by the scheduler and submitted to the node.
    /// The node completes the class instantiating by providing the object with node side data.
    /// This class is distinct from the task engine in that it (possibly) travels cross process
    /// between the engine and the node carrying with it the data needed to instantiate the task
    /// engine. The task engine can't subsume this class because the task engine is bound to the
    /// node process and can't travel cross process.
    /// </summary>
    internal class TaskExecutionState
    {
        #region Constructors
        /// <summary>
        /// The constructor obtains the state information and the
        /// callback delegate.
        /// </summary>
        internal TaskExecutionState
        (
            TaskExecutionMode howToExecuteTask,
            Lookup lookupForInference,
            Lookup lookupForExecution,
            XmlElement taskXmlNode,
            ITaskHost hostObject,
            string projectFileOfTaskNode,
            string parentProjectFullFileName,
            string executionDirectory,
            int handleId,
            BuildEventContext buildEventContext
        )
        {
            ErrorUtilities.VerifyThrow(taskXmlNode != null, "Must have task node");
 
            this.howToExecuteTask = howToExecuteTask;
            this.lookupForInference = lookupForInference;
            this.lookupForExecution = lookupForExecution;
            this.hostObject = hostObject;
            this.projectFileOfTaskNode = projectFileOfTaskNode;
            this.parentProjectFullFileName = parentProjectFullFileName;
            this.executionDirectory = executionDirectory;
            this.handleId = handleId;
            this.buildEventContext = buildEventContext;
            this.taskXmlNode = taskXmlNode;
        }
        #endregion
 
        #region Properties
 
        internal int HandleId
        {
            get
            {
                return this.handleId;
            }
            set
            {
                this.handleId = value;
            }
        }
 
        internal EngineLoggingServices LoggingService
        {
            get
            {
                return this.loggingService;
            }
            set
            {
                this.loggingService = value;
            }
        }
 
        internal TaskExecutionModule ParentModule
        {
            get
            {
                return this.parentModule;
            }
            set
            {
                this.parentModule = value;
            }
        }
 
        internal string ExecutionDirectory
        {
            get
            {
                return this.executionDirectory;
            }
        }
 
        internal bool ProfileExecution
        {
            get
            {
                return this.profileExecution;
            }
            set
            {
                this.profileExecution = value;
            }
        }
 
        #endregion
 
        #region Methods
        /// <summary>
        /// The thread procedure executes the tasks and calls callback once it is done
        /// </summary>
        internal virtual void ExecuteTask()
        {
            bool taskExecutedSuccessfully = true;
 
            Exception thrownException = null;
            bool dontPostOutputs = false;
 
            if (profileExecution)
            {
                startTime = DateTime.Now.Ticks;
            }
 
            try
            {
                TaskEngine taskEngine = new TaskEngine(
                            taskXmlNode,
                            hostObject,
                            projectFileOfTaskNode,
                            parentProjectFullFileName,
                            loggingService,
                            handleId,
                            parentModule,
                            buildEventContext);
 
                // Set the directory to the one appropriate for the task
                if (FileUtilities.GetCurrentDirectoryStaticBuffer(currentDirectoryBuffer) != executionDirectory)
                {
                    Directory.SetCurrentDirectory(executionDirectory);
                }
                // if we're skipping task execution because the target is up-to-date, we
                // need to go ahead and infer all the outputs that would have been emitted;
                // alternatively, if we're doing an incremental build, we need to infer the
                // outputs that would have been produced if all the up-to-date items had
                // been built by the task
                if ((howToExecuteTask & TaskExecutionMode.InferOutputsOnly) != TaskExecutionMode.Invalid)
                {
                    bool targetInferenceSuccessful = TaskEngineExecuteTask
                        (taskEngine,
                         TaskExecutionMode.InferOutputsOnly,
                         lookupForInference);
 
                    ErrorUtilities.VerifyThrow(targetInferenceSuccessful, "A task engine should never fail to infer its task's up-to-date outputs.");
                }
 
                // execute the task using the items that need to be (re)built
                if ((howToExecuteTask & TaskExecutionMode.ExecuteTaskAndGatherOutputs) != TaskExecutionMode.Invalid)
                {
                    taskExecutedSuccessfully =
                      TaskEngineExecuteTask
                        (taskEngine,
                            TaskExecutionMode.ExecuteTaskAndGatherOutputs,
                            lookupForExecution
                        );
                }
            }
            // We want to catch all exceptions and pass them on to the engine
            catch (Exception e)
            {
                thrownException = e;
                taskExecutedSuccessfully = false;
 
                // In single threaded mode the exception can be thrown on the current thread
                if (parentModule.RethrowTaskExceptions())
                {
                    dontPostOutputs = true;
                    throw;
                }
            }
            finally
            {
                if (!dontPostOutputs)
                {
                    long executionTime = profileExecution ? DateTime.Now.Ticks - startTime : 0;
                    // Post the outputs to the engine
                    parentModule.PostTaskOutputs(handleId, taskExecutedSuccessfully, thrownException, executionTime);
                }
            }
        }
 
        /// <summary>
        /// This method is called to adjust the execution time for the task by subtracting the time
        /// spent waiting for results
        /// </summary>
        /// <param name="entryTime"></param>
        internal void NotifyOfWait(long waitStartTime)
        {
            // Move the start time forward by the period of the wait
            startTime += (DateTime.Now.Ticks - waitStartTime);
        }
 
        #region MethodsNeededForUnitTesting
        /// <summary>
        /// Since we could not derrive from TaskEngine and have no Interface, we need to overide the method in here and
        /// replace the calls when testing the class because of the calls to TaskEngine. If at a future time we get a mock task
        /// engine, Interface or a non sealed TaskEngine these methods can disappear.
        /// </summary>
        /// <returns></returns>
        internal virtual bool TaskEngineExecuteTask(
            TaskEngine taskEngine,
            TaskExecutionMode howTaskShouldBeExecuted,
            Lookup lookup
        )
        {
            return taskEngine.ExecuteTask
                 (
                     howTaskShouldBeExecuted,
                     lookup
                 );
        }
        #endregion
 
        #endregion
 
        #region Fields set by the Engine thread
 
        private TaskExecutionMode howToExecuteTask;
 
        private Lookup lookupForInference;
        private Lookup lookupForExecution;
 
        private ITaskHost hostObject;
        private string projectFileOfTaskNode;
        private string parentProjectFullFileName;
        private string executionDirectory;
        private int handleId;
        private BuildEventContext buildEventContext;
 
        #endregion
 
        #region Fields set by the Task thread
 
        private TaskExecutionModule parentModule;
        private EngineLoggingServices loggingService;
        private XmlElement taskXmlNode;
        private long startTime;
        private bool profileExecution;
        private static StringBuilder currentDirectoryBuffer = new StringBuilder(270);
 
        #endregion
    }
}