Example #1
0
 /// <summary>
 /// Order dependencies by children first.
 /// </summary>
 public static IReadOnlyList <PackageSpec> SortPackagesByDependencyOrder(
     IEnumerable <PackageSpec> packages)
 {
     return(TopologicalSortUtility.SortPackagesByDependencyOrder(
                items: packages,
                comparer: StringComparer.Ordinal,
                getId: GetPackageSpecId,
                getDependencies: GetPackageSpecDependencyIds));
 }
        public void TopologicalSortUtility_GivenASingleDepChainVerifyReverseOrder()
        {
            var packages = new[]
            {
                new PackageDependencyInfo(new PackageIdentity("b", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("a") }),
                new PackageDependencyInfo(new PackageIdentity("c", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("b") }),
                new PackageDependencyInfo(new PackageIdentity("a", NuGetVersion.Parse("1.0.0")), Enumerable.Empty <PackageDependency>()),
            };

            var sorted = TopologicalSortUtility.SortPackagesByDependencyOrder(packages);

            sorted.Select(e => e.Id).Should().ContainInOrder(new[] { "a", "b", "c" });
        }
        public void TopologicalSortUtility_GivenUnrelatedPackagesWithMissingDepsVerifySortOrder()
        {
            var packages = new[]
            {
                new PackageDependencyInfo(new PackageIdentity("b", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("x") }),
                new PackageDependencyInfo(new PackageIdentity("a", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("y") }),
                new PackageDependencyInfo(new PackageIdentity("C", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("z") }),
            };

            var sorted = TopologicalSortUtility.SortPackagesByDependencyOrder(packages);

            sorted.Select(e => e.Id).Should().ContainInOrder(new[] { "C", "b", "a" });
        }
        public void TopologicalSortUtility_SortOrderOfDiamondDependencyWithMissingVerifyNoChange()
        {
            var packages = new[]
            {
                new PackageDependencyInfo(new PackageIdentity("a", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("b"), new PackageDependency("c"), new PackageDependency("x") }),
                new PackageDependencyInfo(new PackageIdentity("b", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("d"), new PackageDependency("x") }),
                new PackageDependencyInfo(new PackageIdentity("c", NuGetVersion.Parse("1.0.0")), new[] { new PackageDependency("d") }),
                new PackageDependencyInfo(new PackageIdentity("d", NuGetVersion.Parse("1.0.0")), Enumerable.Empty <PackageDependency>()),
            };

            var sorted = TopologicalSortUtility.SortPackagesByDependencyOrder(packages);

            sorted.Select(e => e.Id).Should().ContainInOrder(new[] { "d", "c", "b", "a" });
        }
        public void TopologicalSortUtility_GivenInterlinkedDepsVerifyRevSortOrder()
        {
            var packages = new[]
            {
                CreateInfo("f", "e", "d", "c", "b", "a"),
                CreateInfo("e", "d", "c", "b", "a"),
                CreateInfo("d", "c", "b", "a"),
                CreateInfo("c", "b", "a"),
                CreateInfo("b", "a"),
                CreateInfo("a")
            };

            var sorted = TopologicalSortUtility.SortPackagesByDependencyOrder(packages);

            sorted.Select(e => e.Id).Should().ContainInOrder(new[] { "a", "b", "c", "d", "e", "f" });
        }
