| File: FrameworkPackages\FrameworkPackages.cs | Web Access |
| Project: src\sdk\src\Tasks\Microsoft.NET.Build.Tasks\Microsoft.NET.Build.Tasks.csproj (Microsoft.NET.Build.Tasks) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Microsoft.ComponentDetection.Detectors.NuGet; #nullable disable using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using global::NuGet.Frameworks; using global::NuGet.Versioning; using Microsoft.Build.Framework; using Microsoft.NET.Build.Tasks; using Microsoft.NET.Build.Tasks.ConflictResolution; /// <summary> /// Represents a set of packages that are provided by a specific framework. /// At the moment this only represents the packages that are provided by the Microsoft.NETCore.App framework. /// We could extend this to represent the packages provided by other frameworks like Microsoft.AspNetCore.App and Microsoft.WindowsDesktop.App. /// </summary> internal sealed partial class FrameworkPackages : IEnumerable<KeyValuePair<string, NuGetVersion>>, IEnumerable { private const string DefaultFrameworkKey = ""; private static readonly ConcurrentDictionary<NuGetFramework, ConcurrentDictionary<string, FrameworkPackages>> FrameworkPackagesByFramework = []; static FrameworkPackages() { NETStandard20.Register(); NETStandard21.Register(); NETCoreApp20.Register(); NETCoreApp21.Register(); NETCoreApp22.Register(); NETCoreApp30.Register(); NETCoreApp31.Register(); NETCoreApp50.Register(); NETCoreApp60.Register(); NETCoreApp70.Register(); NETCoreApp80.Register(); NETCoreApp90.Register(); } public FrameworkPackages(NuGetFramework framework, string frameworkName) { this.Framework = framework; this.FrameworkName = frameworkName; } public FrameworkPackages(NuGetFramework framework, string frameworkName, FrameworkPackages frameworkPackages) : this(framework, frameworkName) => this.Packages = new(frameworkPackages.Packages); public NuGetFramework Framework { get; } public string FrameworkName { get; } public Dictionary<string, NuGetVersion> Packages { get; } = new Dictionary<string, NuGetVersion>(StringComparer.OrdinalIgnoreCase); private static string GetFrameworkKey(string frameworkName) => frameworkName switch { FrameworkNames.NetStandardLibrary => DefaultFrameworkKey, FrameworkNames.NetCoreApp => DefaultFrameworkKey, _ => frameworkName, }; internal static void Register(params FrameworkPackages[] toRegister) { foreach (var frameworkPackages in toRegister) { if (!FrameworkPackagesByFramework.TryGetValue(frameworkPackages.Framework, out var frameworkPackagesForVersion)) { FrameworkPackagesByFramework[frameworkPackages.Framework] = frameworkPackagesForVersion = []; } var frameworkKey = GetFrameworkKey(frameworkPackages.FrameworkName); frameworkPackagesForVersion[frameworkKey] = frameworkPackages; } } public static FrameworkPackages[] GetFrameworkPackages(NuGetFramework framework, string[] frameworkReferences, bool acceptNearestMatch = false) { var frameworkPackages = new List<FrameworkPackages>(); if (frameworkReferences.Length == 0) { frameworkReferences = [DefaultFrameworkKey]; } foreach (var frameworkReference in frameworkReferences) { var frameworkKey = GetFrameworkKey(frameworkReference); NuGetFramework nearestFramework; if (acceptNearestMatch) { var reducer = new FrameworkReducer(); var candidateFrameworks = FrameworkPackagesByFramework.Where(pair => pair.Value.ContainsKey(frameworkKey)).Select(pair => pair.Key); nearestFramework = reducer.GetNearest(framework, candidateFrameworks); } else { nearestFramework = framework; } if (FrameworkPackagesByFramework.TryGetValue(nearestFramework, out var frameworkPackagesForVersion) && frameworkPackagesForVersion.TryGetValue(frameworkKey, out var frameworkPackage)) { frameworkPackages.Add(frameworkPackage); } } return frameworkPackages.ToArray(); } public static FrameworkPackages LoadFrameworkPackagesFromPack(Logger log, NuGetFramework framework, string frameworkName, AbsolutePath targetingPackRoot) { if (framework is null || framework.Framework != FrameworkConstants.FrameworkIdentifiers.NetCoreApp) { return null; } if (!string.IsNullOrEmpty(targetingPackRoot.Value)) { var packsFolder = Path.Combine(targetingPackRoot, frameworkName + ".Ref"); var originalPacksFolder = Path.Combine(targetingPackRoot.OriginalValue, frameworkName + ".Ref"); log.LogMessage("Looking for targeting packs in {0}", originalPacksFolder); if (Directory.Exists(packsFolder)) { var packVersionPattern = $"{framework.Version.Major}.{framework.Version.Minor}.*"; var packDirectories = Directory.GetDirectories(packsFolder, packVersionPattern); log.LogMessage("Pack directories found: {0}", string.Join(Environment.NewLine, packDirectories.Select(d => Path.Combine(originalPacksFolder, Path.GetFileName(d))))); var packageOverrides = packDirectories .Select(d => (Overrides: Path.Combine(d, "data", "PackageOverrides.txt"), OriginalOverrides: Path.Combine(originalPacksFolder, Path.GetFileName(d), "data", "PackageOverrides.txt"), Version: ParseVersion(Path.GetFileName(d)))) .Where(d => File.Exists(d.Overrides)) .OrderByDescending(d => d.Version) .FirstOrDefault(); if (packageOverrides.Overrides is not null) { log.LogMessage("Found package overrides file {0}", packageOverrides.OriginalOverrides); // Adapted from https://github.com/dotnet/sdk/blob/c3a8f72c3a5491c693ff8e49e7406136a12c3040/src/Tasks/Common/ConflictResolution/PackageOverride.cs#L52-L68 var packageOverrideLines = File.ReadAllLines(packageOverrides.Overrides); var frameworkPackages = new FrameworkPackages(framework, frameworkName); foreach (var packageOverride in PackageOverride.CreateOverriddenPackages(packageOverrideLines)) { frameworkPackages.Packages[packageOverride.Item1] = packageOverride.Item2; } return frameworkPackages; } else { log.LogMessage("No package overrides found in {0}", originalPacksFolder); } } else { log.LogMessage("Targeting pack folder {0} does not exist", originalPacksFolder); } } return null; static NuGetVersion ParseVersion(string versionString) => NuGetVersion.TryParse(versionString, out var version) ? version : null; } private void Add(string id, string version) { if (string.IsNullOrEmpty(version)) { this.Packages.Remove(id); } else { // intentionally redirect to indexer to allow for overwrite this.Packages[id] = NuGetVersion.Parse(version); } } public bool IsAFrameworkComponent(string id, NuGetVersion version) => this.Packages.TryGetValue(id, out var frameworkPackageVersion) && frameworkPackageVersion >= version; IEnumerator<KeyValuePair<string, NuGetVersion>> IEnumerable<KeyValuePair<string, NuGetVersion>>.GetEnumerator() => this.Packages.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); internal static class FrameworkNames { public const string AspNetCoreApp = "Microsoft.AspNetCore.App"; public const string NetCoreApp = "Microsoft.NETCore.App"; public const string NetStandardLibrary = "NETStandard.Library"; public const string WindowsDesktopApp = "Microsoft.WindowsDesktop.App"; } }