WACS.Cli
1.3.0
See the version list below for details.
dotnet tool install --global WACS.Cli --version 1.3.0
dotnet new tool-manifest
dotnet tool install --local WACS.Cli --version 1.3.0
#tool dotnet:?package=WACS.Cli&version=1.3.0
nuke :add-package WACS.Cli --version 1.3.0
WACS
The unified WebAssembly toolchain for .NET. One CLI — wacs — covers
running, compiling, inspecting, and generating bindings for
WebAssembly modules and components, backed by the
WACS interpreter, the
WACS.Transpiler.Lib
AOT engine, and the
WACS.ComponentModel.Bindgen.Lib
binding generator.
Note: This tool supersedes
wasm-transpile(WACS.Transpiler). The legacy package is deprecated; installWACS.Cliinstead. The package id isWACS.Cli(the bareWACSid is the runtime library,Wacs.Core); the tool command users type iswacs.
Installation
dotnet tool install -g WACS.Cli
wacs --help
Verb structure
wacs uses a verb-based subcommand layout (matches wasmtime /
wasmer industry precedent — keeps "run" flags from cluttering the
"compile" surface):
| Verb | Purpose | Engine |
|---|---|---|
run |
Execute a .wasm module |
interpreter (default) or transpiler |
build |
Transpile to a .dll |
transpiler |
aot |
Build a self-contained NativeAOT native binary (transpile + scaffold + dotnet publish) |
transpiler + ILC |
inspect |
Diagnostics: WAT dump, stats, exports/imports | parse-only |
bindgen |
Generate C# bindings from WIT (forward) or regenerate WIT + bindings from a transpiled .dll (reverse) |
parse-only |
Direct-run shortcut
If the first argument is a .wasm / .wat file path that exists,
wacs defaults to the run verb:
wacs my.wasm # → wacs run my.wasm
wacs build app.wasm -o app.dll # explicit verb
wacs inspect app.wasm --stats # explicit verb
Verb keywords (run / build / inspect / help / --help / -h
/ version / --version) bypass the shortcut. Anything else that
isn't a verb keyword and doesn't look like a wasm path is treated as a
verb name (so a typo gives a parse error rather than running the
wrong file).
Engine choice
--engine interpreter (default for run) parses + executes via the
WACS interpreter — the AOT-safe path with full instrumentation hooks
(gas counter, dotTrace bracket, per-instruction logging, stats).
--engine transpiler JITs the module to .NET IL via
Reflection.Emit, then runs through CLR-native dispatch with imports
proxied back to the interpreter (mixed-mode). Roughly 64× the
interpreter's throughput on compute-bound workloads (CoreMark on M3
Max: 17 552 iter/s vs 274 for the polymorphic interpreter — see the
root README
for the full table).
build always uses the transpiler — its job is to produce a .dll.
run and build both auto-detect component vs core wasm via the
layer header byte; component-mode routing happens transparently
when you pass .component.wasm input.
wacs run — execution
wacs run [files]... [options] [-- argv...]
Examples
Run with _start (WASI command):
wacs run app.wasm -e PATH=/usr/bin -d ./data
# WASI Preview 1: env vars, preopened directory, _start dispatch
Invoke a specific export with arguments:
wacs run module.wasm --call add -- 7 35
# → Result:[i32=42]
Trailing args after -- are forwarded to the chosen export (parsed
as the function's wasm parameter types) or to WASI argv when
running _start.
Run a component with WASI Preview 2 (direct-linked):
wacs run app.component.wasm --wasip2
# auto-routes to transpiler engine + WasiPreview2Bundle when
# --wasip2 / --host-package is set (those flags only make sense
# with the bundle path)
Multi-module composition via ModuleLinker:
wacs run a.wasm b.wasm --call quadruple -- 7
# → 28 (B's quadruple → A's double, twice, via shared runtime)
Each input registers under its filename basename so cross-module imports resolve through the runtime's binding table. The chosen export runs on the last input.
Through the AOT transpiler (mixed-mode):
wacs run app.wasm --engine transpiler --wasi
# transpiles in-process via Reflection.Emit, then runs through
# CLR-native dispatch with WASI imports proxied back to the
# interpreter
Profile a hot path:
wacs run app.wasm --profile
# JetBrains dotTrace measure-profiler bracket; snapshot lands
# in the OS-default profiler temp dir
Instrumented runs (interpreter only):
wacs run app.wasm --gas-limit 1000000 --log-gas
# trap if total instructions exceed 1M; print final count
wacs run app.wasm --log-execution Calls --calculate-lines
# log every call instruction with its source line number
wacs run app.wasm --stats Function
# per-function instruction counts after the run
Custom host bindings:
wacs run app.wasm --bind ./MyGameHost.dll
# load + activate every IBindable in MyGameHost.dll, wire into runtime
run flag reference
| Flag | Default | Notes |
|---|---|---|
--call <export> |
_start |
Function to invoke. Args after -- are parsed per its wasm signature. |
--engine |
interpreter |
interpreter or transpiler (Reflection.Emit AOT, mixed-mode imports). |
-m, --module <name> |
_ |
Name to register the instantiated module under. |
-e, --env K=V |
— | WASI Preview 1 environ. Repeat or comma-separate. |
-d, --dir <path> |
— | WASI Preview 1 preopen. Repeat or comma-separate. |
--wasi |
off | Bind WASI Preview 1 host imports. |
--bind <asm> |
— | Load IBindable host packages. Repeat or comma-separate. |
--host-package <name> |
— | Component-mode [WitSource] host package(s). |
--wasip2 |
off | Shorthand --host-package Wacs.WASI.Preview2. |
--profile |
off | JetBrains dotTrace measure-profiler session. |
--log-gas |
off | Print total instructions executed. |
--gas-limit <N> |
0 (∞) | Trap if instructions exceed N. |
--log-progress <N> |
-1 (off) | Print . every N instructions. |
--log-execution <flags> |
None | None\|Computes\|Calls\|Branches\|Memory\|All. |
--calculate-lines |
off | Line-number mapping for instruction logs. |
--stats <detail> |
None | None\|Total\|Instruction\|Function. |
--super |
off | Super-instruction fusion (interpreter only). |
--switch |
off | Source-generated switch runtime (interpreter only). |
--simd |
scalar | --engine transpiler SIMD strategy: interpreter | scalar | intrinsics. |
--no-tail-calls |
off | --engine transpiler only. |
--max-fn-size <N> |
0 | --engine transpiler only. Skip large fns. |
--data-storage |
compressed | --engine transpiler only: compressed | raw | static. |
--no-validate |
off | Skip module validation after parse. |
-v, --verbose |
off | Parser timing + diagnostics on stderr. |
wacs build — transpile to .dll
wacs build [files]... -o <output> [options]
Examples
Single-file core wasm:
wacs build app.wasm -o app.dll
Multi-file linker composition:
wacs build a.wasm b.wasm -o b.dll
# → wrote a.dll, b.dll (siblings land at <basename>.dll alongside)
Component with WASI Preview 2 + runnable Main:
wacs build app.component.wasm --wasip2 --emit-main \
--entry-point greet -o app.dll
# Component-mode: --wasip2 resolves WASI imports to inline IL
# (no delegate hop). --emit-main bakes Program.Main(string[])
# into the output that constructs the bundle, instantiates the
# module, and invokes greet.
Tune the output:
wacs build app.wasm -o app.dll \
--simd intrinsics \
--data-storage static \
--namespace MyApp.Wasm
# SIMD via Vector128<T> hardware intrinsics; data segments as
# static byte[] fields; root namespace MyApp.Wasm
build flag reference
| Flag | Default | Notes |
|---|---|---|
-o, --output <path> |
(required) | Output .dll path. With multi-input, names the LAST input; siblings → <basename>.dll. |
--namespace |
CompiledWasm |
Root namespace for generated types. |
-m, --module <name> |
WasmModule |
Generated Module class name. |
--wasi |
off | Bake WASI Preview 1 bindings into the build runtime. |
--bind <asm> |
— | Custom IBindable host libraries (build-time). |
--host-package <name> |
— | Component-mode [WitSource] packages. |
--wasip2 |
off | Shorthand --host-package Wacs.WASI.Preview2. |
--emit-main |
off | Bake Program.Main(string[]) into the output. |
--entry-point <export> |
_start |
Export Main invokes. |
--main-class <name> |
Program |
Generated Program class name. |
--simd |
scalar | interpreter \| scalar \| intrinsics. |
--no-tail-calls |
off | Disable CIL tail. prefix. |
--max-fn-size <N> |
0 | Skip transpilation of large functions. |
--data-storage |
compressed | compressed \| raw \| static. |
--gc-checking <flags> |
None | Extra GC type-check layers. |
--no-validate |
off | Skip module validation. |
-v, --verbose |
off | Diagnostics + per-function counts. |
wacs aot — wasm → NativeAOT native binary
wacs aot <input.wasm> [-o <output>] [options] [-- argv...]
End-to-end: transpile the input wasm to a stable-named .dll,
scaffold a throwaway consumer csproj that statically references it
plus the WACS runtime support assemblies, and run dotnet publish -p:PublishAot=true -r <rid>. The resulting native binary is copied
to the output path; the temp build dir is removed unless
--keep-temp.
Cold-start equivalent of transpiler-aot-linked from
docs/COLDSTART.md: new Module() → typed
direct call into the wasm function. No JIT, no Reflection.Emit, no
Assembly.Load, no MethodInfo.Invoke at run time.
Examples
Compute-only module to native binary:
wacs aot fib.wasm -o fib
./fib # native exe — no .NET runtime required
WASI Preview 1 (the wasi-libc / wasi-sdk world):
wacs aot coremark.wasm --wasi -o coremark
./coremark 1 1 1 1 # trailing argv forwarded to the guest
--wasi references WACS.WASI.Preview1 from the scaffolded
consumer; the source generator in WACS.HostBindings.SourceGen emits
an IImports adapter that wires the wasm's
wasi_snapshot_preview1.* imports straight to the
[WacsImport]-annotated statics. No reflection, no DispatchProxy,
fully NativeAOT-trim-safe.
Component with WASI Preview 2:
wacs aot app.component.wasm --wasip2 --entry-point greet -o app
./app
The component's wasi:* imports are direct-linked at transpile time
against the typed C# host interfaces in WACS.WASI.Preview2; the
consumer constructs the WasiPreview2Bundle via
Microsoft.Extensions.DependencyInjection before invoking the
named export.
Pick the AotLinked emission target (smaller binary):
wacs aot fib.wasm --aot-linked -o fib
# Skips the codec wrapper. Default emission keeps it; AotLinked
# inlines memory/data/globals/tables straight into the Module ctor
# and lets the trimmer dead-strip the codec machinery.
aot flag reference
| Flag | Default | Notes |
|---|---|---|
-o, --output <path> |
<inputBasename> in cwd |
Path for the produced native binary (no .exe suffix by default — set explicitly on Windows). |
--rid <rid> |
host RID | Target .NET runtime identifier (osx-arm64, linux-x64, win-x64). |
--entry-point <export> |
_start |
WASM export the emitted Program.Main invokes (scalar args only). |
--namespace <name> |
WacsAot |
Root namespace for generated types in the transpiled .dll. |
--simd |
scalar |
interpreter \| scalar \| intrinsics. |
--aot-linked |
off | Use the EmissionTarget.AotLinked emission target — skips the codec wrapper for a smaller binary. Covers memory / data / globals / tables / element segments. |
--wasi |
off | Bake WACS.WASI.Preview1 bindings into the produced binary. |
--wasip2 |
off | Component-mode counterpart to --wasi — direct-links wasi:* imports against WACS.WASI.Preview2. |
--preopen <H::G> |
— | WASI directory preopen <host-path>::<guest-path>. Repeat for multiple. Only with --wasi. |
--keep-temp |
off | Don't delete the scaffolded build dir (useful for inspecting the generated csproj / Program.cs). |
-v, --verbose |
off | Print each step (transpile, scaffold, publish, copy). |
Trailing positional args after the input file are forwarded to the
guest as argv when --wasi is set.
wacs inspect — diagnostics
wacs inspect <file> [options]
Parse-only. No instantiation, no execution, no transpilation.
Examples
Stats summary (default behavior with no flags):
$ wacs inspect module.wasm
file module.wasm
kind core wasm module
types 3
functions 12 (4 imported)
exports 5
memories 1
tables 1
globals 0
data 2 segment(s), 1024 bytes total
elements 1 segment(s)
Component stats:
$ wacs inspect app.component.wasm
file app.component.wasm
kind wasm component
core modules 3 (768 bytes total)
nested components 0
types 7
canons 4
exports 1
custom sections 1
raw sections 26
List exports / imports:
wacs inspect module.wasm --exports
wacs inspect module.wasm --imports
Dump WAT (round-trips back through the text parser):
wacs inspect module.wasm --dump-wat # to stdout
wacs inspect module.wasm --dump-wat --output-dir . # writes module.wat
inspect flag reference
| Flag | Notes |
|---|---|
--stats |
Default when no other flag is given. |
--exports |
List exports (kind + name). |
--imports |
List imports (kind + module.name). |
--dump-wat |
Render parser-friendly WAT (core only — components route to their embedded core modules). |
--output-dir <path> |
Write <basename>.wat here instead of stdout. |
wacs bindgen — WIT ↔ C# bindings
wacs bindgen <input> -o <output-dir> [options]
Two directions on one verb, auto-detected by input shape:
- Forward — a
.witfile or a directory tree of WIT files (recurses intodeps/) →[WitSource]-tagged C# interfaces. Use this when authoring components: pre-generate the host binding surface for offline AOT targets that can't use the runtime transpiler. - Reverse — a transpiled
.dllcarrying embedded component-type metadata → regenerated WIT + C# bindings. Use this when the original.witis gone but the shipped.dllis still on hand.
Examples
Forward, single WIT file:
wacs bindgen ./wit/hello.wit -o ./Generated/
Forward, WIT directory tree (with deps/):
wacs bindgen ./wit -o ./Generated/
# Recurses into deps/ subdirectories. Headerless files attribute
# to the parent package per the wit-bindgen-csharp convention.
Reverse, regenerate from a transpiled .dll:
wacs bindgen ./app.dll -o ./regenerated/
# Extracts the embedded component-type metadata, decodes it to
# WIT, and regenerates the [WitSource]-tagged C# binding surface.
# Errors with exit code 3 if the .dll has no embedded WIT.
Reverse with raw bytes preserved:
wacs bindgen ./app.dll -o ./regen --write-wit
# Also writes <basename>.componenttype.bin alongside the .cs files
# (useful for `wasm-tools component wit` round-trip inspection).
bindgen flag reference
| Flag | Notes |
|---|---|
<input> (positional) |
.wit / WIT directory / .dll. Direction inferred from extension. |
-o, --output <dir> (required) |
Output directory. One .cs file per emitted interface / world / package. |
--namespace <name> |
Root C# namespace override. Currently a warning — Phase 1d uses pinned wit-bindgen-csharp conventions; explicit override is a follow-up. |
--write-wit |
Reverse mode only. Persist the raw extracted component-type bytes alongside the .cs files. |
-v, --verbose |
Per-file emission progress. |
Migration from wasm-transpile
The legacy wasm-transpile (WACS.Transpiler) CLI keeps working
unchanged — it ships a stderr deprecation banner pointing at wacs
but every flag still functions. Concrete migrations:
wasm-transpile |
wacs |
|---|---|
wasm-transpile -i x.wasm -o x.dll |
wacs build x.wasm -o x.dll |
wasm-transpile -i x.wasm -o x.dll --run |
wacs run x.wasm |
wasm-transpile -i x.wasm -o x.dll --wasi --run |
wacs run x.wasm --wasi |
wasm-transpile -i x.wasm -o x.dll --wasip2 --emit-main |
wacs build x.wasm --wasip2 --emit-main -o x.dll |
wasm-transpile -i a.wasm,b.wasm -o b.dll |
wacs build a.wasm b.wasm -o b.dll |
wasm-transpile -i x.wasm -o x.dll --engine interpreter --run |
wacs run x.wasm --engine interpreter |
The standalone
WACS.ComponentModel.Bindgenpackage (wit-bindgen-wacsCLI) was rolled into thebindgenverb here before its first NuGet release; users only ever seewacs bindgen. TheWACS.ComponentModel.Bindgen.Libpackage (programmatic surface) is unaffected — source generators and build-time integrations should keep referencing it directly.
The -i short flag is retired (Console used it for --invoke,
Transpiler for --input — incompatible). Inputs are positional in
wacs; the --call long flag replaces --invoke.
License
WACS is distributed under the Apache 2.0 License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.10.0 | 95 | 5/18/2026 |
| 1.8.0 | 95 | 5/16/2026 |
| 1.7.6 | 92 | 5/14/2026 |
| 1.7.5 | 84 | 5/14/2026 |
| 1.7.4 | 96 | 5/12/2026 |
| 1.7.2 | 87 | 5/12/2026 |
| 1.6.7 | 94 | 5/12/2026 |
| 1.5.26 | 107 | 5/11/2026 |
| 1.5.21 | 103 | 5/10/2026 |
| 1.5.18 | 98 | 5/10/2026 |
| 1.5.14 | 93 | 5/10/2026 |
| 1.4.1 | 100 | 5/9/2026 |
| 1.3.0 | 97 | 5/7/2026 |
| 1.2.0 | 110 | 5/1/2026 |
| 1.1.0 | 99 | 5/1/2026 |