Enlighten ResolveNonMSBuildProjectOutput task for multithreaded mode
Parent: #11834
Context
ResolveNonMSBuildProjectOutput is the second-highest-execution unmigrated task (19.8B total executions) listed in the migration epic (#11834) under "Other (either simple or with unknown issues)". It resolves output paths for non-MSBuild project references by matching against a pre-resolved project output XML blob provided by the IDE.
The task has no cwd-dependent operations:
- No
Environment.CurrentDirectory, Path.GetFullPath, or direct File.*/Directory.* usage
- The only file I/O is
AssemblyName.GetAssemblyName(resolvedPath.ItemSpec) — this operates on pre-resolved absolute paths extracted from the PreresolvedProjectOutputs XML string. The paths come from the IDE/solution configuration, not from relative path resolution
- Base class
ResolveProjectBase uses reference.GetMetadata("FullPath") — engine-resolved absolute paths
- All mutable state is instance-scoped (solution configuration cache, project reference lists)
This is an attribute-only migration: the task needs [MSBuildMultiThreadableTask] but does NOT need IMultiThreadableTask or TaskEnvironment because its file access uses pre-resolved absolute paths and it performs no relative path resolution, environment variable access, or process launches.
Approach
Apply the PR #13045 (attribute-only) pattern: add [MSBuildMultiThreadableTask] to the class declaration. No IMultiThreadableTask implementation is needed.
- Decorate
ResolveNonMSBuildProjectOutput with [MSBuildMultiThreadableTask].
- No other code changes required — the task is already thread-safe by design.
ChangeWave consideration
No behavioral change — the attribute simply declares the task safe for multithreaded scheduling. Observable behavior (return value, [Output] properties, error/warning messages) is byte-identical. No ChangeWave needed.
Test coverage assessment
Existing unit tests
Tests exist for project reference resolution, pre-resolved output matching, managed assembly detection, and unresolved reference handling.
Gaps to fill in this PR
- G1 — Concurrency test. Two
ResolveNonMSBuildProjectOutput instances with different PreresolvedProjectOutputs XML and ProjectReferences execute concurrently, asserting each produces correct, independent ResolvedOutputPaths and UnresolvedProjectReferences output. Verifies no shared mutable state interference.
- G2 — Baseline parity. Verify all existing tests continue to pass with no behavioral changes.
[Output] ResolvedOutputPaths and [Output] UnresolvedProjectReferences items have identical ItemSpec and metadata.
Acceptance criteria
Implementation order
- Apply
[MSBuildMultiThreadableTask] to ResolveNonMSBuildProjectOutput.
- Add G1 (concurrency) test.
- Run
Tasks.UnitTests; verify clean.
- Run repo build with
-v quiet to ensure no new warnings.
Risks / open questions
AssemblyName.GetAssemblyName() performs actual disk I/O for assembly probing. However, it operates on pre-resolved absolute paths from the IDE blob — no relative path resolution is involved. Under MT execution, concurrent calls to GetAssemblyName on different files are safe (no shared state in the static method).
GetAssemblyName is injected via a delegate property for testability. The default delegate is AssemblyName.GetAssemblyName — a static, stateless method.
- Base class
ResolveProjectBase is shared with AssignProjectConfiguration. The [MSBuildMultiThreadableTask] attribute has Inherited = false, so each concrete class must be independently decorated.
References
Enlighten ResolveNonMSBuildProjectOutput task for multithreaded mode
Parent: #11834
Context
ResolveNonMSBuildProjectOutputis the second-highest-execution unmigrated task (19.8B total executions) listed in the migration epic (#11834) under "Other (either simple or with unknown issues)". It resolves output paths for non-MSBuild project references by matching against a pre-resolved project output XML blob provided by the IDE.The task has no cwd-dependent operations:
Environment.CurrentDirectory,Path.GetFullPath, or directFile.*/Directory.*usageAssemblyName.GetAssemblyName(resolvedPath.ItemSpec)— this operates on pre-resolved absolute paths extracted from thePreresolvedProjectOutputsXML string. The paths come from the IDE/solution configuration, not from relative path resolutionResolveProjectBaseusesreference.GetMetadata("FullPath")— engine-resolved absolute pathsThis is an attribute-only migration: the task needs
[MSBuildMultiThreadableTask]but does NOT needIMultiThreadableTaskorTaskEnvironmentbecause its file access uses pre-resolved absolute paths and it performs no relative path resolution, environment variable access, or process launches.Approach
Apply the PR #13045 (attribute-only) pattern: add
[MSBuildMultiThreadableTask]to the class declaration. NoIMultiThreadableTaskimplementation is needed.ResolveNonMSBuildProjectOutputwith[MSBuildMultiThreadableTask].ChangeWave consideration
No behavioral change — the attribute simply declares the task safe for multithreaded scheduling. Observable behavior (return value,
[Output]properties, error/warning messages) is byte-identical. No ChangeWave needed.Test coverage assessment
Existing unit tests
Tests exist for project reference resolution, pre-resolved output matching, managed assembly detection, and unresolved reference handling.
Gaps to fill in this PR
ResolveNonMSBuildProjectOutputinstances with differentPreresolvedProjectOutputsXML andProjectReferencesexecute concurrently, asserting each produces correct, independentResolvedOutputPathsandUnresolvedProjectReferencesoutput. Verifies no shared mutable state interference.[Output] ResolvedOutputPathsand[Output] UnresolvedProjectReferencesitems have identical ItemSpec and metadata.Acceptance criteria
ResolveNonMSBuildProjectOutputdecorated with[MSBuildMultiThreadableTask].IMultiThreadableTaskimplementation (not needed — no cwd-dependent operations).net472andnet10.0.[Output] ResolvedOutputPathsand[Output] UnresolvedProjectReferencesitems have byte-identicalItemSpecand metadata.multithreaded-task-migrationSKILL sign-off checklist walked and passes.Implementation order
[MSBuildMultiThreadableTask]toResolveNonMSBuildProjectOutput.Tasks.UnitTests; verify clean.-v quietto ensure no new warnings.Risks / open questions
AssemblyName.GetAssemblyName()performs actual disk I/O for assembly probing. However, it operates on pre-resolved absolute paths from the IDE blob — no relative path resolution is involved. Under MT execution, concurrent calls toGetAssemblyNameon different files are safe (no shared state in the static method).GetAssemblyNameis injected via a delegate property for testability. The default delegate isAssemblyName.GetAssemblyName— a static, stateless method.ResolveProjectBaseis shared withAssignProjectConfiguration. The[MSBuildMultiThreadableTask]attribute hasInherited = false, so each concrete class must be independently decorated.References
.github/skills/multithreaded-task-migration/SKILL.md