├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── CMakeLists.txt
├── README.md
├── wasm-sample-app
├── .gitignore
├── README.md
├── source.c
└── target.wasm
├── wasm-wasi-sample-app
├── README.md
├── program.c
└── program.wasm
├── wasmer-c-api-example.c
└── wasmer-wasi-c-api-example.c
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: wasmer-c-api build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | strategy:
8 | matrix:
9 | os: [ubuntu-latest, macos-latest, windows-latest]
10 | runs-on: ${{ matrix.os }}
11 |
12 | steps:
13 | - name: checkout
14 | uses: actions/checkout@v1
15 | - name: install rust
16 | uses: actions-rs/toolchain@v1.0.0
17 | with:
18 | profile: minimal
19 | toolchain: stable
20 | override: true
21 | - name: cmake
22 | if: matrix.os != 'windows-latest'
23 | run: cmake .
24 | - name: cmake windows
25 | if: matrix.os == 'windows-latest'
26 | run: cmake . -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND"
27 | - name: make
28 | run: make
29 | - name: run the examples (Unix)
30 | if: matrix.os != 'windows-latest'
31 | run: |
32 | ./wasmer-c-api-example
33 | ./wasmer-wasi-c-api-example
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | CMakeLists.txt.user
2 | CMakeCache.txt
3 | CMakeFiles
4 | CMakeScripts
5 | Testing
6 | Makefile
7 | cmake_install.cmake
8 | install_manifest.txt
9 | compile_commands.json
10 | CTestTestfile.cmake
11 | _deps
12 | wasmer
13 | rust-build
14 | wasmer-c-api-example
15 | wasmer-wasi-c-api-example
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 2.6)
2 | project (WasmerCApiExample)
3 | add_executable(wasmer-c-api-example wasmer-c-api-example.c)
4 | add_executable(wasmer-wasi-c-api-example wasmer-wasi-c-api-example.c)
5 |
6 | include(ExternalProject)
7 | set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust-build)
8 | ExternalProject_Add(
9 | wasmer-runtime-c-api
10 | DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build
11 | GIT_REPOSITORY https://github.com/wasmerio/wasmer.git
12 | GIT_TAG origin/master
13 | CONFIGURE_COMMAND ""
14 | BUILD_COMMAND cargo build -p wasmer-runtime-c-api
15 | COMMAND cargo build -p wasmer-runtime-c-api
16 | BINARY_DIR "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/"
17 | INSTALL_COMMAND ""
18 | LOG_BUILD ON)
19 | add_dependencies(wasmer-c-api-example wasmer-runtime-c-api)
20 |
21 | if(WIN32)
22 | set(WASMER_LIB "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/target/debug/wasmer_runtime_c_api.dll")
23 | else()
24 | set(WASMER_LIB "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/target/debug/libwasmer_runtime_c_api${CMAKE_SHARED_LIBRARY_SUFFIX}")
25 | endif()
26 |
27 | if(NOT WASMER_LIB)
28 | message(FATAL_ERROR "wasmer library not found")
29 | endif()
30 |
31 | target_link_libraries(wasmer-c-api-example general ${WASMER_LIB})
32 | target_link_libraries(wasmer-wasi-c-api-example general ${WASMER_LIB})
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Note: This repository is outdated and archived. It showcased the deprecated Wasmer C API. The new C API lands in [the Wasmer repository](https://github.com/wasmerio/wasmer/tree/master/lib/c-api) with [its examples](https://github.com/wasmerio/wasmer/tree/master/lib/c-api/examples) and [its online documentation](https://wasmerio.github.io/wasmer/crates/wasmer_c_api/index.html).**
2 |
3 |
4 |
5 | # C-API Embedder App Example
6 |
7 | [](https://github.com/wasmerio/wasmer-c-api/actions)
8 |
9 | This repo showcases how to use the [wasmer-runtime-c-api](https://crates.io/crates/wasmer-runtime-c-api/) from C.
10 |
11 | The documentation containing installation instructions, and additional examples can be found at the [Wasmer C/C++ Integration Documentation](https://docs.wasmer.io/runtime/c-integration/runtime-c-integration-installation).
12 |
13 | See [`wasmer-c-api-example.c`](./wasmer-c-api-example.c) and [`wasmer-wasi-c-api-example.c`](./wasmer-wasi-c-api-example.c) for the example implementations.
14 |
15 | The `wasm-sample-app` and `wasmer-wasi-c-api-example` directories contain example C wasm apps to run in the embedder app.
16 |
17 | ## Requirements
18 | - CMake
19 | - Emscripten to build the sample app
20 |
21 | ## Building & Running
22 |
23 | ```bash
24 | # Building the wasm-sample-app
25 | cd wasm-sample-app && emcc source.c -Os -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS="['_hello_wasm']" -o target.wasm
26 |
27 | # Building the wasm-wasi-sample-app
28 | cd wasmer-wasi-c-api-example && wasicc program.c -Oz -o program.wasm -Wl,--allow-undefined -Wl,--export-all
29 |
30 | # Build the embedder api
31 | cmake .
32 | make
33 |
34 | # Run the executables
35 | ./wasmer-c-api-example
36 | ./wasmer-wasi-c-api-example
37 | ```
38 |
39 | ## Reference API Documentation
40 |
41 | The Reference API for the Wasmer C API can be found [here](https://wasmerio.github.io/wasmer/c/runtime-c-api/).
42 |
--------------------------------------------------------------------------------
/wasm-sample-app/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | **/*.rs.bk
3 | Cargo.lock
4 |
--------------------------------------------------------------------------------
/wasm-sample-app/README.md:
--------------------------------------------------------------------------------
1 | # Hello World
2 |
3 | ## Requirements
4 | - Emscripten: https://emscripten.org/
5 |
6 | ## Building
7 | `emcc source.c -Os -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS="['_hello_wasm']" -o target.wasm`
8 |
9 | ## Version
10 | The `target.wasm` file was build with the following version:
11 | ```
12 | emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.38.21
13 | clang version 6.0.1 (emscripten 1.38.21 : 1.38.21)
14 | ```
--------------------------------------------------------------------------------
/wasm-sample-app/source.c:
--------------------------------------------------------------------------------
1 | extern void print_str(char *ptr, int len);
2 |
3 | int hello_wasm()
4 | {
5 | char *str = "Hello, World!";
6 | print_str(str, 13);
7 | return 0;
8 | }
--------------------------------------------------------------------------------
/wasm-sample-app/target.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasmerio/wasmer-c-api/0fb412b502289e326fa11af4ec420c9d8836a484/wasm-sample-app/target.wasm
--------------------------------------------------------------------------------
/wasm-wasi-sample-app/README.md:
--------------------------------------------------------------------------------
1 | # WASI Sample App
2 |
3 | ## Requirements
4 | - Install [wasienv] (instructions in its README.md) or manually set up Clang with a WASI sysroot
5 |
6 | ## Building
7 | ```sh
8 | # we must pass extra linker flags because we're extending the normal WASI ABI
9 | wasicc program.c -Oz -o program.wasm -Wl,--allow-undefined -Wl,--export-all
10 | ```
11 |
12 | [wasienv]: https://github.com/wasienv/wasienv
13 |
--------------------------------------------------------------------------------
/wasm-wasi-sample-app/program.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | extern void host_print(char *ptr, int len);
7 |
8 | __attribute__((used))
9 | int print_random_number_of_length(int size)
10 | {
11 | while (size > 0) {
12 | int digit = random() % 10;
13 | putchar(digit + '0');
14 | --size;
15 | }
16 | putchar('\n');
17 |
18 | return 0;
19 | }
20 |
21 | int main(int argc, char* argv[])
22 | {
23 | printf("The program name is %s\n", argv[0]);
24 | printf("Found %d arguments\n\n", argc);
25 | char* static_str = "Here's a string from the guest";
26 | host_print(static_str, strlen(static_str));
27 |
28 | srandom(time(NULL));
29 | }
30 |
--------------------------------------------------------------------------------
/wasm-wasi-sample-app/program.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasmerio/wasmer-c-api/0fb412b502289e326fa11af4ec420c9d8836a484/wasm-wasi-sample-app/program.wasm
--------------------------------------------------------------------------------
/wasmer-c-api-example.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "rust-build/src/wasmer-runtime-c-api/lib/runtime-c-api/wasmer.h"
3 | #include
4 | #include
5 | #include
6 |
7 | static int print_str_called = false;
8 |
9 | // Host function that will be imported into the Web Assembly Instance
10 | void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
11 | {
12 | print_str_called = true;
13 | const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
14 | uint8_t *mem_bytes = wasmer_memory_data(memory);
15 | printf("%.*s", len, mem_bytes + ptr);
16 | }
17 |
18 | // Use the last_error API to retrieve error messages
19 | void print_wasmer_error()
20 | {
21 | int error_len = wasmer_last_error_length();
22 | printf("Error len: `%d`\n", error_len);
23 | char *error_str = malloc(error_len);
24 | wasmer_last_error_message(error_str, error_len);
25 | printf("Error str: `%s`\n", error_str);
26 | }
27 |
28 | int main()
29 | {
30 | // Create a new func to hold the parameter and signature
31 | // of our `print_str` host function
32 | wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
33 | wasmer_value_tag returns_sig[] = {};
34 | wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
35 |
36 | // Create module name for our imports
37 | // represented in bytes for UTF-8 compatability
38 | const char *module_name = "env";
39 | wasmer_byte_array module_name_bytes = { .bytes = (const uint8_t *) module_name,
40 | .bytes_len = strlen(module_name) };
41 |
42 | // Define a function import
43 | const char *import_name = "_print_str";
44 | wasmer_byte_array import_name_bytes = { .bytes = (const uint8_t *) import_name,
45 | .bytes_len = strlen(import_name) };
46 | wasmer_import_t func_import = { .module_name = module_name_bytes,
47 | .import_name = import_name_bytes,
48 | .tag = WASM_FUNCTION,
49 | .value.func = func };
50 |
51 | // Define a memory import
52 | const char *import_memory_name = "memory";
53 | wasmer_byte_array import_memory_name_bytes = { .bytes = (const uint8_t *) import_memory_name,
54 | .bytes_len = strlen(import_memory_name) };
55 | wasmer_import_t memory_import = { .module_name = module_name_bytes,
56 | .import_name = import_memory_name_bytes,
57 | .tag = WASM_MEMORY };
58 | wasmer_memory_t *memory = NULL;
59 | wasmer_limit_option_t max = { .has_some = true,
60 | .some = 256 };
61 | wasmer_limits_t descriptor = { .min = 256,
62 | .max = max };
63 |
64 | wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
65 | if (memory_result != WASMER_OK)
66 | {
67 | print_wasmer_error();
68 | }
69 | memory_import.value.memory = memory;
70 |
71 | // Define a global import
72 | const char *import_global_name = "__memory_base";
73 | wasmer_byte_array import_global_name_bytes = { .bytes = (const uint8_t *) import_global_name,
74 | .bytes_len = strlen(import_global_name) };
75 | wasmer_import_t global_import = { .module_name = module_name_bytes,
76 | .import_name = import_global_name_bytes,
77 | .tag = WASM_GLOBAL };
78 |
79 | wasmer_value_t val = { .tag = WASM_I32,
80 | .value.I32 = 1024 };
81 | wasmer_global_t *global = wasmer_global_new(val, false);
82 | global_import.value.global = global;
83 |
84 | // Define an array containing our imports
85 | wasmer_import_t imports[] = {func_import, global_import, memory_import};
86 |
87 | // Read the wasm file bytes
88 | FILE *file = fopen("wasm-sample-app/target.wasm", "r");
89 | fseek(file, 0, SEEK_END);
90 | long len = ftell(file);
91 | uint8_t *bytes = malloc(len);
92 | fseek(file, 0, SEEK_SET);
93 | fread(bytes, 1, len, file);
94 | fclose(file);
95 |
96 | // Creates a WebAssembly Instance from wasm bytes and imports
97 | wasmer_instance_t *instance = NULL;
98 | wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 3);
99 | printf("Compile result: %d\n", compile_result);
100 |
101 | if (compile_result != WASMER_OK)
102 | {
103 | print_wasmer_error();
104 | }
105 |
106 | assert(compile_result == WASMER_OK);
107 |
108 | // Call the exported "hello_wasm" function of our instance
109 | wasmer_value_t params[] = {};
110 | wasmer_value_t result_one;
111 | wasmer_value_t results[] = {result_one};
112 | wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
113 | printf("\nCall result: %d\n", call_result);
114 | assert(call_result == WASMER_OK);
115 | assert(print_str_called);
116 |
117 | // Use *_destroy methods to cleanup as specified in the header documentation
118 | wasmer_import_func_destroy(func);
119 | wasmer_global_destroy(global);
120 | wasmer_memory_destroy(memory);
121 | wasmer_instance_destroy(instance);
122 |
123 | return 0;
124 | }
125 |
--------------------------------------------------------------------------------
/wasmer-wasi-c-api-example.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "rust-build/src/wasmer-runtime-c-api/lib/runtime-c-api/wasmer.h"
3 | #include
4 | #include
5 | #include
6 |
7 | static int host_print_called = false;
8 |
9 | // Host function that will be imported into the Web Assembly Instance
10 | void host_print(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
11 | {
12 | host_print_called = true;
13 | const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
14 | uint8_t *mem_bytes = wasmer_memory_data(memory);
15 | printf("PRINTING FROM THE HOST: \"%.*s\"\n", len, mem_bytes + ptr);
16 | }
17 |
18 | // helper function to print byte array to stdout
19 | void print_byte_array(wasmer_byte_array *arr) {
20 | for (int i = 0; i < arr->bytes_len; ++i) {
21 | putchar(arr->bytes[i]);
22 | }
23 | }
24 |
25 | // Use the last_error API to retrieve error messages
26 | void print_wasmer_error()
27 | {
28 | int error_len = wasmer_last_error_length();
29 | printf("Error len: `%d`\n", error_len);
30 | char *error_str = malloc(error_len);
31 | wasmer_last_error_message(error_str, error_len);
32 | printf("Error str: `%s`\n", error_str);
33 | }
34 |
35 | int main()
36 | {
37 | // Create a new func to hold the parameter and signature
38 | // of our `host_print` host function
39 | wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
40 | wasmer_value_tag returns_sig[] = {};
41 | wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0);
42 |
43 | // Create module name for our imports
44 | // represented in bytes for UTF-8 compatability
45 | const char *module_name = "env";
46 | wasmer_byte_array module_name_bytes = { .bytes = (const uint8_t *) module_name,
47 | .bytes_len = strlen(module_name) };
48 |
49 | // Define a function import
50 | const char *import_name = "host_print";
51 | wasmer_byte_array import_name_bytes = { .bytes = (const uint8_t *) import_name,
52 | .bytes_len = strlen(import_name) };
53 | wasmer_import_t func_import = { .module_name = module_name_bytes,
54 | .import_name = import_name_bytes,
55 | .tag = WASM_FUNCTION,
56 | .value.func = func };
57 |
58 | // Define a memory import
59 | const char *import_memory_name = "memory";
60 | wasmer_byte_array import_memory_name_bytes = { .bytes = (const uint8_t *) import_memory_name,
61 | .bytes_len = strlen(import_memory_name) };
62 | wasmer_import_t memory_import = { .module_name = module_name_bytes,
63 | .import_name = import_memory_name_bytes,
64 | .tag = WASM_MEMORY };
65 | wasmer_memory_t *memory = NULL;
66 | wasmer_limit_option_t max = { .has_some = true,
67 | .some = 256 };
68 | wasmer_limits_t descriptor = { .min = 256,
69 | .max = max };
70 |
71 | wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
72 | if (memory_result != WASMER_OK)
73 | {
74 | print_wasmer_error();
75 | }
76 | memory_import.value.memory = memory;
77 |
78 | // Define a global import
79 | const char *import_global_name = "__memory_base";
80 | wasmer_byte_array import_global_name_bytes = { .bytes = (const uint8_t *) import_global_name,
81 | .bytes_len = strlen(import_global_name) };
82 | wasmer_import_t global_import = { .module_name = module_name_bytes,
83 | .import_name = import_global_name_bytes,
84 | .tag = WASM_GLOBAL };
85 |
86 | wasmer_value_t val = { .tag = WASM_I32,
87 | .value.I32 = 1024 };
88 | wasmer_global_t *global = wasmer_global_new(val, false);
89 | global_import.value.global = global;
90 |
91 | // Get the WASI base imports
92 | const char *arg1_val = "wasi-example-program-name";
93 | const char *arg2_val = "--help";
94 | wasmer_byte_array args[2] = { { .bytes = (const uint8_t *) arg1_val,
95 | .bytes_len = strlen(arg1_val) },
96 | { .bytes = (const uint8_t *) arg2_val,
97 | .bytes_len = strlen(arg2_val) } };
98 | const int num_args = (sizeof args) / (sizeof args[0]);
99 |
100 | const char *env1_val = "COLOR_OUTPUT=1";
101 | wasmer_byte_array envs[1] = { { .bytes = (const uint8_t *) env1_val,
102 | .bytes_len = strlen(env1_val) } };
103 | const int num_envs = (sizeof envs) / (sizeof envs[0]);
104 |
105 | wasmer_import_object_t *import_object =
106 | wasmer_wasi_generate_import_object(args, num_args, envs, num_envs, NULL, 0, NULL, 0);
107 | // Define an array containing our imports
108 | wasmer_import_t extra_imports[] = {func_import, global_import, memory_import};
109 |
110 | // Add our custom imports to the WASI ImportObject
111 | wasmer_import_object_extend(import_object, extra_imports, 3);
112 |
113 | // Read the wasm file bytes
114 | FILE *file = fopen("wasm-wasi-sample-app/program.wasm", "r");
115 | fseek(file, 0, SEEK_END);
116 | long len = ftell(file);
117 | uint8_t *bytes = malloc(len);
118 | fseek(file, 0, SEEK_SET);
119 | fread(bytes, 1, len, file);
120 | fclose(file);
121 |
122 | // iterate through and print out our imports
123 | wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object);
124 |
125 | puts("Functions in import object:");
126 | while ( !wasmer_import_object_iter_at_end(func_iter) ) {
127 | wasmer_import_t import = { 0 };
128 | wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import);
129 | assert(result == WASMER_OK);
130 |
131 | print_byte_array(&import.module_name);
132 | putchar(' ');
133 | print_byte_array(&import.import_name);
134 | putchar('\n');
135 |
136 | assert(import.tag == WASM_FUNCTION);
137 | assert(import.value.func);
138 | wasmer_import_object_imports_destroy(&import, 1);
139 | }
140 | wasmer_import_object_iter_destroy(func_iter);
141 | func_iter = NULL;
142 |
143 | // Creates a WebAssembly Module from wasm bytes
144 | wasmer_module_t *module = NULL;
145 | wasmer_compile(&module, bytes, len);
146 |
147 | // Creates a WebAssembly Instance from a Module and ImportObject
148 | wasmer_instance_t *instance = NULL;
149 | wasmer_result_t compile_result = wasmer_module_import_instantiate(&instance, module, import_object);
150 | printf("Compile result: %d\n", compile_result);
151 |
152 | if (compile_result != WASMER_OK)
153 | {
154 | print_wasmer_error();
155 | }
156 |
157 | assert(compile_result == WASMER_OK);
158 |
159 | // Call the pre-main function that invokes main, "_start".
160 | // This is necessary to use the WASI filesystem until WASI finishes support for
161 | // WASI Wasm modules as libraries
162 | wasmer_value_t params[] = { { .tag = WASM_I32, .value.I32 = 42 } };
163 | wasmer_value_t result_one = { 0 };
164 | wasmer_value_t results[] = {result_one};
165 | wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1);
166 | printf("Call result: %d (fn result %d %d)\n", call_result, results[0].tag, results[0].value.I32);
167 | assert(call_result == WASMER_OK);
168 | assert(host_print_called);
169 |
170 | // now call the extra function
171 | call_result = wasmer_instance_call(instance, "print_random_number_of_length", params, 1, results, 1);
172 | printf("Call result: %d (fn result %d %d)\n", call_result, results[0].tag, results[0].value.I32);
173 | assert(call_result == WASMER_OK);
174 |
175 | // Use *_destroy methods to cleanup as specified in the header documentation
176 | wasmer_import_func_destroy(func);
177 | wasmer_global_destroy(global);
178 | wasmer_memory_destroy(memory);
179 | wasmer_import_object_destroy(import_object);
180 | wasmer_instance_destroy(instance);
181 |
182 | return 0;
183 | }
184 |
--------------------------------------------------------------------------------