Skip to content

Commit 4887c69

Browse files
committed
C#: Choose between .NET framework or core DLLs in standalone
1 parent a31f946 commit 4887c69

File tree

5 files changed

+147
-56
lines changed

5 files changed

+147
-56
lines changed

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

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,6 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
7777
? allFiles.SelectFileNamesByExtension(".dll").ToList()
7878
: options.DllDirs.Select(Path.GetFullPath).ToList();
7979

80-
// Find DLLs in the .Net / Asp.Net Framework
81-
if (options.ScanNetFrameworkDlls)
82-
{
83-
var runtime = new Runtime(dotnet);
84-
var runtimeLocation = runtime.GetRuntime(options.UseSelfContainedDotnet);
85-
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
86-
dllDirNames.Add(runtimeLocation);
87-
88-
if (fileContent.UseAspNetDlls && runtime.GetAspRuntime() is string aspRuntime)
89-
{
90-
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspRuntime}");
91-
dllDirNames.Add(aspRuntime);
92-
}
93-
}
94-
9580
if (options.UseNuGet)
9681
{
9782
dllDirNames.Add(packageDirectory.DirInfo.FullName);
@@ -111,6 +96,43 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
11196
DownloadMissingPackages(allNonBinaryFiles);
11297
}
11398

99+
// Find DLLs in the .Net / Asp.Net Framework
100+
if (options.ScanNetFrameworkDlls)
101+
{
102+
// TODO: check if the nuget restore process has already downloaded any Core or Framework reference assemblies.
103+
// If so, we don't have to do the below.
104+
// `Microsoft.NETCore.App.Ref` or `Microsoft.NETFramework.ReferenceAssemblies.*`
105+
106+
var runtime = new Runtime(dotnet);
107+
string? runtimeLocation = null;
108+
109+
if (options.UseSelfContainedDotnet)
110+
{
111+
runtimeLocation = runtime.ExecutingRuntime;
112+
}
113+
else if (fileContent.IsNewProjectStructureUsed)
114+
{
115+
runtimeLocation = runtime.NetCoreRuntime;
116+
}
117+
else if (fileContent.IsLegacyProjectStructureUsed)
118+
{
119+
runtimeLocation = runtime.DesktopRuntime;
120+
}
121+
122+
runtimeLocation ??= runtime.ExecutingRuntime;
123+
124+
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
125+
dllDirNames.Add(runtimeLocation);
126+
127+
if (fileContent.IsNewProjectStructureUsed
128+
&& fileContent.UseAspNetCoreDlls
129+
&& runtime.AspNetCoreRuntime is string aspRuntime)
130+
{
131+
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspRuntime}");
132+
dllDirNames.Add(aspRuntime);
133+
}
134+
}
135+
114136
assemblyCache = new AssemblyCache(dllDirNames, progressMonitor);
115137
AnalyseSolutions(solutions);
116138

@@ -198,7 +220,7 @@ private void GenerateSourceFileFromImplicitUsings()
198220
usings.UnionWith(new[] { "System", "System.Collections.Generic", "System.IO", "System.Linq", "System.Net.Http", "System.Threading",
199221
"System.Threading.Tasks" });
200222

201-
if (fileContent.UseAspNetDlls)
223+
if (fileContent.UseAspNetCoreDlls)
202224
{
203225
usings.UnionWith(new[] { "System.Net.Http.Json", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Hosting",
204226
"Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Routing", "Microsoft.Extensions.Configuration",

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

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ public HashSet<string> AllPackages
3131
}
3232
}
3333

34-
private bool useAspNetDlls = false;
34+
private bool useAspNetCoreDlls = false;
3535

3636
/// <summary>
37-
/// True if any file in the source directory indicates that ASP.NET is used.
38-
/// The following heuristic is used to decide, if ASP.NET is used:
37+
/// True if any file in the source directory indicates that ASP.NET Core is used.
38+
/// The following heuristic is used to decide, if ASP.NET Core is used:
3939
/// If any file in the source directory contains something like (this will most like be a .csproj file)
4040
/// <Project Sdk="Microsoft.NET.Sdk.Web">
4141
/// <FrameworkReference Include="Microsoft.AspNetCore.App"/>
4242
/// </summary>
43-
public bool UseAspNetDlls
43+
public bool UseAspNetCoreDlls
4444
{
4545
get
4646
{
4747
initialize.Run();
48-
return useAspNetDlls;
48+
return useAspNetCoreDlls;
4949
}
5050
}
5151

@@ -60,6 +60,27 @@ public bool UseImplicitUsings
6060
}
6161
}
6262