Example #6
0
        /// <summary>
        /// Get ordered dependencies from the lock file
        /// </summary>
        /// <param name="lockFile"></param>
        /// <returns></returns>
        public static IReadOnlyList <LibraryIdentity> GetOrderedLockFileDependencies(LockFile lockFile)
        {
            var results = new List <LibraryIdentity>();

            var dependencies = new HashSet <PackageDependencyInfo>(PackageIdentity.Comparer);
            var typeMappings = new Dictionary <PackageDependencyInfo, LibraryIdentity>(PackageIdentity.Comparer);

            foreach (var target in lockFile.Targets)
            {
                foreach (var targetLibrary in target.Libraries)
                {
                    var identity   = new PackageIdentity(targetLibrary.Name, targetLibrary.Version);
                    var dependency = new PackageDependencyInfo(identity, targetLibrary.Dependencies);
                    dependencies.Add(dependency);

                    if (!typeMappings.ContainsKey(dependency))
                    {
                        var libraryIdentity = new LibraryIdentity(
                            targetLibrary.Name,
                            targetLibrary.Version,
                            LibraryType.Parse(targetLibrary.Type));

                        typeMappings.Add(dependency, libraryIdentity);
                    }
                }
            }

            // Sort dependencies
            var sortedDependencies = TopologicalSortUtility.SortPackagesByDependencyOrder(dependencies);

            foreach (var dependency in sortedDependencies)
            {
                // Convert back
                // PackageDependencyInfo -> LibraryIdentity
                results.Add(typeMappings[dependency]);
            }

            return(results);
        }
