Skip to content

Commit

Permalink
Merge pull request #147 from rajsite/feature-c-tooling-continued
Browse files Browse the repository at this point in the history
Add initial documentation for C tooling [continued]
  • Loading branch information
kate-goldenring authored Aug 27, 2024
2 parents 36f0c66 + 9d36dc3 commit c844c93
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
1 change: 1 addition & 0 deletions component-model/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

- [Language Support for Components](./language-support.md)
- [Rust](./language-support/rust.md)
- [C/C++](./language-support/c.md)
- [Javascript](./language-support/javascript.md)
- [Python](./language-support/python.md)
- [Go](./language-support/go.md)
Expand Down
2 changes: 2 additions & 0 deletions component-model/src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The WebAssembly Component Model is a broad-reaching architecture for building in
| [Components] | [Python] | [Running] |
| [Interfaces] | [Rust] | [Distributing] |
| [Worlds] | [Go] | |
| | [C/C++][C] | |

[Why Components?]: ./design/why-component-model.md
[Components]: ./design/components.md
Expand All @@ -18,6 +19,7 @@ The WebAssembly Component Model is a broad-reaching architecture for building in
[Python]: ./language-support/python.md
[Rust]: ./language-support/rust.md
[Go]: ./language-support/go.md
[C]: ./language-support/c.md

[Composing]: ./creating-and-consuming/composing.md
[Running]: ./creating-and-consuming/running.md
Expand Down
3 changes: 3 additions & 0 deletions component-model/src/language-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ components within and among toolchains.
- [Python Tooling](./language-support/python.md)
- [Building a Component with `componentize-py`](./language-support/python.md#building-a-component-with-componentize-py)
- [Running components from Python Applications](./language-support/python.md#running-components-from-python-applications)
- [C/C++ Tooling](./language-support/c.md)
- [Building a Component with `wit-bindgen` and `wasm-tools`](./language-support/c.md#building-a-component-with-wit-bindgen-and-wasm-tools)
- [Running a Component from C/C++ Applications](./language-support/c.md#running-a-component-from-cc-applications)

## Language Agnostic Tooling

Expand Down
108 changes: 108 additions & 0 deletions component-model/src/language-support/c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
## C/C++ Tooling

### Building a Component with `wit-bindgen` and `wasm-tools`

[`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a given `.wit` file. Although it is less integrated into language toolchains than other tools such as `cargo-component`, it can currently generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more language generators to be added in the future.

`wit-bindgen` can be used to generate C applications that can be compiled directly to Wasm modules using `clang` with a `wasm32-wasi` target.

First, install the CLI for [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen#cli-installation), [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools), and the [`WASI SDK`](https://github.com/webassembly/wasi-sdk).

The WASI SDK will install a local version of `clang` configured with a wasi-sysroot. Follow [these instructions](https://github.com/WebAssembly/wasi-sdk#use) to configure it for use. Note that you can also use your installed system or emscripten `clang` by building with `--target=wasm32-wasi` but you will need some artifacts from WASI SDK to enable and link that build target (more information is available in WASI SDK's docs).

Start by generating a C skeleton from `wit-bindgen` using the [sample `add.wit` file](../../examples/example-host/add.wit):
```sh
>wit-bindgen c add.wit
Generating "example.c"
Generating "example.h"
Generating "example_component_type.o"
```

This has generated several files - an `example.h` (based on the name of your `world`) with the prototype of the `add` function - `int32_t example_add(int32_t x, int32_t y);`, as well as some generated code in `example.c` that interfaces with the component model ABI to call your function. Additionally, `example_component_type.o` contains object code referenced in `example.c` from an `extern` that must be linked via clang.

Next, create an `add.c` that implements your function defined in `example.h`:
```c
#include "example.h"

int32_t example_add(int32_t x, int32_t y)
{
return x + y;
}
```
Now, you can compile the function into a Wasm module via clang:
```sh
clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor
```

> Note: Use the `clang` included in the WASI SDK installation, for example at `<WASI_SDK_PATH>/bin/clang`.
Next, you need to transform the module into a component. For this example, you can use `wasm-tools component new`:
```sh
wasm-tools component new ./add-core.wasm -o add-component.wasm
```

Do note this will fail if your code references any WASI APIs that must be imported. This requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components.

For example, modifying the above to reference `printf()` would compile:
```c
#include "example.h"
#include <stdio.h>

int32_t example_add(int32_t x, int32_t y)
{
int32_t result = x + y;
printf("%d", result);
return result;
}
```
However, the module would fail to transform to a component:
```sh
>wasm-tools component new ./add-core.wasm -o add-component.wasm
error: failed to encode a component from module
Caused by:
0: failed to decode world from module
1: module was not valid
2: module requires an import interface named `wasi_snapshot_preview1`
```

Install the appropriate reactor adapter module [as documented here](https://github.com/bytecodealliance/wit-bindgen#creating-components-wasi) - you can either get the linked release of `wasi_snapshot_preview1.reactor.wasm` and rename it to `wasi_snapshot_preview1.wasm`, or build it directly from source in `wasmtime` following the [instructions here](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter) (make sure you `git submodule update --init` first).

Now, you can adapt preview1 to preview2 to build a component:
```sh
wasm-tools component new add-core.wasm --adapt wasi_snapshot_preview1.wasm -o add-component.wasm
```

Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary):
```sh
>wasm-tools component wit add-component.wasm
package root:component;

world root {
import wasi:io/[email protected];
import wasi:io/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:cli/[email protected];
import wasi:clocks/[email protected];
import wasi:filesystem/[email protected];
import wasi:filesystem/[email protected];

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

### Running a Component from C/C++ Applications

It is not yet possible to run a Component using the `wasmtime` `c-api` - [see this issue](https://github.com/bytecodealliance/wasmtime/issues/6987). The c-api is preferred to trying to directly use the Rust crate in C++.

However, C/C++ language guest components can be composed with components written in any other language and run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI or any other host.

See the [Rust Tooling guide](../language-support/rust.md#running-a-component-from-rust-applications) for instructions on how to run this component from the Rust `example-host`.

0 comments on commit c844c93

Please sign in to comment.