private static async Task <IReadOnlyCollection <SourcePackageDependencyInfo> > GetAvailablePackageDependencies(
            NuGetPackageManager packageManager,
            NugetFolderProject nuGetProject,
            PackageIdentity packageIdentity,
            ResolutionContext resolutionContext,
            INuGetProjectContext nuGetProjectContext,
            ISet <SourceRepository> primarySources,
            ISet <SourceRepository> allSources,
            CancellationToken token)
        {
            var targetFramework = nuGetProject.GetMetadata <NuGetFramework>(NuGetProjectMetadataKeys.TargetFramework);
            var primaryPackages = new List <PackageIdentity> {
                packageIdentity
            };
            var gatherContext = new GatherContext()
            {
                InstalledPackages    = Array.Empty <PackageIdentity>(),
                PrimaryTargets       = primaryPackages,
                TargetFramework      = targetFramework,
                PrimarySources       = primarySources.ToList(),
                AllSources           = allSources.ToList(),
                PackagesFolderSource = packageManager.PackagesFolderSourceRepository,
                ResolutionContext    = resolutionContext,
                AllowDowngrades      = false,
                ProjectContext       = nuGetProjectContext
            };

            return(await ResolverGather.GatherAsync(gatherContext, token));
        }
        public async Task ResolverGather_Complex()
        {
            ResolutionContext context = new ResolutionContext(Resolver.DependencyBehavior.Lowest, true);

            PackageIdentity target = new PackageIdentity("a", new NuGetVersion(1, 0, 0));
            IEnumerable <PackageIdentity> targets = new PackageIdentity[] { target };

            NuGetFramework framework = NuGetFramework.Parse("net451");

            List <PackageDependencyInfo> packages1 = new List <PackageDependencyInfo>()
            {
                new PackageDependencyInfo("c", new NuGetVersion(1, 0, 0), new PackageDependency[] { }),
            };

            List <PackageDependencyInfo> packages2 = new List <PackageDependencyInfo>()
            {
                new PackageDependencyInfo("b", new NuGetVersion(1, 0, 0), new PackageDependency[] { new PackageDependency("c", new VersionRange(new NuGetVersion(1, 0, 0))) }),
            };

            List <PackageDependencyInfo> packages3 = new List <PackageDependencyInfo>()
            {
                new PackageDependencyInfo("a", new NuGetVersion(1, 0, 0), new PackageDependency[] { new PackageDependency("b", new VersionRange(new NuGetVersion(1, 0, 0))) }),
            };

            List <Lazy <INuGetResourceProvider> > providers1 = new List <Lazy <INuGetResourceProvider> >();

            providers1.Add(new Lazy <INuGetResourceProvider>(() => new TestDependencyInfoProvider(packages1)));

            List <Lazy <INuGetResourceProvider> > providers2 = new List <Lazy <INuGetResourceProvider> >();

            providers2.Add(new Lazy <INuGetResourceProvider>(() => new TestDependencyInfoProvider(packages2)));

            List <Lazy <INuGetResourceProvider> > providers3 = new List <Lazy <INuGetResourceProvider> >();

            providers3.Add(new Lazy <INuGetResourceProvider>(() => new TestDependencyInfoProvider(packages3)));

            List <SourceRepository> repos = new List <SourceRepository>();

            repos.Add(new SourceRepository(new PackageSource("http://1"), providers1));
            repos.Add(new SourceRepository(new PackageSource("http://2"), providers2));
            repos.Add(new SourceRepository(new PackageSource("http://3"), providers3));

            var results = await ResolverGather.GatherPackageDependencyInfo(context, targets, Enumerable.Empty <PackageIdentity>(),
                                                                           framework, repos, repos, CancellationToken.None);

            var check = results.OrderBy(e => e.Id).ToList();

            Assert.Equal(3, check.Count);
            Assert.Equal("a", check[0].Id);
            Assert.Equal("b", check[1].Id);
            Assert.Equal("c", check[2].Id);
        }
