From 53b6528bc0eb7b7ff2cb3f1f056b2d313faca843 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 21 Oct 2025 12:46:42 +0000 Subject: [PATCH 1/2] =?UTF-8?q?[msbuild]=20Source=20update=20ef8ee47=20?= =?UTF-8?q?=E2=86=92=20995a3dc=20Diff:=20https://github.com/dotnet/msbuild?= =?UTF-8?q?/compare/ef8ee47aa94a43657e516677db73ed77bdbe92bf..995a3dce4178?= =?UTF-8?q?8caebf2b8ca6602a7431f08bfd06?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: https://github.com/dotnet/msbuild/commit/ef8ee47aa94a43657e516677db73ed77bdbe92bf To: https://github.com/dotnet/msbuild/commit/995a3dce41788caebf2b8ca6602a7431f08bfd06 [[ commit created by automation ]] --- src/msbuild/.vsts-dotnet.yml | 2 +- src/msbuild/azure-pipelines/vs-insertion.yml | 2 +- src/msbuild/eng/Versions.props | 3 +- .../common/post-build/nuget-verification.ps1 | 2 +- .../Microsoft.Build.Engine.UnitTests.csproj | 3 + .../Build.UnitTests/NetTaskHost_E2E_Tests.cs | 90 +++++++++++++++++-- .../ExampleNetTask/ExampleTask/ExampleTask.cs | 9 +- .../TestNetTaskWithImplicitParams.csproj | 18 ++++ .../TestNetTaskWithImplicitParams/global.json | 9 ++ .../NodeProviderOutOfProcTaskHost.cs | 4 +- .../TaskFactories/AssemblyTaskFactory.cs | 7 +- .../src/Build/Instance/TaskRegistry.cs | 8 +- src/msbuild/src/Shared/TypeLoader.cs | 28 ++++-- .../src/UnitTests.Shared/TestEnvironment.cs | 49 +++++----- src/source-manifest.json | 4 +- 15 files changed, 185 insertions(+), 53 deletions(-) create mode 100644 src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/TestNetTaskWithImplicitParams.csproj create mode 100644 src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/global.json diff --git a/src/msbuild/.vsts-dotnet.yml b/src/msbuild/.vsts-dotnet.yml index 7e32fbacdad4..e367ebaa07f7 100644 --- a/src/msbuild/.vsts-dotnet.yml +++ b/src/msbuild/.vsts-dotnet.yml @@ -49,7 +49,7 @@ variables: - name: SkipApplyOptimizationData value: true - name: EnableReleaseOneLocBuild - value: true # Enable loc for vs17.14 + value: true # Enable loc for vs18.0 - name: Codeql.Enabled value: true - group: DotNet-MSBuild-SDLValidation-Params diff --git a/src/msbuild/azure-pipelines/vs-insertion.yml b/src/msbuild/azure-pipelines/vs-insertion.yml index 94486895eb42..913c108c9e02 100644 --- a/src/msbuild/azure-pipelines/vs-insertion.yml +++ b/src/msbuild/azure-pipelines/vs-insertion.yml @@ -68,7 +68,7 @@ variables: - name: AutoInsertTargetBranch ${{ if eq(variables['Build.SourceBranchName'], 'vs18.0') }}: value: 'rel/d18.0' - ${{ if eq(variables['Build.SourceBranchName'], 'vs17.14') }}: + ${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.14') }}: value: 'rel/d17.14' ${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.13') }}: value: 'rel/d17.13' diff --git a/src/msbuild/eng/Versions.props b/src/msbuild/eng/Versions.props index 76fdff319f9d..2a5f8bc32544 100644 --- a/src/msbuild/eng/Versions.props +++ b/src/msbuild/eng/Versions.props @@ -3,11 +3,10 @@ - 18.0.0release + 18.0.2release 17.14.8 15.1.0.0 servicing - test diff --git a/src/msbuild/eng/common/post-build/nuget-verification.ps1 b/src/msbuild/eng/common/post-build/nuget-verification.ps1 index a365194a9389..ac5c69ffcac5 100644 --- a/src/msbuild/eng/common/post-build/nuget-verification.ps1 +++ b/src/msbuild/eng/common/post-build/nuget-verification.ps1 @@ -30,7 +30,7 @@ [CmdletBinding(PositionalBinding = $false)] param( [string]$NuGetExePath, - [string]$PackageSource = "https://api.nuget.org/v3/index.json", + [string]$PackageSource = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json", [string]$DownloadPath, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$args diff --git a/src/msbuild/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj b/src/msbuild/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj index 7125da363e50..def08b182d1d 100644 --- a/src/msbuild/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj +++ b/src/msbuild/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj @@ -134,6 +134,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/msbuild/src/Build.UnitTests/NetTaskHost_E2E_Tests.cs b/src/msbuild/src/Build.UnitTests/NetTaskHost_E2E_Tests.cs index e11ad0fb9452..195fcae8edc3 100644 --- a/src/msbuild/src/Build.UnitTests/NetTaskHost_E2E_Tests.cs +++ b/src/msbuild/src/Build.UnitTests/NetTaskHost_E2E_Tests.cs @@ -31,8 +31,8 @@ public NetTaskHost_E2E_Tests(ITestOutputHelper output) [WindowsFullFrameworkOnlyFact] public void NetTaskHostTest() { - using TestEnvironment env = TestEnvironment.Create(_output, setupDotnetEnvVars: true); - var bootstrapCorePath = Path.Combine(RunnerUtilities.BootstrapRootPath, "core", Constants.DotnetProcessName); + using TestEnvironment env = TestEnvironment.Create(_output); + var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT"); string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj"); @@ -45,7 +45,7 @@ public void NetTaskHostTest() successTestTask.ShouldBeTrue(); testTaskOutput.ShouldContain($"The task is executed in process: dotnet"); - testTaskOutput.ShouldContain($"Process path: {bootstrapCorePath}", customMessage: testTaskOutput); + testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput); var customTaskAssemblyLocation = Path.GetFullPath(Path.Combine(AssemblyLocation, "..", LatestDotNetCoreForMSBuild, "ExampleTask.dll")); var resource = ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( @@ -57,11 +57,70 @@ public void NetTaskHostTest() testTaskOutput.ShouldNotContain(resource); } + [WindowsFullFrameworkOnlyFact] + public void NetTaskHost_CorrectPathsEscapingTest() + { + using TestEnvironment env = TestEnvironment.Create(_output); + var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT"); + string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj"); + string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask); + + if (!successTestTask) + { + _output.WriteLine(testTaskOutput); + } + + successTestTask.ShouldBeTrue(); + testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput); + + // Explicitly validate escaping for paths with spaces + var msBuildDllPathMatch = System.Text.RegularExpressions.Regex.Match( + testTaskOutput, + @"Arg\[0\]:\s*(.+)", + System.Text.RegularExpressions.RegexOptions.CultureInvariant); + + msBuildDllPathMatch.Success.ShouldBeTrue(); + string msBuildDllPath = msBuildDllPathMatch.Groups[1].Value.Trim(); + + if (msBuildDllPath.Contains(" ")) + { + // Extract all space-delimited parts of the path and verify none are in subsequent args + var pathParts = msBuildDllPath.Split([' '], StringSplitOptions.RemoveEmptyEntries); + if (pathParts.Length > 1) + { + // Check that later path segments don't appear as separate command line arguments + var arg1Match = System.Text.RegularExpressions.Regex.Match( + testTaskOutput, + @"Arg\[1\]:\s*(.+)", + System.Text.RegularExpressions.RegexOptions.CultureInvariant); + + if (arg1Match.Success) + { + string arg1Value = arg1Match.Groups[1].Value.Trim(); + // Arg[1] should be a flag (starts with /), not a path fragment + arg1Value.ShouldMatch(@"^[/-]", "Arg[1] should be a flag, not a continuation of the path"); + } + } + } + + // Validate if path is in quotes in command line + var cmdLineMatch = System.Text.RegularExpressions.Regex.Match( + testTaskOutput, + @"Command line:\s*(.+)", + System.Text.RegularExpressions.RegexOptions.CultureInvariant); + + if (cmdLineMatch.Success) + { + string cmdLine = cmdLineMatch.Groups[1].Value.Trim(); + string quotedArg0 = $"\"{msBuildDllPath}\""; + cmdLine.ShouldContain(quotedArg0); + } + } + [WindowsFullFrameworkOnlyFact] public void MSBuildTaskInNetHostTest() { - using TestEnvironment env = TestEnvironment.Create(_output, setupDotnetEnvVars: true); - var bootstrapCorePath = Path.Combine(RunnerUtilities.BootstrapRootPath, "core", Constants.DotnetProcessName); + using TestEnvironment env = TestEnvironment.Create(_output); string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestMSBuildTaskInNet", "TestMSBuildTaskInNet.csproj"); @@ -75,5 +134,26 @@ public void MSBuildTaskInNetHostTest() successTestTask.ShouldBeTrue(); testTaskOutput.ShouldContain($"Hello TEST"); } + + [WindowsFullFrameworkOnlyFact] + public void NetTaskWithImplicitHostParamsTest() + { + using TestEnvironment env = TestEnvironment.Create(_output); + var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT"); + + string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTaskWithImplicitParams", "TestNetTaskWithImplicitParams.csproj"); + + string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask); + + if (!successTestTask) + { + _output.WriteLine(testTaskOutput); + } + + successTestTask.ShouldBeTrue(); + testTaskOutput.ShouldContain($"The task is executed in process: dotnet"); + testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput); + testTaskOutput.ShouldContain("/nodereuse:False"); + } } } diff --git a/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/ExampleTask/ExampleTask.cs b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/ExampleTask/ExampleTask.cs index 04e8df02a533..563724a07f73 100644 --- a/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/ExampleTask/ExampleTask.cs +++ b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/ExampleTask/ExampleTask.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Diagnostics; using Microsoft.Build.Framework; @@ -22,9 +23,15 @@ public override bool Execute() Log.LogMessage(MessageImportance.High, $"The task is executed in process: {executingProcess}"); Log.LogMessage(MessageImportance.High, $"Process path: {processPath}"); + string[] args = Environment.GetCommandLineArgs(); + for (int i = 0; i < args.Length; i++) + { + Log.LogMessage(MessageImportance.High, $"Arg[{i}]: {args[i]}"); + } + return true; } - catch (System.Exception ex) + catch (Exception ex) { Log.LogError($"Failed to determine executing process: {ex.Message}"); return false; diff --git a/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/TestNetTaskWithImplicitParams.csproj b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/TestNetTaskWithImplicitParams.csproj new file mode 100644 index 000000000000..d969c9a295d2 --- /dev/null +++ b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/TestNetTaskWithImplicitParams.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + + + + $([System.IO.Path]::GetFullPath('$([System.IO.Path]::Combine('$(AssemblyLocation)', '..'))')) + $([System.IO.Path]::Combine('$(TestProjectFolder)', '$(TargetFramework)', 'ExampleTask.dll')) + + + + + + + + + diff --git a/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/global.json b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/global.json new file mode 100644 index 000000000000..611555aea5f3 --- /dev/null +++ b/src/msbuild/src/Build.UnitTests/TestAssets/ExampleNetTask/TestNetTaskWithImplicitParams/global.json @@ -0,0 +1,9 @@ +{ + "sdk": { + // This global.json is needed to prevent builds running in tests using the bootstrap layout from walking + // up the repo tree and resolving our sdk.paths, instead of the bootstrap layout's SDK. + // See https://github.com/dotnet/runtime/issues/118488 for details. + "allowPrerelease": true, + "rollForward": "latestMajor" + } +} diff --git a/src/msbuild/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/msbuild/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index 30af960b9868..33baf04b0fc4 100644 --- a/src/msbuild/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/msbuild/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -644,8 +644,8 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor return false; } - // if runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments. - string commandLineArgsPlaceholder = "{0} /nologo /nodemode:2 /nodereuse:{1} /low:{2} "; + // If runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments. + string commandLineArgsPlaceholder = "\"{0}\" /nologo /nodemode:2 /nodereuse:{1} /low:{2} "; IList nodeContexts; int nodeId = (int)hostContext; diff --git a/src/msbuild/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/msbuild/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 0880c7dee8bc..8e700d6ff92a 100644 --- a/src/msbuild/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/msbuild/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -274,8 +274,10 @@ internal LoadedType InitializeFactory( ErrorUtilities.VerifyThrowArgumentNull(loadInfo); VerifyThrowIdentityParametersValid(taskFactoryIdentityParameters, elementLocation, taskName, "Runtime", "Architecture"); + bool taskHostParamsMatchCurrentProc = true; if (taskFactoryIdentityParameters != null) { + taskHostParamsMatchCurrentProc = TaskHostParametersMatchCurrentProcess(taskFactoryIdentityParameters); _factoryIdentityParameters = new Dictionary(taskFactoryIdentityParameters, StringComparer.OrdinalIgnoreCase); } @@ -283,7 +285,8 @@ internal LoadedType InitializeFactory( _isTaskHostFactory = (taskFactoryIdentityParameters != null && taskFactoryIdentityParameters.TryGetValue(Constants.TaskHostExplicitlyRequested, out string isTaskHostFactory) - && isTaskHostFactory.Equals("true", StringComparison.OrdinalIgnoreCase)); + && isTaskHostFactory.Equals("true", StringComparison.OrdinalIgnoreCase)) + || !taskHostParamsMatchCurrentProc; try { @@ -293,7 +296,7 @@ internal LoadedType InitializeFactory( string assemblyName = loadInfo.AssemblyName ?? Path.GetFileName(loadInfo.AssemblyFile); using var assemblyLoadsTracker = AssemblyLoadsTracker.StartTracking(targetLoggingContext, AssemblyLoadingContext.TaskRun, assemblyName); - _loadedType = _typeLoader.Load(taskName, loadInfo, _taskHostFactoryExplicitlyRequested); + _loadedType = _typeLoader.Load(taskName, loadInfo, _taskHostFactoryExplicitlyRequested, taskHostParamsMatchCurrentProc); ProjectErrorUtilities.VerifyThrowInvalidProject(_loadedType != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty); } catch (TargetInvocationException e) diff --git a/src/msbuild/src/Build/Instance/TaskRegistry.cs b/src/msbuild/src/Build/Instance/TaskRegistry.cs index ef60ac899ce6..9ffae09a742d 100644 --- a/src/msbuild/src/Build/Instance/TaskRegistry.cs +++ b/src/msbuild/src/Build/Instance/TaskRegistry.cs @@ -1489,8 +1489,12 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo bool isAssemblyTaskFactory = String.Equals(TaskFactoryAttributeName, AssemblyTaskFactory, StringComparison.OrdinalIgnoreCase); bool isTaskHostFactory = String.Equals(TaskFactoryAttributeName, TaskHostFactory, StringComparison.OrdinalIgnoreCase); - _taskFactoryParameters ??= new(); - TaskFactoryParameters.Add(Constants.TaskHostExplicitlyRequested, isTaskHostFactory.ToString()); + _taskFactoryParameters ??= []; + + if (isTaskHostFactory) + { + TaskFactoryParameters.Add(Constants.TaskHostExplicitlyRequested, isTaskHostFactory.ToString()); + } if (isAssemblyTaskFactory || isTaskHostFactory) { diff --git a/src/msbuild/src/Shared/TypeLoader.cs b/src/msbuild/src/Shared/TypeLoader.cs index f96c218f22b2..145b5b08e50c 100644 --- a/src/msbuild/src/Shared/TypeLoader.cs +++ b/src/msbuild/src/Shared/TypeLoader.cs @@ -212,9 +212,10 @@ private static Assembly LoadAssemblyUsingMetadataLoadContext(AssemblyLoadInfo as internal LoadedType Load( string typeName, AssemblyLoadInfo assembly, - bool useTaskHost = false) + bool useTaskHost = false, + bool taskHostParamsMatchCurrentProc = true) { - return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, useTaskHost); + return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, useTaskHost, taskHostParamsMatchCurrentProc); } /// @@ -227,7 +228,7 @@ internal LoadedType ReflectionOnlyLoad( string typeName, AssemblyLoadInfo assembly) { - return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, useTaskHost: false); + return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, useTaskHost: false, taskHostParamsMatchCurrentProc: true); } /// @@ -235,7 +236,12 @@ internal LoadedType ReflectionOnlyLoad( /// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type /// found will be returned. /// - private LoadedType GetLoadedType(ConcurrentDictionary, ConcurrentDictionary> cache, string typeName, AssemblyLoadInfo assembly, bool useTaskHost) + private LoadedType GetLoadedType( + ConcurrentDictionary, ConcurrentDictionary> cache, + string typeName, + AssemblyLoadInfo assembly, + bool useTaskHost, + bool taskHostParamsMatchCurrentProc) { // A given type filter have been used on a number of assemblies, Based on the type filter we will get another dictionary which // will map a specific AssemblyLoadInfo to a AssemblyInfoToLoadedTypes class which knows how to find a typeName in a given assembly. @@ -246,7 +252,7 @@ private LoadedType GetLoadedType(ConcurrentDictionary, AssemblyInfoToLoadedTypes typeNameToType = loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _)); - return typeNameToType.GetLoadedTypeByTypeName(typeName, useTaskHost); + return typeNameToType.GetLoadedTypeByTypeName(typeName, useTaskHost, taskHostParamsMatchCurrentProc); } /// @@ -316,11 +322,11 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly /// /// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly /// - internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost) + internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost, bool taskHostParamsMatchCurrentProc) { ErrorUtilities.VerifyThrowArgumentNull(typeName); - if (useTaskHost && _assemblyLoadInfo.AssemblyFile is not null) + if (ShouldUseMetadataLoadContext(useTaskHost, taskHostParamsMatchCurrentProc)) { return GetLoadedTypeFromTypeNameUsingMetadataLoadContext(typeName); } @@ -374,6 +380,14 @@ internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost) return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly ?? type.Assembly, typeof(ITaskItem), loadedViaMetadataLoadContext: false) : null; } + /// + /// Determine whether an assembly is likely to be used out of process and thus loaded with a . + /// + /// Task Host Parameter was specified explicitly in XML or through environment variable. + /// The parameter defines if Runtime/Architecture explicitly defined in XML match current process. + private bool ShouldUseMetadataLoadContext(bool useTaskHost, bool taskHostParamsMatchCurrentProc) => + (useTaskHost || !taskHostParamsMatchCurrentProc) && _assemblyLoadInfo.AssemblyFile is not null; + private LoadedType GetLoadedTypeFromTypeNameUsingMetadataLoadContext(string typeName) { return _publicTypeNameToLoadedType.GetOrAdd(typeName, typeName => diff --git a/src/msbuild/src/UnitTests.Shared/TestEnvironment.cs b/src/msbuild/src/UnitTests.Shared/TestEnvironment.cs index 654b230254de..1525ccbfa945 100644 --- a/src/msbuild/src/UnitTests.Shared/TestEnvironment.cs +++ b/src/msbuild/src/UnitTests.Shared/TestEnvironment.cs @@ -10,11 +10,9 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Shared.Debugging; using Microsoft.Build.Shared.FileSystem; -using Microsoft.Build.UnitTests.Shared; using Shouldly; using Xunit; using Xunit.Abstractions; @@ -58,15 +56,10 @@ public partial class TestEnvironment : IDisposable /// (MSBuild_*.txt) in the temp directory and treats their presence as test failures. /// Set to true to disable this monitoring for tests that expect build failures. /// - /// - /// When true, configures .NET-specific environment variables including PATH, - /// DOTNET_ROOT, and DOTNET_HOST_PATH to point to the bootstrap .NET installation. - /// This ensures tests use the correct .NET runtime and SDK versions. - /// /// /// A configured TestEnvironment instance with the specified settings applied. /// - public static TestEnvironment Create(ITestOutputHelper output = null, bool ignoreBuildErrorFiles = false, bool setupDotnetEnvVars = false) + public static TestEnvironment Create(ITestOutputHelper output = null, bool ignoreBuildErrorFiles = false) { var env = new TestEnvironment(output ?? new DefaultOutput()); @@ -76,27 +69,12 @@ public static TestEnvironment Create(ITestOutputHelper output = null, bool ignor env.WithInvariant(new BuildFailureLogInvariant()); } - if (setupDotnetEnvVars) - { - SetupDotnetEnvironmentVariables(); - } - // Clear these two environment variables first in case pre-setting affects the test. env.SetEnvironmentVariable("MSBUILDLIVELOGGER", null); env.SetEnvironmentVariable("MSBUILDTERMINALLOGGER", null); env.SetEnvironmentVariable("MSBUILDUSESERVER", null); return env; - - void SetupDotnetEnvironmentVariables() - { - var coreDirectory = Path.Combine(RunnerUtilities.BootstrapRootPath, "core"); - var bootstrapCorePath = Path.Combine(coreDirectory, Constants.DotnetProcessName); - - _ = env.SetEnvironmentVariable("PATH", $"{coreDirectory}{Path.PathSeparator}{Environment.GetEnvironmentVariable("PATH")}"); - _ = env.SetEnvironmentVariable("DOTNET_ROOT", coreDirectory); - _ = env.SetEnvironmentVariable("DOTNET_HOST_PATH", bootstrapCorePath); - } } private TestEnvironment(ITestOutputHelper output) @@ -363,6 +341,22 @@ CommonWriterType OutPutHelperWriter(ITestOutputHelper output) } } + /// + /// Gets the original value of an environment variable. Can be needed to assert in a test. + /// + public string GetEnvironmentVariable(string variableName) + { + for (int i = 0; i < _variants.Count; i++) + { + if (_variants[i] is TransientTestEnvironmentVariable ttev && ttev.EnvironmentVariableName == variableName) + { + return ttev.OriginalValue; + } + } + + return null; + } + /// /// Create an test variant used to change the value of an environment variable during a test. Original value /// will be restored when complete. @@ -810,10 +804,11 @@ public TransientTestEnvironmentVariable(string environmentVariableName, string n Environment.SetEnvironmentVariable(environmentVariableName, newValue); } - public override void Revert() - { - Environment.SetEnvironmentVariable(_environmentVariableName, _originalValue); - } + public string EnvironmentVariableName => _environmentVariableName; + + public string OriginalValue => _originalValue; + + public override void Revert() => Environment.SetEnvironmentVariable(_environmentVariableName, _originalValue); } public class TransientWorkingDirectory : TransientTestState diff --git a/src/source-manifest.json b/src/source-manifest.json index c492dc5dde1d..bb21de65d8ff 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -55,10 +55,10 @@ "commitSha": "2cb9ee0a5d63b52ad7bfbd9364b90b991a26fd72" }, { - "barId": 285653, + "barId": 287967, "path": "msbuild", "remoteUri": "https://github.com/dotnet/msbuild", - "commitSha": "ef8ee47aa94a43657e516677db73ed77bdbe92bf" + "commitSha": "995a3dce41788caebf2b8ca6602a7431f08bfd06" }, { "barId": 286446, From c16072f7234775d39480d9b54c54eab7e2d8eee2 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 21 Oct 2025 12:46:48 +0000 Subject: [PATCH 2/2] Update dependencies --- src/msbuild/eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msbuild/eng/Version.Details.xml b/src/msbuild/eng/Version.Details.xml index 8fa567e98a6f..6a9b86f5ab63 100644 --- a/src/msbuild/eng/Version.Details.xml +++ b/src/msbuild/eng/Version.Details.xml @@ -1,6 +1,6 @@ - +