File: DependencyContextBuilder.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.NET.Build.Tasks\Microsoft.NET.Build.Tasks.csproj (Microsoft.NET.Build.Tasks)
// 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.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Extensions.DependencyModel;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
using NuGet.RuntimeModel;
using NuGet.Versioning;
 
namespace Microsoft.NET.Build.Tasks
{
    internal class DependencyContextBuilder
    {
        private readonly SingleProjectInfo _mainProjectInfo;
        private readonly bool _includeRuntimeFileVersions;
        private IEnumerable<ReferenceInfo> _referenceAssemblies;
        private IEnumerable<ReferenceInfo> _directReferences;
        private IEnumerable<ReferenceInfo> _dependencyReferences;
        private Dictionary<string, List<ReferenceInfo>> _compileReferences;
        private Dictionary<string, List<ResolvedFile>> _resolvedNuGetFiles;
        private Dictionary<string, SingleProjectInfo> _referenceProjectInfos;
        private Dictionary<string, List<RuntimePackAssetInfo>> _runtimePackAssets;
        private CompilationOptions _compilationOptions;
        private string _referenceAssembliesPath;
        private Dictionary<PackageIdentity, string> _filteredPackages;
        private bool _includeMainProjectInDepsFile = true;
        private bool _trimLibrariesWithoutAssets = true;
        private readonly Dictionary<string, DependencyLibrary> _dependencyLibraries;
        private readonly Dictionary<string, List<LibraryDependency>> _libraryDependencies;
        private readonly List<string> _mainProjectDependencies;
        private readonly HashSet<PackageIdentity> _packagesToBeFiltered;
        private readonly bool _isFrameworkDependent;
        private readonly string _platformLibrary;
        private readonly string _dotnetFrameworkName;
        private readonly string _runtimeIdentifier;
        private readonly bool _isPortable;
        private readonly HashSet<string> _usedLibraryNames;
        private readonly RuntimeGraph _runtimeGraph;
 
        private Dictionary<ReferenceInfo, string> _referenceLibraryNames;
 
        // This resolver is only used for building file names, so that base path is not required.
        private readonly VersionFolderPathResolver _versionFolderPathResolver = new(rootPath: null);
 
        private const string NetCorePlatformLibrary = "Microsoft.NETCore.App";
 
        public DependencyContextBuilder(SingleProjectInfo mainProjectInfo, bool includeRuntimeFileVersions, RuntimeGraph runtimeGraph, ProjectContext projectContext, LockFileLookup libraryLookup)
        {
            _mainProjectInfo = mainProjectInfo;
            _includeRuntimeFileVersions = includeRuntimeFileVersions;
            _runtimeGraph = runtimeGraph;
 
            _dependencyLibraries = projectContext.LockFileTarget.Libraries
                .Select(lockFileTargetLibrary =>
                {
                    var dependencyLibrary = new DependencyLibrary(lockFileTargetLibrary.Name, lockFileTargetLibrary.Version, lockFileTargetLibrary.Type);
 
                    LockFileLibrary library;
                    if (libraryLookup.TryGetLibrary(lockFileTargetLibrary, out library))
                    {
                        dependencyLibrary.Sha512 = library.Sha512;
                        dependencyLibrary.Path = library.Path;
                        dependencyLibrary.MSBuildProject = library.MSBuildProject;
                    }
 
                    return dependencyLibrary;
                }).ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase);
 
            _libraryDependencies = new Dictionary<string, List<LibraryDependency>>(StringComparer.OrdinalIgnoreCase);
            foreach (var library in projectContext.LockFileTarget.Libraries)
            {
                _libraryDependencies[library.Name] = library.Dependencies
                    .Select(d => new LibraryDependency()
                    {
                        Name = d.Id,
                        MinVersion = d.VersionRange.MinVersion
                    }).ToList();
            }
 
            _mainProjectDependencies = projectContext.GetTopLevelDependencies().ToList();
            _packagesToBeFiltered = projectContext.PackagesToBeFiltered;
 
            _isFrameworkDependent = projectContext.IsFrameworkDependent;
            _platformLibrary = projectContext.PlatformLibrary?.Name;
            _dotnetFrameworkName = projectContext.LockFileTarget.TargetFramework.DotNetFrameworkName;
            _runtimeIdentifier = projectContext.LockFileTarget.RuntimeIdentifier;
            _isPortable = projectContext.IsPortable;
 
            _usedLibraryNames = new HashSet<string>(_dependencyLibraries.Keys, StringComparer.OrdinalIgnoreCase);
        }
 
