Skip to content

Make samples/WasiPlayground actually publish and boot MTP on wasi-wasm#8558

Open
Evangelink wants to merge 3 commits into
mainfrom
dev/amauryleve/wasi-playground-fixups
Open

Make samples/WasiPlayground actually publish and boot MTP on wasi-wasm#8558
Evangelink wants to merge 3 commits into
mainfrom
dev/amauryleve/wasi-playground-fixups

Conversation

@Evangelink
Copy link
Copy Markdown
Member

Summary

Following PRs #7137 and #7146, Microsoft.Testing.Platform actually boots on the wasi-wasm runtime - but samples/WasiPlayground itself wouldn't even publish a dotnet.wasm bundle 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.csproj

Add three properties (with an inline comment explaining why) so dotnet publish produces a runnable bundle on 11.0.100-preview.5:

  • <UsingWasiRuntimeWorkload>true</UsingWasiRuntimeWorkload> - workaround for the preview-5 SDK manifest issue where $(UsingWasiRuntimeWorkload) never resolves to true for net10.0 projects, so the WASI Sdk targets are never imported and no dotnet.wasm bundle is produced.
  • <WasmSingleFileBundle>false</WasmSingleFileBundle> - single-file bundling requires the wasi-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 load icudt.dat from cwd.

Also removed the duplicate <OutputType>Exe</OutputType> line.

samples/WasiPlayground/README.md

Rewrite the README to reflect the current working procedure:

  1. Install wasi-experimental-net10 + wasm-tools-net10 workloads.
  2. dotnet publish samples\WasiPlayground\WasiPlayground.csproj -c Debug -f net10.0.
  3. Copy icudt.dat from the runtime pack into the AppBundle (still required even with InvariantGlobalization=true because the pre-built dotnet.wasm does not embed it).
  4. Run with wasmtime run -S http --dir . -- dotnet.wasm WasiPlayground (the -S http flag is required because the runtime imports wasi:http).

Document the current end-state: MTP boots and prints [wasi-wasm - net10.0], then fails inside the compiler-generated Main wrapper that synchronously waits on Task (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:

.\.dotnet\dotnet.exe publish samples\WasiPlayground\WasiPlayground.csproj -c Debug -f net10.0
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\
cd artifacts\bin\WasiPlayground\Debug\net10.0\wasi-wasm\AppBundle
wasmtime run -S http --dir . -- dotnet.wasm WasiPlayground

Output:

Microsoft.Testing.Platform v...  [wasi-wasm - net10.0]
...
Unhandled Exception:
System.PlatformNotSupportedException: Arg_PlatformNotSupported
   at System.Threading.Tasks.Task.InternalWaitCore(...)
   at System.Threading.Tasks.Task.InternalWait(...)
   at Program.<Main>(String[] args)

That final exception is the known async-Main blocker - tracked in #5366 - and intentionally not addressed here.

Related issues / follow-ups

Copilot AI review requested due to automatic review settings May 25, 2026 12:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.csproj WASI publish settings (disable single-file bundle, enable invariant globalization, force WASI workload import workaround).
  • Rewrite samples/WasiPlayground/README.md with 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

Comment thread samples/WasiPlayground/README.md Outdated
Comment on lines +26 to +29
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 ^
Copy link
Copy Markdown
Member Author

@Evangelink Evangelink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# Dimension Verdict
17 Documentation Accuracy 🟡 2 MODERATE

✅ 20/21 dimensions clean.

  • Documentation Accuracy — InvariantGlobalization explanation 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 OutputType correctly 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\
```

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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 load icudt.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). |

Comment thread samples/WasiPlayground/README.md Outdated
`async Task Main`, which calls `Task.GetAwaiter().GetResult()` &rarr;
`Task.Wait()`. On single-threaded `wasi-wasm` (no thread pool) this throws
`PlatformNotSupportedException`. Tracked separately so this sample can act as
the canonical repro.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Evangelink and others added 3 commits May 25, 2026 18:09
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>
Copilot AI review requested due to automatic review settings May 25, 2026 16:11
@Evangelink Evangelink force-pushed the dev/amauryleve/wasi-playground-fixups branch from 8283951 to 72e6094 Compare May 25, 2026 16:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 3

Comment on lines +17 to +18
// AddHangDumpProvider is intentionally not registered: hang dumps rely on
// System.Diagnostics.Process which is unsupported on wasi (see #8564).
Comment thread eng/restore-toolset.ps1
# targets net10.0 with <UsingWasiRuntimeWorkload>true</UsingWasiRuntimeWorkload>
# and would otherwise fail with NETSDK1147 in CI.

$RequiredWorkloads = @('wasi-experimental-net10')
Comment thread eng/restore-toolset.ps1
Comment on lines +37 to +41
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants