private static IReadOnlyCollection <SourcePackageDependencyInfo> PruneAvailablePackageDependencies(
            PackageIdentity packageIdentity,
            ResolutionContext resolutionContext,
            IEnumerable <SourcePackageDependencyInfo> availablePackageDependencies)
        {
            var prunedAvailablePackages = PrunePackageTree.RemoveAllVersionsForIdExcept(
                availablePackageDependencies,
                packageIdentity);

            if (resolutionContext.IncludePrerelease)
            {
                return(prunedAvailablePackages.ToList());
            }

            return(PrunePackageTree.PrunePreleaseForStableTargets(
                       prunedAvailablePackages,
                       new[] { packageIdentity },
                       new[] { packageIdentity }).ToList());
        }
示例#2
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));
        }