        public DependencyContextBuilder(
            SingleProjectInfo mainProjectInfo,
            bool includeRuntimeFileVersions,
            ITaskItem[] runtimeFrameworks,
            string runtimeIdentifier,
            bool isSelfContained,
            string platformLibraryName,
            string targetFramework)
        {
            _mainProjectInfo = mainProjectInfo;
            _includeRuntimeFileVersions = includeRuntimeFileVersions;
 
            _isFrameworkDependent = LockFileExtensions.IsFrameworkDependent(
                runtimeFrameworks,
                isSelfContained,
                runtimeIdentifier,
                string.IsNullOrWhiteSpace(platformLibraryName));
 
            _isPortable = _isFrameworkDependent && (string.IsNullOrEmpty(_runtimeIdentifier) || _runtimeIdentifier == "any");
 
            if (_isFrameworkDependent != true || _isPortable != true)
            {
                throw new ArgumentException(
                    $"{nameof(DependencyContextBuilder)} Does not support non FrameworkDependent without assetfile. " +
                    $"runtimeFrameworks: {string.Join(",", runtimeFrameworks.Select(r => r.ItemSpec))} " +
                    $"isSelfContained: {isSelfContained} " +
                    $"runtimeIdentifier: {runtimeIdentifier} " +
                    $"platformLibraryName: {platformLibraryName}");
            }
 
            _platformLibrary = platformLibraryName;
 
            //  NOTE: This uses the TargetFramework (ie "net5.0") as the deps.json runtimeTarget name, instead of
            //  the TargetFrameworkMoniker (ie ".NETCoreApp,Version=v5.0"), which is normally used.
            //
            //  This constructor should only be used for C++/CLI, and is used because PackageReference isn't
            //  currently supported in that context so there is no assets file to read from.
            //
            //  Using the TargetFramework instead should have minimal impact.
            _dotnetFrameworkName = targetFramework;
            _runtimeIdentifier = runtimeIdentifier;
 
            _dependencyLibraries = new Dictionary<string, DependencyLibrary>();
            _libraryDependencies = new Dictionary<string, List<LibraryDependency>>();
            _mainProjectDependencies = new List<string>();
            _packagesToBeFiltered = null;
 
            _usedLibraryNames = new HashSet<string>();
        }
 
        private bool IncludeCompilationLibraries => _compilationOptions != null;
 
        private Dictionary<ReferenceInfo, string> ReferenceLibraryNames
        {
            get
            {
                if (_referenceLibraryNames == null)
                {
                    _referenceLibraryNames = new Dictionary<ReferenceInfo, string>();
                }
 
                return _referenceLibraryNames;
            }
        }
 
        public DependencyContextBuilder WithReferenceAssemblies(IEnumerable<ReferenceInfo> referenceAssemblies)
        {
            // note: ReferenceAssembly libraries only export compile-time stuff
            // since they assume the runtime library is present already
            _referenceAssemblies = referenceAssemblies;
            return this;
        }
 
        public DependencyContextBuilder WithDirectReferences(IEnumerable<ReferenceInfo> directReferences)
        {
            _directReferences = directReferences;
            return this;
        }
 
        public DependencyContextBuilder WithDependencyReferences(IEnumerable<ReferenceInfo> dependencyReferences)
        {
            _dependencyReferences = dependencyReferences;
            return this;
        }
 
        public DependencyContextBuilder WithCompileReferences(IEnumerable<ReferenceInfo> compileReferences)
        {
            _compileReferences = new Dictionary<string, List<ReferenceInfo>>(StringComparer.OrdinalIgnoreCase);
            foreach (var group in compileReferences.GroupBy(r => r.PackageName, StringComparer.OrdinalIgnoreCase))
            {
                _compileReferences.Add(group.Key, group.ToList());
            }
 
            return this;
        }
 
        public DependencyContextBuilder WithResolvedNuGetFiles(IEnumerable<ResolvedFile> resolvedNuGetFiles)
        {
            _resolvedNuGetFiles = new Dictionary<string, List<ResolvedFile>>(StringComparer.OrdinalIgnoreCase);
            foreach (var group in resolvedNuGetFiles.GroupBy(f => f.PackageName, StringComparer.OrdinalIgnoreCase))
            {
                _resolvedNuGetFiles.Add(group.Key, group.ToList());
            }
 
            return this;
        }
 
        public DependencyContextBuilder WithReferenceProjectInfos(Dictionary<string, SingleProjectInfo> referenceProjectInfos)
        {
            _referenceProjectInfos = referenceProjectInfos;
            return this;
        }
 
        public DependencyContextBuilder WithMainProjectInDepsFile(bool includeMainProjectInDepsFile)
        {
            _includeMainProjectInDepsFile = includeMainProjectInDepsFile;
            return this;
        }
 
        public DependencyContextBuilder WithTrimLibrariesWithoutAssets(bool trimLibrariesWithoutAssets)
        {
            _trimLibrariesWithoutAssets = trimLibrariesWithoutAssets;
            return this;
        }
 
