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); }
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, 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))); }
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 }, }); }
/// <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); }
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)); }
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)); }
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); } }
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); }
public GraphEdge(GraphEdge <TItem> outerEdge, GraphItem <TItem> item, LibraryDependency edge) { OuterEdge = outerEdge; Item = item; Edge = edge; }
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); } }