diff --git a/build.cmd b/build.cmd index d86f2fb8b5cb..3d0115980b65 100644 --- a/build.cmd +++ b/build.cmd @@ -10,7 +10,5 @@ if %errorlevel%==0 ( set skipFlags="/p:SkipUsingCrossgen=true /p:SkipBuildingInstallers=true" ) set DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT=true -powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -command "& """%~dp0eng\common\build.ps1""" -restore -build -msbuildEngine dotnet %skipFlags% %*" - -endlocal +powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -command "& """%~dp0eng\common\build.ps1""" -restore -build -msbuildEngine dotnet %skipFlags% /tlp:summary %*" exit /b %ErrorLevel% diff --git a/build.sh b/build.sh index 96189c91eadb..49b1e0e55773 100755 --- a/build.sh +++ b/build.sh @@ -14,4 +14,4 @@ if [[ "$@" != *"-pack"* ]]; then fi export DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT="true" -. "$ScriptRoot/eng/common/build.sh" --build --restore $skipFlags "$@" +. "$ScriptRoot/eng/common/build.sh" --build --restore $skipFlags /tlp:summary "$@" diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs index 5bf7a44cb716..6b891c586a23 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs @@ -32,11 +32,46 @@ public sealed class DotNetMSBuildSdkResolver : SdkResolver private readonly Func _getMsbuildRuntime; private readonly NETCoreSdkResolver _netCoreSdkResolver; + private const string DOTNET_HOST = nameof(DOTNET_HOST); private const string DotnetHostExperimentalKey = "DOTNET_EXPERIMENTAL_HOST_PATH"; private const string MSBuildTaskHostRuntimeVersion = "SdkResolverMSBuildTaskHostRuntimeVersion"; - + private const string SdkResolverHonoredGlobalJson = "SdkResolverHonoredGlobalJson"; + private const string SdkResolverGlobalJsonPath = "SdkResolverGlobalJsonPath"; private static CachingWorkloadResolver _staticWorkloadResolver = new(); + /// + /// This is a workaround for MSBuild API compatibility in older VS hosts. + /// To allow for working with the MSBuild 17.15 APIs while not blowing up our ability to build the SdkResolver in + /// VS-driven CI pipelines, we probe and invoke only if the expected method is present. + /// Once we can update our MSBuild API dependency this can go away. + /// + private static UpdatedSdkResultFactorySuccess? _factorySuccessFunc = TryLocateNewMSBuildFactory(); + + /// + /// This represents the 'open delegate' form of the updated SdkResultFactory.IndicateSuccess method with environment variable support. + /// Because it is an open delegate, we can provide an object instance to be called as the first argument. + /// + public delegate SdkResult UpdatedSdkResultFactorySuccess(SdkResultFactory factory, string sdkPath, string? sdkVersion, IDictionary? propertiesToAdd, IDictionary? itemsToAdd, List? warnings, IDictionary? environmentVariablesToAdd); + + private static UpdatedSdkResultFactorySuccess? TryLocateNewMSBuildFactory() + { + if (typeof(SdkResultFactory).GetMethod("IndicateSuccess", [ + typeof(string), // path to sdk + typeof(string), // sdk version + typeof(IDictionary), // properties to add + typeof(IDictionary), // items to add + typeof(List), // warnings + typeof(IDictionary) // environment variables to add + ]) is MethodInfo m) + { + return Delegate.CreateDelegate( + typeof(Func?, IDictionary?, List?, IDictionary?, SdkResult>), + null, + m) as UpdatedSdkResultFactorySuccess; + } + return null; + } + private bool _shouldLog = false; public DotNetMSBuildSdkResolver() @@ -68,6 +103,7 @@ private sealed class CachedState public string? GlobalJsonPath; public IDictionary? PropertiesToAdd; public CachingWorkloadResolver? WorkloadResolver; + public IDictionary? EnvironmentVariablesToAdd; } public override SdkResult? Resolve(SdkReference sdkReference, SdkResolverContext context, SdkResultFactory factory) @@ -78,6 +114,7 @@ private sealed class CachedState string? globalJsonPath = null; IDictionary? propertiesToAdd = null; IDictionary? itemsToAdd = null; + IDictionary? environmentVariablesToAdd = null; List? warnings = null; CachingWorkloadResolver? workloadResolver = null; @@ -99,6 +136,7 @@ private sealed class CachedState globalJsonPath = priorResult.GlobalJsonPath; propertiesToAdd = priorResult.PropertiesToAdd; workloadResolver = priorResult.WorkloadResolver; + environmentVariablesToAdd = priorResult.EnvironmentVariablesToAdd; logger?.LogMessage($"\tDotnet root: {dotnetRoot}"); logger?.LogMessage($"\tMSBuild SDKs Dir: {msbuildSdksDir}"); @@ -139,9 +177,9 @@ private sealed class CachedState logger?.LogMessage($"\tResolved SDK directory: {resolverResult.ResolvedSdkDirectory}"); logger?.LogMessage($"\tglobal.json path: {resolverResult.GlobalJsonPath}"); logger?.LogMessage($"\tFailed to resolve SDK from global.json: {resolverResult.FailedToResolveSDKSpecifiedInGlobalJson}"); - - msbuildSdksDir = Path.Combine(resolverResult.ResolvedSdkDirectory, "Sdks"); - netcoreSdkVersion = new DirectoryInfo(resolverResult.ResolvedSdkDirectory).Name; + string dotnetSdkDir = resolverResult.ResolvedSdkDirectory; + msbuildSdksDir = Path.Combine(dotnetSdkDir, "Sdks"); + netcoreSdkVersion = new DirectoryInfo(dotnetSdkDir).Name; globalJsonPath = resolverResult.GlobalJsonPath; // 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 } string? fullPathToMuxer = - TryResolveMuxerFromSdkResolution(resolverResult) + TryResolveMuxerFromSdkResolution(dotnetSdkDir) ?? Path.Combine(dotnetRoot, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet); if (File.Exists(fullPathToMuxer)) { + // keeping this in until this component no longer needs to handle 17.14. propertiesToAdd ??= new Dictionary(); propertiesToAdd.Add(DotnetHostExperimentalKey, fullPathToMuxer); + // this is the future-facing implementation. + environmentVariablesToAdd ??= new Dictionary(1) + { + [DOTNET_HOST] = fullPathToMuxer + }; } else { - logger?.LogMessage($"Could not set '{DotnetHostExperimentalKey}' because dotnet executable '{fullPathToMuxer}' does not exist."); + logger?.LogMessage($"Could not set '{DOTNET_HOST}' environment variable because dotnet executable '{fullPathToMuxer}' does not exist."); } string? runtimeVersion = dotnetRoot != null ? - _getMsbuildRuntime(resolverResult.ResolvedSdkDirectory, dotnetRoot) : + _getMsbuildRuntime(dotnetSdkDir, dotnetRoot) : null; if (!string.IsNullOrEmpty(runtimeVersion)) { @@ -224,7 +268,7 @@ private sealed class CachedState if (resolverResult.FailedToResolveSDKSpecifiedInGlobalJson) { - logger?.LogMessage($"Could not resolve SDK specified in '{resolverResult.GlobalJsonPath}'. Ignoring global.json for this resolution."); + logger?.LogMessage($"Could not resolve SDK specified in '{globalJsonPath}'. Ignoring global.json for this resolution."); if (warnings == null) { @@ -241,8 +285,9 @@ private sealed class CachedState } propertiesToAdd ??= new Dictionary(); - propertiesToAdd.Add("SdkResolverHonoredGlobalJson", "false"); - propertiesToAdd.Add("SdkResolverGlobalJsonPath", resolverResult.GlobalJsonPath); + propertiesToAdd.Add(SdkResolverHonoredGlobalJson, "false"); + // TODO: this would ideally be reported anytime it was non-null - that may cause more imports though? + propertiesToAdd.Add(SdkResolverGlobalJsonPath, globalJsonPath); if (logger != null) { @@ -258,7 +303,8 @@ private sealed class CachedState NETCoreSdkVersion = netcoreSdkVersion, GlobalJsonPath = globalJsonPath, PropertiesToAdd = propertiesToAdd, - WorkloadResolver = workloadResolver + WorkloadResolver = workloadResolver, + EnvironmentVariablesToAdd = environmentVariablesToAdd }; // First check if requested SDK resolves to a workload SDK pack @@ -284,8 +330,14 @@ private sealed class CachedState Strings.MSBuildSDKDirectoryNotFound, msbuildSdkDir); } - - return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings); + if (_factorySuccessFunc != null) + { + return _factorySuccessFunc(factory, msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings, environmentVariablesToAdd); + } + else + { + return factory.IndicateSuccess(msbuildSdkDir, netcoreSdkVersion, propertiesToAdd, itemsToAdd, warnings); + } } /// @@ -295,10 +347,10 @@ private sealed class CachedState /// SDK layouts always have a defined relationship to the location of the muxer - /// the muxer binary should be exactly two directories above the SDK directory. /// - private static string? TryResolveMuxerFromSdkResolution(SdkResolutionResult resolverResult) + private static string? TryResolveMuxerFromSdkResolution(string resolvedSdkDirectory) { var expectedFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Constants.DotNetExe : Constants.DotNet; - var currentDir = resolverResult.ResolvedSdkDirectory; + var currentDir = resolvedSdkDirectory; var expectedDotnetRoot = Path.GetDirectoryName(Path.GetDirectoryName(currentDir)); var expectedMuxerPath = Path.Combine(expectedDotnetRoot, expectedFileName); if (File.Exists(expectedMuxerPath)) diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj index 58403f591e60..592fea73c335 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj @@ -127,9 +127,11 @@ + Condition="!($([System.String]::Copy('$(ResolvedDependenciesList)').Contains('%(ExpectedDependencies.Identity)')))" + ContinueOnError="true" /> + Condition="!($([System.String]::Copy('$(ExpectedDependenciesList)').Contains('%(ResolvedDependencies.FusionName)')))" + ContinueOnError="true" />