public override bool Execute()
        {
            PackageIdentity[] latestPackages = NuGetPackageInfos
                                               .Select(item =>
            {
                var id      = item.GetMetadata(NuGetPackageInfoId);
                var version = item.GetMetadata(NuGetPackageInfoVersion);

                if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(version))
                {
                    return(null);
                }

                return(new PackageIdentity(id, NuGetVersion.Parse(version)));
            })
                                               .Where(identity => identity != null)
                                               .GroupBy(identity => identity.Id)
                                               .Select(g => g.OrderBy(id => id.Version).Last())
                                               .OrderBy(id => id.Id)
                                               .ToArray();

            Directory.CreateDirectory(Path.GetDirectoryName(OutputPath));

            using (var outStream = File.Open(OutputPath, FileMode.Create))
                using (var sw = new StreamWriter(outStream, new UTF8Encoding(false)))
                {
                    sw.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
                    sw.WriteLine(@"<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">");
                    sw.WriteLine(@"  <PropertyGroup>");
                    foreach (PackageIdentity packageIdentity in latestPackages)
                    {
                        string formattedId = s_packageNamePascalCasingRegex.Replace(
                            packageIdentity.Id,
                            match => match.Groups?["FirstPartChar"].Value.ToUpperInvariant()
                            ?? string.Empty);

                        string propertyName = $"{formattedId}PackageVersion";

                        string condition = string.Empty;
                        if (OnlyUpdatePrereleaseVersions)
                        {
                            condition = $@" Condition=""$({propertyName}.Contains('-'))""";
                        }

                        sw.WriteLine($"    <{propertyName}{condition}>{packageIdentity.Version}</{propertyName}>");
                    }
                    sw.WriteLine(@"  </PropertyGroup>");
                    sw.WriteLine(@"</Project>");
                }

            return(true);
        }
Пример #2
0
        public override bool Execute()
        {
            string[] packageIdentities = NuGetPackageInfos
                                         .Select(item => $"{item.GetMetadata("PackageId")}/{item.GetMetadata("PackageVersion")}")
                                         .ToArray();

            // Remove trailing slash from dir to avoid EnumerateFiles method adding slash twice,
            // which would cause multiple unique paths per file and break the hash set.
            ProjectDirectories = ProjectDirectories
                                 .Where(Directory.Exists)
                                 .Select(dir => dir.TrimEnd('/', '\\'))
                                 .ToArray();

            Log.LogMessage(MessageImportance.High, "Finding project.assets.json files...");

            Dictionary <string, IEnumerable <string> > projectDirAssetFiles = ProjectDirectories
                                                                              .ToDictionary(
                dir => dir,
                dir => Directory.EnumerateFiles(
                    dir,
                    "project.assets.json",
                    SearchOption.AllDirectories));

            Log.LogMessage(MessageImportance.High, "Reading usage info...");

            var assetFileUsages = new ConcurrentDictionary <string, Usage[]>();

            Parallel.ForEach(
                projectDirAssetFiles.Values.SelectMany(v => v).Distinct(),
                assetsFile =>
            {
                string contents = File.ReadAllText(assetsFile);

                assetFileUsages.TryAdd(
                    assetsFile,
                    packageIdentities
                    .Where(id => IsIdentityInText(id, contents))
                    .Select(id => new Usage
                {
                    AssetsFile      = assetsFile,
                    PackageIdentity = id
                })
                    .ToArray());
            });

            Log.LogMessage(MessageImportance.High, "Associating usage info with projects...");

            var usedAssetFiles = new HashSet <string>();
            var usages         = new List <Usage>();

            foreach (string dir in ProjectDirectories)
            {
                foreach (string assetsFile in projectDirAssetFiles[dir])
                {
                    if (usedAssetFiles.Add(assetsFile))
                    {
                        foreach (var usage in assetFileUsages[assetsFile])
                        {
                            usage.ProjectDirectory = dir;
                            usages.Add(usage);
                        }
                    }
                }
            }

            Log.LogMessage(MessageImportance.High, $"Writing data to '{DataFile}'...");

            Directory.CreateDirectory(Path.GetDirectoryName(DataFile));

            string[] unused = packageIdentities
                              .Where(id => !usages.Any(u => IsIdentityEqual(id, u.PackageIdentity)))
                              .ToArray();

            var data = new UsageData
            {
                UnusedPackages = unused,
                Usages         = usages.ToArray()
            };

            File.WriteAllText(DataFile, JsonConvert.SerializeObject(data, Formatting.Indented));

            return(!Log.HasLoggedErrors);
        }
