From e84d1b423f86a8d30d56fe123bdc4df99723a434 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 10:49:34 +0000 Subject: [PATCH 1/7] 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 83b78d8b231865532ff1a9566bc9078b1b462bf2 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 11:10:06 +0000 Subject: [PATCH 2/7] (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 917bab6d23fdf030b08195ec2fdb0b109baffaf2 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Thu, 5 Mar 2026 03:38:04 -0800 Subject: [PATCH 3/7] 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]: # From db5a3424b1369e273f17559abcc5a362dedf3f43 Mon Sep 17 00:00:00 2001 From: Kate Goldenring Date: Wed, 15 Apr 2026 13:00:16 -0700 Subject: [PATCH 4/7] Suggested updates to proof of concept C host documentation - Fills in documentation on how to use the C host - Updates Dockerfile to be cross architecture - Adds a new Dockerfile that is just the host Signed-off-by: Kate Goldenring --- .../{Dockerfile => Dockerfile.guest_and_host} | 39 +++++-- .../examples/example-c-host/Dockerfile.host | 35 ++++++ .../examples/example-c-host/justfile | 2 - .../building-a-simple-component/c.md | 110 +++++++++++++----- 4 files changed, 148 insertions(+), 38 deletions(-) rename component-model/examples/example-c-host/{Dockerfile => Dockerfile.guest_and_host} (51%) create mode 100644 component-model/examples/example-c-host/Dockerfile.host delete mode 100644 component-model/examples/example-c-host/justfile diff --git a/component-model/examples/example-c-host/Dockerfile b/component-model/examples/example-c-host/Dockerfile.guest_and_host similarity index 51% rename from component-model/examples/example-c-host/Dockerfile rename to component-model/examples/example-c-host/Dockerfile.guest_and_host index cbd7281..d4ef0b6 100644 --- a/component-model/examples/example-c-host/Dockerfile +++ b/component-model/examples/example-c-host/Dockerfile.guest_and_host @@ -2,27 +2,46 @@ FROM ubuntu:24.04 AS build ARG WASMTIME_VERSION=42.0.1 ARG WASI_SDK_VERSION=27 +ARG TARGETARCH 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 +RUN set -eux; \ + case "${TARGETARCH}" in \ + amd64) WASMTIME_ARCH=x86_64 ;; \ + arm64) WASMTIME_ARCH=aarch64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-${WASMTIME_ARCH}-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 +RUN set -eux; \ + case "${TARGETARCH}" in \ + amd64) WASI_SDK_ARCH=amd64 ;; \ + arm64) WASI_SDK_ARCH=arm64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + curl -sLO "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-linux.deb"; \ + dpkg -i "wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-linux.deb"; \ + rm "wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-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 \ +RUN set -eux; \ + case "${TARGETARCH}" in \ + amd64) TOOL_ARCH=x86_64 ;; \ + arm64) TOOL_ARCH=aarch64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + curl -sL "https://github.com/bytecodealliance/wit-bindgen/releases/download/v${WIT_BINDGEN_VERSION}/wit-bindgen-${WIT_BINDGEN_VERSION}-${TOOL_ARCH}-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}-${TOOL_ARCH}-linux.tar.gz" \ | tar xz --strip-components=1 -C /usr/local/bin --wildcards '*/wasm-tools' WORKDIR /src @@ -55,4 +74,4 @@ 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"] +CMD ["1", "2", "/opt/adder.wasm"] \ No newline at end of file diff --git a/component-model/examples/example-c-host/Dockerfile.host b/component-model/examples/example-c-host/Dockerfile.host new file mode 100644 index 0000000..032c9fd --- /dev/null +++ b/component-model/examples/example-c-host/Dockerfile.host @@ -0,0 +1,35 @@ +FROM ubuntu:24.04 AS build + +ARG WASMTIME_VERSION=42.0.1 +ARG TARGETARCH + +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 set -eux; \ + case "${TARGETARCH}" in \ + amd64) WASMTIME_ARCH=x86_64 ;; \ + arm64) WASMTIME_ARCH=aarch64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-${WASMTIME_ARCH}-linux-c-api.tar.xz" \ + | tar xJ --strip-components=1 -C /usr/local; \ + ldconfig + +WORKDIR /src + +# 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 + +ENTRYPOINT ["adder-host"] +CMD ["1", "2", "/component/add.wasm"] \ No newline at end of file diff --git a/component-model/examples/example-c-host/justfile b/component-model/examples/example-c-host/justfile deleted file mode 100644 index 91a5e52..0000000 --- a/component-model/examples/example-c-host/justfile +++ /dev/null @@ -1,2 +0,0 @@ -@_default: - echo "TODO"; exit 1 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 852f390..cf3a2ee 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 @@ -29,7 +29,6 @@ with the ability to add more language generators in the future. [wasi]: https://wasi.dev/ [rust]: https://www.rust-lang.org/learn/get-started [sample-wit]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit -[cargo-config]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/Cargo.toml ## 1. Download dependencies @@ -303,39 +302,98 @@ world root { ... ``` -### 6. Run the component from the example host +## 6. Run the component from the example C host -The following section requires you to have [a Rust toolchain][rust] installed. +This repository includes a C application that can execute components that implement the add interface. This application embeds Wasmtime using the Wasmtime C API: +`component-model/examples/example-c-host/host.c`. -> [!WARNING] -> You must be careful to use a version of the adapter (`wasi_snapshot_preview1.wasm`) -> that is compatible with the version of `wasmtime` that will be used, -> to ensure that WASI interface versions (and relevant implementation) match. -> (The `wasmtime` version is specified in [the Cargo configuration file][cargo-config] -> for the example host.) +The application expects three arguments: the two numbers to add and the Wasm component that executed the addition. For example: -{{#include ../example-host-part1.md}} +```sh +./adder-host +``` + +You can either use a Dockerfile to execute your add component with the C application or directly run the application. + +### Option A: Compile and run the host directly + +If the Wasmtime C API headers and library are installed on your system, +you can compile and run the host directly: -A successful run should show the following output -(of course, the paths to your example host and adder component will vary, -and you should substitute `adder.wasm` with `adder.component.wasm` -if you followed the manual instructions above): +On Linux, the following commands install the C API artifacts in `/usr/local` +using the same approach as `Dockerfile.host`: -{{#include ../example-host-part2.md}} +```console +sudo apt-get update +sudo apt-get install -y --no-install-recommends \ + gcc libc6-dev curl xz-utils ca-certificates -## 7. Run the component from the example C host +WASMTIME_VERSION=42.0.1 +case "$(uname -m)" in + x86_64) WASMTIME_ARCH=x86_64 ;; + aarch64|arm64) WASMTIME_ARCH=aarch64 ;; + *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac -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. +curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-${WASMTIME_ARCH}-linux-c-api.tar.xz" \ + | sudo tar xJ --strip-components=1 -C /usr/local -*TODO: the C++ API is nicer and may be more appropriate for example code* +sudo ldconfig +``` -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. +```console +cd component-model/examples/example-c-host +gcc -o adder-host host.c -lwasmtime +./adder-host 1 2 /absolute/path/to/adder.wasm +``` + +If `libwasmtime.so` is not in a default library path on Linux, +set `LD_LIBRARY_PATH` before running: + +```console +LD_LIBRARY_PATH=/path/to/wasmtime/lib ./adder-host 1 2 /absolute/path/to/adder.wasm +``` + +Expected output: + +```sh +1 + 2 = 3 +``` + +### Option B: Run with Docker (`Dockerfile.host`) + +Instead of installing the Wasmtime C API, you can use the provided Dockerfile which builds the C application. + +From `component-model/examples/example-c-host`: + +```console +cp ../../examples/example-host/add.wasm ./add.wasm + +docker build \ + -f Dockerfile.host \ + -t example-c-host:latest \ + . +``` + +Then run the container, passing in the component as a volume. + +```console +docker run --rm \ + -v /absolute/path/to/component-docs/component-model/examples/example-c-host/adder.wasm:/component/add.wasm:ro \ + example-c-host:latest 1 2 /component/add.wasm +``` + +Expected output: + +```sh +1 + 2 = 3 +``` -*TODO: work in `wasmtime --invoke 'add(2,2)' adder.wasm`* +`Dockerfile.guest_and_host` is also provided in the same directory if you want +an all-in-one image that builds both the guest component and the C host. -[!NOTE]: # -[!WARNING]: # +Its guest build path follows the same sequence described in steps 2-4: +`wit-bindgen c ...`, then `/opt/wasi-sdk/bin/wasm32-wasip2-clang ...`. +The Dockerfile additionally automates fetching `world.wit` and `component.c` +from this repository and pins tool versions for reproducibility. +At the time of writing, `Dockerfile.guest_and_host` is Linux `x86_64`-specific. From b5c1f11326785cab2346eccd29fc72bf9857bc70 Mon Sep 17 00:00:00 2001 From: Kate Goldenring Date: Wed, 15 Apr 2026 14:25:10 -0700 Subject: [PATCH 5/7] Add wasmtime invoke to the C docs Signed-off-by: Kate Goldenring --- .../building-a-simple-component/c.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 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 cf3a2ee..b23a0d7 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 @@ -302,7 +302,22 @@ world root { ... ``` -## 6. Run the component from the example C host +## 6. Run the component with `wasmtime --invoke` + +If you want to quickly run the `add` export without writing a host application that embeds Wasmtime, +you can invoke it directly with the Wasmtime CLI. + +```console +wasmtime run --invoke 'add(2, 2)' adder.wasm +``` + +Depending on your Wasmtime version, the shorthand form may also work: + +```console +wasmtime --invoke 'add(2,2)' adder.wasm +``` + +## 7. Run the component from the example C host This repository includes a C application that can execute components that implement the add interface. This application embeds Wasmtime using the Wasmtime C API: `component-model/examples/example-c-host/host.c`. @@ -367,7 +382,7 @@ Instead of installing the Wasmtime C API, you can use the provided Dockerfile wh From `component-model/examples/example-c-host`: ```console -cp ../../examples/example-host/add.wasm ./add.wasm +cp ../../examples/example-host/add.wasm ./adder.wasm docker build \ -f Dockerfile.host \ From 0e8f8ed4e6dba54b04e0b4a51d600e1c1293c1e4 Mon Sep 17 00:00:00 2001 From: "exe.dev user" Date: Wed, 15 Apr 2026 21:55:34 +0000 Subject: [PATCH 6/7] Simplify Dockerfiles: drop guest_and_host, rename to Dockerfile, use volume mount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete Dockerfile.guest_and_host (broken wasi-sdk arch mapping; unnecessary now that upstream add.wasm is available via rebase) - Rename Dockerfile.host → Dockerfile - Fix missing trailing newline - Update c.md: remove guest_and_host references, fix Docker instructions to use volume mount with $(pwd)-relative path Co-authored-by: Shelley --- .../{Dockerfile.host => Dockerfile} | 2 +- .../example-c-host/Dockerfile.guest_and_host | 77 ------------------- .../building-a-simple-component/c.md | 32 ++++---- 3 files changed, 15 insertions(+), 96 deletions(-) rename component-model/examples/example-c-host/{Dockerfile.host => Dockerfile} (96%) delete mode 100644 component-model/examples/example-c-host/Dockerfile.guest_and_host diff --git a/component-model/examples/example-c-host/Dockerfile.host b/component-model/examples/example-c-host/Dockerfile similarity index 96% rename from component-model/examples/example-c-host/Dockerfile.host rename to component-model/examples/example-c-host/Dockerfile index 032c9fd..f603d5d 100644 --- a/component-model/examples/example-c-host/Dockerfile.host +++ b/component-model/examples/example-c-host/Dockerfile @@ -32,4 +32,4 @@ RUN ldconfig COPY --from=build /usr/local/bin/adder-host /usr/local/bin/adder-host ENTRYPOINT ["adder-host"] -CMD ["1", "2", "/component/add.wasm"] \ No newline at end of file +CMD ["1", "2", "/component/add.wasm"] diff --git a/component-model/examples/example-c-host/Dockerfile.guest_and_host b/component-model/examples/example-c-host/Dockerfile.guest_and_host deleted file mode 100644 index d4ef0b6..0000000 --- a/component-model/examples/example-c-host/Dockerfile.guest_and_host +++ /dev/null @@ -1,77 +0,0 @@ -FROM ubuntu:24.04 AS build - -ARG WASMTIME_VERSION=42.0.1 -ARG WASI_SDK_VERSION=27 -ARG TARGETARCH - -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 set -eux; \ - case "${TARGETARCH}" in \ - amd64) WASMTIME_ARCH=x86_64 ;; \ - arm64) WASMTIME_ARCH=aarch64 ;; \ - *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac; \ - curl -sL "https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-${WASMTIME_ARCH}-linux-c-api.tar.xz" \ - | tar xJ --strip-components=1 -C /usr/local; \ - ldconfig - -# Install wasi-sdk (for building the guest component) -RUN set -eux; \ - case "${TARGETARCH}" in \ - amd64) WASI_SDK_ARCH=amd64 ;; \ - arm64) WASI_SDK_ARCH=arm64 ;; \ - *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac; \ - curl -sLO "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-linux.deb"; \ - dpkg -i "wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-linux.deb"; \ - rm "wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_SDK_ARCH}-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 set -eux; \ - case "${TARGETARCH}" in \ - amd64) TOOL_ARCH=x86_64 ;; \ - arm64) TOOL_ARCH=aarch64 ;; \ - *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac; \ - curl -sL "https://github.com/bytecodealliance/wit-bindgen/releases/download/v${WIT_BINDGEN_VERSION}/wit-bindgen-${WIT_BINDGEN_VERSION}-${TOOL_ARCH}-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}-${TOOL_ARCH}-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"] \ No newline at end of file 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 b23a0d7..953deaa 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 @@ -336,7 +336,7 @@ If the Wasmtime C API headers and library are installed on your system, you can compile and run the host directly: On Linux, the following commands install the C API artifacts in `/usr/local` -using the same approach as `Dockerfile.host`: +using the same approach as the `Dockerfile`: ```console sudo apt-get update @@ -375,27 +375,23 @@ Expected output: 1 + 2 = 3 ``` -### Option B: Run with Docker (`Dockerfile.host`) +### Option B: Run with Docker Instead of installing the Wasmtime C API, you can use the provided Dockerfile which builds the C application. From `component-model/examples/example-c-host`: ```console -cp ../../examples/example-host/add.wasm ./adder.wasm - -docker build \ - -f Dockerfile.host \ - -t example-c-host:latest \ - . +cd component-model/examples/example-c-host +docker build -t example-c-host:latest . ``` -Then run the container, passing in the component as a volume. +Then run the container, passing in the component as a volume: ```console docker run --rm \ - -v /absolute/path/to/component-docs/component-model/examples/example-c-host/adder.wasm:/component/add.wasm:ro \ - example-c-host:latest 1 2 /component/add.wasm + -v "$(pwd)/../example-host/add.wasm":/component/add.wasm:ro \ + example-c-host:latest ``` Expected output: @@ -404,11 +400,11 @@ Expected output: 1 + 2 = 3 ``` -`Dockerfile.guest_and_host` is also provided in the same directory if you want -an all-in-one image that builds both the guest component and the C host. +The default command runs `adder-host 1 2 /component/add.wasm`, +so you can also override the arguments: -Its guest build path follows the same sequence described in steps 2-4: -`wit-bindgen c ...`, then `/opt/wasi-sdk/bin/wasm32-wasip2-clang ...`. -The Dockerfile additionally automates fetching `world.wit` and `component.c` -from this repository and pins tool versions for reproducibility. -At the time of writing, `Dockerfile.guest_and_host` is Linux `x86_64`-specific. +```console +docker run --rm \ + -v "$(pwd)/../example-host/add.wasm":/component/add.wasm:ro \ + example-c-host:latest 40 2 /component/add.wasm +``` From 9f9b3d880d84bc1bde603672332584045c35983b Mon Sep 17 00:00:00 2001 From: "exe.dev user" Date: Wed, 15 Apr 2026 22:14:42 +0000 Subject: [PATCH 7/7] Add CI workflow for C host example Tests both the native build (gcc + wasmtime C API) and the Docker workflow against the checked-in add.wasm. Co-authored-by: Shelley --- .github/workflows/c-host.yml | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/c-host.yml diff --git a/.github/workflows/c-host.yml b/.github/workflows/c-host.yml new file mode 100644 index 0000000..92ffcec --- /dev/null +++ b/.github/workflows/c-host.yml @@ -0,0 +1,61 @@ +name: c-host + +on: + merge_group: + push: + branches: + - main + + pull_request: + branches: + - main + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + WASMTIME_VERSION: "42.0.1" + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: 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" \ + | sudo tar xJ --strip-components=1 -C /usr/local + sudo ldconfig + + - name: Build the C host + working-directory: component-model/examples/example-c-host + run: gcc -o adder-host host.c -lwasmtime + + - name: Run the C host + working-directory: component-model/examples/example-c-host + run: | + ./adder-host 1 2 ../example-host/add.wasm | tee output.txt + grep -q '1 + 2 = 3' output.txt + + docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Build Docker image + working-directory: component-model/examples/example-c-host + run: docker build -t example-c-host:latest . + + - name: Test Docker image + working-directory: component-model/examples/example-c-host + run: | + docker run --rm \ + -v "$(pwd)/../example-host/add.wasm":/component/add.wasm:ro \ + example-c-host:latest | tee output.txt + grep -q '1 + 2 = 3' output.txt