[wasm] Resolve crossgen2 through the SDK - #133413
Conversation
Fixes dotnet#133353 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
|
Tagging subscribers to this area: @dotnet/runtime-infrastructure |
There was a problem hiding this comment.
🔵 Needs a closer look
It changes SDK/workload MSBuild resolution and packaging behavior for WebAssembly relinking, which has broad restore/build impact that’s hard to fully validate from diff-only review.
Pull request overview
Updates the CoreCLR WebAssembly (browser-wasm and WASI) native relink pipeline to resolve crossgen2 via the .NET SDK pack mechanism (without enabling ReadyToRun), and removes the older workload-based crossgen2 plumbing used by workload tests.
Changes:
- Request the crossgen2 pack via
RequiresCrossgen2Packand resolve@(Crossgen2Tool)as a fallback generator path for portable call-helper table generation. - Remove workload manifest references/imports for the crossgen2 pack, plus the obsolete
Sdk.propsshim packaging route. - Update workload tests to use their configured target framework/pack versions and add regression coverage for “native relink resolves crossgen2 without ReadyToRun” and “missing pack reports missing generator (not NETSDK1094)”.
File summaries
| File | Description |
|---|---|
| src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs | Uses runtime pack version for KnownFrameworkReference and updates KnownWebAssemblySdkPack/KnownCrossgen2Pack versions for CoreCLR test projects. |
| src/mono/wasm/Wasm.Build.Tests/CoreCLRWasmNativeDefaultsTests.cs | Adds tests validating SDK crossgen2 resolution without ReadyToRun and improved missing-generator error reporting. |
| src/mono/wasi/build/WasiApp.CoreCLR.targets | Requests crossgen2 pack and adds SDK-based fallback resolution via ResolveReadyToRunCompilers + @(Crossgen2Tool) for generator execution. |
| src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in | Removes the workload Sdk.props import for the crossgen2 pack in the CoreCLR browser-wasm workload path. |
| src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.json.in | Drops the crossgen2 pack from the wasm-tools workload pack list. |
| src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | Requests crossgen2 pack early and adds SDK-based generator resolution via ResolveReadyToRunCompilers + @(Crossgen2Tool); improves related error messages. |
| src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.props | Stops packaging the removed Crossgen2/Sdk.props into the pack. |
| src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props | Removes the obsolete Sdk.props shim that set $(Crossgen2ToolPath) for workload-based acquisition. |
| eng/Subsets.props | Updates rationale/comments for producing the crossgen2 host pack for CoreCLR wasm workload test legs. |
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 0
- Review effort level: Lite
Keep the crossgen2 publish-reference fix and scoped runtime-pack overrides while retaining SDK-based crossgen2 resolution. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47
There was a problem hiding this comment.
🔵 Needs a closer look
It modifies WASM build/workload acquisition behavior and MSBuild target orchestration in ways that are difficult to fully validate for all restore/build permutations without human review and broader CI signal.
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 1
- Review effort level: Lite
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
There was a problem hiding this comment.
🔵 Needs a closer look
RequiresCrossgen2Pack is currently requested unconditionally for CoreCLR-WASI projects, which can force unnecessary crossgen2 pack acquisition in configurations that will never run the generator.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/mono/wasi/build/WasiApp.CoreCLR.targets:29
RequiresCrossgen2Packis being set unconditionally for all CoreCLR-WASI projects, even though this file only uses crossgen2 when generating an app bundle (and not for single-file bundles). This can force an unnecessary crossgen2 pack acquisition during restore for library/other configurations that will never invoke the call-helper generator.
- Files reviewed: 9/9 changed files
- Comments generated: 0 new
- Review effort level: Lite
Preserve the upstream Crossgen2Path rename while retaining SDK-based acquisition. Update resolver guards, diagnostics, and test assertions to use the same override name. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f6d6e5a4-5b25-4198-b42d-d5b2dc781f47
There was a problem hiding this comment.
🟡 Changes recommended
The new unconditional RequiresCrossgen2Pack defaults can trigger unnecessary crossgen2 pack acquisition during restore for configurations that won’t invoke crossgen2, and should be gated to avoid restore overhead.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
src/mono/wasi/build/WasiApp.CoreCLR.targets:33
RequiresCrossgen2Packis set totrueby default even whenWasmGenerateAppBundleisfalse(e.g., library builds), but this targets file only uses crossgen2 when generating an app bundle (and not in single-file mode). Gating the pack request to the same condition avoids unnecessary pack acquisition during restore for configurations that will never invoke crossgen2.
<PropertyGroup>
<WasmAppRuntimeFlavor>CoreCLR</WasmAppRuntimeFlavor>
<UseMonoRuntime>false</UseMonoRuntime>
<RequiresCrossgen2Pack Condition="'$(RequiresCrossgen2Pack)' == ''">true</RequiresCrossgen2Pack>
<!-- The Mono per-app clang relink (WasmBuildNative) is not used; CoreCLR links the
wasihost corehost (libWasiHost.a) per-app through its own targets below. -->
<WasmBuildNative>false</WasmBuildNative>
<WasmSingleFileBundle Condition="'$(WasmSingleFileBundle)' == ''">false</WasmSingleFileBundle>
- Files reviewed: 9/9 changed files
- Comments generated: 1
- Review effort level: Lite
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
There was a problem hiding this comment.
🔵 Needs a closer look
The WASI targets currently request the crossgen2 pack unconditionally, which can force unnecessary restore/download work in configurations where the relink/generator targets won’t run.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/mono/wasi/build/WasiApp.CoreCLR.targets:29
RequiresCrossgen2Packis requested unconditionally for WASI CoreCLR. WhenWasmGenerateAppBundleis explicitly disabled (orWasmSingleFileBundle=true), the per-app relink/generator targets in this file won’t run, so forcing the SDK to acquire the crossgen2 pack adds avoidable restore/download cost.
- Files reviewed: 9/9 changed files
- Comments generated: 0 new
- Review effort level: Lite
| native.wasm.targets uses RuntimeFlavor to gate the CoreCLR-only relink registry. --> | ||
| <RuntimeFlavor Condition="'$(RuntimeFlavor)' == ''">CoreCLR</RuntimeFlavor> | ||
|
|
||
| <!-- Request crossgen2 here: WebAssembly.Pack is not imported before the first restore. |
There was a problem hiding this comment.
This means the crossgen2 will be required only when the workload is installed, but according to discussion with Pavel, R2R should work even without workload. Is that true?
If that's the case, we need to more this to dotnet/sdk where root skeleton for the SDK lives. Putting it there results in requesting the crossgen2 before nuget restore.
There was a problem hiding this comment.
R2R should work even without workload. Is that true?
still yes
There was a problem hiding this comment.
This is independent from R2R compilation and this path is generator specific. R2R compilation adds the pack when ReadyToRunEnabled && ReadyToRunUseCrossgen2. https://github.com/dotnet/sdk/blob/02d39b6a25a62e5ac313ab8449704c4428fa68f3/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs#L482
The relinking with generator currently needs workload as it still produces C files.
| """; | ||
| insertAtEnd += | ||
| $$""" | ||
| <Target Name="_UpdateKnownWebAssemblySdkPack" BeforeTargets="ProcessFrameworkReferences" |
There was a problem hiding this comment.
This target is already removed on main. The packs are correctly resolved from the installed workload in dotnet-latest
There was a problem hiding this comment.
I still see it on main
runtime/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs
Lines 178 to 185 in 906f6c7
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
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved crossgen2 resolution issues remain for R2R-enabled and disabled scenarios, including WASI SDK packaging.
Review details
Suppressed comments (5)
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets:653
ResolveReadyToRunCompilersdoes not always produce aCrossgen2Tool: whenPublishReadyToRunUseCrossgen2is false it validates/emits the crossgen tool instead (seesrc/tasks/Crossgen2Tasks/ResolveReadyToRunCompilers.cs:63-83,140-180). This target forwards that project setting unchanged even though the native relink always invokes crossgen2, so a project that disables R2R's crossgen2 selection reaches the fallback with no@(Crossgen2Tool)and then fails with the missing-generator error. Resolve crossgen2 independently of the R2R compiler preference, without changing whether ReadyToRun runs.
[!NOTE] This review comment was generated by GitHub Copilot.
<CallTarget Targets="ResolveReadyToRunCompilers"
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
src/mono/browser/build/BrowserWasmApp.CoreCLR.targets:653
- When
PublishReadyToRun=true,CreateReadyToRunImagesalready runsResolveReadyToRunCompilersthrough_PrepareForReadyToRunCompilation(src/tasks/Crossgen2Tasks/Microsoft.NET.CrossGen.targets:362-403), which leaves@(Crossgen2Tool)populated. This guard does not check that item, so it invokes the resolver a second time; the later assignment at line 715 can then produce a semicolon-separatedCrossgen2Path, which is not an executable path. Skip this call when@(Crossgen2Tool)is already available, and add coverage for R2R-enabled publish.
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
src/mono/wasi/build/WasiApp.CoreCLR.targets:29
- This target is only imported from
WasiApp.InTree.targets;Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgprojdoes not package it, and the packaged SDK importsWasiApp.targetsinstead. Consequently, an out-of-repo CoreCLR-WASI project never sees thisRequiresCrossgen2Pack/Crossgen2Toolpath and still cannot resolve the generator through the SDK. If WASI is in scope, package and select the CoreCLR target for the SDK; otherwise keep this explicitly in-tree rather than presenting it as the WASI equivalent of the browser fix.
[!NOTE] This review comment was created by GitHub Copilot.
<RequiresCrossgen2Pack Condition="'$(RequiresCrossgen2Pack)' == ''">true</RequiresCrossgen2Pack>
src/mono/wasi/build/WasiApp.CoreCLR.targets:97
ResolveReadyToRunCompilersdoes not always produce aCrossgen2Tool: whenPublishReadyToRunUseCrossgen2is false it validates/emits the crossgen tool instead (seesrc/tasks/Crossgen2Tasks/ResolveReadyToRunCompilers.cs:63-83,140-180). This target forwards that project setting unchanged even though the WASI relink always invokes crossgen2, so a project that disables R2R's crossgen2 selection reaches the fallback with no@(Crossgen2Tool)and then fails with the missing-generator error. Resolve crossgen2 independently of the R2R compiler preference, without changing whether ReadyToRun runs.
[!NOTE] This review comment was generated by GitHub Copilot.
<CallTarget Targets="ResolveReadyToRunCompilers"
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
src/mono/wasi/build/WasiApp.CoreCLR.targets:97
- When
PublishReadyToRun=true, the SDK publish path can already have runResolveReadyToRunCompilersthrough_PrepareForReadyToRunCompilation(src/tasks/Crossgen2Tasks/Microsoft.NET.CrossGen.targets:362-403), leaving@(Crossgen2Tool)populated. Because this guard ignores that item, it can resolve the compiler again and the fallback at line 171 can turn multiple item identities into an invalid semicolon-separatedCrossgen2Path. Skip the call when@(Crossgen2Tool)is already populated, and add an R2R-enabled regression case.
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
- Files reviewed: 9/9 changed files
- Comments generated: 0 new
- Review effort level: Lite
The stock wasm-tools workload does not acquire a browser-wasm crossgen2 nor set Crossgen2ToolPath, so BrowserWasmApp.CoreCLR.targets errors with 'crossgen2 was not found' for CoreCLR native-relink. The host crossgen2 pack already ships a wasm-capable crossgen2 (clrjit_universal_wasm), so reference it and point PortableCallHelpersGeneratorPath at it. Skipped for the from-source path (Crossgen2InBuildDir) and once the SDK provides crossgen2. Temporary until dotnet/runtime#133413 + dotnet/sdk#55785 flow into the daily SDK.
Fixes #133353.
Use the SDK to acquire crossgen2 for CoreCLR wasm native relinking without enabling ReadyToRun. Preserve explicit and in-repo generator paths, and remove the obsolete crossgen2 workload declarations and
Sdk.propsshim.Request the pack before the first restore. When no pack is resolved, report the missing generator rather than an unrelated ReadyToRun error.
Workload tests use their configured target framework and pack version.
Validation
Four build/publish regression cases passed. A standalone CoreCLR browser app outside the repository built and ran using repo-produced packages and native code passing a 24-byte struct by value.
Validation used official VMR SDK
12.0.100-alpha.1.26458.102, without an SDK code overlay, with the rebuilt native wasm SDK pack and existing repo-produced runtime packs.The required SDK support (dotnet/sdk#56119 and dotnet/sdk#55785) reached runtime through #133344.
Note
This PR description was drafted with GitHub Copilot.