Skip to content

[browser] CoreCLR in-tree relink#126946

Open
maraf wants to merge 2 commits intomainfrom
maraf/WasmCoreCLRNativeBuild-squashed
Open

[browser] CoreCLR in-tree relink#126946
maraf wants to merge 2 commits intomainfrom
maraf/WasmCoreCLRNativeBuild-squashed

Conversation

@maraf
Copy link
Copy Markdown
Member

@maraf maraf commented Apr 15, 2026

Clean PR for original #125607

Summary

Implements the WASM native re-link pipeline for CoreCLR browser-wasm, replacing the stub WasmBuildApp / WasmTriggerPublishApp targets with a full Emscripten-based native build. This allows CoreCLR WASM apps to include custom native code via NativeFileReference items by re-linking dotnet.native.wasm from the CoreCLR static libraries shipped in the runtime pack.

Changes

src/mono/browser/build/BrowserWasmApp.CoreCLR.targets (+612 lines)

The core of this PR. Replaces the two stub targets with a complete native re-link pipeline:

  • Properties: Sets IsBrowserWasmProject, TargetsBrowser, forces WasmEnableExceptionHandling=true and WasmEnableSIMD=true (CoreCLR requires both), configures emcc as the compiler.

  • Entry points: WasmBuildApp (after Build) and WasmTriggerPublishApp (after Publish) with nested-publish support matching the Mono pattern.

  • Orchestrator: _CoreCLRWasmBuildAppCore chains the pipeline stages:

    1. _CoreCLRWasmInitialize — validates prerequisites, resolves runtime pack paths, creates intermediate directories.
    2. _CoreCLRSetupEmscripten — locates the Emscripten SDK (workload or EMSDK_PATH), sets environment variables for emcc.
    3. _CoreCLRPrepareForNativeBuild — resolves optimization flags, collects NativeFileReference items, builds compile flags (always includes -fwasm-exceptions -msimd128).
    4. _CoreCLRGenerateManagedToNative — runs ManagedToNativeGenerator to produce P/Invoke and interp-to-native tables from managed assemblies.
    5. _CoreCLRWriteCompileRsp — generates a coreclr_compat.h header with type/macro stubs (MethodDesc, PCODE, LOG, PORTABILITY_ASSERT, etc.) so ManagedToNativeGenerator output compiles outside the full CoreCLR build context. Writes the compile response file.
    6. _CoreCLRCompileNativeSources — invokes EmccCompile on user sources and generated tables.
    7. _CoreCLRWriteLinkRsp — builds linker arguments mirroring browserhost/CMakeLists.txt: CoreCLR static libraries (libBrowserHost.a, libcoreclr_static.a, libcoreclrpal.a, etc.), JS libraries, ES6 module settings, memory configuration, exported functions/runtime methods.
    8. _CoreCLRLinkNative — invokes emcc with the link response file, producing dotnet.native.js, dotnet.native.wasm, and symbol maps.
    9. _CoreCLRCompleteNativeBuild — replaces pre-built runtime pack native assets with the re-linked versions.
    10. _CoreCLREmitAssembliesFinal — emits the final managed assembly list with satellite assembly handling.
  • Validates that users cannot disable EH or SIMD (errors out with a clear message).

  • Supports incremental builds via Inputs/Outputs on the link target.

  • Supports Debug/Release optimization flags (-O0/-O1/-O2).

  • Respects InvariantGlobalization and InvariantTimezone to skip ICU/timezone libraries.

eng/native.wasm.targets (+5/-1)

Adds Condition="'$(IsBrowserWasmProject)' != 'true'" to the ICU and timezone NuGet PackageReference items. During app-level relink the runtime pack already contains the pre-built native files; pulling in these packages at app build time would inject their contentFiles (timezone data files, READMEs) into the app's Content items, causing VFS / StaticWebAssets validation failures.

src/native/corehost/corehost.proj (+2)

Adds libSystem.Native.Browser.extpost.js to the list of files copied into the runtime pack's native directory. This JS file is required by the emcc linker during per-app native relinking (it's referenced as --extern-post-js in the link arguments).

src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs (+60)

Adds EnsureXHarnessAvailable() — a thread-safe helper that runs dotnet tool restore once per test process when XHARNESS_CLI_PATH is not set (local dev scenario). Prevents test failures caused by missing xharness CLI when running browser tests locally.

src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs (+39/-28)

  • Extracts the CoreCLR-specific project property injection (version overrides, UseMonoRuntime=false, UsingBrowserRuntimeWorkload=false, KnownFrameworkReference/KnownWebAssemblySdkPack updates) into a reusable AddCoreClrProjectProperties() method.
  • Calls this method from both CreateWasmTemplateProject and CopyTestAsset, fixing a gap where template-created projects were missing the CoreCLR properties.
  • Calls EnsureXHarnessAvailable() before browser test runs.

Architecture

The pipeline mirrors the existing Mono WASM native build but is adapted for CoreCLR's different static library set and requirements:

App Build/Publish
  → WasmBuildApp / WasmTriggerPublishApp
    → _CoreCLRWasmBuildAppCore
      → Initialize (validate runtime pack)
      → Setup Emscripten (SDK paths, env vars)
      → Prepare (flags, NativeFileReference)
      → ManagedToNativeGenerator (P/Invoke tables)
      → Write compile RSP + compat header
      → EmccCompile (user sources + generated tables)
      → Write link RSP (mirrors CMakeLists.txt)
      → emcc link → dotnet.native.{js,wasm}
      → Replace runtime pack assets with re-linked output

