Skip to content

[wasm] Move app-local vs runtime-pack conflict resolution into ComputeWasmBuildAssets (after .NET 12 branch) #129741

Description

@radekdoulik

Note

This issue was drafted with the assistance of GitHub Copilot.

Summary

Follow-up to #129334. That PR fixed a self-contained browser-wasm CoreCLR bug where a non-functional runtime-pack assembly stub shadows the functional app-local copy in the _framework bundle. The fix lives in a CoreCLR-only target and works, but the root cause is in the shared WebAssembly SDK bundling task. We should move the resolution down into ComputeWasmBuildAssets later, but deliberately not now (see "Why defer" below).

Root cause

Microsoft.NET.Sdk.WebAssembly.ComputeWasmBuildAssets dedupes the webcil bundle candidates by relative path, first-wins, version-blind:

  • src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs ~L162:
    if (!uniqueRelativePaths.Add(relativePath))
    {
        // Skipping duplicate relative path ... (first candidate in iteration order wins)
        continue;
    }
  • The candidate list is @(WasmAssembliesToBundle), populated from @(IntermediateAssembly) + @(ReferenceCopyLocalPaths) (.dll only) in Microsoft.NET.Sdk.WebAssembly.Browser.targets (~L158-160). Whichever copy of a given file name appears first wins — regardless of AssemblyVersion.

For a self-contained CoreCLR app, @(ReferenceCopyLocalPaths) can contain two copies of the same assembly: the runtime-pack copy (under $(MicrosoftNetCoreAppRuntimePackDir)) and an app-local copy referenced out-of-band at a different version. The canonical case is System.Runtime.Serialization.Formatters: the shared framework ships a non-functional 8.1.0.0 stub while the app references the functional 11.0.0.0 build, so when the stub wins the dedup, Assembly.Load(11.0.0.0) fails at runtime with FileNotFoundException.

Current workaround (shipped in #129334)

A new target _CoreCLRPreferAppLocalAssembliesOverRuntimePack in src/mono/browser/build/BrowserWasmApp.CoreCLR.targets runs before the bundle candidates are computed. It feeds the same-named runtime-pack/app-local pairs through the SDK's own ResolvePackageFileConflicts task (with PreferredPackages for exact publish parity) and prunes the losing copies from @(ReferenceCopyLocalPaths), so the higher AssemblyVersion wins — exactly as a desktop self-contained publish would. This target is imported only for RuntimeFlavor == CoreCLR, so the common Mono path is untouched.

Proposed proper fix (this issue)

Make the dedup in ComputeWasmBuildAssets (or a shared step feeding it) version-aware, so the same conflict resolution applies on the common Mono/CoreCLR path and both runtime flavors benefit. Once that lands, the _CoreCLRPreferAppLocalAssembliesOverRuntimePack band-aid in BrowserWasmApp.CoreCLR.targets can be removed.

Why defer (do NOT do this now)

The ComputeWasmBuildAssets task and WasmAssembliesToBundle are on the common Mono + CoreCLR build path. Changing the dedup behavior there now risks regressing .NET 11 Mono, which currently does not hit this bug. To keep the .NET 11 blast radius minimal, #129334 intentionally scoped the fix to the CoreCLR-only targets file.

Plan: after the repo branches for .NET 12, move the resolution into ComputeWasmBuildAssets on main, validate both Mono and CoreCLR self-contained browser-wasm builds, and delete the CoreCLR-only workaround.

Acceptance criteria

  • Version-aware conflict resolution implemented in ComputeWasmBuildAssets (or a shared pre-bundle step) on the common path.
  • Both Mono and CoreCLR self-contained browser-wasm builds bundle the higher-AssemblyVersion copy (verified with the System.Runtime.Serialization.Formatters 8.1.0.0-stub-vs-11.0.0.0 case).
  • _CoreCLRPreferAppLocalAssembliesOverRuntimePack target and its guarded UsingTask removed from src/mono/browser/build/BrowserWasmApp.CoreCLR.targets.
  • System.Formats.Nrbf.Tests (and any similarly affected suites) stay green on browser-wasm CoreCLR.

References

  • PR: [wasm][coreclr] Re-enable System.Formats.Nrbf tests #129334
  • Workaround target: src/mono/browser/build/BrowserWasmApp.CoreCLR.targets (_CoreCLRPreferAppLocalAssembliesOverRuntimePack)
  • Root-cause dedup: src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs (uniqueRelativePaths, ~L162)
  • Candidate population: src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets (~L158-160)

Metadata

Metadata

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions