From e3015e5acfc42a981f27e750513a0246b352d86e Mon Sep 17 00:00:00 2001 From: Quoc Truong Date: Mon, 18 Jul 2016 14:14:59 -0700 Subject: [PATCH] Improve performance of installing packages with dependencies for nugetprovider and find-package --- .../NugetLightClient.cs | 25 +++++++++++---- .../Cmdlets/FindPackage.cs | 31 ++++++++++++++----- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.PackageManagement.NuGetProvider/NugetLightClient.cs b/src/Microsoft.PackageManagement.NuGetProvider/NugetLightClient.cs index 70c3fcdc99..5c4856e5e6 100644 --- a/src/Microsoft.PackageManagement.NuGetProvider/NugetLightClient.cs +++ b/src/Microsoft.PackageManagement.NuGetProvider/NugetLightClient.cs @@ -480,7 +480,7 @@ HashSet temporarilyMarked = new HashSet(new PackageItemComparer()); // checks that there are no dependency loop - hasDependencyLoop = !DepthFirstVisit(packageItem, temporarilyMarked, permanentlyMarked, dependencyToBeInstalled, request); + hasDependencyLoop = !DepthFirstVisit(packageItem, temporarilyMarked, permanentlyMarked, dependencyToBeInstalled, new HashSet(), request); if (!hasDependencyLoop) { @@ -502,9 +502,10 @@ /// /// /// + /// /// /// - internal static bool DepthFirstVisit(PackageItem packageItem, HashSet temporarilyMarked, HashSet permanentlyMarked, List dependencyToBeInstalled, NuGetRequest request) + internal static bool DepthFirstVisit(PackageItem packageItem, HashSet temporarilyMarked, HashSet permanentlyMarked, List dependencyToBeInstalled, HashSet dependenciesProcessed, NuGetRequest request) { // dependency loop detected because the element is temporarily marked if (temporarilyMarked.Contains(packageItem)) @@ -523,9 +524,9 @@ temporarilyMarked.Add(packageItem); // Visit the dependency - foreach (var dependency in GetPackageDependenciesHelper(packageItem, request)) + foreach (var dependency in GetPackageDependenciesHelper(packageItem, dependenciesProcessed, request)) { - if (!DepthFirstVisit(dependency, temporarilyMarked, permanentlyMarked, dependencyToBeInstalled, request)) + if (!DepthFirstVisit(dependency, temporarilyMarked, permanentlyMarked, dependencyToBeInstalled, dependenciesProcessed, request)) { // if dfs returns false then we have encountered a loop return false; @@ -549,8 +550,9 @@ /// Returns the package dependencies of packageItem. We only return the dependencies that are not installed in the destination folder of request /// /// + /// /// - private static IEnumerable GetPackageDependenciesHelper(PackageItem packageItem, NuGetRequest request) + private static IEnumerable GetPackageDependenciesHelper(PackageItem packageItem, HashSet depedenciesToProcessed, NuGetRequest request) { if (packageItem.Package.DependencySetList == null) { @@ -567,6 +569,13 @@ foreach (var dep in depSet.Dependencies) { + var depKey = string.Format(CultureInfo.InvariantCulture, "{0}!#!{1}", dep.Id, dep.DependencyVersion.ToStringSafe()); + + if (depedenciesToProcessed.Contains(depKey)) + { + continue; + } + // Get the min dependencies version string minVersion = dep.DependencyVersion.MinVersion.ToStringSafe(); @@ -617,6 +626,8 @@ if (installed) { + // already processed this so don't need to do this next time + depedenciesToProcessed.Add(dep.Id); request.Verbose(String.Format(CultureInfo.CurrentCulture, Messages.AlreadyInstalled, dep.Id)); // already have a dependency so move on continue; @@ -634,7 +645,9 @@ } // Get the package that is the latest version - yield return dependentPackageItem.OrderByDescending(each => each.Version).FirstOrDefault(); + yield return dependentPackageItem.OrderByDescending(each => each.Version).FirstOrDefault(); + + depedenciesToProcessed.Add(depKey); } } } diff --git a/src/Microsoft.PowerShell.PackageManagement/Cmdlets/FindPackage.cs b/src/Microsoft.PowerShell.PackageManagement/Cmdlets/FindPackage.cs index c85a89a622..5567feb865 100644 --- a/src/Microsoft.PowerShell.PackageManagement/Cmdlets/FindPackage.cs +++ b/src/Microsoft.PowerShell.PackageManagement/Cmdlets/FindPackage.cs @@ -57,7 +57,12 @@ namespace Microsoft.PowerShell.PackageManagement.Cmdlets { return true; } - protected override void ProcessPackage(PackageProvider provider, IEnumerable searchKey, SoftwareIdentity package) { + protected override void ProcessPackage(PackageProvider provider, IEnumerable searchKey, SoftwareIdentity package) + { + ProcessPackage(provider, searchKey, package, IncludeDependencies ? new HashSet() : null); + } + + private void ProcessPackage(PackageProvider provider, IEnumerable searchKey, SoftwareIdentity package, HashSet processedDependencies) { try { @@ -71,14 +76,24 @@ namespace Microsoft.PowerShell.PackageManagement.Cmdlets { foreach (var dep in package.Dependencies) { // note: future work may be needed if the package sources currently selected by the user don't // contain the dependencies. - var dependendcies = PackageManagementService.FindPackageByCanonicalId(dep, this); - var depPkg = dependendcies.OrderByDescending(pp => pp, SoftwareIdentityVersionComparer.Instance).FirstOrDefault(); - if (depPkg == null) { - missingDependencies.Add(dep); - Warning(Constants.Messages.UnableToFindDependencyPackage, dep); - } else { - ProcessPackage(depPkg.Provider, searchKey.Select(each => each + depPkg.Name).ToArray(), depPkg); + // this dep is not processed yet + if (!processedDependencies.Contains(dep)) + { + var dependendcies = PackageManagementService.FindPackageByCanonicalId(dep, this); + var depPkg = dependendcies.OrderByDescending(pp => pp, SoftwareIdentityVersionComparer.Instance).FirstOrDefault(); + + processedDependencies.Add(dep); + + if (depPkg == null) + { + missingDependencies.Add(dep); + Warning(Constants.Messages.UnableToFindDependencyPackage, dep); + } + else + { + ProcessPackage(depPkg.Provider, searchKey.Select(each => each + depPkg.Name).ToArray(), depPkg, processedDependencies); + } } } if (missingDependencies.Any()) {