Key differences from Mono WASM native build

  • EH & SIMD always on: CoreCLR WASM requires -fwasm-exceptions and -msimd128; user cannot disable them.
  • Different static libraries: Links libBrowserHost.a, libcoreclr_static.a, libcoreclrpal.a, etc. instead of libmonosgen-2.0.a.
  • Compat header: Generates coreclr_compat.h with type stubs so ManagedToNativeGenerator output (which references CoreCLR internals like MethodDesc, PCODE) compiles in the app build context without the full CoreCLR source tree.
  • Link flags: Mirror src/native/corehost/browserhost/CMakeLists.txt rather than the Mono wasm build.

Contributes to #123670
Contributes to #126100

Copilot AI review requested due to automatic review settings April 15, 2026 11:28
@maraf maraf requested a review from akoeplinger as a code owner April 15, 2026 11:28
Implements the WASM native re-link pipeline for CoreCLR browser-wasm,
replacing the stub WasmBuildApp / WasmTriggerPublishApp targets with a
full Emscripten-based native build.

Contributes to #123670
Contributes to #126100

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@maraf maraf force-pushed the maraf/WasmCoreCLRNativeBuild-squashed branch from d2627a4 to d7a9bc5 Compare April 15, 2026 11:34
Copy link
Copy Markdown
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

Implements an in-tree CoreCLR browser-wasm native re-link pipeline (emcc-based) so apps can re-link dotnet.native.wasm against CoreCLR static libs and include custom native code via NativeFileReference.

Changes:

  • Replaces stub CoreCLR WASM app targets with a full native compile/link pipeline driven by emcc response files.
  • Adjusts build/pack targets to support relink inputs (native assets + JS extern-post-js) and avoid ICU/timezone NuGet content pollution during app-level relink.
  • Updates WASM build tests to better support CoreCLR template projects and ensure xharness availability for local runs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets Adds the CoreCLR browser-wasm native relink MSBuild pipeline (initialize → generate → compile → link → register assets).
eng/native.wasm.targets Skips ICU/timezone package refs for app-level relink and adjusts multithreading CMake arg logic.
src/native/corehost/corehost.proj Adds runtime-pack copy of libSystem.Native.Browser.extpost.js and new host build CMake arg wiring.
src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs Adds a one-time tool-restore helper to ensure xharness is present locally.
src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs Refactors CoreCLR-specific template property injection; ensures xharness availability; improves Blazor run readiness.
src/mono/browser/build/WasmApp.InTree.props Imports shared WASM props for CoreCLR and sets CoreCLR defaults (incl. WasmBuildNative).
src/mono/sample/wasm/Directory.Build.props Ensures RuntimeFlavor is set early for in-tree sample builds (nested publish correctness).
Comments suppressed due to low confidence (3)

src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs:236

  • BootConfigFileName is part of MSBuildOptions and is used by tests (e.g., ModuleConfigTests.OverrideBootConfigName), but the MSBuild invocation no longer adds -p:WasmBootConfigFileName=... when this option is set. This makes the option ineffective and will likely break those tests. Reintroduce passing the property (while still omitting the implicit default when null).

        buildOptions.ExtraBuildEnvironmentVariables["TreatPreviousAsCurrent"] = "false";

        (CommandResult res, string logFilePath) = BuildProjectWithoutAssert(configuration, info.ProjectName, buildOptions);

src/native/corehost/corehost.proj:92

  • $(BuildNativeHostTests.ToUpper()) / $(BuildNativeHostProduct.ToUpper()) is interpreted by MSBuild as a property name containing dots (not a string method call), so this will likely expand to empty and produce -DCLR_CMAKE_BUILD_HOST_TESTS= / -D...PRODUCT=. Use an MSBuild property function instead (e.g., $([System.String]::Copy('$(BuildNativeHostTests)').ToUpperInvariant())) or pass the lowercase value if CMake accepts it.
      <BuildArgs>$(BuildArgs) -cmakeargs "-DCLR_CMAKE_BUILD_HOST_TESTS=$(BuildNativeHostTests.ToUpper())"</BuildArgs>
      <BuildArgs>$(BuildArgs) -cmakeargs "-DCLR_CMAKE_BUILD_HOST_PRODUCT=$(BuildNativeHostProduct.ToUpper())"</BuildArgs>

src/native/corehost/corehost.proj:169

  • Same issue as above: $(BuildNativeHostTests.ToUpper()) / $(BuildNativeHostProduct.ToUpper()) will not call ToUpper() in MSBuild and likely expands to empty. Switch to an MSBuild property function (or another explicit transformation) so the CMake definitions receive TRUE/FALSE as intended.
      <BuildArgs>$(BuildArgs) -cmakeargs "-DCLR_CMAKE_BUILD_HOST_TESTS=$(BuildNativeHostTests.ToUpper())"</BuildArgs>
      <BuildArgs>$(BuildArgs) -cmakeargs "-DCLR_CMAKE_BUILD_HOST_PRODUCT=$(BuildNativeHostProduct.ToUpper())"</BuildArgs>

Comment thread src/mono/browser/build/BrowserWasmApp.CoreCLR.targets
Comment thread src/mono/browser/build/BrowserWasmApp.CoreCLR.targets
Comment thread src/mono/browser/build/BrowserWasmApp.CoreCLR.targets
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

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

@maraf maraf added the os-browser Browser variant of arch-wasm label Apr 16, 2026
@maraf maraf added this to the 11.0.0 milestone Apr 16, 2026
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.

6 participants