        public DependencyContextBuilder WithRuntimePackAssets(IEnumerable<RuntimePackAssetInfo> runtimePackAssets)
        {
            _runtimePackAssets = new Dictionary<string, List<RuntimePackAssetInfo>>();
            foreach (var runtimePackGroup in runtimePackAssets.GroupBy(a => a.PackageName))
            {
                var dependencyLibrary = new DependencyLibrary("runtimepack." + runtimePackGroup.Key,
                    NuGetVersion.Parse(runtimePackGroup.First().PackageVersion),
                    "runtimepack");
 
                _dependencyLibraries.Add(dependencyLibrary.Name, dependencyLibrary);
 
                _runtimePackAssets[dependencyLibrary.Name] = runtimePackGroup.ToList();
            }
            return this;
        }
 
        public DependencyContextBuilder WithCompilationOptions(CompilationOptions compilationOptions)
        {
            _compilationOptions = compilationOptions;
            return this;
        }
 
        public DependencyContextBuilder WithReferenceAssembliesPath(string referenceAssembliesPath)
        {
            // if the path is empty, we want to use the original string instead of a single trailing character.
            if (string.IsNullOrEmpty(referenceAssembliesPath) ||
                referenceAssembliesPath[referenceAssembliesPath.Length - 1] == Path.DirectorySeparatorChar)
            {
                _referenceAssembliesPath = referenceAssembliesPath;
            }
            else
            {
                _referenceAssembliesPath = referenceAssembliesPath + Path.DirectorySeparatorChar;
            }
 
            return this;
        }
 
        public DependencyContextBuilder WithPackagesThatWereFiltered(Dictionary<PackageIdentity, string> packagesThatWhereFiltered)
        {
            _filteredPackages = packagesThatWhereFiltered;
            return this;
        }
 