63+
private bool isLegacyProjectStructureUsed = false;
64+
65+
public bool IsLegacyProjectStructureUsed
66+
{
67+
get
68+
{
69+
initialize.Run();
70+
return isLegacyProjectStructureUsed;
71+
}
72+
}
73+
74+
private bool isNewProjectStructureUsed = false;
75+
public bool IsNewProjectStructureUsed
76+
{
77+
get
78+
{
79+
initialize.Run();
80+
return isNewProjectStructureUsed;
81+
}
82+
}
83+
6384
public HashSet<string> CustomImplicitUsings
6485
{
6586
get
@@ -141,9 +162,9 @@ private void DoInitialize()
141162
}
142163

143164
// Determine if ASP.NET is used.
144-
if (!useAspNetDlls)
165+
if (!useAspNetCoreDlls)
145166
{
146-
useAspNetDlls =
167+
useAspNetCoreDlls =
147168
IsGroupMatch(line, ProjectSdk(), "Sdk", "Microsoft.NET.Sdk.Web") ||
148169
IsGroupMatch(line, FrameworkReference(), "Include", "Microsoft.AspNetCore.App");
149170
}
@@ -164,6 +185,12 @@ private void DoInitialize()
164185
implicitUsingNamespaces.Add(ns);
165186
}
166187
}
188+
189+
// Determine project structure:
190+
isLegacyProjectStructureUsed |= MicrosoftCSharpTargets().IsMatch(line);
191+
isNewProjectStructureUsed |= ProjectSdk().IsMatch(line);
192+
isNewProjectStructureUsed |= FrameworkReference().IsMatch(line);
193+
// TODO: we could also check `<Sdk Name="Microsoft.NET.Sdk" />`
167194
}
168195
}
169196
catch (Exception ex)
@@ -184,6 +211,9 @@ private void DoInitialize()
184211

