Skip to content

Commit 5eb7a1e

Browse files
committed
Provide PrunePackageReference data for NuGet based on target framework and framework references
1 parent 569c39b commit 5eb7a1e

17 files changed

+2051
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
namespace Microsoft.ComponentDetection.Detectors.NuGet;
2+
3+
using System;
4+
using System.Collections;
5+
using System.Collections.Concurrent;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using global::NuGet.Frameworks;
10+
using global::NuGet.Versioning;
11+
12+
/// <summary>
13+
/// Represents a set of packages that are provided by a specific framework.
14+
/// At the moment this only represents the packages that are provided by the Microsoft.NETCore.App framework.
15+
/// We could extend this to represent the packages provided by other frameworks like Microsoft.AspNetCore.App and Microsoft.WindowsDesktop.App.
16+
/// </summary>
17+
internal sealed partial class FrameworkPackages : IEnumerable<KeyValuePair<string, NuGetVersion>>, IEnumerable
18+
{
19+
private const string DefaultFrameworkKey = "";
20+
private static readonly ConcurrentDictionary<NuGetFramework, ConcurrentDictionary<string, FrameworkPackages>> FrameworkPackagesByFramework = [];
21+
22+
static FrameworkPackages()
23+
{
24+
NETStandard20.Register();
25+
NETStandard21.Register();
26+
NET461.Register();
27+
NETCoreApp20.Register();
28+
NETCoreApp21.Register();
29+
NETCoreApp22.Register();
30+
NETCoreApp30.Register();
31+
NETCoreApp31.Register();
32+
NETCoreApp50.Register();
33+
NETCoreApp60.Register();
34+
NETCoreApp70.Register();
35+
NETCoreApp80.Register();
36+
NETCoreApp90.Register();
37+
}
38+
39+
public FrameworkPackages(NuGetFramework framework, string frameworkName)
40+
{
41+
this.Framework = framework;
42+
this.FrameworkName = frameworkName;
43+
}
44+
45+
public FrameworkPackages(NuGetFramework framework, string frameworkName, FrameworkPackages frameworkPackages)
46+
: this(framework, frameworkName) => this.Packages = new(frameworkPackages.Packages);
47+
48+
public NuGetFramework Framework { get; }
49+
50+
public string FrameworkName { get; }
51+
52+
public Dictionary<string, NuGetVersion> Packages { get; } = new Dictionary<string, NuGetVersion>(StringComparer.OrdinalIgnoreCase);
53+
54+
private static string GetFrameworkKey(string frameworkName) =>
55+
frameworkName switch
56+
{
57+
FrameworkNames.NetStandardLibrary => DefaultFrameworkKey,
58+
FrameworkNames.NetCoreApp => DefaultFrameworkKey,
59+
_ => frameworkName,
60+
};
61+
62+
internal static void Register(params FrameworkPackages[] toRegister)
63+
{
64+
foreach (var frameworkPackages in toRegister)
65+
{
66+
if (!FrameworkPackagesByFramework.TryGetValue(frameworkPackages.Framework, out var frameworkPackagesForVersion))
67+
{
68+
FrameworkPackagesByFramework[frameworkPackages.Framework] = frameworkPackagesForVersion = [];
69+
}
70+
71+
var frameworkKey = GetFrameworkKey(frameworkPackages.FrameworkName);
72+
frameworkPackagesForVersion[frameworkKey] = frameworkPackages;
73+
}
74+
}
75+
76+
public static FrameworkPackages[] GetFrameworkPackages(NuGetFramework framework, string[] frameworkReferences)
77+
{
78+
var frameworkPackages = new List<FrameworkPackages>();
79+
80+
if (frameworkReferences.Length == 0)
81+
{
82+
frameworkReferences = [DefaultFrameworkKey];
83+
}
84+
85+
foreach (var frameworkReference in frameworkReferences)
86+
{
87+
var frameworkKey = GetFrameworkKey(frameworkReference);
88+
if (FrameworkPackagesByFramework.TryGetValue(framework, out var frameworkPackagesForVersion) &&
89+
frameworkPackagesForVersion.TryGetValue(frameworkKey, out var frameworkPackage))
90+
{
91+
frameworkPackages.Add(frameworkPackage);
92+
}
93+
else
94+
{
95+
// if we didn't predefine the package overrides, load them from the targeting pack
96+
// we might just leave this out since in future frameworks we'll have this functionality built into NuGet.Frameworks
97+
var frameworkPackagesFromPack = LoadFrameworkPackagesFromPack(framework, frameworkReference) ?? new FrameworkPackages(framework, frameworkReference);
98+
99+
Register(frameworkPackagesFromPack);
100+
101+
frameworkPackages.Add(frameworkPackagesFromPack);
102+
}
103+
}
104+
105+
return frameworkPackages.ToArray();
106+
}
107+
108+
private static FrameworkPackages LoadFrameworkPackagesFromPack(NuGetFramework framework, string frameworkName)
109+
{
110+
if (framework is null || framework.Framework != FrameworkConstants.FrameworkIdentifiers.NetCoreApp)
111+
{
112+
return null;
113+
}
114+
115+
var reducer = new FrameworkReducer();
116+
var frameworkKey = GetFrameworkKey(frameworkName);
117+
var candidateFrameworks = FrameworkPackagesByFramework.Where(pair => pair.Value.ContainsKey(frameworkKey)).Select(pair => pair.Key);
118+
var nearestFramework = reducer.GetNearest(framework, candidateFrameworks);
119+
120+
var frameworkPackages = nearestFramework is null ?
121+
new FrameworkPackages(framework, frameworkName) :
122+
new FrameworkPackages(framework, frameworkName, FrameworkPackagesByFramework[nearestFramework][frameworkKey]);
123+
124+
// packs location : %ProgramFiles%\dotnet\packs
125+
var packsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "packs", frameworkName + ".Ref");
126+
if (Directory.Exists(packsFolder))
127+
{
128+
var packVersionPattern = $"{framework.Version.Major}.{framework.Version.Minor}.*";
129+
var packDirectories = Directory.GetDirectories(packsFolder, packVersionPattern);
130+
var packageOverridesFile = packDirectories
131+
.Select(d => (Overrides: Path.Combine(d, "data", "PackageOverrides.txt"), Version: ParseVersion(Path.GetFileName(d))))
132+
.Where(d => File.Exists(d.Overrides))
133+
.OrderByDescending(d => d.Version)
134+
.FirstOrDefault().Overrides;
135+
136+
if (packageOverridesFile is not null)
137+
{
138+
// Adapted from https://github.com/dotnet/sdk/blob/c3a8f72c3a5491c693ff8e49e7406136a12c3040/src/Tasks/Common/ConflictResolution/PackageOverride.cs#L52-L68
139+
var packageOverrides = File.ReadAllLines(packageOverridesFile);
140+
141+
foreach (var packageOverride in packageOverrides)
142+
{
143+
var packageOverrideParts = packageOverride.Trim().Split('|');
144+
145+
if (packageOverrideParts.Length == 2)
146+
{
147+
var packageId = packageOverrideParts[0];
148+
var packageVersion = ParseVersion(packageOverrideParts[1]);
149+
150+
frameworkPackages.Packages[packageId] = packageVersion;
151+
}
152+
}
153+
}
154+
}
155+
156+
return frameworkPackages;
157+
158+
static NuGetVersion ParseVersion(string versionString) => NuGetVersion.TryParse(versionString, out var version) ? version : null;
159+
}
160+
161+
private void Add(string id, string version)
162+
{
163+
// intentionally redirect to indexer to allow for overwrite
164+
this.Packages[id] = NuGetVersion.Parse(version);
165+
}
166+
167+
public bool IsAFrameworkComponent(string id, NuGetVersion version) => this.Packages.TryGetValue(id, out var frameworkPackageVersion) && frameworkPackageVersion >= version;
168+
169+
IEnumerator<KeyValuePair<string, NuGetVersion>> IEnumerable<KeyValuePair<string, NuGetVersion>>.GetEnumerator() => this.Packages.GetEnumerator();
170+
171+
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
172+
173+
internal static class FrameworkNames
174+
{
175+
public const string AspNetCoreApp = "Microsoft.AspNetCore.App";
176+
public const string NetCoreApp = "Microsoft.NETCore.App";
177+
public const string NetStandardLibrary = "NETStandard.Library";
178+
public const string WindowsDesktopApp = "Microsoft.WindowsDesktop.App";
179+
}
180+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Microsoft.ComponentDetection.Detectors.NuGet;
2+
3+
using static global::NuGet.Frameworks.FrameworkConstants.CommonFrameworks;
4+
5+
/// <summary>
6+
/// Framework packages for .NETFramework,Version=v4.6.1.
7+
/// </summary>
8+
internal partial class FrameworkPackages
9+
{
10+
internal static class NET461
11+
{
12+
internal static FrameworkPackages Instance { get; } = new(Net461, DefaultFrameworkKey, NETStandard20.Instance);
13+
14+
internal static void Register() => FrameworkPackages.Register(Instance);
15+
}
16+
}

0 commit comments

Comments
 (0)