C/C++ Tooling
Building a Component with wit-bindgen and wasm-tools
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, wasm-tools, and the WASI SDK.
The WASI SDK will install a local version of clang configured with a wasi-sysroot. Follow these instructions 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:
>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:
#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:
clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor
Use the
clangincluded 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:
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:
#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:
>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 - 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 (make sure you git submodule update --init first).
Now, you can adapt preview1 to preview2 to build a component:
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):
>wasm-tools component wit add-component.wasm
package root:component;
world root {
import wasi:io/error@0.2.0;
import wasi:io/streams@0.2.0;
import wasi:cli/stdin@0.2.0;
import wasi:cli/stdout@0.2.0;
import wasi:cli/stderr@0.2.0;
import wasi:cli/terminal-input@0.2.0;
import wasi:cli/terminal-output@0.2.0;
import wasi:cli/terminal-stdin@0.2.0;
import wasi:cli/terminal-stdout@0.2.0;
import wasi:cli/terminal-stderr@0.2.0;
import wasi:clocks/wall-clock@0.2.0;
import wasi:filesystem/types@0.2.0;
import wasi:filesystem/preopens@0.2.0;
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. 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 for instructions on how to run this component from the Rust example-host.