|
2 | 2 |
|
3 | 3 | using Microsoft.Win32; |
4 | 4 | using System; |
| 5 | +using System.Collections.Generic; |
| 6 | +using System.Diagnostics; |
5 | 7 | using System.IO; |
6 | | -using System.Runtime.InteropServices; |
| 8 | +using System.Linq; |
7 | 9 |
|
8 | 10 | namespace NUnit.Engine |
9 | 11 | { |
10 | 12 | public static class DotNet |
11 | 13 | { |
12 | | - public static string GetInstallDirectory() => Environment.Is64BitProcess |
13 | | - ? GetX64InstallDirectory() : GetX86InstallDirectory(); |
| 14 | + private const string X64_SUBKEY1 = @"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\"; |
| 15 | + private const string X64_SUBKEY2 = @"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x64\"; |
| 16 | + private const string X86_SUBKEY1 = @"SOFTWARE\dotnet\SetUp\InstalledVersions\x86\InstallLocation\"; |
| 17 | + private const string X86_SUBKEY2 = @"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\"; |
14 | 18 |
|
15 | | - public static string GetInstallDirectory(bool x86) => x86 |
16 | | - ? GetX86InstallDirectory() : GetX64InstallDirectory(); |
| 19 | +#pragma warning disable CA1416 |
| 20 | + private static readonly Lazy<string> _x64InstallDirectory = new Lazy<string>(() => |
| 21 | + Environment.GetEnvironmentVariable("DOTNET_ROOT") ?? ( |
| 22 | + OS.IsWindows |
| 23 | + ? (string) Registry.LocalMachine.OpenSubKey(X64_SUBKEY1)?.GetValue("Path") ?? |
| 24 | + (string) Registry.LocalMachine.OpenSubKey(X64_SUBKEY2)?.GetValue("Path") ?? @"C:\Program Files\dotnet" |
| 25 | + : "/usr/shared/dotnet/")); |
| 26 | + private static readonly Lazy<string> _x86InstallDirectory = new Lazy<string>(() => |
| 27 | + Environment.GetEnvironmentVariable("DOTNET_ROOT_X86") ?? ( |
| 28 | + OS.IsWindows |
| 29 | + ? (string) Registry.LocalMachine.OpenSubKey(X86_SUBKEY1)?.GetValue("InstallLocation") ?? |
| 30 | + (string) Registry.LocalMachine.OpenSubKey(X86_SUBKEY2)?.GetValue("InstallLocation") ?? @"C:\Program Files (x86)\dotnet" |
| 31 | + : "/usr/shared/dotnet/")); |
| 32 | +#pragma warning restore CA1416 |
| 33 | + |
| 34 | + private static Lazy<List<RuntimeInfo>> _x64Runtimes = new Lazy<List<RuntimeInfo>>(() => [.. GetAllRuntimes(x86: false)]); |
| 35 | + private static Lazy<List<RuntimeInfo>> _x86Runtimes = new Lazy<List<RuntimeInfo>>(() => [.. GetAllRuntimes(x86: true)]); |
| 36 | + |
| 37 | + public enum Architecture |
| 38 | + { |
| 39 | + Unspecified, |
| 40 | + X64, |
| 41 | + X86 |
| 42 | + } |
17 | 43 |
|
18 | | - private static string _x64InstallDirectory; |
19 | | - public static string GetX64InstallDirectory() |
| 44 | + public class RuntimeInfo |
20 | 45 | { |
21 | | - if (_x64InstallDirectory == null) |
22 | | - _x64InstallDirectory = Environment.GetEnvironmentVariable("DOTNET_ROOT"); |
| 46 | + public string Name; |
| 47 | + public Version Version; |
| 48 | + public string Path; |
| 49 | + |
| 50 | + public RuntimeInfo(string name, string version, string path) |
| 51 | + : this(name, new Version(version), path) { } |
23 | 52 |
|
24 | | - if (_x64InstallDirectory == null) |
| 53 | + public RuntimeInfo(string name, Version version, string path) |
25 | 54 | { |
26 | | -#if NETFRAMEWORK |
27 | | - if (Path.DirectorySeparatorChar == '\\') |
28 | | -#else |
29 | | - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
30 | | -#endif |
31 | | - { |
32 | | - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\"); |
33 | | - _x64InstallDirectory = (string)key?.GetValue("Path"); |
34 | | - } |
35 | | - else |
36 | | - _x64InstallDirectory = "/usr/shared/dotnet/"; |
| 55 | + Name = name; |
| 56 | + Version = version; |
| 57 | + Path = path; |
37 | 58 | } |
| 59 | + } |
| 60 | + |
| 61 | + /// <summary> |
| 62 | + /// Get the correct install directory, depending on whether we need X86 or X64 architecture. |
| 63 | + /// </summary> |
| 64 | + /// <param name="x86">Flag indicating whether the X86 architecture is needed</param> |
| 65 | + /// <returns></returns> |
| 66 | + public static string GetInstallDirectory(bool x86) => x86 |
| 67 | + ? _x86InstallDirectory.Value : _x64InstallDirectory.Value; |
38 | 68 |
|
39 | | - return _x64InstallDirectory; |
| 69 | + /// <summary> |
| 70 | + /// Get the correct dotnet.exe, depending on whether we need X86 or X64 architecture. |
| 71 | + /// </summary> |
| 72 | + /// <param name="x86">Flag indicating whether the X86 architecture is needed</param> |
| 73 | + /// <returns></returns> |
| 74 | + public static string GetDotnetExecutable(bool x86) => Path.Combine(GetInstallDirectory(x86), OS.IsWindows ? "dotnet.exe" : "dotnet"); |
| 75 | + |
| 76 | + public static IEnumerable<RuntimeInfo> GetRuntimes(string name, bool x86) |
| 77 | + { |
| 78 | + var runtimes = x86 ? _x86Runtimes.Value : _x64Runtimes.Value; |
| 79 | + return runtimes.Where(r => r.Name == name); |
40 | 80 | } |
41 | 81 |
|
42 | | - private static string _x86InstallDirectory; |
43 | | - public static string GetX86InstallDirectory() |
| 82 | + private static IEnumerable<RuntimeInfo> GetAllRuntimes(bool x86) |
44 | 83 | { |
45 | | - if (_x86InstallDirectory == null) |
46 | | - _x86InstallDirectory = Environment.GetEnvironmentVariable("DOTNET_ROOT_X86"); |
| 84 | + foreach (string line in DotnetCommand("--list-runtimes", x86: x86)) |
| 85 | + { |
| 86 | + string[] parts = line.Trim().Split([' '], 3); |
| 87 | + yield return new RuntimeInfo(parts[0], parts[1], parts[2].Trim(['[', ']'])); |
| 88 | + } |
| 89 | + } |
47 | 90 |
|
48 | | - if (_x86InstallDirectory == null) |
| 91 | + private static IEnumerable<string> DotnetCommand(string arguments, bool x86 = false) |
| 92 | + { |
| 93 | + var process = new Process |
49 | 94 | { |
50 | | -#if NETFRAMEWORK |
51 | | - if (Path.DirectorySeparatorChar == '\\') |
52 | | -#else |
53 | | - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
54 | | -#endif |
| 95 | + StartInfo = new ProcessStartInfo |
55 | 96 | { |
56 | | - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\"); |
57 | | - _x86InstallDirectory = (string)key?.GetValue("InstallLocation"); |
| 97 | + FileName = GetDotnetExecutable(x86), |
| 98 | + Arguments = arguments, |
| 99 | + UseShellExecute = false, |
| 100 | + RedirectStandardOutput = true, |
| 101 | + CreateNoWindow = true |
58 | 102 | } |
59 | | - else |
60 | | - _x86InstallDirectory = "/usr/shared/dotnet/"; |
| 103 | + }; |
| 104 | + |
| 105 | + try |
| 106 | + { |
| 107 | + process.Start(); |
| 108 | + } |
| 109 | + catch (Exception) |
| 110 | + { |
| 111 | + // Failed to start dotnet command. Assume no versions are installed and just return |
| 112 | + yield break; |
61 | 113 | } |
62 | 114 |
|
63 | | - return _x86InstallDirectory; |
| 115 | + while (!process.StandardOutput.EndOfStream) |
| 116 | + yield return process.StandardOutput.ReadLine(); |
64 | 117 | } |
65 | 118 | } |
66 | 119 | } |
0 commit comments