File: AssemblyDependency\Node\RarNodeExecuteResponse.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// 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 Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using ParameterType = Microsoft.Build.Tasks.AssemblyDependency.RarTaskParameters.ParameterType;
 
namespace Microsoft.Build.Tasks.AssemblyDependency
{
    /// <summary>
    /// Extracts and hydrates task outputs for ResolveAssemblyReference.
    /// </summary>
    internal sealed class RarNodeExecuteResponse : INodePacket
    {
        private Dictionary<string, TaskParameter> _taskOutputs = new(StringComparer.Ordinal);
        private int _numCopyLocalFiles;
        private bool _success;
 
        internal RarNodeExecuteResponse(ResolveAssemblyReference rar, bool success)
        {
            _taskOutputs = RarTaskParameters.Get(ParameterType.Output, rar);
            _numCopyLocalFiles = rar.CopyLocalFiles.Length;
            _success = success;
        }
 
        internal RarNodeExecuteResponse(ITranslator translator) => Translate(translator);
 
        public NodePacketType Type => NodePacketType.RarNodeExecuteResponse;
 
        internal bool Success => _success;
 
        public void Translate(ITranslator translator)
        {
            // TODO: The main outputs (e.g. ResolvedFiles, ResolvedDependencyFiles) will go through a different TaskItem
            // serialization path. Sequential items in each output share the same set of metadata keys and similar values,
            // so we can save a ton of overhead (and precompute the CopyLocal check) by breaking out of TaskParameter.
            translator.TranslateDictionary(ref _taskOutputs, StringComparer.Ordinal, TaskParameter.FactoryForDeserialization);
            translator.Translate(ref _numCopyLocalFiles);
            translator.Translate(ref _success);
        }
 
        internal void SetTaskOutputs(ResolveAssemblyReference rar)
        {
            RarTaskParameters.Set(ParameterType.Output, rar, _taskOutputs);
 
            if (_numCopyLocalFiles == 0)
            {
                return;
            }
 
            // CopyLocalFiles consists of a list of references. Although the reference equality itself doesn't matter since
            // the engine will later create copies, we can skip serializing these and reconstruct them on the client.
            int i = 0;
            ITaskItem[] copyLocalFiles = new ITaskItem[_numCopyLocalFiles];
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.ResolvedFiles);
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.ResolvedDependencyFiles);
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.RelatedFiles);
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.SatelliteFiles);
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.SerializationAssemblyFiles);
            FindCopyLocalFiles(copyLocalFiles, ref i, rar.ScatterFiles);
 
            static void FindCopyLocalFiles(ITaskItem[] copyLocalFiles, ref int i, ITaskItem[] items)
            {
                foreach (ITaskItem taskItem in items)
                {
                    // Prefer ITaskItem2 as it skips escaping checks. This should always be true coming from RAR.
                    string isCopyLocal = taskItem is ITaskItem2 taskItem2
                        ? taskItem2.GetMetadataValueEscaped(ItemMetadataNames.copyLocal)
                        : taskItem.GetMetadata(ItemMetadataNames.copyLocal);
 
                    if (string.Equals(isCopyLocal, "true", StringComparison.OrdinalIgnoreCase))
                    {
                        copyLocalFiles[i] = taskItem;
                        i++;
                    }
                }
            }
 
            rar.CopyLocalFiles = copyLocalFiles;
        }
    }
}