Skip to content

[browser] CoreCLR in-tree relink#125607

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

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

Conversation

@maraf
Copy link
Copy Markdown
Member

@maraf maraf commented Mar 16, 2026

Contributes to #123670
Contributes to #126100

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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
- 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 <[email protected]>
…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 <[email protected]>
…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 <[email protected]>
@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

This comment was marked as off-topic.

Copilot AI review requested due to automatic review settings March 16, 2026 10:42

This comment was marked as off-topic.

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 <[email protected]>
@github-actions

This comment has been minimized.

@pavelsavara
Copy link
Copy Markdown
Member

It would be good to enable some of the WBT which are currently disabled because of this.

maraf and others added 2 commits March 28, 2026 08:48
CoreCLR browser-wasm always requires exception handling and SIMD, so
remove the conditional defaults and simplify the flag generation. This
removes the dead code paths for WasmEnableExceptionHandling=false and
WasmEnableSIMD=false which cannot apply to CoreCLR.

Co-authored-by: Copilot <[email protected]>
…on-true for CoreCLR

Capture user-set values before overwriting them with true, then emit
a build error in _CoreCLRWasmInitialize if either was explicitly set
to a non-true value. This gives a clear message instead of a silent
override or a cryptic link failure.

Co-authored-by: Copilot <[email protected]>
Copilot AI review requested due to automatic review settings March 28, 2026 08:52
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

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

Comment on lines +185 to +187
<_ToolchainMissingErrorMessage Condition="'$(_ToolchainMissingErrorMessage)' == '' and '$(_UsingEMSDK_PATH)' != 'true' and '$(_EMSDKMissingPaths)' != ''">Emscripten from the workload is missing some paths: $(_EMSDKMissingPaths).</_ToolchainMissingErrorMessage>
<_ToolchainMissingErrorMessage Condition="'$(_ToolchainMissingErrorMessage)' == '' and '$(_UsingEMSDK_PATH)' == 'true' and !Exists($(EMSDK_PATH))">Could not find Emscripten sdk at %24(EMSDK_PATH)=$(EMSDK_PATH).</_ToolchainMissingErrorMessage>
<_ToolchainMissingErrorMessage Condition="'$(_ToolchainMissingErrorMessage)' == '' and '$(_UsingEMSDK_PATH)' == 'true' and '$(_EMSDKMissingPaths)' != ''">Specified Emscripten sdk at %24(EMSDK_PATH)=$(EMSDK_PATH) is missing some paths: $(_EMSDKMissingPaths).</_ToolchainMissingErrorMessage>
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Exists($(EMSDK_PATH)) check is missing quotes around the path. If EMSDK_PATH contains spaces (common on Windows), the condition can evaluate incorrectly and produce a misleading toolchain-missing error. Use Exists('$(EMSDK_PATH)') here for correct MSBuild parsing.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +20
<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>
<!-- Post Wasm MVP features -->
<WasmEnableExceptionHandling Condition="'$(WasmEnableExceptionHandling)' == ''">true</WasmEnableExceptionHandling>
<WasmEnableSIMD Condition="'$(WasmEnableSIMD)' == ''">$(WasmEnableExceptionHandling)</WasmEnableSIMD>

<WasmBuildAppAfterThisTarget Condition="'$(WasmBuildAppAfterThisTarget)' == '' and '$(DisableAutoWasmBuildApp)' != 'true'">Build</WasmBuildAppAfterThisTarget>
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a temporary backup of BrowserWasmApp.CoreCLR.targets and it is not referenced anywhere in the repo. Keeping a full duplicate of build logic risks divergence and increases maintenance/packaging noise. Please remove this file (or replace it with a much smaller design note under a docs/research location if needed).

Copilot uses AI. Check for mistakes.
DependsOnTargets="GenerateEmccExports">

<PropertyGroup>
<_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]"</_EmccExportedRuntimeMethods>
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$(_EmccExportedRuntimeMethods) is being formatted as a quoted bracketed array (e.g. "[BROWSER_HOST,'FS',...]"), but the emcc invocation used by CoreCLR’s browserhost build expects a comma-separated list (see browserhost/CMakeLists.txt: -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS}). As written, the linker will receive an invalid value and fail or export the wrong runtime methods. Please change this to emit the same comma-separated format as CMake (including the required BROWSER_HOST prefix) and avoid embedding extra quotes/brackets.

Suggested change
<_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]"</_EmccExportedRuntimeMethods>
<_EmccExportedRuntimeMethods>BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%(Identity)', ',')</_EmccExportedRuntimeMethods>

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

maraf and others added 2 commits March 28, 2026 09:49
Run all Wasm.Build.Tests with TestCategory 'native' using
RuntimeFlavor=CoreCLR and dotnet-none (no workload).

