| File: BackEnd\TaskHostTaskComplete.cs | Web Access |
| Project: 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.Collections.Generic; using System.Diagnostics; #if FEATURE_REPORTFILEACCESSES using Microsoft.Build.Experimental.FileAccess; #endif using Microsoft.Build.Shared; #nullable disable namespace Microsoft.Build.BackEnd; /// <summary> /// TaskHostTaskComplete contains all the information the owning worker node /// needs from the task host on completion of task execution. /// </summary> internal class TaskHostTaskComplete : INodePacket { #if FEATURE_REPORTFILEACCESSES private List<FileAccessData> _fileAccessData; #endif /// <summary> /// Result of the task's execution. /// </summary> private TaskCompleteType _taskResult; /// <summary> /// If the task threw an exception during its initialization or execution, /// save it here. /// </summary> private Exception _taskException; /// <summary> /// If there's an additional message that should be attached to the error /// logged beyond "task X failed unexpectedly", save it here. May be null. /// </summary> private string _taskExceptionMessage; /// <summary> /// If the message saved in taskExceptionMessage requires arguments, save /// them here. May be null. /// </summary> private string[] _taskExceptionMessageArgs; /// <summary> /// The set of parameters / values from the task after it finishes execution. /// </summary> private Dictionary<string, TaskParameter> _taskOutputParameters = null; /// <summary> /// The process environment at the end of task execution. /// </summary> private Dictionary<string, string> _buildProcessEnvironment = null; /// <summary> /// How <see cref="_buildProcessEnvironment"/> is represented on the wire. Defaults to /// <see cref="InvariantPayloadTransferMode.Full"/> so older (legacy) code paths keep their existing behavior. /// </summary> private InvariantPayloadTransferMode _environmentMode = InvariantPayloadTransferMode.Full; /// <summary> /// Initializes a new instance of the <see cref="TaskHostTaskComplete"/> class. /// </summary> /// <param name="result">The result of the task's execution.</param> /// <param name="buildProcessEnvironment">The build process environment as it was at the end of the task's execution.</param> public TaskHostTaskComplete(OutOfProcTaskHostTaskResult result, IDictionary<string, string> buildProcessEnvironment) { Assumed.NotNull(result); _taskResult = result.Result; _taskException = result.TaskException; _taskExceptionMessage = result.ExceptionMessage; _taskExceptionMessageArgs = result.ExceptionMessageArgs; if (result.FinalParameterValues != null) { _taskOutputParameters = new Dictionary<string, TaskParameter>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, object> parameter in result.FinalParameterValues) { _taskOutputParameters[parameter.Key] = parameter.Value as TaskParameter ?? new TaskParameter(parameter.Value); } } if (buildProcessEnvironment != null) { _buildProcessEnvironment = buildProcessEnvironment as Dictionary<string, string>; if (_buildProcessEnvironment == null) { _buildProcessEnvironment = new Dictionary<string, string>(buildProcessEnvironment); } } } #if FEATURE_REPORTFILEACCESSES /// <summary> /// Initializes a new instance of the <see cref="TaskHostTaskComplete"/> class with reported file accesses. /// </summary> /// <param name="result">The result of the task's execution.</param> /// <param name="fileAccessData">The file accesses reported by the task.</param> /// <param name="buildProcessEnvironment">The build process environment as it was at the end of the task's execution.</param> public TaskHostTaskComplete( OutOfProcTaskHostTaskResult result, List<FileAccessData> fileAccessData, IDictionary<string, string> buildProcessEnvironment) : this(result, buildProcessEnvironment) { _fileAccessData = fileAccessData; } #endif /// <summary> /// For deserialization. /// </summary> private TaskHostTaskComplete() { } /// <summary> /// Result of the task's execution. /// </summary> public TaskCompleteType TaskResult { [DebuggerStepThrough] get { return _taskResult; } } /// <summary> /// If the task threw an exception during its initialization or execution, /// save it here. /// </summary> public Exception TaskException { [DebuggerStepThrough] get { return _taskException; } } /// <summary> /// If there's an additional message that should be attached to the error /// logged beyond "task X failed unexpectedly", put it here. May be null. /// </summary> public string TaskExceptionMessage { [DebuggerStepThrough] get { return _taskExceptionMessage; } } /// <summary> /// If there are arguments that need to be formatted into the message being /// sent, set them here. May be null. /// </summary> public string[] TaskExceptionMessageArgs { [DebuggerStepThrough] get { return _taskExceptionMessageArgs; } } /// <summary> /// Task parameters and their values after the task has finished. /// </summary> public Dictionary<string, TaskParameter> TaskOutputParameters { [DebuggerStepThrough] get { if (_taskOutputParameters == null) { _taskOutputParameters = new Dictionary<string, TaskParameter>(StringComparer.OrdinalIgnoreCase); } return _taskOutputParameters; } } /// <summary> /// The process environment. /// </summary> public Dictionary<string, string> BuildProcessEnvironment { [DebuggerStepThrough] get { return _buildProcessEnvironment; } } /// <summary> /// How the build process environment is transferred on the wire. See <see cref="InvariantPayloadTransferMode"/>. /// Set by the task host when the negotiated packet version supports environment delta transfer and the task /// did not change the environment. /// </summary> internal InvariantPayloadTransferMode EnvironmentMode { [DebuggerStepThrough] get { return _environmentMode; } [DebuggerStepThrough] set { _environmentMode = value; } } /// <summary> /// The type of this packet. /// </summary> public NodePacketType Type { get { return NodePacketType.TaskHostTaskComplete; } } #if FEATURE_REPORTFILEACCESSES /// <summary> /// Gets the file accesses reported by the task. /// </summary> public List<FileAccessData> FileAccessData { [DebuggerStepThrough] get => _fileAccessData; } #endif /// <summary> /// Translates the packet to/from binary form. /// </summary> /// <param name="translator">The translator to use.</param> public void Translate(ITranslator translator) { translator.TranslateEnum(ref _taskResult, (int)_taskResult); translator.TranslateException(ref _taskException); translator.Translate(ref _taskExceptionMessage); translator.Translate(ref _taskExceptionMessageArgs); translator.TranslateDictionary(ref _taskOutputParameters, StringComparer.OrdinalIgnoreCase, TaskParameter.FactoryForDeserialization); TranslateBuildProcessEnvironment(translator); #if FEATURE_REPORTFILEACCESSES translator.Translate(ref _fileAccessData, static (ITranslator translator, ref FileAccessData data) => { ITranslatable translatable = data; translatable.Translate(translator); // FileAccessData is a struct; preserve the changes made to the boxed value during deserialization. data = (FileAccessData)translatable; }); #else bool hasFileAccessData = false; translator.Translate(ref hasFileAccessData); #endif } private void TranslateBuildProcessEnvironment(ITranslator translator) { if (translator.NegotiatedPacketVersion >= NodePacketTypeExtensions.EnvironmentDeltaMinVersion) { translator.TranslateEnum(ref _environmentMode, (int)_environmentMode); if (_environmentMode == InvariantPayloadTransferMode.Full) { translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase); } } else { translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase); } } /// <summary> /// Factory for deserialization. /// </summary> internal static INodePacket FactoryForDeserialization(ITranslator translator) { TaskHostTaskComplete taskComplete = new TaskHostTaskComplete(); taskComplete.Translate(translator); return taskComplete; } }