185212
[GeneratedRegex("<Using.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
186213
private static partial Regex CustomImplicitUsingDeclarations();
214+
215+
[GeneratedRegex("<Import.*\\sProject=\".*Microsoft\\.CSharp\\.targets\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
216+
private static partial Regex MicrosoftCSharpTargets();
187217
}
188218

189219
internal interface IUnsafeFileReader

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

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ internal partial class Runtime
1919
private readonly IDotNet dotNet;
2020
private readonly Lazy<Dictionary<string, DotNetVersion>> newestRuntimes;
2121
private Dictionary<string, DotNetVersion> NewestRuntimes => newestRuntimes.Value;
22-
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
2322

2423
public Runtime(IDotNet dotNet)
2524
{
@@ -70,17 +69,17 @@ private static IEnumerable<string> DesktopRuntimes
7069
{
7170
get
7271
{
73-
var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono");
74-
var monoDirs = monoPath is not null
75-
? new[] { monoPath }
76-
: new[] { "/usr/lib/mono", @"C:\Program Files\Mono\lib\mono" };
77-
7872
if (Directory.Exists(@"C:\Windows\Microsoft.NET\Framework64"))
7973
{
8074
return Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*")
8175
.OrderByDescending(Path.GetFileName);
8276
}
8377

78+
var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono");
79+
var monoDirs = monoPath is not null
80+
? new[] { Path.GetFullPath(Path.Combine(monoPath, "..", "lib", "mono")), monoPath }
81+
: new[] { "/usr/lib/mono", "/usr/local/mono", "/usr/local/bin/mono", @"C:\Program Files\Mono\lib\mono" };
82+
8483
var dir = monoDirs.FirstOrDefault(Directory.Exists);
8584

8685
if (dir is not null)
@@ -107,33 +106,23 @@ private static IEnumerable<string> DesktopRuntimes
107106
}
108107

109108
/// <summary>
110-
/// Gets the .NET runtime location to use for extraction.
109+
/// Gets the Dotnet Core location.
111110
/// </summary>
112-
public string GetRuntime(bool useSelfContained)
113-
{
114-
if (useSelfContained)
115-
{
116-
return ExecutingRuntime;
117-
}
111+
public string? NetCoreRuntime => GetVersion(netCoreApp);
118112

119-
// Location of the newest .NET Core Runtime.
120-
if (GetVersion(netCoreApp) is string path)
121-
{
122-
return path;
123-
}
124-
125-
if (DesktopRuntimes.Any())
126-
{
127-
return DesktopRuntimes.First();
128-
}
113+
/// <summary>
114+
/// Gets the .NET Framework location. Either the installation folder on Windows or Mono
115+
/// </summary>
116+
public string? DesktopRuntime => DesktopRuntimes?.FirstOrDefault();
129117

130-
// A bad choice if it's the self-contained runtime distributed in codeql dist.
131-
return ExecutingRuntime;
132-
}
118+
/// <summary>
119+
/// Gets the executiing runtime location, this is the self contained runtime shipped in the CodeQL CLI bundle.
120+
/// </summary>
121+
public string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
133122

134123
/// <summary>
135-
/// Gets the ASP.NET runtime location to use for extraction, if one exists.
124+
/// Gets the ASP.NET Core location.
136125
/// </summary>
137-
public string? GetAspRuntime() => GetVersion(aspNetCoreApp);
126+
public string? AspNetCoreRuntime => GetVersion(aspNetCoreApp);
138127
}
139128
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Semmle.Extraction.CSharp.DependencyFetching.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
99
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
1010
<Nullable>enable</Nullable>
11+
<NoWarn>$(NoWarn);CA1822</NoWarn>
1112
</PropertyGroup>
1213

1314
<ItemGroup>

csharp/extractor/Semmle.Extraction.Tests/FileContent.cs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using Xunit;
22
using System.Collections.Generic;
33
using Semmle.Extraction.CSharp.DependencyFetching;
4+
using System;
45

56
namespace Semmle.Extraction.Tests
67
{
78
internal class UnsafeFileReaderStub : IUnsafeFileReader
89
{
9-
private readonly List<string> lines;
10+
private readonly IEnumerable<string> lines;
1011

11-
public UnsafeFileReaderStub(List<string> lines)
12+
public UnsafeFileReaderStub(IEnumerable<string> lines)
1213
{
1314
this.lines = lines;
1415
}
@@ -24,7 +25,7 @@ public IEnumerable<string> ReadLines(string file)
2425

2526
internal class TestFileContent : FileContent
2627
{
27-
public TestFileContent(List<string> lines) : base(new ProgressMonitor(new LoggerStub()),
28+
public TestFileContent(IEnumerable<string> lines) : base(new ProgressMonitor(new LoggerStub()),
2829
new List<string>() { "test1.cs" },
2930
new UnsafeFileReaderStub(lines))
3031
{ }
@@ -48,7 +49,7 @@ public void TestFileContent1()
4849

4950
// Execute
5051
var allPackages = fileContent.AllPackages;
51-
var useAspNetDlls = fileContent.UseAspNetDlls;
52+
var useAspNetDlls = fileContent.UseAspNetCoreDlls;
5253

5354
// Verify
5455
Assert.False(useAspNetDlls);
@@ -72,7 +73,7 @@ public void TestFileContent2()
7273
var fileContent = new TestFileContent(lines);
7374

7475
// Execute
75-
var useAspNetDlls = fileContent.UseAspNetDlls;
76+
var useAspNetDlls = fileContent.UseAspNetCoreDlls;
7677
var allPackages = fileContent.AllPackages;
7778

7879
// Verify
@@ -136,5 +137,53 @@ public void TestFileContent_ImplicitUsingsAdditional()
136137
Assert.Contains("Ns0.Ns1", customImplicitUsings);
137138
Assert.Contains("Ns2", customImplicitUsings);
138139
}
140+
141+
[Fact]
142+
public void TestFileContent_LegacyProjectStructure()
143+
{
144+
// Setup
145+
var input =
146+
"""
147+
<?xml version="1.0" encoding="utf-8"?>
148+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
149+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
150+
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
151+
""";
152+
var lines = input.Split(Environment.NewLine);
153+
var fileContent = new TestFileContent(lines);
154+
155+
// Execute
156+
var isLegacy = fileContent.IsLegacyProjectStructureUsed;
157+
var isNew = fileContent.IsNewProjectStructureUsed;
158+
159+
// Verify
160+
Assert.True(isLegacy);
161+
Assert.False(isNew);
162+
}
163+
164+
[Fact]
165+
public void TestFileContent_NewProjectStructure()
166+
{
167+
// Setup
168+
var input =
169+
"""
170+
<Project Sdk="Microsoft.NET.Sdk">
171+
<PropertyGroup>
172+
<TargetFrameworks>net461;net70</TargetFrameworks>
173+
</PropertyGroup>
174+
</Project>
175+
""";
176+
var lines = input.Split(Environment.NewLine);
177+
178+
var fileContent = new TestFileContent(lines);
179+
180+
// Execute
181+
var isLegacy = fileContent.IsLegacyProjectStructureUsed;
182+
var isNew = fileContent.IsNewProjectStructureUsed;
183+
184+
// Verify
185+
Assert.True(isNew);
186+
Assert.False(isLegacy);
187+
}
139188
}
140189
}

0 commit comments

Comments
 (0)