From e7927eb6dcaf8bc1df49bc27ada1d94a0648cc99 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sun, 5 Jul 2026 12:18:47 -0500 Subject: [PATCH 01/10] Prototype: Enable wasm-tools workload for CoreCLR WASM targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new 'wasm-tools-coreclr' workload that provides native relinking support for CoreCLR browser-wasm projects without pulling in Mono dependencies (MonoAOTCompiler, MonoTargets.Sdk, Mono runtime pack). Unlike Mono, CoreCLR doesn't need its runtime pack bundled in the workload — the SDK resolves it via the standard KnownRuntimePack mechanism. The workload only provides: - Build targets for native relinking (BrowserWasmApp.CoreCLR.targets) - Emscripten SDK (shared via microsoft-net-sdk-emscripten) Changes: - Package BrowserWasmApp.CoreCLR.targets into WebAssembly.Sdk NuGet pack - Add conditional import in Sdk.targets.in (UseMonoRuntime switches path) - Add wasm-tools-coreclr workload to manifest (no Mono deps) - Add CoreCLR import group in WorkloadManifest.targets.in - Skip MonoAOTCompiler import when UseMonoRuntime=false Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj | 1 + .../Sdk/Sdk.targets.in | 3 ++- .../WorkloadManifest.targets.in | 12 ++++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj index 52e43c93f0336a..e27ba5d289a1e5 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj @@ -17,6 +17,7 @@ + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/Sdk.targets.in b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/Sdk.targets.in index 5185a8cdd05f9a..9d3818cdbe0b5f 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/Sdk.targets.in +++ b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/Sdk.targets.in @@ -13,5 +13,6 @@ - + + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in index 4c69e7792d7529..ad0e43d11e1f6b 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in @@ -153,7 +153,7 @@ - + @@ -204,7 +204,7 @@ - + @@ -212,6 +212,14 @@ + + + + + + From 3774df2c0fbeaeeb0a169c37bf0c06b77c123ec7 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sun, 5 Jul 2026 13:02:31 -0500 Subject: [PATCH 02/10] Fix BrowserWasmApp.CoreCLR.targets for workload import path Guard the native.wasm.targets import with a condition so it only loads when RepositoryEngineeringDir is set (in-tree builds). For the workload path, inline the GenerateEmccExports target with the required exported runtime methods and functions. Emscripten SDK acquisition is already handled by EmSdkRepo.Defaults.props + the emscripten workload extension. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../build/BrowserWasmApp.CoreCLR.targets | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 89bb233df7d208..1a7615170de340 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -75,8 +75,12 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 6f17897ab1170920f59613ca9f8b9eda5c1ff602 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sun, 5 Jul 2026 16:20:39 -0500 Subject: [PATCH 03/10] Fix CoreCLR WASM runtime pack and test issues - Include libminipal.a in browser-wasm CoreCLR runtime pack. It was excluded from LibrariesSharedFrameworkDir glob but unlike libz.a and libSystem.IO.Compression.Native.a it only exists in sharedFramework/ (not the parent LibrariesNativeArtifactsPath), so the exclusion prevented it from shipping at all. libminipal.a is a distinct library from libcoreclrminipal.a and is needed by the emcc linker. - Fix PInvokeTableGeneratorTests to use correct CoreCLR output filename callhelpers-pinvoke.cpp instead of pinvoke-table.cpp. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/liveBuilds.targets | 3 +-- src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 6dd6ce0407489d..9675c20fde1e56 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -245,8 +245,7 @@ $(LibrariesSharedFrameworkDir)*.a; $(LibrariesSharedFrameworkDir)*.dat; " - Exclude="$(LibrariesSharedFrameworkDir)libminipal.a; - $(LibrariesSharedFrameworkDir)libSystem.IO.Compression.Native.a; + Exclude="$(LibrariesSharedFrameworkDir)libSystem.IO.Compression.Native.a; $(LibrariesSharedFrameworkDir)libz.a" IsNative="true" /> diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 13adf51cd4925b..79ff413f306cf2 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -334,7 +334,7 @@ private async Task EnsureWasmAbiRulesAreFollowed(Configuration config, bool aot) // FIXME: Not possible in in-process mode for some reason, even with verbosity at "diagnostic" // Assert.Contains("Adding pinvoke signature FD for method 'Test.", output); - string pinvokeTableFileName = IsCoreClrRuntime ? "pinvoke-table.cpp" : "pinvoke-table.h"; + string pinvokeTableFileName = IsCoreClrRuntime ? "callhelpers-pinvoke.cpp" : "pinvoke-table.h"; string pinvokeTable = File.ReadAllText(Path.Combine(objDir, pinvokeTableFileName)); // Verify that the invoke is in the pinvoke table. Under various circumstances we will silently skip it, // for example if the module isn't found From 09111d13ee329d5e89ca982dcae41758779590fa Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sun, 5 Jul 2026 16:51:43 -0500 Subject: [PATCH 04/10] Fix static web asset ordering for CoreCLR WASM native relink The _CoreCLRWasmNativeForBuild target must run before _ComputeWasmBuildCandidates (not just _ResolveWasmOutputs) so the relinked dotnet.native.wasm replaces the runtime pack version in the static web assets pipeline. Without this, the runtime pack binary is served and user P/Invoke modules fail with DllNotFoundException. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 1a7615170de340..36e716e98c6760 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -230,7 +230,7 @@ served binary would be the original (runtime pack) dotnet.native.wasm. --> @@ -371,7 +371,7 @@ <_CoreCLRWasmBuildAppCoreDependsOn> _CoreCLRWasmInitialize; _CoreCLRSetupEmscripten; - _GatherWasmFilesToBuild; + _CoreCLRGatherWasmFiles; _CoreCLRPrepareForNativeBuild; _CoreCLRGenerateManagedToNative; _CoreCLRWriteCompileRsp; @@ -387,6 +387,16 @@ Condition="'$(WasmBuildNative)' == 'true'" DependsOnTargets="$(_CoreCLRWasmBuildAppCoreDependsOn)" /> + + + From 7b4ab212357acc17664aa92c0279c18d05fb9ea3 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sun, 5 Jul 2026 19:08:54 -0500 Subject: [PATCH 06/10] Fix duplicate libminipal.a in runtime pack packaging Restore the exclusion of libminipal.a from the LibrariesSharedFrameworkDir glob. The file is already included via CoreCLRSharedFrameworkDir (line 111), so including it from both locations causes NU5118 duplicate file error during nupkg creation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/liveBuilds.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 9675c20fde1e56..6dd6ce0407489d 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -245,7 +245,8 @@ $(LibrariesSharedFrameworkDir)*.a; $(LibrariesSharedFrameworkDir)*.dat; " - Exclude="$(LibrariesSharedFrameworkDir)libSystem.IO.Compression.Native.a; + Exclude="$(LibrariesSharedFrameworkDir)libminipal.a; + $(LibrariesSharedFrameworkDir)libSystem.IO.Compression.Native.a; $(LibrariesSharedFrameworkDir)libz.a" IsNative="true" /> From 9bd0d9e736d4eea5ec3edd36048dbc81f1578341 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 8 Jul 2026 17:25:35 -0500 Subject: [PATCH 07/10] Fix GenerateEmccExports import ordering for in-tree precedence Move native.wasm.targets import after the fallback GenerateEmccExports target so that when building in-tree, the authoritative definition in eng/native.wasm.targets wins (MSBuild last-definition-wins). For the workload path where native.wasm.targets is not available, the fallback list in BrowserWasmApp.CoreCLR.targets is used instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../build/BrowserWasmApp.CoreCLR.targets | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index ba67818ca42077..d2cfad252927a5 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -75,13 +75,6 @@ - - - @@ -96,10 +89,10 @@ TaskFactory="TaskHostFactory" Condition="'$(WasmAppBuilderTasksAssemblyPath)' != ''" /> - + @@ -145,6 +138,13 @@ + + + From 1af0afe9d429b3d34ac6b9502a0bb8ec066dd841 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 8 Jul 2026 17:34:15 -0500 Subject: [PATCH 08/10] Add CoreCLR browser-wasm runtime pack to wasm-tools workload Include the CoreCLR browser-wasm runtime pack in the wasm-tools workload manifest so it gets bundled and downloaded together with the other wasm-tools packs when the workload is installed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../WorkloadManifest.json.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.json.in index 4287c024b8f898..7b600623e798d8 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.json.in @@ -10,6 +10,7 @@ "Microsoft.NET.Runtime.WebAssembly.Sdk.${NetVersion}", "Microsoft.NET.Sdk.WebAssembly.Pack.${NetVersion}", "Microsoft.NETCore.App.Runtime.Mono.${NetVersion}.browser-wasm", + "Microsoft.NETCore.App.Runtime.${NetVersion}.browser-wasm", "Microsoft.NETCore.App.Runtime.AOT.Cross.${NetVersion}.browser-wasm" ], "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], @@ -458,6 +459,13 @@ "any": "Microsoft.NETCore.App.Runtime.Mono.browser-wasm" } }, + "Microsoft.NETCore.App.Runtime.${NetVersion}.browser-wasm" : { + "kind": "framework", + "version": "${PackageVersion}", + "alias-to": { + "any": "Microsoft.NETCore.App.Runtime.browser-wasm" + } + }, "Microsoft.NETCore.App.Runtime.Mono.multithread.${NetVersion}.browser-wasm" : { "kind": "framework", "version": "${PackageVersion}", From 90250be27f2b9d0520c20941b7367ee23b8139c6 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 15:06:09 -0500 Subject: [PATCH 09/10] Stage CoreCLR wasm runtime pack for wasm-tools workload install in WBT legs The wasm-tools workload manifest now includes the CoreCLR browser-wasm runtime pack, so InstallWorkloadForTesting requires that pack in the local package feed. The Mono Wasm.Build.Tests legs only staged Mono packs, causing the workload install to fail with 'microsoft.netcore.app.runtime.browser-wasm is not found'. Add an opt-in includeCoreClrRuntimePack parameter to browser-wasm-build-tests that stages the CoreCLR runtime pack from the CoreCLR build artifact, enable it in runtime.yml, and add a CoreCLR browser-wasm build-only job to the extra-platforms wasm pipeline so its WBT leg can stage the pack too. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: fab61255-0f2b-4412-9a07-8eef5b99b3e0 --- .../templates/browser-wasm-build-tests.yml | 28 +++++++++++++++++++ .../runtime-extra-platforms-wasm.yml | 28 +++++++++++++++++++ eng/pipelines/runtime.yml | 3 ++ 3 files changed, 59 insertions(+) diff --git a/eng/pipelines/common/templates/browser-wasm-build-tests.yml b/eng/pipelines/common/templates/browser-wasm-build-tests.yml index 39fca50137411f..40fc27aa919be1 100644 --- a/eng/pipelines/common/templates/browser-wasm-build-tests.yml +++ b/eng/pipelines/common/templates/browser-wasm-build-tests.yml @@ -7,6 +7,10 @@ parameters: platforms: [] shouldContinueOnError: false extraBuildArgs: '' + # The wasm-tools workload manifest includes the CoreCLR browser-wasm runtime pack, so + # installing the workload for testing requires that pack in the local feed. Only pipelines + # that build the CoreCLR browser-wasm runtime (e.g. runtime.yml) can stage it here. + includeCoreClrRuntimePack: false jobs: @@ -44,9 +48,13 @@ jobs: - ${{ if eq(platform, 'browser_wasm') }}: - build_browser_wasm_linux_Release_MultiThreaded_BuildOnly - build_browser_wasm_linux_Release_SingleThreaded_BuildOnly + - ${{ if eq(parameters.includeCoreClrRuntimePack, true) }}: + - build_browser_wasm_linux_Release_CoreCLR - ${{ if eq(platform, 'browser_wasm_win') }}: - build_browser_wasm_windows_Release_MultiThreaded_BuildOnly - build_browser_wasm_windows_Release_SingleThreaded_BuildOnly + - ${{ if eq(parameters.includeCoreClrRuntimePack, true) }}: + - build_browser_wasm_windows_Release_CoreCLR isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} testGroup: innerloop nameSuffix: WasmBuildTests @@ -85,6 +93,26 @@ jobs: TargetFolder: '$(Build.SourcesDirectory)/artifacts' CleanTargetFolder: false + # Download the CoreCLR runtime pack. The wasm-tools workload manifest now includes + # the CoreCLR browser-wasm runtime pack, so installing the workload for testing + # requires the pack to be present in the local package feed. + - ${{ if eq(parameters.includeCoreClrRuntimePack, true) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download built nugets for CoreCLR runtime + inputs: + buildType: current + artifactName: BuildArtifacts_browser_wasm_$(_hostedOs)_Release_CoreCLR + downloadType: single + downloadPath: '$(Build.SourcesDirectory)/artifacts' + + - task: CopyFiles@2 + displayName: Copy CoreCLR runtime pack + inputs: + SourceFolder: '$(Build.SourcesDirectory)/artifacts/BuildArtifacts_browser_wasm_$(_hostedOs)_Release_CoreCLR' + Contents: packages/$(_BuildConfig)/Shipping/Microsoft.NETCore.App.Runtime.browser-wasm.* + TargetFolder: '$(Build.SourcesDirectory)/artifacts' + CleanTargetFolder: false + # Download WBT - task: DownloadBuildArtifacts@0 displayName: Download Wasm.Build.Tests diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index 1fd298c4ecead8..8efd2d83a95181 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -187,6 +187,31 @@ jobs: publishArtifactsForWorkload: true publishWBT: false + # Release build of browser wasm for CoreCLR. Only stages the CoreCLR runtime pack + # (BuildArtifacts_browser_wasm__Release_CoreCLR) that the wasm-tools workload + # install in the Wasm.Build.Tests job below requires. + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: Release + platforms: + - browser_wasm + - browser_wasm_win + jobParameters: + nameSuffix: CoreCLR + buildArgs: -s clr+libs+packs -c Release -rc $(_BuildConfig) /p:TestAssemblies=false /p:InstallWorkloadForTesting=false + timeoutInMinutes: 120 + postBuildSteps: + - template: /eng/pipelines/common/wasm-post-build-steps.yml + parameters: + publishArtifactsForWorkload: true + publishWBT: false + extraVariablesTemplates: + - template: /eng/pipelines/common/templates/runtimes/test-variables.yml + parameters: + testGroup: innerloop + liveLibrariesBuildConfig: Release + # Browser Wasm.Build.Tests - template: /eng/pipelines/common/templates/browser-wasm-build-tests.yml parameters: @@ -194,6 +219,9 @@ jobs: - browser_wasm - browser_wasm_win extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) + # The CoreCLR build-only job above stages the CoreCLR runtime pack for the + # wasm-tools workload install. + includeCoreClrRuntimePack: true - template: /eng/pipelines/common/templates/simple-wasm-build-tests.yml parameters: diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 0e06ca814dfddd..d25d1357e8b958 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -1068,6 +1068,9 @@ extends: - browser_wasm_win alwaysRun: ${{ variables.isRollingBuild }} extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) + # This pipeline builds the CoreCLR browser-wasm runtime (nameSuffix: CoreCLR), + # so stage its runtime pack for the wasm-tools workload install. + includeCoreClrRuntimePack: true # Wasm runtime tests - template: /eng/pipelines/common/templates/wasm-runtime-tests.yml From f42e498b42f4ce8117e19f48c04c8c177f054d08 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Wed, 15 Jul 2026 09:44:42 +0200 Subject: [PATCH 10/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index d2cfad252927a5..d35e3d18130cd6 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -91,6 +91,7 @@