IEnumerable GetNuGetDependencies()

in build/Xamarin.Build/MSBuild/UpdateInvertedDependencies.cs [97:260]


        IEnumerable<InvertedDependency> GetNuGetDependencies ()
        {
            var packagesToProjects = new Dictionary<PackageReference, List<Project>> ();
            var packageRoots = new HashSet<string> ();
            List<string> nugetFlatContainerBaseUris = null;

            IEnumerable<PackageReference> SelectPackages (Project project)
            {
                var packageReferences = project
                    .GetItems ("PackageReference")
                    .Select (pr => new PackageReference (
                        pr.EvaluatedInclude,
                        pr.GetMetadataValue ("Version")));

                var packageRoot = project.GetPropertyValue ("NuGetPackageRoot");
                if (!string.IsNullOrEmpty (packageRoot))
                    packageRoots.Add (packageRoot);

                foreach (var packageReference in packageReferences) {
                    if (ExcludePackageReferences.Any (excluded => string.Equals (
                        excluded, packageReference.Id, StringComparison.OrdinalIgnoreCase)))
                        continue;

                    if (!packagesToProjects.TryGetValue (packageReference, out var projects))
                        packagesToProjects.Add (packageReference, projects = new List<Project> ());

                    projects.Add (project);

                    yield return packageReference;
                }
            }

            string MakePackagePath (PackageReference packageReference)
                => Path.Combine (
                    packageReference.Id,
                    packageReference.Version,
                    packageReference.Id + ".nuspec");

            string GetNuSpecPath (PackageReference packageReference)
            {
                var packagePath = MakePackagePath (packageReference);

                var searchPaths = new List<string> (packageRoots);
                if (NuspecCacheDirectory != null)
                    searchPaths.Add (NuspecCacheDirectory);

                return searchPaths
                    .SelectMany (packageRoot => new [] {
                        Path.Combine (packageRoot, packagePath.ToLowerInvariant ()),
                        Path.Combine (packageRoot, packagePath)
                    })
                    .FirstOrDefault (File.Exists);
            }

            void DownloadNuSpec (PackageReference packageReference)
            {
                if (NuGetTool == null || NuspecCacheDirectory == null)
                    return;

                if (nugetFlatContainerBaseUris == null) {
                    var serializer = JsonSerializer.CreateDefault ();
                    nugetFlatContainerBaseUris = Exec
                        .Run (NuGetTool, "sources", "-Format", "Short")
                        .Where (source => source.StartsWith ("E ", StringComparison.Ordinal))
                        .Select (source => source.Substring (2))
                        .Select (source => {
                            try {
                                var (stream, contentType) = HttpGet (source);
                                using (stream)
                                using (var streamReader = new StreamReader (stream))
                                using (var jsonReader = new JsonTextReader (streamReader))
                                    return serializer
                                        .Deserialize<NuGetResources> (jsonReader)
                                        .Resources
                                        .Where (resource => resource.Type == "PackageBaseAddress/3.0.0")
                                        .Select (resource => resource.Id)
                                        .SingleOrDefault ();
                            } catch (HttpRequestException) {
                                return null;
                            }
                        })
                        .Where (source => source != null)
                        .ToList ();
                }

                foreach (var baseUri in nugetFlatContainerBaseUris) {
                    var relativePath = MakePackagePath (packageReference).ToLowerInvariant ();
                    var localPath = Path.Combine (NuspecCacheDirectory, relativePath);
                    var uri = $"{baseUri}{relativePath.Replace (Path.DirectorySeparatorChar, '/')}";

                    try {
                        Directory.CreateDirectory (Path.GetDirectoryName (localPath));

                        var (stream, contentType) = HttpGet (uri);
                        using (stream) {
                            if (contentType.EndsWith ("/xml", StringComparison.OrdinalIgnoreCase)) {
                                using (var fileStream = File.Create (localPath))
                                    stream.CopyTo (fileStream);
                            } else {
                                // MyGet does not implement NuGet v3 PackageBaseAddress/3.0.0 at all and will
                                // only and always return the nupkg as content and never the nuspec :(
                                // https://twitter.com/MyGetTeam/status/1011688120121810944
                                Log.LogMessage ("-> non-nuspec/XML detected; assuming nupkg archive");
                                using (var archive = new SIOCZipArchive (stream, ZipArchiveMode.Read))
                                    archive
                                        .Entries
                                        .First (entry => string.Equals (
                                            entry.FullName,
                                            packageReference.Id + ".nuspec",
                                            StringComparison.OrdinalIgnoreCase))
                                        .ExtractToFile (
                                            localPath,
                                            overwrite: true);
                            }
                        }

                        break;
                    } catch (HttpRequestException) {
                    }
                }
            }

            var projectCollection = new ProjectCollection ();

            return Exec.Run (
                new ProcessStartInfo {
                    FileName = "git",
                    WorkingDirectory = WorkingDirectory
                }, "ls-files", "--recurse-submodules", "*.csproj", "*.fsproj", "*.vbproj")
                .Where (projectPath => !ExcludeProjectNames.Contains (Path.GetFileNameWithoutExtension (projectPath)))
                .OrderBy (projectPath => projectPath)
                .Select (projectPath => projectCollection.LoadProject (projectPath))
                .SelectMany (SelectPackages)
                .OrderBy (pr => pr)
                .Distinct ()
                .Select (packageReference => {
                    var invertedDependency = InvertedDependency.Create (
                        this,
                        DependencyKind.NuGet,
                        packageReference.Id,
                        packageReference.Version);

                    invertedDependency.DependentProjects = packagesToProjects [packageReference]
                        .Select (p => Path.GetFileNameWithoutExtension (p.FullPath))
                        .ToArray ();

                    var nuspecPath = GetNuSpecPath (packageReference);
                    if (nuspecPath == null) {
                        DownloadNuSpec (packageReference);
                        nuspecPath = GetNuSpecPath (packageReference);
                    }

                    if (nuspecPath != null) {
                        Log.LogMessage ("-> Parsing nuspec {0}", nuspecPath);
                        var nuspec = XDocument.Load (nuspecPath);
                        var ns = nuspec.Root.GetDefaultNamespace ();
                        var metadata = nuspec.Root.Element (ns + "metadata");
                        invertedDependency.ProjectUrl = metadata?.Element (ns + "projectUrl")?.Value;
                        invertedDependency.LicenseUrl = metadata?.Element (ns + "licenseUrl")?.Value;
                    }

                    return invertedDependency;
                });
        }