diff --git a/Source/GenerateSharp/Opal/Logger/Log.cs b/Source/GenerateSharp/Opal/Logger/Log.cs
index 9cb1bb7a..d7f03f54 100644
--- a/Source/GenerateSharp/Opal/Logger/Log.cs
+++ b/Source/GenerateSharp/Opal/Logger/Log.cs
@@ -4,121 +4,121 @@
namespace Opal
{
- using global::System;
-
- ///
- /// The static logger class.
- ///
- public class Log
- {
- private static int activeId = 0;
- private static TraceListener? listener = null;
-
- ///
- /// Register the single event listener.
- ///
- /// The listener.
- public static void RegisterListener(TraceListener? listener)
- {
- Log.listener = listener;
- }
-
- ///
- /// Get access to the single event listener.
- ///
- public static TraceListener EnsureListener()
- {
- if (listener == null)
- throw new InvalidOperationException("No Listener registered.");
- return listener;
- }
-
- ///
- /// Set the active ids to use for each event.
- ///
- public static int GetActiveId()
- {
- return activeId;
- }
-
- public static void SetActiveId(int value)
- {
- activeId = value;
- }
-
- ///
- /// Log a high priority message.
- ///
- /// The message.
- /// The message id.
- public static void HighPriority(string message, int id)
- {
- EnsureListener().TraceEvent(TraceEventFlag.HighPriority, id, message);
- }
-
- public static void HighPriority(string message)
- {
- HighPriority(message, activeId);
- }
-
- ///
- /// Log a generic infomational message.
- ///
- /// The message.
- /// The message id.
- public static void Info(string message, int id)
- {
- EnsureListener().TraceEvent(TraceEventFlag.Information, id, message);
- }
-
- public static void Info(string message)
- {
- Info(message, activeId);
- }
-
- ///
- /// Log a diagnostic message.
- ///
- /// The message.
- /// The message id.
- public static void Diag(string message, int id)
- {
- EnsureListener().TraceEvent(TraceEventFlag.Diagnostic, id, message);
- }
-
- public static void Diag(string message)
- {
- Diag(message, activeId);
- }
-
- ///
- /// Log a warning message.
- ///
- /// The message.
- /// The message id.
- public static void Warning(string message, int id)
- {
- EnsureListener().TraceEvent(TraceEventFlag.Warning, id, message);
- }
-
- public static void Warning(string message)
- {
- Warning(message, activeId);
- }
-
- ///
- /// Log an error message.
- ///
- /// The message.
- /// The message id.
- public static void Error(string message, int id)
- {
- EnsureListener().TraceEvent(TraceEventFlag.Error, id, message);
- }
-
- public static void Error(string message)
- {
- Error(message, activeId);
- }
- }
+ using global::System;
+
+ ///
+ /// The static logger class.
+ ///
+ public class Log
+ {
+ private static int activeId = 0;
+ private static TraceListener? listener = null;
+
+ ///
+ /// Register the single event listener.
+ ///
+ /// The listener.
+ public static void RegisterListener(TraceListener? listener)
+ {
+ Log.listener = listener;
+ }
+
+ ///
+ /// Get access to the single event listener.
+ ///
+ public static TraceListener EnsureListener()
+ {
+ if (listener == null)
+ throw new InvalidOperationException("No Listener registered.");
+ return listener;
+ }
+
+ ///
+ /// Set the active ids to use for each event.
+ ///
+ public static int GetActiveId()
+ {
+ return activeId;
+ }
+
+ public static void SetActiveId(int value)
+ {
+ activeId = value;
+ }
+
+ ///
+ /// Log a high priority message.
+ ///
+ /// The message.
+ /// The message id.
+ public static void HighPriority(string message, int id)
+ {
+ EnsureListener().TraceEvent(TraceEventFlag.HighPriority, id, message);
+ }
+
+ public static void HighPriority(string message)
+ {
+ HighPriority(message, activeId);
+ }
+
+ ///
+ /// Log a generic infomational message.
+ ///
+ /// The message.
+ /// The message id.
+ public static void Info(string message, int id)
+ {
+ EnsureListener().TraceEvent(TraceEventFlag.Information, id, message);
+ }
+
+ public static void Info(string message)
+ {
+ Info(message, activeId);
+ }
+
+ ///
+ /// Log a diagnostic message.
+ ///
+ /// The message.
+ /// The message id.
+ public static void Diag(string message, int id)
+ {
+ EnsureListener().TraceEvent(TraceEventFlag.Diagnostic, id, message);
+ }
+
+ public static void Diag(string message)
+ {
+ Diag(message, activeId);
+ }
+
+ ///
+ /// Log a warning message.
+ ///
+ /// The message.
+ /// The message id.
+ public static void Warning(string message, int id)
+ {
+ EnsureListener().TraceEvent(TraceEventFlag.Warning, id, message);
+ }
+
+ public static void Warning(string message)
+ {
+ Warning(message, activeId);
+ }
+
+ ///
+ /// Log an error message.
+ ///
+ /// The message.
+ /// The message id.
+ public static void Error(string message, int id)
+ {
+ EnsureListener().TraceEvent(TraceEventFlag.Error, id, message);
+ }
+
+ public static void Error(string message)
+ {
+ Error(message, activeId);
+ }
+ }
}
diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs
new file mode 100644
index 00000000..7fb00f09
--- /dev/null
+++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs
@@ -0,0 +1,109 @@
+//
+// Copyright (c) Soup. All rights reserved.
+//
+
+using Opal;
+using Opal.System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Soup.Build.Discover.UnitTests
+{
+ [Collection("Opal")]
+ public class DotNetSDKUtilitiesUnitTests
+ {
+ [Fact]
+ public void FindDotNet6Refs()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockFileSystem = new MockFileSystem();
+ using var scopedFileSystem = new ScopedSingleton(mockFileSystem);
+
+ mockFileSystem.RegisterDirectoryChildren(
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/"),
+ new List()
+ {
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.7"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.8"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.9"),
+ });
+
+ var result = DotNetSDKUtilities.FindDotNet6Refs();
+
+ Assert.Equal("6.0.9", result.Version);
+ Assert.Equal(new Path("C:/Program Files/dotnet/"), result.Path);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "HIGH: FindNewestDotNet6RuntimeVersion: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/",
+ "INFO: CheckFile: 5.0.0",
+ "INFO: CheckFile: 6.0.7",
+ "INFO: CheckFile: 6.0.8",
+ "INFO: CheckFile: 6.0.9",
+ },
+ testListener.GetMessages());
+
+ // Verify expected file system requests
+ Assert.Equal(
+ new List()
+ {
+ "GetDirectoryChildren: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/",
+ },
+ mockFileSystem.GetRequests());
+ }
+
+ [Fact]
+ public void FindDotNet6Refs_SkipReleaseCandidate()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockFileSystem = new MockFileSystem();
+ using var scopedFileSystem = new ScopedSingleton(mockFileSystem);
+
+ mockFileSystem.RegisterDirectoryChildren(
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/"),
+ new List()
+ {
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.7"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.8"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.9"),
+ new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/7.0.0-rc.1.22426.10"),
+ });
+
+ var result = DotNetSDKUtilities.FindDotNet6Refs();
+
+ Assert.Equal("6.0.9", result.Version);
+ Assert.Equal(new Path("C:/Program Files/dotnet/"), result.Path);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "HIGH: FindNewestDotNet6RuntimeVersion: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/",
+ "INFO: CheckFile: 5.0.0",
+ "INFO: CheckFile: 6.0.7",
+ "INFO: CheckFile: 6.0.8",
+ "INFO: CheckFile: 6.0.9",
+ "INFO: CheckFile: 7.0.0-rc.1.22426.10",
+ },
+ testListener.GetMessages());
+
+ // Verify expected file system requests
+ Assert.Equal(
+ new List()
+ {
+ "GetDirectoryChildren: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/",
+ },
+ mockFileSystem.GetRequests());
+ }
+ }
+}
diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs
index f6b9fdb6..c1c1a01e 100644
--- a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs
+++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs
@@ -11,6 +11,7 @@
namespace Soup.Build.Discover.UnitTests
{
+ [Collection("Opal")]
public class SwhereManagerUnitTests
{
[Fact]
diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs
new file mode 100644
index 00000000..ce49b7f6
--- /dev/null
+++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs
@@ -0,0 +1,221 @@
+//
+// Copyright (c) Soup. All rights reserved.
+//
+
+using Opal;
+using Opal.System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Soup.Build.Discover.UnitTests
+{
+ [Collection("Opal")]
+ public class VSWhereUtilitiesUnitTests
+ {
+ [Fact]
+ public async Task FindRoslynInstallAsync()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockProcessManager = new MockProcessManager();
+ using var scopedProcessManager = new ScopedSingleton(mockProcessManager);
+
+ mockProcessManager.RegisterExecuteResult(
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath",
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\n");
+
+ bool includePrerelease = false;
+ var result = await VSWhereUtilities.FindRoslynInstallAsync(includePrerelease);
+
+ Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/Roslyn/"), result);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath",
+ "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Community",
+ },
+ testListener.GetMessages());
+
+ // Verify expected process requests
+ Assert.Equal(
+ new List()
+ {
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath",
+ "ProcessStart: 1",
+ "WaitForExit: 1",
+ "GetStandardOutput: 1",
+ "GetStandardError: 1",
+ "GetExitCode: 1",
+ },
+ mockProcessManager.GetRequests());
+ }
+
+ [Fact]
+ public async Task FindRoslynInstallAsync_Prerelease()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockProcessManager = new MockProcessManager();
+ using var scopedProcessManager = new ScopedSingleton(mockProcessManager);
+
+ mockProcessManager.RegisterExecuteResult(
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease",
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\n");
+
+ bool includePrerelease = true;
+ var result = await VSWhereUtilities.FindRoslynInstallAsync(includePrerelease);
+
+ Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/MSBuild/Current/Bin/Roslyn/"), result);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease",
+ "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Preview",
+ },
+ testListener.GetMessages());
+
+ // Verify expected process requests
+ Assert.Equal(
+ new List()
+ {
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease",
+ "ProcessStart: 1",
+ "WaitForExit: 1",
+ "GetStandardOutput: 1",
+ "GetStandardError: 1",
+ "GetExitCode: 1",
+ },
+ mockProcessManager.GetRequests());
+ }
+
+ [Fact]
+ public async Task FindMSVCInstallAsync()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockProcessManager = new MockProcessManager();
+ using var scopedProcessManager = new ScopedSingleton(mockProcessManager);
+
+ var mockFileSystem = new MockFileSystem();
+ using var scopedFileSystem = new ScopedSingleton(mockFileSystem);
+
+ mockProcessManager.RegisterExecuteResult(
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath",
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\n");
+
+ mockFileSystem.CreateMockFile(
+ new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt"),
+ new MockFile(new System.IO.MemoryStream(Encoding.UTF8.GetBytes("14.33.31629\r\n"))));
+
+ bool includePrerelease = false;
+ var result = await VSWhereUtilities.FindMSVCInstallAsync(includePrerelease);
+
+ Assert.Equal("14.33.31629", result.Version);
+ Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.33.31629/"), result.Path);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath",
+ "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Community",
+ "HIGH: Using VC Version: 14.33.31629",
+ },
+ testListener.GetMessages());
+
+ // Verify expected file system requests
+ Assert.Equal(
+ new List()
+ {
+ "Exists: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt",
+ "OpenRead: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt",
+ },
+ mockFileSystem.GetRequests());
+
+ // Verify expected process requests
+ Assert.Equal(
+ new List()
+ {
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath",
+ "ProcessStart: 1",
+ "WaitForExit: 1",
+ "GetStandardOutput: 1",
+ "GetStandardError: 1",
+ "GetExitCode: 1",
+ },
+ mockProcessManager.GetRequests());
+ }
+
+ [Fact]
+ public async Task FindMSVCInstallAsync_Prerelease()
+ {
+ // Register the test listener
+ var testListener = new TestTraceListener();
+ using var scopedTraceListener = new ScopedTraceListenerRegister(testListener);
+
+ var mockFileSystem = new MockFileSystem();
+ using var scopedFileSystem = new ScopedSingleton(mockFileSystem);
+
+ var mockProcessManager = new MockProcessManager();
+ using var scopedProcessManager = new ScopedSingleton(mockProcessManager);
+
+ mockProcessManager.RegisterExecuteResult(
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease",
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\n");
+
+ mockFileSystem.CreateMockFile(
+ new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt"),
+ new MockFile(new System.IO.MemoryStream(Encoding.UTF8.GetBytes("14.34.31823\r\n"))));
+
+ bool includePrerelease = true;
+ var result = await VSWhereUtilities.FindMSVCInstallAsync(includePrerelease);
+
+ Assert.Equal("14.34.31823", result.Version);
+ Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.34.31823/"), result.Path);
+
+ // Verify expected logs
+ Assert.Equal(
+ new List()
+ {
+ "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease",
+ "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Preview",
+ "HIGH: Using VC Version: 14.34.31823",
+ },
+ testListener.GetMessages());
+
+ // Verify expected file system requests
+ Assert.Equal(
+ new List()
+ {
+ "Exists: C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt",
+ "OpenRead: C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt",
+ },
+ mockFileSystem.GetRequests());
+
+ // Verify expected process requests
+ Assert.Equal(
+ new List()
+ {
+ "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease",
+ "ProcessStart: 1",
+ "WaitForExit: 1",
+ "GetStandardOutput: 1",
+ "GetStandardError: 1",
+ "GetExitCode: 1",
+ },
+ mockProcessManager.GetRequests());
+ }
+ }
+}
diff --git a/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs b/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs
index 9e39724d..d820665d 100644
--- a/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs
+++ b/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs
@@ -7,7 +7,7 @@
namespace Soup.Build.Discover
{
- internal static class DotNetSDKUtilities
+ public static class DotNetSDKUtilities
{
public static (string Version, Path Path) FindDotNet6Refs()
{
@@ -29,10 +29,12 @@ private static string FindNewestDotNet6RuntimeVersion(Path dotnetSDKInstallPath)
var name = child.Path.GetFileName();
Log.Info("CheckFile: " + name);
- // Parse the version string
- var version = SemanticVersion.Parse(name);
- if (version.Major == 6 && version > currentVersion)
- currentVersion = version;
+ // Attempt to parse the version string
+ if (SemanticVersion.TryParse(name, out var version))
+ {
+ if (version.Major == 6 && version > currentVersion)
+ currentVersion = version;
+ }
}
if (currentVersion == new SemanticVersion(0, 0, 0))
diff --git a/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs b/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs
index 9fd995f7..a431e280 100644
--- a/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs
+++ b/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs
@@ -10,7 +10,7 @@
namespace Soup.Build.Discover
{
- internal static class VSWhereUtilities
+ public static class VSWhereUtilities
{
///
/// Attempt to find Roslyn compiler installation
diff --git a/global.json b/global.json
new file mode 100644
index 00000000..dc98c6e0
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+ "sdk": {
+ "version": "6.0.401"
+ }
+}
\ No newline at end of file