Skip to content

Commit a653f61

Browse files
authored
Merge pull request #1781 from nunit/issue-1779
Enhanced assembly resolution for windows and aspnetcore
2 parents 924d5b1 + 652621a commit a653f61

File tree

11 files changed

+173
-252
lines changed

11 files changed

+173
-252
lines changed

src/Directory.Build.props

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<!-- Informational Settings -->
1313
<Company>NUnit Software</Company>
1414
<Copyright>Copyright (c) 2022 Charlie Poole, Rob Prouse</Copyright>
15+
<NoWarn>$(NoWarn);NU1603</NoWarn>
1516
</PropertyGroup>
1617

17-
</Project>
18+
</Project>

src/NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@
3535
<Reference Include="System.Web" />
3636
</ItemGroup>
3737

38-
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1' OR '$(TargetFramework)'=='net6.0' OR '$(TargetFramework)'=='net8.0'">
39-
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
40-
</ItemGroup>
41-
4238
<ItemGroup>
4339
<ProjectReference Include="..\..\TestData\mock-assembly\mock-assembly.csproj" />
4440
<ProjectReference Include="..\..\TestData\notest-assembly\notest-assembly.csproj" />

src/NUnitEngine/nunit.engine.core/DotNetHelper.cs

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,118 @@
22

33
using Microsoft.Win32;
44
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
57
using System.IO;
6-
using System.Runtime.InteropServices;
8+
using System.Linq;
79

810
namespace NUnit.Engine
911
{
1012
public static class DotNet
1113
{
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\";
1418

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+
}
1743

18-
private static string _x64InstallDirectory;
19-
public static string GetX64InstallDirectory()
44+
public class RuntimeInfo
2045
{
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) { }
2352

24-
if (_x64InstallDirectory == null)
53+
public RuntimeInfo(string name, Version version, string path)
2554
{
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;
3758
}
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;
3868

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);
4080
}
4181

42-
private static string _x86InstallDirectory;
43-
public static string GetX86InstallDirectory()
82+
private static IEnumerable<RuntimeInfo> GetAllRuntimes(bool x86)
4483
{
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+
}
4790

48-
if (_x86InstallDirectory == null)
91+
private static IEnumerable<string> DotnetCommand(string arguments, bool x86 = false)
92+
{
93+
var process = new Process
4994
{
50-
#if NETFRAMEWORK
51-
if (Path.DirectorySeparatorChar == '\\')
52-
#else
53-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
54-
#endif
95+
StartInfo = new ProcessStartInfo
5596
{
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
58102
}
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;
61113
}
62114

63-
return _x86InstallDirectory;
115+
while (!process.StandardOutput.EndOfStream)
116+
yield return process.StandardOutput.ReadLine();
64117
}
65118
}
66119
}

src/NUnitEngine/nunit.engine.core/Internal/DirectoryFinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public IEnumerable<IDirectory> GetDirectories(IDirectory startDirectory, string
3131
Guard.ArgumentNotNull(startDirectory, nameof(startDirectory));
3232
Guard.ArgumentNotNull(pattern, nameof(pattern));
3333

34-
if (Path.DirectorySeparatorChar == '\\')
34+
if (OS.IsWindows)
3535
pattern = pattern.Replace(Path.DirectorySeparatorChar, '/');
3636

3737
var dirList = new List<IDirectory> { startDirectory };

src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public static bool SamePathOrUnder( string path1, string path2 )
147147
return string.Compare( path1, path2, RunningOnWindows ) == 0;
148148

149149
// path 2 is longer than path 1: see if initial parts match
150-
if ( string.Compare( path1, path2.Substring( 0, length1 ), RunningOnWindows ) != 0 )
150+
if ( string.Compare( path1, path2.Substring( 0, length1 ), RunningOnWindows) != 0 )
151151
return false;
152152

153153
// must match through or up to a directory separator boundary
@@ -179,6 +179,8 @@ public static bool IsFullyQualifiedPath(string path )
179179
: IsFullyQualifiedUnixPath(path);
180180
}
181181

182+
private static bool RunningOnWindows => DirectorySeparatorChar == '\\';
183+
182184
/// <summary>
183185
/// Returns a value that indicates whether the specified file path is fully qualified or not on Windows operating systems.
184186
/// </summary>
@@ -250,8 +252,6 @@ private static bool IsValidDriveSpecifier(char c)
250252
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
251253
}
252254

253-
private static bool RunningOnWindows => DirectorySeparatorChar == '\\';
254-
255255
private static string[] SplitPath(string path)
256256
{
257257
char[] separators = new char[] { DirectorySeparatorChar, AltDirectorySeparatorChar };

0 commit comments

Comments
 (0)