| File: BackEnd\NodePacketTypeExtensions.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// 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.IO; using Microsoft.Build.Internal; namespace Microsoft.Build.BackEnd; /// <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. /// /// null: CLR2 (NET35) task host. Version-dependent fields skipped (not compiled in NET35). /// 0: The constant value for Framework-to-Framework (CLR4) task host. Supports HostServices, TargetName, ProjectFile. /// 1: .NET task host support. /// 2: Added support for translating/reading HostServices, ProjectFile, TargetName in TaskHostConfiguration. /// 3: Added App Host support. /// 4: Added IsRunningMultipleNodes, Request/ReleaseCores, BuildProjectFile callbacks support for OOP TaskHost. /// 5: Added delta transfer for the invariant payloads in TaskHostConfiguration / TaskHostTaskComplete: /// the build process environment and the CurrentSolutionConfigurationContents solution-level configuration /// blob are each sent once per connection, then only an "unchanged" marker per task. /// /// When incrementing this version, ensure compatibility with existing /// task hosts and update the corresponding deserialization logic. /// </summary> public const byte PacketVersion = 5; /// <summary> /// The minimum negotiated packet version that supports delta transfer of the invariant /// <see cref="NodePacketType.TaskHostConfiguration"/> / <see cref="NodePacketType.TaskHostTaskComplete"/> /// payloads (the build process environment and the CurrentSolutionConfigurationContents blob). /// </summary> public const byte EnvironmentDeltaMinVersion = 5; // 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, HandshakeOptions.TaskHost) && Handshake.IsHandshakeOptionEnabled(handshakeOptions, HandshakeOptions.NET)) { 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); /// <summary> /// Negotiates the packet version to use for communication between nodes. /// Returns the lower of the two versions to ensure compatibility between /// nodes that may be running different versions of MSBuild. /// /// This allows forward and backward compatibility when nodes with different /// packet versions communicate - they will use the lowest common version /// that both understand. /// </summary> /// <param name="otherPacketVersion">The packet version supported by the other node.</param> /// <returns>The negotiated protocol version that both nodes can use (the minimum of the two versions).</returns> public static byte GetNegotiatedPacketVersion(byte otherPacketVersion) => Math.Min(PacketVersion, otherPacketVersion); }