예제 #1
0
        public static async Task <GraphItem <RemoteResolveResult> > FindLibraryEntryAsync(
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            RemoteWalkContext context,
            CancellationToken cancellationToken)
        {
            GraphItem <RemoteResolveResult> graphItem = null;
            var currentCacheContext = context.CacheContext;

            // Try up to two times to get the package. The second
            // retry will refresh the cache if a package is listed
            // but fails to download. This can happen if the feed prunes
            // the package.
            for (var i = 0; i < 2 && graphItem == null; i++)
            {
                var match = await FindLibraryMatchAsync(
                    libraryRange,
                    framework,
                    outerEdge,
                    context.RemoteLibraryProviders,
                    context.LocalLibraryProviders,
                    context.ProjectLibraryProviders,
                    currentCacheContext,
                    context.Logger,
                    cancellationToken);

                if (match == null)
                {
                    return(CreateUnresolvedMatch(libraryRange));
                }

                try
                {
                    graphItem = await CreateGraphItemAsync(match, framework, currentCacheContext, context.Logger, cancellationToken);
                }
                catch (InvalidCacheProtocolException) when(i == 0)
                {
                    // 1st failure, invalidate the cache and try again.
                    // Clear the on disk and memory caches during the next request.
                    currentCacheContext = currentCacheContext.WithRefreshCacheTrue();
                }
                catch (PackageNotFoundProtocolException ex) when(match.Provider.IsHttp && match.Provider.Source != null)
                {
                    // 2nd failure, the feed is likely corrupt or removing packages too fast to keep up with.
                    var message = string.Format(CultureInfo.CurrentCulture,
                                                Strings.Error_PackageNotFoundWhenExpected,
                                                match.Provider.Source,
                                                ex.PackageIdentity.ToString());

                    throw new FatalProtocolException(message, ex);
                }
            }

            return(graphItem);
        }
예제 #2
0
        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)));
        }
예제 #3
0
        public static Task <GraphItem <RemoteResolveResult> > FindLibraryCachedAsync(
            ConcurrentDictionary <LibraryRangeCacheKey, Task <GraphItem <RemoteResolveResult> > > cache,
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            RemoteWalkContext context,
            CancellationToken cancellationToken)
        {
            var key = new LibraryRangeCacheKey(libraryRange, framework);

            return(cache.GetOrAdd(key, (cacheKey) =>
                                  FindLibraryEntryAsync(cacheKey.LibraryRange, framework, outerEdge, context, cancellationToken)));
        }
예제 #4
0
        private async Task <GraphItem <RemoteResolveResult> > FindLibraryEntry(
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            CancellationToken cancellationToken)
        {
            var match = await FindLibraryMatch(libraryRange, framework, outerEdge, cancellationToken);

            if (match == null)
            {
                return(CreateUnresolvedMatch(libraryRange));
            }

            IEnumerable <LibraryDependency> dependencies;

            // For local matches such as projects get the dependencies from the LocalLibrary property.
            var localMatch = match as LocalMatch;

            if (localMatch != null)
            {
                dependencies = localMatch.LocalLibrary.Dependencies;
            }
            else
            {
                // Look up the dependencies from the source
                dependencies = await match.Provider.GetDependenciesAsync(
                    match.Library,
                    framework,
                    _context.CacheContext,
                    _context.Logger,
                    cancellationToken);
            }

            return(new GraphItem <RemoteResolveResult>(match.Library)
            {
                Data = new RemoteResolveResult
                {
                    Match = match,
                    Dependencies = dependencies
                },
            });
        }
예제 #5
0
        /// <summary>
        /// Returns root directory of the parent project.
        /// This will be null if the reference is from a non-project type.
        /// </summary>
        private static string GetRootPathForParentProject(GraphEdge <RemoteResolveResult> outerEdge)
        {
            if (outerEdge != null &&
                outerEdge.Item.Key.Type == LibraryType.Project &&
                outerEdge.Item.Data.Match.Path != null)
            {
                var projectJsonPath = new FileInfo(outerEdge.Item.Data.Match.Path);

                // For files in the root of the drive this will be null
                if (projectJsonPath.Directory.Parent == null)
                {
                    return(projectJsonPath.Directory.FullName);
                }
                else
                {
                    return(projectJsonPath.Directory.Parent.FullName);
                }
            }

            return(null);
        }
