Skip to content

Phase out ChangeWaves 17.10, 17.12, 17.14 and 18.3 - #14551

Merged
JanProvaznik merged 5 commits into
dotnet:mainfrom
JanProvaznik:dev/janprovaznik/retire-changewaves-17x-18.3
Jul 30, 2026
Merged

JanProvaznik merged 5 commits into
dotnet:mainfrom
JanProvaznik:dev/janprovaznik/retire-changewaves-17x-18.3

Conversation

@JanProvaznik

@JanProvaznik JanProvaznik commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

Fixes #13264
Fixes #13265
Fixes #13266

Context

Change waves 17.10, 17.12 and 17.14 are long out of rotation, and 18.3 rotates out with them now that the repo is on 18.10. Per ChangeWaves-Dev.md, retiring a wave means deleting the Version field, removing the AreFeaturesEnabled conditions so the feature becomes standard behavior, dropping the tests that pinned the opted-out behavior, and cleaning up the dead code left behind.

After this the rotation is 18.4 – 18.10, and the "will be removed in the release accompanying .NET 11" bucket in ChangeWaves.md is empty, so it is dropped.

Changes Made

One commit per wave, plus one prerequisite bootstrap fix:

Overlay freshly-built MSBuild files into the bootstrap SDK's Current folder

BootstrapNetCore overlays our build output onto the acquired SDK at sdk\<ver>\, but the .NET SDK layout keeps some of MSBuild's own props/targets under sdk\<ver>\Current\, because they are imported through $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\... and $(MSBuildToolsVersion) is Current there. Microsoft.Common.props is the notable case: in an SDK layout it exists only under Current\, and Microsoft.NET.Sdk's Sdk.props imports it from that path for every SDK-style project.

Our build output has no Current\ subdirectory, so the overlay never replaced it. That means every bootstrap build has silently been evaluating the Microsoft.Common.props belonging to whatever older MSBuild the acquired SDK shipped with, and changes to src\Tasks\Microsoft.Common.props never took effect in bootstrap-based tests.

Retiring 17.10 made that visible: the stale props still evaluated $([MSBuild]::AreFeaturesEnabled('17.10')), which trips the Debug assert in ChangeWaves and took down 54 BuildCheck end-to-end tests. Fixing the overlay addresses the root cause and leaves the assert doing exactly the job it was added for.

Phase out ChangeWave 17.10 — makes unconditional: NuGet.Frameworks/AppDomain assembly loading in MSBuild.exe and VS, process-wide SDK resolver caching (CachingSdkResolverLoader) plus the .NET-only default-resolver fast path, target switch unquoting, Link metadata on Resource items, -version trailing newline, Traits refresh on environment change, Exec not trimming leading whitespace and not exporting LANG/LC_ALL on Unix, ToolTask console-code-page encoding, skipping project extension imports during restore, and property reassignment tracking. Also deletes StableStringHashLegacy and the NoInlining guard on StableStringHash that only existed to keep StringTools out of the opted-out path.

Phase out ChangeWave 17.12 — makes unconditional: TaskParameterEventArgs for scalar task parameters and output properties (the legacy textual form and OutputPropertyLogMessage path are gone), InvariantCulture for Convert.ToString during evaluation, BuildRequestDataFlags compatibility in ResultsCache, emitting evaluation props/items when any sink asks, ProjectStartedEventArgs returning empty collections instead of null, binlog {} wildcard file names, and loading Microsoft.DotNet.MSBuildSdkResolver into the default load context.

