[browser] More Wasm.Build.Tests on CoreCLR#128296
Open
maraf wants to merge 51 commits into
Open
Conversation
Remove class-level [TestCategory("native")] from NativeBuildTests,
NativeLibraryTests, PInvokeTableGeneratorTests, and DllImportTests so
non-AOT methods pass the CoreCLR xunit filter (-notrait category=native).
AOT-only methods get a method-level [TestCategory("native")] to keep
them excluded on CoreCLR.
NativeLibraryTests: four mixed theories (both aot=false and aot=true
inline data) are split into a non-AOT theory and a separate AOT-only
theory tagged 'native', because xunit trait filtering is per-method,
not per-theory-row.
Co-authored-by: Copilot <[email protected]>
Change [TestCategory("native")] to [TestCategory("native-coreclr")] for tests
that use native relink but should also run on CoreCLR:
- IcuShardingTests: CustomIcuShard, AutomaticShardSelectionDependingOnEnvLocale
- IcuShardingTests2: DefaultAvailableIcuShardsFromRuntimePack
- IcuTests: FullIcuFromRuntimePackWithInvariant, FullIcuFromRuntimePackWithCustomIcu
- InvariantGlobalizationTests: RelinkingWithoutAOT
- MemoryTests: entire class (AllocateLargeHeapThenRepeatedlyInterop)
CoreCLR always has native relink support, so these tests can run on both
Mono and CoreCLR. The CoreCLR xunit filter excludes category=native but
not category=native-coreclr.
Co-authored-by: Copilot <[email protected]>
Without these entries in BuildWasmAppsJobsListCoreCLR.txt, the test
classes whose class-level [TestCategory("native")] was removed (or
retagged to native-coreclr) in this PR would still not be scheduled
as Helix work items for the CoreCLR BuildWasmApps scenario.
Co-authored-by: Copilot <[email protected]>
EnsureWasmTemplatesInstalled placed .dotnet-cli-home next to the template nupkg (which on Linux Helix agents is under the read-only correlation payload), causing 'Read-only file system' / 'Access denied' errors for every test class that calls CreateWasmTemplateProject. Always use BuildEnvironment.TmpPath — the harness's writable scratch dir — instead. Co-authored-by: Copilot <[email protected]>
EnsureWasmTemplatesInstalled set DOTNET_CLI_HOME to TmpPath/.dotnet-cli-home when running 'dotnet new install'. The subsequent 'dotnet new <template>' call goes through DotNetCommand(useDefaultArgs: false), which does not apply BuildEnvironment.EnvVars to the child process, so it inherited the Helix-set DOTNET_CLI_HOME (e.g. /root/helix/work/workitem/e/.dotnet). Two different DOTNET_CLI_HOME values mean two different template caches: the install lands in one, the lookup misses in the other, producing 'No templates or subcommands found matching: wasmbrowser' (exit 103) on every test class that calls CreateWasmTemplateProject. Prefer the inherited DOTNET_CLI_HOME so install and lookup share a cache; fall back to TmpPath only when not set (preserves the read-only-FS fix for environments without an inherited value). Reproduced locally with DOTNET_CLI_HOME=/tmp/helix-cli-home; templates were installed under wbt artifacts/.dotnet-cli-home but 'dotnet new' read from /tmp/helix-cli-home. After the fix, both paths share /tmp/helix-cli-home and 'dotnet new wasmbrowser' succeeds. Also adds [diag] Console.WriteLine entries logging both the install-time and invocation-time DOTNET_CLI_HOME so Helix work-item console logs make similar future regressions trivial to diagnose. Co-authored-by: Copilot <[email protected]>
- Inline callhelpers.hpp / minipal/entrypoints.h declarations into the pre-included coreclr_compat.h and stop emitting #include lines for those headers from the CoreCLR generators, so pinvoke-table.cpp and wasm_m2n_invoke.g.cpp compile on Helix where the in-repo header paths do not exist. - Make BuildEnvironment.GetRuntimePackDir flavor-aware: on CoreCLR the runtime pack is Microsoft.NETCore.App.Runtime.<rid> (no .Mono. segment). - Mark DllImportTests.NativeLibraryWithVariadicFunctions as Mono-only; CoreCLR's PInvoke generator does not emit the expected varargs warning. Co-authored-by: Copilot <[email protected]>
….targets
The previous attempt to generate coreclr_compat.h from a _CompatHeaderLines
ItemGroup hit an MSBuild item-spec dedup/parsing issue: items whose Include
value contained both `{...}` braces and embedded `%3B` semicolons were
silently dropped from the output (verified with a minimal repro). That left
the generated header with missing `StringToWasmSigThunk` and
`ReverseThunkMapEntry` struct declarations, so pinvoke-table.cpp /
wasm_m2n_invoke.g.cpp failed to compile on Helix.
Replace the inline ItemGroup generation with a static coreclr_compat.h next
to BrowserWasmApp.CoreCLR.targets and `-include` it via
$(MSBuildThisFileDirectory). The file ships into the Helix correlation
payload via the existing HelixCorrelationPayload Include="$(BrowserBuildTargetsDir)"
mapping (sendtohelix-browser.targets), so it lands at
correlation/build/wasm/coreclr_compat.h alongside the targets file.
Co-authored-by: Copilot <[email protected]>
The ICU tests override Program.cs with code that does not use JS interop, but kept the wasmbrowser template's main.js which calls setModuleImports and getAssemblyExports. With CoreCLR-Wasm, the trimmer drops System.Runtime.InteropServices.JavaScript because nothing managed roots it, and JS startup then fails when trying to bind its exports. Add a minimal icu_main.js tailored for ICU tests (no JS interop calls, runMainAndExit, diagnostic tracing + console forwarding) and have CreateIcuProject overwrite the template main.js with it. Co-authored-by: Copilot <[email protected]>
The force-include of coreclr_compat.h is required for the ManagedToNativeGenerator output (pinvoke-table.cpp, wasm_m2n_invoke.g.cpp), but it breaks user .c sources brought in via NativeFileReference: the compat header uses C++-style un-tagged struct references that don't compile in C. Split the emcc invocation into two passes with separate RSP files: - user NativeFileReference sources (.c/.cpp) without -include compat - generated cpp from ManagedToNativeGenerator with -include compat Co-authored-by: Copilot <[email protected]>
…ble-non-aot-tests # Conflicts: # eng/testing/scenarios/BuildWasmAppsJobsListCoreCLR.txt
ManagedToNativeGenerator output (wasm_m2n_invoke.g.cpp) uses NOINLINE, which is defined in src/native/minipal/utils.h. On Helix, the WBT correlation payload only had coreclr_compat.h — no NOINLINE definition — so per-app native re-link failed for newly-enabled CoreCLR WBT classes (InvariantGlobalizationTests.RelinkingWithoutAOT, DllImportTests.*). Stage src/native/minipal/utils.h into the WBT correlation payload at build/minipal and force-include it from BrowserWasmApp.CoreCLR.targets alongside coreclr_compat.h. The include directory is provided to the targets via MINIPAL_INCLUDE_DIR (HelixPreCommand on Helix, RunScriptCommands locally), with an in-repo fallback for full repo builds. Co-authored-by: Copilot <[email protected]>
IcuShardingTests2.DefaultAvailableIcuShardsFromRuntimePack publishes from the per-test NuGet feed (NUGET_PACKAGES override), but ProjectProviderBase.AssertIcuAssets compared the published shard against BuildEnvironment.GetRuntimeNativeDir(...), which resolves to the SDK-installed dotnet/packs/ copy. On CoreCLR no-workload Helix runs the two diverge and the asserts fail looking for icudt_EFIGS/CJK/no_CJK.dat in dotnet-none/packs while the publish-time pack used a different copy under wbt artifacts/nuget/.../microsoft.netcore.app.runtime.browser-wasm. Parse 'MicrosoftNetCoreAppRuntimePackDir' from MSBuild output (the existing regex used by AssertRuntimePackPath) and thread it through AssertBundle / AssertBundleOptions. AssertIcuAssets prefers that path when present, so ICU shard comparisons read from the same pack the publish output came from. For workload runs the resolved path equals GetRuntimeNativeDir, so behavior is unchanged. Co-authored-by: Copilot <[email protected]>
The CoreCLR WASM relink path in BrowserWasmApp.CoreCLR.targets references
'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.extpost.js'
as the emcc --extern-post-js input, and corehost.proj copies the file
into the local runtime pack output directory. However, the file is not
listed in the platform manifest in
src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props, so
it gets dropped when the Microsoft.NETCore.App.Runtime.browser-wasm
NuGet package is created. CI legs that consume the packed runtime pack
(e.g. Wasm.Build.Tests on Helix using the per-test NuGet feed) then fail
the link with:
emcc : error : '--extern-post-js': file not found:
.../microsoft.netcore.app.runtime.browser-wasm/11.0.0-ci/runtimes/
browser-wasm/native/libSystem.Native.Browser.extpost.js
Add the file to the manifest alongside the other libSystem.Native.Browser.*
entries so it ships with the runtime pack.
Co-authored-by: Copilot <[email protected]>
The previous commit added the file to PlatformManifestFileEntry in
Microsoft.NETCore.App/Directory.Build.props, but that only registers
the file in the platform manifest — it does not cause the file to be
included in the runtime-pack nupkg. The nupkg sources its content from
$(LibrariesSharedFrameworkDir) (artifacts/bin/native/<rid-tfm>/sharedFramework/)
via the *.js glob. Because libSystem.Native.Browser.extpost.js is a
hand-written passthrough source (not a rollup output), nothing was
installing it into that directory.
Add an explicit cmake install for the file in
src/native/libs/Common/JavaScript/CMakeLists.txt so it lands in
sharedFramework and is picked up by:
- the runtime-pack nuspec (-> ships in
Microsoft.NETCore.App.Runtime.browser-wasm.nupkg)
- corehost.proj's CopyWasmNativeFiles via its existing
$(LibrariesSharedFrameworkDir)*.js glob (-> populates the in-tree
runtime pack layout)
Drop the now-redundant explicit include in
src/native/corehost/corehost.proj that pointed at the source file
directly; it is no longer needed once the *.js glob picks it up.
Verified: rebuilt libs.native and the runtime-pack sfxproj; the
generated nupkg now contains
runtimes/browser-wasm/native/libSystem.Native.Browser.extpost.js,
which previously was missing.
Co-authored-by: Copilot <[email protected]>
WBT requires all .a archives (libBrowserHost.a, libSystem.Native.Browser.a, libicu*.a, etc.) in the runtime pack to relink with emcc per-app. - Set IncludeStaticLibrariesInPack=true for browser/wasi CoreCLR sfxproj so static libs from LibrariesRuntimeFiles are not filtered out. - Add libBrowserHost.a to the platform manifest. - Exclude duplicate libminipal.a from LibrariesSharedFrameworkDir; the CoreCLR-side libminipal.a is the one referenced by browserhost. Co-authored-by: Copilot <[email protected]>
…veLibrary present The Mono path's _SetWasmBuildNativeDefaults auto-flips WasmBuildNative=true when a project has @(NativeFileReference) items, so a project with native P/Invoke targets gets relinked into dotnet.native.wasm. The CoreCLR path had no equivalent, so apps with a NativeLibrary/NativeFileReference would build successfully but fail at runtime with 'DllNotFoundException: dynamic linking not enabled'. Add _CoreCLRSetWasmBuildNativeDefaults that flips WasmBuildNative=true when either NativeFileReference or NativeLibrary items are present. NativeLibrary is later converted to NativeFileReference in _CoreCLRPrepareForNativeBuild, so we count both at the entry-point level. Run the defaults via DependsOnTargets (rather than BeforeTargets) so the mutated WasmBuildNative is visible when _CoreCLRWasmBuildAppCore's Condition is evaluated. The outer entry-point WasmBuildNative check is removed because the inner _CoreCLRWasmBuildAppCore already gates on it. Co-authored-by: Copilot <[email protected]>
libSystem.IO.Compression.Native.a, libz.a, and libminipal.a are installed into both $(CoreCLRSharedFrameworkDir) (consumed by corehost when linking dotnet.native.js, and packed via the CoreCLRSharedFrameworkDir RuntimeFiles glob in liveBuilds.targets) and $(LibrariesSharedFrameworkDir) (packed via the browser+CoreCLR LibrariesRuntimeFiles ItemGroup). With IncludeStaticLibrariesInPack=true, both flows produce runtimes/browser-wasm/native/<file>.a, causing NuGet pack to emit NU5118 (warnings-as-errors). Extend the existing Exclude= for libminipal.a to also drop the LibrariesSharedFrameworkDir copies of libSystem.IO.Compression.Native.a and libz.a. corehost continues to consume the CoreCLRSharedFrameworkDir copy at link time, and the runtime pack ends up with exactly one copy of each archive. Co-authored-by: Copilot <[email protected]>
Both DllImportWithFunctionPointersCompilesWithoutWarning and DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithWarning use the WasmBasicTestApp main.js, which unconditionally calls getAssemblyExports on the main assembly. With CoreCLR-Wasm the trimmer drops System.Runtime.InteropServices.JavaScript because the test program does not use JS interop, so JS startup fails with Arg_TargetInvocationException out of JSHostImplementation.BindAssemblyExports. Mirror the IcuTestsBase fix and overwrite the template main.js with the minimal icu_main.js (no JS interop calls, just runMainAndExit) inside PrepareProjectForVariadicFunction. Co-authored-by: Copilot <[email protected]>
Generalize the icu_main.js fix into a shared ReplaceMainJsWithMinimalRunMain helper on WasmTemplateTestsBase, and apply it to additional CoreCLR-Wasm NoWorkload tests whose managed program does not use JS interop. Without it, the WasmBasicTestApp / wasmbrowser template main.js calls getAssemblyExports, which fails at startup with Arg_TargetInvocationException out of JSHostImplementation.BindAssemblyExports because the trimmer drops System.Runtime.InteropServices.JavaScript. Tests updated: - InvariantGlobalizationTests.RelinkingWithoutAOT (and AOT path) - NativeBuildTests.SimpleNativeBuild - NativeLibraryTests.ProjectWithNativeReference / ProjectWithNativeLibrary - PInvokeTableGeneratorTests.UCOWithSpecialCharacters - PInvokeTableGeneratorTests.BuildNativeInNonEnglishCulture Also refactor the existing inline copies in DllImportTests and IcuTestsBase to use the new helper. Co-authored-by: Copilot <[email protected]>
The test loads WasmAppBuilder.dll from the Microsoft.NET.Runtime.WebAssembly.Sdk workload pack, which is not present in the CoreCLR-Wasm NoWorkload Helix payload, so the test fails with DirectoryNotFoundException there. Restrict it to the Mono pipeline. Co-authored-by: Copilot <[email protected]>
This reverts commit 51b99d0.
CoreCLR WBT runs in NoWorkload mode but still performs per-app native relink via BrowserWasmApp.CoreCLR.targets (imported by data/Local.Directory.Build.targets). AssertBuildBundle was forcing expectedFileType=FromRuntimePack whenever isUsingWorkloads was false, causing it to look for dotnet.native.* in fx/_framework/ instead of obj/.../wasm/for-build/ where the CoreCLR relink actually places them. Treat CoreCLR as having native-rebuild capability regardless of isUsingWorkloads so GetExpectedFileType (which honors the test-supplied isNativeBuild hint) is consulted. This matches the existing CoreCLR detection pattern already used in this file for dotnet.diagnostics.js. Co-authored-by: Copilot <[email protected]>
When InvariantGlobalization=true, the CoreCLR app re-link omits the ICU static libraries (libSystem.Globalization.Native.a, libicu*.a), so wasm_load_icu_data is undefined. libBrowserHost.footer.js was listing it in explicitDeps, which caused emcc to emit --export=wasm_load_icu_data and wasm-ld to fail with: wasm-ld : error : undefined symbol: wasm_load_icu_data. Required by BrowserHost_ExternalAssemblyProbe The loadIcuData() JS code path only runs when ICU data is actually present, so the symbol doesn't need to be a hard link-time dependency. Drop it from explicitDeps so invariant-globalization apps relink. Cherry-picked from dotnet#127906. Co-authored-by: Copilot <[email protected]>
NativeLibraryTests were shipping the pristine runtimepack dotnet.native.wasm instead of the per-app relinked one, so user pinvokes (e.g. print_line in AppUsingNativeLib) failed at runtime with DllNotFoundException. Two issues: 1) Without WasmBuildOnlyAfterPublish, WasmBuildApp ran a build-phase relink into for-build/ before ManagedToNativeGenerator had the publish-phase pinvoke set, baking a dotnet.native.wasm with a pinvoke-table.cpp missing the user entries. Mirror Mono's auto-default in BrowserWasmApp.CoreCLR.targets. 2) The WebAssembly SDK's _WasmNative target forces WasmTriggerPublishApp to run BeforeTargets=ProcessPublishFilesForWasm so the for-publish/ relink output is the WasmNativeAsset that ComputeWasmPublishAssets picks up. It is gated on UsingBrowserRuntimeWorkload == true, which is false in the CoreCLR no-workload flow, so the nested publish only ran AfterTargets= Publish - too late, and the runtimepack wasm was published instead. Add an equivalent _CoreCLRWasmNative trigger in the CoreCLR targets. Also align WasmTriggerPublishApp/WasmNestedPublishApp with the Mono pattern: use OriginalItemName metadata to discriminate WasmAssembliesFinal vs WasmNativeAsset vs FileWrites returned from the nested publish, instead of the .dll/non-.dll heuristic that misclassified satellite resource .wasm files as native assets. Co-authored-by: Copilot <[email protected]>
The CoreCLR ManagedToNativeGenerator emits pinvoke-table.cpp (see BrowserWasmApp.CoreCLR.targets), while Mono emits pinvoke-table.h. The test hard-coded .h and failed with FileNotFoundException on the CoreCLR-Wasm Helix leg. Co-authored-by: Copilot <[email protected]>
Define _WasmNativeForBuild in BrowserWasmApp.CoreCLR.targets so it chains _GatherWasmFilesToBuild -> WasmBuildApp. The WebAssembly SDK's own _WasmNativeForBuild is gated on UsingBrowserRuntimeWorkload, which we don't want to flip on globally because it makes the SDK demand the wasm-tools workload and breaks NoWorkload Wasm.Build.Tests scenarios. Overriding the target avoids that gating while still ensuring WasmAssembliesToBundle is populated for the CoreCLR M2N generator. Co-authored-by: Copilot <[email protected]>
Populate WasmAssembliesToBundle from @(IntermediateAssembly) and @(ReferenceCopyLocalPaths) before _CoreCLRGenerateManagedToNative runs, so the M2N generator scans the app's own assemblies (not just CoreLib). This fixes pinvoke / UCO blittability diagnostics that depend on inspecting types declared in the app or its referenced libraries. Co-authored-by: Copilot <[email protected]>
NativeFileReference items with .c/.cpp extensions are routed to _WasmSourceFileToCompile and only added to _WasmNativeFileForLinking after M2N generation. Add their FileName directly so the pinvoke table includes their exported entries. Co-authored-by: Copilot <[email protected]>
UnmanagedCallersOnly callbacks under [DisableRuntimeMarshalling] may have non-blittable struct parameters (e.g. LayoutKind.Auto multi-field). MapType already maps these to void* in the emitted C prototype, but FixedSymbolName threw InvalidSignatureCharException when SignatureMapper produced no token. Fall back to the by-ref-struct token (IND) for any value type that doesn't otherwise resolve, giving callbacks a stable symbol name. Co-authored-by: Copilot <[email protected]>
This was referenced May 17, 2026
- Add .NET Foundation MIT license header to coreclr_compat.h - Remove unused destructuring/variable from icu_main.js Co-authored-by: Copilot <[email protected]>
maraf
commented
May 17, 2026
…imal_main.js - Drop ConditionalTheory(IsMonoRuntime) on InvariantGlobalization tests; TestCategory=native already excludes them from the CoreCLR lane. - Flip RelinkingWithoutAOT back to TestCategory=native so it runs on Mono only. - Rename icu_main.js to minimal_main.js (the file is not ICU-specific). Co-authored-by: Copilot <[email protected]>
pavelsavara
reviewed
May 18, 2026
| <_EmccCFlags Include="-I"$(RepoRoot)src/coreclr/vm/wasm"" Condition="Exists('$(RepoRoot)src/coreclr/vm/wasm/callhelpers.hpp')" /> | ||
| <_EmccCFlags Include="-I"$(RepoRoot)src/native"" Condition="Exists('$(RepoRoot)src/native/minipal/entrypoints.h')" /> | ||
| <_EmccCFlags Include="-include "$(_WasmIntermediateOutputPath)coreclr_compat.h"" /> | ||
| <_VmWasmIncludeDir Condition="'$(CORECLR_VM_WASM_INCLUDE_DIR)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$(CORECLR_VM_WASM_INCLUDE_DIR)'))))</_VmWasmIncludeDir> |
Member
There was a problem hiding this comment.
What kind of restructuring we would need to do that we don't have to ship minpal etc with workload ?
Is coreclr_compat.h going away after R2R will generate the entrypoints ?
Member
Author
There was a problem hiding this comment.
Yes, all of these are needed only for the ManagedToNative generator. So temporal solution. That was my motivation to make it only a WBT payload
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.
Note
This PR description was AI-generated (GitHub Copilot CLI).
Follow-up to #127073. Plumbs the CoreCLR-on-WASM publish-time native re-link end-to-end so apps without the WebAssembly workload can rebuild
dotnet.native.wasmagainst their own[UnmanagedCallersOnly]/NativeFileReference/NativeLibraryinputs, and then enables theWasm.Build.Tests(WBT) classes that exercise that path on CoreCLR.Runtime pack & Helix payload
src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxprojIncludeStaticLibrariesInPack=trueon browser/wasi so.afiles ship in the runtime pack and apps can relink withemcc.src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.propslibBrowserHost.aandlibSystem.Native.Browser.extpost.jsto the platform manifest.eng/liveBuilds.targetslibminipal.a,libSystem.IO.Compression.Native.a,libz.afrom the runtime-tests browser-host file set (they're libraries content, not host content).src/libraries/sendtohelix-browser.targetssrc/native/minipal/{utils,entrypoints}.handsrc/coreclr/vm/wasm/callhelpers.hppinto the Helix correlation payload (underbuild/minipal/andbuild/coreclr-vm-wasm/), and exposeMINIPAL_INCLUDE_DIR/CORECLR_VM_WASM_INCLUDE_DIRso per-app relink can resolve the#includes emitted byManagedToNativeGenerator.src/native/corehost/corehost.proj,src/native/corehost/browserhost/libBrowserHost.footer.js,src/native/libs/Common/JavaScript/CMakeLists.txtBrowserWasmApp.CoreCLR.targetsrework_CoreCLRSetWasmBuildNativeDefaults: auto-enableWasmBuildNativewhen the project hasNativeFileReferenceorNativeLibraryitems (mirrors a subset of the Mono path's_SetWasmBuildNativeDefaults)._CoreCLRWasmNativeForBuild/_CoreCLRWasmNative: force the build-phase / publish-phase relink to run before_ResolveWasmOutputs/ProcessPublishFilesForWasmin CoreCLR no-workload mode (the SDK's_WasmNativeForBuild/_WasmNativeare gated onUsingBrowserRuntimeWorkload=true).WasmBuildOnlyAfterPublishdefault flipped totrueduring publish so a stalefor-build/dotnet.native.wasm(whose pinvoke-table is generated before the user assembly is available) doesn't get staged into the publish bundle.WasmNestedPublishApp) now returnsWasmAssembliesFinal/WasmNativeAsset/FileWriteswith anOriginalItemNamemetadata key, andWasmTriggerPublishApproutes results back by that key rather than by file extension.DeployOnBuild/_IsPublishingare cleared on the nested invocation.emcc-compile.rspfor userNativeFileReference.c/.cppsources — no force-included compat header (would break C sources).emcc-compile-generated.rspforManagedToNativeGeneratoroutput (pinvoke-table.cpp,wasm_m2n_invoke.g.cpp) — force-includescoreclr_compat.h, adds-Ifor minipal andcoreclr/vm/wasm, both env-var-overridable._WasmPInvokeModulesnow also include user.c/.cppsource filenames (so pinvoke scanning finds them).New
src/mono/browser/build/coreclr_compat.hReplaces the previous inline-generated stub header in
_CoreCLRWriteCompileRsp. Provides the minimal prerequisite types (MethodDesc/PCODE/ULONG/INTERP_STACK_SLOT_SIZE) and logging/assertion stubs (LF_INTEROP,LL_INFO1000,LOG,PORTABILITY_ASSERT) that the real CoreCLR headers (<callhelpers.hpp>,<minipal/entrypoints.h>) assume are already in scope fromvm/common.h. The generated.cppfiles keep including the real headers directly.Wasm.Build.Tests infrastructure
Common/BuildEnvironment.csMicrosoft.NETCore.App.Runtime.{rid}(no flavor segment) for CoreCLR; propagateMINIPAL_INCLUDE_DIR/CORECLR_VM_WASM_INCLUDE_DIRenv vars to child builds.Common/EnvironmentVariables.csWasm.Build.Tests.csprojcategory=native,category=mono,category=workloadon CoreCLR.BrowserStructures/AssertBundleOptions.csRuntimePackDiroption so ICU asset assertions can resolve sources from the build-resolved runtime pack instead of a workload-installed pack.ProjectProviderBase.cs,WasmSdkBasedProjectProvider.cs,Templates/WasmTemplateTestsBase.csAssertBundleOptions, and addReplaceMainJsWithMinimalRunMainhelper for tests whose program doesn't use JS interop (otherwise the trimmer drops the JS interop assembly and the templatemain.js'sgetAssemblyExportscall fails at startup).data/RunScriptTemplate.shEnabled WBT classes on CoreCLR
eng/testing/scenarios/BuildWasmAppsJobsListCoreCLR.txtadds 9 classes; xunit merges class-level and method-level traits, so each class drops its class-level[TestCategory("native")]and re-applies a sub-category per method:NativeBuildTestsAOTNotSupportedWithNoTrimming,IntermediateBitcodeToObjectFilesAreNotLLVMIR,NativeBuildIsRequired(AOT-only).DllImportTestsPInvokeTableGeneratorTestsEnsureWasmAbiRulesAreFollowedInAOT,EnsureComInteropCompilesInAOT.UnmanagedCallersOnly_NamespacedandIcallWithOverloadedParametersAndEnumkept as[TestCategory("mono")](reverse-thunk linking /WasmAppBuilder.dllworkload pack not yet available on CoreCLR).EnsureWasmAbiRulesAreFollowedreadspinvoke-table.cppon CoreCLR vspinvoke-table.hon Mono, with format-specific assertion.NativeLibraryTests[BuildAndRun(aot:false)]and[BuildAndRun(aot:true)]into a non-AOT method and an_AOTmethod taggednative, delegating to a shared…Corehelper. The pre-existing[ActiveIssue(#103566)]onProjectUsingSkiaSharpis applied to both halves.IcuShardingTests,IcuShardingTests2,IcuTestsnativeasnative-coreclrso they run on CoreCLR.IcuTestsBaseReplaceMainJsWithMinimalRunMain(ICU test program doesn't use JS interop).InvariantGlobalizationTests[ConditionalTheory(... IsMonoRuntime)]on both theories — gated on CoreCLR until #128219 (libSystem.Globalization.Native stub) is resolved;RelinkingWithoutAOTre-taggednative-coreclr; usesReplaceMainJsWithMinimalRunMain.MemoryTestsnative-coreclr.Splitting (rather than runtime-skip) keeps test reporting honest: AOT-on-CoreCLR shows up as "excluded by filter" instead of a false pass.
Test assets
src/mono/wasm/testassets/EntryPoints/PInvoke/{AbiRules,BittableDifferentAssembly,BittableDifferentAssembly_Lib,BittableSameAssembly}.cs— adjustments so the existing PInvoke ABI tests compile/run under the CoreCLR-WASM pinvoke-table layout.src/mono/wasm/testassets/EntryPoints/icu_main.js(new) — minimal main.js for ICU test apps that don't use JS interop.src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs— tracks the test-side expectations.Not in scope: other
[TestCategory("native")]classes in WBT (BuildPublishTests,NativeRebuildTests/*,WasmSIMDTests,SatelliteAssembliesTests, …) — those encode reasons beyond "class-levelnativeis too broad" and are left for a follow-up.Validation
./dotnet.sh build src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj -c Release /p:TargetOS=browser /p:RuntimeFlavor=CoreCLR /p:Scenario=BuildWasmApps→ 0W/0E.