Skip to content
Open
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
61 changes: 61 additions & 0 deletions .github/workflows/c-host.yml
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the "host" wording here be renamed "application" both in file naming and in content

Original file line number Diff line number Diff line change
@@ -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
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
35 changes: 35 additions & 0 deletions component-model/examples/example-c-host/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
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.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* C host for the adder WebAssembly component.
* C application that embeds Wasmtime and executes a component that does addition

*
* 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -303,40 +302,109 @@ world root {
...
```

### 6. Run the component from the example host
## 6. Run the component with `wasmtime --invoke`

The following section requires you to have [a Rust toolchain][rust] installed.
If you want to quickly run the `add` export without writing a host application that embeds Wasmtime,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If you want to quickly run the `add` export without writing a host application that embeds Wasmtime,
If you want to quickly run the `add` export without writing an application that embeds Wasmtime,

you can invoke it directly with the Wasmtime CLI.

> [!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.)
```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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## 7. Run the component from the example C host
## 7. Run the component from C/C++ Applications


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`.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`component-model/examples/example-c-host/host.c`.
`component-model/examples/example-c-application/app.c`.


The application expects three arguments: the two numbers to add and the Wasm component that executed the addition. For example:

```sh
./adder-host <x> <y> <path-to-component.wasm>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
./adder-host <x> <y> <path-to-component.wasm>
./app <x> <y> <path-to-component.wasm>

```

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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Option A: Compile and run the host directly
### Option A: Run the C application directly


If the Wasmtime C API headers and library are installed on your system,
you can compile and run the host directly:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
you can compile and run the host directly:
you can compile and run the application directly:


On Linux, the following commands install the C API artifacts in `/usr/local`
using the same approach as the `Dockerfile`:

```console
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
gcc libc6-dev curl xz-utils ca-certificates

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

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

sudo ldconfig
```

```console
cd component-model/examples/example-c-host
gcc -o adder-host host.c -lwasmtime
./adder-host 1 2 /absolute/path/to/adder.wasm
Comment on lines +360 to +362
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cd component-model/examples/example-c-host
gcc -o adder-host host.c -lwasmtime
./adder-host 1 2 /absolute/path/to/adder.wasm
cd component-model/examples/example-c-application
gcc -o app app.c -lwasmtime
./app 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

Instead of installing the Wasmtime C API, you can use the provided Dockerfile which builds the C application.

{{#include ../example-host-part1.md}}
From `component-model/examples/example-c-host`:

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):
```console
cd component-model/examples/example-c-host
docker build -t example-c-host:latest .
```

{{#include ../example-host-part2.md}}
Then run the container, passing in the component as a volume:

## 7. Run the component from C/C++ Applications
```console
docker run --rm \
-v "$(pwd)/../example-host/add.wasm":/component/add.wasm:ro \
example-c-host:latest
```

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++.
Expected output:

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.
```sh
1 + 2 = 3
```

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).
The default command runs `adder-host 1 2 /component/add.wasm`,
so you can also override the arguments:

[!NOTE]: #
[!WARNING]: #
```console
docker run --rm \
-v "$(pwd)/../example-host/add.wasm":/component/add.wasm:ro \
example-c-host:latest 40 2 /component/add.wasm
```
Loading