From c969f5683a2d68aa756a41f8677452855797ff3c Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 10:49:34 +0000 Subject: [PATCH 1/3] Proof of concept C host TODO deduplicate with add.wasm see #333 ```sh cd component-model/examples/example-c-host gcc -o host host.c -lwasmtime && ./host 6 7 adder.wasm ``` Co-authored-by: Shelley --- .../examples/example-c-host/.gitignore | 1 + .../examples/example-c-host/adder.wasm | 1 + .../examples/example-c-host/host.c | 127 ++++++++++++++++++ .../examples/example-c-host/justfile | 2 + 4 files changed, 131 insertions(+) create mode 100644 component-model/examples/example-c-host/.gitignore create mode 120000 component-model/examples/example-c-host/adder.wasm create mode 100644 component-model/examples/example-c-host/host.c create mode 100644 component-model/examples/example-c-host/justfile diff --git a/component-model/examples/example-c-host/.gitignore b/component-model/examples/example-c-host/.gitignore new file mode 100644 index 0000000..c70dc2d --- /dev/null +++ b/component-model/examples/example-c-host/.gitignore @@ -0,0 +1 @@ +host diff --git a/component-model/examples/example-c-host/adder.wasm b/component-model/examples/example-c-host/adder.wasm new file mode 120000 index 0000000..be994d3 --- /dev/null +++ b/component-model/examples/example-c-host/adder.wasm @@ -0,0 +1 @@ +../example-host/add.wasm \ No newline at end of file diff --git a/component-model/examples/example-c-host/host.c b/component-model/examples/example-c-host/host.c new file mode 100644 index 0000000..fafa0af --- /dev/null +++ b/component-model/examples/example-c-host/host.c @@ -0,0 +1,127 @@ +/** + * C host for the adder WebAssembly component. + * + * Uses the Wasmtime C API's component model support to load and run + * a component that exports: docs:adder/add.add(u32, u32) -> u32 + */ + +#include +#include +#include +#include + +static void exit_if_error(const char *step, wasmtime_error_t *error) { + if (error == NULL) + return; + wasm_byte_vec_t error_message; + wasmtime_error_message(error, &error_message); + wasmtime_error_delete(error); + fprintf(stderr, "error: failed to %s\n%.*s\n", step, (int)error_message.size, + error_message.data); + wasm_byte_vec_delete(&error_message); + exit(1); +} + +int main(int argc, char *argv[]) { + if (argc != 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + uint32_t x = (uint32_t)atoi(argv[1]); + uint32_t y = (uint32_t)atoi(argv[2]); + const char *path = argv[3]; + + // 1. Create engine with component model enabled + wasm_config_t *config = wasm_config_new(); + wasmtime_config_wasm_component_model_set(config, true); + wasm_engine_t *engine = wasm_engine_new_with_config(config); + + // 2. Read the component file + FILE *f = fopen(path, "rb"); + if (!f) { + fprintf(stderr, "error: could not open %s\n", path); + return 1; + } + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + uint8_t *wasm_bytes = malloc(fsize); + fread(wasm_bytes, 1, fsize, f); + fclose(f); + + // 3. Compile the component + wasmtime_component_t *component = NULL; + exit_if_error("compile component", + wasmtime_component_new(engine, wasm_bytes, fsize, &component)); + free(wasm_bytes); + + // 4. Create linker and add WASI P2 + wasmtime_component_linker_t *linker = + wasmtime_component_linker_new(engine); + exit_if_error("add WASI to linker", + wasmtime_component_linker_add_wasip2(linker)); + + // 5. Create store with WASI config + wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); + wasmtime_context_t *context = wasmtime_store_context(store); + exit_if_error("set WASI config", + wasmtime_context_set_wasi(context, wasi_config_new())); + + // 6. Instantiate + wasmtime_component_instance_t instance; + exit_if_error("instantiate component", + wasmtime_component_linker_instantiate(linker, context, component, &instance)); + + // 7. Look up the exported "add" function. + // The export is nested: first find the "docs:adder/add@0.1.0" instance, + // then the "add" function within it. + wasmtime_component_export_index_t *iface_idx = + wasmtime_component_instance_get_export_index( + &instance, context, NULL, + "docs:adder/add@0.1.0", strlen("docs:adder/add@0.1.0")); + if (iface_idx == NULL) { + fprintf(stderr, "error: could not find export 'docs:adder/add@0.1.0'\n"); + return 1; + } + + wasmtime_component_export_index_t *func_idx = + wasmtime_component_instance_get_export_index( + &instance, context, iface_idx, + "add", strlen("add")); + wasmtime_component_export_index_delete(iface_idx); + if (func_idx == NULL) { + fprintf(stderr, "error: could not find function 'add'\n"); + return 1; + } + + wasmtime_component_func_t func; + bool found = wasmtime_component_instance_get_func( + &instance, context, func_idx, &func); + wasmtime_component_export_index_delete(func_idx); + if (!found) { + fprintf(stderr, "error: could not get function handle for 'add'\n"); + return 1; + } + + // 8. Call the function: add(x, y) -> u32 + wasmtime_component_val_t args[2] = { + {.kind = WASMTIME_COMPONENT_U32, .of.u32 = x}, + {.kind = WASMTIME_COMPONENT_U32, .of.u32 = y}, + }; + wasmtime_component_val_t results[1] = {0}; + + exit_if_error("call 'add'", + wasmtime_component_func_call(&func, context, args, 2, results, 1)); + + printf("%u + %u = %u\n", x, y, results[0].of.u32); + + // 9. Cleanup + wasmtime_component_val_delete(&results[0]); + wasmtime_store_delete(store); + wasmtime_component_linker_delete(linker); + wasmtime_component_delete(component); + wasm_engine_delete(engine); + + return 0; +} diff --git a/component-model/examples/example-c-host/justfile b/component-model/examples/example-c-host/justfile new file mode 100644 index 0000000..91a5e52 --- /dev/null +++ b/component-model/examples/example-c-host/justfile @@ -0,0 +1,2 @@ +@_default: + echo "TODO"; exit 1 From 2c00f7a280150ae7e94db3564f6f0321c07ba89a Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 11:10:06 +0000 Subject: [PATCH 2/3] (reproducibility) add Dockerfile I had some difficulty setting up the C toolchains, here's everything pinned down as a sanity check ```sh docker build -t example-c-host . docker run example-c-host ``` Co-authored-by: Shelley --- .../examples/example-c-host/Dockerfile | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 component-model/examples/example-c-host/Dockerfile diff --git a/component-model/examples/example-c-host/Dockerfile b/component-model/examples/example-c-host/Dockerfile new file mode 100644 index 0000000..cbd7281 --- /dev/null +++ b/component-model/examples/example-c-host/Dockerfile @@ -0,0 +1,58 @@ +FROM ubuntu:24.04 AS build + +ARG WASMTIME_VERSION=42.0.1 +ARG WASI_SDK_VERSION=27 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc libc6-dev curl xz-utils ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Install wasmtime C API +RUN curl -sL https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-x86_64-linux-c-api.tar.xz \ + | tar xJ --strip-components=1 -C /usr/local \ + && ldconfig + +# Install wasi-sdk (for building the guest component) +RUN curl -sLO https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.deb \ + && dpkg -i wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.deb \ + && rm wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.deb + +# Install wit-bindgen and wasm-tools (pin versions) +ARG WIT_BINDGEN_VERSION=0.53.1 +ARG WASM_TOOLS_VERSION=1.245.1 +RUN curl -sL https://github.com/bytecodealliance/wit-bindgen/releases/download/v${WIT_BINDGEN_VERSION}/wit-bindgen-${WIT_BINDGEN_VERSION}-x86_64-linux.tar.gz \ + | tar xz --strip-components=1 -C /usr/local/bin --wildcards '*/wit-bindgen' \ + && curl -sL https://github.com/bytecodealliance/wasm-tools/releases/download/v${WASM_TOOLS_VERSION}/wasm-tools-${WASM_TOOLS_VERSION}-x86_64-linux.tar.gz \ + | tar xz --strip-components=1 -C /usr/local/bin --wildcards '*/wasm-tools' + +WORKDIR /src + +# Fetch guest source from upstream component-docs +ARG COMPONENT_DOCS_REV=7c1b5a9 +RUN curl -sL -o world.wit \ + https://raw.githubusercontent.com/bytecodealliance/component-docs/${COMPONENT_DOCS_REV}/component-model/examples/tutorial/wit/adder/world.wit \ + && mkdir -p wit/adder && mv world.wit wit/adder/world.wit +RUN curl -sL -o component.c \ + https://raw.githubusercontent.com/bytecodealliance/component-docs/${COMPONENT_DOCS_REV}/component-model/examples/tutorial/c/adder/component.c + +# Build the guest component +RUN wit-bindgen c wit/adder/world.wit +RUN /opt/wasi-sdk/bin/wasm32-wasip2-clang \ + -o adder.wasm \ + -mexec-model=reactor \ + component.c adder.c adder_component_type.o + +# Build the host +COPY host.c . +RUN gcc -o /usr/local/bin/adder-host host.c -lwasmtime + +# Runtime image +FROM ubuntu:24.04 + +COPY --from=build /usr/local/lib/libwasmtime.so /usr/local/lib/ +RUN ldconfig +COPY --from=build /usr/local/bin/adder-host /usr/local/bin/adder-host +COPY --from=build /src/adder.wasm /opt/adder.wasm + +ENTRYPOINT ["adder-host"] +CMD ["1", "2", "/opt/adder.wasm"] From aa3e99dc5db33cb2cc1d6ee9ccc494119215b9e5 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 03:38:04 -0800 Subject: [PATCH 3/3] Revise instructions for running components in C/C++ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentation is not my forté but the current "running things from C is impossible due to #issue (closed)" is _definitely_ out of date. Needs revision into a more helpful pointer to the code introduced in this PR, whatever its project name and contents shall end up being. Possibly broken out into a separate .md fragments like the rust one, possibly that was only necessary to share instructions with rust.md and the existence of the c/c++ host can stay local to this file. --- .../building-a-simple-component/c.md | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/component-model/src/language-support/building-a-simple-component/c.md b/component-model/src/language-support/building-a-simple-component/c.md index 3dbc38d..852f390 100644 --- a/component-model/src/language-support/building-a-simple-component/c.md +++ b/component-model/src/language-support/building-a-simple-component/c.md @@ -323,20 +323,19 @@ if you followed the manual instructions above): {{#include ../example-host-part2.md}} -## 7. Run the component from C/C++ Applications +## 7. Run the component from the example C host -It is not yet possible to run a WebAssembly Component using the `wasmtime` C API. -See [`wasmtime` issue #6987](https://github.com/bytecodealliance/wasmtime/issues/6987) for more details. -The C API is preferred over directly using the example host Rust crate in C++. +After setting up the wasmtime toolchain +*TODO: somehow,* +the `example-c-host` project can be built, and run our `adder` much like the rust one. -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. +*TODO: the C++ API is nicer and may be more appropriate for example code* -See the [Rust Tooling guide](./rust.md#running-a-component-from-rust-applications) -for instructions on how to run this component from the Rust `example-host` -(replacing the path to `add.wasm` with your `adder.wasm` or `adder.component.wasm` above). +C/C++ language guest components can also 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. + +*TODO: work in `wasmtime --invoke 'add(2,2)' adder.wasm`* [!NOTE]: # [!WARNING]: #