Skip to content

Restrict partial evaluation to ProjectInstance - #14340

Merged
ViktorHofer merged 2 commits into
mainfrom
vihofer/remove-partial-eval-from-project
Jul 13, 2026
Merged

ViktorHofer merged 2 commits into
mainfrom
vihofer/remove-partial-eval-from-project

Conversation

@ViktorHofer

Copy link
Copy Markdown
Member

Summary

Partial (stop-after-pass) evaluation was exposed on both Project and ProjectInstance. This PR restricts it to ProjectInstance only.

Project is mutable and cached by ProjectCollection, which makes a partially-evaluated Project a footgun:

  • A cached partial Project can be handed back to a caller expecting a full evaluation, serving stale partial state, or
  • It gets silently upgraded to a full evaluation (re-evaluated in place) when something touches later-pass state — discarding the performance win the caller asked for, as hidden work.

ProjectInstance is an uncached, read-only evaluation snapshot with none of these traps, so partial evaluation now lives there exclusively.

Changes

  • Project: removed all partial-eval honoring — the public EvaluationStage property, the ProjectImpl stage field + member guards, the partial→Full upgrade blocks in ReevaluateIfNecessary/CreateProjectInstance, and the evaluationStage constructor parameters. Project.FromFile/FromProjectRootElement/FromXmlReader now throw ArgumentException when a non-Full stage is requested via ProjectOptions (new OM_PartialEvaluationNotSupportedForProject resource + xlf entries).
  • ProjectCollection.LoadProject: dropped the now-dead partial cache upgrade-in-place logic.
  • XMake (-getProperty/-getItem, no-target path): switched from Project.FromFile to ProjectInstance.FromFile. ProjectInstance does not log pre-evaluation project-load failures (malformed XML) the way Project did, so the catch block now surfaces MSB4025 in canonical format (guarded by HasBeenLogged to avoid double-logging genuine evaluation errors).
  • JsonOutputFormatter: removed orphaned Project-based item formatter overloads.
  • Benchmark: PartialEvaluationBenchmark now uses ProjectInstance.
  • Docs/tests: updated the partial-evaluation spec and reworked the unit tests.

ProjectInstance retains full partial-evaluation support (EvaluationStage, member guards, OM_PartialEvaluationMemberUnavailable/OM_PartialEvaluationCannotBuild).

Testing

  • PartialEvaluation_Tests (Engine.UnitTests): 13/13 pass.
  • -getProperty/-getItem CLI tests + full XMakeAppTests (CommandLine.UnitTests): pass (incl. GetPropertyWithInvalidProjectThrowsInvalidProjectFileExceptionNotInternalError, which verifies MSB4025 is still emitted).
  • Full repo build clean.

The opt-in partial (stop-after-pass) evaluation feature was exposed on both
Project and ProjectInstance. Project is mutable and cached by ProjectCollection,
so a partial Project is a footgun: it either serves stale partial state from the
cache or gets silently upgraded to a full evaluation (discarding the perf win)
when handed to something that requires later passes. ProjectInstance is an
uncached, read-only snapshot and is trap-proof, so partial evaluation now lives
there exclusively.

Changes:
- Remove all partial-eval honoring from Project: the public EvaluationStage
  property, the ProjectImpl stage field/guards, the partial->Full upgrade blocks
  in ReevaluateIfNecessary/CreateProjectInstance, and the evaluationStage ctor
  parameters. Project factories now throw ArgumentException when a non-Full stage
  is requested via ProjectOptions (new OM_PartialEvaluationNotSupportedForProject
  resource + xlf entries).
- Drop the now-dead cache upgrade-in-place logic in ProjectCollection.LoadProject.
- Switch the -getProperty/-getItem (no-target) CLI path in XMake to
  ProjectInstance.FromFile, and log MSB4025 for pre-evaluation project load
  failures (Project logged these before throwing; ProjectInstance does not).
- Clean up orphaned Project-based JSON item formatter overloads.
- Point the partial-eval benchmark at ProjectInstance.
- Update the partial-evaluation spec and unit tests accordingly.

ProjectInstance retains full partial-evaluation support (EvaluationStage,
member guards, OM_PartialEvaluationMemberUnavailable/CannotBuild).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 008efa7a-b4fc-4f33-9dc7-d37d3f0e93c3
Copilot AI review requested due to automatic review settings July 13, 2026 14:05
@ViktorHofer
ViktorHofer temporarily deployed to copilot-pat-pool July 13, 2026 14:05 — with GitHub Actions Inactive
@ViktorHofer
ViktorHofer temporarily deployed to copilot-pat-pool July 13, 2026 14:05 — with GitHub Actions Inactive
@ViktorHofer
ViktorHofer temporarily deployed to copilot-pat-pool July 13, 2026 14:07 — with GitHub Actions Inactive

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

