Example #1
0
        private static void WalkTreeCheckCycleAndNearestWins(CyclesAndDowngrades context, GraphNode <RemoteResolveResult> node)
        {
            // Cycle:
            //
            // A -> B -> A (cycle)
            //
            // Downgrade:
            //
            // A -> B -> C -> D 2.0 (downgrade)
            //        -> D 1.0
            //
            // Potential downgrades that turns out to not be downgrades:
            //
            // 1. This should never happen in practice since B would have never been valid to begin with.
            //
            //    A -> B -> C -> D 2.0
            //           -> D 1.0
            //      -> D 2.0
            //
            // 2. This occurs if none of the sources have version C 1.0 so C 1.0 is bumped up to C 2.0.
            //
            //   A -> B -> C 2.0
            //     -> C 1.0

            var cycles            = context.Cycles;
            var workingDowngrades = context.Downgrades;

            if (node.Disposition == Disposition.Cycle)
            {
                cycles.Add(node);

                // Remove this node from the tree so the nothing else evaluates this.
                // This is ok since we have a parent pointer and we can still print the path
                node.OuterNode.InnerNodes.Remove(node);

                return;
            }

            if (node.Disposition != Disposition.PotentiallyDowngraded)
            {
                return;
            }

            // REVIEW: This could probably be done in a single pass where we keep track
            // of what is nearer as we walk down the graph (BFS)
            for (var n = node.OuterNode; n != null; n = n.OuterNode)
            {
                var innerNodes = n.InnerNodes;
                var count      = innerNodes.Count;
                for (var i = 0; i < count; i++)
                {
                    var sideNode = innerNodes[i];
                    if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name))
                    {
                        // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference
                        if (sideNode.Key.VersionRange != null &&
                            node.Key.VersionRange != null &&
                            !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange))
                        {
                            // Is the resolved version actually within node's version range? This happen if there
                            // was a different request for a lower version of the library than this version range
                            // allows but no matching library was found, so the library is bumped up into this
                            // version range.
                            var resolvedVersion = sideNode?.Item?.Data?.Match?.Library?.Version;
                            if (resolvedVersion != null && node.Key.VersionRange.Satisfies(resolvedVersion))
                            {
                                continue;
                            }

                            workingDowngrades[node] = sideNode;
                        }
                        else
                        {
                            workingDowngrades.Remove(node);
                        }
                    }
                }
            }

            // Remove this node from the tree so the nothing else evaluates this.
            // This is ok since we have a parent pointer and we can still print the path
            node.OuterNode.InnerNodes.Remove(node);
        }
Example #2
0
        private static void CheckCycleAndNearestWins <TItem>(this GraphNode <TItem> root,
                                                             List <DowngradeResult <TItem> > downgrades,
                                                             List <GraphNode <TItem> > cycles)
        {
            // Cycle

            // A -> B -> A (cycle)

            // Downgrade

            // A -> B -> C -> D 2.0 (downgrage)
            //        -> D 1.0

            // Potential downgrade that turns out to not downgrade
            // This should never happen in practice since B would have never been valid to begin with.

            // A -> B -> C -> D 2.0
            //        -> D 1.0
            //   -> D 2.0

            var workingDowngrades = new Dictionary <GraphNode <TItem>, GraphNode <TItem> >();

            root.ForEach(node =>
            {
                if (node.Disposition == Disposition.Cycle)
                {
                    cycles.Add(node);

                    // Remove this node from the tree so the nothing else evaluates this.
                    // This is ok since we have a parent pointer and we can still print the path
                    node.OuterNode.InnerNodes.Remove(node);

                    return;
                }

                if (node.Disposition != Disposition.PotentiallyDowngraded)
                {
                    return;
                }

                // REVIEW: This could probably be done in a single pass where we keep track
                // of what is nearer as we walk down the graph (BFS)
                for (var n = node.OuterNode; n != null; n = n.OuterNode)
                {
                    foreach (var sideNode in n.InnerNodes)
                    {
                        if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name))
                        {
                            // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference
                            if (sideNode.Key.VersionRange != null &&
                                node.Key.VersionRange != null &&
                                !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange))
                            {
                                workingDowngrades[node] = sideNode;
                            }
                            else
                            {
                                workingDowngrades.Remove(node);
                            }
                        }
                    }
                }

                // Remove this node from the tree so the nothing else evaluates this.
                // This is ok since we have a parent pointer and we can still print the path
                node.OuterNode.InnerNodes.Remove(node);
            });

            downgrades.AddRange(workingDowngrades.Select(p => new DowngradeResult <TItem>
            {
                DowngradedFrom = p.Key,
                DowngradedTo   = p.Value
            }));
        }
