Skip to content

[browser] WebAssembly SDK targets more incremental#125367

Draft
maraf wants to merge 3 commits intomainfrom
maraf/WasmSdkIncremental
Draft

[browser] WebAssembly SDK targets more incremental#125367
maraf wants to merge 3 commits intomainfrom
maraf/WasmSdkIncremental

Conversation

@maraf
Copy link
Member

@maraf maraf commented Mar 10, 2026

Summary

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

Changes

Boot JSON Generation (commit 1)

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

Webcil Conversion (commit 2)

Split _ResolveWasmOutputs into 3 targets:

  • _ComputeWasmBuildCandidates (always runs) -- resolves build asset candidates via ComputeWasmBuildAssets
  • _ConvertBuildDllsToWebcil (incremental) -- runs ConvertDllsToWebcil only when DLL inputs are newer than webcil outputs
  • _ResolveWasmOutputs (always runs) -- reconstructs webcil items via MSBuild item transforms and calls DefineStaticWebAssets

Incrementalism Proof

Binlog analysis comparing baseline (no changes, no-op rebuild) vs PR branch (no-op rebuild with outputs up-to-date):

Target                                            Base       PR     Change
-------------------------------------------------------------------------
_GenerateBuildWasmBootJson                       144ms      4ms
_WriteBuildWasmBootJsonFile                        N/A     SKIP     NEW+SKIPPED
_ResolveBuildWasmBootJsonEndpoints                 N/A     38ms     NEW
_ResolveWasmOutputs                              175ms    103ms
_ConvertBuildDllsToWebcil                          N/A     SKIP     NEW+SKIPPED
_ComputeWasmBuildCandidates                        N/A     SKIP     NEW+SKIPPED

Baseline: 12 targets ran, 2 skipped, 1445ms total WASM target time
PR:       13 targets ran, 5 skipped, 1289ms total WASM target time
Reduction: ~10.8% in WASM target execution time

Key savings on incremental rebuilds:

  • ConvertDllsToWebcil task (~35ms) -- skipped when webcil outputs are newer than DLL inputs
  • GenerateWasmBootJson task (~95ms) -- skipped when boot JSON is newer than all assembly/JS inputs

Clean builds show negligible overhead (~1%, within noise).

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

MSBuild Condition evaluation order

_ConvertBuildDllsToWebcil has Condition="'$(_WasmEnableWebcil)' == 'true'" but MSBuild evaluates Condition before DependsOnTargets. Since _WasmEnableWebcil is set by _ResolveWasmConfiguration (inside _ComputeWasmBuildCandidates), the parent _ResolveWasmOutputs explicitly lists _ComputeWasmBuildCandidates in its DependsOnTargets before _ConvertBuildDllsToWebcil.

Culture/non-culture DLL separation

Non-culture DLLs and culture-specific DLLs have different metadata (RelatedAsset only exists on culture items). Using %(RelatedAsset) in item transforms causes MSB4096 batching errors when applied to items without that metadata. The fix uses separate intermediate item names (_WasmWebcilConvertedNonCulture, _WasmWebcilConvertedCulture) to avoid cross-contamination.

Testing

  • Clean build and publish succeed
  • No-op rebuild produces correct output
  • Binlog analysis confirms incremental targets skip when outputs are up-to-date

maraf and others added 2 commits March 9, 2026 12:55
Split _GenerateBuildWasmBootJson and GeneratePublishWasmBootJson into
smaller targets to enable Inputs/Outputs-based incrementalism for the
expensive GenerateWasmBootJson task invocations.

Build path split:
- _ResolveBuildWasmBootJsonEndpoints: always runs, resolves endpoints
- _WriteBuildWasmBootJsonFile: incremental (Inputs/Outputs), writes boot JSON
- _GenerateBuildWasmBootJson: always runs, defines boot config as static web asset

Publish path split:
- _ResolvePublishWasmBootJsonInputs: always runs, resolves publish inputs
- GeneratePublishWasmBootJson: incremental (Inputs/Outputs), writes boot JSON

When no inputs have changed, the GenerateWasmBootJson task (~100ms) is
skipped entirely. The always-running targets handle endpoint resolution
and asset definition to ensure downstream item collections remain populated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split the monolithic _ResolveWasmOutputs target into three targets to
enable MSBuild Inputs/Outputs-based skip optimization for DLL-to-webcil
conversion:

- _ComputeWasmBuildCandidates (always runs): resolves build asset
  candidates via ComputeWasmBuildAssets, separates DLL candidates into
  culture/non-culture groups, and computes expected webcil output paths.

- _ConvertBuildDllsToWebcil (incremental): runs ConvertDllsToWebcil
  only when input DLLs are newer than their webcil outputs. The task
  also retains its internal per-file timestamp checks as a secondary
  optimization.

- _ResolveWasmOutputs (always runs): reconstructs webcil candidate
  items from build asset candidates using MSBuild item transforms
  (matching the ConvertDllsToWebcil task's path/metadata logic), then
  calls DefineStaticWebAssets to produce the final asset definitions.

Culture and non-culture DLLs are separated into distinct intermediate
items (_WasmWebcilConvertedNonCulture / _WasmWebcilConvertedCulture) to
avoid MSBuild batching errors on metadata like RelatedAsset that only
culture items define.

The _ResolveWasmOutputs target lists _ComputeWasmBuildCandidates
explicitly in its DependsOnTargets (before _ConvertBuildDllsToWebcil)
to ensure _WasmEnableWebcil is set by _ResolveWasmConfiguration before
the conversion target's Condition is evaluated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@maraf maraf added this to the 11.0.0 milestone Mar 10, 2026
@maraf maraf self-assigned this Mar 10, 2026
@maraf maraf added arch-wasm WebAssembly architecture area-Build-mono labels Mar 10, 2026
Copilot AI review requested due to automatic review settings March 10, 2026 08:18
@maraf maraf added the os-browser Browser variant of arch-wasm label Mar 10, 2026
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 improves MSBuild incrementalism for WebAssembly SDK build targets by splitting monolithic targets into smaller, more focused targets with proper Inputs/Outputs declarations. This allows MSBuild to skip expensive operations (webcil DLL conversion, boot JSON generation) on no-op rebuilds when inputs haven't changed.

Changes:

  • The _ResolveWasmOutputs target is split into _ComputeWasmBuildCandidates (resolve/classify candidates), _ConvertBuildDllsToWebcil (incremental webcil conversion), and _ResolveWasmOutputs (reconstruct webcil metadata and define static web assets).
  • The _GenerateBuildWasmBootJson target is split into _ResolveBuildWasmBootJsonEndpoints (resolve endpoints), _WriteBuildWasmBootJsonFile (incremental boot JSON generation), and _GenerateBuildWasmBootJson (define static web asset for boot config).
  • The GeneratePublishWasmBootJson target is split into _ResolvePublishWasmBootJsonInputs (resolve inputs) and GeneratePublishWasmBootJson (incremental publish boot JSON generation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-Build-mono os-browser Browser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants