Skip to content

[browser][coreclr] R2R native relink publish fails with NETSDK1152 #136499

Closed

Description

@radekdoulik

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>.wasm image;
  • the app's generated obj/.../R2R/<assembly>.wasm image.

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=true
  • WasmBuildNative=true using 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:

  1. Implement ReadyToRun for CoreCLR browser-wasm #132339 (8baacb006f5615ebd705bdf9de98ca5df7bf1ba1, 2026-08-25) added the prebuilt pack R2R images and _WasmCoreClrPruneR2RFromPublish, with the prune attached only to ProcessPublishFilesForWasm.
  2. 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 .wasm relative 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.

Activity

  1. added this to the 12.0.0 milestone on Oct 27, 2026
  2. dotnet-policy-service commented on Oct 27, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
    See info in area-owners.md if you want to be subscribed.

  3. dotnet-policy-service commented on Oct 27, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/crossgen-contrib
    See info in area-owners.md if you want to be subscribed.

  4. lewing commented on Oct 31, 2026

    @lewing
    Member

    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: _WasmCoreClrPruneR2RFromPublish now runs before both _HandleFileConflictsForPublish and ProcessPublishFilesForWasm. It also selects the runtime pack's images by exact directory ('%(RootDir)%(Directory)' == '$(_WasmRuntimePackR2RDir)'), so the per-app obj/.../R2R images are kept. An SDK built from the VMR after 2026-10-30 should publish without NETSDK1152.

    Test coverage note: NativeLibraryTests.ProjectWithNativeReference_ReadyToRun already 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions