Skip to content

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

Open
Evangelink wants to merge 5 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 5 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 thread samples/WasiPlayground/Program.cs Outdated
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).
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.

Fixed in 95a79bf — comment now references #8557.

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')
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.

In 95a79bf I left `` at just wasi-experimental-net10 because that's all CI's `dotnet build` needs. `wasm-tools-net10` (which the README lists) is only needed for `dotnet publish`, which CI doesn't run — added a comment in the script to explain the discrepancy.

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.

Doh, my bad — meant to write that I left $RequiredWorkloads at just wasi-experimental-net10 because that's all CI's dotnet build needs. wasm-tools-net10 (which the README lists) is only needed for dotnet publish, which CI doesn't run — added a comment in the script to explain the discrepancy. (Previous reply had a PowerShell-escaping mishap that ate the variable name; this is the same content.)

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

Removed --skip-sign-check in 95a79bf. If the unsigned preview workload manifest turns out to fail to install in CI, I'll put it back with a tracking issue.

Evangelink and others added 2 commits May 26, 2026 16:42
- Program.cs: correct issue link from #8564 to #8557.
- README.md: drop trailing blank line (markdownlint MD012).
- WasiPlayground.csproj: only set IsTestingPlatformApplication when not running under 'dotnet test'. The Windows Debug Test step on TestFx.slnx now invokes WasmAppHost (because the proper wasi workload installation produces a real wasm bundle) and fails because Windows CI agents have no wasmtime on PATH.
- restore-toolset.ps1: drop --skip-sign-check (don't weaken supply-chain integrity) and note why wasm-tools-net10 is intentionally omitted (only needed for 'dotnet publish').

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 26, 2026 14:53
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: 2

Comment thread eng/restore-toolset.ps1
Comment on lines +1 to +16
# Installs .NET SDK workloads needed by projects in this repository.
#
# This script is dot-sourced by eng/common/build.ps1's InitializeCustomToolset
# AFTER InitializeDotNetCli has bootstrapped the repo-local .dotnet/ SDK, so
# $RepoRoot and the helpers from eng/common/tools.ps1 are in scope.
#
# Currently required by samples/WasiPlayground/WasiPlayground.csproj which
# targets net10.0 with <UsingWasiRuntimeWorkload>true</UsingWasiRuntimeWorkload>
# and would otherwise fail with NETSDK1147 in CI.

$RequiredWorkloads = @('wasi-experimental-net10')

# Note: `wasm-tools-net10` is documented in samples/WasiPlayground/README.md
# as a prerequisite for `dotnet publish`, but it is not needed by the repo's
# `dotnet build` so we keep the CI install minimal.

Comment on lines +9 to +36
- .NET 10 (or newer) SDK matching `global.json` at the repo root.
- The `wasi-experimental-net10` and `wasm-tools-net10` SDK workloads:

```cmd
dotnet workload install wasi-experimental-net10 wasm-tools-net10
```

- [wasmtime](https://docs.wasmtime.dev/cli-install.html) on `PATH`.

Then:

1. From the repo root, publish the sample:

```cmd
dotnet publish samples\WasiPlayground\WasiPlayground.csproj -c Debug -f net10.0
```

2. The pre-built `dotnet.wasm` does not embed the ICU data file, so copy it
next to the bundle. First list the installed runtime-pack version (a
single folder name such as `10.0.8`), then substitute it into the copy
command:

```cmd
dir /b .dotnet\packs\Microsoft.NETCore.App.Runtime.Mono.wasi-wasm

copy .dotnet\packs\Microsoft.NETCore.App.Runtime.Mono.wasi-wasm\<runtime-version>\runtimes\wasi-wasm\native\icudt.dat ^
artifacts\bin\WasiPlayground\Debug\net10.0\wasi-wasm\AppBundle\
```
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