|
| 1 | +## C/C++ Tooling |
| 2 | + |
| 3 | +### Building a Component with `wit-bindgen` and `wasm-tools` |
| 4 | + |
| 5 | +[`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. |
| 6 | + |
| 7 | +`wit-bindgen` can be used to generate C applications that can be compiled directly to Wasm modules using `clang` with a `wasm32-wasi` target. |
| 8 | + |
| 9 | +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). |
| 10 | + |
| 11 | +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). |
| 12 | + |
| 13 | +Start by generating a C skeleton from `wit-bindgen` using the [sample `add.wit` file](../../examples/example-host/add.wit): |
| 14 | +```sh |
| 15 | +>wit-bindgen c add.wit |
| 16 | +Generating "example.c" |
| 17 | +Generating "example.h" |
| 18 | +Generating "example_component_type.o" |
| 19 | +``` |
| 20 | + |
| 21 | +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. |
| 22 | + |
| 23 | +Next, create an `add.c` that implements your function defined in `example.h`: |
| 24 | +```c |
| 25 | +#include "example.h" |
| 26 | + |
| 27 | +int32_t example_add(int32_t x, int32_t y) |
| 28 | +{ |
| 29 | + return x + y; |
| 30 | +} |
| 31 | +``` |
| 32 | +
|
| 33 | +Now, you can compile the function into a Wasm module via clang: |
| 34 | +```sh |
| 35 | +clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor |
| 36 | +``` |
| 37 | + |
| 38 | +> Note: Use the `clang` included in the WASI SDK installation, for example at `<WASI_SDK_PATH>/bin/clang`. |
| 39 | +
|
| 40 | +Next, you need to transform the module into a component. For this example, you can use `wasm-tools component new`: |
| 41 | +```sh |
| 42 | +wasm-tools component new ./add-core.wasm -o add-component.wasm |
| 43 | +``` |
| 44 | + |
| 45 | +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. |
| 46 | + |
| 47 | +For example, modifying the above to reference `printf()` would compile: |
| 48 | +```c |
| 49 | +#include "example.h" |
| 50 | +#include <stdio.h> |
| 51 | + |
| 52 | +int32_t example_add(int32_t x, int32_t y) |
| 53 | +{ |
| 54 | + int32_t result = x + y; |
| 55 | + printf("%d", result); |
| 56 | + return result; |
| 57 | +} |
| 58 | +``` |
| 59 | +
|
| 60 | +However, the module would fail to transform to a component: |
| 61 | +```sh |
| 62 | +>wasm-tools component new ./add-core.wasm -o add-component.wasm |
| 63 | +error: failed to encode a component from module |
| 64 | +
|
| 65 | +Caused by: |
| 66 | + 0: failed to decode world from module |
| 67 | + 1: module was not valid |
| 68 | + 2: module requires an import interface named `wasi_snapshot_preview1` |
| 69 | +``` |
| 70 | + |
| 71 | +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). |
| 72 | + |
| 73 | +Now, you can adapt preview1 to preview2 to build a component: |
| 74 | +```sh |
| 75 | +wasm-tools component new add-core.wasm --adapt wasi_snapshot_preview1.wasm -o add-component.wasm |
| 76 | +``` |
| 77 | + |
| 78 | +Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary): |
| 79 | +```sh |
| 80 | +>wasm-tools component wit add-component.wasm |
| 81 | +package root:component; |
| 82 | + |
| 83 | +world root { |
| 84 | + |
| 85 | + |
| 86 | + import wasi:cli/ [email protected]; |
| 87 | + import wasi:cli/ [email protected]; |
| 88 | + import wasi:cli/ [email protected]; |
| 89 | + import wasi:cli/ [email protected]; |
| 90 | + import wasi:cli/ [email protected]; |
| 91 | + import wasi:cli/ [email protected]; |
| 92 | + import wasi:cli/ [email protected]; |
| 93 | + import wasi:cli/ [email protected]; |
| 94 | + import wasi:clocks/ [email protected]; |
| 95 | + import wasi:filesystem/ [email protected]; |
| 96 | + import wasi:filesystem/ [email protected]; |
| 97 | + |
| 98 | + export add: func(x: s32, y: s32) -> s32; |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +### Running a Component from C/C++ Applications |
| 103 | + |
| 104 | +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++. |
| 105 | + |
| 106 | +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. |
| 107 | + |
| 108 | +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 commit comments