From 2ca2e2e2c62d3d78aca6fea1ceb735d82cd58d6e Mon Sep 17 00:00:00 2001 From: Shivanshu <112751483+shivanshu877@users.noreply.github.com> Date: Sat, 9 May 2026 21:37:20 +0530 Subject: [PATCH 1/2] docs: add usage guides for WITOverlayGenerator and WITExtractor Addresses the two open items in issue #71 ("Enhance documentation"): - Example and usage of WITOverlayGenerator - Example and usage of WITExtractor Both documents are derived from the source code of the respective modules (WITOverlayGen.swift, WITExtractor.swift) and their SPM plugins (WITOverlayPlugin, WITExtractorPlugin). --- Documentation/ComponentModel/WITExtractor.md | 182 ++++++++++++++++++ .../ComponentModel/WITOverlayGenerator.md | 148 ++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 Documentation/ComponentModel/WITExtractor.md create mode 100644 Documentation/ComponentModel/WITOverlayGenerator.md diff --git a/Documentation/ComponentModel/WITExtractor.md b/Documentation/ComponentModel/WITExtractor.md new file mode 100644 index 00000000..09360555 --- /dev/null +++ b/Documentation/ComponentModel/WITExtractor.md @@ -0,0 +1,182 @@ +# WITExtractor — Usage Guide + +`WITExtractor` derives a [WIT](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Types) interface description from an **existing Swift module**. It is the reverse of `WITOverlayGenerator`: you start with Swift source and get a `.wit` file plus a Swift overlay that exports your module as a Wasm component. + +## When to use it + +Use `WITExtractor` when you have a Swift library you want to expose as a WebAssembly component but you do not want to maintain a hand-written WIT file. The extractor introspects the compiled module and generates both: + +1. A `.wit` file — the component interface other languages can consume. +2. A Swift overlay `.swift` file — Canonical-ABI entry points that export your Swift functions to the component model host. + +## How it works + +`WITExtractor` shells out to `swift-api-digester` (bundled with the Swift toolchain) to obtain a JSON description of the module's public API. It then: + +1. **Collects types** — maps Swift types to WIT types (see table below). +2. **Builds a source summary** — collects public functions, structs, and enums. +3. **Translates to WIT** — emits a `package :` block containing a single interface named after the module. +4. **Generates a Swift overlay** — emits `@_expose(wasm, ...)` entry points that lift/lower Canonical-ABI values. + +> **Platform constraint:** `WITExtractor` requires macOS 11+ because it uses `Foundation.Process` to launch `swift-api-digester`. It is not available on iOS, watchOS, tvOS, or visionOS. + +## Type mapping + +The extractor translates public Swift types into WIT types as follows: + +| Swift | WIT | Notes | +|-------|-----|-------| +| `struct` with stored properties | `record { … }` | Each stored property becomes a named field | +| `enum` with no associated values | `enum { … }` | Case names are kebab-cased | +| `enum` with associated values | `variant { … }` | Payload types are mapped recursively | +| `func(…) -> T` (free function) | `: func(…) -> T` | Only top-level public functions | +| `Bool` | `bool` | | +| `Int8`–`Int64`, `UInt8`–`UInt64` | `s8`–`s64`, `u8`–`u64` | | +| `Float`, `Double` | `f32`, `f64` | | +| `String` | `string` | | + +Types that cannot be mapped emit a diagnostic and are skipped; the rest of the interface is still emitted. + +--- + +## Usage via SPM command plugin (`WITExtractorPlugin`) + +`WITExtractorPlugin` is a **command plugin** (not a build plugin), so you run it explicitly with `swift package plugin`. + +### 1. Add WasmKit as a dependency + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/swiftwasm/WasmKit.git", .upToNextMinor(from: "0.2.0")), +], +``` + +### 2. Run the plugin + +```sh +swift package plugin --allow-writing-to-package-directory \ + extract-wit \ + --target MyLibrary +``` + +The plugin: +1. Builds `MyLibrary` in the default configuration. +2. Locates `swift-api-digester` next to `swiftc` in the active toolchain. +3. Runs `wit-tool extract-wit` with the built module. +4. Writes two output files to the plugin work directory: + - `MyLibrary.wit` — the extracted WIT interface. + - `MyLibrary_WITOverlay.swift` — the Swift export shim. + +Pass `--output-mapping ` to write a JSON file containing the exact output paths: + +```sh +swift package plugin --allow-writing-to-package-directory \ + extract-wit \ + --target MyLibrary \ + --output-mapping /tmp/output-paths.json +``` + +```json +{ + "witOutputPath": "/path/to/.build/plugins/WITExtractorPlugin/.../MyLibrary.wit", + "swiftOutputPath": "/path/to/.build/plugins/WITExtractorPlugin/.../MyLibrary_WITOverlay.swift" +} +``` + +### Setting the toolchain path manually + +If the plugin cannot infer `swift-api-digester` from the build log, set the environment variable: + +```sh +WIT_EXTRACTOR_SWIFTC_PATH=/path/to/swiftc \ + swift package plugin --allow-writing-to-package-directory \ + extract-wit --target MyLibrary +``` + +--- + +## Usage via `wit-tool` CLI + +For scripted use, call the `extract-wit` subcommand directly: + +```sh +swift run wit-tool extract-wit \ + --swift-api-digester $(xcrun --find swift-api-digester) \ + --module-name MyLibrary \ + --package-name my-library \ + --wit-output-path ./output/my-library.wit \ + --swift-output-path ./output/MyLibrary_WITOverlay.swift \ + -- -I .build/debug/Modules +``` + +Arguments after `--` are forwarded verbatim to `swift-api-digester`. + +| Flag | Required | Description | +|------|----------|-------------| +| `--swift-api-digester` | Yes | Path to the `swift-api-digester` binary | +| `--module-name` | Yes | Swift module name to introspect | +| `--package-name` | Yes | WIT package name (used in `package :`) | +| `--namespace` | No (default: `swift`) | WIT namespace | +| `--wit-output-path` | Yes | Where to write the `.wit` file | +| `--swift-output-path` | Yes | Where to write the overlay `.swift` file | + +--- + +## End-to-end example + +Given a Swift module `MathLib` with the following public API: + +```swift +// Sources/MathLib/MathLib.swift +public struct Vector2 { + public var x: Float + public var y: Float + public init(x: Float, y: Float) { self.x = x; self.y = y } +} + +public func addVectors(_ a: Vector2, _ b: Vector2) -> Vector2 { + Vector2(x: a.x + b.x, y: a.y + b.y) +} +``` + +Running `extract-wit --target MathLib --package-name math-lib` produces a WIT file similar to: + +```wit +// DO NOT EDIT. +// +// Generated by the WITExtractor + +package swift:math-lib + +interface math-lib { + record vector2 { + x: f32, + y: f32, + } + + add-vectors: func(a: vector2, b: vector2) -> vector2; +} +``` + +And a Swift overlay that provides the `@_expose(wasm, "math-lib#add-vectors")` entry point required by the component model ABI. + +--- + +## Diagnostics + +Types or functions that cannot be mapped are reported as diagnostics on stderr. The rest of the interface is still generated. For example: + +``` +warning: skipping field 'items' of 'Cart': unsupported type 'Array' +``` + +Review diagnostics to understand which parts of your API were excluded. + +--- + +## See also + +- [WITOverlayGenerator.md](WITOverlayGenerator.md) — going the other direction: WIT → Swift bindings +- [CanonicalABI.md](CanonicalABI.md) — how WIT values are encoded over core Wasm +- [WebAssembly Component Model spec](https://github.com/WebAssembly/component-model) diff --git a/Documentation/ComponentModel/WITOverlayGenerator.md b/Documentation/ComponentModel/WITOverlayGenerator.md new file mode 100644 index 00000000..2f5b0578 --- /dev/null +++ b/Documentation/ComponentModel/WITOverlayGenerator.md @@ -0,0 +1,148 @@ +# WITOverlayGenerator — Usage Guide + +`WITOverlayGenerator` generates Swift bindings from a [WIT](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Types) package. Given a `.wit` file that describes a *world* or *interface*, it produces Swift source that lets you either: + +- **implement** the interface as a WebAssembly guest component (compiled with SwiftWasm), or +- **call** the interface from a Swift host runtime that embeds WasmKit. + +## Concepts + +| Term | Meaning | +|------|---------| +| **WIT** | The IDL used by the WebAssembly Component Model to describe interfaces. | +| **Guest** | The Wasm module that *implements* an interface. | +| **Host** | The Swift process that *runs* the Wasm module via WasmKit. | +| **Overlay** | The generated Swift glue code that bridges WIT types to/from Swift. | + +The generator supports two targets, selected with `--target`: + +| `--target` | Generated code | Typical use | +|------------|---------------|-------------| +| `guest` | `#if arch(wasm32)` guards, `@_expose(wasm, ...)` entry points, Canonical-ABI lift/lower | Wasm component compiled with `swiftc -target wasm32-...` | +| `host` | `import WasmKit` bindings, function-call wrappers | macOS/Linux host that embeds WasmKit | + +## Usage via SPM build plugin (`WITOverlayPlugin`) + +`WITOverlayPlugin` is a **build tool plugin**: it runs automatically during `swift build` and generates a Swift overlay file for each target that uses it. + +### 1. Add WasmKit as a package dependency + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/swiftwasm/WasmKit.git", .upToNextMinor(from: "0.2.0")), +], +``` + +### 2. Configure the target + +```swift +.target( + name: "MyComponent", + dependencies: [ + // Required shim for the Canonical ABI runtime helpers + .product(name: "_CabiShims", package: "WasmKit"), + ], + plugins: [ + .plugin(name: "WITOverlayPlugin", package: "WasmKit"), + ] +), +``` + +> **Note:** `_CabiShims` **must** be listed as a dependency; the plugin emits a build error if it is absent. + +### 3. Place your WIT file + +Create a `wit/` directory directly inside the target's source directory and put your `.wit` file there: + +``` +Sources/ + MyComponent/ + wit/ + my-world.wit ← WIT package consumed by the plugin + MyComponent.swift ← your Swift implementation +``` + +Example `my-world.wit`: + +```wit +package example:my-component; + +world my-world { + export greet: func(name: string) -> string; +} +``` + +### 4. Build + +```sh +swift build +``` + +The plugin invokes `wit-tool generate-overlay --target guest ` and writes the generated file to the plugin work directory as `Overlay.swift`. That file is compiled automatically alongside your own Swift sources. + +The generated file contains the `#if arch(wasm32)` -guarded Canonical-ABI entry points that the Wasm component model linker expects. + +--- + +## Usage via `wit-tool` CLI + +For scripted or CI workflows you can call the CLI directly: + +```sh +swift run wit-tool generate-overlay \ + --target guest \ + path/to/wit/ \ + -o path/to/GeneratedOverlay.swift +``` + +Omit `-o` to print the generated source to stdout. + +For a host-side binding: + +```sh +swift run wit-tool generate-overlay \ + --target host \ + path/to/wit/ \ + -o path/to/HostBindings.swift +``` + +The host-side output `import WasmKit` and provides typed Swift wrappers that call into a loaded `WasmKit.Runtime` instance. + +### Validating a WIT package + +Before generating, you can validate your WIT package: + +```sh +swift run wit-tool validate path/to/wit/ +# or a single file: +swift run wit-tool validate path/to/interface.wit +``` + +--- + +## Type mapping + +| WIT type | Guest Swift type | Notes | +|----------|-----------------|-------| +| `bool` | `Bool` | | +| `u8`–`u64`, `s8`–`s64` | `UInt8`–`UInt64`, `Int8`–`Int64` | | +| `f32`, `f64` | `Float`, `Double` | | +| `string` | `String` | Copied through Wasm linear memory | +| `record { … }` | `struct` | Fields mapped recursively | +| `variant { … }` | `enum` with associated values | | +| `enum { … }` | `enum` (no payloads) | | +| `list` | `[T]` | | +| `option` | `T?` | | +| `result` | `Result` | | + +The Canonical ABI encoding/decoding is handled by the generated overlay code; you do not need to write any manual memory management. + +--- + +## See also + +- [CanonicalABI.md](CanonicalABI.md) — how WIT values are encoded as core Wasm values +- [SemanticsNotes.md](SemanticsNotes.md) — WIT semantics analysis in WasmKit +- [WITExtractor.md](WITExtractor.md) — going the other direction: Swift module → WIT +- [WebAssembly Component Model spec](https://github.com/WebAssembly/component-model) From ef224eb93cdc58ea9cc65bffc1aef72e90b37af6 Mon Sep 17 00:00:00 2001 From: shivanshu877 Date: Fri, 22 May 2026 11:22:56 +0530 Subject: [PATCH 2/2] docs: fix WITExtractor.md inaccuracies per @MaxDesiatov review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues: 1. CLI executable name was wrong — `wit-tool` doesn't exist; the Package.swift declares `WITTool`. Changed `swift run wit-tool` to `swift run WITTool`. 2. End-to-end example mixed the plugin form (`--target`) with the CLI `--package-name` arg. The plugin only accepts `--target`, `--sdk`, and `--output-mapping`, and auto-derives the WIT package name from `context.package.displayName`. Reworked the "Running…" snippet to use the plugin form properly, with a parenthetical pointing at the CLI form for callers that need to override the package name. 3. WITExtractor only extracts declarations annotated with `@_spi(WIT)` (confirmed in `Sources/WITExtractor/SourceSummary.swift` line 221). Added a note in the "When to use it" section and applied `@_spi(WIT)` to the example's `Vector2` and `addVectors`. --- Documentation/ComponentModel/WITExtractor.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Documentation/ComponentModel/WITExtractor.md b/Documentation/ComponentModel/WITExtractor.md index 09360555..8c96f44f 100644 --- a/Documentation/ComponentModel/WITExtractor.md +++ b/Documentation/ComponentModel/WITExtractor.md @@ -9,6 +9,8 @@ Use `WITExtractor` when you have a Swift library you want to expose as a WebAsse 1. A `.wit` file — the component interface other languages can consume. 2. A Swift overlay `.swift` file — Canonical-ABI entry points that export your Swift functions to the component model host. +> **Opt-in via `@_spi(WIT)`:** Only declarations annotated with `@_spi(WIT)` are included in the extracted interface. Public declarations without this attribute are deliberately skipped so you can keep an existing public API surface while exposing a focused subset to WIT. See the example below. + ## How it works `WITExtractor` shells out to `swift-api-digester` (bundled with the Swift toolchain) to obtain a JSON description of the module's public API. It then: @@ -98,10 +100,10 @@ WIT_EXTRACTOR_SWIFTC_PATH=/path/to/swiftc \ ## Usage via `wit-tool` CLI -For scripted use, call the `extract-wit` subcommand directly: +For scripted use, call the `extract-wit` subcommand on the `WITTool` executable directly: ```sh -swift run wit-tool extract-wit \ +swift run WITTool extract-wit \ --swift-api-digester $(xcrun --find swift-api-digester) \ --module-name MyLibrary \ --package-name my-library \ @@ -129,18 +131,28 @@ Given a Swift module `MathLib` with the following public API: ```swift // Sources/MathLib/MathLib.swift +@_spi(WIT) public struct Vector2 { public var x: Float public var y: Float public init(x: Float, y: Float) { self.x = x; self.y = y } } +@_spi(WIT) public func addVectors(_ a: Vector2, _ b: Vector2) -> Vector2 { Vector2(x: a.x + b.x, y: a.y + b.y) } ``` -Running `extract-wit --target MathLib --package-name math-lib` produces a WIT file similar to: +(Without `@_spi(WIT)` these declarations would be skipped by the extractor.) + +Running the plugin against this target: + +```sh +swift package plugin --allow-writing-to-package-directory extract-wit --target MathLib +``` + +(The plugin derives the WIT package name from `context.package.displayName`. To set it explicitly, use the CLI form with `--package-name math-lib`.) Produces a WIT file similar to: ```wit // DO NOT EDIT.