[browser] Wasm boot JSON: touch a stamp, not the boot JSON itself (fix #129280)#129591
Merged
Conversation
The build and publish boot JSON targets used <Touch Files="$(_WasmBuildBootJsonPath)" /> to give MSBuild's Inputs/Outputs check a current output timestamp despite GenerateWasmBootJson's content-preservation behavior (PR dotnet#125367). However the boot JSON ($(_WasmBuildBootJsonPath), typically obj/.../dotnet.js, and the publish equivalent) is also a source for the StaticWebAssets compression pipeline and the PreserveNewest copy target. Bumping its mtime on every successful build defeats their incrementality checks and breaks `dotnet build -question`: Building target "_BuildCopyStaticWebAssetsPreserveNewest" partially, because some output files are out of date with respect to their input files. [_BuildStaticWebAssetsPreserveNewest: Input = obj/.../compressed/{hash}-{0}-{fp}-{fp}.gz, Output = bin/.../wwwroot/_framework/dotnet.{fp}.js.gz] Input file is newer than output file. This is the same regression that PR dotnet#118637 (Aug 2025) originally fixed by introducing ArtifactWriter.PersistFileIfChanged. PR dotnet#125367's <Touch> on the boot JSON re-broke it by bumping the file's mtime unconditionally after every successful run. Replace the <Touch> on the boot JSON with a <Touch> on a separate `wasm-bootjson-{build,publish}.complete.stamp` file, and use that stamp as the target's Outputs=. MSBuild now sees a current output timestamp for its own incrementality check on the boot-JSON target, without touching the boot JSON itself — preserving content-mtime semantics for downstream consumers. Fixes dotnet#129280 Fixes the downstream cascade observed on PR dotnet#129454 (WBT `TypeScriptDefinitionsCopiedToWwwrootOnBuild(Debug, emitTypeScriptDts:True)`). Same fix applied to both the build (`_WriteBuildWasmBootJsonFile`) and publish (`GeneratePublishWasmBootJson`) paths since both have identical shape and the same downstream consumers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the WebAssembly Browser MSBuild targets to avoid touching the boot JSON itself (which can cascade timestamp changes into StaticWebAssets compression/copy), and instead uses a separate “complete” stamp file as the incremental Outputs= marker for boot JSON generation targets.
Changes:
- Switch
_WriteBuildWasmBootJsonFileandGeneratePublishWasmBootJsonto usewasm-bootjson-{build,publish}.complete.stampasOutputs=. - Touch the new stamp files (instead of the boot JSON) to satisfy MSBuild’s timestamp-based incremental checks.
- Track the new stamp files in
FileWrites(note: build wrapper update suggested; publish wrapper update still needed).
Addresses Copilot review feedback on dotnet#129591: the .complete.stamp files added in the prior commit are only listed in FileWrites inside the incremental targets (_WriteBuildWasmBootJsonFile, GeneratePublishWasmBootJson). When those targets are skipped on incremental builds, the in-body <ItemGroup> entries are not populated, so `dotnet clean` would leave the stamps behind. Mirror the existing pattern from PR dotnet#125367: also add the stamp paths to FileWrites in the always-run wrapper targets (_GenerateBuildWasmBootJson and _AddPublishWasmBootJsonToStaticWebAssets) that track the boot JSON itself for the same reason. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
maraf
approved these changes
Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Wasm.Build.Tests.WasmTemplateTests.TypeScriptDefinitionsCopiedToWwwrootOnBuild(config: Debug, emitTypeScriptDts: True)fails itsdotnet build -questionrebuild check with:This is the same family of bug as #129280 (different surface: upstream variant where MSBuild flags
_WriteBuildWasmBootJsonFileitself as stale; ours is the downstream cascade through compression).Root cause
Binlog analysis:
_WriteBuildWasmBootJsonFilerunsGenerateWasmBootJson(usesArtifactWriter.PersistFileIfChanged, preserves old mtime on unchanged content) followed by<Touch Files="$(_WasmBuildBootJsonPath)" />(added in [browser] WebAssembly SDK targets more incremental #125367 to keep MSBuild's I/O check on this target happy).$(_WasmBuildBootJsonPath)—obj/{cfg}/{tfm}/dotnet.js— is also a source for the StaticWebAssets compression pipeline (GenerateBuildCompressedStaticWebAssets→GZipCompress).GZipCompress.csskips wheninput.mtime < output.mtime(strict<). WhenTouchlands close enough in time to the.gzwrite (or any subsequent target re-stamps dotnet.js), equal/newer mtimes send the next build's compression task down the re-compress path.obj/.../{0}.gzmtime pastbin/.../dotnet.{fp}.js.gz(which was copied during build WIP: repo consolidation scouting kick-off - make clr build locally on Windows #1 and isn't touched since), so_BuildCopyStaticWebAssetsPreserveNewestreports its input newer than its output.-questionfails.This is a regression of #118637 (Aug 2025, "Override boot config only when the content changes"), which fixed the exact same
_BuildCopyStaticWebAssetsPreserveNewestsymptom in dotnet/aspnetcore#63207 by introducingArtifactWriter.PersistFileIfChanged. PR #125367's<Touch>undid that protection.Fix
Touch a separate
wasm-bootjson-{build,publish}.complete.stampfile rather than the boot JSON itself, and use the stamp as the target'sOutputs=. MSBuild's incrementality check on the boot-JSON target stays correct (its declared output has a current mtime after every successful run), while the boot JSON's mtime remains content-derived — preserving #118637's invariant for downstream consumers.Applied to both:
_WriteBuildWasmBootJsonFile(build)GeneratePublishWasmBootJson(publish)Both have identical shape and the same downstream consumers (StaticWebAssets compression / Copy targets).
What's not fixed here
_ConvertBuildDllsToWebcil(line 431) uses the same<Touch>pattern but on per-item-batched@(_WasmConvertedWebcilOutputs). It is probably exposed to the same cascade for the webcil files; not addressed here pending a per-item analysis.dotnet/sdk:GZipCompress/BrotliCompress<mtime check could become<=to harden the entire StaticWebAssets pipeline against this class of cascade. Worth a separate dotnet/sdk PR.Verification
dotnet/runtimePR [main] Source code updates from dotnet/dotnet #129454 build https://dev.azure.com/dnceng-public/public/_build/results?buildId=1470806 (browser-wasm windows Release WasmBuildTests, workitemWBT-NoWebcil-MONO-ST-Wasm.Build.Tests.WasmTemplateTests).TypeScriptDefinitionsCopiedToWwwrootOnBuild(Debug|Release, emitTypeScriptDts:True)exercises thedotnet build -questionsecond-build check that this fix addresses.Fixes #129280
Note
This pull request was created with the assistance of GitHub Copilot.