Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions component-model/examples/example-c-host/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
host
58 changes: 58 additions & 0 deletions component-model/examples/example-c-host/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
1 change: 1 addition & 0 deletions component-model/examples/example-c-host/adder.wasm
127 changes: 127 additions & 0 deletions component-model/examples/example-c-host/host.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wasmtime.h>

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 <x> <y> <component.wasm>\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;
}
2 changes: 2 additions & 0 deletions component-model/examples/example-c-host/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@_default:
echo "TODO"; exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -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]: #