private Task <GraphItem <RemoteResolveResult> > FindLibraryCached(
            ConcurrentDictionary <LibraryRangeCacheKey, Task <GraphItem <RemoteResolveResult> > > cache,
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            CancellationToken cancellationToken)
        {
            var key = new LibraryRangeCacheKey(libraryRange, framework);

            return(cache.GetOrAdd(key, (cacheKey) =>
                                  FindLibraryEntry(cacheKey.LibraryRange, framework, outerEdge, cancellationToken)));
        }
        public static Task <GraphItem <RemoteResolveResult> > FindLibraryCachedAsync(
            ConcurrentDictionary <LibraryRangeCacheKey, Task <GraphItem <RemoteResolveResult> > > cache,
            LibraryRange libraryRange,
            NuGetFramework framework,
            string runtimeIdentifier,
            RemoteWalkContext context,
            CancellationToken cancellationToken)
        {
            var key = new LibraryRangeCacheKey(libraryRange, framework);

            return(cache.GetOrAdd(key, (cacheKey) =>
                                  FindLibraryEntryAsync(cacheKey.LibraryRange, framework, runtimeIdentifier, context, cancellationToken)));
        }
        private async Task <GraphNode <RemoteResolveResult> > CreateGraphNode(
            LibraryRange libraryRange,
            NuGetFramework framework,
            string runtimeName,
            RuntimeGraph runtimeGraph,
            Func <LibraryRange, DependencyResult> predicate,
            GraphEdge <RemoteResolveResult> outerEdge)
        {
            var dependencies        = new List <LibraryDependency>();
            var runtimeDependencies = new HashSet <string>();

            if (!string.IsNullOrEmpty(runtimeName) && runtimeGraph != null)
            {
                // HACK(davidfowl): This is making runtime.json support package redirects

                // Look up any additional dependencies for this package
                foreach (var runtimeDependency in runtimeGraph.FindRuntimeDependencies(runtimeName, libraryRange.Name))
                {
                    var libraryDependency = new LibraryDependency
                    {
                        LibraryRange = new LibraryRange()
                        {
                            Name           = runtimeDependency.Id,
                            VersionRange   = runtimeDependency.VersionRange,
                            TypeConstraint = LibraryDependencyTarget.PackageProjectExternal
                        }
                    };

                    if (StringComparer.OrdinalIgnoreCase.Equals(runtimeDependency.Id, libraryRange.Name))
                    {
                        if (libraryRange.VersionRange != null &&
                            runtimeDependency.VersionRange != null &&
                            libraryRange.VersionRange.MinVersion < runtimeDependency.VersionRange.MinVersion)
                        {
                            libraryRange = libraryDependency.LibraryRange;
                        }
                    }
                    else
                    {
                        // Otherwise it's a dependency of this node
                        dependencies.Add(libraryDependency);
                        runtimeDependencies.Add(libraryDependency.Name);
                    }
                }
            }

            var node = new GraphNode <RemoteResolveResult>(libraryRange)
            {
                // Resolve the dependency from the cache or sources
                Item = await FindLibraryCached(
                    _context.FindLibraryEntryCache,
                    libraryRange,
                    framework,
                    outerEdge,
                    CancellationToken.None)
            };

            Debug.Assert(node.Item != null, "FindLibraryCached should return an unresolved item instead of null");
            if (node.Key.VersionRange != null &&
                node.Key.VersionRange.IsFloating)
            {
                var cacheKey = new LibraryRangeCacheKey(node.Key, framework);

                _context.FindLibraryEntryCache.TryAdd(cacheKey, Task.FromResult(node.Item));
            }

            var tasks = new List <Task <GraphNode <RemoteResolveResult> > >();

            if (dependencies.Count > 0)
            {
                // Create a new item on this node so that we can update it with the new dependencies from
                // runtime.json files
                // We need to clone the item since they can be shared across multiple nodes
                node.Item = new GraphItem <RemoteResolveResult>(node.Item.Key)
                {
                    Data = new RemoteResolveResult()
                    {
                        Dependencies = dependencies.Concat(node.Item.Data.Dependencies.Where(d => !runtimeDependencies.Contains(d.Name))).ToList(),
                        Match        = node.Item.Data.Match
                    }
                };
            }

            foreach (var dependency in node.Item.Data.Dependencies)
            {
                // Skip dependencies if the dependency edge has 'all' excluded and
                // the node is not a direct dependency.
                if (outerEdge == null ||
                    dependency.SuppressParent != LibraryIncludeFlags.All)
                {
                    var result = predicate(dependency.LibraryRange);

                    if (result == DependencyResult.Acceptable)
                    {
                        // Dependency edge from the current node to the dependency
                        var innerEdge = new GraphEdge <RemoteResolveResult>(outerEdge, node.Item, dependency);

                        tasks.Add(CreateGraphNode(
                                      dependency.LibraryRange,
                                      framework,
                                      runtimeName,
                                      runtimeGraph,
                                      ChainPredicate(predicate, node, dependency),
                                      innerEdge));
                    }
                    else
                    {
                        // Keep the node in the tree if we need to look at it later
                        if (result == DependencyResult.PotentiallyDowngraded ||
                            result == DependencyResult.Cycle)
                        {
                            var dependencyNode = new GraphNode <RemoteResolveResult>(dependency.LibraryRange)
                            {
                                Disposition = result == DependencyResult.Cycle ? Disposition.Cycle : Disposition.PotentiallyDowngraded
                            };

                            dependencyNode.OuterNode = node;
                            node.InnerNodes.Add(dependencyNode);
                        }
                    }
                }
            }

            while (tasks.Any())
            {
                // Wait for any node to finish resolving
                var task = await Task.WhenAny(tasks);

                // Extract the resolved node
                tasks.Remove(task);
                var dependencyNode = await task;
                dependencyNode.OuterNode = node;

                node.InnerNodes.Add(dependencyNode);
            }

            return(node);
        }