File: INodePacket.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using System.IO;
using Microsoft.Build.Internal;
 
namespace Microsoft.Build.BackEnd
{
    #region Enums
 
    /// <summary>
    /// Enumeration of all of the packet types used for communication.
    /// Uses lower 6 bits for packet type (0-63), upper 2 bits reserved for flags.
    /// </summary>
    internal enum NodePacketType : byte
    {
        // Mask for extracting packet type (lower 6 bits)
        TypeMask = 0x3F, // 00111111
 
        /// <summary>
        /// Notifies the Node to set a configuration for a particular build.  This is sent before
        /// any BuildRequests are made and will not be sent again for a particular build.  This instructs
        /// the node to prepare to receive build requests.
        ///
        /// Contains:
        /// Build ID
        /// Environment variables
        /// Logging Services Configuration
        /// Node ID
        /// Default Global Properties
        /// Toolset Definition Locations
        /// Startup Directory
        /// UI Culture Information
        /// App Domain Configuration XML
        /// </summary>
        NodeConfiguration = 0x00,
 
        /// <summary>
        /// A BuildRequestConfiguration object.
        /// When sent TO a node, this informs the node of a build configuration.
        /// When sent FROM a node, this requests a BuildRequestConfigurationResponse to map the configuration to the
        /// appropriate global configuration ID.
        ///
        /// Contents:
        /// Configuration ID
        /// Project Filename
        /// Project Properties
        /// Project Tools Version
        /// </summary>
        BuildRequestConfiguration, // 0x01
 
        /// <summary>
        /// A response to a request to map a build configuration
        ///
        /// Contents:
        /// Node Configuration ID
        /// Global Configuration ID
        /// </summary>
        BuildRequestConfigurationResponse, // 0x02
 
        /// <summary>
        /// Information about a project that has been loaded by a node.
        ///
        /// Contents:
        /// Global Configuration ID
        /// Initial Targets
        /// Default Targets
        /// </summary>
        ProjectLoadInfo, // 0x03
 
        /// <summary>
        /// Packet used to inform the scheduler that a node's active build request is blocked.
        ///
        /// Contents:
        /// Build Request ID
        /// Active Targets
        /// Blocked Target, if any
        /// Child Requests, if any
        /// </summary>
        BuildRequestBlocker, // 0x04
 
        /// <summary>
        /// Packet used to unblocked a blocked request on a node.
        ///
        /// Contents:
        /// Build Request ID
        /// Build Results for child requests, if any.
        /// </summary>
        BuildRequestUnblocker, // 0x05
 
        /// <summary>
        /// A BuildRequest object
        ///
        /// Contents:
        /// Build Request ID
        /// Configuration ID
        /// Project Instance ID
        /// Targets
        /// </summary>
        BuildRequest, // 0x06
 
        /// <summary>
        /// A BuildResult object
        ///
        /// Contents:
        /// Build ID
        /// Project Instance ID
        /// Targets
        /// Outputs (per Target)
        /// Results (per Target)
        /// </summary>
        BuildResult, // 0x07
 
        /// <summary>
        /// A logging message.
        ///
        /// Contents:
        /// Build Event Type
        /// Build Event Args
        /// </summary>
        LogMessage, // 0x08
 
        /// <summary>
        /// Informs the node that the build is complete.
        ///
        /// Contents:
        /// Prepare For Reuse
        /// </summary>
        NodeBuildComplete, // 0x09
 
        /// <summary>
        /// Reported by the node (or node provider) when a node has terminated.  This is the final packet that will be received
        /// from a node.
        ///
        /// Contents:
        /// Reason
        /// </summary>
        NodeShutdown, // 0x0A
 
        /// <summary>
        /// Notifies the task host to set the task-specific configuration for a particular task execution.
        /// This is sent in place of NodeConfiguration and gives the task host all the information it needs
        /// to set itself up and execute the task that matches this particular configuration.
        ///
        /// Contains:
        /// Node ID (of parent MSBuild node, to make the logging work out)
        /// Startup directory
        /// Environment variables
        /// UI Culture information
        /// App Domain Configuration XML
        /// Task name
        /// Task assembly location
        /// Parameter names and values to set to the task prior to execution
        /// </summary>
        TaskHostConfiguration, // 0x0B
 
        /// <summary>
        /// Informs the parent node that the task host has finished executing a
        /// particular task.  Does not need to contain identifying information
        /// about the task, because the task host will only ever be connected to
        /// one parent node at a a time, and will only ever be executing one task
        /// for that node at any one time.
        ///
        /// Contents:
        /// Task result (success / failure)
        /// Resultant parameter values (for output gathering)
        /// </summary>
        TaskHostTaskComplete, // 0x0C
 
        /// <summary>
        /// Message sent from the node to its paired task host when a task that
        /// supports ICancellableTask is cancelled.
        ///
        /// Contents:
        /// (nothing)
        /// </summary>
        TaskHostTaskCancelled, // 0x0D
 
        /// <summary>
        /// Message sent from a node when it needs to have an SDK resolved.
        /// </summary>
        ResolveSdkRequest, // 0x0E
 
        /// <summary>
        /// Message sent back to a node when an SDK has been resolved.
        /// </summary>
        ResolveSdkResponse, // 0x0F
 
        /// <summary>
        /// Message sent from a node when a task is requesting or returning resources from the scheduler.
        /// </summary>
        ResourceRequest, // 0x10
 
        /// <summary>
        /// Message sent back to a node informing it about the resource that were granted by the scheduler.
        /// </summary>
        ResourceResponse, // 0x11
 
        /// <summary>
        /// Message sent from a node reporting a file access.
        /// </summary>
        FileAccessReport, // 0x12
 
