Repository navigation
Description
Description
A CoreCLR browser-wasm publish with both ReadyToRun and native relinking can contain two ResolvedFileToPublish items for each framework assembly at the same relative path:
- the runtime pack's prebuilt
runtimes/browser-wasm/native/r2r/<assembly>.wasmimage; - the app's generated
obj/.../R2R/<assembly>.wasmimage.
The SDK correctly rejects these duplicates with NETSDK1152.
This is specific to native relinking because relinking runs WasmNestedPublishApp. That nested publish deliberately does not schedule ProcessPublishFilesForWasm, but _WasmCoreClrPruneR2RFromPublish is hooked only with BeforeTargets="ProcessPublishFilesForWasm". The prune therefore never runs in the nested project instance, and both copies reach _HandleFileConflictsForPublish after ComputeFilesToPublish.
A normal non-relinking publish schedules ProcessPublishFilesForWasm, so the pack copies are removed before duplicate checking.
Affected configuration
RuntimeIdentifier=browser-wasm- CoreCLR (
RuntimeFlavor=CoreCLR/UseMonoRuntime=false) PublishReadyToRun=trueWasmBuildNative=trueusing the workload/native-relink route
Observed with SDK 12.0.100-alpha.1.26523.103 in two independent Avalonia applications. Example error:
error NETSDK1152: Found multiple publish output files with the same relative path:
.../packs/Microsoft.NETCore.App.Runtime.browser-wasm/12.0.0-alpha.1.26523.103/runtimes/browser-wasm/native/r2r/System.Private.CoreLib.wasm,
.../obj/.../R2R/System.Private.CoreLib.wasm
Public failing run: https://github.com/Blazor-Playground/macro-benchmarks/actions/runs/40376678643
Source boundary
The problem was introduced in two stages:
- Implement ReadyToRun for CoreCLR browser-wasm #132339 (
8baacb006f5615ebd705bdf9de98ca5df7bf1ba1, 2026-08-25) added the prebuilt pack R2R images and_WasmCoreClrPruneR2RFromPublish, with the prune attached only toProcessPublishFilesForWasm. - Port runtime PR dotnet/runtime#133111 R2R changes to SDK Crossgen tasks sdk#56395 (
8c2c860f72848ab0126756d21836b7d8eb1420dc, 2026-10-02) ported WebAssembly R2R output naming into the external SDK task, so per-app outputs use the same.wasmrelative paths as the pack images.
The first complete public SDK containing both halves is 12.0.100-alpha.1.26506.108. Current dotnet/runtime main still has the outer-only prune hook.
Suggested fix
Run the prune for both the normal WebAssembly publish consumer and the SDK duplicate check. Since the later hook runs after per-app images exist, select runtime-pack items by exact path rather than by filename, otherwise the desired obj/R2R images can also be removed.
For example:
<Target Name="_WasmCoreClrPruneR2RFromPublish"
Condition="'$(_WasmEnableWebcil)' == 'true'"
DependsOnTargets="_WasmCoreClrSelectR2RDirectories"
BeforeTargets="ProcessPublishFilesForWasm;_HandleFileConflictsForPublish">
<ItemGroup>
<_WasmPackR2RToPrune Include="$(_WasmRuntimePackR2RDir)*.wasm"
Condition="'$(_WasmRuntimePackR2RDir)' != ''" />
<ResolvedFileToPublish Remove="@(_WasmPackR2RToPrune)" />
</ItemGroup>
</Target>A local equivalent was validated with the affected SDK for both avalonia-bench and semi-avalonia: publishing completed without NETSDK1152, native relinking retained SkiaSharp/HarfBuzz, and both applications booted successfully in Chrome.
Testing note
The regression test needs a workload-enabled, out-of-tree CoreCLR publish with PublishReadyToRun=true and WasmBuildNative=true. Existing no-workload Wasm.Build.Tests native cases do not run the nested publish and therefore do not exercise this failure mode.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
dotnet-policy-service commentedon Oct 27, 2026
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.
dotnet-policy-service commentedon Oct 27, 2026
Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.
lewing commentedon Oct 31, 2026
This is fixed on main by 33846ae ("Prune pack R2R images before publish conflict detection"), which landed in #136619 and flowed to the VMR in dotnet/dotnet#10564 (2026-10-30).
It matches the suggested fix:
_WasmCoreClrPruneR2RFromPublishnow runs before both_HandleFileConflictsForPublishandProcessPublishFilesForWasm. It also selects the runtime pack's images by exact directory ('%(RootDir)%(Directory)' == '$(_WasmRuntimePackR2RDir)'), so the per-appobj/.../R2Rimages are kept. An SDK built from the VMR after 2026-10-30 should publish withoutNETSDK1152.Test coverage note:
NativeLibraryTests.ProjectWithNativeReference_ReadyToRunalready covers this configuration (workload +WasmBuildNative+PublishReadyToRun). It doesn't hit the collision yet because the repo's SDK predates the wasm R2R output naming from dotnet/sdk#56395, and the Crossgen2Tasks shim is applied only for composite builds. It will start covering the collision once the repo SDK moves forward.Note
This comment was generated with the help of GitHub Copilot.