Restricts partial (stop-after-pass) evaluation support to ProjectInstance only, removing/denying partial evaluation paths on mutable, ProjectCollection-cached Project to avoid stale cached partial state and hidden upgrade-to-full work.

Changes:

  • MSBuild CLI -getProperty/-getItem no-target path now evaluates via ProjectInstance.FromFile and updates JSON item formatting accordingly.
  • Project factories reject non-Full ProjectOptions.EvaluationStage and ProjectCollection.LoadProject drops now-dead partial-upgrade cache logic.
  • Updates tests, benchmarks, resources/localization, and the partial-evaluation spec to reflect the ProjectInstance-only model.
Show a summary per file
File Description
src/MSBuild/XMake.cs Switches no-target -getProperty/-getItem evaluation to ProjectInstance and adjusts error surfacing and JSON item formatting calls.
src/MSBuild/JsonOutputFormatter.cs Removes Project-based item JSON formatting overloads; keeps ProjectInstance path.
src/MSBuild.Benchmarks/PartialEvaluationBenchmark.cs Updates benchmark to mirror CLI by loading via ProjectInstance.
src/Build/Resources/xlf/Strings.zh-Hant.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.zh-Hans.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.tr.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.ru.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.pt-BR.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.pl.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.ko.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.ja.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.it.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.fr.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.es.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.de.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/xlf/Strings.cs.xlf Adds localized placeholder entry for OM_PartialEvaluationNotSupportedForProject.
src/Build/Resources/Strings.resx Adds OM_PartialEvaluationNotSupportedForProject resource string.
src/Build/Definition/ProjectOptions.cs Documents that partial stages are honored only for ProjectInstance, and Project factories throw.
src/Build/Definition/ProjectCollection.cs Removes partial-eval upgrade-in-place logic from the loaded-project cache.
src/Build/Definition/Project.cs Removes Project partial-eval plumbing/guards and adds up-front rejection for partial stages in Project factory methods.
src/Build.UnitTests/Evaluation/PartialEvaluation_Tests.cs Reworks tests to validate Project factories reject partial stages and ProjectInstance retains behavior.
documentation/specs/proposed/partial-evaluation.md Updates spec to explicitly scope partial evaluation to ProjectInstance and explains why Project does not support it.

Copilot's findings

  • Files reviewed: 22/22 changed files
  • Comments generated: 2

Comment thread src/Build/Definition/Project.cs
Comment thread src/MSBuild/XMake.cs Outdated
- ThrowIfPartialEvaluationRequested now validates options for null up front, so
  the public Project.From* factories throw ArgumentNullException (not
  NullReferenceException) when passed null ProjectOptions. Added a covering test.
- The -getProperty/-getItem invalid-project error path now prints ex.Message
  instead of ex.BaseMessage so the project file path is retained in the MSB4025
  output, matching the prior Project-based behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 008efa7a-b4fc-4f33-9dc7-d37d3f0e93c3
