| File: Resolution\UninstallResolver.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.PackageManagement\NuGet.PackageManagement.csproj (NuGet.PackageManagement) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #nullable disable using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using NuGet.Packaging.Core; namespace NuGet.PackageManagement { public static class UninstallResolver { public static IDictionary<PackageIdentity, HashSet<PackageIdentity>> GetPackageDependents(IEnumerable<PackageDependencyInfo> dependencyInfoEnumerable, IEnumerable<PackageIdentity> installedPackages, out IDictionary<PackageIdentity, HashSet<PackageIdentity>> dependenciesDict) { var dependentsDict = new Dictionary<PackageIdentity, HashSet<PackageIdentity>>(PackageIdentity.Comparer); dependenciesDict = new Dictionary<PackageIdentity, HashSet<PackageIdentity>>(PackageIdentity.Comparer); foreach (var dependencyInfo in dependencyInfoEnumerable) { var packageIdentity = new PackageIdentity(dependencyInfo.Id, dependencyInfo.Version); foreach (var dependency in dependencyInfo.Dependencies) { var dependencyPackageIdentity = installedPackages.FirstOrDefault(i => dependency.Id.Equals(i.Id, StringComparison.OrdinalIgnoreCase) && dependency.VersionRange.Satisfies(i.Version)); if (dependencyPackageIdentity != null) { // Update the package dependents dictionary HashSet<PackageIdentity> dependents; if (!dependentsDict.TryGetValue(dependencyPackageIdentity, out dependents)) { dependentsDict[dependencyPackageIdentity] = dependents = new HashSet<PackageIdentity>(PackageIdentity.Comparer); } dependents.Add(packageIdentity); // Update the package dependencies dictionary HashSet<PackageIdentity> dependencies; if (!dependenciesDict.TryGetValue(packageIdentity, out dependencies)) { dependenciesDict[packageIdentity] = dependencies = new HashSet<PackageIdentity>(PackageIdentity.Comparer); } dependencies.Add(dependencyPackageIdentity); } } } return dependentsDict; } public static ICollection<PackageIdentity> GetPackagesToBeUninstalled(PackageIdentity packageIdentity, IEnumerable<PackageDependencyInfo> dependencyInfoEnumerable, IEnumerable<PackageIdentity> installedPackages, UninstallationContext uninstallationContext) { IDictionary<PackageIdentity, HashSet<PackageIdentity>> dependenciesDictionary; var dependentsDictionary = GetPackageDependents(dependencyInfoEnumerable, installedPackages, out dependenciesDictionary); var packagesMarkedForUninstall = MarkPackagesToBeUninstalled(packageIdentity, dependenciesDictionary, uninstallationContext); CheckIfPackageCanBeUninstalled(packageIdentity, dependenciesDictionary, dependentsDictionary, uninstallationContext, packagesMarkedForUninstall); return packagesMarkedForUninstall; } private static void CheckIfPackageCanBeUninstalled(PackageIdentity packageIdentity, IDictionary<PackageIdentity, HashSet<PackageIdentity>> dependenciesDict, IDictionary<PackageIdentity, HashSet<PackageIdentity>> dependentsDict, UninstallationContext uninstallationContext, HashSet<PackageIdentity> packagesMarkedForUninstall) { HashSet<PackageIdentity> dependents; if (dependentsDict.TryGetValue(packageIdentity, out dependents) && dependents != null) { if (!uninstallationContext.ForceRemove) { var unMarkedDependents = dependents.Where(d => !packagesMarkedForUninstall.Contains(d)).ToList(); if (unMarkedDependents.Count > 0) { throw CreatePackageHasDependentsException(packageIdentity, unMarkedDependents); } } } HashSet<PackageIdentity> dependencies; if (uninstallationContext.RemoveDependencies && dependenciesDict.TryGetValue(packageIdentity, out dependencies) && dependencies != null) { foreach (var dependency in dependencies) { CheckIfPackageCanBeUninstalled(dependency, dependenciesDict, dependentsDict, uninstallationContext, packagesMarkedForUninstall); } } } private static HashSet<PackageIdentity> MarkPackagesToBeUninstalled(PackageIdentity packageIdentity, IDictionary<PackageIdentity, HashSet<PackageIdentity>> dependenciesDict, UninstallationContext uninstallationContext) { var breathFirstSearchQueue = new Queue<PackageIdentity>(); var markedPackages = new List<PackageIdentity>(); breathFirstSearchQueue.Enqueue(packageIdentity); while (breathFirstSearchQueue.Count > 0) { var headPackage = breathFirstSearchQueue.Dequeue(); markedPackages.Add(headPackage); HashSet<PackageIdentity> dependencies; if (uninstallationContext.RemoveDependencies && dependenciesDict.TryGetValue(headPackage, out dependencies) && dependencies != null) { foreach (var dependency in dependencies) { if (markedPackages.Contains(dependency)) { // Put it back at the end markedPackages.Remove(dependency); markedPackages.Add(dependency); } else { breathFirstSearchQueue.Enqueue(dependency); } } } } return new HashSet<PackageIdentity>(markedPackages, PackageIdentity.Comparer); } private static InvalidOperationException CreatePackageHasDependentsException(PackageIdentity packageIdentity, List<PackageIdentity> packageDependents) { if (packageDependents.Count == 1) { return new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.PackageHasDependent, packageIdentity, packageDependents[0])); } return new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.PackageHasDependents, packageIdentity, string.Join(", ", packageDependents.Select(d => d.ToString())))); } } }