Пример #3
0
        public override bool Execute()
        {
            DateTime startTime = DateTime.Now;

            Log.LogMessage(MessageImportance.High, "Writing package usage data...");

            string[] projectDirectoriesOutsideRoot = ProjectDirectories.NullAsEmpty()
                                                     .Where(dir => !dir.StartsWith(RootDir, StringComparison.Ordinal))
                                                     .ToArray();

            if (projectDirectoriesOutsideRoot.Any())
            {
                throw new ArgumentException(
                          $"All ProjectDirectories must be in RootDir '{RootDir}', but found " +
                          string.Join(", ", projectDirectoriesOutsideRoot));
            }

            Log.LogMessage(MessageImportance.Low, "Finding set of RIDs...");

            string[] possibleRids = PlatformsRuntimeJsonFiles.NullAsEmpty()
                                    .SelectMany(ReadRidsFromRuntimeJson)
                                    .Distinct()
                                    .ToArray();

            Log.LogMessage(MessageImportance.Low, "Reading package identities...");

            PackageIdentity[] restored = RestoredPackageFiles.NullAsEmpty()
                                         .Select(ReadNuGetPackageInfos.ReadIdentity)
                                         .Distinct()
                                         .ToArray();

            PackageIdentity[] tarballPrebuilt = TarballPrebuiltPackageFiles.NullAsEmpty()
                                                .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                .Distinct()
                                                .ToArray();

            PackageIdentity[] referencePackages = ReferencePackageFiles.NullAsEmpty()
                                                  .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                  .Distinct()
                                                  .ToArray();

            PackageIdentity[] sourceBuilt = SourceBuiltPackageFiles.NullAsEmpty()
                                            .Select(ReadNuGetPackageInfos.ReadIdentity)
                                            .Distinct()
                                            .ToArray();

            IEnumerable <PackageIdentity> prebuilt = restored.Except(sourceBuilt).Except(referencePackages);

            PackageIdentity[] toCheck = NuGetPackageInfos.NullAsEmpty()
                                        .Select(item => new PackageIdentity(
                                                    item.GetMetadata("PackageId"),
                                                    NuGetVersion.Parse(item.GetMetadata("PackageVersion"))))
                                        .Concat(prebuilt)
                                        .ToArray();

            Log.LogMessage(MessageImportance.Low, "Finding project.assets.json files...");

            string[] assetFiles = Directory
                                  .GetFiles(RootDir, "project.assets.json", SearchOption.AllDirectories)
                                  .Select(GetPathRelativeToRoot)
                                  .Except(IgnoredProjectAssetsJsonFiles.NullAsEmpty().Select(GetPathRelativeToRoot))
                                  .ToArray();

            if (!string.IsNullOrEmpty(ProjectAssetsJsonArchiveFile))
            {
                Log.LogMessage(MessageImportance.Low, "Archiving project.assets.json files...");

                Directory.CreateDirectory(Path.GetDirectoryName(ProjectAssetsJsonArchiveFile));

                using (var projectAssetArchive = new ZipArchive(
                           File.Open(
                               ProjectAssetsJsonArchiveFile,
                               FileMode.Create,
                               FileAccess.ReadWrite),
                           ZipArchiveMode.Create))
                {
                    // Only one entry can be open at a time, so don't do this during the Parallel
                    // ForEach later.
                    foreach (var relativePath in assetFiles)
                    {
                        using (var stream = File.OpenRead(Path.Combine(RootDir, relativePath)))
                            using (Stream entryWriter = projectAssetArchive
                                                        .CreateEntry(relativePath, CompressionLevel.Optimal)
                                                        .Open())
                            {
                                stream.CopyTo(entryWriter);
                            }
                    }
                }
            }

            Log.LogMessage(MessageImportance.Low, "Reading usage info...");

            var usages = new ConcurrentBag <Usage>();

            Parallel.ForEach(
                assetFiles,
                assetFile =>
            {
                JObject jObj;

                using (var file = File.OpenRead(Path.Combine(RootDir, assetFile)))
                    using (var reader = new StreamReader(file))
                        using (var jsonReader = new JsonTextReader(reader))
                        {
                            jObj = (JObject)JToken.ReadFrom(jsonReader);
                        }

                var properties = new HashSet <string>(
                    jObj.SelectTokens("$.targets.*").Children()
                    .Concat(jObj.SelectToken("$.libraries"))
                    .Select(t => ((JProperty)t).Name)
                    .Distinct(),
                    StringComparer.OrdinalIgnoreCase);

                var directDependencies = jObj.SelectTokens("$.project.frameworks.*.dependencies").Children().Select(dep =>
                                                                                                                    new
                {
                    name           = ((JProperty)dep).Name,
                    target         = dep.SelectToken("$..target")?.ToString(),
                    version        = VersionRange.Parse(dep.SelectToken("$..version")?.ToString()),
                    autoReferenced = dep.SelectToken("$..autoReferenced")?.ToString() == "True",
                })
                                         .ToArray();

                foreach (var identity in toCheck
                         .Where(id => properties.Contains(id.Id + "/" + id.Version.OriginalVersion)))
                {
                    var directDependency =
                        directDependencies?.FirstOrDefault(
                            d => d.name == identity.Id &&
                            d.version.Satisfies(identity.Version));
                    usages.Add(Usage.Create(
                                   assetFile,
                                   identity,
                                   directDependency != null,
                                   directDependency?.autoReferenced == true,
                                   possibleRids));
                }
            });

            Log.LogMessage(MessageImportance.Low, "Searching for unused packages...");

            foreach (PackageIdentity restoredWithoutUsagesFound in
                     toCheck.Except(usages.Select(u => u.PackageIdentity)))
            {
                usages.Add(Usage.Create(
                               null,
                               restoredWithoutUsagesFound,
                               false,
                               false,
                               possibleRids));
            }

            // Packages that were included in the tarball as prebuilts, but weren't even restored.
            PackageIdentity[] neverRestoredTarballPrebuilts = tarballPrebuilt
                                                              .Except(restored)
                                                              .ToArray();

            Log.LogMessage(MessageImportance.Low, $"Writing data to '{DataFile}'...");

            var data = new UsageData
            {
                CreatedByRid = TargetRid,
                Usages       = usages.ToArray(),
                NeverRestoredTarballPrebuilts = neverRestoredTarballPrebuilts,
                ProjectDirectories            = ProjectDirectories
                                                ?.Select(GetPathRelativeToRoot)
                                                .ToArray()
            };

            Directory.CreateDirectory(Path.GetDirectoryName(DataFile));
            File.WriteAllText(DataFile, data.ToXml().ToString());

            Log.LogMessage(
                MessageImportance.High,
                $"Writing package usage data... done. Took {DateTime.Now - startTime}");

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            DateTime startTime = DateTime.Now;

            Log.LogMessage(MessageImportance.High, "Writing package usage data...");

            string[] projectDirectoriesOutsideRoot = ProjectDirectories.NullAsEmpty()
                                                     .Where(dir => !dir.StartsWith(RootDir, StringComparison.Ordinal))
                                                     .ToArray();

            if (projectDirectoriesOutsideRoot.Any())
            {
                throw new ArgumentException(
                          $"All ProjectDirectories must be in RootDir '{RootDir}', but found " +
                          string.Join(", ", projectDirectoriesOutsideRoot));
            }

            Log.LogMessage(MessageImportance.Low, "Finding set of RIDs...");

            string[] possibleRids = PlatformsRuntimeJsonFiles.NullAsEmpty()
                                    .SelectMany(ReadRidsFromRuntimeJson)
                                    .Distinct()
                                    .ToArray();

            Log.LogMessage(MessageImportance.Low, "Reading package identities...");

            PackageIdentity[] restored = RestoredPackageFiles.NullAsEmpty()
                                         .Select(ReadNuGetPackageInfos.ReadIdentity)
                                         .Distinct()
                                         .ToArray();

            PackageIdentity[] tarballPrebuilt = TarballPrebuiltPackageFiles.NullAsEmpty()
                                                .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                .Distinct()
                                                .ToArray();

            PackageIdentity[] sourceBuilt = SourceBuiltPackageFiles.NullAsEmpty()
                                            .Select(ReadNuGetPackageInfos.ReadIdentity)
                                            .Distinct()
                                            .ToArray();

            IEnumerable <PackageIdentity> prebuilt = restored.Except(sourceBuilt);

            PackageIdentity[] toCheck = NuGetPackageInfos.NullAsEmpty()
                                        .Select(item => new PackageIdentity(
                                                    item.GetMetadata("PackageId"),
                                                    NuGetVersion.Parse(item.GetMetadata("PackageVersion"))))
                                        .Concat(prebuilt)
                                        .ToArray();

            Log.LogMessage(MessageImportance.Low, "Finding project.assets.json files...");

            string[] assetFiles = Directory.GetFiles(
                RootDir,
                "project.assets.json",
                SearchOption.AllDirectories);

            if (!string.IsNullOrEmpty(ProjectAssetsJsonArchiveFile))
            {
                Log.LogMessage(MessageImportance.Low, "Archiving project.assets.json files...");

                Directory.CreateDirectory(Path.GetDirectoryName(ProjectAssetsJsonArchiveFile));

                using (var projectAssetArchive = new ZipArchive(
                           File.Open(
                               ProjectAssetsJsonArchiveFile,
                               FileMode.Create,
                               FileAccess.ReadWrite),
                           ZipArchiveMode.Create))
                {
                    // Only one entry can be open at a time, so don't do this during the Parallel
                    // ForEach later.
                    foreach (var file in assetFiles)
                    {
                        string relativePath = file.Substring(RootDir.Length);
                        using (var stream = File.OpenRead(file))
                            using (Stream entryWriter = projectAssetArchive
                                                        .CreateEntry(relativePath, CompressionLevel.Optimal)
                                                        .Open())
                            {
                                stream.CopyTo(entryWriter);
                            }
                    }
                }
            }

            Log.LogMessage(MessageImportance.Low, "Reading usage info...");

            var usages = new ConcurrentBag <Usage>();

            Parallel.ForEach(
                assetFiles,
                assetFile =>
            {
                var properties = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                using (var file = File.OpenRead(assetFile))
                    using (var reader = new StreamReader(file))
                        using (var jsonReader = new JsonTextReader(reader))
                        {
                            while (jsonReader.Read())
                            {
                                if (jsonReader.TokenType == JsonToken.PropertyName &&
                                    jsonReader.Value is string value)
                                {
                                    properties.Add(value);
                                }
                            }
                        }

                foreach (var identity in toCheck
                         .Where(id => properties.Contains(id.Id + "/" + id.Version.OriginalVersion)))
                {
                    usages.Add(Usage.Create(
                                   // Store relative path for future report generation.
                                   assetFile.Substring(RootDir.Length),
                                   identity,
                                   possibleRids));
                }
            });

            Log.LogMessage(MessageImportance.Low, "Searching for unused packages...");

            foreach (PackageIdentity restoredWithoutUsagesFound in
                     toCheck.Except(usages.Select(u => u.PackageIdentity)))
            {
                usages.Add(Usage.Create(
                               null,
                               restoredWithoutUsagesFound,
                               possibleRids));
            }

            // Packages that were included in the tarball as prebuilts, but weren't even restored.
            PackageIdentity[] neverRestoredTarballPrebuilts = tarballPrebuilt
                                                              .Except(restored)
                                                              .ToArray();

            Log.LogMessage(MessageImportance.Low, $"Writing data to '{DataFile}'...");

            var data = new UsageData
            {
                CreatedByRid = TargetRid,
                Usages       = usages.ToArray(),
                NeverRestoredTarballPrebuilts = neverRestoredTarballPrebuilts,
                ProjectDirectories            = ProjectDirectories
                                                ?.Select(dir => dir.Substring(RootDir.Length))
                                                .ToArray()
            };

            Directory.CreateDirectory(Path.GetDirectoryName(DataFile));
            File.WriteAllText(DataFile, data.ToXml().ToString());

            Log.LogMessage(
                MessageImportance.High,
                $"Writing package usage data... done. Took {DateTime.Now - startTime}");

            return(!Log.HasLoggedErrors);
        }