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); }
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); }
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); }