        public DependencyContext Build(string[] userRuntimeAssemblies = null)
        {
            CalculateExcludedLibraries();
 
            List<ModifiableRuntimeLibrary> runtimeLibraries = new();
 
            if (_includeMainProjectInDepsFile)
            {
                runtimeLibraries.Add(GetProjectRuntimeLibrary());
            }
 
            runtimeLibraries.AddRange(GetRuntimePackLibraries());
 
            foreach (var library in _dependencyLibraries.Values
                .Where(l => !l.ExcludeFromRuntime && l.Type != "runtimepack"))
            {
                var runtimeLibrary = GetRuntimeLibrary(library, userRuntimeAssemblies);
                if (runtimeLibrary != null)
                {
                    runtimeLibraries.Add(runtimeLibrary);
                }
            }
 
            var directAndDependencyReferences = _directReferences ?? Enumerable.Empty<ReferenceInfo>();
            if (_dependencyReferences != null)
            {
                directAndDependencyReferences = directAndDependencyReferences.Concat(_dependencyReferences);
            }
 
            foreach (var directReference in directAndDependencyReferences)
            {
                var runtimeLibrary = new ModifiableRuntimeLibrary(new RuntimeLibrary(
                    type: "reference",
                    name: GetReferenceLibraryName(directReference),
                    version: directReference.Version,
                    hash: string.Empty,
                    runtimeAssemblyGroups: [new RuntimeAssetGroup(string.Empty, [CreateRuntimeFile(directReference.FileName, directReference.FullPath)])],
                    nativeLibraryGroups: [],
                    resourceAssemblies: CreateResourceAssemblies(directReference.ResourceAssemblies),
                    dependencies: [],
                    path: null,
                    hashPath: null,
                    runtimeStoreManifestName: null,
                    serviceable: false));
 
                runtimeLibraries.Add(runtimeLibrary);
            }
 
            /*
             * We now need to modify runtimeLibraries to eliminate those that don't have any runtime assets. We follow the following steps:
             * 0. Construct a reverse dependencies list: all runtimeLibraries that depend on this one
             * 1. If runtimeAssemblyGroups, nativeLibraryGroups, dependencies, and resourceAssemblies are all empty, remove this runtimeLibrary as well as any dependencies on it.
             * 2. Add all runtimeLibraries to a list of to-be-processed libraries called libraryCandidatesForRemoval
             * 3. libraryCandidatesForRemoval.Pop() --> if there are no runtimeAssemblyGroups, nativeLibraryGroups, or resourceAssemblies, and either dependencies is empty or all
             *      dependencies have something else that depends on them, remove it (and from libraryCandidatesForRemoval), adding everything that depends on this to
             *      libraryCandidatesForRemoval if it isn't already there
             * Repeat 3 until libraryCandidatesForRemoval is empty
             */
 
            // Rather than adding the main project's dependencies, we added references to them, i.e., Foo.Reference.dll
            // instead of Foo.dll. This adds Foo.dll as a reference directly so another reference can be removed if it
            // isn't necessary because of a direct reference from the main project.
            var referenceNameToRealName = new Dictionary<string, string>();
            if (_includeMainProjectInDepsFile)
            {
                var mainProjectReferences = _directReferences;
                if (IncludeCompilationLibraries && _referenceAssemblies != null)
                {
                    if (mainProjectReferences == null)
                    {
                        mainProjectReferences = _referenceAssemblies;
                    }
                    else
                    {
                        mainProjectReferences = mainProjectReferences.Concat(_referenceAssemblies);
                    }
                }
 
                if (mainProjectReferences != null)
                {
                    foreach (var directReference in mainProjectReferences)
                    {
                        referenceNameToRealName[GetReferenceLibraryName(directReference)] = directReference.Name;
                    }
                }
            }
 
            var libraries = runtimeLibraries.ToDictionary(lib => lib.Library.Name, lib => lib);
            foreach (var reference in runtimeLibraries)
            {
                foreach (var dependency in reference.Library.Dependencies)
                {
                    var name = referenceNameToRealName.TryGetValue(dependency.Name, out var realName) ? realName : dependency.Name;
                    if (libraries.TryGetValue(name, out var dep))
                    {
                        dep.Dependents.Add(reference.Library.Name);
                    }
                }
            }
 
            if (_trimLibrariesWithoutAssets)
            {
                var unprocessedLibraries = runtimeLibraries.ToHashSet();
                while (unprocessedLibraries.Any())
                {
                    var lib = unprocessedLibraries.First();
                    unprocessedLibraries.Remove(lib);
 
                    if (lib.Library.Name.Equals("xunit", StringComparison.OrdinalIgnoreCase) ||
                        lib.Library.Name.Equals("xunit.core", StringComparison.OrdinalIgnoreCase))
                    {
                        // Special case xunit and xunit.core, they should not be removed because the xUnit v2 runner looks for these libraries in the deps.json to
                        // identify test projects.
                        // See https://github.com/dotnet/sdk/issues/49248
                        continue;
                    }
 
                    if (lib.Library.RuntimeAssemblyGroups.Count == 0 && lib.Library.NativeLibraryGroups.Count == 0 && lib.Library.ResourceAssemblies.Count == 0)
                    {
                        if (lib.Library.Dependencies.All(d => !libraries.TryGetValue(d.Name, out var dependency) || dependency.Dependents.Count > 1))
                        {
                            runtimeLibraries.Remove(lib);
                            libraries.Remove(lib.Library.Name);
                            foreach (var dependency in lib.Library.Dependencies)
                            {
                                if (libraries.TryGetValue(dependency.Name, out ModifiableRuntimeLibrary value))
                                {
                                    value.Dependents.Remove(lib.Library.Name);
                                }
                            }
 
                            foreach (var dependent in lib.Dependents)
                            {
                                if (libraries.TryGetValue(dependent, out var dep))
                                {
                                    unprocessedLibraries.Add(dep);
                                }
                            }
                        }
                    }
                }
            }
 
            List<CompilationLibrary> compilationLibraries = new();
            if (IncludeCompilationLibraries)
            {
                if (_includeMainProjectInDepsFile)
                {
                    var dependencies = GetProjectDependencies();
 
                    var projectCompilationLibrary = new CompilationLibrary(
                        type: "project",
                        name: _mainProjectInfo.Name,
                        version: _mainProjectInfo.Version,
                        hash: string.Empty,
                        assemblies: new[] { _mainProjectInfo.OutputName },
                        dependencies: dependencies,
                        serviceable: false);
 
                    compilationLibraries.Add(projectCompilationLibrary);
                }
 
                if (_referenceAssemblies != null)
                {
                    foreach (var referenceAssembly in _referenceAssemblies)
                    {
                        string resolvedPath;
                        if (!string.IsNullOrEmpty(_referenceAssembliesPath) &&
                            referenceAssembly.FullPath?.StartsWith(_referenceAssembliesPath) == true)
                        {
                            resolvedPath = referenceAssembly.FullPath.Substring(_referenceAssembliesPath.Length);
                        }
                        else
                        {
                            resolvedPath = Path.GetFileName(referenceAssembly.FullPath);
                        }
 
                        compilationLibraries.Add(new CompilationLibrary(
                            type: "referenceassembly",
                            name: GetReferenceLibraryName(referenceAssembly),
                            version: referenceAssembly.Version,
                            hash: string.Empty,
                            assemblies: new[] { resolvedPath },
                            dependencies: Enumerable.Empty<Dependency>(),
                            serviceable: false));
                    }
                }
 
                foreach (var library in _dependencyLibraries.Values
                    .Where(l => !l.ExcludeFromCompilation && l.Type != "runtimepack"))
                {
                    var compilationLibrary = GetCompilationLibrary(library);
                    if (compilationLibrary != null)
                    {
                        compilationLibraries.Add(compilationLibrary);
                    }
                }
 
                if (_directReferences != null)
                {
                    foreach (var directReference in _directReferences)
                    {
                        compilationLibraries.Add(new CompilationLibrary(
                            type: "reference",
                            name: GetReferenceLibraryName(directReference),
                            version: directReference.Version,
                            hash: string.Empty,
                            assemblies: new[] { directReference.FileName },
                            dependencies: Enumerable.Empty<Dependency>(),
                            serviceable: false));
                    }
                }
            }
 
            var targetInfo = new TargetInfo(
                _dotnetFrameworkName,
                _runtimeIdentifier,
                runtimeSignature: string.Empty,
                _isPortable);
 
            // Compute the runtime fallback graph
            //
            // If the input RuntimeGraph is empty, or we're not compiling
            // for a specific RID, then an runtime fallback graph is empty
            //
            // Otherwise, it is the set of all runtimes compatible with (inheriting)
            // the target runtime-identifier.
            var runtimeFallbackGraph =
                (_runtimeGraph == null || _runtimeIdentifier == null) ?
                    new RuntimeFallbacks[] { } :
                    _runtimeGraph.Runtimes
                        .Select(runtimeDict => _runtimeGraph.ExpandRuntime(runtimeDict.Key))
                        .Where(expansion => expansion.Contains(_runtimeIdentifier))
                        .Select(expansion => new RuntimeFallbacks(expansion.First(), expansion.Skip(1))); // ExpandRuntime return runtime itself as first item.
 
            var libraryNames = runtimeLibraries.Select(lib => lib.Library.Name).Concat(compilationLibraries.Select(lib => lib.Name)).ToHashSet();
 
            return new DependencyContext(
            targetInfo,
            _compilationOptions ?? CompilationOptions.Default,
            compilationLibraries,
            runtimeLibraries.Select(library => new RuntimeLibrary(
                library.Library.Type,
                library.Library.Name,
                library.Library.Version,
                library.Library.Hash,
                library.Library.RuntimeAssemblyGroups,
                library.Library.NativeLibraryGroups,
                library.Library.ResourceAssemblies,
                library.Library.Dependencies.Where(dependency => libraryNames.Contains(dependency.Name)).ToList(),
                library.Library.Serviceable,
                library.Library.Path,
                library.Library.HashPath,
                library.Library.RuntimeStoreManifestName)),
            runtimeFallbackGraph);
        }
 
