Skip to content

Commit 44be7df

Browse files
authored
Merge pull request #14957 from tamasvajk/standalone/prefer-framework-assemblies
C#: Prefer framework assemblies over arbitrary nuget equivalents
2 parents d9ca912 + 31c1caf commit 44be7df

File tree

3 files changed

+54
-26
lines changed

3 files changed

+54
-26
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/AssemblyCache.cs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.IO;
43
using System.Linq;
54

@@ -20,7 +19,7 @@ internal class AssemblyCache
2019
/// assembly cache.
2120
/// </param>
2221
/// <param name="progressMonitor">Callback for progress.</param>
23-
public AssemblyCache(IEnumerable<string> paths, ProgressMonitor progressMonitor)
22+
public AssemblyCache(IEnumerable<string> paths, IEnumerable<string> frameworkPaths, ProgressMonitor progressMonitor)
2423
{
2524
foreach (var path in paths)
2625
{
@@ -40,7 +39,7 @@ public AssemblyCache(IEnumerable<string> paths, ProgressMonitor progressMonitor)
4039
progressMonitor.LogInfo("AssemblyCache: Path not found: " + path);
4140
}
4241
}
43-
IndexReferences();
42+
IndexReferences(frameworkPaths);
4443
}
4544

4645
/// <summary>
@@ -57,27 +56,21 @@ private void AddReferenceDirectory(string dir)
5756
}
5857
}
5958

60-
private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
61-
6259
/// <summary>
6360
/// Indexes all DLLs we have located.
6461
/// Because this is a potentially time-consuming operation, it is put into a separate stage.
6562
/// </summary>
66-
private void IndexReferences()
63+
private void IndexReferences(IEnumerable<string> frameworkPaths)
6764
{
6865
// Read all of the files
6966
foreach (var filename in pendingDllsToIndex)
7067
{
7168
IndexReference(filename);
7269
}
7370

74-
// Index "assemblyInfo" by version string
75-
// The OrderBy is used to ensure that we by default select the highest version number.
7671
foreach (var info in assemblyInfoByFileName.Values
7772
.OrderBy(info => info.Name)
78-
.ThenBy(info => info.NetCoreVersion ?? emptyVersion)
79-
.ThenBy(info => info.Version ?? emptyVersion)
80-
.ThenBy(info => info.Filename))
73+
.OrderAssemblyInfosByPreference(frameworkPaths))
8174
{
8275
foreach (var index in info.IndexStrings)
8376
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Semmle.Extraction.CSharp.DependencyFetching
6+
{
7+
internal static class AssemblyCacheExtensions
8+
{
9+
private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
10+
11+
/// <summary>
12+
/// This method orders AssemblyInfos by version numbers (.net core version first, then assembly version). Finally, it orders by filename to make the order deterministic.
13+
/// </summary>
14+
public static IOrderedEnumerable<AssemblyInfo> OrderAssemblyInfosByPreference(this IEnumerable<AssemblyInfo> assemblies, IEnumerable<string> frameworkPaths)
15+
{
16+
// prefer framework assemblies over others
17+
int initialOrdering(AssemblyInfo info) => frameworkPaths.Any(framework => info.Filename.StartsWith(framework, StringComparison.OrdinalIgnoreCase)) ? 1 : 0;
18+
19+
var ordered = assemblies is IOrderedEnumerable<AssemblyInfo> o
20+
? o.ThenBy(initialOrdering)
21+
: assemblies.OrderBy(initialOrdering);
22+
23+
return ordered
24+
.ThenBy(info => info.NetCoreVersion ?? emptyVersion)
25+
.ThenBy(info => info.Version ?? emptyVersion)
26+
.ThenBy(info => info.Filename);
27+
}
28+
}
29+
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,18 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
128128
DownloadMissingPackages(allNonBinaryFiles, dllPaths);
129129
}
130130

131+
var frameworkLocations = new HashSet<string>();
132+
131133
// Find DLLs in the .Net / Asp.Net Framework
132134
// This block needs to come after the nuget restore, because the nuget restore might fetch the .NET Core/Framework reference assemblies.
133135
if (options.ScanNetFrameworkDlls)
134136
{
135-
AddNetFrameworkDlls(dllPaths);
136-
AddAspNetCoreFrameworkDlls(dllPaths);
137-
AddMicrosoftWindowsDesktopDlls(dllPaths);
137+
AddNetFrameworkDlls(dllPaths, frameworkLocations);
138+
AddAspNetCoreFrameworkDlls(dllPaths, frameworkLocations);
139+
AddMicrosoftWindowsDesktopDlls(dllPaths, frameworkLocations);
138140
}
139141

140-
assemblyCache = new AssemblyCache(dllPaths, progressMonitor);
142+
assemblyCache = new AssemblyCache(dllPaths, frameworkLocations, progressMonitor);
141143
AnalyseSolutions(solutions);
142144

143145
foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename))
@@ -146,7 +148,7 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
146148
}
147149

