Skip to content

Migrate manifest-handling tasks to TaskEnvironment API#13177

Open
JanProvaznik wants to merge 12 commits intodotnet:mainfrom
JanProvaznik:resource-manifests-migration
Open

Migrate manifest-handling tasks to TaskEnvironment API#13177
JanProvaznik wants to merge 12 commits intodotnet:mainfrom
JanProvaznik:resource-manifests-migration

Conversation

@JanProvaznik
Copy link
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
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

@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
…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>
return absolutePath;
}

string? defaultManifestPath = ToolLocationHelper.GetPathToDotNetFrameworkFile(DefaultManifestName, TargetDotNetFrameworkVersion.Version46);
Copy link
Member

Choose a reason for hiding this comment

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

This might be not safe call.

/// </comment>
public sealed class ResolveManifestFiles : TaskExtension
[MSBuildMultiThreadableTask]
public sealed class ResolveManifestFiles : TaskExtension, IMultiThreadableTask
Copy link
Member

Choose a reason for hiding this comment

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

This class also dependent on ToolLocationHelper through AssemblyIdentity.IsInFramework - is this pose a risk?

/// </summary>
public abstract class GenerateManifestBase : Task
[MSBuildMultiThreadableTask]
public abstract class GenerateManifestBase : Task, IMultiThreadableTask
Copy link
Member

Choose a reason for hiding this comment

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

this class seems to call ToolLocationHelper functions when validating the manifests in ApplicationManifest.ValidateReferencesForClickOnceApplication()

…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.
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

2 participants

Comments