From 75fcf55d9a1425ac32c61e25dd6166d68b9cce1c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Fri, 12 Jun 2026 17:28:24 +0200 Subject: [PATCH 1/5] [wasm] Re-enable System.Formats.Nrbf tests on browser CoreCLR The System.Formats.Nrbf.Tests suite was excluded on browser-wasm + CoreCLR because test discovery crashed with a FileNotFoundException for System.Runtime.Serialization.Formatters, Version=11.0.0.0. Root cause: for a self-contained browser-wasm app, @(ReferenceCopyLocalPaths) contains two copies of System.Runtime.Serialization.Formatters - the shared framework's non-functional 8.1.0.0 stub (from the runtime pack) and the functional 11.0.0.0 build the test references app-local. The WebAssembly SDK task ComputeWasmBuildAssets dedupes webcil bundle candidates by relative path on a first-wins, version-blind basis, so the 8.1.0.0 stub got bundled into _framework and shadowed the app-local 11.0.0.0 copy. xunit discovery then materializes [InlineData(FormatterTypeStyle.X)] attribute blobs, triggering an Assembly.Load of the 11.0.0.0 build, which is not present - aborting discovery. Unlike desktop (framework-dependent deployment, or single-file publish whose bundler consumes the already version-resolved ResolvedFileToPublish set), the wasm webcil bundle is assembled at build time from candidates that are not version-conflict-resolved against ProjectReferences. Fix: add a CoreCLR-wasm target that applies copy-local-wins semantics before the bundle candidates are gathered - it drops runtime-pack copy-local assemblies that are shadowed by a same-named app-local copy. Apps without an app-local copy of a framework assembly are unaffected. With the fix the bundled Formatters webcil is the functional 11.0.0.0 build, discovery finds 153 test cases (was 6) and the suite passes; the ReadTests hierarchy correctly skips on browser since BinaryFormatter is unsupported there. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/libraries/tests.proj | 2 -- .../build/BrowserWasmApp.CoreCLR.targets | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 4de29857494251..3a733a0604e79f 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -185,8 +185,6 @@ - - diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index e94f759abc4f69..b83bf16ec25a33 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -178,6 +178,40 @@ '$(WasmBuildingForNestedPublish)' != 'true' and '$(UsingBrowserRuntimeWorkload)' != 'true'" /> + + + + + <_CoreCLRAppLocalAssembly Include="@(ReferenceCopyLocalPaths)" + Condition="'%(Extension)' == '.dll' and !$([System.String]::Copy('%(FullPath)').StartsWith('$(MicrosoftNetCoreAppRuntimePackDir)'))" /> + + + <_CoreCLRAppLocalAssemblyNames>;@(_CoreCLRAppLocalAssembly->'%(FileName)%(Extension)'); + + + + + + + From 564acab1c1756bce783721cb319acecf083cd622 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 15 Jun 2026 10:43:54 +0200 Subject: [PATCH 2/5] Feedback Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/libraries/tests.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 3a733a0604e79f..c9711aff2d5bfb 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -183,7 +183,7 @@ - + From 1a4d41f993d16487f7ddfb646862eb8f4ce551ac Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 15 Jun 2026 10:49:01 +0200 Subject: [PATCH 3/5] Normalize runtime pack dir before StartsWith checks $(MicrosoftNetCoreAppRuntimePackDir) is not normalized in this file (it can come straight from %(ResolvedRuntimePack.PackageDirectory) or a global property), so a missing trailing separator or non-native path separators could make the StartsWith checks fail to identify runtime-pack assemblies, silently skipping the dedupe. Normalize it once with [MSBuild]::NormalizeDirectory and use that for both checks so it reliably matches %(FullPath). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index b83bf16ec25a33..b5a004c061f140 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -195,10 +195,15 @@ + + + <_CoreCLRRuntimePackDirNormalized>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)')) + <_CoreCLRAppLocalAssembly Include="@(ReferenceCopyLocalPaths)" - Condition="'%(Extension)' == '.dll' and !$([System.String]::Copy('%(FullPath)').StartsWith('$(MicrosoftNetCoreAppRuntimePackDir)'))" /> + Condition="'%(Extension)' == '.dll' and !$([System.String]::Copy('%(FullPath)').StartsWith('$(_CoreCLRRuntimePackDirNormalized)'))" /> <_CoreCLRAppLocalAssemblyNames>;@(_CoreCLRAppLocalAssembly->'%(FileName)%(Extension)'); @@ -207,7 +212,7 @@ From df2f4e587d47288e1f1d852c4a0d6942c95bf066 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 15 Jun 2026 12:07:12 +0200 Subject: [PATCH 4/5] Resolve runtime pack dir with ResolvedRuntimePack fallback The target previously only ran when $(MicrosoftNetCoreAppRuntimePackDir) was set, but WBT/Helix-created CoreCLR-wasm projects can leave that property empty and rely on %(ResolvedRuntimePack.PackageDirectory) instead, so the target was skipped and the runtime-pack vs app-local shadowing persisted for them. Resolve the runtime pack dir inside the target the same way the WebAssembly SDK does (Browser.targets): prefer the property, fall back to the ResolvedRuntimePack metadata, then normalize. Both ItemGroups are guarded so an unresolved dir is a safe no-op. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../build/BrowserWasmApp.CoreCLR.targets | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index b5a004c061f140..354e89cc9d903c 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -194,25 +194,29 @@ --> + Condition="'$(IsBrowserWasmProject)' == 'true'"> - - <_CoreCLRRuntimePackDirNormalized>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)')) + + <_CoreCLRRuntimePackDir>$(MicrosoftNetCoreAppRuntimePackDir) + <_CoreCLRRuntimePackDir Condition="'$(_CoreCLRRuntimePackDir)' == ''">%(ResolvedRuntimePack.PackageDirectory) + <_CoreCLRRuntimePackDir Condition="'$(_CoreCLRRuntimePackDir)' != ''">$([MSBuild]::NormalizeDirectory('$(_CoreCLRRuntimePackDir)')) - + <_CoreCLRAppLocalAssembly Include="@(ReferenceCopyLocalPaths)" - Condition="'%(Extension)' == '.dll' and !$([System.String]::Copy('%(FullPath)').StartsWith('$(_CoreCLRRuntimePackDirNormalized)'))" /> + Condition="'%(Extension)' == '.dll' and !$([System.String]::Copy('%(FullPath)').StartsWith('$(_CoreCLRRuntimePackDir)'))" /> <_CoreCLRAppLocalAssemblyNames>;@(_CoreCLRAppLocalAssembly->'%(FileName)%(Extension)'); - + From 58e4d6679f0f2195923eaadc1cfffb5e1d9ee597 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 15 Jun 2026 15:37:20 +0200 Subject: [PATCH 5/5] Run dedupe after ResolveReferences to guarantee populated item list _GatherWasmFilesToBuild has no DependsOnTargets, so a target hooked only via BeforeTargets could run before ResolveReferences populates @(ReferenceCopyLocalPaths), making the dedupe a no-op. Add DependsOnTargets=ResolveReferences so the prune is correct-by-construction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 354e89cc9d903c..52dece99895f9c 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -191,9 +191,13 @@ Resolve it the way a self-contained app loads assemblies (copy-local wins): drop runtime-pack copies shadowed by a same-named app-local copy before the candidates are gathered. Apps without an app-local copy of a framework assembly are unaffected. + + DependsOnTargets=ResolveReferences guarantees @(ReferenceCopyLocalPaths) is populated before we prune; + _GatherWasmFilesToBuild (unlike _ComputeWasmBuildCandidates) does not pull it in on its own. -->