Skip to content

Commit 9f375de

Browse files
committed
C#: Improve logic for looking up .NET runtime in standalone mode
Instead of only considering a fixed set of paths for `dotnet` and `mono`, first attempt to lookup the paths based on the `PATH` environment variable. This change also fixes a potential `System.IO.DirectoryNotFoundException` exception, which could be thrown when the `shared/Microsoft.NETCore.App` folder was not present.
1 parent 23a2bf1 commit 9f375de

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Runtime.InteropServices;
44
using System.IO;
55
using System.Linq;
6+
using Semmle.Util;
67

78
namespace Semmle.Extraction.CSharp.Standalone
89
{
@@ -20,11 +21,14 @@ public static IEnumerable<string> CoreRuntimes
2021
{
2122
get
2223
{
23-
string[] dotnetDirs = { "/usr/share/dotnet", @"C:\Program Files\dotnet" };
24+
var dotnetPath = FileUtils.FindExecutableOnPath("dotnet");
25+
var dotnetDirs = dotnetPath != null
26+
? new[] { dotnetPath }
27+
: new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" };
28+
var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App"));
2429

25-
foreach (var dir in dotnetDirs.Where(Directory.Exists))
26-
return Directory.EnumerateDirectories(Path.Combine(dir, "shared", "Microsoft.NETCore.App")).
27-
OrderByDescending(d => Path.GetFileName(d));
30+
foreach (var dir in coreDirs.Where(Directory.Exists))
31+
return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName);
2832
return Enumerable.Empty<string>();
2933
}
3034
}
@@ -37,19 +41,22 @@ public static IEnumerable<string> DesktopRuntimes
3741
{
3842
get
3943
{
40-
string[] monoDirs = { "/usr/lib/mono", @"C:\Program Files\Mono\lib\mono" };
44+
var monoPath = FileUtils.FindExecutableOnPath("mono");
45+
var monoDirs = monoPath != null
46+
? new[] { monoPath }
47+
: new[] { "/usr/lib/mono", @"C:\Program Files\Mono\lib\mono" };
4148

4249
if (Directory.Exists(@"C:\Windows\Microsoft.NET\Framework64"))
4350
{
44-
return System.IO.Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*").
45-
OrderByDescending(d => Path.GetFileName(d));
51+
return Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*").
52+
OrderByDescending(Path.GetFileName);
4653
}
4754

4855
foreach (var dir in monoDirs.Where(Directory.Exists))
4956
{
50-
return System.IO.Directory.EnumerateDirectories(dir).
57+
return Directory.EnumerateDirectories(dir).
5158
Where(d => Char.IsDigit(Path.GetFileName(d)[0])).
52-
OrderByDescending(d => Path.GetFileName(d));
59+
OrderByDescending(Path.GetFileName);
5360
}
5461

5562
return Enumerable.Empty<string>();

csharp/extractor/Semmle.Util/FileUtils.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System;
12
using System.IO;
3+
using System.Linq;
24

35
namespace Semmle.Util
46
{
@@ -50,5 +52,23 @@ public static void TryDelete(string file)
5052
// Ignore
5153
}
5254
}
55+
56+
/// <summary>
57+
/// Finds the path for the executable <paramref name="exe"/> based on the
58+
/// <code>PATH</code> environment variable, and in the case of Windows the
59+
/// <code>PATHEXT</code> environment variable.
60+
///
61+
/// Returns <code>null</code> of no path can be found.
62+
/// </summary>
63+
public static string FindExecutableOnPath(string exe)
64+
{
65+
var isWindows = Win32.IsWindows();
66+
var paths = Environment.GetEnvironmentVariable("PATH").Split(isWindows ? ';' : ':');
67+
var exes = isWindows
68+
? Environment.GetEnvironmentVariable("PATHEXT").Split(';').Select(ext => exe + ext)
69+
: new[] { exe };
70+
var candidates = paths.Where(path => exes.Any(exe0 => File.Exists(Path.Combine(path, exe0))));
71+
return candidates.FirstOrDefault();
72+
}
5373
}
5474
}

0 commit comments

Comments
 (0)