148150
RemoveNugetAnalyzerReferences();
149-
ResolveConflicts();
151+
ResolveConflicts(frameworkLocations);
150152

151153
// Output the findings
152154
foreach (var r in usedReferences.Keys.OrderBy(r => r))
@@ -228,7 +230,7 @@ private void RemoveNugetAnalyzerReferences()
228230
}
229231
}
230232

231-
private void AddNetFrameworkDlls(ISet<string> dllPaths)
233+
private void AddNetFrameworkDlls(ISet<string> dllPaths, ISet<string> frameworkLocations)
232234
{
233235
// Multiple dotnet framework packages could be present.
234236
// The order of the packages is important, we're adding the first one that is present in the nuget cache.
@@ -241,6 +243,7 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths)
241243
if (frameworkPath.Path is not null)
242244
{
243245
dllPaths.Add(frameworkPath.Path);
246+
frameworkLocations.Add(frameworkPath.Path);
244247
progressMonitor.LogInfo($"Found .NET Core/Framework DLLs in NuGet packages at {frameworkPath.Path}. Not adding installation directory.");
245248

246249
for (var i = frameworkPath.Index + 1; i < packagesInPrioOrder.Length; i++)
@@ -270,6 +273,7 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths)
270273

271274
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
272275
dllPaths.Add(runtimeLocation);
276+
frameworkLocations.Add(runtimeLocation);
273277
}
274278

275279
private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllPaths)
@@ -294,7 +298,7 @@ private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllP
294298
}
295299
}
296300

297-
private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
301+
private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths, ISet<string> frameworkLocations)
298302
{
299303
if (!fileContent.IsNewProjectStructureUsed || !fileContent.UseAspNetCoreDlls)
300304
{
@@ -306,20 +310,25 @@ private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
306310
{
307311
progressMonitor.LogInfo($"Found ASP.NET Core in NuGet packages. Not adding installation directory.");
308312
dllPaths.Add(aspNetCorePackage);
313+
frameworkLocations.Add(aspNetCorePackage);
314+
return;
309315
}
310-
else if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
316+
317+
if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
311318
{
312319
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspNetCoreRuntime}");
313320
dllPaths.Add(aspNetCoreRuntime);
321+
frameworkLocations.Add(aspNetCoreRuntime);
314322
}
315323
}
316324

317-
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
325+
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths, ISet<string> frameworkLocations)
318326
{
319327
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework) is string windowsDesktopApp)
320328
{
321329
progressMonitor.LogInfo($"Found Windows Desktop App in NuGet packages.");
322330
dllPaths.Add(windowsDesktopApp);
331+
frameworkLocations.Add(windowsDesktopApp);
323332
}
324333
}
325334

@@ -472,7 +481,7 @@ private string GetTemporaryWorkingDirectory(string subfolder)
472481
/// If the same assembly name is duplicated with different versions,
473482
/// resolve to the higher version number.
474483
/// </summary>
475-
private void ResolveConflicts()
484+
private void ResolveConflicts(IEnumerable<string> frameworkPaths)
476485
{
477486
var sortedReferences = new List<AssemblyInfo>();
478487
foreach (var usedReference in usedReferences)
@@ -488,11 +497,8 @@ private void ResolveConflicts()
488497
}
489498
}
490499

491-
var emptyVersion = new Version(0, 0);
492500
sortedReferences = sortedReferences
493-
.OrderBy(r => r.NetCoreVersion ?? emptyVersion)
494-
.ThenBy(r => r.Version ?? emptyVersion)
495-
.ThenBy(r => r.Filename)
501+
.OrderAssemblyInfosByPreference(frameworkPaths)
496502
.ToList();
497503

498504
var finalAssemblyList = new Dictionary<string, AssemblyInfo>();

0 commit comments

Comments
 (0)