Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/MSBuildLocator/DotNetSdkLocationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Text;
using System.Text.RegularExpressions;

#nullable enable
Expand Down Expand Up @@ -117,11 +118,13 @@ public static IEnumerable<VisualStudioInstance> GetInstances(string workingDirec
static IEnumerable<string> GetAllAvailableSDKs(bool allowAllDotnetLocations)
{
bool foundSdks = false;
int rc = 0;
StringBuilder? errorMessage = null;
foreach (string dotnetPath in s_dotnetPathCandidates.Value)
{
int rc = NativeMethods.hostfxr_get_available_sdks(exe_dir: dotnetPath, out string[]? resolvedPaths);
rc = NativeMethods.hostfxr_get_available_sdks(exe_dir: dotnetPath, out string[]? resolvedPaths, out errorMessage);

if (rc == 0 && resolvedPaths != null)
if (resolvedPaths != null)
{
foundSdks = true;

Expand All @@ -140,17 +143,19 @@ static IEnumerable<string> GetAllAvailableSDKs(bool allowAllDotnetLocations)
// Errors are automatically printed to stderr. We should not continue to try to output anything if we failed.
if (!foundSdks)
{
throw new InvalidOperationException(SdkResolutionExceptionMessage(nameof(NativeMethods.hostfxr_get_available_sdks)));
throw new InvalidOperationException(SdkResolutionExceptionMessage(nameof(NativeMethods.hostfxr_get_available_sdks), rc, errorMessage));
}
}

// Determines the directory location of the SDK accounting for global.json and multi-level lookup policy.
static string? GetSdkFromGlobalSettings(string workingDirectory)
{
string? resolvedSdk = null;
int rc = 0;
StringBuilder? errorMessage = null;
foreach (string dotnetPath in s_dotnetPathCandidates.Value)
{
int rc = NativeMethods.hostfxr_resolve_sdk2(exe_dir: dotnetPath, working_dir: workingDirectory, flags: 0, out resolvedSdk, out _);
rc = NativeMethods.hostfxr_resolve_sdk2(exe_dir: dotnetPath, working_dir: workingDirectory, flags: 0, out resolvedSdk, out _, out errorMessage);

if (rc == 0)
{
Expand All @@ -160,7 +165,7 @@ static IEnumerable<string> GetAllAvailableSDKs(bool allowAllDotnetLocations)
}

return string.IsNullOrEmpty(resolvedSdk)
? throw new InvalidOperationException(SdkResolutionExceptionMessage(nameof(NativeMethods.hostfxr_resolve_sdk2)))
? throw new InvalidOperationException(SdkResolutionExceptionMessage(nameof(NativeMethods.hostfxr_resolve_sdk2), rc, errorMessage))
: resolvedSdk;
}
}
Expand Down Expand Up @@ -229,7 +234,8 @@ private static IntPtr HostFxrResolver(Assembly assembly, string libraryName)
throw new InvalidOperationException(error);
}

private static string SdkResolutionExceptionMessage(string methodName) => $"Failed to find all versions of .NET Core MSBuild. Call to {methodName}. There may be more details in stderr.";
private static string SdkResolutionExceptionMessage(string methodName, int rc, StringBuilder? errorMessage) =>
$"Error while calling hostfxr function {methodName}. Error code: {rc} Detailed error: {errorMessage}";

private static List<string> ResolveDotnetPathCandidates()
{
Expand Down
49 changes: 47 additions & 2 deletions src/MSBuildLocator/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;

namespace Microsoft.Build.Locator
{
Expand All @@ -25,17 +26,19 @@ private enum hostfxr_resolve_sdk2_result_key_t
global_json_path = 1,
};

internal static int hostfxr_resolve_sdk2(string exe_dir, string working_dir, hostfxr_resolve_sdk2_flags_t flags, out string resolved_sdk_dir, out string global_json_path)
internal static int hostfxr_resolve_sdk2(string exe_dir, string working_dir, hostfxr_resolve_sdk2_flags_t flags, out string resolved_sdk_dir, out string global_json_path, out StringBuilder errorMessage)
{
Debug.Assert(t_resolve_sdk2_resolved_sdk_dir is null);
Debug.Assert(t_resolve_sdk2_global_json_path is null);
try
{
unsafe
{
using var errorHandler = new ErrorHandler();
int result = hostfxr_resolve_sdk2(exe_dir, working_dir, flags, &hostfxr_resolve_sdk2_callback);
resolved_sdk_dir = t_resolve_sdk2_resolved_sdk_dir;
global_json_path = t_resolve_sdk2_global_json_path;
errorMessage = t_hostfxr_error_builder;
return result;
}
}
Expand Down Expand Up @@ -72,15 +75,17 @@ private static unsafe void hostfxr_resolve_sdk2_callback(hostfxr_resolve_sdk2_re
}
}

internal static int hostfxr_get_available_sdks(string exe_dir, out string[] sdks)
internal static int hostfxr_get_available_sdks(string exe_dir, out string[] sdks, out StringBuilder errorMessage)
{
Debug.Assert(t_get_available_sdks_result is null);
try
{
unsafe
{
using var errorHandler = new ErrorHandler();
int result = hostfxr_get_available_sdks(exe_dir, &hostfxr_get_available_sdks_callback);
sdks = t_get_available_sdks_result;
errorMessage = t_hostfxr_error_builder;
return result;
}
}
Expand Down Expand Up @@ -108,6 +113,29 @@ private static unsafe void hostfxr_get_available_sdks_callback(int count, void**
t_get_available_sdks_result = result;
}

[LibraryImport(HostFxrName)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
private static unsafe partial delegate* unmanaged[Cdecl]<void*, void> hostfxr_set_error_writer(delegate* unmanaged[Cdecl]<void*, void> error_writer);

[ThreadStatic]
private static StringBuilder t_hostfxr_error_builder;

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
private static unsafe void hostfxr_error_writer_callback(void* message)
{
t_hostfxr_error_builder ??= new StringBuilder();
if (OperatingSystem.IsWindows())
{
// Avoid allocating temporary string on Windows.
t_hostfxr_error_builder.Append(MemoryMarshal.CreateReadOnlySpanFromNullTerminated((char*)message));
t_hostfxr_error_builder.AppendLine();
}
else
{
t_hostfxr_error_builder.AppendLine(Utf8StringMarshaller.ConvertToManaged((byte*)message));
}
}

[CustomMarshaller(typeof(string), MarshalMode.Default, typeof(AutoStringMarshaller))]
internal static unsafe class AutoStringMarshaller
{
Expand All @@ -117,6 +145,23 @@ internal static unsafe class AutoStringMarshaller

public static string ConvertToManaged(void* ptr) => Marshal.PtrToStringAuto((nint)ptr);
}

private unsafe readonly ref struct ErrorHandler
{
private readonly delegate* unmanaged[Cdecl]<void*, void> _previousCallback;

public ErrorHandler()
{
Debug.Assert(t_hostfxr_error_builder is null);
_previousCallback = hostfxr_set_error_writer(&hostfxr_error_writer_callback);
}

public void Dispose()
{
hostfxr_set_error_writer(_previousCallback);
t_hostfxr_error_builder = null;
}
}
}
}
#endif
Loading