You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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.
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.
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
_frameworkbundle. 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 intoComputeWasmBuildAssetslater, but deliberately not now (see "Why defer" below).Root cause
Microsoft.NET.Sdk.WebAssembly.ComputeWasmBuildAssetsdedupes the webcil bundle candidates by relative path, first-wins, version-blind:src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs~L162:@(WasmAssembliesToBundle), populated from@(IntermediateAssembly)+@(ReferenceCopyLocalPaths)(.dllonly) inMicrosoft.NET.Sdk.WebAssembly.Browser.targets(~L158-160). Whichever copy of a given file name appears first wins — regardless ofAssemblyVersion.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 isSystem.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 withFileNotFoundException.Current workaround (shipped in #129334)
A new target
_CoreCLRPreferAppLocalAssembliesOverRuntimePackinsrc/mono/browser/build/BrowserWasmApp.CoreCLR.targetsruns before the bundle candidates are computed. It feeds the same-named runtime-pack/app-local pairs through the SDK's ownResolvePackageFileConflictstask (withPreferredPackagesfor exact publish parity) and prunes the losing copies from@(ReferenceCopyLocalPaths), so the higherAssemblyVersionwins — exactly as a desktop self-contained publish would. This target is imported only forRuntimeFlavor == 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_CoreCLRPreferAppLocalAssembliesOverRuntimePackband-aid inBrowserWasmApp.CoreCLR.targetscan be removed.Why defer (do NOT do this now)
The
ComputeWasmBuildAssetstask andWasmAssembliesToBundleare 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
ComputeWasmBuildAssetsonmain, validate both Mono and CoreCLR self-contained browser-wasm builds, and delete the CoreCLR-only workaround.Acceptance criteria
ComputeWasmBuildAssets(or a shared pre-bundle step) on the common path.AssemblyVersioncopy (verified with theSystem.Runtime.Serialization.Formatters8.1.0.0-stub-vs-11.0.0.0 case)._CoreCLRPreferAppLocalAssembliesOverRuntimePacktarget and its guardedUsingTaskremoved fromsrc/mono/browser/build/BrowserWasmApp.CoreCLR.targets.System.Formats.Nrbf.Tests(and any similarly affected suites) stay green on browser-wasm CoreCLR.References
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets(_CoreCLRPreferAppLocalAssembliesOverRuntimePack)src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs(uniqueRelativePaths, ~L162)src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets(~L158-160)