From f51d9f7b82265eeb7778329f36897a1feff738e3 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 8 Sep 2026 09:10:00 +0200 Subject: [PATCH 1/6] [wasm] Resolve crossgen2 through the SDK Fixes #133353 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- eng/Subsets.props | 10 ++--- .../Microsoft.NETCore.App/Crossgen2/Sdk.props | 18 -------- .../Microsoft.NETCore.App.Crossgen2.props | 4 -- .../build/BrowserWasmApp.CoreCLR.targets | 24 ++++++++--- .../WorkloadManifest.json.in | 17 +------- .../WorkloadManifest.targets.in | 11 +---- src/mono/wasi/build/WasiApp.CoreCLR.targets | 24 +++++++---- .../CoreCLRWasmNativeDefaultsTests.cs | 43 +++++++++++++++++++ .../Templates/WasmTemplateTestsBase.cs | 3 ++ 9 files changed, 86 insertions(+), 68 deletions(-) delete mode 100644 src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props diff --git a/eng/Subsets.props b/eng/Subsets.props index 0674e93cf4ded7..8b1a2dafc496cd 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -761,13 +761,9 @@ diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props deleted file mode 100644 index a5cdb6c55a638d..00000000000000 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props +++ /dev/null @@ -1,18 +0,0 @@ - - - - <_Crossgen2PackExeSuffix Condition="$([MSBuild]::IsOSPlatform('Windows'))">.exe - $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', 'tools', 'crossgen2$(_Crossgen2PackExeSuffix)')) - - diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.props index cdec0b7f158ee7..a400c42adf25c7 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.props @@ -18,10 +18,6 @@ Include="$(CrossgenPublishProject)" OutputItemType="_RawCrossgenPublishFiles" ReferenceOutputAssembly="false" /> - - - CoreCLR + + true + <_CoreCLRInvalidEH Condition="'$(WasmEnableExceptionHandling)' != '' and '$(WasmEnableExceptionHandling)' != 'true'">true <_CoreCLRInvalidSIMD Condition="'$(WasmEnableSIMD)' != '' and '$(WasmEnableSIMD)' != 'true'">true @@ -640,6 +644,15 @@ + + + + + <_WasmPInvokeTablePath>$(_WasmIntermediateOutputPath)callhelpers-pinvoke.cpp @@ -694,21 +707,20 @@ <_WasmManagedAssemblies Include="$(_CoreLibPath)" /> - + <_PortableCallHelpersGeneratorExeSuffix Condition="'$(OS)' == 'Windows_NT'">.exe $([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_PortableCallHelpersGeneratorExeSuffix)')) $(Crossgen2ToolPath) + @(Crossgen2Tool) + Text="Could not resolve crossgen2. Update the .NET SDK and restore the project, or set %24(PortableCallHelpersGeneratorPath) to a crossgen2 executable." /> + Text="Set %24(PortableCallHelpersGeneratorPath) to a crossgen2 executable, not the DLL '$(PortableCallHelpersGeneratorPath)'." /> + Text="crossgen2 executable not found: '$(PortableCallHelpersGeneratorPath)'. Build or restore crossgen2, or correct %24(PortableCallHelpersGeneratorPath)." /> <_PortableCallHelpersGeneratorRsp>$(_WasmIntermediateOutputPath)callhelpers-generator.rsp 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 48286c531fca63..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 @@ -11,8 +11,7 @@ "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", - "Microsoft.NETCore.App.Crossgen2.${NetVersion}" + "Microsoft.NETCore.App.Runtime.AOT.Cross.${NetVersion}.browser-wasm" ], "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "win-arm64", "linux-x64", "linux-arm64", "linux-musl-x64", "linux-musl-arm64", "osx-x64", "osx-arm64" ] @@ -453,20 +452,6 @@ "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-arm64.Cross.wasi-wasm" } }, - "Microsoft.NETCore.App.Crossgen2.${NetVersion}": { - "kind": "Sdk", - "version": "${PackageVersion}", - "alias-to": { - "win-x64": "Microsoft.NETCore.App.Crossgen2.win-x64", - "win-arm64": "Microsoft.NETCore.App.Crossgen2.win-arm64", - "linux-x64": "Microsoft.NETCore.App.Crossgen2.linux-x64", - "linux-arm64": "Microsoft.NETCore.App.Crossgen2.linux-arm64", - "linux-musl-x64": "Microsoft.NETCore.App.Crossgen2.linux-musl-x64", - "linux-musl-arm64": "Microsoft.NETCore.App.Crossgen2.linux-musl-arm64", - "osx-x64": "Microsoft.NETCore.App.Crossgen2.osx-x64", - "osx-arm64": "Microsoft.NETCore.App.Crossgen2.osx-arm64" - } - }, "Microsoft.NETCore.App.Runtime.Mono.${NetVersion}.browser-wasm" : { "kind": "framework", "version": "${PackageVersion}", 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 7420fab69354c2..e1b3a6351ae054 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 @@ -212,18 +212,11 @@ - + - diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index a1dd6dd842ceeb..8d2fd6350628ca 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -26,6 +26,7 @@ CoreCLR false + true false @@ -86,6 +87,16 @@ + + + + + - + <_PortableCallHelpersGeneratorExeSuffix Condition="'$(OS)' == 'Windows_NT'">.exe $([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_PortableCallHelpersGeneratorExeSuffix)')) $(Crossgen2ToolPath) + @(Crossgen2Tool) + Text="Could not resolve crossgen2. Update the .NET SDK and restore the project, or set %24(PortableCallHelpersGeneratorPath) to a crossgen2 executable." /> + Text="Set %24(PortableCallHelpersGeneratorPath) to a crossgen2 executable, not the DLL '$(PortableCallHelpersGeneratorPath)'." /> + Text="crossgen2 executable not found: '$(PortableCallHelpersGeneratorPath)'. Build or restore crossgen2, or correct %24(PortableCallHelpersGeneratorPath)." /> <_PortableCallHelpersGeneratorRsp>$(_WasiRelinkObjDir)callhelpers-generator.rsp diff --git a/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs b/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs index 883cfbbab3d7d8..89db57f0947b93 100644 --- a/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs @@ -151,6 +151,49 @@ public void PublishReadyToRunDirectoryMatchesSdkOutputCasing() Assert.Equal(Path.Combine(GetObjDir(config), "R2R") + Path.DirectorySeparatorChar, match.Groups[1].Value); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void NativeRelinkResolvesCrossgen2WithoutReadyToRun(bool publish) + { + ProjectInfo info = CopyTestAsset( + Configuration.Debug, + aot: false, + TestAsset.WasmBasicTestApp, + "coreclr_sdk_crossgen2", + extraProperties: $$""" + false + true + {{publish}} + """, + insertAtEnd: $$""" + + + + + + + + + + + + """); + + // Run the generator, then stop before native compilation. + string output = publish + ? PublishProject(info, Configuration.Debug, new PublishOptions(ExpectSuccess: false)).buildOutput + : BuildProject(info, Configuration.Debug, new BuildOptions(ExpectSuccess: false)).buildOutput; + + Assert.Contains("Stopping after validating SDK crossgen2", output); + } + private string? BuildAndGetWasmBuildNativeLine(string projectPrefix, string extraProperties, bool expectSuccess) => BuildAndGetOutput(projectPrefix, extraProperties, extraItems: "", expectSuccess).line; diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index ff96c139964e50..8bd706ff381ac3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -180,6 +180,9 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref 11.0.0-{{versionSuffix}} + + 11.0.0-{{versionSuffix}} + """; From 80000263cb6a03afd2ada6a4e5d3b2ececacf8a9 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 8 Sep 2026 09:48:41 +0200 Subject: [PATCH 2/6] [wasm] Report missing crossgen2 packs without ReadyToRun errors Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- .../build/BrowserWasmApp.CoreCLR.targets | 2 +- src/mono/wasi/build/WasiApp.CoreCLR.targets | 2 +- .../CoreCLRWasmNativeDefaultsTests.cs | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index eac10542d5c6f2..cceb9f3320d7aa 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -650,7 +650,7 @@ BeforeTargets="_CoreCLRGenerateManagedToNative" DependsOnTargets="ResolveFrameworkReferences"> + Condition="'$(PortableCallHelpersGeneratorPath)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" /> diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 8d2fd6350628ca..35228605c2e0c3 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -94,7 +94,7 @@ Condition="'$(WasmGenerateAppBundle)' == 'true' and '$(WasmSingleFileBundle)' != 'true'" DependsOnTargets="ResolveFrameworkReferences"> + Condition="'$(PortableCallHelpersGeneratorPath)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" /> false + false + true + {{publish}} + """); + + string output = publish + ? PublishProject(info, Configuration.Debug, new PublishOptions(ExpectSuccess: false)).buildOutput + : BuildProject(info, Configuration.Debug, new BuildOptions(ExpectSuccess: false)).buildOutput; + + Assert.Contains("Could not resolve crossgen2. Update the .NET SDK and restore the project, or set $(PortableCallHelpersGeneratorPath) to a crossgen2 executable.", output); + Assert.DoesNotContain("NETSDK1094", output); + } + private string? BuildAndGetWasmBuildNativeLine(string projectPrefix, string extraProperties, bool expectSuccess) => BuildAndGetOutput(projectPrefix, extraProperties, extraItems: "", expectSuccess).line; From 3cf530b7a9b00471454cf4472e84cd1590271527 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 8 Sep 2026 12:49:56 +0200 Subject: [PATCH 3/6] [wasm] Use configured pack versions in build tests Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- .../Templates/WasmTemplateTestsBase.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index 8bd706ff381ac3..af7f23e0f96686 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -158,7 +158,7 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref if (!s_buildEnv.IsCoreClrRuntime) return; - string versionSuffix = s_buildEnv.IsRunningOnCI ? "ci" : "dev"; + string packVersion = s_buildEnv.GetRuntimePackVersion(DefaultTargetFramework); extraProperties += """ @@ -167,9 +167,9 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref extraItems += $$""" - 11.0.0-{{versionSuffix}} - 11.0.0-{{versionSuffix}} - 11.0.0-{{versionSuffix}} + {{packVersion}} + {{packVersion}} + {{packVersion}} browser-wasm;%(RuntimePackRuntimeIdentifiers) """; @@ -178,10 +178,10 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref - 11.0.0-{{versionSuffix}} + {{packVersion}} - 11.0.0-{{versionSuffix}} + {{packVersion}} From fe80022bb669efdb39126bd98b3576a325f2858e Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 9 Sep 2026 20:54:37 +0200 Subject: [PATCH 4/6] [wasm] Clarify CoreCLR WebAssembly pack update target Name the test-only target for both packs it updates. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- .../wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index b44664c5b0f532..6603a8338ddf86 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -175,7 +175,7 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref """; insertAtEnd += $$""" - From 8a4585d968b282f9fceeda6c06daf6cf1c26b87f Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 10 Sep 2026 12:17:25 +0200 Subject: [PATCH 5/6] [wasm] Skip crossgen2 pack request when native relinking is disabled Keep the early request for implicit relinking, but honor an explicit WasmBuildNative=false. Preserve explicit pack requests and independent SDK acquisition for ReadyToRun. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 9cb5015c56af13..a277500ae12aa9 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -26,8 +26,8 @@ CoreCLR - true + Only skip an explicit opt-out; otherwise native relinking may be inferred later. --> + true <_CoreCLRInvalidEH Condition="'$(WasmEnableExceptionHandling)' != '' and '$(WasmEnableExceptionHandling)' != 'true'">true From 42d1feb73856f2bed2637025b1e1a2892bdf446b Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Fri, 11 Sep 2026 10:09:14 +0200 Subject: [PATCH 6/6] [wasm] Rely on publish defaults in crossgen2 tests Remove redundant WasmBuildOnlyAfterPublish settings from both SDK acquisition theories. Retain the assertion that helper generation runs in nested publish. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47 --- .../wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs b/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs index ccd8a520ad9292..b99fee0e087c78 100644 --- a/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs @@ -161,10 +161,9 @@ public void NativeRelinkResolvesCrossgen2WithoutReadyToRun(bool publish) aot: false, TestAsset.WasmBasicTestApp, "coreclr_sdk_crossgen2", - extraProperties: $$""" + extraProperties: """ false true - {{publish}} """, insertAtEnd: $$""" @@ -204,11 +203,10 @@ public void NativeRelinkWithoutCrossgen2PackReportsMissingGenerator(bool publish aot: false, TestAsset.WasmBasicTestApp, "coreclr_missing_crossgen2", - extraProperties: $$""" + extraProperties: """ false false true - {{publish}} """); string output = publish