예제 #1
0
        private bool TryUpdate(IEnumerable <IPackage> dependents, ConflictResult conflictResult, IPackage package, out IEnumerable <IPackage> incompatiblePackages)
        {
            // Key dependents by id so we can look up the old package later
            var dependentsLookup   = dependents.ToDictionary(d => d.Id, StringComparer.OrdinalIgnoreCase);
            var compatiblePackages = new Dictionary <IPackage, IPackage>();

            // Initialize each compatible package to null
            foreach (var dependent in dependents)
            {
                compatiblePackages[dependent] = null;
            }

            // Get compatible packages in one batch so we don't have to make requests for each one
            var packages = from p in SourceRepository.FindCompatiblePackages(ConstraintProvider, dependentsLookup.Keys, package, TargetFramework, AllowPrereleaseVersions)
                           group p by p.Id into g
                           let oldPackage = dependentsLookup[g.Key]
                                            select new
            {
                OldPackage = oldPackage,
                NewPackage = g.Where(p => p.Version > oldPackage.Version)
                             .OrderBy(p => p.Version)
                             .ResolveSafeVersion()
            };

            foreach (var p in packages)
            {
                compatiblePackages[p.OldPackage] = p.NewPackage;
            }

            // Get all packages that have an incompatibility with the specified package i.e.
            // We couldn't find a version in the repository that works with the specified package.
            incompatiblePackages = compatiblePackages.Where(p => p.Value == null)
                                   .Select(p => p.Key);

            if (incompatiblePackages.Any())
            {
                return(false);
            }

            IPackageConstraintProvider currentConstraintProvider = ConstraintProvider;

            try
            {
                // Add a constraint for the incoming package so we don't try to update it by mistake.
                // Scenario:
                // A 1.0 -> B [1.0]
                // B 1.0.1, B 1.5, B 2.0
                // A 2.0 -> B (any version)
                // We have A 1.0 and B 1.0 installed. When trying to update to B 1.0.1, we'll end up trying
                // to find a version of A that works with B 1.0.1. The version in the above case is A 2.0.
                // When we go to install A 2.0 we need to make sure that when we resolve it's dependencies that we stay within bounds
                // i.e. when we resolve B for A 2.0 we want to keep the B 1.0.1 we've already chosen instead of trying to grab
                // B 1.5 or B 2.0. In order to achieve this, we add a constraint for version of B 1.0.1 so we stay within those bounds for B.

                // Respect all existing constraints plus an additional one that we specify based on the incoming package
                var constraintProvider = new DefaultConstraintProvider();
                constraintProvider.AddConstraint(package.Id, new VersionSpec(package.Version));
                ConstraintProvider = new AggregateConstraintProvider(ConstraintProvider, constraintProvider);

                // Mark the incoming package as visited so that we don't try walking the graph again
                Marker.MarkVisited(package);

                var failedPackages = new List <IPackage>();
                // Update each of the existing packages to more compatible one
                foreach (var pair in compatiblePackages)
                {
                    try
                    {
                        // Remove the old package
                        Uninstall(pair.Key, conflictResult.DependentsResolver, conflictResult.Repository);

                        // Install the new package
                        Walk(pair.Value);
                    }
                    catch
                    {
                        // If we failed to update this package (most likely because of a conflict further up the dependency chain)
                        // we keep track of it so we can report an error about the top level package.
                        failedPackages.Add(pair.Key);
                    }
                }

                incompatiblePackages = failedPackages;

                return(!incompatiblePackages.Any());
            }
            finally
            {
                // Restore the current constraint provider
                ConstraintProvider = currentConstraintProvider;

                // Mark the package as processing again
                Marker.MarkProcessing(package);
            }
        }