public async Task<GraphNode> CreateGraphNode(RestoreContext context, LibraryRange libraryRange, Func<string, bool> predicate) { var dependencies = new List<LibraryDependency>(); if (context.RuntimeDependencies != null) { DependencySpec dependencyMapping; if (context.RuntimeDependencies.TryGetValue(libraryRange.Name, out dependencyMapping)) { foreach (var runtimeDependency in dependencyMapping.Implementations.Values) { var libraryDependency = new LibraryDependency { LibraryRange = new LibraryRange(runtimeDependency.Name, frameworkReference: false) { VersionRange = VersionUtility.ParseVersionRange(runtimeDependency.Version) } }; // HACK(davidfowl): This is making runtime.json support package redirects if (libraryDependency.LibraryRange.Name == libraryRange.Name) { // It's replacing the current version, we need to override rather than adding a (potentially circular) dependency if (libraryRange.VersionRange != null && libraryDependency.LibraryRange.VersionRange != null && libraryRange.VersionRange.MinVersion < libraryDependency.LibraryRange.VersionRange.MinVersion) { libraryRange = libraryDependency.LibraryRange; } } else { // Otherwise it's a dependency of this node dependencies.Add(libraryDependency); } } } } var node = new GraphNode { LibraryRange = libraryRange, Item = await FindLibraryCached(context, libraryRange), }; if (node.Item != null) { if (node.LibraryRange.VersionRange != null && node.LibraryRange.VersionRange.VersionFloatBehavior != SemanticVersionFloatBehavior.None) { lock (context.GraphItemCache) { if (!context.GraphItemCache.ContainsKey(node.LibraryRange)) { context.GraphItemCache[node.LibraryRange] = Task.FromResult(node.Item); } } } dependencies.AddRange(node.Item.Dependencies); var tasks = new List<Task<GraphNode>>(); foreach (var dependency in dependencies) { if (predicate(dependency.Name)) { tasks.Add(CreateGraphNode(context, dependency.LibraryRange, ChainPredicate(predicate, node.Item, dependency))); } } while (tasks.Any()) { var task = await Task.WhenAny(tasks); tasks.Remove(task); var dependency = await task; node.Dependencies.Add(dependency); } } return node; }
public async Task<GraphNode> CreateGraphNode(RestoreContext context, LibraryRange libraryRange, Func<string, bool> predicate) { var sw = new Stopwatch(); sw.Start(); var node = new GraphNode { LibraryRange = libraryRange, Item = await FindLibraryCached(context, libraryRange), }; if (node.Item != null) { if (node.LibraryRange.VersionRange != null && node.LibraryRange.VersionRange.VersionFloatBehavior != SemanticVersionFloatBehavior.None) { lock (context.GraphItemCache) { if (!context.GraphItemCache.ContainsKey(node.LibraryRange)) { context.GraphItemCache[node.LibraryRange] = Task.FromResult(node.Item); } } } var tasks = new List<Task<GraphNode>>(); var dependencies = node.Item.Dependencies ?? Enumerable.Empty<LibraryDependency>(); foreach (var dependency in dependencies) { if (predicate(dependency.Name)) { tasks.Add(CreateGraphNode(context, dependency.LibraryRange, ChainPredicate(predicate, node.Item, dependency))); if (context.RuntimeSpecs != null) { foreach (var runtimeSpec in context.RuntimeSpecs) { DependencySpec dependencyMapping; if (runtimeSpec.Dependencies.TryGetValue(dependency.Name, out dependencyMapping)) { foreach (var dependencyImplementation in dependencyMapping.Implementations.Values) { tasks.Add(CreateGraphNode( context, new LibraryRange(dependencyImplementation.Name, frameworkReference: false) { VersionRange = VersionUtility.ParseVersionRange(dependencyImplementation.Version) }, ChainPredicate(predicate, node.Item, dependency))); } break; } } } } } while (tasks.Any()) { var task = await Task.WhenAny(tasks); tasks.Remove(task); var dependency = await task; node.Dependencies.Add(dependency); } } return node; }
void ForEach <TState>(GraphNode node, TState state, Func <GraphNode, TState, TState> callback) { var childState = callback(node, state); ForEach(node.Dependencies, childState, callback); }
private void Reduce(GraphNode root) { var patience = 1000; var incomplete = true; while (incomplete && --patience != 0) { var tracker = new Tracker(); // track non-rejected, apply rejection recursively ForEach(root, true, (node, state) => { if (!state || node.Disposition == GraphNode.DispositionType.Rejected) { node.Disposition = GraphNode.DispositionType.Rejected; } else { var lib = node?.Item?.Match?.Library; if (lib != null) { tracker.Track( lib.Name, lib.Version); } } return(node.Disposition != GraphNode.DispositionType.Rejected); }); // mark items under disputed nodes as ambiguous ForEach(root, "Walking", (node, state) => { if (node.Disposition == GraphNode.DispositionType.Rejected) { return("Rejected"); } var lib = node?.Item?.Match?.Library; if (lib == null) { return(state); } if (state == "Walking" && tracker.IsDisputed(node.Item.Match.Library.Name)) { return("Disputed"); } if (state == "Disputed") { tracker.MarkAmbiguous(node.Item.Match.Library.Name); } return(state); }); // accept or reject nodes that are acceptable and not ambiguous ForEach(root, true, (node, state) => { if (!state || node.Disposition == GraphNode.DispositionType.Rejected || tracker.IsAmbiguous(node?.Item?.Match?.Library?.Name)) { return(false); } if (node.Disposition == GraphNode.DispositionType.Acceptable) { var isBestVersion = tracker.IsBestVersion( node?.Item?.Match?.Library?.Name, node?.Item?.Match?.Library?.Version); node.Disposition = isBestVersion ? GraphNode.DispositionType.Accepted : GraphNode.DispositionType.Rejected; } return(node.Disposition == GraphNode.DispositionType.Accepted); }); incomplete = false; ForEach(root, node => incomplete |= node.Disposition == GraphNode.DispositionType.Acceptable); } }
void ForEach(GraphNode node, Action <GraphNode> callback) { callback(node); ForEach(node.Dependencies, callback); }