Skip to content

Commit 9e7011b

Browse files
authored
Expand MSBuildSdkResolver (#45364)
1 parent 0c9021a commit 9e7011b

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Reflection;
5+
using System.Text.Json;
56
using Microsoft.Build.Framework;
67
using Microsoft.DotNet.Cli;
78
using Microsoft.DotNet.Configurer;
@@ -28,23 +29,28 @@ public sealed class DotNetMSBuildSdkResolver : SdkResolver
2829

2930
private readonly Func<string, string?> _getEnvironmentVariable;
3031
private readonly Func<string>? _getCurrentProcessPath;
32+
private readonly Func<string, string, string?> _getMsbuildRuntime;
3133
private readonly NETCoreSdkResolver _netCoreSdkResolver;
3234

35+
private const string DotnetHost = "DOTNET_HOST_PATH";
36+
private const string MSBuildTaskHostRuntimeVersion = "SdkResolverMSBuildTaskHostRuntimeVersion";
37+
3338
private static CachingWorkloadResolver _staticWorkloadResolver = new();
3439

3540
private bool _shouldLog = false;
3641

3742
public DotNetMSBuildSdkResolver()
38-
: this(Environment.GetEnvironmentVariable, null, VSSettings.Ambient)
43+
: this(Environment.GetEnvironmentVariable, null, GetMSbuildRuntimeVersion, VSSettings.Ambient)
3944
{
4045
}
4146

4247
// Test constructor
43-
public DotNetMSBuildSdkResolver(Func<string, string?> getEnvironmentVariable, Func<string>? getCurrentProcessPath, VSSettings vsSettings)
48+
public DotNetMSBuildSdkResolver(Func<string, string?> getEnvironmentVariable, Func<string>? getCurrentProcessPath, Func<string, string, string?> getMsbuildRuntime, VSSettings vsSettings)
4449
{
4550
_getEnvironmentVariable = getEnvironmentVariable;
4651
_getCurrentProcessPath = getCurrentProcessPath;
4752
_netCoreSdkResolver = new NETCoreSdkResolver(getEnvironmentVariable, vsSettings);
53+
_getMsbuildRuntime = getMsbuildRuntime;
4854

4955
if (_getEnvironmentVariable(EnvironmentVariableNames.DOTNET_MSBUILD_SDK_RESOLVER_ENABLE_LOG) is string val &&
5056
(string.Equals(val, "true", StringComparison.OrdinalIgnoreCase) ||
@@ -190,6 +196,30 @@ private sealed class CachedState
190196
minimumVSDefinedSDKVersion);
191197
}
192198

199+
string dotnetExe = Path.Combine(dotnetRoot, Constants.DotNetExe);
200+
if (File.Exists(dotnetExe))
201+
{
202+
propertiesToAdd ??= new Dictionary<string, string?>();
203+
propertiesToAdd.Add(DotnetHost, dotnetExe);
204+
}
205+
else
206+
{
207+
logger?.LogMessage($"Could not set '{DotnetHost}' because dotnet executable '{dotnetExe}' does not exist.");
208+
}
209+
210+
string? runtimeVersion = dotnetRoot != null ?
211+
_getMsbuildRuntime(resolverResult.ResolvedSdkDirectory, dotnetRoot) :
212+
null;
213+
if (!string.IsNullOrEmpty(runtimeVersion))
214+
{
215+
propertiesToAdd ??= new Dictionary<string, string?>();
216+
propertiesToAdd.Add(MSBuildTaskHostRuntimeVersion, runtimeVersion);
217+
}
218+
else
219+
{
220+
logger?.LogMessage($"Could not set '{MSBuildTaskHostRuntimeVersion}' because runtime version could not be determined.");
221+
}
222+
193223
if (resolverResult.FailedToResolveSDKSpecifiedInGlobalJson)
194224
{
195225
logger?.LogMessage($"Could not resolve SDK specified in '{resolverResult.GlobalJsonPath}'. Ignoring global.json for this resolution.");
@@ -208,10 +238,7 @@ private sealed class CachedState
208238
warnings.Add(Strings.GlobalJsonResolutionFailed);
209239
}
210240

211-
if (propertiesToAdd == null)
212-
{
213-
propertiesToAdd = new Dictionary<string, string?>();
214-
}
241+
propertiesToAdd ??= new Dictionary<string, string?>();
215242
propertiesToAdd.Add("SdkResolverHonoredGlobalJson", "false");
216243
propertiesToAdd.Add("SdkResolverGlobalJsonPath", resolverResult.GlobalJsonPath);
217244

@@ -259,6 +286,28 @@ private sealed class CachedState
259286
return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings);
260287
}
261288