        private ModifiableRuntimeLibrary GetProjectRuntimeLibrary()
        {
            RuntimeAssetGroup[] runtimeAssemblyGroups = new[] { new RuntimeAssetGroup(string.Empty, _mainProjectInfo.OutputName) };
 
            var dependencies = GetProjectDependencies();
 
            //  Runtime pack assets only get added as dependencies to the runtime (not the compile) project
            if (_runtimePackAssets != null)
            {
                foreach (var runtimePackName in _runtimePackAssets.Keys)
                {
                    dependencies.Add(_dependencyLibraries[runtimePackName].Dependency);
                }
            }
 
            return new ModifiableRuntimeLibrary(new RuntimeLibrary(
                type: "project",
                name: GetUniqueLibraryName(_mainProjectInfo.Name, "Project"),
                version: _mainProjectInfo.Version,
                hash: string.Empty,
                runtimeAssemblyGroups: runtimeAssemblyGroups,
                nativeLibraryGroups: Array.Empty<RuntimeAssetGroup>(),
                resourceAssemblies: CreateResourceAssemblies(_mainProjectInfo.ResourceAssemblies),
                dependencies: dependencies,
                path: null,
                hashPath: null,
                runtimeStoreManifestName: GetRuntimeStoreManifestName(_mainProjectInfo.Name, _mainProjectInfo.Version),
                serviceable: false));
        }
 
        private List<Dependency> GetProjectDependencies()
        {
            List<Dependency> dependencies = new();
            foreach (var dependencyName in _mainProjectDependencies)
            {
                if (_dependencyLibraries.TryGetValue(dependencyName, out var dependencyLibrary))
                {
                    //  Include dependency if it would be included either as a runtime or compilation
                    //  (if compilation libraries are being included) library
                    if (!dependencyLibrary.ExcludeFromRuntime ||
                        (IncludeCompilationLibraries && !dependencyLibrary.ExcludeFromCompilation))
                    {
                        dependencies.Add(dependencyLibrary.Dependency);
                    }
                }
            }
 
            var references = _directReferences;
            if (IncludeCompilationLibraries && _referenceAssemblies != null)
            {
                if (references == null)
                {
                    references = _referenceAssemblies;
                }
                else
                {
                    references = references.Concat(_referenceAssemblies);
                }
            }
 
            if (references != null)
            {
                foreach (var directReference in references)
                {
                    dependencies.Add(
                        new Dependency(
                            GetReferenceLibraryName(directReference),
                            directReference.Version));
                }
            }
 
            return dependencies;
        }
 
        private IEnumerable<ModifiableRuntimeLibrary> GetRuntimePackLibraries()
        {
            if (_runtimePackAssets == null)
            {
                return [];
            }
            return _runtimePackAssets.Select(runtimePack =>
            {
                var runtimeAssemblyGroup = new RuntimeAssetGroup(string.Empty,
                    runtimePack.Value.Where(asset => asset.AssetType == AssetType.Runtime)
                    .Select(asset => CreateRuntimeFile(asset.DestinationSubPath, asset.SourcePath)));
 
                var nativeLibraryGroup = new RuntimeAssetGroup(string.Empty,
                    runtimePack.Value.Where(asset => asset.AssetType == AssetType.Native)
                    .Select(asset => CreateRuntimeFile(asset.DestinationSubPath, asset.SourcePath)));
 
                return new ModifiableRuntimeLibrary(new RuntimeLibrary(
                    type: "runtimepack",
                    name: runtimePack.Key,
                    version: runtimePack.Value.First().PackageVersion,
                    hash: string.Empty,
                    runtimeAssemblyGroups: [runtimeAssemblyGroup],
                    nativeLibraryGroups: [nativeLibraryGroup],
                    resourceAssemblies: [],
                    dependencies: [],
                    serviceable: false));
            });
        }
 
