mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
C#: Use dotnet --list-runtimes to find runtime locations.
This commit is contained in:
@@ -66,7 +66,7 @@ namespace Semmle.BuildAnalyser
|
||||
// Find DLLs in the .Net Framework
|
||||
if (options.ScanNetFrameworkDlls)
|
||||
{
|
||||
var runtimeLocation = Runtime.GetRuntime(options.UseSelfContainedDotnet);
|
||||
var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet);
|
||||
progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}");
|
||||
dllDirNames.Add(runtimeLocation);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.BuildAnalyser
|
||||
{
|
||||
@@ -8,6 +10,7 @@ namespace Semmle.BuildAnalyser
|
||||
/// </summary>
|
||||
internal class DotNet
|
||||
{
|
||||
private const string dotnet = "dotnet";
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
|
||||
public DotNet(ProgressMonitor progressMonitor)
|
||||
@@ -19,26 +22,26 @@ namespace Semmle.BuildAnalyser
|
||||
private void Info()
|
||||
{
|
||||
// TODO: make sure the below `dotnet` version is matching the one specified in global.json
|
||||
progressMonitor.RunningProcess("dotnet --info");
|
||||
using var proc = Process.Start("dotnet", "--info");
|
||||
progressMonitor.RunningProcess($"{dotnet} --info");
|
||||
using var proc = Process.Start(dotnet, "--info");
|
||||
proc.WaitForExit();
|
||||
var ret = proc.ExitCode;
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed("dotnet", "--info", ret);
|
||||
throw new Exception($"dotnet --info failed with exit code {ret}.");
|
||||
progressMonitor.CommandFailed(dotnet, "--info", ret);
|
||||
throw new Exception($"{dotnet} --info failed with exit code {ret}.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool RunCommand(string args)
|
||||
{
|
||||
progressMonitor.RunningProcess($"dotnet {args}");
|
||||
using var proc = Process.Start("dotnet", args);
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
using var proc = Process.Start(dotnet, args);
|
||||
proc.WaitForExit();
|
||||
if (proc.ExitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed("dotnet", args, proc.ExitCode);
|
||||
progressMonitor.CommandFailed(dotnet, args, proc.ExitCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -62,5 +65,22 @@ namespace Semmle.BuildAnalyser
|
||||
var args = $"add \"{folder}\" package \"{package}\" --no-restore";
|
||||
return RunCommand(args);
|
||||
}
|
||||
|
||||
public IList<string> GetListedRuntimes()
|
||||
{
|
||||
var args = "--list-runtimes";
|
||||
var pi = new ProcessStartInfo(dotnet, args)
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
var exitCode = pi.ReadOutput(out var runtimes);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(dotnet, args, exitCode);
|
||||
return new List<string>();
|
||||
}
|
||||
return runtimes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semmle.BuildAnalyser;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Standalone
|
||||
@@ -10,33 +12,97 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
/// <summary>
|
||||
/// Locates .NET Runtimes.
|
||||
/// </summary>
|
||||
internal static class Runtime
|
||||
internal partial class Runtime
|
||||
{
|
||||
private readonly DotNet dotNet;
|
||||
public Runtime(DotNet dotNet) => this.dotNet = dotNet;
|
||||
|
||||
private sealed class Version : IComparable<Version>
|
||||
{
|
||||
private readonly string Dir;
|
||||
public int Major { get; }
|
||||
public int Minor { get; }
|
||||
public int Patch { get; }
|
||||
|
||||
|
||||
public string FullPath => Path.Combine(Dir, this.ToString());
|
||||
|
||||
|
||||
public Version(string version, string dir)
|
||||
{
|
||||
var parts = version.Split('.');
|
||||
Major = int.Parse(parts[0]);
|
||||
Minor = int.Parse(parts[1]);
|
||||
Patch = int.Parse(parts[2]);
|
||||
Dir = dir;
|
||||
}
|
||||
|
||||
public int CompareTo(Version? other) =>
|
||||
other is null ? 1 : GetHashCode().CompareTo(other.GetHashCode());
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is not null && obj is Version other && other.FullPath == FullPath;
|
||||
|
||||
public override int GetHashCode() =>
|
||||
(Major * 1000 + Minor) * 1000 + Patch;
|
||||
|
||||
public override string ToString() =>
|
||||
$"{Major}.{Minor}.{Patch}";
|
||||
}
|
||||
|
||||
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// Locates .NET Core Runtimes.
|
||||
/// </summary>
|
||||
private static IEnumerable<string> CoreRuntimes
|
||||
private static readonly string NetCoreApp = "Microsoft.NETCore.App";
|
||||
private static readonly string AspNetCoreApp = "Microsoft.AspNetCore.App";
|
||||
|
||||
private static void AddOrUpdate(Dictionary<string, Version> dict, string framework, Version version)
|
||||
{
|
||||
get
|
||||
if (!dict.TryGetValue(framework, out var existing) || existing.CompareTo(version) < 0)
|
||||
{
|
||||
var dotnetPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "dotnet.exe" : "dotnet");
|
||||
var dotnetDirs = dotnetPath is not null
|
||||
? new[] { dotnetPath }
|
||||
: new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" };
|
||||
var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App"));
|
||||
|
||||
var dir = coreDirs.FirstOrDefault(Directory.Exists);
|
||||
if (dir is not null)
|
||||
{
|
||||
return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
dict[framework] = version;
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)\s\[(\S+)\]$")]
|
||||
private static partial Regex RuntimeRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Parses the output of `dotnet --list-runtimes` to get a map from a runtime to the location of
|
||||
/// the newest version of the runtime.
|
||||
/// It is assume that the format of a listed runtime is something like:
|
||||
/// Microsoft.NETCore.App 7.0.2 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
|
||||
/// </summary>
|
||||
private static Dictionary<string, Version> ParseRuntimes(IList<string> listed)
|
||||
{
|
||||
// Parse listed runtimes.
|
||||
var runtimes = new Dictionary<string, Version>();
|
||||
listed.ForEach(r =>
|
||||
{
|
||||
var match = RuntimeRegex().Match(r);
|
||||
if (match.Success)
|
||||
{
|
||||
AddOrUpdate(runtimes, match.Groups[1].Value, new Version(match.Groups[2].Value, match.Groups[3].Value));
|
||||
}
|
||||
});
|
||||
|
||||
return runtimes;
|
||||
}
|
||||
|
||||
private Dictionary<string, Version> GetNewestRuntimes()
|
||||
{
|
||||
try
|
||||
{
|
||||
var listed = dotNet.GetListedRuntimes();
|
||||
return ParseRuntimes(listed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ex is System.ComponentModel.Win32Exception || ex is FileNotFoundException)
|
||||
{
|
||||
return new Dictionary<string, Version>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Locates .NET Desktop Runtimes.
|
||||
/// This includes Mono and Microsoft.NET.
|
||||
@@ -69,24 +135,33 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetRuntimes()
|
||||
{
|
||||
// Gets the newest version of the installed runtimes.
|
||||
var newestRuntimes = GetNewestRuntimes();
|
||||
|
||||
// Location of the newest .NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(NetCoreApp, out var netCoreApp))
|
||||
{
|
||||
yield return netCoreApp.FullPath;
|
||||
}
|
||||
|
||||
// Location of the newest ASP.NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(AspNetCoreApp, out var aspNetCoreApp))
|
||||
{
|
||||
yield return aspNetCoreApp.FullPath;
|
||||
}
|
||||
|
||||
foreach (var r in DesktopRuntimes)
|
||||
yield return r;
|
||||
|
||||
// A bad choice if it's the self-contained runtime distributed in codeql dist.
|
||||
yield return ExecutingRuntime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the .NET runtime location to use for extraction
|
||||
/// </summary>
|
||||
public static string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : Runtimes.First();
|
||||
|
||||
private static IEnumerable<string> Runtimes
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var r in CoreRuntimes)
|
||||
yield return r;
|
||||
|
||||
foreach (var r in DesktopRuntimes)
|
||||
yield return r;
|
||||
|
||||
// A bad choice if it's the self-contained runtime distributed in codeql dist.
|
||||
yield return ExecutingRuntime;
|
||||
}
|
||||
}
|
||||
public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user