Skip to content

[browser] WebAssembly SDK targets more incremental - #125367

Merged
maraf merged 13 commits into
mainfrom
maraf/WasmSdkIncremental
May 15, 2026
Merged

[browser] WebAssembly SDK targets more incremental#125367
maraf merged 13 commits into
mainfrom
maraf/WasmSdkIncremental

Conversation

@maraf

@marafmaraf commented Mar 10, 2026

Copy link
Copy Markdown
Member

Summary

Improves MSBuild incrementalism for WebAssembly browser build targets in Microsoft.NET.Sdk.WebAssembly.Browser.targets. On no-op rebuilds where inputs have not changed, the expensive ConvertDllsToWebcil and GenerateWasmBootJson tasks are now skipped via MSBuild's Inputs/Outputs mechanism.

Changes

Webcil Conversion

Split _ResolveWasmOutputs into 3 targets:

  • _ComputeWasmBuildCandidates (always runs) — resolves build asset candidates, classifies DLLs vs framework pass-throughs, computes expected webcil output paths
  • _ConvertBuildDllsToWebcil (incremental) — runs ConvertDllsToWebcil only when DLL inputs are newer than webcil outputs
  • _ResolveWasmOutputs (always runs) — reconstructs webcil items via MSBuild item transforms, classifies framework candidates, calls DefineStaticWebAssets

Boot JSON Generation

Split _GenerateBuildWasmBootJson into 3 targets:

  • _ResolveBuildWasmBootJsonEndpoints (always runs) — resolves endpoints and fingerprinted assets
  • _WriteBuildWasmBootJsonFile (incremental) — writes boot JSON file only when inputs change
  • _GenerateBuildWasmBootJson (always runs) — defines static web assets from the boot JSON output

Split GeneratePublishWasmBootJson into 2 targets:

  • _ResolvePublishWasmBootJsonInputs (always runs) — resolves publish endpoints
  • GeneratePublishWasmBootJson (incremental) — writes boot JSON only when inputs change

Touch outputs for content-comparison tasks

Both ConvertDllsToWebcil (Utils.MoveIfDifferent) and GenerateWasmBootJson (ArtifactWriter.PersistFileIfChanged) use content-comparison write patterns that preserve old file timestamps when output content is unchanged. This defeats MSBuild's timestamp-based Inputs/Outputs incrementalism. Added <Touch> after each task invocation to ensure output timestamps reflect the current build session.

FileWrites in always-run wrapper targets

When incremental targets are skipped, <FileWrites> items inside their body are not populated. Added <FileWrites> to the always-run wrapper targets (_GenerateBuildWasmBootJson, _AddPublishWasmBootJsonToStaticWebAssets, _ResolveWasmOutputs) so dotnet clean works correctly.

Incrementalism Proof

Binlog analysis of Wasm.Browser.Sample — build 1 (clean) vs build 2 (no-op rebuild):

Build Path

Target Build 1 Build 2 Status
----------------------------------------------------------------------
_ComputeWasmBuildCandidates 59ms 62ms Runs (item producer, always needed)
_ConvertBuildDllsToWebcil 60ms 7ms ✅ SKIPPED — outputs up-to-date
_ResolveWasmOutputs 138ms 116ms Runs (DefineStaticWebAssets, item producer)
_ResolveBuildWasmBootJsonEndpoints 21ms 20ms Runs (item producer)
_WriteBuildWasmBootJsonFile 104ms 4ms ✅ SKIPPED — output up-to-date
_GenerateBuildWasmBootJson 3ms 3ms Runs (item producer)

Design Notes

Why split instead of just adding Inputs/Outputs?

MSBuild's Inputs/Outputs mechanism skips the entire target body when outputs are up-to-date. Since _ResolveWasmOutputs and _GenerateBuildWasmBootJson both contained file-writing tasks AND item-defining tasks (DefineStaticWebAssets), making them incremental would break downstream targets that depend on the items they produce. The split separates file I/O (incremental) from item definitions (always-run).

Why Touch after task execution?

Both ConvertDllsToWebcil and GenerateWasmBootJson implement "write only if content changed" patterns internally. While this avoids unnecessary downstream rebuilds, it defeats MSBuild's Inputs/Outputs check because unchanged outputs retain timestamps from a previous build session. The <Touch> ensures output timestamps always reflect the current build.

Framework asset classification