        private ModifiableRuntimeLibrary GetRuntimeLibrary(DependencyLibrary library, string[] userRuntimeAssemblies)
        {
            GetCommonLibraryProperties(library,
                out string hash,
                out HashSet<Dependency> libraryDependencies,
                out bool serviceable,
                out string path,
                out string hashPath,
                out SingleProjectInfo referenceProjectInfo);
 
            if (referenceProjectInfo is UnreferencedProjectInfo)
            {
                // unreferenced ProjectInfos will be added later as simple dll dependencies
                return null;
            }
 
            List<RuntimeAssetGroup> runtimeAssemblyGroups = new();
            List<RuntimeAssetGroup> nativeLibraryGroups = new();
            List<ResourceAssembly> resourceAssemblies = new();
 
            if (library.Type == "project" && !(referenceProjectInfo is UnreferencedProjectInfo))
            {
                var fileName = Path.GetFileNameWithoutExtension(library.Path);
                var assemblyPath = userRuntimeAssemblies?.FirstOrDefault(p => Path.GetFileNameWithoutExtension(p).Equals(fileName));
                var runtimeFile = !string.IsNullOrWhiteSpace(assemblyPath) && File.Exists(assemblyPath) ? CreateRuntimeFile(referenceProjectInfo.OutputName, assemblyPath) :
                                  !string.IsNullOrWhiteSpace(library.Path) && File.Exists(library.Path) ? CreateRuntimeFile(referenceProjectInfo.OutputName, library.Path) :
                                  new RuntimeFile(referenceProjectInfo.OutputName, string.Empty, string.Empty);
                runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty, [runtimeFile]));
 
