File: OutOfProcTaskHost.cs
Web Access
Project: ..\..\..\src\MSBuildTaskHost\MSBuildTaskHost.csproj (MSBuildTaskHost)
// 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.Diagnostics;
using Microsoft.Build.TaskHost.BackEnd;
using Microsoft.Build.TaskHost.Utilities;
 
namespace Microsoft.Build.TaskHost;
 
/// <summary>
/// This is the Out-Of-Proc Task Host for supporting Cross-Targeting tasks.
/// </summary>
/// <remarks>
/// It will be responsible for:
/// - Task execution
/// - Communicating with the MSBuildApp process, specifically the TaskHostFactory
///   (Logging messages, receiving Tasks from TaskHostFactory, sending results and other messages).
/// </remarks>
public static class OutOfProcTaskHost
{
    /// <summary>
    /// Enumeration of the various ways in which the MSBuildTaskHost.exe application can exit.
    /// </summary>
    internal enum ExitType
    {
        /// <summary>
        /// The application executed successfully.
        /// </summary>
        Success,
 
        /// <summary>
        /// We received a request from MSBuild.exe to terminate.
        /// </summary>
        TerminateRequest,
 
        /// <summary>
        /// A logger aborted the build.
        /// </summary>
        LoggerAbort,
 
        /// <summary>
        /// A logger failed unexpectedly.
        /// </summary>
        LoggerFailure,
 
        /// <summary>
        /// The Task Host Node did not terminate gracefully.
        /// </summary>
        TaskHostNodeFailed,
 
        /// <summary>
        /// An unexpected failure.
        /// </summary>
        Unexpected
    }
 
    /// <summary>
    /// Main Entry Point.
    /// </summary>
    /// <remarks>
    /// We won't execute any tasks in the main thread, so we don't need to be in an STA.
    /// </remarks>
    // UNDONE: Setting [MTAThread] is almost certainly incorrect for MSBuildTaskHost.
    // Prior to .NET Framework 4.0, all of MSBuild ran in an STA. However, the change
    // that makes MSBuildTaskHost run in an MTA was made over 10 years ago.
    [MTAThread]
    public static int Main()
        => Execute() == ExitType.Success ? 0 : 1;
 
    /// <summary>
    /// Orchestrates the execution of the application.
    /// Also responsible for top-level error handling.
    /// </summary>
    /// <returns>
    /// A value of Success if the bootstrapping succeeds.
    /// </returns>
    internal static ExitType Execute()
    {
        switch (Environment.GetEnvironmentVariable("MSBUILDDEBUGONSTART"))
        {
 
#if FEATURE_DEBUG_LAUNCH
            case "1":
                Debugger.Launch();
                break;
#endif
 
            case "2":
                // Sometimes easier to attach rather than deal with JIT prompt
                Console.WriteLine($"Waiting for debugger to attach ({EnvironmentUtilities.ProcessPath} PID {EnvironmentUtilities.CurrentProcessId}).  Press enter to continue...");
 
                Console.ReadLine();
                break;
 
            case "3":
                // Value "3" skips debugging for TaskHost processes but debugs the main MSBuild process
                // This is useful when you want to debug MSBuild but not the child TaskHost processes
                break;
        }
 
        bool restart = false;
        do
        {
            var oopTaskHostNode = new OutOfProcTaskHostNode();
            NodeEngineShutdownReason taskHostShutDownReason = oopTaskHostNode.Run(out Exception? taskHostShutDownException);
 
            if (taskHostShutDownException != null)
            {
                return ExitType.TaskHostNodeFailed;
            }
 
            switch (taskHostShutDownReason)
            {
                case NodeEngineShutdownReason.BuildComplete:
                    return ExitType.Success;
 
                case NodeEngineShutdownReason.BuildCompleteReuse:
                    restart = true;
                    break;
 
                default:
                    return ExitType.TaskHostNodeFailed;
            }
        }
        while (restart);
 
        // Should not happen
        ErrorUtilities.ThrowInternalErrorUnreachable();
        return ExitType.Unexpected;
    }
}