Skip to content

Commit 0062d0a

Browse files
authored
Use real SdkResolver APIs to set .NET environment variables for .NET TaskHost purposes (#50091)
2 parents 4314e73 + cb45b92 commit 0062d0a

File tree

4 files changed

+73
-21
lines changed

4 files changed

+73
-21
lines changed

build.cmd

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@ if %errorlevel%==0 (
1010
set skipFlags="/p:SkipUsingCrossgen=true /p:SkipBuildingInstallers=true"
1111
)
1212
set DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT=true
13-
powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -command "& """%~dp0eng\common\build.ps1""" -restore -build -msbuildEngine dotnet %skipFlags% %*"
14-
15-
endlocal
13+
powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -command "& """%~dp0eng\common\build.ps1""" -restore -build -msbuildEngine dotnet %skipFlags% /tlp:summary %*"
1614
exit /b %ErrorLevel%

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ if [[ "$@" != *"-pack"* ]]; then
1414
fi
1515

1616
export DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT="true"
17-
. "$ScriptRoot/eng/common/build.sh" --build --restore $skipFlags "$@"
17+
. "$ScriptRoot/eng/common/build.sh" --build --restore $skipFlags /tlp:summary "$@"

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

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,46 @@ public sealed class DotNetMSBuildSdkResolver : SdkResolver
3232
private readonly Func<string, string, string?> _getMsbuildRuntime;
3333
private readonly NETCoreSdkResolver _netCoreSdkResolver;
3434

35+
private const string DOTNET_HOST = nameof(DOTNET_HOST);
3536
private const string DotnetHostExperimentalKey = "DOTNET_EXPERIMENTAL_HOST_PATH";
3637
private const string MSBuildTaskHostRuntimeVersion = "SdkResolverMSBuildTaskHostRuntimeVersion";
37-
38+
private const string SdkResolverHonoredGlobalJson = "SdkResolverHonoredGlobalJson";
39+
private const string SdkResolverGlobalJsonPath = "SdkResolverGlobalJsonPath";
3840
private static CachingWorkloadResolver _staticWorkloadResolver = new();
3941

42+
/// <summary>
43+
/// This is a workaround for MSBuild API compatibility in older VS hosts.
44+
/// To allow for working with the MSBuild 17.15 APIs while not blowing up our ability to build the SdkResolver in
45+
/// VS-driven CI pipelines, we probe and invoke only if the expected method is present.
46+
/// Once we can update our MSBuild API dependency this can go away.
47+
/// </summary>
48+
private static UpdatedSdkResultFactorySuccess? _factorySuccessFunc = TryLocateNewMSBuildFactory();
49+
50+
/// <summary>
51+
/// This represents the 'open delegate' form of the updated SdkResultFactory.IndicateSuccess method with environment variable support.
52+
/// Because it is an open delegate, we can provide an object instance to be called as the first argument.
53+
/// </summary>
54+
public delegate SdkResult UpdatedSdkResultFactorySuccess(SdkResultFactory factory, string sdkPath, string? sdkVersion, IDictionary<string, string?>? propertiesToAdd, IDictionary<string, SdkResultItem>? itemsToAdd, List<string>? warnings, IDictionary<string, string?>? environmentVariablesToAdd);
55+
56+
private static UpdatedSdkResultFactorySuccess? TryLocateNewMSBuildFactory()
57+
{
58+
if (typeof(SdkResultFactory).GetMethod("IndicateSuccess", [
59+
typeof(string), // path to sdk
60+
typeof(string), // sdk version
61+
typeof(IDictionary<string, string>), // properties to add
62+
typeof(IDictionary<string, SdkResultItem>), // items to add
63+
typeof(List<string>), // warnings
64+
typeof(IDictionary<string, string>) // environment variables to add
65+
]) is MethodInfo m)
66+
{
67+
return Delegate.CreateDelegate(
68+
typeof(Func<SdkResultFactory, string, string?, IDictionary<string, string?>?, IDictionary<string, SdkResultItem>?, List<string>?, IDictionary<string, string?>?, SdkResult>),
69+
null,
70+
m) as UpdatedSdkResultFactorySuccess;
71+
}
72+
return null;
73+
}
74+
4075
private bool _shouldLog = false;
4176

4277
public DotNetMSBuildSdkResolver()
@@ -68,6 +103,7 @@ private sealed class CachedState
68103
public string? GlobalJsonPath;
69104
public IDictionary<string, string?>? PropertiesToAdd;
70105
public CachingWorkloadResolver? WorkloadResolver;
106+
public IDictionary<string, string?>? EnvironmentVariablesToAdd;
71107
}
72108

73109
public override SdkResult? Resolve(SdkReference sdkReference, SdkResolverContext context, SdkResultFactory factory)
@@ -78,6 +114,7 @@ private sealed class CachedState
78114
string? globalJsonPath = null;
79115
IDictionary<string, string?>? propertiesToAdd = null;
80116
IDictionary<string, SdkResultItem>? itemsToAdd = null;
117+
IDictionary<string, string?>? environmentVariablesToAdd = null;
81118
List<string>? warnings = null;
82119
CachingWorkloadResolver? workloadResolver = null;
83120

@@ -99,6 +136,7 @@ private sealed class CachedState
99136
globalJsonPath = priorResult.GlobalJsonPath;
100137
propertiesToAdd = priorResult.PropertiesToAdd;
101138
workloadResolver = priorResult.WorkloadResolver;
139+
environmentVariablesToAdd = priorResult.EnvironmentVariablesToAdd;
102140

103141
logger?.LogMessage($"\tDotnet root: {dotnetRoot}");
104142
logger?.LogMessage($"\tMSBuild SDKs Dir: {msbuildSdksDir}");
@@ -139,9 +177,9 @@ private sealed class CachedState
139177
logger?.LogMessage($"\tResolved SDK directory: {resolverResult.ResolvedSdkDirectory}");
140178
logger?.LogMessage($"\tglobal.json path: {resolverResult.GlobalJsonPath}");
141179
logger?.LogMessage($"\tFailed to resolve SDK from global.json: {resolverResult.FailedToResolveSDKSpecifiedInGlobalJson}");
142-
143-
msbuildSdksDir = Path.Combine(resolverResult.ResolvedSdkDirectory, "Sdks");
144-
netcoreSdkVersion = new DirectoryInfo(resolverResult.ResolvedSdkDirectory).Name;
180+
string dotnetSdkDir = resolverResult.ResolvedSdkDirectory;
181+
msbuildSdksDir = Path.Combine(dotnetSdkDir, "Sdks");
182+
netcoreSdkVersion = new DirectoryInfo(dotnetSdkDir).Name;
145183
globalJsonPath = resolverResult.GlobalJsonPath;
146184

147185
// These are overrides that are used to force the resolved SDK tasks and targets to come from a given
@@ -197,20 +235,26 @@ private sealed class CachedState
197235
}
198236

199237
string? fullPathToMuxer =
200-
TryResolveMuxerFromSdkResolution(resolverResult)
238+
TryResolveMuxerFromSdkResolution(dotnetSdkDir)
201239
?? Path.Combine(dotnetRoot, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet);
202240
if (File.Exists(fullPathToMuxer))
203241
{
242+
// keeping this in until this component no longer needs to handle 17.14.
204243
propertiesToAdd ??= new Dictionary<string, string?>();
205244
propertiesToAdd.Add(DotnetHostExperimentalKey, fullPathToMuxer);
245+
// this is the future-facing implementation.
246+
environmentVariablesToAdd ??= new Dictionary<string, string?>(1)
247+
{
248+
[DOTNET_HOST] = fullPathToMuxer
249+
};
206250
}
207251
else
208252
{
209-
logger?.LogMessage($"Could not set '{DotnetHostExperimentalKey}' because dotnet executable '{fullPathToMuxer}' does not exist.");
253+
logger?.LogMessage($"Could not set '{DOTNET_HOST}' environment variable because dotnet executable '{fullPathToMuxer}' does not exist.");
210254
}
211255

212256
string? runtimeVersion = dotnetRoot != null ?
213-
_getMsbuildRuntime(resolverResult.ResolvedSdkDirectory, dotnetRoot) :
257+
_getMsbuildRuntime(dotnetSdkDir, dotnetRoot) :
214258
null;
215259
if (!string.IsNullOrEmpty(runtimeVersion))
216260
{
@@ -224,7 +268,7 @@ private sealed class CachedState
224268

225269
if (resolverResult.FailedToResolveSDKSpecifiedInGlobalJson)
226270
{
227-
logger?.LogMessage($"Could not resolve SDK specified in '{resolverResult.GlobalJsonPath}'. Ignoring global.json for this resolution.");
271+
logger?.LogMessage($"Could not resolve SDK specified in '{globalJsonPath}'. Ignoring global.json for this resolution.");
228272

229273
if (warnings == null)
230274
{
@@ -241,8 +285,9 @@ private sealed class CachedState
241285
}
242286

243287
propertiesToAdd ??= new Dictionary<string, string?>();
244-
propertiesToAdd.Add("SdkResolverHonoredGlobalJson", "false");
245-
propertiesToAdd.Add("SdkResolverGlobalJsonPath", resolverResult.GlobalJsonPath);
288+
propertiesToAdd.Add(SdkResolverHonoredGlobalJson, "false");
289+
// TODO: this would ideally be reported anytime it was non-null - that may cause more imports though?
290+
propertiesToAdd.Add(SdkResolverGlobalJsonPath, globalJsonPath);
246291

247292
if (logger != null)
248293
{
@@ -258,7 +303,8 @@ private sealed class CachedState
258303
NETCoreSdkVersion = netcoreSdkVersion,
259304
GlobalJsonPath = globalJsonPath,
260305
PropertiesToAdd = propertiesToAdd,
261-
WorkloadResolver = workloadResolver
306+
WorkloadResolver = workloadResolver,
307+
EnvironmentVariablesToAdd = environmentVariablesToAdd
262308
};
263309

264310
// First check if requested SDK resolves to a workload SDK pack
@@ -284,8 +330,14 @@ private sealed class CachedState
284330
Strings.MSBuildSDKDirectoryNotFound,
285331
msbuildSdkDir);
286332
}
287-
288-
return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings);
333+
if (_factorySuccessFunc != null)
334+
{
335+
return _factorySuccessFunc(factory, msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings, environmentVariablesToAdd);
336+
}
337+
else
338+
{
339+
return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings);
340+
}
289341
}
290342

291343
/// <summary>
@@ -295,10 +347,10 @@ private sealed class CachedState
295347
/// SDK layouts always have a defined relationship to the location of the muxer -
296348
/// the muxer binary should be exactly two directories above the SDK directory.
297349
/// </remarks>
298-
private static string? TryResolveMuxerFromSdkResolution(SdkResolutionResult resolverResult)
350+
private static string? TryResolveMuxerFromSdkResolution(string resolvedSdkDirectory)
299351
{
300352
var expectedFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet;
301-
var currentDir = resolverResult.ResolvedSdkDirectory;
353+
var currentDir = resolvedSdkDirectory;
302354
var expectedDotnetRoot = Path.GetDirectoryName(Path.GetDirectoryName(currentDir));
303355
var expectedMuxerPath = Path.Combine(expectedDotnetRoot, expectedFileName);
304356
if (File.Exists(expectedMuxerPath))

src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,11 @@
127127
</PropertyGroup>
128128

129129
<Error Text="$(AssemblyName) is expected to depend on %(ExpectedDependencies.Identity). $(DependencyMismatchErrorText)"
130-
Condition="!($([System.String]::Copy('$(ResolvedDependenciesList)').Contains('%(ExpectedDependencies.Identity)')))" />
130+
Condition="!($([System.String]::Copy('$(ResolvedDependenciesList)').Contains('%(ExpectedDependencies.Identity)')))"
131+
ContinueOnError="true" />
131132
<Error Text="$(AssemblyName) is not expected to depend on %(ResolvedDependencies.FusionName). $(DependencyMismatchErrorText)"
132-
Condition="!($([System.String]::Copy('$(ExpectedDependenciesList)').Contains('%(ResolvedDependencies.FusionName)')))" />
133+
Condition="!($([System.String]::Copy('$(ExpectedDependenciesList)').Contains('%(ResolvedDependencies.FusionName)')))"
134+
ContinueOnError="true" />
133135
</Target>
134136

135137
</Project>

0 commit comments

Comments
 (0)