Skip to content

Instantly share code, notes, and snippets.

@joelverhagen

joelverhagen/Program.cs

Last active Sep 8, 2020
Embed
What would you like to do?
Get supported frameworks of a package
using NuGet.Client;
using NuGet.ContentModel;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.RuntimeModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace GetPackageCompat
{
class Program
{
static void Main(string[] args)
{
// General idea:
// 1. Enumerate all project target frameworks that are interesting (via telemetry)
// 2. For each package, enumerate support frameworks
// 3. For each package, list all project target frameworks supported
// 4. At search time, query for packages with the project target framework in their list
var nupkgPath = string.Empty;
nupkgPath = @"C:\Users\jver\.nuget\packages\newtonsoft.json\12.0.3\newtonsoft.json.12.0.3.nupkg";
nupkgPath = @"C:\Users\jver\.nuget\packages\microsoft.aspnet.mvc\5.2.3\microsoft.aspnet.mvc.5.2.3.nupkg";
nupkgPath = @"C:\Users\jver\.nuget\packages\xunit\2.4.1\xunit.2.4.1.nupkg";
nupkgPath = @"C:\Users\jver\.nuget\packages\xunit.core\2.4.1\xunit.core.2.4.1.nupkg";
nupkgPath = @"C:\Users\jver\.nuget\packages\xunit.extensibility.core\2.4.1\xunit.extensibility.core.2.4.1.nupkg";
using var packageArchiveReader = new PackageArchiveReader(nupkgPath);
GetSupportFrameworks(packageArchiveReader).ToList();
}
private static IEnumerable<NuGetFramework> GetSupportFrameworks(PackageReaderBase packageReader)
{
Console.WriteLine(packageReader.GetIdentity().Id + " " + packageReader.GetIdentity().Version.ToFullString());
/// Based on <see cref="CompatibilityChecker"/> and <see cref="LockFileUtils"/>.
// There are several caveats of this implementation:
// 1. Does not consider transitive dependencies -- can lead to false positives
// 2. Assumes package reference compatibility logic, not packages.config -- can lead to false positives and false negatives
// 3. Assumes dependency package type -- can lead to false positives
// 4. Missing lib/contract back-compat check -- can lead to false positives
// 5. Missing <references> filtering -- can lead to false positives
// 6. Considers framework assembly groups for package-based frameworks -- can lead to false positives
// 7. Does not consider runtime identifiers -- can lead to false positives
var files = packageReader.GetFiles();
var hasAssemblies = files.Any(p =>
p.StartsWith("ref/", StringComparison.OrdinalIgnoreCase)
|| p.StartsWith("lib/", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(" - Has assemblies: " + hasAssemblies);
if (!hasAssemblies)
{
yield return NuGetFramework.AnyFramework;
}
var items = new ContentItemCollection();
items.Load(files);
var conventions = new ManagedCodeConventions(new RuntimeGraph());
var patterns = new[]
{
conventions.Patterns.CompileRefAssemblies,
conventions.Patterns.CompileLibAssemblies,
conventions.Patterns.RuntimeAssemblies,
conventions.Patterns.ContentFiles,
conventions.Patterns.ResourceAssemblies,
};
var msbuildPatterns = new[]
{
conventions.Patterns.MSBuildTransitiveFiles,
conventions.Patterns.MSBuildFiles,
conventions.Patterns.MSBuildMultiTargetingFiles,
};
var groups = patterns
.SelectMany(p => items.FindItemGroups(p));
// Filter out MSBuild assets that don't match the package ID.
var packageId = packageReader.GetIdentity().Id;
var msbuildGroups = msbuildPatterns
.SelectMany(p => items.FindItemGroups(p))
.Where(g => HasBuildItemsForPackageId(g.Items, packageId));
var frameworksFromAssets = groups
.Concat(msbuildGroups)
.SelectMany(p => p.Properties)
.Where(pair => pair.Key == ManagedCodeConventions.PropertyNames.TargetFrameworkMoniker)
.Select(pair => pair.Value)
.Cast<NuGetFramework>()
.Distinct();
Console.WriteLine(" - Assets in the .nupkg:");
foreach (var f in frameworksFromAssets)
{
Console.WriteLine($" - {f.GetShortFolderName()}");
yield return f;
}
var nuspecReader = packageReader.NuspecReader;
var frameworksFromFrameworkAssemblyGroups = nuspecReader
.GetFrameworkAssemblyGroups()
.Select(g => g.TargetFramework)
.Distinct();
Console.WriteLine(" - <frameworkAssembly> in .nuspec:");
foreach (var f in frameworksFromFrameworkAssemblyGroups)
{
Console.WriteLine($" - {f.GetShortFolderName()}");
yield return f;
}
var frameworksFromFrameworkReferenceGroups = nuspecReader
.GetFrameworkRefGroups()
.Select(g => g.TargetFramework)
.Distinct();
Console.WriteLine(" - <frameworkReference> in .nuspec:");
foreach (var f in frameworksFromFrameworkReferenceGroups)
{
Console.WriteLine($" - {f.GetShortFolderName()}");
yield return f;
}
}
private static bool HasBuildItemsForPackageId(IEnumerable<ContentItem> items, string packageId)
{
foreach (var item in items)
{
var fileName = Path.GetFileName(item.Path);
if (fileName == PackagingCoreConstants.EmptyFolder)
{
return true;
}
if ($"{packageId}.props".Equals(fileName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if ($"{packageId}.targets".Equals(fileName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment