Migrate GetRestoreSettingsTask to the multithreadable task model#7543
Migrate GetRestoreSettingsTask to the multithreadable task model#7543OvesN wants to merge 10 commits into
Conversation
…rk build The [MSBuildMultiThreadableTask] marker attribute only exists in Microsoft.Build.Framework 18.6 and later. The .NET (SDK) build of NuGet's MSBuild tasks gets it from the package, but the .NET Framework build compiles against the in-box MSBuild reference assemblies, which do not contain the type. This adds a self-defined compatibility shim (guarded by #if NETFRAMEWORK, in the Microsoft.Build.Framework namespace) so the shared task source can apply the marker without conditional compilation at every task. MSBuild detects the attribute by namespace and name only, so the shim is recognized by 18.6+ hosts and ignored by older ones. This is a prerequisite for migrating relative-path-resolving restore tasks to the multithreadable execution model. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark the task with [MSBuildMultiThreadableTask] and, on the .NET (SDK) build, implement IMultiThreadableTask so MSBuild can run it in-process during multithreaded builds (dotnet build -mt). Add a MakeAbsolute helper that anchors relative directory inputs to the project directory via TaskEnvironment (guarded for null/empty, which GetAbsolutePath throws on). It is applied to the directory inputs that are NOT reserved-guaranteed absolute and are used as path-resolution bases: RestoreSolutionDirectory (= $(SolutionDir), user-overridable) and RestoreRootConfigDirectory. MSBuildStartupDirectory and ProjectUniqueName are reserved MSBuild values that are always absolute, so no separate absolutization is required for them. On .NET Framework MakeAbsolute is a pass-through, preserving the existing behavior. Caveat: command-line override paths (RestoreConfigFile, RestorePackagesPathOverride, RestoreRepositoryPathOverride) flow through the shared UriUtility.GetAbsolutePath, whose Path.GetFullPath(local) / Path.Combine behavior can still consult the current directory/drive for rooted or drive-relative inputs such as "\foo" or "C:". That is pre-existing UriUtility behavior (identical on .NET Framework), affects only pathological inputs, and is out of scope for this task. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the local variables (local -> localPath, relativeUri -> relativeUriReference, absoluteUri -> absoluteUriReference) and rewrite the comments to describe what the method actually does: it distinguishes a local filesystem path from a remote source URL. A value with no URI scheme parses as a "relative" reference - which covers every local path, including rooted ones such as C:\x, \foo and \\server\share - and is resolved against the root directory; values with a scheme (http://, https://, ...) are remote URLs and are returned unchanged. Comment/rename only; no behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nikao/migrate-GetRestoreSettingsTask-to-mt merge
There was a problem hiding this comment.
(AI review, results were not that good)
Reviewed with two automated passes: an MT-migration call-chain review and an MSBuild code review. Overall the migration is correct and net472-safe — every path input reaching Execute() is absolutized before hitting a filesystem API, the netcore-only APIs are correctly #if-guarded, and the TaskEnvironment.Fallback default is right. Inline notes below; none are hard blockers.
Minor (no inline, line unchanged): the #if DEBUG read of DEBUG_RESTORE_SETTINGS_TASK still goes through _environmentVariableReader (process env) rather than TaskEnvironment.GetEnvironmentVariable — negligible since it's a debugger-attach flag.
jankratochvilcz
left a comment
There was a problem hiding this comment.
Left two smaller comments (the AI reviews on this repo seem not that great with our skills compared to running them in MSBuild / SDK 😢)
| /// Collection for tests that mutate the process-wide current directory. | ||
| /// </summary> | ||
| [CollectionDefinition(nameof(UriUtilityCurrentDirectoryCollection), DisableParallelization = true)] | ||
| public class UriUtilityCurrentDirectoryCollection |
There was a problem hiding this comment.
Should we add a test that actually tests when we pass absolute paths?
| [InlineData("nested/../child")] | ||
| [InlineData("./relative")] | ||
| [InlineData("https://api.nuget.org/v3/index.json")] | ||
| public void UriUtility_GetAbsolutePath_WithAbsoluteRoot_IsIndependentOfCurrentDirectory(string source) |
There was a problem hiding this comment.
(nit) - personally, I find these tests hard to read as we're not testing against a static expected value - I think it's good if assertions themselves are trivial as it makes it easier to review that the test is asserting what we expect.
Bug
Fixes: dotnet/msbuild#14186
Description
Migrates
GetRestoreSettingsTaskto MSBuild's multithreaded (in-process) task model so it runs safely in parallel builds.IMultiThreadableTask/[MSBuildMultiThreadableTask](net472 uses a polyfill attribute) and absolutizes path inputs throughTaskEnvironment, so path resolution no longer depends on the process working directory.UriUtility.GetAbsolutePathnow anchors resolution to the root directory on .NET, fixing a current-directory/drive leak for rooted-but-relative paths such asC:or\foo.PR Checklist