The Framework SourceType classification (from #125329) is preserved. Since the incremental _ConvertBuildDllsToWebcil target only receives DLL items, framework candidate classification is done in MSBuild item groups in _ComputeWasmBuildCandidates instead of via the task's PassThroughCandidates output. Items with WasmNativeBuildOutput metadata are per-project (Computed); items without are Framework assets needing per-project materialization.

Culture/non-culture DLL separation

Culture-specific DLLs have RelatedAsset metadata that non-culture DLLs lack. Using %(RelatedAsset) in item transforms causes MSB4096 batching errors. The fix uses separate intermediate items (_WasmWebcilConvertedNonCulture, _WasmWebcilConvertedCulture).

Testing

  • Clean build and no-op rebuild succeed with zero errors
  • No-op rebuild correctly skips _ConvertBuildDllsToWebcil and _WriteBuildWasmBootJsonFile
  • Binlog analysis verified with MSBuild.StructuredLogger

Note

This PR description was generated with assistance from GitHub Copilot.

@marafmaraf added this to the 11.0.0 milestone Mar 10, 2026
@marafmaraf self-assigned this Mar 10, 2026
@marafmaraf added arch-wasm WebAssembly architecture area-Build-mono labels Mar 10, 2026
CopilotAI review requested due to automatic review settings March 10, 2026 08:18
@marafmaraf added the os-browser Browser variant of arch-wasm label Mar 10, 2026

This comment was marked as resolved.

This comment was marked as resolved.

CopilotAI review requested due to automatic review settings March 12, 2026 20:44

This comment was marked as resolved.

Split monolithic targets into incremental chains:
Webcil conversion:
- _ComputeWasmBuildCandidates (always runs): resolves candidates, classifies DLLs
vs framework pass-throughs, computes expected webcil output paths
- _ConvertBuildDllsToWebcil (incremental): DLL-to-webcil conversion with
Inputs/Outputs, Touch to fix content-comparison timestamp preservation
- _ResolveWasmOutputs (always runs): reconstructs webcil items, classifies
framework candidates, defines static web assets
Build boot JSON:
- _ResolveBuildWasmBootJsonEndpoints (always runs): endpoint resolution
- _WriteBuildWasmBootJsonFile (incremental): JSON file generation with
Inputs/Outputs, Touch for timestamp fix
- _GenerateBuildWasmBootJson (always runs): static web asset registration
Publish boot JSON:
- _ResolvePublishWasmBootJsonInputs (always runs): input resolution
- GeneratePublishWasmBootJson (incremental): JSON file generation with
Inputs/Outputs, Touch for timestamp fix
FileWrites are added to always-run wrapper targets so dotnet clean
works correctly even when incremental targets are skipped.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@maraf
marafforce-pushed the maraf/WasmSdkIncremental branch from 084b5ee to ab57788CompareApril 1, 2026 11:42
@github-actions

This comment has been minimized.

Two new tests verify WASM build incrementalism via binlog analysis:
- IncrementalBuild_NoChanges_SkipsWebcilAndBootJson: Builds twice with
no changes, asserts _ConvertBuildDllsToWebcil and _WriteBuildWasmBootJsonFile
are skipped on the second build.
- IncrementalBuild_SourceChange_RunsWebcilForAppOnly: Builds, modifies
a C# source file, rebuilds, then asserts the webcil/boot JSON targets
run but only the app assembly is re-converted (framework DLLs skipped
by the task's internal per-file timestamp check).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings April 1, 2026 11:57

This comment was marked as resolved.

@github-actions

This comment has been minimized.

marafand others added 2 commits April 1, 2026 14:09
…amps
- Add missing file inputs to _WriteBuildWasmBootJsonFile: VFS assets, config files, and dotnet.js template
- Add property stamp files for both build and publish boot JSON targets using WriteOnlyWhenDifferent to detect property-only changes (e.g., WasmDebugLevel, environment name, globalization flags)
- Build stamp: wasm-bootjson-build.stamp
- Publish stamp: wasm-bootjson-publish.stamp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings April 1, 2026 14:27
CopilotAI review requested due to automatic review settings April 24, 2026 10:08

This comment was marked as resolved.

@github-actions

This comment has been minimized.

…andaloneHosting to stamps
- Reuse %(WebcilOutputPath) metadata in _ResolveWasmOutputs instead of
recomputing paths from _WasmBuildWebcilPath + FileName/AssetTraitValue,
eliminating duplicated path logic and risk of future divergence.
- Add to both build and publish boot
JSON property stamps so standalone-vs-hosted hosting mode changes
correctly invalidate the incremental boot JSON targets.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment was marked as resolved.

CopilotAI review requested due to automatic review settings April 29, 2026 06:53
@maraf
maraf marked this pull request as ready for review April 29, 2026 07:00
@maraf
maraf requested a review from a teamApril 29, 2026 07:00

This comment was marked as resolved.

Add two new test cases to RebuildTests:
- IncrementalBuild_NoChanges_SkipsWebcilAndBootJson_AOT: verifies AOT
publish incremental skip of boot JSON generation on no-op rebuild
- IncrementalBuild_PropertyChange_RunsBootJson: verifies that changing
WasmDebugLevel (property-only, no source changes) invalidates the
boot JSON stamp file and triggers regeneration while skipping webcil
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI 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

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

Comment threadsrc/mono/wasm/Wasm.Build.Tests/RebuildTests.cs
Comment threadsrc/mono/wasm/Wasm.Build.Tests/RebuildTests.cs
@pavelsavara

Copy link
Copy Markdown
Member

Does change of (wasm related) properties force full rebuild ?

@maraf
maraf merged commit 8752339 into mainMay 15, 2026
44 of 48 checks passed
@maraf
maraf deleted the maraf/WasmSdkIncremental branch May 15, 2026 12:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-Build-monoos-browserBrowser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@maraf@pavelsavara@lewing