Make samples/WasiPlayground actually publish and boot MTP on wasi-wasm#8558
Make samples/WasiPlayground actually publish and boot MTP on wasi-wasm#8558Evangelink wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the samples/WasiPlayground sample so it can be published into a runnable wasi-wasm bundle with the repo-pinned .NET SDK, and refreshes the README to document the current end-to-end repro path for MTP on WASI.
Changes:
- Adjust
WasiPlayground.csprojWASI publish settings (disable single-file bundle, enable invariant globalization, force WASI workload import workaround). - Rewrite
samples/WasiPlayground/README.mdwith updated prerequisites, publish/run steps, and current known failure mode.
Show a summary per file
| File | Description |
|---|---|
| samples/WasiPlayground/WasiPlayground.csproj | Adds WASI publish/workaround properties so dotnet publish produces a runnable dotnet.wasm bundle on the pinned preview SDK. |
| samples/WasiPlayground/README.md | Documents the updated workflow to publish, stage ICU data, and run under wasmtime; records current platform limitations. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 1
| 2. The pre-built `dotnet.wasm` does not embed the ICU data file, so copy it next to the bundle: | ||
|
|
||
| ```cmd | ||
| copy .dotnet\packs\Microsoft.NETCore.App.Runtime.Mono.wasi-wasm\10.0.7\runtimes\wasi-wasm\native\icudt.dat ^ |
Evangelink
left a comment
There was a problem hiding this comment.
| # | Dimension | Verdict |
|---|---|---|
| 17 | Documentation Accuracy | 🟡 2 MODERATE |
✅ 20/21 dimensions clean.
- Documentation Accuracy —
InvariantGlobalizationexplanation needs clarification to prevent user confusion - Documentation Accuracy — Missing issue reference (#5366) for the tracked blocker
This PR successfully makes the WasiPlayground sample buildable and documents the current WASI support status. The changes are well-scoped and the MSBuild properties are correctly configured. The two documentation clarity issues above are recommended improvements to prevent user confusion, but they don't block the PR's core objective.
Dimensions reviewed:
- ✅ Code Structure & Simplification — Duplicate
OutputTypecorrectly removed - ✅ MSBuild Authoring — Properties correctly formatted with helpful comments
- ✅ Naming & Conventions — Follows conventions
- ✅ Scope & PR Discipline — Single concern, appropriate references
- 🟡 Documentation Accuracy — Two clarity improvements needed (see inline comments)
Dimensions N/A (no applicable code): Algorithmic Correctness, Threading & Concurrency, Security, Public API, Performance, Cross-TFM, Resource Management, Defensive Coding, Localization, Test Isolation, Assertion Quality, Flakiness, Test Coverage, Data-Driven Tests, Analyzer Quality, IPC Wire, Build Infrastructure
Generated by Expert Code Review (on open) for issue #8558 · ● 1.1M
| copy .dotnet\packs\Microsoft.NETCore.App.Runtime.Mono.wasi-wasm\10.0.7\runtimes\wasi-wasm\native\icudt.dat ^ | ||
| artifacts\bin\WasiPlayground\Debug\net10.0\wasi-wasm\AppBundle\ | ||
| ``` | ||
|
|
There was a problem hiding this comment.
[MODERATE] Documentation Accuracy
The interaction between InvariantGlobalization=true and the manual icudt.dat copy step is confusing. The table at the bottom says:
InvariantGlobalization=true| Trimmed builds otherwise crash on startup trying to loadicudt.dat.
But step 2 still instructs users to manually copy icudt.dat. This creates ambiguity:
- If the setting prevents crashes, why copy the file?
- If you still need the file, what does the setting accomplish?
Scenario: A developer reads the table, sees that InvariantGlobalization=true prevents the icudt.dat crash, and skips step 2 thinking it's unnecessary. The sample then fails at runtime.
Recommendation: Clarify in the table explanation that the property prevents the trimmer from crashing, but the pre-built dotnet.wasm binary still requires icudt.dat at runtime because it was compiled with ICU support. For example:
| `InvariantGlobalization=true` | Prevents the trimmer from crashing during publish, but the pre-built `dotnet.wasm` still requires `icudt.dat` at runtime (see step 2). || `async Task Main`, which calls `Task.GetAwaiter().GetResult()` → | ||
| `Task.Wait()`. On single-threaded `wasi-wasm` (no thread pool) this throws | ||
| `PlatformNotSupportedException`. Tracked separately so this sample can act as | ||
| the canonical repro. |
There was a problem hiding this comment.
[MODERATE] Documentation Accuracy
Missing issue reference. The text says "Tracked separately" but doesn't link to the tracking issue. According to the PR description, this should reference #5366 (the Task.InternalWaitCore blocker).
Recommendation: Add the issue link for traceability:
`PlatformNotSupportedException`. Tracked in issue #5366 so this sample can act as
the canonical repro.After PRs #7137 and #7146, Microsoft.Testing.Platform boots on the wasi-wasm runtime, but the sample wouldn't even produce a `dotnet.wasm` bundle out of the box on the current preview SDK (`11.0.100-preview.5`): * `WorkloadManifest.Wasi.targets` only resolves `$(UsingWasiRuntimeWorkload)` to `true` when `$(TargetsCurrent)` (net11.0) is true, never when `$(TargetsNet10)` is true, so the WASI Sdk targets are never imported. * The pre-built `dotnet.wasm` tries to read `icudt.dat` from `cwd` even when `InvariantGlobalization` is on, so trimmed builds crash on startup. * `WasmSingleFileBundle=true` requires `wasi-sdk` (clang) to relink the native runtime, which is a heavy prerequisite for a sample. Set the matching properties explicitly on the project so a plain `dotnet publish` produces a runnable bundle, and document the new working build / run procedure (and the remaining `Task.Wait` blocker tracked in #5366) in the README. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
samples/WasiPlayground/WasiPlayground.csproj sets <UsingWasiRuntimeWorkload> which makes Microsoft.NET.Sdk.ImportWorkloads.targets emit NETSDK1147 unless the wasi-experimental-net10 workload is installed in the repo-local .dotnet/ SDK that Arcade bootstraps. Plug into Arcade's restore-toolset.ps1 extension point so the workload is installed after InitializeDotNetCli, both in CI and for local 'build.cmd' runs. The script fast-paths when the workload is already installed by parsing 'dotnet workload list' output (no network). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Program.cs: - Drop AddHangDumpProvider() registration: PR #8564 marked it [UnsupportedOSPlatform(\"wasi\")], so CA1416 now flags the call site in a wasi-wasm sample. Hang dumps rely on System.Diagnostics.Process which is not available on wasi; leave a comment in place of the call to explain the intentional omission. README.md: - Replace the hard-coded runtime-pack version 10.0.7 (already out of sync with global.json 10.0.8) with a 'dir /b' lookup followed by a <runtime-version> placeholder. - Clarify InvariantGlobalization=true: it prevents the trimmer from crashing during publish but the pre-built dotnet.wasm still loads ICU at runtime, so step 2 is still required. - Link the tracked async-Main blocker to #5366 instead of saying 'Tracked separately'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8283951 to
72e6094
Compare
| // AddHangDumpProvider is intentionally not registered: hang dumps rely on | ||
| // System.Diagnostics.Process which is unsupported on wasi (see #8564). |
| # targets net10.0 with <UsingWasiRuntimeWorkload>true</UsingWasiRuntimeWorkload> | ||
| # and would otherwise fail with NETSDK1147 in CI. | ||
|
|
||
| $RequiredWorkloads = @('wasi-experimental-net10') |
| Write-Host "Installing .NET SDK workloads: $($missing -join ', ')" | ||
| & $dotnet workload install @missing --skip-sign-check | ||
| if ($LASTEXITCODE -ne 0) { | ||
| Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install workloads '$($missing -join ', ')' (dotnet workload install exit code $LASTEXITCODE)." | ||
| ExitWithExitCode $LASTEXITCODE |
Summary
Following PRs #7137 and #7146, Microsoft.Testing.Platform actually boots on the
wasi-wasmruntime - butsamples/WasiPlaygrounditself wouldn't even publish adotnet.wasmbundle on the current preview SDK. This PR makes the sample build and run as far as MTP currently can on wasi, and documents the working procedure so others can repro the remaining blockers.Changes
samples/WasiPlayground/WasiPlayground.csprojAdd three properties (with an inline comment explaining why) so
dotnet publishproduces a runnable bundle on11.0.100-preview.5:<UsingWasiRuntimeWorkload>true</UsingWasiRuntimeWorkload>- workaround for the preview-5 SDK manifest issue where$(UsingWasiRuntimeWorkload)never resolves totruefor net10.0 projects, so the WASI Sdk targets are never imported and nodotnet.wasmbundle is produced.<WasmSingleFileBundle>false</WasmSingleFileBundle>- single-file bundling requires thewasi-sdk(clang) toolchain to relink the native runtime, which is a heavy prerequisite to drop on a sample. Keeping the managed assemblies on disk avoids it.<InvariantGlobalization>true</InvariantGlobalization>- the trimmed build otherwise crashes on startup trying to loadicudt.datfromcwd.Also removed the duplicate
<OutputType>Exe</OutputType>line.samples/WasiPlayground/README.mdRewrite the README to reflect the current working procedure:
wasi-experimental-net10+wasm-tools-net10workloads.dotnet publish samples\WasiPlayground\WasiPlayground.csproj -c Debug -f net10.0.icudt.datfrom the runtime pack into theAppBundle(still required even withInvariantGlobalization=truebecause the pre-builtdotnet.wasmdoes not embed it).wasmtime run -S http --dir . -- dotnet.wasm WasiPlayground(the-S httpflag is required because the runtime importswasi:http).Document the current end-state: MTP boots and prints
[wasi-wasm - net10.0], then fails inside the compiler-generatedMainwrapper that synchronously waits onTask(tracked in #5366) - this PR doesn't try to fix that upstream issue, it just makes the sample reproduce it cleanly.Also call out the non-obvious build switches in a small table at the bottom of the README.
Validation
Verified locally on Windows with the repo-local
Q:\src\testfx\.dotnet\dotnet.exe:Output:
That final exception is the known async-
Mainblocker - tracked in #5366 - and intentionally not addressed here.Related issues / follow-ups
Task.InternalWaitCoreblocker shown above)[UnsupportedOSPlatform("wasi")]to HangDump / Console / Process surface (filed during this investigation)