Skip to content

fix: bring documentation and tutorials up to date with latest tooling #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
417 changes: 245 additions & 172 deletions component-model/examples/example-host/Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions component-model/examples/example-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
[dependencies]
async-std = { version = "1.12.0", features = ["attributes"] }
clap = { version = "4.3.19", features = ["derive"] }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "2ade3ad", features = ["component-model"] }
wasmtime-wasi = { git = "https://github.com/bytecodealliance/wasmtime", rev = "2ade3ad" }
wasi-cap-std-sync = { git = "https://github.com/bytecodealliance/wasmtime", rev = "2ade3ad" }
wasmtime = "18.0.1"
wasmtime-wasi = { version = "18.0.1" }
anyhow = "1.0.72"
20 changes: 6 additions & 14 deletions component-model/examples/example-host/src/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anyhow::Context;
use std::path::PathBuf;
use wasmtime::component::*;
use wasmtime::{Config, Engine, Store};
use wasmtime_wasi::preview2::{command, Table, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime_wasi::preview2::{command, WasiCtx, WasiCtxBuilder, WasiView};

wasmtime::component::bindgen!({
path: "add.wit",
Expand Down Expand Up @@ -34,33 +34,25 @@ pub async fn add(path: PathBuf, x: i32, y: i32) -> wasmtime::Result<i32> {
}

struct ServerWasiView {
table: Table,
table: ResourceTable,
ctx: WasiCtx,
}

impl ServerWasiView {
fn new() -> Self {
let table = Table::new();
let table = ResourceTable::new();
let ctx = WasiCtxBuilder::new().inherit_stdio().build();

Self { table, ctx }
}
}

impl WasiView for ServerWasiView {
fn table(&self) -> &Table {
&self.table
}

fn table_mut(&mut self) -> &mut Table {
fn table(&mut self) -> &mut ResourceTable {
&mut self.table
}

fn ctx(&self) -> &WasiCtx {
&self.ctx
}

fn ctx_mut(&mut self) -> &mut WasiCtx {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.ctx
}
}
}
2 changes: 1 addition & 1 deletion component-model/examples/tutorial/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ wasm-tools compose command/target/wasm32-wasi/release/command.wasm -d composed.w
Now, run the component with wasmtime:

```sh
wasmtime run --wasm component-model command.wasm 1 2 add
wasmtime run command.wasm 1 2 add
1 + 2 = 3
```
2 changes: 1 addition & 1 deletion component-model/src/creating-and-consuming/composing.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The [`wasm-tools` suite](https://github.com/bytecodealliance/wasm-tools) include

To compose a component with the components it directly depends on, run:

```
```sh
wasm-tools compose path/to/component.wasm -d path/to/dep1.wasm -d path/to/dep2.wasm -o composed.wasm
```

Expand Down
6 changes: 2 additions & 4 deletions component-model/src/creating-and-consuming/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ You can "run" a component by calling one of its exports. In some cases, this req

You must use a recent version of `wasmtime` ([`v14.0.0` or greater](https://github.com/bytecodealliance/wasmtime/releases)), as earlier releases of the `wasmtime` command line do not include component model support.

> If you build the `wasmtime` CLI from source, you must pass `--features component-model` to the build command.

To run your component, run:

```sh
wasmtime run --wasm component-model <path-to-wasm-file>
wasmtime run <path-to-wasm-file>
```

## Running components with custom exports
Expand All @@ -28,6 +26,6 @@ If you're writing a library-style component - that is, one that exports a custom

5. Compose your command component with your library component by running `wasm-tools compose <path/to/command.wasm> -d <path/to/library.wasm> -o main.wasm`.

6. Run the composed component using `wasmtime run --wasm component-model main.wasm`
6. Run the composed component using `wasmtime run main.wasm`

See [Composing Components](./composing.md) for more details.
1 change: 0 additions & 1 deletion component-model/src/implementations/jco.md

This file was deleted.

1 change: 0 additions & 1 deletion component-model/src/implementations/wasmtime.md

This file was deleted.

21 changes: 15 additions & 6 deletions component-model/src/language-support/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@
[`jco`](https://github.com/bytecodealliance/jco) is a fully native JS tool for working with the
emerging WebAssembly Components specification in JavaScript.

### Building a Component with `jco`
## Building a Component with `jco`

A component can be created from a JS module using `jco componentize`. First, install `jco` and
`componentize-js`:
A component can be created from a JS module using `jco componentize`. First, install `jco` and `componentize-js`:

```sh
$ npm install @bytecodealliance/jco
$ npm install @bytecodealliance/componentize-js
```

Create a JavaScript module that implements the `add` function in [`add.wit`](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit):
Next, create or download the WIT world you would like to target. For this example we will use an [`example`
world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) with an `add` function:

```wit
package example:component;
world example {
export add: func(x: s32, y: s32) -> s32;
}
```

Create a JavaScript module that implements the `add` function in the `example` world.

```js
export function add(x, y) {
Expand All @@ -24,7 +33,7 @@ export function add(x, y) {
Now, use `jco` to create a component from the JS module:

```sh
$ jco componentize add.js --wit add.wit -n example -o add.wasm
$ jco componentize add.js --wit add.wit --world-name example --out add.wasm
OK Successfully written add.wasm with imports ().
```

Expand All @@ -36,7 +45,7 @@ $ cargo run --release -- 1 2 ../path/to/add.wasm
1 + 2 = 3
```

### Running a Component from JavaScript Applications
## Running a Component from JavaScript Applications

As the JavaScript runtime cannot yet execute Wasm components, a component must be transpiled into
JavaScript and a core module and then executed. `jco` automates this transpilation:
Expand Down
114 changes: 86 additions & 28 deletions component-model/src/language-support/python.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,51 @@
# Python Tooling

### Building a Component with `componentize-py`
## Building a Component with `componentize-py`

[`componentize-py`](https://github.com/dicej/componentize-py) is a tool that converts a Python
application to a WebAssembly component.

Create a Python program that implements the `add` function in the [`example`
world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit). Note that it imports the bindings that will be created by
`componentize-py`:
First, install [Python 3.10 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install `componentize-py`:

```sh
$ cat<<EOT >> guest.py
pip install componentize-py
```

Next, create or download the WIT world you would like to target. For this example we will use an [`example`
world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) with an `add` function:

```wit
package example:component;

world example {
export add: func(x: s32, y: s32) -> s32;
}
```

If you want to generate bindings produced for the WIT world (for an IDE or typechecker), you can generate them using the `bindings` subcommand. Specify the path to the WIT interface with the world you are targeting:

```sh
$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example bindings .
```

> Note: you do not need to generate the bindings in order to `componentize` in the next step. `componentize` will generate bindings on-the-fly and bundle them into the produced component.

You can see that bindings were created in an `example` package which contains an `Example` protocol with an `add` method that we can implement:

```sh
$ cat<<EOT >> app.py
import example

class Example(example.Example):
def add(x: int, y: int) -> int:
def add(self, x: int, y: int) -> int:
return x + y
EOT
```

[Install `componentize-py`](https://github.com/dicej/componentize-py#installing-from-pypi) and
generate a component from `guest.py`.
We now can compile our application to a Wasm component using the `componentize` subcommand:

```sh
$ pip install componentize-py
$ componentize-py -d /path/to/examples/example-host/add.wit -w example componentize guest -o add.wasm
$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example componentize app -o add.wasm
Component built successfully
```

Expand All @@ -36,41 +57,78 @@ $ cargo run --release -- 1 2 ../path/to/add.wasm
1 + 2 = 3
```

### Running components from Python Applications
See [`componentize-py`'s examples](https://github.com/bytecodealliance/componentize-py/tree/main/examples) to try out build HTTP, CLI, and TCP components from Python applications.


### Building a Component that Exports an Interface

The [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) exports a function. However, to use your component from another component, it must export an interface. That being said, you rarely find WIT that does not contain an interface. (Most WITs you'll see in the wild do use interfaces; we've been simplifying by exporting a function.) Let's expand our example world to export an interface rather than directly export the function.

```wit
// add-interface.wit
package example:component;

interface add {
add: func(a: u32, b: u32) -> u32;
}

world example {
export add;
}
```

If you peek at the bindings, you'll notice that we now implement a class for the `add` interface rather than for the `example` world. This is a consistent pattern. As you export more interfaces from your world, you implement more classes. Our add example gets the slight update of:

Wasm components can also be invoked from Python applications. This walks through the tooling needed
to call the `app.wasm` component from the previous section from a Python application. First, install
`wasmtime-py`, being sure to use a version [this PR has
merged](https://github.com/bytecodealliance/wasmtime-py/pull/171) or working off that branch.
```py
# app.py
import example

class Add(example.Example):
Copy link
Collaborator

Choose a reason for hiding this comment

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

The explanatory paragraph said "we now implement a class for the add interface rarher than the example world, but this still inherits from example.Example. It would be good to clarify what makes this implement that specific interface (of all the ones in the example world). Is it the class name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes class name changes

def add(self, a: int, b: int) -> int:
return a + b
```

> Note: be sure to use at least Python 3.11
Once again, compile an application to a Wasm component using the `componentize` subcommand:

```sh
$ git clone https://github.com/dicej/wasmtime-py
$ (cd wasmtime-py && python ci/download-wasmtime.py && python ci/build-rust.py && pip install .)
$ componentize-py --wit-path add-interface.wit --world example componentize app -o add.wasm
Component built successfully
```

Now, generate the bindings to be able to call the component from a Python host application.
## Running components from Python Applications

Wasm components can also be invoked from Python applications. This walks through using tooling
to call the [`app.wasm` component from the examples](../../examples/example-host/add.wasm).

> Note: `wasmtime-py` does not currently support running components build with `componentize-py`. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), which components built with `componentize-py` always use, since `componentize-py` unconditionally imports most of the `wasi:cli` world.

First, install [Python 3.11 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install [`wasmtime-py`](https://github.com/bytecodealliance/wasmtime-py):

```sh
$ python3 -m wasmtime.bindgen add.wasm --out-dir add
$ pip install wasmtime
```

The generated package `add` has all of the requisite exports/imports for the component and is
annotated with types to assist with type-checking and self-documentation as much as possible.
First, generate the bindings to be able to call the component from a Python host application.

```sh
# Get an add component that does not import the WASI CLI world
$ wget https://github.com/bytecodealliance/component-docs/raw/main/component-model/examples/example-host/add.wasm
$ python3 -m wasmtime.bindgen add.wasm --out-dir add
```

Now, create a Python program to run the component. Note that imports for WASI preview 2 are
explicitly set to null. This is because when creating a component from a Python module,
`componentize-py` pulls in extra WASI Preview 2 imports, even if they are not used by the component.
Currently, language toolchains are likely to pull in more than a component declares in WAT.
The generated package `add` has all of the requisite exports/imports for the
component and is annotated with types to assist with type-checking and
self-documentation as much as possible. Inside the package is a `Root` class
with an `add` function that calls the component's exported `add` function. We
can now write a Python program that calls `add`:

```py
from add import Root, RootImports
from add import Root
from wasmtime import Store

def main():
store = Store()
component = Root(store, RootImports(poll=None, monotonic_clock=None, wall_clock=None, streams=None, filesystem=None, random=None, environment=None, preopens=None, exit=None, stdin=None, stdout=None, stderr=None))
component = Root(store)
print("1 + 2 = ", component.add(store, 1, 2))

if __name__ == '__main__':
Expand Down
8 changes: 4 additions & 4 deletions component-model/src/language-support/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime)
Rust bindings, bring in WASI worlds, and execute the component.

```sh
$ cd examples/add-host
$ cd examples/example-host
$ cargo run --release -- 1 2 ../add/target/wasm32-wasi/release/add.wasm
1 + 2 = 3
```
Expand Down Expand Up @@ -215,9 +215,9 @@ You can write Rust in this project, just as you normally would, including import

To run your command component:

```
```sh
cargo component build
wasmtime run --wasm component-model ./target/wasm32-wasi/debug/<name>.wasm
wasmtime run ./target/wasm32-wasi/debug/<name>.wasm
```

> **WARNING:** If your program prints to standard out or error, you may not see the printed output! Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`.
Expand Down Expand Up @@ -273,6 +273,6 @@ fn main() {
6. Run the composed component:

```sh
$ wasmtime run --wasm component-model ./my-composed-command.wasm
$ wasmtime run ./my-composed-command.wasm
1 + 1 = 579 # might need to go back and do some work on the calculator implementation
```
6 changes: 4 additions & 2 deletions component-model/src/runtimes/wasmtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
[Wasmtime](https://github.com/bytecodealliance/wasmtime/) is the reference implementation of the Component Model. It supports running components that implement the [`wasi:cli/command` world](https://github.com/WebAssembly/wasi-cli/blob/main/wit/command.wit) and serving components the implement the [`wasi:http/proxy` world](https://github.com/WebAssembly/wasi-http/blob/main/wit/proxy.wit).

## Running command components with Wasmtime
To run a command component with wasmtime, execute:
To run a command component with Wasmtime, execute:

```sh
wasmtime run --wasm component-model <path-to-wasm-file>
wasmtime run <path-to-wasm-file>
```

> If you are using an older version of `wasmtime`, you may need to add the `--wasm component-model` flag to specify that you are running a component rather than a core module.

By default, Wasmtime denies the component access to all system resources. For example, the component cannot access the file system or environment variables. See the [Wasmtime guide](https://docs.wasmtime.dev/) for information on granting access, and for other Wasmtime features.

## Running HTTP components with Wasmtime
Expand Down
2 changes: 1 addition & 1 deletion component-model/src/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Now it all adds up! Run the final component with the `wasmtime` CLI, ensuring yo
the `wasmtime` command line do not include component model support.

```sh
wasmtime run --wasm component-model final.wasm 1 2 add
wasmtime run final.wasm 1 2 add
1 + 2 = 3
```

Expand Down