Skip to content

Migrate manifest-handling tasks to TaskEnvironment API#13177

Merged
JanProvaznik merged 22 commits intodotnet:mainfrom
JanProvaznik:resource-manifests-migration
Apr 30, 2026
Merged

Migrate manifest-handling tasks to TaskEnvironment API#13177
JanProvaznik merged 22 commits intodotnet:mainfrom
JanProvaznik:resource-manifests-migration

Conversation

@JanProvaznik
Copy link
Copy Markdown
Member

@JanProvaznik JanProvaznik commented Feb 2, 2026

Migrate manifest-handling tasks to TaskEnvironment API

Summary

This PR migrates manifest-handling MSBuild tasks to support multithreaded execution (-mt mode) by implementing the IMultiThreadableTask interface and using TaskEnvironment for thread-safe file operations.

Fixes #13172

Changes

Tasks Migrated

Task Changes
GenerateManifestBase Base class - added TaskEnvironment, replaced Directory.GetCurrentDirectory() with TaskEnvironment.ProjectDirectory, absolutized InputManifest and OutputManifest paths
GenerateApplicationManifest Absolutized TrustInfoFile path in GetRequestedExecutionLevel()
GenerateDeploymentManifest Inherits from GenerateManifestBase - no additional changes needed
ResolveManifestFiles Added attribute + interface, replaced Path.GetFullPath() calls with TaskEnvironment.GetAbsolutePath()
AddToWin32Manifest Added attribute + interface, absolutized ApplicationManifest and output paths
UpdateManifest Added attribute + interface to both .NET Framework and .NET implementations
CreateManifestResourceName Base class - added TaskEnvironment, absolutized paths before FileExists and FileStream operations
CreateCSharpManifestResourceName Added [MSBuildMultiThreadableTask] attribute
CreateVisualBasicManifestResourceName Added [MSBuildMultiThreadableTask] attribute

Test Updates

Updated unit tests to initialize TaskEnvironment:

  • AddToWin32Manifest_Tests.cs
  • CreateCSharpManifestResourceName_Tests.cs (13 test methods)
  • CreateVisualBasicManifestResourceName_Tests.cs (6 test methods)

Testing

Unit Tests

All 148 manifest-related unit tests pass:

dotnet test src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj --filter "FullyQualifiedName~Manifest"

E2E Tests

Created 5 end-to-end test projects validating behavior in -m -mt mode:

Demo Description Result
1 CreateCSharpManifestResourceName - C# resources ✅ Matches VS MSBuild
2 CreateVisualBasicManifestResourceName - VB resources ✅ Matches VS MSBuild
3 AddToWin32Manifest - PreferNativeArm64 ✅ Matches VS MSBuild
4 Non-resx resources (JSON, XML, binary, text) ✅ Matches VS MSBuild
5 DependentUpon convention with subfolders ✅ Matches VS MSBuild

E2E test package attached: manifest-e2e-tests.zip
manifest-e2e-tests.zip

To run:

# Extract and run
Expand-Archive manifest-e2e-tests.zip -DestinationPath e2e-tests
cd e2e-tests
.\run-tests.ps1  # Full comparison with VS MSBuild
.\run-tests.ps1 -SkipVSComparison  # Quick test with dev MSBuild only

Implementation Notes

manifest-e2e-tests.zip

Pattern Used

[MSBuildMultiThreadableTask]
public class MyTask : Task, IMultiThreadableTask
{
    public TaskEnvironment TaskEnvironment { get; set; }
    
    public override bool Execute()
    {
        // Absolutize paths before file operations
        AbsolutePath absolutePath = TaskEnvironment.GetAbsolutePath(inputPath);
        if (File.Exists(absolutePath))
        {
            // Safe to use in multithreaded context
        }
    }
}

Key Replacements

  • Directory.GetCurrentDirectory()TaskEnvironment.ProjectDirectory
  • Path.GetFullPath(path)TaskEnvironment.GetAbsolutePath(path)
  • All file I/O operations now receive absolute paths