289+
private static string? GetMSbuildRuntimeVersion(string sdkDirectory, string dotnetRoot)
290+
{
291+
// 1. Get the runtime version from the MSBuild.runtimeconfig.json file
292+
string runtimeConfigPath = Path.Combine(sdkDirectory, "MSBuild.runtimeconfig.json");
293+
if (!File.Exists(runtimeConfigPath)) return null;
294+
295+
using var stream = File.OpenRead(runtimeConfigPath);
296+
using var jsonDoc = JsonDocument.Parse(stream);
297+
298+
JsonElement root = jsonDoc.RootElement;
299+
if (!root.TryGetProperty("runtimeOptions", out JsonElement runtimeOptions) ||
300+
!runtimeOptions.TryGetProperty("framework", out JsonElement framework)) return null;
301+
302+
string? runtimeName = framework.GetProperty("name").GetString();
303+
string? runtimeVersion = framework.GetProperty("version").GetString();
304+
305+
// 2. Check that the runtime version is installed (in shared folder)
306+
return (!string.IsNullOrEmpty(runtimeName) && !string.IsNullOrEmpty(runtimeVersion) &&
307+
Directory.Exists(Path.Combine(dotnetRoot, "shared", runtimeName, runtimeVersion)))
308+
? runtimeVersion : null;
309+
}
310+
262311
private static SdkResult Failure(SdkResultFactory factory, ResolverLogger? logger, SdkLogger sdkLogger, string format, params object?[] args)
263312
{
264313
string error = string.Format(format, args);

src/Resolvers/Microsoft.DotNet.NativeWrapper/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal static class Constants
77
{
88
public const string HostFxr = "hostfxr";
99
public const string DotNet = "dotnet";
10+
public const string DotNetExe = "dotnet.exe";
1011
public const string PATH = "PATH";
1112
public const string DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR = "DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR";
1213

test/Microsoft.DotNet.MSBuildSdkResolver.Tests/GivenAnMSBuildSdkResolver.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
1313
{
1414
public class GivenAnMSBuildSdkResolver : SdkTest
1515
{
16+
private const string DotnetHost = "DOTNET_HOST_PATH";
17+
private const string MSBuildTaskHostRuntimeVersion = "SdkResolverMSBuildTaskHostRuntimeVersion";
1618

1719
public GivenAnMSBuildSdkResolver(ITestOutputHelper logger) : base(logger)
1820
{
@@ -200,7 +202,10 @@ public void ItReturnsHighestSdkAvailableThatIsCompatibleWithMSBuild(bool disallo
200202
result.Success.Should().BeTrue($"No error expected. Error encountered: {string.Join(Environment.NewLine, result.Errors ?? new string[] { })}. Mocked Process Path: {environment.ProcessPath}. Mocked Path: {environment.PathEnvironmentVariable}");
201203
result.Path.Should().Be((disallowPreviews ? compatibleRtm : compatiblePreview).FullName);
202204
result.AdditionalPaths.Should().BeNull();
203-
result.PropertiesToAdd.Should().BeNull();
205+
result.PropertiesToAdd.Count.Should().Be(2);
206+
result.PropertiesToAdd.Should().ContainKey(DotnetHost);
207+
result.PropertiesToAdd.Should().ContainKey(MSBuildTaskHostRuntimeVersion);
208+
result.PropertiesToAdd[MSBuildTaskHostRuntimeVersion].Should().Be("mockRuntimeVersion");
204209
result.Version.Should().Be(disallowPreviews ? "98.98.98" : "99.99.99-preview");
205210
result.Warnings.Should().BeNullOrEmpty();
206211
result.Errors.Should().BeNullOrEmpty();
@@ -274,9 +279,12 @@ public void ItReturnsHighestSdkAvailableThatIsCompatibleWithMSBuildWhenVersionIn
274279
result.Success.Should().BeTrue($"No error expected. Error encountered: {string.Join(Environment.NewLine, result.Errors ?? new string[] { })}. Mocked Process Path: {environment.ProcessPath}. Mocked Path: {environment.PathEnvironmentVariable}");
275280
result.Path.Should().Be((disallowPreviews ? compatibleRtm : compatiblePreview).FullName);
276281
result.AdditionalPaths.Should().BeNull();
277-
result.PropertiesToAdd.Count.Should().Be(2);
278-
result.PropertiesToAdd.ContainsKey("SdkResolverHonoredGlobalJson");
279-
result.PropertiesToAdd.ContainsKey("SdkResolverGlobalJsonPath");
282+
result.PropertiesToAdd.Count.Should().Be(4);
283+
result.PropertiesToAdd.Should().ContainKey(DotnetHost);
284+
result.PropertiesToAdd.Should().ContainKey(MSBuildTaskHostRuntimeVersion);
285+
result.PropertiesToAdd[MSBuildTaskHostRuntimeVersion].Should().Be("mockRuntimeVersion");
286+
result.PropertiesToAdd.Should().ContainKey("SdkResolverHonoredGlobalJson");
287+
result.PropertiesToAdd.Should().ContainKey("SdkResolverGlobalJsonPath");
280288
result.PropertiesToAdd["SdkResolverHonoredGlobalJson"].Should().Be("false");
281289
result.Version.Should().Be(disallowPreviews ? "98.98.98" : "99.99.99-preview");
282290
result.Warnings.Should().BeEquivalentTo(new[] { "Unable to locate the .NET SDK version '1.2.3' as specified by global.json, please check that the specified version is installed." });
@@ -584,6 +592,7 @@ public SdkResolver CreateResolver(bool useAmbientSettings = false)
584592
GetEnvironmentVariable,
585593
// force current executable location to be the mocked dotnet executable location
586594
() => ProcessPath,
595+
(x, y) => "mockRuntimeVersion",
587596
useAmbientSettings
588597
? VSSettings.Ambient
589598
: new VSSettings(VSSettingsFile?.FullName, DisallowPrereleaseByDefault));

0 commit comments

Comments
 (0)