private static IEnumerable <ResolverPackage> FindCircularDependency(ResolverPackage package, IEnumerable <string> parents, IEnumerable <ResolverPackage> solution) { // avoid checking depths beyond 20 packages deep if (parents.Count() < 20 && package != null && !package.Absent && package.Dependencies.Any()) { // walk the dependencies foreach (var dependency in package.Dependencies.OrderBy(d => d.Id, StringComparer.OrdinalIgnoreCase)) { var dependencyPackage = solution.FirstOrDefault(solutionPackage => StringComparer.OrdinalIgnoreCase.Equals(solutionPackage.Id, dependency.Id)); if (parents.Contains(dependency.Id, StringComparer.OrdinalIgnoreCase)) { // loop detected return(new ResolverPackage[] { package, dependencyPackage }); } // recurse on dependencies var result = FindCircularDependency(dependencyPackage, parents.Concat(new string[] { package.Id }), solution); if (result.Any()) { return((new ResolverPackage[] { package }).Concat(result)); } } } // end of the walk return(Enumerable.Empty <ResolverPackage>()); }
/// <summary> /// Ex: PackageA (> 1.0.0) /// </summary> private static string FormatDependencyConstraint(ResolverPackage package, string dependencyId) { // The range may not exist, or may inclue all versions. For this reason we trim the string afterwards to remove extra spaces due to empty ranges var range = package.FindDependencyRange(dependencyId); var dependencyString = String.Format(CultureInfo.InvariantCulture, "{0} {1}", dependencyId, range == null ? string.Empty : range.ToNonSnapshotRange().PrettyPrint()).Trim(); // A 1.0.0 dependency: B (= 1.5) return($"'{package.Id} {package.Version.ToNormalizedString()} {Strings.DependencyConstraint}: {dependencyString}'"); }
private static IEnumerable <PackageDependency> GetBrokenDependencies(ResolverPackage package, IEnumerable <ResolverPackage> packages) { foreach (var dependency in package.Dependencies) { var target = packages.FirstOrDefault(targetPackage => StringComparer.OrdinalIgnoreCase.Equals(targetPackage.Id, dependency.Id)); if (!IsDependencySatisfied(dependency, target)) { yield return(dependency); } } yield break; }
private static List <ResolverPackage> FindCircularDependency(ResolverPackage package, Dictionary <string, ResolverPackage> packageLookUp, HashSet <ResolverPackage> visitedPackages) { // avoid checking depths beyond 20 packages deep if (package != null && !package.Absent && package.Dependencies.Any()) { var queue = new Queue <QueueNode>(); // added the first initial node to queue var node = new QueueNode(package, new List <ResolverPackage>()); queue.Enqueue(node); // BFS traversal to traverse through all the packages to find out circular dependency while (queue.Count > 0) { var source = queue.Dequeue(); // access parent packages list and add current package as well var parentPackages = new List <ResolverPackage>(source.ParentPackages); parentPackages.Add(source.Package); // walk the dependencies foreach (var dependency in source.Package.Dependencies.OrderBy(d => d.Id, StringComparer.OrdinalIgnoreCase)) { var dependencyPackage = packageLookUp[dependency.Id]; // If already visited, then it means it doesn't have any circular dependency so we can avoid processing this node again if (!visitedPackages.Contains(dependencyPackage)) { if (parentPackages.Contains(dependencyPackage)) { // circular dependency detected parentPackages.Add(dependencyPackage); return(parentPackages); } // add child node to Queue to process further var newQNode = new QueueNode(dependencyPackage, parentPackages); queue.Enqueue(newQNode); } } } } // add processed packages to local cache visitedPackages.Add(package); // end of the walk return(new List <ResolverPackage>()); }
/// <summary> /// Check if two packages can exist in the same solution. /// This is used by the resolver. /// </summary> internal static bool ShouldRejectPackagePair(ResolverPackage p1, ResolverPackage p2) { var p1ToP2Dependency = p1.FindDependencyRange(p2.Id); if (p1ToP2Dependency != null) { return(p2.Absent || !p1ToP2Dependency.Satisfies(p2.Version)); } var p2ToP1Dependency = p2.FindDependencyRange(p1.Id); if (p2ToP1Dependency != null) { return(p1.Absent || !p2ToP1Dependency.Satisfies(p1.Version)); } return(false); }
public QueueNode(ResolverPackage package, List <ResolverPackage> parentPackages) { Package = package; ParentPackages = parentPackages; }
private static bool IsDependencySatisfied(PackageDependency dependency, ResolverPackage package) { return(package != null && !package.Absent && (dependency.VersionRange == null || dependency.VersionRange.Satisfies(package.Version))); }
/// <summary> /// This will try and get broken dependencies for target or it's dependencies WRT installed packages as well as all available packages to install /// </summary> /// <param name="package">target package</param> /// <param name="solution">last best known solution</param> /// <param name="availablePackages">all available packages from all sources</param> /// <returns>list of broken dependencies</returns> private static IEnumerable <PackageDependency> GetBrokenDependenciesWithInstalledPackages(ResolverPackage package, IEnumerable <ResolverPackage> solution, IEnumerable <PackageDependencyInfo> availablePackages, HashSet <string> visitedPackages) { if (visitedPackages.Contains(package.Id)) { yield break; } // BFS traversal of graph var queue = new Queue <ResolverPackage>(); queue.Enqueue(package); while (queue.Count > 0) { var node = queue.Dequeue(); foreach (var dependency in node.Dependencies) { var target = solution.FirstOrDefault(targetPackage => StringComparer.OrdinalIgnoreCase.Equals(targetPackage.Id, dependency.Id)); // true, if solution doesn't contain this dependency or // if neither solution package satisfy this dependency version nor available packages had any package which could satisfy this dependency. // This is required because our solution might not have selected the right version of this dependent package. if (target == null || (!IsDependencySatisfied(dependency, target) && !availablePackages.Where(p => StringComparer.OrdinalIgnoreCase.Equals(p.Id, dependency.Id)).Any(p => IsDependencySatisfied(dependency, p)))) { yield return(dependency); } if (target != null && visitedPackages.Add(dependency.Id)) { queue.Enqueue(target); } } } yield break; }