Results: 16 passed, 295 failed (311 total)
- 89 xharness tool missing (built OK, can't run)
- 59 NETSDK1147 (workload not propagated to referenced projects)
- 38 template missing (needs wasm-tools workload)
- 89 assertion failures (Mono-specific expectations)
- 20 other (build/publish/misc failures)

Co-authored-by: Copilot <[email protected]>
Introduce a new 'coreclr-native' test category for Wasm.Build.Tests that
have been verified to pass with CoreCLR native relinking. The CoreCLR
xunit filter excludes 'native' but not 'coreclr-native', so these 16
test cases (9 methods) will now run in CoreCLR CI.

Tests tagged coreclr-native:
- MemoryTests (all 2 methods - class level)
- NativeBuildTests.ZipArchiveInteropTest
- DllImportTests.UnmanagedCallback_WithFunctionPointers_CompilesWithWarnings
- PInvokeTableGeneratorTests (3 blittable struct methods)
- Blazor.BuildPublishTests.DefaultTemplate_AOT_WithWorkload
- Blazor.MiscTests.BugRegression_60479_WithRazorClassLib

Non-passing methods that previously inherited the class-level 'native'
category now have method-level [TestCategory("native")] to preserve
the existing Mono-only behavior.

Co-authored-by: Copilot <[email protected]>
@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings March 28, 2026 10:14
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

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

<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.Utils.js" Kind="js-library" />
<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Runtime.InteropServices.JavaScript.Native.js" Kind="js-library" />
<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libBrowserHost.js" Kind="js-library" />
<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.extpost.js" Kind="extern-post-js" />
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runtime pack JS input libSystem.Native.Browser.extpost.js is included unconditionally. If the runtime pack being used doesn't contain this file (e.g., older pack, partial build, or CopyWasmNativeFiles failing), the emcc link will fail with a missing input file. Consider guarding this item with an Exists(...) condition and/or emitting a clear error when it’s required for relinking but not present.

Suggested change
<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.extpost.js" Kind="extern-post-js" />
<_CoreCLRJsLibrary Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.extpost.js"
Kind="extern-post-js"
Condition="Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.Browser.extpost.js')" />

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
{
"run_date": "2026-03-28",
"branch": "maraf/WasmCoreCLRNativeBuild",
"runtime_flavor": "CoreCLR",
"sdk": "dotnet-none (11.0.100-preview.2.26116.109)",
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks like a large, generated/one-off research artifact (multi-thousand-line JSON test result dump). Checking this into the main repo will significantly bloat the PR and future clones without providing build/runtime value. Consider removing it from the PR (or replacing with a short summarized markdown report) and attaching the raw results to the PR/issue instead.

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

maraf and others added 2 commits March 30, 2026 13:27
When XHARNESS_CLI_PATH is not set (local dev, non-CI), the xharness
dotnet tool may not be restored in the SDK under test (e.g. dotnet-none).
This adds EnsureXHarnessAvailable() which runs 'dotnet tool restore'
once before the first xharness invocation, using the repo-root
.config/dotnet-tools.json manifest. Thread-safe, no-op after first call,
and skipped entirely when CI provides XHARNESS_CLI_PATH.

Fixes 89 XHARNESS_TOOL_MISSING test failures.

Co-authored-by: Copilot <[email protected]>
Re-categorize 12 test methods (43 test cases) from 'native' to
'coreclr-native' so they run in CoreCLR CI. These tests previously
failed only because xharness was not installed (XHARNESS_TOOL_MISSING);
with the auto-restore fix they should pass.

Methods re-tagged:
- Blazor.BuildPublishTests.Test_WasmStripILAfterAOT (2 cases)
- Blazor.SimpleRunTests.BlazorPublishRunTest (3 cases)
- DllImportTests: 3 FunctionPointer methods (6 cases)
- InvariantGlobalizationTests.RelinkingWithoutAOT (6 cases)
- InvariantTimezoneTests: AOT + RelinkingWithoutAOT (18 cases)
- PInvokeTableGeneratorTests: 4 methods (8 cases)

9 additional methods (46 cases) with mixed XHARNESS/NETSDK1147
failures are left as 'native' — their AOT parameter combos require
workload and would fail in the no-workload CoreCLR CI configuration.

Co-authored-by: Copilot <[email protected]>
Copilot AI review requested due to automatic review settings March 30, 2026 13:27
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

Copilot reviewed 20 out of 22 changed files in this pull request and generated no new comments.

@github-actions
Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #125607

Note

This review was generated by GitHub Copilot using automated code analysis.

Holistic Assessment

Motivation: This PR implements per-app native re-linking for CoreCLR browser-wasm, matching the capability Mono already has. The motivation is clear and justified — without this, CoreCLR WASM apps cannot include custom native code via NativeFileReference.

Approach: The approach is sound — it mirrors the existing Mono build pipeline architecture while remaining self-contained (no import of WasmApp.Common.targets). Link flags correctly match src/native/corehost/browserhost/CMakeLists.txt. The test category split (native for Mono-only, coreclr-native for CoreCLR-capable) integrates cleanly with the existing xUnit trait filtering in the .csproj.

Summary: ⚠️ Needs Changes. The PR contains committed research artifacts and a backup file that must be removed before merge. There are also a few build system issues worth addressing.


Detailed Findings

❌ Committed research artifacts and backup file — Must be removed

The branch includes 7 files in a .research/ directory (6,211 lines total) and a BrowserWasmApp.CoreCLR.targets.backup file. These are development/analysis artifacts that should not be merged:

  • .research/build-wasm-browser-sample.md
  • .research/native-build-analysis.md
  • .research/native-coreclr-test-report.md
  • .research/native-coreclr-test-results.json
  • .research/runtime-native-build-analysis.md
  • .research/wasm-native-build-analysis.md
  • .research/wasm-native-build-binlog-analysis.md
  • src/mono/browser/build/BrowserWasmApp.CoreCLR.targets.backup

These should be removed in a cleanup commit before merge.

⚠️ WasmDirSep property is undefined — Glob pattern won't match

In BrowserWasmApp.CoreCLR.targets line 166:

<_SystemRuntimePathItem Include="$(MicrosoftNetCoreAppRuntimePackRidDir)lib$(WasmDirSep)net*$(WasmDirSep)System.Runtime.dll" />

WasmDirSep is not defined anywhere in the repository. This results in the glob pattern ...libnet*System.Runtime.dll instead of the intended ...lib/net*/System.Runtime.dll. Compare with WasmApp.Common.targets line 316 which uses a literal backslash:

<_SystemRuntimePathItem Include="$(MicrosoftNetCoreAppRuntimePackRidDir)\lib\net*\System.Runtime.dll" />

Impact: MicrosoftNetCoreAppRuntimePackRidLibTfmDir won't be resolved via this glob. The fallback path for CoreLib (line 326, using the native dir) likely compensates, but if any other code depends on MicrosoftNetCoreAppRuntimePackRidLibTfmDir being set, it will fail. Should use \ or / literal separators like the Mono equivalent.

⚠️ Duplicate -fwasm-exceptions -msimd128 flags in the link step

The _CoreCLRLinkNative target passes these flags twice:

  1. Via $(_WasmDefaultFlags) on the command line (line 562): -fwasm-exceptions -msimd128
  2. Via @(_WasmLinkRsp) which contains @(_EmccCommonFlags) (lines 264-265, 487): same -fwasm-exceptions -msimd128

While emcc tolerates duplicate flags, this is redundant. Consider either removing $(_WasmDefaultFlags) from the Exec command (since the RSP already includes them via _EmccCommonFlags), or removing them from _EmccCommonFlags in the link args.

⚠️ WasmNativeStrip property is set but never consumed

Lines 43-45 set WasmNativeStrip=true for CI AOT builds and the comment mentions avoiding OOM during wasm-opt, but there is no wasm-opt or strip step in the CoreCLR targets. The property is dead code. Either remove it or add the corresponding strip/optimize step.

⚠️ Missing trailing newline in BrowserWasmApp.CoreCLR.targets

The file ends without a trailing newline character (confirmed via hex dump). While not a functional issue, this is a git hygiene concern — it produces "No newline at end of file" warnings in diffs and doesn't match the convention used by other .targets files in the repo.

💡 EnsureXHarnessAvailable() has no process timeout

BuildTestBase.cs line 166 calls process.WaitForExit() without a timeout. If dotnet tool restore hangs (e.g., network issues resolving NuGet packages), the test process will block indefinitely. Consider adding a timeout (e.g., process.WaitForExit(120_000)) with an appropriate error message.

💡 Commit messages include non-conventional entries

Some commit messages in the branch history are unusual (e.g., "Analysis", "Second round", "Cleanup" ×4, "Fix", "The changes are clean and focused. Three surgical edits:"). Consider squashing these into clean, descriptive commits before merge to maintain a clear git history.

✅ Test category changes are correctly structured

The split between [TestCategory("native")] (Mono-only, excluded by CoreCLR CI via -notrait category=native) and [TestCategory("coreclr-native")] (runs on CoreCLR CI) is well-designed. The per-method granularity in DllImportTests, NativeBuildTests, and PInvokeTableGeneratorTests correctly identifies which tests are Mono-specific vs CoreCLR-compatible.

AddCoreClrProjectProperties extraction is a good refactor

The extraction of CoreCLR project property injection into a shared AddCoreClrProjectProperties method (and its use from both CreateWasmTemplateProject and CopyTestAsset) eliminates duplication and ensures consistency. The addition of <UsingBrowserRuntimeWorkload>false</UsingBrowserRuntimeWorkload> is appropriate for CoreCLR projects.

✅ Link flags correctly mirror CMakeLists.txt

The emcc link flags in _CoreCLRWriteLinkRsp (memory sizes, module config, exported functions/methods, JS library files) match src/native/corehost/browserhost/CMakeLists.txt. The library ordering follows the CMake convention with BrowserHost linked first.

eng/native.wasm.targets condition is well-scoped

The Condition="'$(IsBrowserWasmProject)' != 'true'" on the ICU/timezone NuGet package references correctly prevents content file pollution during app-level relink while keeping them available for the runtime build itself. The comment clearly explains the rationale.

corehost.proj change correctly ships extpost.js

Adding libSystem.Native.Browser.extpost.js from the source tree to the runtime pack native directory is necessary for the re-link step. The file exists at the referenced path and the --extern-post-js flag in the link step depends on it being available.

Generated by Code Review for issue #125607 ·

Copy link
Copy Markdown
Member Author

maraf commented Apr 1, 2026

Note

This comment was generated with the assistance of GitHub Copilot.

Investigation: Delivering CoreCLR Native Build Targets to Wasm.Build.Tests

As part of enabling template-based native tests (IcuTests, NativeBuildTests, etc.) in the CoreCLR CI pipeline, I investigated how to make BrowserWasmApp.CoreCLR.targets available to standalone test projects created by dotnet new wasmbrowser.

The Problem

Test projects created during Wasm.Build.Tests are standalone — they live in temp directories and use the SDK targets (or Local.Directory.Build.targets in no-workload mode). Currently, BrowserWasmApp.CoreCLR.targets is only imported via WasmApp.InTree.targets for in-tree repo builds. When a test project sets <NativeFileReference>, the old WorkloadManifest.targets just warns (because WasmNativeWorkloadAvailable != true) and native relinking never happens.

This means tests like BuildWithUndefinedNativeSymbol and ProjectWithDllImportsRequiringMarshalIlGen pass/fail for the wrong reasons — native build is silently skipped.

Root Cause: Missing Import Chain

BrowserWasmApp.CoreCLR.targets depends on repo-level paths that don't exist in standalone test projects:

BrowserWasmApp.CoreCLR.targets
  ├── Import: $(RepositoryEngineeringDir)native.wasm.targets   ← repo path
  ├── UsingTask: $(WasmAppBuilderTasksAssemblyPath)             ← task DLL path
  └── Import: EmSdkRepo.Defaults.props (needs EMSDK_PATH)      ← emscripten

How It Works Today

Context How targets are delivered
In-tree builds WasmApp.InTree.targets imports BrowserWasmApp.CoreCLR.targets when RuntimeFlavor=CoreCLR
Mono workload wasm-tools workload installs all targets + tasks into the SDK
Helix CI (CoreCLR) Correlation payload ships build/wasm/, build/wasm-shared/, build/WasmAppBuilder/, build/emsdk/; run script sets env vars

Proposed Approach

Wire up BuildEnvironment.cs + Local.Directory.Build.targets to mirror the Helix correlation payload pattern for local runs:

  1. BuildEnvironment.cs (CoreCLR mode): Set environment/MSBuild properties pointing to repo artifacts:

    • RepositoryEngineeringDir{repoRoot}/eng/
    • WasmAppBuilderTasksAssemblyPathartifacts/bin/WasmAppBuilder/...
    • EMSDK_PATH → from system or artifacts/obj/emsdk/
  2. Local.Directory.Build.targets: Add conditional import of BrowserWasmApp.CoreCLR.targets when RuntimeFlavor=CoreCLR

  3. AddCoreClrProjectProperties(): Set <RuntimeFlavor>CoreCLR</RuntimeFlavor> in each test project csproj

This keeps the same code paths for local dev and Helix CI — just different values for the path properties.

What's Already Done (This PR Branch)

  • ✅ Changed test traits from nativecoreclr-native (5 test classes)
  • ✅ Added EnsureWasmTemplatesInstalled() to auto-install WASM templates (mirrors xharness pattern)
  • ✅ Added test classes to BuildWasmAppsJobsListCoreCLR.txt
  • ✅ Verified template install + project creation works end-to-end

What Remains

  • Implement the target delivery mechanism described above
  • Verify BuildWithUndefinedNativeSymbol and ProjectWithDllImportsRequiringMarshalIlGen actually perform native linking
  • Handle AOT-dependent tests (need skip conditions for no-workload CoreCLR mode)
  • Handle ICU sharding tests (need native build for ICU data replacement)

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.

3 participants