예제 #6
0
        private Task <RemoteMatch> FindProjectMatch(
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            CancellationToken cancellationToken)
        {
            RemoteMatch result = null;

            // Check if projects are allowed for this dependency
            if (libraryRange.TypeConstraintAllowsAnyOf(
                    (LibraryDependencyTarget.Project | LibraryDependencyTarget.ExternalProject)))
            {
                // Find the root directory of the parent project if one exists.
                // This is used for resolving global json.
                var parentProjectRoot = GetRootPathForParentProject(outerEdge);

                foreach (var provider in _context.ProjectLibraryProviders)
                {
                    if (provider.SupportsType(libraryRange.TypeConstraint))
                    {
                        var match = provider.GetLibrary(libraryRange, framework, parentProjectRoot);
                        if (match != null)
                        {
                            result = new LocalMatch
                            {
                                LocalLibrary  = match,
                                Library       = match.Identity,
                                LocalProvider = provider,
                                Provider      = new LocalDependencyProvider(provider),
                                Path          = match.Path,
                            };
                        }
                    }
                }
            }

            return(Task.FromResult <RemoteMatch>(result));
        }
예제 #7
0
        public static Task <RemoteMatch> FindProjectMatchAsync(
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            IEnumerable <IDependencyProvider> projectProviders,
            CancellationToken cancellationToken)
        {
            RemoteMatch result = null;

            // Check if projects are allowed for this dependency
            if (libraryRange.TypeConstraintAllowsAnyOf(
                    (LibraryDependencyTarget.Project | LibraryDependencyTarget.ExternalProject)))
            {
                foreach (var provider in projectProviders)
                {
                    if (provider.SupportsType(libraryRange.TypeConstraint))
                    {
                        var match = provider.GetLibrary(libraryRange, framework);

                        if (match != null)
                        {
                            result = new LocalMatch
                            {
                                LocalLibrary  = match,
                                Library       = match.Identity,
                                LocalProvider = provider,
                                Provider      = new LocalDependencyProvider(provider),
                                Path          = match.Path,
                            };
                        }
                    }
                }
            }

            return(Task.FromResult <RemoteMatch>(result));
        }
