1+ // Copyright (c) .NET Foundation. All rights reserved.
2+ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . IO ;
7+ using System . Linq ;
8+ using NuGet . Client ;
9+ using NuGet . ContentModel ;
10+ using NuGet . Frameworks ;
11+ using NuGet . Packaging . Core ;
12+ using NuGet . RuntimeModel ;
13+
14+ namespace NuGetGallery
15+ {
16+ public static class AssetFrameworkHelper
17+ {
18+ /// <summary>
19+ /// This method combines the logic used in restore operations to make a determination about the TFM supported by the package.
20+ /// We have curated a set of compatibility requirements for our needs in NuGet.org. The client logic can be found here:
21+ /// https://github.com/NuGet/NuGet.Client/blob/63255047fe7052cc33b763356ff995d9166f719e/src/NuGet.Core/NuGet.Commands/RestoreCommand/CompatibilityChecker.cs#L252-L294
22+ /// https://github.com/NuGet/NuGet.Client/blob/63255047fe7052cc33b763356ff995d9166f719e/src/NuGet.Core/NuGet.Commands/RestoreCommand/CompatibilityChecker.cs#L439-L442
23+ /// ...and our combination of these elements is below.
24+ /// The logic is essentially this:
25+ /// - Determine whether we're looking at a tools package. In this case we will use tools "pattern sets" (collections of file patterns
26+ /// defined in <see cref="ManagedCodeConventions" />) to assess which frameworks are targeted by the package.
27+ /// - If this isn't a tools package, we look for build-time, runtime, content and resource file patterns
28+ /// For added details on the various cases, see unit tests targeting this method.
29+ /// </summary>
30+ public static IEnumerable < NuGetFramework > GetAssetFrameworks ( string packageId , IReadOnlyList < PackageType > packageTypes , IList < string > packageFiles )
31+ {
32+ var supportedTFMs = Enumerable . Empty < NuGetFramework > ( ) ;
33+ if ( packageFiles != null && packageFiles . Any ( ) )
34+ {
35+ // Setup content items for analysis
36+ var items = new ContentItemCollection ( ) ;
37+ items . Load ( packageFiles ) ;
38+ var runtimeGraph = new RuntimeGraph ( ) ;
39+ var conventions = new ManagedCodeConventions ( runtimeGraph ) ;
40+
41+ // Let's test for tools packages first--they're a special case
42+ var groups = Enumerable . Empty < ContentItemGroup > ( ) ;
43+ if ( packageTypes . Count == 1 && ( packageTypes [ 0 ] == PackageType . DotnetTool ||
44+ packageTypes [ 0 ] == PackageType . DotnetCliTool ) )
45+ {
46+ // Only a package that is a tool package (and nothing else) will be matched against tools pattern set
47+ groups = items . FindItemGroups ( conventions . Patterns . ToolsAssemblies ) ;
48+ }
49+ else
50+ {
51+ // Gather together a list of pattern sets indicating the kinds of packages we wish to evaluate
52+ var patterns = new [ ]
53+ {
54+ conventions . Patterns . CompileRefAssemblies ,
55+ conventions . Patterns . CompileLibAssemblies ,
56+ conventions . Patterns . RuntimeAssemblies ,
57+ conventions . Patterns . ContentFiles ,
58+ conventions . Patterns . ResourceAssemblies ,
59+ } ;
60+
61+ // Add MSBuild to this list, but we need to ensure we have package assets before they make the cut.
62+ // A series of files in the right places won't matter if there's no {id}.props|targets.
63+ var msbuildPatterns = new [ ]
64+ {
65+ conventions . Patterns . MSBuildFiles ,
66+ conventions . Patterns . MSBuildMultiTargetingFiles ,
67+ } ;
68+
69+ // We'll create a set of "groups" --these are content items which satisfy file pattern sets
70+ var standardGroups = patterns
71+ . SelectMany ( p => items . FindItemGroups ( p ) ) ;
72+
73+ // Filter out MSBuild assets that don't match the package ID and append to groups we already have
74+ var msbuildGroups = msbuildPatterns
75+ . SelectMany ( p => items . FindItemGroups ( p ) )
76+ . Where ( g => HasBuildItemsForPackageId ( g . Items , packageId ) ) ;
77+ groups = standardGroups . Concat ( msbuildGroups ) ;
78+ }
79+
80+ // Now that we have a collection of groups which have made it through the pattern set filter, let's transform them into TFMs
81+ supportedTFMs = groups
82+ . SelectMany ( p => p . Properties )
83+ . Where ( pair => pair . Key == ManagedCodeConventions . PropertyNames . TargetFrameworkMoniker )
84+ . Select ( pair => pair . Value )
85+ . Cast < NuGetFramework > ( )
86+ . Distinct ( ) ;
87+ }
88+
89+ return supportedTFMs ;
90+ }
91+
92+ private static bool HasBuildItemsForPackageId ( IEnumerable < ContentItem > items , string packageId )
93+ {
94+ foreach ( var item in items )
95+ {
96+ var fileName = Path . GetFileName ( item . Path ) ;
97+ if ( fileName == PackagingCoreConstants . EmptyFolder )
98+ {
99+ return true ;
100+ }
101+
102+ if ( $ "{ packageId } .props". Equals ( fileName , StringComparison . OrdinalIgnoreCase ) )
103+ {
104+ return true ;
105+ }
106+
107+ if ( $ "{ packageId } .targets". Equals ( fileName , StringComparison . OrdinalIgnoreCase ) )
108+ {
109+ return true ;
110+ }
111+ }
112+
113+ return false ;
114+ }
115+
116+ /// <summary>
117+ /// Framework Generation shortname identifiers used by the Search Service for framework filtering.
118+ /// </summary>
119+ public static class FrameworkGenerationIdentifiers
120+ {
121+ public const string Net = "net" ;
122+
123+ public const string NetFramework = "netframework" ;
124+
125+ public const string NetCoreApp = "netcoreapp" ;
126+
127+ public const string NetStandard = "netstandard" ;
128+ }
129+ }
130+ }
0 commit comments