Example #7
0
        public static List <MSBuildOutputFile> GetMSBuildOutputFiles(PackageSpec project,
                                                                     LockFile assetsFile,
                                                                     IEnumerable <RestoreTargetGraph> targetGraphs,
                                                                     IReadOnlyList <NuGetv3LocalRepository> repositories,
                                                                     RestoreRequest request,
                                                                     string assetsFilePath,
                                                                     bool restoreSuccess,
                                                                     ILogger log)
        {
            // Generate file names
            var targetsPath = GetMSBuildFilePath(project, request, "targets");
            var propsPath   = GetMSBuildFilePath(project, request, "props");

            // Targets files contain a macro for the repository root. If only the user package folder was used
            // allow a replacement. If fallback folders were used the macro cannot be applied.
            // Do not use macros for fallback folders. Use only the first repository which is the user folder.
            var repositoryRoot = repositories.First().RepositoryRoot;

            // Invalid msbuild projects should write out an msbuild error target
            if (!targetGraphs.Any())
            {
                return(GenerateMultiTargetFailureFiles(
                           targetsPath,
                           propsPath,
                           request.ProjectStyle));
            }

            // Add additional conditionals for multi targeting
            var multiTargetingFromMetadata = (request.Project.RestoreMetadata?.CrossTargeting == true);

            var isMultiTargeting = multiTargetingFromMetadata ||
                                   request.Project.TargetFrameworks.Count > 1;

            // ItemGroups for each file.
            var props   = new List <MSBuildRestoreItemGroup>();
            var targets = new List <MSBuildRestoreItemGroup>();

            // MultiTargeting imports are shared between TFMs, to avoid
            // duplicate import warnings only add each once.
            var multiTargetingImportsAdded = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            // Skip runtime graphs, msbuild targets may not come from RID specific packages.
            var ridlessTargets = assetsFile.Targets
                                 .Where(e => string.IsNullOrEmpty(e.RuntimeIdentifier));

            var packagesWithTools = new HashSet <string>(assetsFile.Libraries.Where(i => i.HasTools).Select(i => i.Name), StringComparer.OrdinalIgnoreCase);

            foreach (var ridlessTarget in ridlessTargets)
            {
                // There could be multiple string matches from the MSBuild project.
                var frameworkConditions = GetMatchingFrameworkStrings(project, ridlessTarget.TargetFramework)
                                          .Select(match => string.Format(CultureInfo.InvariantCulture, TargetFrameworkCondition, match))
                                          .ToArray();

                // Find matching target in the original target graphs.
                var targetGraph = targetGraphs.FirstOrDefault(e =>
                                                              string.IsNullOrEmpty(e.RuntimeIdentifier) &&
                                                              ridlessTarget.TargetFramework == e.Framework);

                // Sort by dependency order, child package assets should appear higher in the
                // msbuild targets and props files so that parents can depend on them.
                var sortedGraph = TopologicalSortUtility.SortPackagesByDependencyOrder(ConvertToPackageDependencyInfo(targetGraph.Flattened));

                // Filter out to packages only, exclude projects.
                var packageType = new HashSet <string>(
                    targetGraph.Flattened.Where(e => e.Key.Type == LibraryType.Package)
                    .Select(e => e.Key.Name),
                    StringComparer.OrdinalIgnoreCase);

                // Package -> PackageInfo
                // PackageInfo is kept lazy to avoid hitting the disk for packages
                // with no relevant assets.
                var sortedPackages = sortedGraph.Where(e => packageType.Contains(e.Id))
                                     .Select(sortedPkg =>
                                             new KeyValuePair <LockFileTargetLibrary, Lazy <LocalPackageSourceInfo> >(
                                                 key: ridlessTarget.Libraries.FirstOrDefault(assetsPkg =>
                                                                                             sortedPkg.Version == assetsPkg.Version &&
                                                                                             sortedPkg.Id.Equals(assetsPkg.Name, StringComparison.OrdinalIgnoreCase)),
                                                 value: new Lazy <LocalPackageSourceInfo>(() =>
                                                                                          NuGetv3LocalRepositoryUtility.GetPackage(
                                                                                              repositories,
                                                                                              sortedPkg.Id,
                                                                                              sortedPkg.Version))))
                                     .Where(e => e.Key != null)
                                     .ToArray();

                // build/ {packageId}.targets
                var buildTargetsGroup = new MSBuildRestoreItemGroup();
                buildTargetsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;
                buildTargetsGroup.Position = 2;

                buildTargetsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>
                                                                           pkg.Key.Build.WithExtension(TargetsExtension)
                                                                           .Where(e => pkg.Value.Exists())
                                                                           .Select(e => pkg.Value.GetAbsolutePath(e)))
                                                 .Select(path => GetPathWithMacros(path, repositoryRoot))
                                                 .Select(GenerateImport));

                targets.AddRange(GenerateGroupsWithConditions(buildTargetsGroup, isMultiTargeting, frameworkConditions));

                // props/ {packageId}.props
                var buildPropsGroup = new MSBuildRestoreItemGroup();
                buildPropsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;
                buildPropsGroup.Position = 2;

                buildPropsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>
                                                                         pkg.Key.Build.WithExtension(PropsExtension)
                                                                         .Where(e => pkg.Value.Exists())
                                                                         .Select(e => pkg.Value.GetAbsolutePath(e)))
                                               .Select(path => GetPathWithMacros(path, repositoryRoot))
                                               .Select(GenerateImport));

                props.AddRange(GenerateGroupsWithConditions(buildPropsGroup, isMultiTargeting, frameworkConditions));

                // Create an empty PropertyGroup for package properties
                var packagePathsPropertyGroup = MSBuildRestoreItemGroup.Create("PropertyGroup", Enumerable.Empty <XElement>(), 1000, isMultiTargeting ? frameworkConditions : Enumerable.Empty <string>());

                var projectGraph = targetGraph.Graphs.FirstOrDefault();

                // Packages with GeneratePathProperty=true
                var packageIdsToCreatePropertiesFor = new HashSet <string>(projectGraph.Item.Data.Dependencies.Where(i => i.GeneratePathProperty).Select(i => i.Name), StringComparer.OrdinalIgnoreCase);

                var localPackages = sortedPackages.Select(e => e.Value);

                // Find the packages with matching IDs in the list of sorted packages, filtering out ones that there was no match for or that don't exist
                var packagePathProperties = localPackages
                                            .Where(pkg => pkg?.Value?.Package != null && (packagesWithTools.Contains(pkg.Value.Package.Id) || packageIdsToCreatePropertiesFor.Contains(pkg.Value.Package.Id)) && pkg.Exists())
                                            .Select(pkg => pkg.Value.Package)
                                            // Get the property
                                            .Select(GeneratePackagePathProperty);

                packagePathsPropertyGroup.Items.AddRange(packagePathProperties);

                // Don't bother adding the PropertyGroup if there were no properties added
                if (packagePathsPropertyGroup.Items.Any())
                {
                    props.Add(packagePathsPropertyGroup);
                }

                if (isMultiTargeting)
                {
                    // buildMultiTargeting/ {packageId}.targets
                    var buildCrossTargetsGroup = new MSBuildRestoreItemGroup();
                    buildCrossTargetsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;
                    buildCrossTargetsGroup.Position = 0;

                    buildCrossTargetsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>
                                                                                    pkg.Key.BuildMultiTargeting.WithExtension(TargetsExtension)
                                                                                    .Where(e => pkg.Value.Exists())
                                                                                    .Select(e => pkg.Value.GetAbsolutePath(e)))
                                                          .Where(path => multiTargetingImportsAdded.Add(path))
                                                          .Select(path => GetPathWithMacros(path, repositoryRoot))
                                                          .Select(GenerateImport));

                    targets.AddRange(GenerateGroupsWithConditions(buildCrossTargetsGroup, isMultiTargeting, CrossTargetingCondition));

                    // buildMultiTargeting/ {packageId}.props
                    var buildCrossPropsGroup = new MSBuildRestoreItemGroup();
                    buildCrossPropsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;
                    buildCrossPropsGroup.Position = 0;

                    buildCrossPropsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>
                                                                                  pkg.Key.BuildMultiTargeting.WithExtension(PropsExtension)
                                                                                  .Where(e => pkg.Value.Exists())
                                                                                  .Select(e => pkg.Value.GetAbsolutePath(e)))
                                                        .Where(path => multiTargetingImportsAdded.Add(path))
                                                        .Select(path => GetPathWithMacros(path, repositoryRoot))
                                                        .Select(GenerateImport));

                    props.AddRange(GenerateGroupsWithConditions(buildCrossPropsGroup, isMultiTargeting, CrossTargetingCondition));
                }

                // Write out contentFiles only for XPlat PackageReference projects.
                if (request.ProjectStyle != ProjectStyle.ProjectJson &&
                    request.Project.RestoreMetadata?.SkipContentFileWrite != true)
                {
                    // Create a group for every package, with the nearest from each of allLanguages
                    props.AddRange(sortedPackages.Select(pkg =>
                                                         pkg.Key.ContentFiles
                                                         .Where(e => pkg.Value.Exists())
                                                         .OrderBy(e => e.Path, StringComparer.Ordinal)
                                                         .Select(e =>
                                                                 Tuple.Create(
                                                                     item1: pkg.Key,
                                                                     item2: e,
                                                                     item3: GetPathWithMacros(pkg.Value.GetAbsolutePath(e), repositoryRoot))))
                                   .SelectMany(e => GetLanguageGroups(e))
                                   .SelectMany(group => GenerateGroupsWithConditions(group, isMultiTargeting, frameworkConditions)));
                }
            }

            // Add exclude all condition to all groups
            foreach (var group in props.Concat(targets))
            {
                group.Conditions.Add(ExcludeAllCondition);
            }

            // Create XML, these may be null if the file should be deleted/not written out.
            var propsXML   = GenerateMSBuildFile(props, request.ProjectStyle);
            var targetsXML = GenerateMSBuildFile(targets, request.ProjectStyle);

            // Return all files to write out or delete.
            var files = new List <MSBuildOutputFile>
            {
                new MSBuildOutputFile(propsPath, propsXML),
                new MSBuildOutputFile(targetsPath, targetsXML)
            };

            var packageFolders = repositories.Select(e => e.RepositoryRoot);

            AddNuGetPropertiesToFirstImport(files, packageFolders, repositoryRoot, request.ProjectStyle, assetsFilePath, restoreSuccess);

            return(files);
        }