protected override void OnBeforePackageWalk(IPackage package) { ConflictResult conflict = this.GetConflict(package); if ((conflict != null) && !PackageEqualityComparer.IdAndVersion.Equals(package, conflict.Package)) { IEnumerable <IPackage> source = from dependentPackage in this.GetDependents(conflict) let dependency = dependentPackage.FindDependency(package.Id, this.TargetFramework) where (dependency != null) && !dependency.VersionSpec.Satisfies(package.Version) select dependentPackage; if (source.Any <IPackage>() && !this.TryUpdate(source, conflict, package, out source)) { throw CreatePackageConflictException(package, conflict.Package, source); } if (this._isDowngrade || (package.Version >= conflict.Package.Version)) { this.Uninstall(conflict.Package, conflict.DependentsResolver, conflict.Repository); } else { object[] args = new object[] { package.Id }; throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, NuGetResources.NewerVersionAlreadyReferenced, args)); } } }
private IEnumerable <IPackage> GetDependents(ConflictResult conflict) { // Skip all dependents that are marked for uninstall IEnumerable <IPackage> packages = _operations.GetPackages(PackageAction.Uninstall); return(conflict.DependentsResolver.GetDependents(conflict.Package) .Except(packages, PackageEqualityComparer.IdAndVersion)); }
protected override ConflictResult GetConflict(IPackage package) { ConflictResult conflict = base.GetConflict(package); if (conflict == null) { IPackage conflictingPackage = base.Repository.FindPackage(package.Id); if (conflictingPackage != null) { conflict = new ConflictResult(conflictingPackage, base.Repository, this._dependentsResolver); } } return(conflict); }
protected override ConflictResult GetConflict(IPackage package) { // For project installs we first try to base behavior (using the live graph) // then we look for conflicts for packages installed into the current project. ConflictResult result = base.GetConflict(package); if (result == null) { IPackage existingPackage = Repository.FindPackage(package.Id); if (existingPackage != null) { result = new ConflictResult(existingPackage, Repository, _dependentsResolver); } } return(result); }
protected override ConflictResult GetConflict(IPackage package) { // For project installs we first try to base behavior (using the live graph) // then we look for conflicts for packages installed into the current project. ConflictResult result = base.GetConflict(package); if (result == null) { IPackage existingPackage = Repository.FindPackage(package.Id); if (existingPackage != null) { result = new ConflictResult(existingPackage, Repository, _dependentsResolver); } } return result; }
protected override void OnBeforePackageWalk(IPackage package) { ConflictResult conflictResult = GetConflict(package); if (conflictResult == null) { return; } // If the conflicting package is the same as the package being installed // then no-op if (PackageEqualityComparer.IdAndVersion.Equals(package, conflictResult.Package)) { return; } // First we get a list of dependents for the installed package. // Then we find the dependency in the foreach dependent that this installed package used to satisfy. // We then check if the resolved package also meets that dependency and if it doesn't it's added to the list // i.e. A1 -> C >= 1 // B1 -> C >= 1 // C2 -> [] // Given the above graph, if we upgrade from C1 to C2, we need to see if A and B can work with the new C var incompatiblePackages = from dependentPackage in GetDependents(conflictResult) let dependency = dependentPackage.FindDependency(package.Id, TargetFramework) where dependency != null && !dependency.VersionSpec.Satisfies(package.Version) select dependentPackage; // If there were incompatible packages that we failed to update then we throw an exception if (incompatiblePackages.Any() && !TryUpdate(incompatiblePackages, conflictResult, package, out incompatiblePackages)) { throw CreatePackageConflictException(package, conflictResult.Package, incompatiblePackages); } else if (package.Version < conflictResult.Package.Version) { // REVIEW: Should we have a flag to allow downgrading? throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, NuGetResources.NewerVersionAlreadyReferenced, package.Id)); } else if (package.Version > conflictResult.Package.Version) { Uninstall(conflictResult.Package, conflictResult.DependentsResolver, conflictResult.Repository); } }
private IEnumerable<IPackage> GetDependents(ConflictResult conflict) { // Skip all dependents that are marked for uninstall IEnumerable<IPackage> packages = _operations.GetPackages(PackageAction.Uninstall); return conflict.DependentsResolver.GetDependents(conflict.Package) .Except(packages, PackageEqualityComparer.IdAndVersion); }
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); } }
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); } }
private IEnumerable <IPackage> GetDependents(ConflictResult conflict) { IEnumerable <IPackage> packages = this._operations.GetPackages(PackageAction.Uninstall); return(conflict.DependentsResolver.GetDependents(conflict.Package).Except <IPackage>(packages, ((IEqualityComparer <IPackage>)PackageEqualityComparer.IdAndVersion))); }
private bool TryUpdate(IEnumerable <IPackage> dependents, ConflictResult conflictResult, IPackage package, out IEnumerable <IPackage> incompatiblePackages) { bool flag; Dictionary <string, IPackage> dependentsLookup = Enumerable.ToDictionary <IPackage, string>(dependents, d => d.Id, StringComparer.OrdinalIgnoreCase); Dictionary <IPackage, IPackage> dictionary = new Dictionary <IPackage, IPackage>(); foreach (IPackage package2 in dependents) { dictionary[package2] = null; } foreach (var type in from p in FindCompatiblePackages(this.DependencyResolver, this.ConstraintProvider, dependentsLookup.Keys, package, base.TargetFramework, this.AllowPrereleaseVersions) group p by p.Id into g let oldPackage = dependentsLookup[g.Key] select new { OldPackage = oldPackage, NewPackage = this.SelectDependency(from p in g where p.Version > oldPackage.Version orderby p.Version select p) }) { dictionary[type.OldPackage] = type.NewPackage; } incompatiblePackages = from p in dictionary where p.Value == null select p.Key; if (incompatiblePackages.Any <IPackage>()) { return(false); } IPackageConstraintProvider constraintProvider = this.ConstraintProvider; try { DefaultConstraintProvider provider2 = new DefaultConstraintProvider(); provider2.AddConstraint(package.Id, new VersionSpec(package.Version)); IPackageConstraintProvider[] constraintProviders = new IPackageConstraintProvider[] { this.ConstraintProvider, provider2 }; this.ConstraintProvider = new AggregateConstraintProvider(constraintProviders); base.Marker.MarkVisited(package); List <IPackage> list = new List <IPackage>(); foreach (KeyValuePair <IPackage, IPackage> pair in dictionary) { try { this.Uninstall(pair.Key, conflictResult.DependentsResolver, conflictResult.Repository); base.Walk(pair.Value); } catch { list.Add(pair.Key); } } incompatiblePackages = list; flag = !incompatiblePackages.Any <IPackage>(); } finally { this.ConstraintProvider = constraintProvider; base.Marker.MarkProcessing(package); } return(flag); }
protected override void OnBeforePackageWalk(IPackage package) { ConflictResult conflictResult = GetConflict(package.Id); if (conflictResult == null) { return; } // If the conflicting package is the same as the package being installed // then no-op if (PackageEqualityComparer.IdAndVersion.Equals(package, conflictResult.Package)) { return; } // First we get a list of dependents for the installed package. // Then we find the dependency in the foreach dependent that this installed package used to satisfy. // We then check if the resolved package also meets that dependency and if it doesn't it's added to the list // i.e A1 -> C >= 1 // B1 -> C >= 1 // C2 -> [] // Given the above graph, if we upgrade from C1 to C2, we need to see if A and B can work with the new C var dependents = from dependentPackage in GetDependents(conflictResult) where !IsDependencySatisfied(dependentPackage, package) select dependentPackage; if (dependents.Any()) { throw CreatePackageConflictException(package, conflictResult.Package, dependents); } else if (package.Version < conflictResult.Package.Version) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, NuGetResources.NewerVersionAlreadyReferenced, package.Id)); } else if (package.Version > conflictResult.Package.Version) { // If this package isn't part of the current graph (i.e. hasn't been visited yet) and // is marked for removal, then do nothing. This is so we don't get unnecessary duplicates. if (!Marker.Contains(conflictResult.Package) && _operations.Contains(conflictResult.Package, PackageAction.Uninstall)) { return; } // Uninstall the conflicting package. We set throw on conflicts to false since we've // already decided that there were no conflicts based on the above code. var resolver = new UninstallWalker(conflictResult.Repository, conflictResult.DependentsResolver, NullLogger.Instance, removeDependencies: !IgnoreDependencies, forceRemove: false) { ThrowOnConflicts = false }; foreach (var operation in resolver.ResolveOperations(conflictResult.Package)) { _operations.AddOperation(operation); } } }