/// <summary> /// Dependency walk /// </summary> public override async Task <IEnumerable <PackageDependencyInfo> > ResolvePackages(IEnumerable <PackageIdentity> packages, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token) { if (projectFramework == null) { throw new ArgumentNullException("projectFramework"); } if (packages == null) { throw new ArgumentNullException("packages"); } HashSet <PackageDependencyInfo> results = new HashSet <PackageDependencyInfo>(PackageIdentityComparer.Default); foreach (PackageIdentity package in packages) { VersionRange range = package.HasVersion ? new VersionRange(package.Version, true, package.Version, true) : VersionRange.All; var target = new NuGet.Packaging.Core.PackageDependency(package.Id, range); results.UnionWith(await Seek(target, projectFramework, includePrerelease, Enumerable.Empty <string>(), token)); } // pre-release should not be in the final set, but filter again just to be sure return(results.Where(e => includePrerelease || !e.Version.IsPrerelease)); }
// Thread safe fetch for the target only, no child dependencies private async Task Ensure(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token) { object lockObj = _lockObjsById.GetOrAdd(target.Id, new object()); // lock per package Id lock (lockObj) { VersionRange alreadySearched = null; // Fetch by range is no longer supported, all packages will be fetched if (!_rangeSearched.TryGetValue(target.Id, out alreadySearched)) { // server search IEnumerable <IPackage> repoPackages = V2Client.FindPackagesById(target.Id); _rangeSearched.AddOrUpdate(target.Id, VersionRange.All, (k, v) => VersionRange.All); HashSet <PackageDependencyInfo> foundPackages = null; // add everything to found if (!_found.TryGetValue(target.Id, out foundPackages)) { foundPackages = new HashSet <PackageDependencyInfo>(PackageIdentity.Comparer); _found.TryAdd(target.Id, foundPackages); } // add current packages to found IEnumerable <PackageDependencyInfo> packageVersions = repoPackages.Select(p => CreateDependencyInfo(p, projectFramework)); foundPackages.UnionWith(packageVersions); } } }
// Thread safe retrieval of fetched packages for the target only, no child dependencies private IEnumerable <PackageDependencyInfo> Get(NuGet.Packaging.Core.PackageDependency target, bool includePrerelease) { HashSet <PackageDependencyInfo> packages = null; if (!_found.TryGetValue(target.Id, out packages)) { return(Enumerable.Empty <PackageDependencyInfo>()); } else { object lockObj = _lockObjsById.GetOrAdd(target.Id, new object()); // lock per package Id lock (lockObj) { return(packages.Where(p => includePrerelease || !p.Version.IsPrerelease).Where(p => target.VersionRange.Satisfies(p.Version))); } } }
/// <summary> /// Recursive package dependency info gather /// </summary> private async Task <IEnumerable <PackageDependencyInfo> > Seek(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, IEnumerable <string> parents, CancellationToken token) { // check if we are cancelled token.ThrowIfCancellationRequested(); List <PackageDependencyInfo> results = new List <PackageDependencyInfo>(); // circular dependency check protection if (!parents.Contains(target.Id, StringComparer.OrdinalIgnoreCase)) { await Ensure(target, projectFramework, includePrerelease, token); var packages = Get(target, includePrerelease); results.AddRange(packages); // combine all version ranges found for an id into a single range var toSeek = packages.SelectMany(g => g.Dependencies).GroupBy(d => d.Id, StringComparer.OrdinalIgnoreCase) .OrderBy(d => d.Key) .Select(g => new NuGet.Packaging.Core.PackageDependency(g.Key, VersionRange.Combine(g.Select(d => d.VersionRange)))); // recurse Stack <Task <IEnumerable <PackageDependencyInfo> > > tasks = new Stack <Task <IEnumerable <PackageDependencyInfo> > >(); foreach (NuGet.Packaging.Core.PackageDependency dep in toSeek) { // run tasks on another thread var task = Task.Run(async() => await Seek(dep, projectFramework, includePrerelease, parents.Concat(new string[] { target.Id }), token)); tasks.Push(task); } // add child dep results foreach (var task in tasks) { results.AddRange(await task); } } return(results); }
private async Task <IEnumerable <NuGetVersion> > CacheGetAllVersionsOfDependency(NuGetFactory factory, NuGet.Packaging.Core.PackageDependency dependency) { return(await CacheGetAllVersionsOfPackage(factory, dependency.Id)); }
/// <summary> /// Dependency walk /// </summary> public override async Task<IEnumerable<PackageDependencyInfo>> ResolvePackages(IEnumerable<PackageIdentity> packages, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token) { if (projectFramework == null) { throw new ArgumentNullException("projectFramework"); } if (packages == null) { throw new ArgumentNullException("packages"); } HashSet<PackageDependencyInfo> results = new HashSet<PackageDependencyInfo>(PackageIdentityComparer.Default); foreach (PackageIdentity package in packages) { VersionRange range = package.HasVersion ? new VersionRange(package.Version, true, package.Version, true) : VersionRange.All; var target = new NuGet.Packaging.Core.PackageDependency(package.Id, range); results.UnionWith(await Seek(target, projectFramework, includePrerelease, Enumerable.Empty<string>(), token)); } // pre-release should not be in the final set, but filter again just to be sure return results.Where(e => includePrerelease || !e.Version.IsPrerelease); }
// Thread safe fetch for the target only, no child dependencies private async Task Ensure(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token) { object lockObj = _lockObjsById.GetOrAdd(target.Id, new object()); // lock per package Id lock (lockObj) { VersionRange alreadySearched = null; if (!_rangeSearched.TryGetValue(target.Id, out alreadySearched)) { alreadySearched = EmptyRange; } if (alreadySearched == null || !target.VersionRange.IsSubSetOrEqualTo(alreadySearched)) { // find what we haven't checked already var needed = NeededRange(alreadySearched, target.VersionRange); // adjust prerelease, is this needed? needed = ModifyRange(needed, includePrerelease); if (!_versionRangeComparer.Equals(needed, EmptyRange)) { // server search IEnumerable <IPackage> repoPackages = null; if (_useFindById) { // Ranges fail in some cases for local repos, to work around this just collect every // version of the package to filter later repoPackages = V2Client.FindPackagesById(target.Id); } else { // DataService Repository repoPackages = V2Client.FindPackages(target.Id, GetVersionSpec(needed), includePrerelease, false); } List <VersionRange> currentRanges = new List <VersionRange>(); currentRanges.Add(target.VersionRange); if (alreadySearched != null) { currentRanges.Add(alreadySearched); } // update the already searched range VersionRange combined = null; if (_useFindById) { // for local repos we found all possible versions combined = VersionRange.All; } else { // for non-local repos find the real range combined = VersionRange.Combine(currentRanges); } _rangeSearched.AddOrUpdate(target.Id, combined, (k, v) => combined); HashSet <PackageDependencyInfo> foundPackages = null; // add everything to found if (!_found.TryGetValue(target.Id, out foundPackages)) { foundPackages = new HashSet <PackageDependencyInfo>(PackageIdentity.Comparer); _found.TryAdd(target.Id, foundPackages); } // add current packages to found IEnumerable <PackageDependencyInfo> packageVersions = repoPackages.Select(p => CreateDependencyInfo(p, projectFramework)); foundPackages.UnionWith(packageVersions); } } } }