                resourceAssemblies.AddRange(referenceProjectInfo.ResourceAssemblies
                                .Select(r => new ResourceAssembly(r.RelativePath, r.Culture)));
            }
            else
            {
                if (_resolvedNuGetFiles != null && _resolvedNuGetFiles.TryGetValue(library.Name, out var resolvedNuGetFiles))
                {
                    var runtimeFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Runtime &&
                                                                !f.IsRuntimeTarget);
 
                    runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty,
                                                runtimeFiles.Select(CreateRuntimeFile)));
 
                    var nativeFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Native &&
                                                                !f.IsRuntimeTarget);
 
                    nativeLibraryGroups.Add(new RuntimeAssetGroup(string.Empty,
                                                nativeFiles.Select(CreateRuntimeFile)));
 
                    var resourceFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Resources &&
                                                                !f.IsRuntimeTarget);
 
                    resourceAssemblies.AddRange(resourceFiles.Select(f => new ResourceAssembly(f.PathInPackage, f.Culture)));
 
                    var runtimeTargets = resolvedNuGetFiles.Where(f => f.IsRuntimeTarget)
                                                                .GroupBy(f => f.RuntimeIdentifier);
 
                    foreach (var runtimeIdentifierGroup in runtimeTargets)
                    {
                        var managedRuntimeTargetsFiles = runtimeIdentifierGroup.Where(f => f.Asset == AssetType.Runtime).ToList();
                        if (managedRuntimeTargetsFiles.Any())
                        {
                            runtimeAssemblyGroups.Add(new RuntimeAssetGroup(runtimeIdentifierGroup.Key,
                                                            managedRuntimeTargetsFiles.Select(CreateRuntimeFile)));
                        }
 
                        var nativeRuntimeTargetsFiles = runtimeIdentifierGroup.Where(f => f.Asset == AssetType.Native).ToList();
                        if (nativeRuntimeTargetsFiles.Any())
                        {
                            nativeLibraryGroups.Add(new RuntimeAssetGroup(runtimeIdentifierGroup.Key,
                                                            nativeRuntimeTargetsFiles.Select(CreateRuntimeFile)));
                        }
                    }
                }
 
            }
 
            var runtimeLibrary = new ModifiableRuntimeLibrary(new RuntimeLibrary(
                type: library.Type,
                name: library.Name,
                version: library.Version.ToString(),
                hash: hash,
                runtimeAssemblyGroups: runtimeAssemblyGroups,
                nativeLibraryGroups: nativeLibraryGroups,
                resourceAssemblies: resourceAssemblies,
                dependencies: libraryDependencies,
                path: path,
                hashPath: hashPath,
                runtimeStoreManifestName: GetRuntimeStoreManifestName(library.Name, library.Version.ToString()),
                serviceable: serviceable));
 
            return runtimeLibrary;
        }
 
        private CompilationLibrary GetCompilationLibrary(DependencyLibrary library)
        {
            GetCommonLibraryProperties(library,
                out string hash,
                out HashSet<Dependency> libraryDependencies,
                out bool serviceable,
                out string path,
                out string hashPath,
                out SingleProjectInfo referenceProjectInfo);
 
            List<string> assemblies = new();
 
            if (library.Type == "project" && !(referenceProjectInfo is UnreferencedProjectInfo))
            {
                assemblies.Add(referenceProjectInfo.OutputName);
            }
            else if (_compileReferences != null && _compileReferences.TryGetValue(library.Name, out var compileReferences))
            {
                foreach (var compileReference in compileReferences)
                {
                    assemblies.Add(compileReference.PathInPackage);
                }
            }
 
            return new CompilationLibrary(
                type: library.Type,
                name: library.Name,
                version: library.Version.ToString(),
                hash,
                assemblies,
                libraryDependencies,
                serviceable,
                path,
                hashPath);
        }
 
        private void GetCommonLibraryProperties(DependencyLibrary library,
                    out string hash,
                    out HashSet<Dependency> dependencies,
                    out bool serviceable,
                    out string path,
                    out string hashPath,
                    out SingleProjectInfo referenceProjectInfo)
        {
            serviceable = true;
            referenceProjectInfo = null;
 
            dependencies = new HashSet<Dependency>();
            List<LibraryDependency> libraryDependencies;
            if (_libraryDependencies.TryGetValue(library.Name, out libraryDependencies))
            {
                foreach (var dependency in libraryDependencies)
                {
                    if (_dependencyLibraries.TryGetValue(dependency.Name, out var libraryDependency))
                    {
                        if (!libraryDependency.ExcludeFromRuntime ||
                            (!libraryDependency.ExcludeFromCompilation && IncludeCompilationLibraries))
                        {
                            dependencies.Add(libraryDependency.Dependency);
                        }
                    }
                }
            }
 
            hash = string.Empty;
            path = null;
            hashPath = null;
            if (library.Type == "package")
            {
                // TEMPORARY: All packages are serviceable in RC2
                // See https://github.com/dotnet/cli/issues/2569
                serviceable = true;
                if (!string.IsNullOrEmpty(library.Sha512))
                {
                    hash = "sha512-" + library.Sha512;
                    hashPath = _versionFolderPathResolver.GetHashFileName(library.Name, library.Version);
                }
 
                path = library.Path;
            }
            else if (library.Type == "project")
            {
                serviceable = false;
                referenceProjectInfo = GetProjectInfo(library);
 
                foreach (var dependencyReference in referenceProjectInfo.DependencyReferences)
                {
                    dependencies.Add(
                        new Dependency(
                            GetReferenceLibraryName(dependencyReference),
                            dependencyReference.Version));
                }
            }
        }
 
        private RuntimeFile CreateRuntimeFile(ResolvedFile resolvedFile)
        {
            string relativePath = resolvedFile.PathInPackage;
            if (string.IsNullOrEmpty(relativePath))
            {
                relativePath = resolvedFile.DestinationSubPath;
            }
            return CreateRuntimeFile(relativePath, resolvedFile.SourcePath);
        }
 
        private RuntimeFile CreateRuntimeFile(string path, string fullPath)
        {
            if (_includeRuntimeFileVersions)
            {
                string fileVersion = FileUtilities.GetFileVersion(fullPath).ToString();
                string assemblyVersion = FileUtilities.TryGetAssemblyVersion(fullPath)?.ToString();
                return new RuntimeFile(path, assemblyVersion, fileVersion);
            }
            else
            {
                return new RuntimeFile(path, null, null);
            }
        }
 
        private static IEnumerable<ResourceAssembly> CreateResourceAssemblies(IEnumerable<ResourceAssemblyInfo> resourceAssemblyInfos)
        {
            return resourceAssemblyInfos
                .Select(r => new ResourceAssembly(r.RelativePath, r.Culture));
        }
 
        private SingleProjectInfo GetProjectInfo(DependencyLibrary library)
        {
            string projectPath = library.MSBuildProject;
            if (string.IsNullOrEmpty(projectPath))
            {
                throw new BuildErrorException(Strings.CannotFindProjectInfo, library.Name);
            }
 
            string mainProjectDirectory = Path.GetDirectoryName(_mainProjectInfo.ProjectPath);
            string fullProjectPath = Path.GetFullPath(Path.Combine(mainProjectDirectory, projectPath));
 
            SingleProjectInfo referenceProjectInfo = null;
            if (_referenceProjectInfos?.TryGetValue(fullProjectPath, out referenceProjectInfo) != true ||
                referenceProjectInfo == null)
            {
                return UnreferencedProjectInfo.Default;
            }
 
            return referenceProjectInfo;
        }
 
        private void CalculateExcludedLibraries()
        {
            Dictionary<string, DependencyLibrary> libraries = _dependencyLibraries;
 
            HashSet<string> runtimeExclusionList = new(StringComparer.OrdinalIgnoreCase);
 
            if (_isFrameworkDependent && !string.IsNullOrEmpty(_platformLibrary))
            {
                //  Exclude platform library and dependencies.
                runtimeExclusionList.Add(_platformLibrary);
 
                Stack<LibraryDependency> dependenciesToWalk = new(_libraryDependencies[_platformLibrary]);
 
                // If the platform library is not Microsoft.NETCore.App, treat it as an implicit dependency.
                // This makes it so Microsoft.AspNet.* 2.x platforms also exclude Microsoft.NETCore.App files.
                if (!string.Equals(_platformLibrary, NetCorePlatformLibrary, StringComparison.OrdinalIgnoreCase))
                {
                    if (_dependencyLibraries.TryGetValue(NetCorePlatformLibrary, out var netCoreDependencyLibrary))
                    {
                        dependenciesToWalk.Push(new LibraryDependency()
                        {
                            Name = netCoreDependencyLibrary.Name,
                            MinVersion = netCoreDependencyLibrary.Version
                        });
                    }
                }
 
                while (dependenciesToWalk.Any())
                {
                    var dependency = dependenciesToWalk.Pop();
                    if (runtimeExclusionList.Contains(dependency.Name))
                    {
                        continue;
                    }
 
                    //  Resolved version of library has to match dependency version exactly, so that we
                    //  don't exclude newer versions of libraries that are part of the platform
                    if (_dependencyLibraries[dependency.Name].Version == dependency.MinVersion)
                    {
                        runtimeExclusionList.Add(dependency.Name);
                        foreach (var newDependency in _libraryDependencies[dependency.Name])
                        {
                            dependenciesToWalk.Push(newDependency);
                        }
                    }
                }
            }
 
            if (_packagesToBeFiltered != null)
            {
                foreach (var packageToFilter in _packagesToBeFiltered)
                {
                    if (_dependencyLibraries.TryGetValue(packageToFilter.Id, out var library))
                    {
                        if (library.Type == "package" &&
                            _dependencyLibraries[packageToFilter.Id].Version == packageToFilter.Version)
                        {
                            runtimeExclusionList.Add(packageToFilter.Id);
                        }
                    }
                }
            }
 
            foreach (var packageToExcludeFromRuntime in runtimeExclusionList)
            {
                _dependencyLibraries[packageToExcludeFromRuntime].ExcludeFromRuntime = true;
            }
        }
 
        private string GetReferenceLibraryName(ReferenceInfo reference)
        {
            if (!ReferenceLibraryNames.TryGetValue(reference, out string name))
            {
                // Reference names can conflict with PackageReference names, so
                // ensure that the Reference names are unique when creating libraries
                name = GetUniqueLibraryName(reference.Name);
 
                ReferenceLibraryNames.Add(reference, name);
                _usedLibraryNames.Add(name);
            }
 
            return name;
        }
 
        private string GetUniqueLibraryName(string name, string qualifier = "Reference")
        {
            if (_usedLibraryNames.Contains(name))
            {
                string startingName = $"{name}.{qualifier}";
                name = startingName;
 
                int suffix = 1;
                while (_usedLibraryNames.Contains(name))
                {
                    name = $"{startingName}{suffix++}";
                }
            }
 
            return name;
        }
 
        private string GetRuntimeStoreManifestName(string packageName, string packageVersion)
        {
            string runtimeStoreManifestName = null;
            if (_filteredPackages != null && _filteredPackages.Any())
            {
                var pkg = new PackageIdentity(packageName, NuGetVersion.Parse(packageVersion));
                _filteredPackages?.TryGetValue(pkg, out runtimeStoreManifestName);
            }
            return runtimeStoreManifestName;
        }
 
        private class DependencyLibrary
        {
            public string Name { get; }
            public NuGetVersion Version { get; }
            public string Type { get; }
            public Dependency Dependency { get; }
            public string Sha512 { get; set; }
            public string Path { get; set; }
            public string MSBuildProject { get; set; }
 
            public bool ExcludeFromRuntime { get; set; }
 
            public bool ExcludeFromCompilation { get; set; }
 
            public DependencyLibrary(string name, NuGetVersion version, string type)
            {
                Name = name;
                Version = version;
                Type = type;
                Dependency = new Dependency(name, version.ToString());
            }
        }
 
        private struct LibraryDependency
        {
            public string Name { get; set; }
            public NuGetVersion MinVersion { get; set; }
        }
 
        private class ModifiableRuntimeLibrary
        {
            // Dependents are assemblies that depend on this library, as opposed to dependencies which are libraries that this one depends on
            public HashSet<string> Dependents { get; set; }
            public RuntimeLibrary Library { get; set; }
 
            public ModifiableRuntimeLibrary(RuntimeLibrary library)
            {
                this.Dependents = new();
                this.Library = library;
            }
        }
    }
}