[browser][CoreCLR] Enable trimmed ReadyToRun library-test CI - #133656
Draft
lewing wants to merge 21 commits into
Draft
[browser][CoreCLR] Enable trimmed ReadyToRun library-test CI#133656lewing wants to merge 21 commits into
lewing wants to merge 21 commits into
Conversation
The MetadataReaderProvider owns a memory-mapped section over the underlying stream. Leaving it to the finalizer keeps the file mapped inside long-lived MSBuild task hosts, so a later writer targeting the same path fails with "user-mapped section open". Observed as crossgen2 failing to rewrite an R2R image that an earlier ConvertDllsToWebcil probe had opened.
ConvertDllsToWebcil may stage a prebuilt ReadyToRun image in place of converting IL. The guard compared assembly versions, which almost never change between incremental builds, so a stale image compiled against a previous IL set passed the check and was staged. With cross-module inlining every image in a bundle belongs to one version bubble that the runtime validates by MVID at load, so that stale image is a startup fail-fast rather than a graceful fallback. Compare MVIDs instead, which turns the failure into a build-time fallback to IL conversion. Detect webcil-in-wasm by content (the wasm magic) rather than by extension, because a prebuilt image may still be named *.dll and PEReader would throw on it, returning null and silently bypassing the guard. The "unreadable identity means accept" fallback is preserved.
…mbly SDK pack The crossgen2 resolution override lived in WasmApp.InTree.props, so only in-tree builds could produce per-app R2R; an out-of-tree app fell through to the base SDK, whose ReadyToRun pipeline predates wasm support and emits composite images. Composite strips the assembly manifest, so the runtime fails coreclr_initialize with 0x80131018 at startup. Ship the wiring from the pack instead, in CoreCLR-only files so no Mono path gains a branch. The import is gated on a props-time signal for an in-build crossgen2 (Crossgen2InBuildDir, or Crossgen2SdkOverridePropsPath in-tree, since liveBuilds.targets sets the former at targets-time), and is inert for stock consumers, which keep resolving crossgen2 through the base SDK. The override probes both the raw in-build layout (crossgen2 at the root) and the shipped Microsoft.NETCore.App.Crossgen2 pack layout (under tools/), and sets Crossgen2Tool directly, because the base SDK resolver keys on ResolvedCrossgen2Pack which a standalone app does not populate for a local build. It can be retired once a restorable wasm crossgen2 pack exists (dotnet/sdk#55785).
…d pipeline Implements the two modes: a dev-loop build stages the prebuilt framework R2R images from the runtime pack and ships the app as IL, while publish crossgens the whole closure per app, trimmed or untrimmed. The main correctness problems addressed: - Per-app R2R images are named <name>.wasm, but ComputeWasmPublishAssets classifies managed assemblies by the .dll extension, so the images were treated as native and leaked to the publish root, leaving the boot config with no coreAssembly. Restore the IL .dll in the publish list so ConvertDllsToWebcil stages the image from PrebuiltR2RDirectory. This has to happen in both the outer and nested passes: a native relink crossgens inside WasmNestedPublishApp, where ProcessPublishFilesForWasm is never scheduled, and _GatherWasmFilesToPublish filters to .dll, dropping every compiled assembly while exiting 0. - ILLink stamps PostprocessAssembly on its own collection rather than ResolvedFileToPublish on the Blazor/static-web-assets route, so the mainline compile list was empty and crossgen2 never ran. - A trimmed publish flow served the full copy-local set from the runtime pack mixed with the trimmed closure, which mixes version bubbles and lets an untrimmed assembly call a member ILLink removed from the trimmed framework. Restrict the served set to the linker output and repoint it there. - Flag flips left derived outputs behind. Static web assets are content-fingerprinted, so a re-stage adds a new name beside the old file instead of replacing it, leaving two copies of an assembly from two different version bubbles. Record the mode and drop the derived outputs when it changes. - Per-app crossgen inputs are deliberately conservative: cross-module inlining means any change must recompile every image, and stale images in obj/R2R are pruned. Composite and non-wasm container formats are rejected with a comprehensible error instead of producing images that fail at startup, and a missing crossgen2 is reported at the point of use. PublishReadyToRun defaults to false; flipping it belongs to the codegen-quality work stream.
The four relink triggers keyed solely on IsBrowserWasmProject, which a Blazor app leaves unset because it resolves the wasm RID late, so WasmBuildNative=true was a silent no-op there and the app shipped the prebuilt dotnet.native.wasm from the runtime pack. OR in WasmBuildNative, which is unambiguous: this file is imported only for CoreCLR browser-wasm apps. Kept as an OR so IsBrowserWasmProject, which also steers ICU and tzdata skipping, is never forced on. Fixes dotnet#133185
The nested publish evaluates ILLink.Tasks.csproj with different global properties, so MSBuild builds it a second time and copies obj to bin over the assembly the outer pass has already loaded, failing with MSB3027. The outer pass has built the task by the time the nested publish runs, so the reference is redundant there as well as harmful.
Covers the dev-loop build (framework R2R staged from the runtime pack, no per-app crossgen), publish trimmed and untrimmed (whole closure compiled per app), both with and without a native relink, and the disabled case. Each publish case drives Home, Counter and Weather in a real browser, which is what distinguishes a bundle that boots from one that merely looks staged. The assertions target failures seen during bring-up that still exit 0: assemblies missing from the staged set relative to the linker closure, duplicate fingerprinted copies of one assembly, managed assemblies leaking outside _framework, and per-app crossgen running (or not) for the mode. Adds the Weather page that the nav menu of the test app already linked to, and ships the in-build crossgen2 plus the wasm-aware Crossgen2Tasks shim as Helix correlation payload so the tests can resolve them there.
Gives the R2R pipeline a library-test vehicle: this suite exercises the trimmed publish flow, where the served bundle must be exactly the linker closure staged as per-app R2R images. CoreCLR only; Mono is unaffected. tests.browser.targets already implies PublishTrimmed from PublishReadyToRun.
… pack on Helix Fixes the gap CI uncovered: the no-workload Wasm.Build.Tests Helix leg has no in-build crossgen2, so the per-app publish tests aborted with a NullReferenceException from the base SDK PrepareForReadyToRunCompilation task (empty Crossgen2Tool). The SDK does restore a crossgen2 pack when PublishReadyToRun is set; the failure was that the override ignored it. - The ResolveReadyToRunCompilers override no longer shadows the base/shim resolver into an empty tool. It uses the in-build crossgen2 when present (in-tree / dev builds) and otherwise resolves the tool from the SDK-restored crossgen2 pack (@(ResolvedCrossgen2Pack)), so @(Crossgen2Tool) is never left empty when a tool is available. - The wasm-aware Crossgen2Tasks shim (the wasm-container crossgen tasks, which the base SDK still lacks) now ships as a Helix correlation payload gated on its own path rather than on the crossgen2 bin dir, which the WBT test leg does not carry, so the shim actually reaches the worker. - GetR2RBuildArgs passes Crossgen2InBuildDir and the shim override paths only when each exists under BASE_DIR. On the no-workload leg crossgen2 is resolved from the SDK pack, so passing a non-existent Crossgen2InBuildDir would otherwise break the call-helpers generator. - The Wasm.Build.Tests CoreCLR project setup pins KnownCrossgen2Pack to the locally built pack version, so the SDK restores the wasm-capable crossgen2 pack from the local feed rather than a default one. Validated in-tree: console-node ReadyToRun publish builds and runs under node.
- ResolveReadyToRunCompilers override: drop the last-resort Crossgen2Path that named a possibly non-existent executable; when the in-build dir has no crossgen2 the tool is left to the SDK-pack fallback (and ultimately the actionable _WasmCoreClrValidateReadyToRun error) instead of failing with "file not found". - Use Update="@(...)" on the CopyToOutputDirectory metadata item groups so the metadata is stamped on the existing items rather than relying on bare metadata-only elements. - Revert the WasmBuildNative relink trigger OR: the relink is driven by IsBrowserWasmProject only, as before. dotnet#133185 does not reproduce on current SDKs and the OR was defensive. - Narrow the stale-mode served-assets cleanup to the fingerprinted *.wasm/*.dll we stage, so the SDK never recursively removes app-authored content under the served directory. - Quote the crossgen2 override path arguments passed by the Wasm.Build.Tests ReadyToRun opt-in.
_WasmCoreClrSuppressNestedPublishAssetCopy and _WasmCoreClrRestoreCopyToOutputDirectory only do anything when _WasmFrameworkCopyToOutputDirectory=PreserveNewest, which is set exclusively by eng/testing/tests.browser.targets so the xunit runner gets framework assets in bin/. For a real app the default is Never, making both a no-op, so they move out of the shipped WebAssembly SDK into the test infra, gated on RuntimeFlavor=CoreCLR. Also drop _WasmCoreClrInvalidateStaleModeOutputs, which deleted stale derived outputs on a mode flip. Incremental mode-flip cleanup will be reimplemented later without deleting files; every test/CI matrix cleans obj per case, so nothing exercised it.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Only embed the JIT-specific IsDynamicCodeCompiled substitution when FeatureDynamicCodeCompiled is enabled. Interpreter-only CoreCLR must retain its false getter after trimming. Fixes dotnet#133615 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Honor browser aggressive trimming and add a test-scoped ReadyToRun switch with matching build and Helix CI lanes. Preserve existing browser descriptors instead of importing Apple-only roots. Keep TestUtilities interpreted for the tracked platform-probe issue, preserve original conformance assertions, and quarantine known WebAssembly R2R failures with a shared browser/WASI predicate. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 6 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
lewing
requested review from
maraf,
pavelsavara and
radekdoulik
and
a lite review from Copilot
September 11, 2026 03:02
Contributor
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Contributor
|
Tagging subscribers to this area: @dotnet/area-infrastructure-libraries |
Integrate dotnet#133639 at 1c1e323, including its renamed substitution resource and regression coverage. Resolve the existing local substitution gate to the landed upstream implementation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved configuration, trimmed-closure assertion, and MVID image-validation findings remain.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs — Reject unreadable prebuilt images before staging |
|
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props — Gate the CoreCLR crossgen override away from Mono builds |
What changed in this PR
Enables trimmed CoreCLR browser library-test CI with ReadyToRun while preserving separate interpreter coverage.
Changes:
- Adds scoped trimming/R2R properties and dedicated CI lanes.
- Adds CoreCLR browser publishing, Crossgen2 resolution, WebCIL staging, and MVID validation.
- Adds R2R test coverage, quarantines, Helix propagation, and workflow documentation.
| File | Summary |
|---|---|
src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs |
Corrects metadata-reader disposal. |
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs |
Validates and stages R2R WebCIL images. |
src/mono/wasm/Wasm.Build.Tests/WebcilInWasmSizesTests.cs |
Covers WebCIL/R2R staging behavior. |
src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs |
Pins Crossgen2 pack versions. |
src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs |
Tests R2R publishing and trimmed closure behavior. |
src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs |
Adds test environment configuration. |
src/mono/wasm/testassets/BlazorBasicTestApp/App/Pages/Weather.razor |
Supports browser interaction testing. |
src/mono/sample/wasm/Directory.Build.props |
Updates sample build properties. |
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props |
Activates CoreCLR R2R wiring. |
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets |
Implements CoreCLR browser R2R publishing. |
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets |
Resolves browser-compatible Crossgen2 tooling. |
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props |
Imports CoreCLR R2R configuration. |
src/mono/browser/build/WasmApp.ReadyToRun.targets |
Updates browser R2R target wiring. |
src/mono/browser/build/WasmApp.InTree.props |
Updates in-tree browser build properties. |
src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs |
Adds targeted R2R quarantines. |
src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs |
Adds targeted R2R quarantines. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs |
Adds R2R-specific test handling. |
src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj |
Updates trimming and assembly staging metadata. |
src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj |
Configures JavaScript interop test publishing. |
src/libraries/sendtohelix-browser.targets |
Propagates browser test settings. |
src/libraries/Directory.Build.props |
Adds test-scoped R2R enablement. |
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs |
Adds WASM R2R detection. |
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj |
Updates CoreLib trimming configuration. |
eng/testing/tests.browser.targets |
Configures browser trimming and R2R settings. |
eng/pipelines/runtime.yml |
Adds standard browser R2R coverage. |
eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml |
Adds extra-platform R2R coverage. |
eng/pipelines/common/templates/wasm-coreclr-library-tests.yml |
Propagates R2R and trimming parameters. |
eng/liveILLink.targets |
Enables browser test trimming configuration. |
docs/workflow/testing/libraries/testing-wasm.md |
Documents local trimmed R2R testing. |
Comment on lines
+277
to
278
| if (prebuiltMvid is null || candidateMvid.Equals(prebuiltMvid.Value)) | ||
| return true; |
| in-tree signal is Crossgen2SdkOverridePropsPath (set props-time in the repo Directory.Build.props). | ||
| Inert for stock consumers (neither set), which resolve crossgen2 through the base SDK. --> | ||
| <Import Project="$(MSBuildThisFileDirectory)Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props" | ||
| Condition="'$(Crossgen2InBuildDir)' != '' or '$(Crossgen2SdkOverridePropsPath)' != ''" /> |
This was referenced Sep 11, 2026
Open
Open
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.


Summary
Addresses #133193 by making
EnableAggressiveTrimming=trueactually enable browser test trimming and adding separateLibraryTestsCoreCLR_R2RChrome CI lanes. Uses the existing browser trimming configuration and descriptors; no Apple-mobile descriptor imports are added.TestWasmReadyToRun, avoiding globalPublishReadyToRun=truereaching host-side build tools.TEST_READY_TO_RUN_MODE=1in browser R2R apps. UsePlatformDetection.IsWasmReadyToRunfor shared browser/WASI R2R quarantines, preserving interpreter coverage..ilprojLink metadata from the TestILAssembly reference so its DLL is staged under the correct VFS name.WasmBuildNativeImplicitInReleaseConfiguration=false.Prerequisites / draft status
Not ready to merge. The remaining unmerged prerequisite is:
245cb70d593f2c8f3a4e23bcc422e7da5926e15b, merged into this branch. Its changes remain visible in the main-based PR diff until it lands.Landed: #133639, the standalone CoreLib
IsDynamicCodeCompiledtrimming correction, is now on main at1c1e32381c1. This branch merged that main revision ine220984f2bc, taking the complete upstream implementation, renamed substitution resource, and regression coverage. The substitution fix no longer appears in this PR's diff.The library-test bring-up itself is commit
6ceb8f2d0cb. The main refresh also includes #133610's stack-trace fix. #133547 has not been integrated. The existing conformance quarantines remain; no test assertions were rewritten to avoid runtime defects.Failure classification and quarantines
TestUtilities.dllfrom R2R. Libraries and test assemblies remain R2R; filesystem and networking tests remain enabled.MethodBase.GetCurrentMethodconformance (#133617)The WASM-wide predicate intentionally anticipates WASI R2R (#133265). The failure reproductions and execution results here are browser results, not a claim of WASI validation.
Local validation
macOS arm64 host, browser-wasm CoreCLR Release, Chromium 153, using the pinned publishing prerequisite.
After merging main at #133639, rebuilt browser CoreCLR, crossgen2, CoreLib, libraries, host, and packs, then rebuilt and reran the complete six-project R2R smoke command. The served native runtime asset byte-matched the rebuilt runtime pack.
Before this main refresh, additional clean-output comparisons ran full UnmanagedMemoryStream, FileSystem, Cryptography, Data.Common, Globalization, and LINQ suites in both actual EAT and trimmed R2R, without the speculative Apple roots: 136,138 passed, 1,141 skipped, zero failed per configuration, with identical counts. Logs explicitly show
PublishTrimmed=trueand the effective descriptors. No replacement roots were required.Also previously built a Helix test archive and ran its extracted contents successfully (UnmanagedMemoryStream, 32/32). Served HTTP library/test WASM assets byte-matched the Crossgen2 R2R outputs while TestUtilities had no native R2R image.
ActiveIssue-filtered cases are omitted from totals rather than counted as skipped. The full supported CI selection has not been validated remotely yet; this draft is for that next step. No performance claim is made.
Note
This PR description and changes were prepared with GitHub Copilot assistance.