| File: TargetFrameworkResolver.cs | Web Access |
| Project: src\arcade\src\Microsoft.DotNet.Build.Tasks.TargetFramework\src\Microsoft.DotNet.Build.Tasks.TargetFramework.csproj (Microsoft.DotNet.Build.Tasks.TargetFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using NuGet.Client; using NuGet.ContentModel; using NuGet.Frameworks; using NuGet.RuntimeModel; using System.Collections.Generic; using System.Linq; namespace Microsoft.DotNet.Build.Tasks.TargetFramework { /// <summary> /// This class uses NuGet's asset selection logic to choose the best TargetFramework given the list of supported TargetFrameworks. /// This behaves in a same way as NuGet selects lib files from a nuget package for a particular TargetFramework. /// </summary> internal class TargetFrameworkResolver { private static readonly Dictionary<string, TargetFrameworkResolver> s_targetFrameworkResolverCache = new(); private readonly ManagedCodeConventions _conventions; private readonly PatternSet _configStringPattern; private TargetFrameworkResolver(string runtimeGraph) { _conventions = new ManagedCodeConventions(JsonRuntimeFormat.ReadRuntimeGraph(runtimeGraph)); _configStringPattern = new PatternSet( _conventions.Properties, groupPatterns: new PatternDefinition[] { // In order to use Nuget's asset allocation, the input needs to be file paths and should contain a trailing slash. new PatternDefinition("{tfm}/"), new PatternDefinition("{tfm}-{rid}/") }, pathPatterns: new PatternDefinition[] { new PatternDefinition("{tfm}/"), new PatternDefinition("{tfm}-{rid}/") }); } public static TargetFrameworkResolver CreateOrGet(string runtimeGraph) { if (!s_targetFrameworkResolverCache.TryGetValue(runtimeGraph, out TargetFrameworkResolver? targetFrameworkResolver)) { targetFrameworkResolver = new TargetFrameworkResolver(runtimeGraph); s_targetFrameworkResolverCache.Add(runtimeGraph, targetFrameworkResolver); } return targetFrameworkResolver!; } public string? GetNearest(IEnumerable<string> frameworks, NuGetFramework framework) { NuGetFramework frameworkWithoutPlatform = NuGetFramework.Parse(framework.DotNetFrameworkName); ContentItemCollection contentCollection = new(); contentCollection.Load(frameworks.Select(f => f + '/').ToArray()); // The platform is expected to be passed-in lower-case but the SDK normalizes "windows" to "Windows" which is why it is lowered again. SelectionCriteria criteria = _conventions.Criteria.ForFrameworkAndRuntime(frameworkWithoutPlatform, framework.Platform.ToLowerInvariant()); string? bestTargetFrameworkString = contentCollection.FindBestItemGroup(criteria, _configStringPattern)?.Items[0].Path; return bestTargetFrameworkString?.Remove(bestTargetFrameworkString.Length - 1); } } }