Checklist

  • Tasks annotated with [MSBuildMultiThreadableTask] attribute
  • Tasks implement IMultiThreadableTask where TaskEnvironment is used
  • All file paths absolutized before I/O operations
  • No use of Directory.GetCurrentDirectory() or Environment.CurrentDirectory
  • Unit tests updated with TaskEnvironment = TaskEnvironmentHelper.CreateForTest()
  • E2E tests verify behavior matches VS MSBuild in -m -mt mode

Copilot AI review requested due to automatic review settings February 2, 2026 13:22
@JanProvaznik JanProvaznik marked this pull request as draft February 2, 2026 13:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates manifest-handling MSBuild tasks to support multithreaded execution (-mt mode) by implementing the IMultiThreadableTask interface and using TaskEnvironment for thread-safe file operations.

Changes:

  • Added [MSBuildMultiThreadableTask] attribute and IMultiThreadableTask interface to 9 manifest-handling tasks
  • Replaced Directory.GetCurrentDirectory() with TaskEnvironment.ProjectDirectory and Path.GetFullPath() with TaskEnvironment.GetAbsolutePath()
  • Updated 20 unit test methods to initialize TaskEnvironment using TaskEnvironmentHelper.CreateForTest()

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
UpdateManifest.cs Added attribute/interface, absolutized paths for both .NET and .NET Framework implementations
ResolveManifestFiles.cs Added attribute/interface, replaced Path.GetFullPath() with TaskEnvironment.GetAbsolutePath()
GenerateManifestBase.cs Base class - added TaskEnvironment, replaced Directory.GetCurrentDirectory(), absolutized manifest paths
GenerateApplicationManifest.cs Absolutized TrustInfoFile and InputManifest paths using TaskEnvironment
CreateManifestResourceName.cs Base class - added attribute/interface, absolutized paths before file operations
CreateCSharpManifestResourceName.cs Added [MSBuildMultiThreadableTask] attribute
CreateVisualBasicManifestResourceName.cs Added [MSBuildMultiThreadableTask] attribute
AddToWin32Manifest.cs Added attribute/interface, absolutized ApplicationManifest and output paths
CreateCSharpManifestResourceName_Tests.cs Updated 13 test methods with TaskEnvironment initialization
CreateVisualBasicManifestResourceName_Tests.cs Updated 6 test methods with TaskEnvironment initialization
AddToWin32Manifest_Tests.cs Updated 1 test method with TaskEnvironment initialization

Comment thread src/Tasks/AddToWin32Manifest.cs Outdated
Comment thread src/Tasks/GenerateApplicationManifest.cs
Comment thread src/Tasks/AddToWin32Manifest.cs Outdated
@JanProvaznik JanProvaznik force-pushed the resource-manifests-migration branch from 23f07ef to c947264 Compare February 2, 2026 13:34
@JanProvaznik JanProvaznik self-assigned this Feb 2, 2026
Migrate the following tasks to support multithreaded execution (-mt mode):
- GenerateManifestBase (base class for manifest generation)
- GenerateApplicationManifest
- GenerateDeploymentManifest (inherits from GenerateManifestBase)
- ResolveManifestFiles
- AddToWin32Manifest
- UpdateManifest
- CreateManifestResourceName (base class for resource naming)
- CreateCSharpManifestResourceName
- CreateVisualBasicManifestResourceName

Changes:
- Add [MSBuildMultiThreadableTask] attribute to all tasks
- Implement IMultiThreadableTask interface where TaskEnvironment is used
- Replace Directory.GetCurrentDirectory() with TaskEnvironment.ProjectDirectory
- Absolutize all paths before file I/O operations using TaskEnvironment.GetAbsolutePath()
- Update unit tests to set TaskEnvironment = TaskEnvironmentHelper.CreateForTest()

