diff --git a/.github/skills/multithreaded-task-migration/SKILL.md b/.github/skills/multithreaded-task-migration/SKILL.md index 2a64b6ecc8b..9dcb22546a5 100644 --- a/.github/skills/multithreaded-task-migration/SKILL.md +++ b/.github/skills/multithreaded-task-migration/SKILL.md @@ -18,7 +18,7 @@ b. Implement `IMultiThreadableTask` **only if** the task needs `TaskEnvironment` [MSBuildMultiThreadableTask] public class MyTask : Task, IMultiThreadableTask { - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; ... } ``` @@ -276,7 +276,7 @@ Assertions: Execute() return value, [Output] exact string, error message content ## Sign-Off Checklist - [ ] `[MSBuildMultiThreadableTask]` on every concrete class (not just base — `Inherited=false`) -- [ ] `IMultiThreadableTask` on classes that use `TaskEnvironment` APIs, with default initializer `= new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance)` +- [ ] `IMultiThreadableTask` on classes that use `TaskEnvironment` APIs, with default initializer `= TaskEnvironment.Fallback` - [ ] Every `[Output]` property: exact string value matches pre-migration - [ ] Every `Log.LogError`/`LogWarning`: path in message matches pre-migration (use `OriginalValue`) - [ ] Every `GetAbsolutePath` call: null/empty exception behavior matches old code path diff --git a/documentation/specs/multithreading/thread-safe-tasks.md b/documentation/specs/multithreading/thread-safe-tasks.md index 3ef98a4b215..4d788be4e97 100644 --- a/documentation/specs/multithreading/thread-safe-tasks.md +++ b/documentation/specs/multithreading/thread-safe-tasks.md @@ -39,7 +39,7 @@ Similar to how MSBuild provides the abstract `Task` class with default implement namespace Microsoft.Build.Utilities; public abstract class MultiThreadableTask : Task, IMultiThreadableTask { - public TaskEnvironment TaskEnvironment{ get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; } ``` diff --git a/src/Build.UnitTests/BackEnd/TaskEnvironment_Tests.cs b/src/Build.UnitTests/BackEnd/TaskEnvironment_Tests.cs index 43b7910f1c0..6ef2f9b2902 100644 --- a/src/Build.UnitTests/BackEnd/TaskEnvironment_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskEnvironment_Tests.cs @@ -31,8 +31,8 @@ private static TaskEnvironment CreateTaskEnvironment(string environmentType) { return environmentType switch { - StubEnvironmentName => TaskEnvironmentHelper.CreateForTest(), - MultithreadedEnvironmentName => new TaskEnvironment(new MultiThreadedTaskEnvironmentDriver(GetResolvedTempPath())), + StubEnvironmentName => TaskEnvironment.Fallback, + MultithreadedEnvironmentName => TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(GetResolvedTempPath()), _ => throw new ArgumentException($"Unknown environment type: {environmentType}") }; } @@ -306,7 +306,7 @@ public void TaskEnvironment_StubEnvironment_ShouldAffectSystemEnvironment() string testVarName = $"MSBUILD_STUB_ISOLATION_TEST_{Guid.NewGuid():N}"; string testVarValue = "stub_test_value"; - var stubEnvironment = TaskEnvironmentHelper.CreateForTest(); + var stubEnvironment = TaskEnvironment.Fallback; try { @@ -336,10 +336,9 @@ public void TaskEnvironment_MultithreadedEnvironment_ShouldBeIsolatedFromSystem( string testVarName = $"MSBUILD_MULTITHREADED_ISOLATION_TEST_{Guid.NewGuid():N}"; string testVarValue = "multithreaded_test_value"; - using var driver = new MultiThreadedTaskEnvironmentDriver( + var multithreadedEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment( GetResolvedTempPath(), new Dictionary(StringComparer.OrdinalIgnoreCase)); - var multithreadedEnvironment = new TaskEnvironment(driver); try { diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index 42d90d11290..c194974c746 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -394,11 +394,11 @@ public void SubmitBuildRequest(BuildRequest request) { string projectDirectoryFullPath = Path.GetDirectoryName(config.ProjectFullPath); var environmentVariables = new Dictionary(_componentHost.BuildParameters.BuildProcessEnvironmentInternal); - taskEnvironment = new TaskEnvironment(new MultiThreadedTaskEnvironmentDriver(projectDirectoryFullPath, environmentVariables)); + taskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDirectoryFullPath, environmentVariables); } else { - taskEnvironment = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + taskEnvironment = TaskEnvironment.Fallback; } BuildRequestEntry entry = new BuildRequestEntry(request, config, taskEnvironment); diff --git a/src/Tasks/AssignTargetPath.cs b/src/Tasks/AssignTargetPath.cs index 43e39efd7a2..f6dbe84130d 100644 --- a/src/Tasks/AssignTargetPath.cs +++ b/src/Tasks/AssignTargetPath.cs @@ -23,7 +23,7 @@ public class AssignTargetPath : TaskExtension, IMultiThreadableTask /// /// Gets or sets the task execution environment for thread-safe path resolution. /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// diff --git a/src/Tasks/Copy.cs b/src/Tasks/Copy.cs index 58ff7cfc6ae..c8f97d9937a 100644 --- a/src/Tasks/Copy.cs +++ b/src/Tasks/Copy.cs @@ -187,7 +187,7 @@ public Copy() public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; #endregion diff --git a/src/Tasks/Delete.cs b/src/Tasks/Delete.cs index b928bf858af..66706765a45 100644 --- a/src/Tasks/Delete.cs +++ b/src/Tasks/Delete.cs @@ -65,7 +65,7 @@ public ITaskItem[] Files public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Verify that the inputs are correct. diff --git a/src/Tasks/DownloadFile.cs b/src/Tasks/DownloadFile.cs index 2386d482d8c..859a77bb711 100644 --- a/src/Tasks/DownloadFile.cs +++ b/src/Tasks/DownloadFile.cs @@ -69,7 +69,7 @@ public sealed class DownloadFile : TaskExtension, ICancelableTask, IIncrementalT public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Gets or sets a to use. This is used by unit tests to mock a connection to a remote server. diff --git a/src/Tasks/FileIO/GetFileHash.cs b/src/Tasks/FileIO/GetFileHash.cs index 9aa91f99ad0..3adffc6f418 100644 --- a/src/Tasks/FileIO/GetFileHash.cs +++ b/src/Tasks/FileIO/GetFileHash.cs @@ -66,7 +66,7 @@ internal static readonly Dictionary> SupportedAlgori public ITaskItem[] Items { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; public override bool Execute() { diff --git a/src/Tasks/FileIO/ReadLinesFromFile.cs b/src/Tasks/FileIO/ReadLinesFromFile.cs index 552db96d7ed..50b182da06f 100644 --- a/src/Tasks/FileIO/ReadLinesFromFile.cs +++ b/src/Tasks/FileIO/ReadLinesFromFile.cs @@ -25,7 +25,7 @@ public class ReadLinesFromFile : TaskExtension, IMultiThreadableTask public ITaskItem File { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Receives lines from file. diff --git a/src/Tasks/FileIO/VerifyFileHash.cs b/src/Tasks/FileIO/VerifyFileHash.cs index 9b6dd12d3d0..2fef9d398b5 100644 --- a/src/Tasks/FileIO/VerifyFileHash.cs +++ b/src/Tasks/FileIO/VerifyFileHash.cs @@ -17,7 +17,7 @@ namespace Microsoft.Build.Tasks public sealed class VerifyFileHash : TaskExtension, ICancelableTask, IMultiThreadableTask { /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// The file path. diff --git a/src/Tasks/FileIO/WriteLinesToFile.cs b/src/Tasks/FileIO/WriteLinesToFile.cs index 0cced7ad137..27dccd84925 100644 --- a/src/Tasks/FileIO/WriteLinesToFile.cs +++ b/src/Tasks/FileIO/WriteLinesToFile.cs @@ -23,7 +23,7 @@ public class WriteLinesToFile : TaskExtension, IIncrementalTask, IMultiThreadabl private static readonly Encoding s_defaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// File to write lines to. diff --git a/src/Tasks/ListOperators/FindUnderPath.cs b/src/Tasks/ListOperators/FindUnderPath.cs index 1347cbd3528..baa106bbebd 100644 --- a/src/Tasks/ListOperators/FindUnderPath.cs +++ b/src/Tasks/ListOperators/FindUnderPath.cs @@ -21,7 +21,7 @@ public class FindUnderPath : TaskExtension, IMultiThreadableTask /// /// Gets or sets the task execution environment for thread-safe path resolution. /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Filter based on whether items fall under this path or not. diff --git a/src/Tasks/MakeDir.cs b/src/Tasks/MakeDir.cs index cc3f4a33ccf..941d924ca97 100644 --- a/src/Tasks/MakeDir.cs +++ b/src/Tasks/MakeDir.cs @@ -35,7 +35,7 @@ public ITaskItem[] Directories public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; private ITaskItem[] _directories; diff --git a/src/Tasks/Move.cs b/src/Tasks/Move.cs index e764d89cfe1..52003eed64f 100644 --- a/src/Tasks/Move.cs +++ b/src/Tasks/Move.cs @@ -76,7 +76,7 @@ public class Move : TaskExtension, ICancelableTask, IIncrementalTask, IMultiThre public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Stop and return (in an undefined state) as soon as possible. diff --git a/src/Tasks/RemoveDir.cs b/src/Tasks/RemoveDir.cs index 83b4569c8d2..23dc5bc914d 100644 --- a/src/Tasks/RemoveDir.cs +++ b/src/Tasks/RemoveDir.cs @@ -25,7 +25,7 @@ public class RemoveDir : TaskExtension, IIncrementalTask, IMultiThreadableTask private ITaskItem[] _directories; /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; [Required] public ITaskItem[] Directories diff --git a/src/Tasks/Touch.cs b/src/Tasks/Touch.cs index b524a842d16..0c5492945b6 100644 --- a/src/Tasks/Touch.cs +++ b/src/Tasks/Touch.cs @@ -50,7 +50,7 @@ public class Touch : TaskExtension, IIncrementalTask, IMultiThreadableTask public ITaskItem[] TouchedFiles { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// Importance: high, normal, low (default normal) diff --git a/src/Tasks/Unzip.cs b/src/Tasks/Unzip.cs index e1a0dfa0de3..9eb4d0ebbec 100644 --- a/src/Tasks/Unzip.cs +++ b/src/Tasks/Unzip.cs @@ -77,7 +77,7 @@ public sealed class Unzip : TaskExtension, ICancelableTask, IIncrementalTask, IM public bool FailIfNotIncremental { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// public void Cancel() diff --git a/src/Tasks/WriteCodeFragment.cs b/src/Tasks/WriteCodeFragment.cs index 7fd8815cbdd..88eff021e28 100644 --- a/src/Tasks/WriteCodeFragment.cs +++ b/src/Tasks/WriteCodeFragment.cs @@ -34,7 +34,7 @@ namespace Microsoft.Build.Tasks public class WriteCodeFragment : TaskExtension, IMultiThreadableTask { /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; private const string TypeNameSuffix = "_TypeName"; private const string IsLiteralSuffix = "_IsLiteral"; private static readonly string[] NamespaceImports = ["System", "System.Reflection"]; diff --git a/src/Tasks/XmlPeek.cs b/src/Tasks/XmlPeek.cs index 026be8fcf65..c3c789e655a 100644 --- a/src/Tasks/XmlPeek.cs +++ b/src/Tasks/XmlPeek.cs @@ -25,7 +25,7 @@ public class XmlPeek : TaskExtension, IMultiThreadableTask #region Properties /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// The XPath Query. diff --git a/src/Tasks/XmlPoke.cs b/src/Tasks/XmlPoke.cs index 1ca7f718c91..31b80aa7e8f 100644 --- a/src/Tasks/XmlPoke.cs +++ b/src/Tasks/XmlPoke.cs @@ -24,7 +24,7 @@ public class XmlPoke : TaskExtension, IMultiThreadableTask #region Properties /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// The XML input as file path. diff --git a/src/Tasks/XslTransformation.cs b/src/Tasks/XslTransformation.cs index 1d760bb619b..bf32159a33a 100644 --- a/src/Tasks/XslTransformation.cs +++ b/src/Tasks/XslTransformation.cs @@ -29,7 +29,7 @@ public class XslTransformation : TaskExtension, IMultiThreadableTask #region Members /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; /// /// The output files. diff --git a/src/Tasks/ZipDirectory.cs b/src/Tasks/ZipDirectory.cs index 30ed730be31..e53bd8722c9 100644 --- a/src/Tasks/ZipDirectory.cs +++ b/src/Tasks/ZipDirectory.cs @@ -59,7 +59,7 @@ public sealed class ZipDirectory : TaskExtension, IIncrementalTask, IMultiThread public string? CompressionLevel { get; set; } /// - public TaskEnvironment TaskEnvironment { get; set; } = new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; public override bool Execute() { diff --git a/src/UnitTests.Shared/TaskEnvironmentHelper.cs b/src/UnitTests.Shared/TaskEnvironmentHelper.cs index 484a5c917a9..172c48ceb5b 100644 --- a/src/UnitTests.Shared/TaskEnvironmentHelper.cs +++ b/src/UnitTests.Shared/TaskEnvironmentHelper.cs @@ -18,7 +18,7 @@ public static class TaskEnvironmentHelper /// A TaskEnvironment suitable for use in tests. public static TaskEnvironment CreateForTest() { - return new TaskEnvironment(MultiProcessTaskEnvironmentDriver.Instance); + return TaskEnvironment.Fallback; } } }