Skip to content

Commit 0cc94b3

Browse files
committed
C#: Prefer framework assemblies over arbitrary nuget equivalents
1 parent fefc02d commit 0cc94b3

File tree

3 files changed

+67
-28
lines changed

3 files changed

+67
-28
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+
bool initialOrdering(AssemblyInfo info) => frameworkPaths.Any(framework => info.Filename.StartsWith(framework, StringComparison.OrdinalIgnoreCase));
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: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,27 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
128128
DownloadMissingPackages(allNonBinaryFiles, dllPaths);
129129
}
130130

131+
var frameworkLocations = new List<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+
var path = AddNetFrameworkDlls(dllPaths);
138+
frameworkLocations.Add(path);
139+
path = AddAspNetCoreFrameworkDlls(dllPaths);
140+
if (path != null)
141+
{
142+
frameworkLocations.Add(path);
143+
}
144+
path = AddMicrosoftWindowsDesktopDlls(dllPaths);
145+
if (path != null)
146+
{
147+
frameworkLocations.Add(path);
148+
}
138149
}
139150

140-
assemblyCache = new AssemblyCache(dllPaths, progressMonitor);
151+
assemblyCache = new AssemblyCache(dllPaths, frameworkLocations, progressMonitor);
141152
AnalyseSolutions(solutions);
142153

143154
foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename))
@@ -146,7 +157,7 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
146157
}
147158

148159
RemoveNugetAnalyzerReferences();
149-
ResolveConflicts();
160+
ResolveConflicts(frameworkLocations);
150161

151162
// Output the findings
152163
foreach (var r in usedReferences.Keys.OrderBy(r => r))
@@ -228,7 +239,7 @@ private void RemoveNugetAnalyzerReferences()
228239
}
229240
}
230241

231-
private void AddNetFrameworkDlls(ISet<string> dllPaths)
242+
private string AddNetFrameworkDlls(ISet<string> dllPaths)
232243
{
233244
// Multiple dotnet framework packages could be present.
234245
// The order of the packages is important, we're adding the first one that is present in the nuget cache.
@@ -248,7 +259,7 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths)
248259
RemoveNugetPackageReference(packagesInPrioOrder[i], dllPaths);
249260
}
250261

251-
return;
262+
return frameworkPath.Path;
252263
}
253264

254265
string? runtimeLocation = null;
@@ -270,6 +281,7 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths)
270281

271282
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
272283
dllPaths.Add(runtimeLocation);
284+
return runtimeLocation;
273285
}
274286

275287
private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllPaths)
@@ -294,33 +306,41 @@ private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllP
294306
}
295307
}
296308

297-
private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
309+
private string? AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
298310
{
299311
if (!fileContent.IsNewProjectStructureUsed || !fileContent.UseAspNetCoreDlls)
300312
{
301-
return;
313+
return null;
302314
}
303315

304316
// First try to find ASP.NET Core assemblies in the NuGet packages
305317
if (GetPackageDirectory(FrameworkPackageNames.AspNetCoreFramework) is string aspNetCorePackage)
306318
{
307319
progressMonitor.LogInfo($"Found ASP.NET Core in NuGet packages. Not adding installation directory.");
308320
dllPaths.Add(aspNetCorePackage);
321+
return aspNetCorePackage;
309322
}
310-
else if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
323+
324+
if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
311325
{
312326
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspNetCoreRuntime}");
313327
dllPaths.Add(aspNetCoreRuntime);
328+
return aspNetCoreRuntime;
314329
}
330+
331+
return null;
315332
}
316333

317-
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
334+
private string? AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
318335
{
319336
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework) is string windowsDesktopApp)
320337
{
321338
progressMonitor.LogInfo($"Found Windows Desktop App in NuGet packages.");
322339
dllPaths.Add(windowsDesktopApp);
340+
return windowsDesktopApp;
323341
}
342+
343+
return null;
324344
}
325345

326346
private string? GetPackageDirectory(string packagePrefix)
@@ -472,7 +492,7 @@ private string GetTemporaryWorkingDirectory(string subfolder)
472492
/// If the same assembly name is duplicated with different versions,
473493
/// resolve to the higher version number.
474494
/// </summary>
475-
private void ResolveConflicts()
495+
private void ResolveConflicts(IEnumerable<string> frameworkPaths)
476496
{
477497
var sortedReferences = new List<AssemblyInfo>();
478498
foreach (var usedReference in usedReferences)
@@ -488,11 +508,8 @@ private void ResolveConflicts()
488508
}
489509
}
490510

491-
var emptyVersion = new Version(0, 0);
492511
sortedReferences = sortedReferences
493-
.OrderBy(r => r.NetCoreVersion ?? emptyVersion)
494-
.ThenBy(r => r.Version ?? emptyVersion)
495-
.ThenBy(r => r.Filename)
512+
.OrderAssemblyInfosByPreference(frameworkPaths)
496513
.ToList();
497514

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

0 commit comments

Comments
 (0)