@ViktorHofer
ViktorHofer requested review from OvesN and baronfel July 13, 2026 15:03
@ViktorHofer
ViktorHofer merged commit 4bdea96 into main Jul 13, 2026
14 checks passed
@ViktorHofer
ViktorHofer deleted the vihofer/remove-partial-eval-from-project branch July 13, 2026 19:55
WarperSan pushed a commit to WarperSan/ThunderPipe that referenced this pull request Sep 17, 2026
Updated
[Microsoft.Build.Utilities.Core](https://github.com/dotnet/msbuild) from
18.9.6 to 18.10.1.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Build.Utilities.Core's
releases](https://github.com/dotnet/msbuild/releases)._

## 18.10.1

## What's Changed
* [vs16.11] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13103
* [vs17.12] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13796
* [vs17.8] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13902
* [vs17.11] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13903
* [vs17.12] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13909
* [vs17.12] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13986
* Add vs18.9 to merge-flow config; retire vs18.3 by @​JanProvaznik in
dotnet/msbuild#14214
* Bump labeler-cache-retention to use issue-labeler v2.1.0 by
@​jeffhandley in dotnet/msbuild#14171
* Bump main to 18.10.0 after vs18.9 snap by @​JanProvaznik in
dotnet/msbuild#14216
* Improve release skill: Phase 2 DARC rules, VMR backflow, deterministic
baseline by @​JanProvaznik in
dotnet/msbuild#14220
* Determinize release: hardcode OptProf baseline + Phase 3.2 baseline
resolver by @​JanProvaznik in
dotnet/msbuild#14222
* Serialize BuildRequestConfiguration.RequestedTargets to fix solution
metaproject MSB4057 in parallel builds by @​ViktorHofer in
dotnet/msbuild#14223
* [main] Update dependencies from nuget/nuget.client by
@​dotnet-maestro[bot] in dotnet/msbuild#14203
* Core support for AbsolutePath/FileInfo/DirectoryInfo and ITaskItem<T>
as task parameters by @​baronfel in
dotnet/msbuild#13971
* [main] Update dependencies from dotnet/roslyn by @​dotnet-maestro[bot]
in dotnet/msbuild#14206
* Fix existence cache kind poisoning by @​AlesProkop in
dotnet/msbuild#14249
* [main] Source code updates from dotnet/dotnet by @​dotnet-maestro[bot]
in dotnet/msbuild#14226
* Don't disable the MSBuild server for /mt builds when node reuse is off
by @​AR-May in dotnet/msbuild#14248
* Enhance expert reviewer guidelines with additional checks. by @​AR-May
in dotnet/msbuild#14255
* [main] Source code updates from dotnet/dotnet by @​dotnet-maestro[bot]
in dotnet/msbuild#14253
* [main] Update dependencies from dotnet/roslyn by @​dotnet-maestro[bot]
in dotnet/msbuild#14268
* [main] Update dependencies from nuget/nuget.client by
@​dotnet-maestro[bot] in dotnet/msbuild#14267
* Bump github/gh-aw-actions/setup from 0.81.6 to 0.82.2 by
@​dependabot[bot] in dotnet/msbuild#14266
* Avoid boxing the struct enumerator in
PropertyDictionary<T>.GetEnumerator() by @​nareshjo in
dotnet/msbuild#14272
* Refresh copy marker when implementation output changes by @​AlesProkop
in dotnet/msbuild#14231
* Send task-host build process environment as delta by @​OvesN in
dotnet/msbuild#14126
* Add regression coverage for metadata newline preservation by
@​VolPlita in dotnet/msbuild#14261
* Fix EmbedInBinlog items with relative paths from child projects by
@​huulinhnguyen-dev in dotnet/msbuild#13990
* Stop requiring VersionPrefix updates in servicing - insert prerelease
versions to VS by @​ViktorHofer in
dotnet/msbuild#14277
* Fix WriteLinesToFile rewriting unchanged file when custom encoding is
used by @​huulinhnguyen-dev in
dotnet/msbuild#14146
* Enable trim/AOT analyzers for Microsoft.Build and clean up annotations
by @​JeremyKuhne in dotnet/msbuild#14064
* [automated] Merge branch 'vs18.9' => 'main' by @​github-actions[bot]
in dotnet/msbuild#14291
* Fix MicroBuild plugin feed URL to use allowed pkgs.dev.azure.com
format by @​AlesProkop in dotnet/msbuild#14295
* Pass ExcludeRestorePackageImports during restore to avoid redundant
evaluations by @​ViktorHofer with @​Copilot in
dotnet/msbuild#14274
* [vs18.7] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13988
* Adopt Clever Test Selection (CTS) as parallel, non-blocking PR
pipeline by @​jankratochvilcz in
dotnet/msbuild#14212
* Harden exceptions when connecting to server by @​JanProvaznik in
dotnet/msbuild#14292
* Update MicrosoftBuildVersion in analyzer template by
@​github-actions[bot] in dotnet/msbuild#13886
* Fix MSBuild Server client dropping build result under WaitAny race
(#​14172) by @​JanProvaznik in
dotnet/msbuild#14251
* Partially revert #​13660: remove NuGet RestoreTask transient TaskHost
workaround by @​JanProvaznik in
dotnet/msbuild#14297
* Disable daily AI credits guardrail for Expert Code Review workflow by
@​JanProvaznik with @​Copilot in
dotnet/msbuild#14314
* Localized file check-in by OneLocBuild Task: Build definition ID 9434:
Build ID 14614733 by @​dotnet-bot in
dotnet/msbuild#14246
* Add opt-in partial (stop-after-pass) project evaluation by
@​ViktorHofer in dotnet/msbuild#14290
* Use partial evaluation for -getProperty/-getItem without a target by
@​ViktorHofer in dotnet/msbuild#14296
* [main] Source code updates from dotnet/dotnet by @​dotnet-maestro[bot]
in dotnet/msbuild#14324
* [main] Update dependencies from dotnet/roslyn by @​dotnet-maestro[bot]
in dotnet/msbuild#14333
* [main] Update dependencies from nuget/nuget.client by
@​dotnet-maestro[bot] in dotnet/msbuild#14330
* Bump github/gh-aw-actions/setup from 0.82.2 to 0.82.8 by
@​dependabot[bot] in dotnet/msbuild#14328
* Restrict partial evaluation to ProjectInstance by @​ViktorHofer in
dotnet/msbuild#14340
 ... (truncated)

Commits viewable in [compare
view](dotnet/msbuild@v18.9.6...v18.10.1).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Microsoft.Build.Utilities.Core&package-manager=nuget&previous-version=18.9.6&new-version=18.10.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants