Skip to content

[browser] CoreCLR in-tree relink#125607

Draft
maraf wants to merge 16 commits intomainfrom
maraf/WasmCoreCLRNativeBuild
Draft

[browser] CoreCLR in-tree relink#125607
maraf wants to merge 16 commits intomainfrom
maraf/WasmCoreCLRNativeBuild

Conversation

@maraf
Copy link
Member

@maraf maraf commented Mar 16, 2026

No description provided.

maraf and others added 11 commits February 24, 2026 09:16
Implement per-app native relinking via emcc for CoreCLR browser-wasm,
matching the capability Mono already has. Unlike Mono, CoreCLR has no C
source files in the runtime pack, so the native build is link-only.

Key changes:
- BrowserWasmApp.CoreCLR.targets: Complete implementation replacing stubs
  - Emscripten toolchain setup (_CoreCLRSetupEmscripten, _CoreCLRSetupToolchain)
  - Build native defaults (WasmBuildNative=false for build, true for publish)
  - emcc link target with all CoreCLR .a libraries + JS library files
  - Heap size calculation (default 128MB matching CMakeLists.txt)
  - WasmBuildApp/WasmTriggerPublishApp/WasmNestedPublishApp target chain
  - wasm-opt post-link optimization
- corehost.proj: Copy libSystem.Native.Browser.extpost.js to runtime pack

The implementation is self-contained and does not import WasmApp.Common.targets
or BrowserWasmApp.targets. It reuses MSBuild task assemblies via UsingTask.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When WasmBuildNative=true, re-link dotnet.native.wasm from CoreCLR static
libraries using emcc, allowing apps to include custom native code via
NativeFileReference items.

The implementation:
- Imports eng/native.wasm.targets for shared Emscripten/ICU/export properties
- Sets up Emscripten toolchain and environment
- Compiles user native sources (NativeFileReference .c/.cpp) with EmccCompile
- Links all CoreCLR .a libraries + user objects into dotnet.native.wasm
- Handles both build and publish (nested publish) flows
- Link flags mirror src/native/corehost/browserhost/CMakeLists.txt
- Gates ICU libraries on InvariantGlobalization
- Supports WasmEnableExceptionHandling, WasmEnableSIMD, custom EmccFlags

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Integrate the CoreCLR ManagedToNativeGenerator MSBuild task into the
native re-link pipeline. This generates P/Invoke dispatch tables,
reverse P/Invoke tables, and interpreter-to-native thunks that are
compiled and linked into dotnet.native.wasm.

Key changes:
- Add _CoreCLRGenerateManagedToNative target that scans managed
  assemblies for P/Invoke signatures and generates C++ source files
- Generate coreclr_compat.h shim header providing type/macro stubs
  (MethodDesc, PCODE, ULONG, LOG, PORTABILITY_ASSERT) so generated
  files compile outside the full CoreCLR build context
- Use .cpp extensions for generated files (they contain extern "C",
  namespaces, and other C++ constructs)
- Fix CoreLib discovery to check native/ dir first (CoreCLR WASM
  ships CoreLib there, not in lib/net11.0/)
- Fix UsingTask assembly paths: EmccCompile and ManagedToNativeGenerator
  both live in WasmAppBuilder.dll, not WasmBuildTasks.dll

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Don't compile reverse-pinvoke-table.cpp separately to avoid duplicate
  symbols with callhelpers-reverse.cpp.o in libcoreclr_static.a
- Condition LLD_REPORT_UNDEFINED and ERROR_ON_UNDEFINED_SYMBOLS to Debug
  only, matching CMakeLists.txt behavior
- Add Dependencies metadata to generated source files for incremental builds
- Use node$(_ExeExt) for cross-platform compat
- Reorder link libraries: libBrowserHost.a first, matching CMakeLists.txt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t string

- Remove MAXIMUM_MEMORY from _EmccCommonFlags (compile+link shared flags);
  it only belongs in the link step's _EmccLinkStepArgs
- Fix PORTABILITY_ASSERT fprintf format: use %25s MSBuild encoding to
  produce correct %s printf format specifier

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…jection

- Add SimpleNativeBuildForCoreCLR test that publishes WasmBrowserRunMainOnly
  with WasmBuildNative=true, verifying the CoreCLR native build pipeline works
- Add CoreCLR property injection to CreateWasmTemplateProject (was missing,
  only CopyTestAsset had it), enabling template-based tests on CoreCLR
- Add UsingBrowserRuntimeWorkload=false to both CoreCLR injection blocks to
  bypass the wasm-tools workload check (NETSDK1147) that fires when
  WasmBuildNative=true triggers _WasmNativeWorkloadNeeded

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@maraf maraf added this to the 11.0.0 milestone Mar 16, 2026
@maraf maraf self-assigned this Mar 16, 2026
@maraf maraf added arch-wasm WebAssembly architecture area-Build-mono labels Mar 16, 2026
Copilot AI review requested due to automatic review settings March 16, 2026 08:40
@maraf maraf added the os-browser Browser variant of arch-wasm label Mar 16, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

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 enables CoreCLR browser-wasm per-app native re-linking (via emcc) in-tree, aligning CoreCLR’s WASM app pipeline more closely with Mono’s ability to incorporate app-provided native code (NativeFileReference). It also adjusts WASM build tests to target the CoreCLR browser runtime packs/SDK packs and adds research notes documenting the build pipeline.

Changes:

  • Copy libSystem.Native.Browser.extpost.js into the CoreCLR browser runtime pack native directory for app relinking.
  • Replace the CoreCLR WASM targets stub with a full WasmBuildApp / nested publish relink pipeline using emcc + ManagedToNativeGenerator.
  • Update WASM template/build tests to use CoreCLR settings and force the right runtime/workload pack versions; add supporting build analysis docs under .research/.

Reviewed changes

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

Show a summary per file
File Description
src/native/corehost/corehost.proj Adds extpost.js to the files copied into the runtime pack for CoreCLR browser-wasm.
src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs Adjusts template/test-asset project generation to use CoreCLR runtime settings and override known pack versions.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets Implements CoreCLR WASM native relink (compile generated sources + link with emcc) for build and publish.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets.backup Adds a backup copy of the CoreCLR targets file.
.research/wasm-native-build-binlog-analysis.md Documents WASM native build behavior inferred from binlogs.
.research/wasm-native-build-analysis.md Expanded WASM native build pipeline analysis and comparison notes.
.research/runtime-native-build-analysis.md Compares Mono vs CoreCLR runtime native build behavior/timings.
.research/native-build-analysis.md Summarizes timings and phases for WasmBuildNative=true scenarios.
.research/build-wasm-browser-sample.md Documents steps and properties for building the CoreCLR WASM browser sample in-tree.

You can also share your feedback on Copilot code review. Take the survey.

<!-- Symbol handling (strict undefined checks in Debug only, matching CMakeLists.txt) -->
<_EmccLinkStepArgs Condition="'$(Configuration)' == 'Debug'" Include="-s LLD_REPORT_UNDEFINED" />
<_EmccLinkStepArgs Condition="'$(Configuration)' == 'Debug'" Include="-s ERROR_ON_UNDEFINED_SYMBOLS=1" />
<_EmccLinkStepArgs Include="--emit-symbol-map" />
<_MicrosoftNetCoreAppRuntimePackNativeDirFiles Include="$(HostSharedFrameworkDir)dotnet.native.js.symbols" />
<_MicrosoftNetCoreAppRuntimePackNativeDirFiles Include="$(HostSharedFrameworkDir)dotnet.native.wasm" />
<!-- extpost.js is needed for per-app native relinking via emcc -->
<_MicrosoftNetCoreAppRuntimePackNativeDirFiles Include="$(MSBuildThisFileDirectory)..\..\libs\System.Native.Browser\libSystem.Native.Browser.extpost.js" />
DependsOnTargets="GenerateEmccExports">

<PropertyGroup>
<_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]"</_EmccExportedRuntimeMethods>
Comment on lines +302 to +304
<!-- Locate System.Private.CoreLib in runtime pack if not already bundled -->
<PropertyGroup>
<_HasCoreLib Condition="'%(_WasmManagedAssemblies.FileName)%(_WasmManagedAssemblies.Extension)' == 'System.Private.CoreLib.dll'">true</_HasCoreLib>
Copilot AI review requested due to automatic review settings March 16, 2026 10:42
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

Adds CoreCLR browser-wasm “in-tree relink” support so apps can re-link dotnet.native.wasm with emcc (e.g., to include custom native code), and adjusts build/test plumbing to support that flow.

Changes:

  • Copy libSystem.Native.Browser.extpost.js into the CoreCLR browser-wasm runtime pack native directory for emcc relinking.
  • Implement CoreCLR-specific WasmBuildApp/publish targets to compile/link native inputs with emcc and register outputs.
  • Refactor WASM template tests to consistently inject CoreCLR-specific project properties/items.

Reviewed changes

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

Show a summary per file
File Description
src/native/corehost/corehost.proj Copies libSystem.Native.Browser.extpost.js into the runtime pack output.
src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs Centralizes CoreCLR-specific project property/item injection for template/test assets.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets Replaces stub with a full CoreCLR emcc compile/link pipeline + nested publish support.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets.backup Adds a backup copy of targets content (appears non-production).
.research/*.md Adds analysis notes/binlog investigations for WASM native build/relink flows.

You can also share your feedback on Copilot code review. Take the survey.

DependsOnTargets="GenerateEmccExports">

<PropertyGroup>
<_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]"</_EmccExportedRuntimeMethods>
<_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' == 'true'" />
<_EmccCommonFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" />
<_EmccCommonFlags Include="-fwasm-exceptions" Condition="'$(WasmEnableExceptionHandling)' == 'true'" />
<_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" />
</ItemGroup>

<!-- Include user JS files marked explicitly for native link -->
<ItemGroup>
Comment on lines +1 to +6
<Project>
<!--
CoreCLR WASM native build targets.

Unlike Mono, CoreCLR has no C source files in the runtime pack to compile per-app.
The native build is link-only: emcc links pre-built .a static libraries + JS library
Comment on lines +148 to +150
<ItemGroup Condition="'$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)' == ''">
<_SystemRuntimePathItem Include="$(MicrosoftNetCoreAppRuntimePackRidDir)lib$(WasmDirSep)net*$(WasmDirSep)System.Runtime.dll" />
</ItemGroup>
On Windows, the EmccCompile task's CompilerBinaryPath was set to bare
'emcc', which relies on the PATH environment variable to resolve. However,
the PATH constructed in _CoreCLRSetupEmscripten was fragmented by MSBuild
item-separator splitting on semicolons, so only the emsdk root directory
ended up in PATH — not the emscripten subdirectory where emcc lives.

Fix by setting WasmClang to an absolute path using
EmscriptenUpstreamEmscriptenPath, matching the existing pattern in the
Mono targets (BrowserWasmApp.targets line 173). Also update the link
step Exec command to use $(WasmClang) instead of bare 'emcc'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
   1. WasmTriggerPublishApp — removed the WasmBuildNative == 'true' gate so the publish pipeline runs for all browser-wasm projects (matching Mono behavior)
   2. WasmNestedPublishApp — switched from hardcoded ComputeFilesToPublish to $(_WasmNestedPublishAppPreTarget);$(WasmNestedPublishAppDependsOn) so that PrepareForWasmBuildApp and _GatherWasmFilesToPublish run (setting WasmMainJSPath, populating StaticWebAssets)
   3. _CoreCLRWasmBuildAppCore — added WasmBuildNative == 'true' condition so the emcc native linking is skipped when not needed, but the rest of the publish pipeline still runs
Copilot AI review requested due to automatic review settings March 17, 2026 13:48
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 adds an in-tree MSBuild pipeline for CoreCLR browser-wasm per-app native re-linking (via emcc), and wires in the missing JS asset required for the link step. It also updates WASM build tests to configure CoreCLR-specific framework/workload properties and includes research notes documenting the existing WASM native build pipeline.

Changes:

  • Package libSystem.Native.Browser.extpost.js into the browser-wasm runtime pack so CoreCLR app re-link can use it.
  • Replace the CoreCLR browser-wasm stub targets with a full compile/link pipeline that can consume NativeFileReference and re-link dotnet.native.*.
  • Refactor WASM template test setup to apply CoreCLR-specific project properties consistently; add .research/* build analysis docs.

Reviewed changes

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

Show a summary per file
File Description
src/native/corehost/corehost.proj Adds libSystem.Native.Browser.extpost.js to the set of runtime-pack native files copied for browser-wasm.
src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs Refactors CoreCLR-specific msbuild property/item injection into a helper and applies it for template and test-asset projects.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets.backup Adds a full CoreCLR WASM targets file as a “backup” copy.
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets Implements the CoreCLR browser-wasm native compile/link/relink target chain (replacing the previous stub).
.research/wasm-native-build-binlog-analysis.md New documentation: binlog-based WASM native build pipeline analysis.
.research/wasm-native-build-analysis.md New documentation: WASM native build analysis and comparison notes.
.research/runtime-native-build-analysis.md New documentation: runtime CI native build analysis for Mono vs CoreCLR.
.research/native-build-analysis.md New documentation: native-build timing/target breakdown for WASM.
.research/build-wasm-browser-sample.md New documentation: steps and notes for building the CoreCLR WASM browser sample.

You can also share your feedback on Copilot code review. Take the survey.

DependsOnTargets="GenerateEmccExports">

<PropertyGroup>
<_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]"</_EmccExportedRuntimeMethods>
Comment on lines +1 to +15
<Project>
<!--
CoreCLR WASM native build targets.

Unlike Mono, CoreCLR has no C source files in the runtime pack to compile per-app.
The native build is link-only: emcc links pre-built .a static libraries + JS library
files to produce dotnet.native.wasm + dotnet.native.js.

This file is self-contained and does NOT import WasmApp.Common.targets or BrowserWasmApp.targets.
It reuses MSBuild tasks (EmccCompile, WasmCalculateInitialHeapSize) via UsingTask declarations.
-->

<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.WasmCalculateInitialHeapSize" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" TaskFactory="TaskHostFactory" />

<PropertyGroup>
@maraf
Copy link
Member Author

maraf commented Mar 19, 2026

Checked build failure analysis

Single error in the entire build (21 min total):

Error: Manifest file at
'artifacts/obj/WebAssembly.Browser.RuntimeConfig.Test/Release/net11.0/browser-wasm/staticwebassets.build.json'
not found.

Failing project: src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig/WebAssembly.Browser.RuntimeConfig.Test.csproj

Failing target: _CleanupReferencedProjectItemGroups (in Microsoft.NET.Sdk.StaticWebAssets.References.targets:16)

Root cause: The test project references a static web assets project (likely WasmAppHost), but the build manifest for the WebAssembly.Browser.RuntimeConfig.Test project was never generated. This typically happens when:

  1. The WASM test project is being built in a configuration (e.g., linux-x64 Checked) where the browser-wasm output was never produced
  2. A dependency ordering issue — the referenced project's static web assets weren't built before this project tried to resolve them

The successful WasmAppHost manifest lives under artifacts/obj/mono/WasmAppHost/browser.wasm.Checked/, but the test expects its own at artifacts/obj/WebAssembly.Browser.RuntimeConfig.Test/Release/net11.0/browser-wasm/. This suggests the test is being published/resolved in a
context where the WASM static web asset pipeline hasn't run for this specific test project.

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