예제 #8
0
        private async Task <RemoteMatch> FindLibraryMatch(
            LibraryRange libraryRange,
            NuGetFramework framework,
            GraphEdge <RemoteResolveResult> outerEdge,
            CancellationToken cancellationToken)
        {
            var projectMatch = await FindProjectMatch(libraryRange, framework, outerEdge, cancellationToken);

            if (projectMatch != null)
            {
                return(projectMatch);
            }

            if (libraryRange.VersionRange == null)
            {
                return(null);
            }

            // The resolution below is only for package types
            if (!libraryRange.TypeConstraintAllows(LibraryDependencyTarget.Package))
            {
                return(null);
            }

            if (libraryRange.VersionRange.IsFloating)
            {
                // For snapshot dependencies, get the version remotely first.
                var remoteMatch = await FindLibraryByVersion(libraryRange, framework, _context.RemoteLibraryProviders, cancellationToken);

                if (remoteMatch != null)
                {
                    // Try to see if the specific version found on the remote exists locally. This avoids any unnecessary
                    // remote access incase we already have it in the cache/local packages folder.
                    var localMatch = await FindLibraryByVersion(remoteMatch.Library, framework, _context.LocalLibraryProviders, cancellationToken);

                    if (localMatch != null &&
                        localMatch.Library.Version.Equals(remoteMatch.Library.Version))
                    {
                        // If we have a local match, and it matches the version *exactly* then use it.
                        return(localMatch);
                    }

                    // We found something locally, but it wasn't an exact match
                    // for the resolved remote match.
                }

                return(remoteMatch);
            }
            else
            {
                // Check for the specific version locally.
                var localMatch = await FindLibraryByVersion(libraryRange, framework, _context.LocalLibraryProviders, cancellationToken);

                if (localMatch != null &&
                    localMatch.Library.Version.Equals(libraryRange.VersionRange.MinVersion))
                {
                    // We have an exact match so use it.
                    return(localMatch);
                }

                // Either we found a local match but it wasn't the exact version, or
                // we didn't find a local match.
                var remoteMatch = await FindLibraryByVersion(libraryRange, framework, _context.RemoteLibraryProviders, cancellationToken);

                if (remoteMatch != null &&
                    localMatch == null)
                {
                    // There wasn't any local match for the specified version but there was a remote match.
                    // See if that version exists locally.
                    localMatch = await FindLibraryByVersion(remoteMatch.Library, framework, _context.LocalLibraryProviders, cancellationToken);
                }

                if (localMatch != null &&
                    remoteMatch != null)
                {
                    // We found a match locally and remotely, so pick the better version
                    // in relation to the specified version.
                    if (libraryRange.VersionRange.IsBetter(
                            current: localMatch.Library.Version,
                            considering: remoteMatch.Library.Version))
                    {
                        return(remoteMatch);
                    }
                    else
                    {
                        return(localMatch);
                    }
                }

                // Prefer local over remote generally.
                return(localMatch ?? remoteMatch);
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
 public GraphEdge(GraphEdge <TItem> outerEdge, GraphItem <TItem> item, LibraryDependency edge)
 {
     OuterEdge = outerEdge;
     Item      = item;
     Edge      = edge;
 }
예제 #11
0
        public static async Task <RemoteMatch> FindLibraryMatchAsync(
            LibraryRange libraryRange,
            NuGetFramework framework,
            string runtimeIdentifier,
            GraphEdge <RemoteResolveResult> outerEdge,
            IEnumerable <IRemoteDependencyProvider> remoteProviders,
            IEnumerable <IRemoteDependencyProvider> localProviders,
            IEnumerable <IDependencyProvider> projectProviders,
            IDictionary <LockFileCacheKey, IList <LibraryIdentity> > lockFileLibraries,
            SourceCacheContext cacheContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            var projectMatch = await FindProjectMatchAsync(libraryRange, framework, outerEdge, projectProviders, cancellationToken);

            if (projectMatch != null)
            {
                return(projectMatch);
            }

            if (libraryRange.VersionRange == null)
            {
                return(null);
            }

            // The resolution below is only for package types
            if (!libraryRange.TypeConstraintAllows(LibraryDependencyTarget.Package))
            {
                return(null);
            }

            var targetFramework = framework;

            if (framework is AssetTargetFallbackFramework)
            {
                targetFramework = (framework as AssetTargetFallbackFramework).RootFramework;
            }

            var key = new LockFileCacheKey(targetFramework, runtimeIdentifier);

            // This is only applicable when packages has to be resolved from packages.lock.json file
            if (lockFileLibraries.TryGetValue(key, out var libraries))
            {
                var library = libraries.FirstOrDefault(lib => StringComparer.OrdinalIgnoreCase.Equals(lib.Name, libraryRange.Name));

                if (library != null)
                {
                    // check for the exact library through local repositories
                    var localMatch = await FindLibraryByVersionAsync(library, framework, localProviders, cacheContext, logger, cancellationToken);

                    if (localMatch != null)
                    {
                        return(localMatch);
                    }

                    // if not found in local repositories, then check the remote repositories
                    var remoteMatch = await FindLibraryByVersionAsync(library, framework, remoteProviders, cacheContext, logger, cancellationToken);

                    // either found or not, we must return from here since we dont want to resolve to any other version
                    // then defined in packages.lock.json file
                    return(remoteMatch);
                }

                // it should never come to this, but as a fail-safe if it ever fails to resolve a package from lock file when
                // it has to... then fail restore.
                return(null);
            }

            if (libraryRange.VersionRange.IsFloating)
            {
                // For snapshot dependencies, get the version remotely first.
                var remoteMatch = await FindLibraryByVersionAsync(libraryRange, framework, remoteProviders, cacheContext, logger, cancellationToken);

                if (remoteMatch != null)
                {
                    // Try to see if the specific version found on the remote exists locally. This avoids any unnecessary
                    // remote access incase we already have it in the cache/local packages folder.
                    var localMatch = await FindLibraryByVersionAsync(remoteMatch.Library, framework, localProviders, cacheContext, logger, cancellationToken);

                    if (localMatch != null &&
                        localMatch.Library.Version.Equals(remoteMatch.Library.Version))
                    {
                        // If we have a local match, and it matches the version *exactly* then use it.
                        return(localMatch);
                    }

                    // We found something locally, but it wasn't an exact match
                    // for the resolved remote match.
                }

                return(remoteMatch);
            }
            else
            {
                // Check for the specific version locally.
                var localMatch = await FindLibraryByVersionAsync(libraryRange, framework, localProviders, cacheContext, logger, cancellationToken);

                if (localMatch != null &&
                    localMatch.Library.Version.Equals(libraryRange.VersionRange.MinVersion))
                {
                    // We have an exact match so use it.
                    return(localMatch);
                }

                // Either we found a local match but it wasn't the exact version, or
                // we didn't find a local match.
                var remoteMatch = await FindLibraryByVersionAsync(libraryRange, framework, remoteProviders, cacheContext, logger, cancellationToken);

                if (remoteMatch != null &&
                    localMatch == null)
                {
                    // There wasn't any local match for the specified version but there was a remote match.
                    // See if that version exists locally.
                    localMatch = await FindLibraryByVersionAsync(remoteMatch.Library, framework, remoteProviders, cacheContext, logger, cancellationToken);
                }

                if (localMatch != null &&
                    remoteMatch != null)
                {
                    // We found a match locally and remotely, so pick the better version
                    // in relation to the specified version.
                    if (libraryRange.VersionRange.IsBetter(
                            current: localMatch.Library.Version,
                            considering: remoteMatch.Library.Version))
                    {
                        return(remoteMatch);
                    }
                    else
                    {
                        return(localMatch);
                    }
                }

                // Prefer local over remote generally.
                return(localMatch ?? remoteMatch);
            }
        }