Move core ProjectReferenceTargets from Managed-only to Common targets#13427
Move core ProjectReferenceTargets from Managed-only to Common targets#13427dfederm wants to merge 1 commit intodotnet:mainfrom
Conversation
Move the core Build/Clean/Rebuild ProjectReferenceTargets protocol from Microsoft.Managed.After.targets into Microsoft.Common.CurrentVersion.targets so it applies to all project types that import the common targets, not just managed languages (C#, VB, F#). This aligns with the static graph spec which prescribes that common protocols be defined in the common targets. Extract the cross-targeting outer build protocol (using .default dispatch) into Microsoft.Common.CrossTargeting.targets where it naturally belongs alongside the DispatchToInnerBuilds target. Refactor DeployOnBuild handling from property-level composition (appending Publish targets to the ProjectReferenceTargetsForBuild property) to item-level composition (separate ProjectReferenceTargets items). This is functionally equivalent since the static graph engine merges all items with the same Include value, and eliminates a subtle duplication in the old code. Microsoft.Managed.After.targets retains managed-specific extensions: Publish, DeployOnBuild, GetCopyToPublishDirectoryItems, WPF graph isolation, and the InnerBuildProperty/InnerBuildPropertyValues definitions. Also fixes: - Update docs.microsoft.com URL to learn.microsoft.com - Convert absolute GitHub links to relative paths in protocol docs - Add prose hints for deep-link references into large targets files - Fix typo: GetTargetFrameworksWithPlatformForSingleTargetFrameworks (plural) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Moves the static-graph ProjectReferenceTargets protocol for core Build/Clean/Rebuild from managed-only targets into the common MSBuild targets so it applies to all project types that import the common targets, aligning implementation with the static graph spec.
Changes:
- Added core static-graph
ProjectReferenceTargets(Build/Clean/Rebuild) toMicrosoft.Common.CurrentVersion.targets. - Added cross-targeting (outer build) static-graph
ProjectReferenceTargetsdispatch protocol toMicrosoft.Common.CrossTargeting.targets. - Refactored managed
DeployOnBuildpropagation from property composition to item-based composition and added new unit tests + doc updates.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Tasks/Microsoft.Managed.After.targets | Removes core Build/Clean/Rebuild protocol and keeps managed-only Publish/DeployOnBuild extensions. |
| src/Tasks/Microsoft.Common.CurrentVersion.targets | Defines core Build/Clean/Rebuild ProjectReferenceTargets protocol for static graph builds. |
| src/Tasks/Microsoft.Common.CrossTargeting.targets | Defines cross-targeting (outer build) static graph protocol for Build/Clean/Rebuild. |
| src/Build.UnitTests/Graph/ProjectReferenceTargetsProtocol_Tests.cs | Adds unit tests validating protocol behavior across managed/non-managed and cross-targeting scenarios. |
| documentation/ProjectReference-Protocol.md | Updates links and clarifies references to protocol/comment locations in targets files. |
| This file defines common build logic for all managed languaged: C#, VisualBasic, F# | ||
| It is imported after the common targets have been imported. | ||
|
|
||
| The core ProjectReferenceTargets for Build, Clean, and Rebuild are defined in | ||
| Microsoft.Common.CurrentVersion.targets so they apply to all project types. | ||
| This file adds managed-specific extensions (Publish, DeployOnBuild, WPF, etc.). |
There was a problem hiding this comment.
In the file header comment, "managed languaged" is a typo. Please change it to "managed languages" while touching this header so the comment is accurate and professional.
| private TestEnvironment _env; | ||
|
|
||
| public ProjectReferenceTargetsProtocolTests(ITestOutputHelper output) | ||
| { | ||
| _env = TestEnvironment.Create(output); |
There was a problem hiding this comment.
This test class constructor passes the ITestOutputHelper directly into TestEnvironment.Create(...) but doesn't store it (and the created TestEnvironment) in readonly fields like other graph tests do. Storing the output helper as a field and using it consistently (e.g., for MockLogger diagnostics) makes failures much easier to diagnose and matches the prevailing pattern in this test suite.
| private TestEnvironment _env; | |
| public ProjectReferenceTargetsProtocolTests(ITestOutputHelper output) | |
| { | |
| _env = TestEnvironment.Create(output); | |
| private readonly TestEnvironment _env; | |
| private readonly ITestOutputHelper _output; | |
| public ProjectReferenceTargetsProtocolTests(ITestOutputHelper output) | |
| { | |
| _output = output; | |
| _env = TestEnvironment.Create(_output); |
| // The outer build uses CrossTargeting protocol | ||
| string outerBuildContent = MultitargetingSpec + CrossTargetingProtocol + CommonCurrentVersionTargetsProtocol + AllDummyTargets; |
There was a problem hiding this comment.
In the cross-targeting test, outerBuildContent includes CommonCurrentVersionTargetsProtocol. In actual cross-targeting builds, managed projects import Microsoft.Common.CrossTargeting.targets (e.g., via Microsoft.CSharp.CrossTargeting.targets) and do not also import Microsoft.Common.CurrentVersion.targets in the outer build. Including the inner-build protocol in the synthetic outer build can change which ProjectReferenceTargets items exist and may make the test less representative (or accidentally mask protocol issues). Consider modeling the outer build with just the cross-targeting protocol + multitargeting spec, and reserving CommonCurrentVersionTargetsProtocol for inner builds / non-cross-targeting projects.
| // The outer build uses CrossTargeting protocol | |
| string outerBuildContent = MultitargetingSpec + CrossTargetingProtocol + CommonCurrentVersionTargetsProtocol + AllDummyTargets; | |
| // The outer build uses CrossTargeting protocol (no current-version protocol in real outer builds) | |
| string outerBuildContent = MultitargetingSpec + CrossTargetingProtocol + AllDummyTargets; |
Summary
Move the core Build/Clean/Rebuild
ProjectReferenceTargetsprotocol fromMicrosoft.Managed.After.targetsintoMicrosoft.Common.CurrentVersion.targetsso it applies to all project types that import the common targets, not just managed languages (C#, VB, F#). This aligns with the static graph spec which prescribes that common protocols be defined in the common targets.What changed
Targets files
Microsoft.Common.CurrentVersion.targets: Now contains the coreProjectReferenceTargetsfor Build, Clean, and Rebuild, includingOuterBuilditems,SkipNonexistentTargetsitems, and the extensibility properties (ProjectReferenceTargetsForBuild, etc.)Microsoft.Common.CrossTargeting.targets: Now contains the cross-targeting outer build protocol (.defaultdispatch) that was previously inlined inManaged.After.targetsMicrosoft.Managed.After.targets: Retains only managed-specific extensions: Publish, DeployOnBuild,GetCopyToPublishDirectoryItems, WPF graph isolation, andInnerBuildProperty/InnerBuildPropertyValuesDeployOnBuild refactor
DeployOnBuild handling changed from property-level composition (appending Publish targets to
$(ProjectReferenceTargetsForBuild)) to item-level composition (separateProjectReferenceTargetsitems). This is functionally equivalent since the static graph engine merges all items with the sameIncludevalue, and eliminates a subtle duplication in the old code whereProjectReferenceTargetsForRebuildincluded publish targets twice.Documentation
docs.microsoft.com→learn.microsoft.comURLTesting
Unit tests (11 new, all passing)
New file
src/Build.UnitTests/Graph/ProjectReferenceTargetsProtocol_Tests.cscovering:.defaultdispatchBuildProjectReferences=falseandNoBuild=truefallbacksManual validation
Tested with old (VS 18 Canary) vs new (bootstrap) MSBuild:
.projwith/graph /isolate— old MSBuild producesMSB4252isolation violation ("target not specified in ProjectReferenceTargets"), new MSBuild resolves the graph correctly-getItem:ProjectReferenceTargets /p:IsGraphBuild=trueon non-managed.proj: old returns empty, new returns full Build/Clean/Rebuild protocol fromMicrosoft.Common.CurrentVersion.targets