Example #3
0
        private Task<RestoreTargetGraph[]> WalkRuntimeDependencies(LibraryRange projectRange, RestoreTargetGraph graph, RuntimeGraph projectRuntimeGraph, RemoteDependencyWalker walker, RemoteWalkContext context, NuGetv3LocalRepository localRepository)
        {
            // Load runtime specs
            _log.LogVerbose("Scanning packages for runtime.json files...");
            var runtimeGraph = projectRuntimeGraph;
            graph.Graph.ForEach(node =>
                {
                    var match = node?.Item?.Data?.Match;
                    if (match == null)
                    {
                        return;
                    }

                    // Locate the package in the local repository
                    var package = localRepository.FindPackagesById(match.Library.Name).FirstOrDefault(p => p.Version == match.Library.Version);
                    if (package != null)
                    {
                        var nextGraph = LoadRuntimeGraph(package);
                        if (nextGraph != null)
                        {
                            _log.LogVerbose($"Merging in runtimes defined in {match.Library}");
                            runtimeGraph = RuntimeGraph.Merge(runtimeGraph, nextGraph);
                        }
                    }
                });

            var resultGraphs = new List<Task<RestoreTargetGraph>>();
            foreach (var runtimeName in projectRuntimeGraph.Runtimes.Keys)
            {
                _log.LogInformation($"Restoring packages for {graph.Framework} on {runtimeName}");
                resultGraphs.Add(WalkDependencies(projectRange, graph.Framework, runtimeName, runtimeGraph, walker, context));
            }

            return Task.WhenAll(resultGraphs);
        }
Example #4
0
        private async Task<RestoreTargetGraph> WalkDependencies(LibraryRange projectRange, NuGetFramework framework, string runtimeIdentifier, RuntimeGraph runtimeGraph, RemoteDependencyWalker walker, RemoteWalkContext context)
        {
            _log.LogInformation($"Restoring packages for {framework}");
            var graph = await walker.WalkAsync(
                projectRange,
                framework,
                runtimeIdentifier,
                runtimeGraph);

            // Resolve conflicts
            _log.LogVerbose($"Resolving Conflicts for {framework}");
            var inConflict = !graph.TryResolveConflicts();

            // Flatten and create the RestoreTargetGraph to hold the packages
            return RestoreTargetGraph.Create(inConflict, framework, runtimeIdentifier, runtimeGraph, graph, context, _log);
        }
Example #5
0
 private Task<RestoreTargetGraph> WalkDependencies(LibraryRange projectRange, NuGetFramework framework, RemoteDependencyWalker walker, RemoteWalkContext context)
 {
     return WalkDependencies(projectRange, framework, null, RuntimeGraph.Empty, walker, context);
 }