Fixes dotnet#13172
@JanProvaznik JanProvaznik force-pushed the resource-manifests-migration branch from c947264 to e5763fd Compare February 2, 2026 16:19
- Fix ResolveManifestFiles to canonicalize paths for dictionary key matching
  (GetAbsolutePath doesn't canonicalize, so wrap with Path.GetFullPath)
- Add 8 focused tests for TaskEnvironment migration:
  - Empty ItemSpec handling
  - Path with .. segments (canonicalization)
  - Forward/mixed slashes normalization
  - Batch processing error handling
  - Deep nesting and spaces in paths
…nd output

- AddToWin32Manifest: Use original ApplicationManifest.ItemSpec in error messages
  instead of absolutized path (manifestDisplayPath)
- AddToWin32Manifest: ManifestPath output preserves original Path.Combine(OutputDirectory, name)
  form instead of always being absolute
- CreateManifestResourceName: Remove unnecessary ?? string.Empty from Path.GetDirectoryName
  to preserve pre-migration exception behavior for root-level resources
- GenerateManifestBase: Pass absolutized path to LockCheck.GetLockedFileMessage so it
  checks the correct file on write failure
Comment thread src/Tasks/UpdateManifest.cs Outdated
Comment thread src/Tasks/ResolveManifestFiles.cs Outdated
Comment thread src/Tasks/AddToWin32Manifest.cs Outdated
…d IMultiThreadableTask

- AddToWin32Manifest: refactor manifestPath to AbsolutePath?, use OriginalValue for display
- ResolveManifestFiles: use GetCanonicalForm() instead of Path.GetFullPath() wrapper
- UpdateManifest (.NET): remove IMultiThreadableTask — TaskEnvironment not used in stub
- Merge compatibility red-team playbook into multithreaded-task-migration skill
@JanProvaznik JanProvaznik marked this pull request as ready for review February 10, 2026 15:54
@JanProvaznik JanProvaznik requested a review from a team as a code owner February 10, 2026 15:54
JanProvaznik added a commit to JanProvaznik/dotnet-skills that referenced this pull request Feb 10, 2026
…kill

Merge adversarial compatibility review guidance into the existing migration skill,
based on real bugs found during manifest task migrations (PR dotnet/msbuild#13177):

- 6 Deadly Compatibility Sins with detect/fix patterns
- Red-team audit protocol with edge-case analysis tables
- Compatibility test generation matrix
- Unified sign-off checklist covering migration + compatibility
- Deduplicated: merged canonicalization sins, unified checklists, trimmed redundant examples
ViktorHofer pushed a commit to ViktorHofer/dotnet-skills that referenced this pull request Feb 12, 2026
…kill (#3)

Merge adversarial compatibility review guidance into the existing migration skill,
based on real bugs found during manifest task migrations (PR dotnet/msbuild#13177):

- 6 Deadly Compatibility Sins with detect/fix patterns
- Red-team audit protocol with edge-case analysis tables
- Compatibility test generation matrix
- Unified sign-off checklist covering migration + compatibility
- Deduplicated: merged canonicalization sins, unified checklists, trimmed redundant examples
@JanProvaznik JanProvaznik force-pushed the resource-manifests-migration branch from 41e492b to 86f7785 Compare February 17, 2026 13:46
JanProvaznik and others added 2 commits February 17, 2026 15:13
Add using alias for Constants in GenerateDeploymentManifest.cs to
disambiguate between Microsoft.Build.Tasks.Deployment.ManifestUtilities.Constants
and the newly-moved Microsoft.Build.Framework.Constants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Tasks/AddToWin32Manifest.cs
Comment thread src/Tasks/CreateManifestResourceName.cs Outdated
Comment thread src/Tasks/GenerateManifestBase.cs Outdated
Comment thread src/Tasks/ResolveManifestFiles.cs
Comment thread src/Tasks/GenerateManifestBase.cs
…nonicalization

Util.RemoveDuplicateItems used Path.GetFullPath (CWD-dependent) for dictionary
keys when deduplicating non-strong-name items. In multithreaded mode, CWD is
meaningless and shared across threads.

Fix: migrate SortItems to accept TaskEnvironment, use
GetAbsolutePath().GetCanonicalForm() instead of Path.GetFullPath().
Move sort calls from property setters to Execute() where TaskEnvironment
is available. All 9 callers are in the two migrated tasks — no overload needed.
Eliminates the last direct analyzer violation (MSBuildTask0001) by making the
delegate signature attest that paths are absolute at the type level.
@JanProvaznik
Copy link
Copy Markdown
Member Author

/azp run msbuild-vmr-validation

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

Comment thread src/Tasks/AddToWin32Manifest.cs Outdated
Comment thread src/Tasks.UnitTests/CreateCSharpManifestResourceName_Tests.cs Outdated
Comment thread src/Tasks/GenerateApplicationManifest.cs Outdated
Comment thread src/Tasks/GenerateManifestBase.cs
Comment thread src/Tasks/GenerateManifestBase.cs
Comment thread src/Tasks/ResolveManifestFiles.cs Outdated
Comment thread src/Tasks/ResolveManifestFiles.cs Outdated
Comment thread src/Tasks/ResolveManifestFiles.cs Outdated
Comment thread src/Tasks/ResolveManifestFiles.cs Outdated
@JanProvaznik JanProvaznik requested a review from OvesN April 28, 2026 11:28
@JanProvaznik
Copy link
Copy Markdown
Member Author

JanProvaznik commented Apr 28, 2026

running exp insertion: passed

@ViktorHofer
Copy link
Copy Markdown
Member

/review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

Expert Code Review (command) completed successfully!

Expert review of PR #13177 complete. The expert-reviewer sub-agent ran all 24 dimensions and posted 3 inline findings (2 MODERATE, 1 NIT) plus the final review verdict. No blocking issues found. 21/24 dimensions clean.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24-Dimension Review Summary — PR #13177 (Manifest Task MT Migration)

Systematic review across 24 dimensions. Only non-LGTM findings are listed below.

Findings

Dimension Verdict Severity File Summary
Test Coverage ISSUE MODERATE ManifestTaskEnvironmentTests.cs:20 Missing ITestOutputHelper injection — all sibling test classes inject it; CI diagnostic output is lost
Test Coverage ISSUE MODERATE ManifestTaskEnvironmentTests.cs:179 ManifestResourceNames.Length.ShouldBe(2) is trivially true (pre-allocated array) — assert actual content
Code Simplification ISSUE NIT Util.cs:428 Redundant (string) cast on AbsolutePath — implicit conversion handles this

Clean Dimensions (21/24)

✅ Backwards Compatibility — Util is internal static class, CreateFileStream delegate is internal, sort-in-Execute is semantically equivalent.
✅ Thread Safety — Directory.GetCurrentDirectory() fully eliminated from new code paths. SatelliteRefAssemblyMap._dictionary is per-instance, no static mutable state.
✅ Correctness — All GetAbsolutePath call sites have proper null/empty guards. _satelliteAssembliesPassedAsReferences initialized unconditionally at top of Execute(). Path resolution via fallback driver is equivalent to old behavior.
✅ API Surface — No public API breaks. New TaskEnvironment properties have defaults (TaskEnvironment.Fallback). Util.SortItems is internal.
✅ Performance — AbsolutePath is a readonly struct (no heap alloc). Extra string allocation in GetCanonicalForm is immeasurable for once-per-project tasks.
✅ ChangeWave Discipline — Multithreaded task execution is new opt-in infrastructure; no ChangeWave needed.
✅ Cross-Platform, Error Messages, Logging, String Comparison, Naming, Nullable, Documentation, Security, Binary Log, Build Infrastructure, Evaluation Model, SDK Integration, Dependency Management, Resource/Cleanup — all clean.

Overall Assessment

The migration is mechanically sound. The core transformation (CWD → TaskEnvironment.ProjectDirectory, Path.GetFullPathGetAbsolutePath().GetCanonicalForm()) is applied consistently. The Manifest.ResolveFiles() parameterless overload correctly gets a documentation warning without modification. The deferred sorting pattern (setter → Execute) is safe given MSBuild's property-set-then-execute lifecycle. No blocking issues found.

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

  • #13177 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
  • #13177 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Expert Code Review (command) for issue #13177 · ● 14.2M

Comment thread src/Tasks/ManifestUtil/Util.cs
Comment thread src/Tasks.UnitTests/ManifestTaskEnvironmentTests.cs Outdated
Comment thread src/Tasks.UnitTests/ManifestTaskEnvironmentTests.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate Manifest-handling tasks to TaskEnvironment API

5 participants