Phase out ChangeWave 17.14 — the only live gate left was SolutionFile.FullPath, which now always throws ArgumentNullException for a null/empty path. The .slnx-parser and RAR custom-culture features originally under this wave were already reverted/replaced (see #11607), so only stale Wave17_14 comments remained in Create{CSharp,VisualBasic}ManifestResourceName.

Phase out ChangeWave 18.3 — WriteLinesToFile always writes atomically (temp file + replace with retry); the non-transactional path and the quarantined test asserting the old data-losing behavior are deleted.

Testing

build.cmd -test on Windows (net11.0 x64 + net472 x86). Everything passes except PortableTasks_Tests.TestDesktopMSBuildShouldRunPortableTask, which shells out to msbuild and fails for lack of a Visual Studio install on this machine, not because of these changes.

Notes

Behavior only changes for builds that were opting out via MSBUILDDISABLEFEATURESFROMVERSION; the default (all waves enabled) code paths make exactly the same decisions as before. Setting MSBUILDDISABLEFEATURESFROMVERSION to a retired wave now clamps to 18.4 and warns MSB4272, which is the documented out-of-rotation behavior.

@JanProvaznik
JanProvaznik marked this pull request as ready for review July 28, 2026 12:36
Copilot AI review requested due to automatic review settings July 28, 2026 12:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 retires ChangeWaves 17.10, 17.12, 17.14, and 18.3 by removing the version constants and wave gating, making the corresponding behaviors unconditional. It also adjusts [MSBuild]::AreFeaturesEnabled(...) behavior so project-supplied wave versions that have rotated out no longer trip Debug-only assertions.

Changes:

  • Retire waves 17.10/17.12/17.14/18.3 by removing checks and deleting legacy code paths and tests that pinned opted-out behavior.
  • Split ChangeWaves evaluation so internal callers keep validation, while project-supplied versions use a non-validating path (via IntrinsicFunctions).
  • Update engine/tasks/targets/docs to reflect the now-unconditional behaviors (SDK resolver caching, structured task parameter logging, Exec trimming/locale behavior, atomic WriteLinesToFile, etc.).

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Utilities/ToolTask.cs Makes ToolTask output/error encoding consistency behavior unconditional (formerly Wave17_10-gated).
src/Tasks/Microsoft.Common.targets Removes Wave17_10 gating from restore-time project extension import defaults.
src/Tasks/Microsoft.Common.props Removes Wave17_10 gating from restore-time project extension import defaults.
src/Tasks/Microsoft.Common.CurrentVersion.targets Makes AssignLinkMetadata + Link propagation for @(Resource) unconditional (formerly Wave17_10-gated).
src/Tasks/Microsoft.Common.CrossTargeting.targets Removes Wave17_10 gating from restore-time extension target import defaults for cross-targeting.
src/Tasks/FileIO/WriteLinesToFile.cs Removes non-transactional implementation; always uses transactional/atomic write path (formerly Wave18_3-gated).
src/Tasks/Exec.cs Makes ConsoleToMSBuild trimming behavior TrimEnd-only and removes Unix locale export behavior (formerly Wave17_10-gated).
src/Tasks/CreateVisualBasicManifestResourceName.cs Cleans up stale Wave17_14-related comment (no functional change).
src/Tasks/CreateCSharpManifestResourceName.cs Cleans up stale Wave17_14-related comment (no functional change).
src/Tasks.UnitTests/WriteLinesToFile_Tests.cs Removes test that validated legacy non-transactional data-loss behavior.
src/Tasks.UnitTests/Exec_Tests.cs Updates tests to reflect that Exec no longer exports LANG/LC_ALL on Unix.
src/MSBuild/XMake.cs Makes -version output always newline-terminated (formerly Wave17_10-gated).
src/MSBuild/CommandLine/CommandLineSwitches.cs Updates unquote-target-switch behavior docs/logic to remove wave/escape-hatch coupling.
src/MSBuild.UnitTests/XMake_Tests.cs Removes test coverage for legacy -version output without trailing newline.
src/MSBuild.UnitTests/CommandLineSwitches_Tests.cs Removes test that pinned legacy quoted target parsing under Wave17_10 opt-out.
src/Framework/Traits.cs Makes Traits environment refresh unconditional; removes Wave17_10-gated escape hatch surface.
src/Framework/ProjectStartedEventArgs.cs Makes GlobalProperties/Properties/Items return empty collections instead of null (formerly Wave17_12-gated).
src/Framework/ChangeWaves.cs Removes retired wave constants, updates AllWaves, and adds AreFeaturesEnabledForProjectSuppliedVersion.
src/Build/Utilities/NuGetFrameworkWrapper.Reflection.cs Makes MSBuild.exe/VS NuGet.Frameworks load path unconditional (formerly Wave17_10-gated).
src/Build/Logging/BinaryLogger/BinaryLogger.cs Makes {} wildcard binlog filename handling unconditional (formerly Wave17_12-gated).
src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs Makes property reassignment tracking default behavior unconditional (formerly Wave17_10-gated).
src/Build/Evaluation/IntrinsicFunctions.cs Removes legacy StableStringHash implementation and routes property function to non-validating change-wave check.
src/Build/Evaluation/Expander/WellKnownFunctions.cs Makes StableStringHash execution unconditional (removes Wave17_10 gate and legacy path).
src/Build/Evaluation/Expander.PropertyExpander.cs Makes Convert.ToString invariant-culture path unconditional (formerly Wave17_12-gated).
src/Build/Construction/Solution/SolutionFile.cs Makes null/empty FullPath throw ArgumentNullException unconditionally (formerly Wave17_14-gated).
src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs Removes legacy textual task parameter/output logging; always uses structured TaskParameterEventArgs (Wave17_12 retirement).
src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs Makes caching SDK resolver loader and .NET default-resolver fast path unconditional (Wave17_10 retirement).
src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs Makes MSBuildSdkResolver default-load-context preference unconditional in the non-ALC build (Wave17_12 retirement).
src/Build/BackEnd/Components/Logging/LoggingService.cs Makes “any sink requests eval data” behavior unconditional (Wave17_12 retirement).
src/Build/BackEnd/Components/Caching/ResultsCache.cs Makes BuildRequestDataFlags compatibility checks unconditional (Wave17_12 retirement).
src/Build.UnitTests/Evaluation/Expander_Tests.cs Removes tests and helper code that validated pre-Wave17_12 Convert.ToString culture behavior.
src/Build.UnitTests/ChangeWaves_Tests.cs Adds coverage ensuring rotated-out waves remain “enabled” for project-supplied versions.
src/Build.UnitTests/BinaryLogger_Tests.cs Removes wave setup now that {} wildcard support is unconditional.
documentation/wiki/Results-Cache.md Removes Wave17_12 opt-out note now that the behavior is unconditional.
documentation/wiki/ChangeWaves.md Moves retired waves into “no longer in rotation” and removes the “removed in .NET 11” bucket.
documentation/Property-tracking-capabilities.md Updates docs to reflect property reassignment tracking default behavior post-Wave17_10 retirement.
documentation/aot/sdk-resolution.md Removes documentation of the legacy non-caching resolver path tied to Wave17_10 opt-out.

@ViktorHofer

Copy link
Copy Markdown
Member

/review

@github-actions

github-actions Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

✅ Expert Code Review (command) completed successfully!

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ChangeWave Retirement Review (17.10, 17.12, 17.14, 18.3)

This PR correctly follows the ChangeWave retirement procedure. Here is my review:

✅ Correctness

All callsites are cleaned up. A grep for Wave17_10, Wave17_12, Wave17_14, Wave18_3 across the entire codebase returns no results — the retirement is complete.

Behavior preservation is correct. In all cases, the branch that was the "new" (enabled) behavior behind the ChangeWave is the one that survived unconditionally.

✅ AreFeaturesEnabledForProjectSuppliedVersion Design

The new method is well-designed and well-documented. The split between:

  • AreFeaturesEnabled(Version wave) — internal use, asserts wave is still in rotation (DEBUG only)
  • AreFeaturesEnabledForProjectSuppliedVersion(Version wave) — for project files calling [MSBuild]::AreFeaturesEnabled, no assertion

...is the right approach. The routing of IntrinsicFunctions.AreFeaturesEnabled to the new method ensures that projects using $([MSBuild]::AreFeaturesEnabled('17.10')) continue to return true without triggering a debug assertion.

The new test RotatedOutWavesAreAlwaysEnabledForProjectSuppliedVersions provides adequate coverage for this behavior.

⚠️ Minor: Missing Newline at End of ChangeWaves.md

The diff shows \ No newline at end of file for documentation/wiki/ChangeWaves.md. The file is missing a trailing newline after the 18.3 section. This is a cosmetic issue but violates standard conventions and .editorconfig rules.

✅ Test Coverage

Removals are all appropriate:

  • ParameterizedSwitchTargetQuotedChangeWaveTest — tested the disabled-wave (old behavior) path; correctly removed
  • VersionSwitchDisableChangeWave — tested the Wave17_10-disabled path; correctly removed
  • ExecSetsLocaleOnUnix(enableChangeWave: true) — the locale-setting path has been removed from Exec.cs; the remaining test (ExecDoesNotSetLocaleOnUnix) correctly validates the new permanent behavior
  • NonTransactionalModeCausesDataLoss — was marked [ActiveIssue], safe to remove with the wave retirement
  • ExpandItem_ConvertToStringUsingInvariantCultureForNumberData_RespectingChangeWave — tested the non-InvariantCulture path; correctly removed

✅ Documentation

ChangeWaves.md correctly moves the retired waves from the "will be removed" section to "No Longer In Rotation". Results-Cache.md, Property-tracking-capabilities.md, and aot/sdk-resolution.md are all updated to remove wave-conditional language.

✅ Binary Log Compatibility

The change in LoggingService.cs (removing the Wave17_12 branch for IncludeEvaluationPropertiesAndItems) correctly keeps the "any sink requested" behavior. No change to event serialization formats.

✅ Public API

No public API changes. All modified code is internal. ProjectStartedEventArgs.GlobalProperties, Properties, and Items now return empty collections instead of null — this is a behavior change, but since Wave17_12 has been in production for multiple releases, all consumers should have already adapted.

Summary

This is a clean, thorough ChangeWave retirement. The only issue worth noting is the missing trailing newline at the end of ChangeWaves.md.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Expert Code Review (command) for #14551 · sonnet46 · 92.6 AIC · ⌖ 5.24 AIC · ⊞ 4.7K
Comment /review to run again

Comment thread src/Framework/ChangeWaves.cs Outdated
JanProvaznik and others added 5 commits July 29, 2026 11:02
…folder

BootstrapNetCore copies our build output over the acquired SDK at
sdk\<ver>\, but the .NET SDK layout keeps some of MSBuild's own props/targets
under sdk\<ver>\Current\ instead, because they are imported through
$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\... and $(MSBuildToolsVersion)
is "Current" there.

Microsoft.Common.props is the notable case: in an SDK layout it exists *only*
under Current\, and Microsoft.NET.Sdk's Sdk.props imports it from that path for
every SDK-style project. Our output has no Current\ subdirectory, so the overlay
never replaced it and every bootstrap build silently evaluated the copy shipped
by whatever older MSBuild the acquired SDK came with - meaning changes to
src\Tasks\Microsoft.Common.props never took effect in bootstrap-based tests.

Retiring change wave 17.10 made this visible: the stale props still evaluated
$([MSBuild]::AreFeaturesEnabled('17.10')), which trips the Debug assert in
ChangeWaves and took down 54 BuildCheck end-to-end tests.

Overlay our freshly-built copy of any file the acquired SDK keeps in Current\.
Directories under Current\ (the Microsoft.Common.targets\ImportAfter\ extension
points) are not matched by the glob and are left alone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2ae1d52-af0e-4c11-9189-1994e36e142b
Wave 17.10 is out of rotation as of MSBuild 18.10, so remove the opt-out and
make its features unconditional:

- AppDomain/NuGet.Frameworks assembly loading in MSBuild.exe and VS
- Process-wide SDK resolver caching (CachingSdkResolverLoader) and the
  .NET-only default-resolver fast path
- Target switch parameter unquoting
- Link metadata on Resource items in AssignLinkMetadata
- -version output ending with a newline
- Traits refresh when the environment changes
- Exec no longer trimming leading whitespace from ConsoleOutput, and no longer
  exporting LANG/LC_ALL on Unix
- ToolTask keeping stdout/stderr encoding consistent with the console code page
- Skipping project extension imports during restore
- Property reassignment tracking on by default

Also drops the now-dead StableStringHashLegacy helper and the NoInlining guard
on StableStringHash that only existed to keep StringTools out of the
opted-out path.

Fixes dotnet#13264

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2ae1d52-af0e-4c11-9189-1994e36e142b
Wave 17.12 is out of rotation as of MSBuild 18.10, so remove the opt-out and
make its features unconditional:

- TaskParameterEventArgs logged for scalar task parameters and task output
  properties (dropping the legacy textual "Task Parameter:"/OutputPropertyLogMessage
  form), including ParameterName/PropertyName
- Convert.ToString during property evaluation uses InvariantCulture for all types
- ResultsCache honors BuildRequestDataFlags compatibility, fixing oversharing
- Evaluation properties/items emitted when any sink requests them
- ProjectStartedEventArgs GlobalProperties/Properties/Items return empty
  collections instead of null
- binlog {} wildcard log file names
- Microsoft.DotNet.MSBuildSdkResolver loaded into the default load context
  (MSBuild.exe only)

Fixes dotnet#13265

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2ae1d52-af0e-4c11-9189-1994e36e142b
Wave 17.14 is out of rotation as of MSBuild 18.10, so remove the opt-out.
The only remaining live gate was SolutionFile.FullPath, which now always
throws ArgumentNullException for a null or empty path.

The .slnx-parser and RAR custom-culture features that originally shipped
under this wave were reverted/replaced earlier (see dotnet#11607), so only stale
Wave17_14 comments remained in Create{CSharp,VisualBasic}ManifestResourceName;
those are cleaned up as well.

Fixes dotnet#13266

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2ae1d52-af0e-4c11-9189-1994e36e142b
Wave 18.3 is out of rotation as of MSBuild 18.10, so remove the opt-out.
WriteLinesToFile now always writes atomically (temp file + replace with retry),
and the non-transactional code path plus the quarantined test that asserted the
old data-losing behavior are deleted.

This retires the last wave in the "will be removed in the release accompanying
.NET 11" group, so that section is dropped from ChangeWaves.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2ae1d52-af0e-4c11-9189-1994e36e142b
@JanProvaznik
JanProvaznik force-pushed the dev/janprovaznik/retire-changewaves-17x-18.3 branch from 595df0d to 5734e7a Compare July 29, 2026 09:24
@JanProvaznik
JanProvaznik requested a review from a team as a code owner July 29, 2026 09:24
@JanProvaznik JanProvaznik added the merge-carefully Merge one at a time, verifying this doesn't break anything down the chain. label Jul 29, 2026
@JanProvaznik

Copy link
Copy Markdown
Member Author

this should be merged when 18.11 is main

This was referenced Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-carefully Merge one at a time, verifying this doesn't break anything down the chain.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase out ChangeWave 17.14 Phase out ChangeWave 17.12 Phase out ChangeWave 17.10

4 participants