Example #6
0
        public async Task<RestoreResult> ExecuteAsync(RestoreRequest request)
        {
            if (request.Project.TargetFrameworks.Count == 0)
            {
                _log.LogError("The project does not specify any target frameworks!");
                return new RestoreResult(success: false, restoreGraphs: Enumerable.Empty<RestoreTargetGraph>());
            }

            var projectLockFilePath = string.IsNullOrEmpty(request.LockFilePath) ?
                Path.Combine(request.Project.BaseDirectory, LockFileFormat.LockFileName) :
                request.LockFilePath;

            _log.LogInformation($"Restoring packages for '{request.Project.FilePath}'");

            _log.LogWarning("TODO: Read and use lock file");

            // Load repositories
            var projectResolver = new PackageSpecResolver(request.Project);
            var nugetRepository = Repository.Factory.GetCoreV3(request.PackagesDirectory);

            var context = new RemoteWalkContext();

            ExternalProjectReference exterenalProjectReference = null;
            if (request.ExternalProjects.Any())
            {
                exterenalProjectReference = new ExternalProjectReference(
                    request.Project.Name, 
                    request.Project.FilePath, 
                    request.ExternalProjects.Select(p => p.Name));
            }

            context.ProjectLibraryProviders.Add(
                new LocalDependencyProvider(
                    new PackageSpecReferenceDependencyProvider(projectResolver, exterenalProjectReference)));

            if (request.ExternalProjects != null)
            {
                context.ProjectLibraryProviders.Add(
                    new LocalDependencyProvider(
                        new ExternalProjectReferenceDependencyProvider(request.ExternalProjects)));
            }

            context.LocalLibraryProviders.Add(
                new SourceRepositoryDependencyProvider(nugetRepository, _log));

            foreach (var provider in request.Sources.Select(s => CreateProviderFromSource(s, request.NoCache)))
            {
                context.RemoteLibraryProviders.Add(provider);
            }

            var remoteWalker = new RemoteDependencyWalker(context);

            var projectRange = new LibraryRange()
            {
                Name = request.Project.Name,
                VersionRange = new VersionRange(request.Project.Version),
                TypeConstraint = LibraryTypes.Project
            };

            // Resolve dependency graphs
            var frameworks = request.Project.TargetFrameworks.Select(f => f.FrameworkName).ToList();
            var graphs = new List<RestoreTargetGraph>();
            var frameworkTasks = new List<Task<RestoreTargetGraph>>();

            foreach (var framework in frameworks)
            {
                frameworkTasks.Add(WalkDependencies(projectRange, framework, remoteWalker, context));
            }

            graphs.AddRange(await Task.WhenAll(frameworkTasks));

            if (graphs.Any(g => g.InConflict))
            {
                _log.LogError("Failed to resolve conflicts");
                return new RestoreResult(success: false, restoreGraphs: graphs);
            }

            // Install the runtime-agnostic packages
            var allInstalledPackages = new HashSet<LibraryIdentity>();
            var localRepository = new NuGetv3LocalRepository(request.PackagesDirectory, checkPackageIdCase: false);
            await InstallPackages(graphs, request.PackagesDirectory, allInstalledPackages, request.MaxDegreeOfConcurrency);

            // Resolve runtime dependencies
            var runtimeGraphs = new List<RestoreTargetGraph>();
            if (request.Project.RuntimeGraph.Runtimes.Count > 0)
            {
                var runtimeTasks = new List<Task<RestoreTargetGraph[]>>();
                foreach (var graph in graphs)
                {
                    runtimeTasks.Add(WalkRuntimeDependencies(projectRange, graph, request.Project.RuntimeGraph, remoteWalker, context, localRepository));
                }

                foreach (var runtimeSpecificGraphs in await Task.WhenAll(runtimeTasks))
                {
                    runtimeGraphs.AddRange(runtimeSpecificGraphs);
                }

                graphs.AddRange(runtimeGraphs);

                if (runtimeGraphs.Any(g => g.InConflict))
                {
                    _log.LogError("Failed to resolve conflicts");
                    return new RestoreResult(success: false, restoreGraphs: graphs);
                }

                // Install runtime-specific packages
                await InstallPackages(runtimeGraphs, request.PackagesDirectory, allInstalledPackages, request.MaxDegreeOfConcurrency);
            }
            else
            {
                _log.LogVerbose("Skipping runtime dependency walk, no runtimes defined in project.json");
            }

            // Build the lock file
            var repository = new NuGetv3LocalRepository(request.PackagesDirectory, checkPackageIdCase: false);
            var lockFile = CreateLockFile(request.Project, graphs, repository);
            var lockFileFormat = new LockFileFormat();
            lockFileFormat.Write(projectLockFilePath, lockFile);

            // Generate Targets/Props files
            WriteTargetsAndProps(request.Project, graphs, repository);

            return new RestoreResult(true, graphs, lockFile);
        }