        /// <summary>
        /// Message sent from a node reporting process data.
        /// </summary>
        ProcessReport, // 0x13
 
 
        /// Notifies the RAR node to set a configuration for a particular build.
        RarNodeEndpointConfiguration,
 
        /// <summary>
        /// A request contains the inputs to the RAR task.
        /// </summary>
        RarNodeExecuteRequest, // 0x14
 
        /// <summary>
        /// A request contains the outputs and log events of a completed RAR task.
        /// </summary>
        RarNodeExecuteResponse, // 0x15
 
        // Reserve space for future core packet types (0x16-0x3B available for expansion)
 
        // Server command packets placed at end of safe range to maintain separation from core packets
        #region ServerNode enums 
 
        /// <summary>
        /// A batch of log events emitted while the RAR task is executing.
        /// </summary>
        RarNodeBufferedLogEvents,
 
        /// <summary>
        /// Command in form of MSBuild command line for server node - MSBuild Server.
        /// </summary>
        ServerNodeBuildCommand = 0x3C, // End of safe range
 
        /// <summary>
        /// Response from server node command.
        /// </summary>
        ServerNodeBuildResult = 0x3D,
 
        /// <summary>
        /// Info about server console activity.
        /// </summary>
        ServerNodeConsoleWrite = 0x3E,
 
        /// <summary>
        /// Command to cancel ongoing build.
        /// </summary>
        ServerNodeBuildCancel = 0x3F, // Last value in safe range (0x3F = 00111111)
 
        #endregion
    }
    #endregion
 
    /// <summary>
    /// This interface represents a packet which may be transmitted using an INodeEndpoint.
    /// Implementations define the serialized form of the data.
    /// </summary>
    internal interface INodePacket : ITranslatable
    {
        #region Properties
        /// <summary>
        /// The type of the packet.  Used to reconstitute the packet using the correct factory.
        /// </summary>
        NodePacketType Type
        {
            get;
        }
 
        #endregion
    }
 
    /// <summary>
    /// Provides utilities for handling node packet types and extended headers in MSBuild's distributed build system.
    /// 
    /// This class manages the communication protocol between build nodes, including:
    /// - Packet versioning for protocol compatibility
    /// - Extended header flags for enhanced packet metadata
    /// - Type extraction and manipulation for network communication
    /// 
    /// The packet format uses the upper 2 bits (6-7) for flags while preserving
    /// the lower 6 bits for the actual packet type enumeration.
    /// </summary>
    internal static class NodePacketTypeExtensions
    {
        /// <summary>
        /// Defines the communication protocol version for node communication.
        /// 
        /// Version 1: Introduced for the .NET Task Host protocol. This version
        /// excludes the translation of appDomainConfig within TaskHostConfiguration
        /// to maintain backward compatibility and reduce serialization overhead.
        /// 
        /// When incrementing this version, ensure compatibility with existing
        /// task hosts and update the corresponding deserialization logic.
        /// </summary>
        public const byte PacketVersion = 1;
 
        // Flag bits in upper 2 bits
        private const byte ExtendedHeaderFlag = 0x40;  // Bit 6: 01000000
 
        /// <summary>
        /// Determines if a packet has an extended header by checking if the extended header flag is set.
        /// Uses bit 6 which is now safely separated from packet type values.
        /// </summary>
        /// <param name="rawType">The raw packet type byte.</param>
        /// <returns>True if the packet has an extended header, false otherwise</returns>
        public static bool HasExtendedHeader(byte rawType) => (rawType & ExtendedHeaderFlag) != 0;
 
        /// <summary>
        /// Get base packet type, stripping all flag bits (bits 6 and 7).
        /// </summary>
        /// <param name="rawType">The raw packet type byte with potential flags.</param>
        /// <returns>The clean packet type without flag bits.</returns>
        public static NodePacketType GetNodePacketType(byte rawType) => (NodePacketType)(rawType & (byte)NodePacketType.TypeMask);
 
        /// <summary>
        /// Create a packet type byte with extended header flag for net task host packets.
        /// </summary>
        /// <param name="handshakeOptions">Handshake options to check.</param>
        /// <param name="type">Base packet type.</param>
        /// <param name="extendedheader">Output byte with flag set if applicable.</param>
        /// <returns>True if extended header flag was set, false otherwise.</returns>
        public static bool TryCreateExtendedHeaderType(HandshakeOptions handshakeOptions, NodePacketType type, out byte extendedheader)
        {
            if (Handshake.IsHandshakeOptionEnabled(handshakeOptions, Handshake.NetTaskHostFlags))
            {
                extendedheader = (byte)((byte)type | ExtendedHeaderFlag);
                return true;
            }
 
            extendedheader = (byte)type;
            return false;
        }
 
        /// <summary>
        /// Reads the protocol version from an extended header in the stream.
        /// This method expects the stream to be positioned at the version byte.
        /// </summary>
        /// <param name="stream">The stream to read the version byte from.</param>
        /// <returns>The protocol version byte read from the stream.</returns>
        /// <exception cref="EndOfStreamException">Thrown when the stream ends unexpectedly while reading the version.</exception>
        public static byte ReadVersion(Stream stream)
        {
            int value = stream.ReadByte();
            if (value == -1)
            {
                throw new EndOfStreamException("Unexpected end of stream while reading version");
            }
 
            return (byte)value;
        }
 
        /// <summary>
        /// Writes the protocol version byte to the extended header in the stream.
        /// This is typically called after writing a packet type with the extended header flag.
        /// </summary>
        /// <param name="stream">The stream to write the version byte to.</param>
        /// <param name="version">The protocol version to write to the stream.</param>
        public static void WriteVersion(Stream stream, byte version) => stream.WriteByte(version);
    }
}