Exemplo n.º 3
0
        private async Task <PackagesContext> GetRequiredPackages(ISet <PackageIdentity> primaryPackages,
                                                                 ISet <PackageIdentity> targetPackages, bool downgradeAllowed, ResolutionContext resolutionContext,
                                                                 NuGetFramework primaryFramework, ILogger logger, CancellationToken token)
        {
            var libraryPackage = GetFramworkLibrary(primaryFramework);

            var gatherContext = new GatherContext
            {
                ResolutionContext    = resolutionContext,
                PrimarySources       = _project.PrimarySources,
                DependencySources    = _project.DependencySources,
                PackagesFolderSource = _project.LocalSourceRepository,
                PrimaryTargets       =
                    primaryPackages
                    .Select(x => targetPackages.Contains(x) ? x : new PackageIdentity(x.Id, version: null))
                    .ToList(),
                AllowDowngrades     = downgradeAllowed,
                PrimaryFramework    = primaryFramework,
                DependencyFramework = MazeFrameworks.MapToNetFramework(primaryFramework),
                Log = logger
            };

            var primaryPackageIds = primaryPackages.Select(x => x.Id).ToHashSet();

            var allPackages = await ResolverGather.GatherAsync(gatherContext, token);

            var frameworkRelevantPackages = primaryPackages.Where(x => allPackages.First(y => y.Equals(x)).Dependencies.Any()).ToHashSet();
            var frameworkRelevantTargets  = targetPackages.Where(x => allPackages.First(y => y.Equals(x)).Dependencies.Any()).ToHashSet();

            //we remove all primary packages that have no dependencies for the current framework meaning they don't support this framework
            var availablePackages = allPackages.Where(x => !primaryPackageIds.Contains(x.Id) || x.Dependencies.Any()).ToList();

            if (!availablePackages.Any()) //packages not available for this framework
            {
                return(new PackagesContext(ImmutableDictionary <PackageIdentity, SourcePackageDependencyInfo> .Empty));
            }

            //available packages now contains all versions of the package and all versions of each depdendency (recursive)
            //we try to prune the results down to only what we would allow to be installed

            //1. remove incorrect library packages
            var prunedAvailablePackages =
                PrunePackageTreeExtensions.RemoveLibraryPackage(availablePackages, libraryPackage);

            //2. remove all versions of the package we want to install except the version we actually want to install
            //   it is not a problem if other primary packages might need an update
            prunedAvailablePackages =
                PrunePackageTreeExtensions.RemoveAllVersionsForIdExcept(prunedAvailablePackages, frameworkRelevantTargets);

            //3. remove the downgrades of primary packages
            if (!downgradeAllowed)
            {
                prunedAvailablePackages =
                    PrunePackageTreeExtensions.PruneDowngrades(prunedAvailablePackages, frameworkRelevantPackages);
            }

            //4. remove prereleases
            if (!resolutionContext.IncludePrerelease)
            {
                prunedAvailablePackages =
                    PrunePackageTree.PrunePreleaseForStableTargets(prunedAvailablePackages, frameworkRelevantPackages,
                                                                   frameworkRelevantTargets);
            }

            /* ===> PackageResolverContext
             * TargetIds            New packages to install or update. These will prefer the highest version.
             * RequiredPackageIds   The required packages (primary)
             * PackagesConfig       Only for logging
             * PreferredVersions    Preferred versions of each package. If the package does not exist here it will use the dependency behavior, or if it is a target the highest version will be used.
             * AvailablePackages    All available packages that should be sorted out
             * DependencyBehavior   The behavior for resolving a package version
             * PackageSources       Only for logging
             * Log                  Logger
             * ========================================================================
             * Return               The complete resulting list
             */

            var resolverContext = new PackageResolverContext(
                resolutionContext.DependencyBehavior,
                targetIds: frameworkRelevantTargets.Select(x => x.Id),
                requiredPackageIds: frameworkRelevantPackages.Select(x => x.Id),
                packagesConfig: Enumerable.Empty <PackageReference>(),
                preferredVersions: frameworkRelevantPackages,
                availablePackages: prunedAvailablePackages,
                packageSources: Enumerable.Empty <PackageSource>(),
                log: logger);

            var packageResolver = new PackageResolver();

            //all final packages (including dependencies)
            var packages      = packageResolver.Resolve(resolverContext, token).ToList(); //that's an array
            var dependencyMap = packages.ToDictionary(x => x, x => availablePackages.First(y => y.Equals(x)),
                                                      PackageIdentity.Comparer);

            //remove library package and it's dependencies because they are always loaded
            var foundLibraryPackage = packages.FirstOrDefault(libraryPackage.IsSameId);

            if (foundLibraryPackage != null)
            {
                if (!foundLibraryPackage.Version.Equals(libraryPackage.Version))
                {
                    throw new InvalidOperationException($"Invalid version of {libraryPackage} found: {foundLibraryPackage}");
                }

                RemovePackage(foundLibraryPackage);

                //TODO remove package also from SourceDependencyInfo?

                void RemovePackage(PackageIdentity packageIdentity)
                {
                    var sourceInfo = dependencyMap[packageIdentity];

                    dependencyMap.Remove(packageIdentity);

                    foreach (var dependency in sourceInfo.Dependencies)
                    {
                        var package = dependencyMap.FirstOrDefault(x => x.Key.IsSameId(dependency)).Key;
                        if (package != null)
                        {
                            RemovePackage(package);
                        }
                    }
                }
            }

            return(new PackagesContext(dependencyMap));
        }