Skip to content

Commit bf4e9d0

Browse files
authored
Add support for the official Wasm C-API (#1009)
* setup new wasmi_capi crate * c-api: add Wasm config support * add docs to config definitions * make wasm_config_t::inner pub(crate) * add wasm_engine_t API * c-api: add lib docs * c-api: re-export the wrapped Wasmi crate * apply rustfmt * fix bug after merging * refactor wasmi_c_api crate and impl * fix build by adding build.rs file * add wasm.h include * add [lib] config to Cargo.toml * fix doc link * update Cargo.lock * update Cargo.lock for wasmi_c_api * exclude wasmi_c_api from no_std CI builds * reorganize capi folder * rename crates/capi -> crates/c_api * add wasmi.h and sub-headers They are all still empty since the API is still evolving. * use wasmtime-c-api-macros proc. macro utility * add extern "C" blocks to Wasmi headers * add missing includes to wasmi.h * add wasmi_engine_clone to Wasmi C API * fix VSCode wasm.h C detection * apply rustfmt * re-do config Wasmi C API * fix doc links and method names * fix compile error * apply rustfmt * improve doc comments * add wasm.h store APIs * apply rustfmt * add empty wasmi/store.h for future extension * add required safety docs * apply rustfmt * silence clippy warning * add utils submodule * return Error instead of FuelError from get/set_fuel APIs * add note doc comment * silence dead code warnings for now (WIP) * add error.h impls to Wasmi C-API * fix docs * add support for store.h+ext in wasmi_c_api * add CMakeLists.txt for building Wasmi C-API * add dummy conf.h.in this might be useful in the future if we have to conditionally compile the Wasmi C-API. Not needed, yet. * add basic README for Wasmi C-API * add doxygen The doxygen.conf.in file was generated using `doxygen -g doxygen.conf.in` * build the correct Wasmi C-API crate via CMake * add README section for Rust crates using Wasmi C-API * adjust GitHub Actions build CI job * use CMake in CI build job * fix CI build script * use | in yml to separate commands * generate docs via CMake in CI * fix doc CI job * add version outputs to CI job * CI: use actions/checkout to initialize submodules * fix doxygen GITHUB_PATH registration * remove doxygen version printing * use >- instead of | * use different run commands * clean-up doc job * further clean-ups * merge tar verification in Doxygen installation * improve cmake doc comment * add format target to C-API CMake * apply clang-format * check formatting of C-API headers in CI * rename CMake format target to check-format * add new CMake format job * add WIP abort method * add wasm_valtype_t and wasm_valkind_t support * add support for types and vecs for the Wasmi C-API * fix intra doc link warnings * add safety comment to wasm_#t#_vec_t::new * silence dead_code warnings for Wasmi C-API for now * derive Copy and Clone for C-like enum types * add new wasmi_c_api_macros crate * use wasmi_c_api_macros crate in wasmi_c_api_impl * fix global type docs * improve docs of wasm_mutability_t * add support for wasm_foreign_t * add support for wasm_trap_t and wasm_frame_t * add definitions for wasm_frame_vec_t * add missing import * improve doc comment * add support for wasm_ref_t and wasm_val_t * fix doc link * add support for wasm_global_t and wasm_extern_t * fix clippy warning * fix bugs in try_from_mut methods * add support for wasm_memory_t * apply rustfmt * add support for wasm_table_t * fix bug in declare_ref proc macro expansion * add std crate feature to wasmi_c_api[_impl] * unsilence dead_code warnings * add support for wasm_func_t * add support for wasm_module_t * add doc comments for wrapping info * add wrap info to wasm_func_t API * add support for wasm_extern_vec_t * add support for wasm_instance_t This will fail to compile until Wasmi supports the Instance::new API as mandated by the C-API. * fix doc links * apply clippy suggestions
1 parent 3d7f8be commit bf4e9d0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+8007
-0
lines changed

.github/workflows/rust.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030
run: >-
3131
cargo build
3232
--package wasmi
33+
--package wasmi_c_api_impl
3334
--locked
3435
--lib
3536
--no-default-features
@@ -49,11 +50,17 @@ jobs:
4950
run: >-
5051
cargo build
5152
--package wasmi
53+
--package wasmi_c_api_impl
5254
--locked
5355
--lib
5456
--no-default-features
5557
--target wasm32-unknown-unknown
5658
--verbose
59+
- name: Build (CMake)
60+
run: |
61+
cmake --version
62+
cmake -S crates/c_api -B target/c_api --install-prefix "$(pwd)/artifacts"
63+
cmake --build target/c_api --target install
5764
5865
test-asan:
5966
name: Test (Address Sanitizer)
@@ -120,6 +127,11 @@ jobs:
120127
run: cargo fmt --all -- --check
121128
- name: Formatting (fuzz)
122129
run: pushd fuzz && cargo fmt --all -- --check && popd
130+
- name: Format (CMake)
131+
run: |
132+
cmake --version
133+
cmake -S crates/c_api -B target/c_api --install-prefix "$(pwd)/artifacts"
134+
cmake --build target/c_api --target check-format
123135
124136
doc:
125137
name: Documentation
@@ -135,6 +147,15 @@ jobs:
135147
env:
136148
RUSTDOCFLAGS: "-D warnings"
137149
run: cargo doc --workspace --locked --all-features --no-deps --document-private-items
150+
- name: Install Doxygen
151+
run: |
152+
tar --version
153+
curl -L https://sourceforge.net/projects/doxygen/files/rel-1.11.0/doxygen-1.11.0.linux.bin.tar.gz/download | tar xzf - -C $GITHUB_WORKSPACE
154+
echo "$GITHUB_WORKSPACE/doxygen-1.11.0/bin" >> $GITHUB_PATH
155+
- name: Configure CMake for Wasmi C-API
156+
run: cmake -S crates/c_api -B target/c_api
157+
- name: Generate Docs via Cmake and Doxygen
158+
run: cmake --build target/c_api --target doc
138159

139160
audit:
140161
name: Audit

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"files.associations": {
3+
"wasm.h": "c"
4+
}
5+
}

Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[workspace]
22
members = [
33
"crates/collections",
4+
"crates/c_api/artifact",
5+
"crates/c_api/macro",
46
"crates/cli",
57
"crates/core",
68
"crates/wasmi",
@@ -27,6 +29,8 @@ wasmi = { version = "0.35.0", path = "crates/wasmi", default-features = false }
2729
wasmi_wasi = { version = "0.35.0", path = "crates/wasi", default-features = false }
2830
wasmi_core = { version = "0.35.0", path = "crates/core", default-features = false }
2931
wasmi_collections = { version = "0.35.0", path = "crates/collections", default-features = false }
32+
wasmi_c_api_impl = { version = "0.35.0", path = "crates/c_api" }
33+
wasmi_c_api_macros = { version = "0.35.0", path = "crates/c_api/macro" }
3034
num-traits = { version = "0.2.8", default-features = false }
3135

3236
[profile.bench]

crates/c_api/CMakeLists.txt

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
cmake_minimum_required(VERSION 3.18)
2+
project(wasmi C)
3+
4+
set(WASMI_USER_CARGO_BUILD_OPTIONS "" CACHE STRING "Additional cargo flags (such as --features) to apply to the build command")
5+
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
6+
option(WASMI_ALWAYS_BUILD "If cmake should always invoke cargo to build Wasmi" ON)
7+
set(WASMI_TARGET "" CACHE STRING "Rust target to build for")
8+
9+
if(NOT WASMI_TARGET)
10+
execute_process(
11+
COMMAND rustc -vV
12+
OUTPUT_VARIABLE RUSTC_VERSION
13+
)
14+
string(REGEX MATCH "host: ([^ \n]+)" RUSTC_VERSION_MATCH "${RUSTC_VERSION}")
15+
string(STRIP "${CMAKE_MATCH_1}" WASMI_TARGET)
16+
endif()
17+
18+
# If the Wasmi Rust crate shall be built using debug or release settings.
19+
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
20+
set(WASMI_BUILD_TYPE "debug")
21+
set(CARGO_PROFILE_PANIC CARGO_PROFILE_DEBUG_PANIC)
22+
else()
23+
set(WASMI_BUILD_TYPE_FLAG "--profile" "bench")
24+
set(WASMI_BUILD_TYPE "release")
25+
set(CARGO_PROFILE_PANIC CARGO_PROFILE_RELEASE_PANIC)
26+
endif()
27+
28+
# Sets the Wasmi target directory.
29+
set(WASMI_TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../target/${WASMI_TARGET}/${WASMI_BUILD_TYPE})
30+
if(WASMI_TARGET MATCHES "darwin")
31+
set(WASMI_SHARED_FILES libwasmi.dylib)
32+
set(WASMI_STATIC_FILES libwasmi.a)
33+
elseif(WASMI_TARGET MATCHES "windows-gnu")
34+
set(WASMI_SHARED_FILES libwasmi.dll.a wasmi.dll)
35+
set(WASMI_STATIC_FILES libwasmi.a)
36+
elseif(WASMI_TARGET MATCHES "windows-msvc")
37+
set(WASMI_SHARED_FILES wasmi.dll.lib wasmi.dll)
38+
set(WASMI_STATIC_FILES wasmi.lib)
39+
else()
40+
set(WASMI_SHARED_FILES libwasmi.so)
41+
set(WASMI_STATIC_FILES libwasmi.a)
42+
endif()
43+
list(TRANSFORM WASMI_SHARED_FILES PREPEND ${WASMI_TARGET_DIR}/)
44+
list(TRANSFORM WASMI_STATIC_FILES PREPEND ${WASMI_TARGET_DIR}/)
45+
46+
# Instructions on how to build and install the Wasmi Rust crate.
47+
find_program(WASMI_CARGO_BINARY cargo REQUIRED)
48+
include(ExternalProject)
49+
ExternalProject_Add(
50+
wasmi-crate
51+
DOWNLOAD_COMMAND ""
52+
CONFIGURE_COMMAND ""
53+
INSTALL_COMMAND "${WASMI_INSTALL_COMMAND}"
54+
BUILD_COMMAND
55+
${CMAKE_COMMAND} -E env ${CARGO_PROFILE_PANIC}=abort
56+
${WASMI_CARGO_BINARY} build
57+
--package wasmi_c_api
58+
--target ${WASMI_TARGET}
59+
--no-default-features
60+
${WASMI_BUILD_TYPE_FLAG}
61+
${WASMI_FEATURES}
62+
${WASMI_USER_CARGO_BUILD_OPTIONS}
63+
USES_TERMINAL_BUILD TRUE
64+
BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/artifact
65+
BUILD_ALWAYS ${WASMI_ALWAYS_BUILD}
66+
BUILD_BYPRODUCTS ${WASMI_SHARED_FILES} ${WASMI_STATIC_FILES}
67+
)
68+
add_library(wasmi INTERFACE)
69+
add_dependencies(wasmi wasmi-crate)
70+
71+
# Handle platform-specific settings for linking
72+
if(BUILD_SHARED_LIBS)
73+
if(NOT WASMI_TARGET MATCHES "windows")
74+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")
75+
endif()
76+
list(GET WASMI_SHARED_FILES 0 WASMI_SHARED_LIB_TO_LINK)
77+
target_link_libraries(wasmi INTERFACE ${WASMI_SHARED_LIB_TO_LINK})
78+
else()
79+
target_link_libraries(wasmi INTERFACE ${WASMI_STATIC_FILES})
80+
81+
if(WASMI_TARGET MATCHES "windows")
82+
target_compile_options(wasmi INTERFACE -DWASM_API_EXTERN= -DWASI_API_EXTERN=)
83+
target_link_libraries(wasmi INTERFACE ws2_32 advapi32 userenv ntdll shell32 ole32 bcrypt)
84+
elseif(NOT WASMI_TARGET MATCHES "darwin")
85+
target_link_libraries(wasmi INTERFACE pthread dl m)
86+
endif()
87+
endif()
88+
89+
target_include_directories(wasmi INTERFACE
90+
${CMAKE_CURRENT_SOURCE_DIR}/include
91+
${CMAKE_BINARY_DIR}/include
92+
)
93+
94+
set(WASMI_GENERATED_CONF_H ${CMAKE_BINARY_DIR}/include/wasmi/conf.h)
95+
configure_file(
96+
${CMAKE_CURRENT_SOURCE_DIR}/include/wasmi/conf.h.in
97+
${WASMI_GENERATED_CONF_H}
98+
)
99+
100+
include(GNUInstallDirs)
101+
install(
102+
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
103+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
104+
FILES_MATCHING PATTERN "*.h" PATTERN "*.hh"
105+
)
106+
install(
107+
FILES ${WASMI_GENERATED_CONF_H}
108+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/wasmi
109+
)
110+
install(
111+
FILES ${WASMI_SHARED_FILES} ${WASMI_STATIC_FILES}
112+
DESTINATION ${CMAKE_INSTALL_LIBDIR}
113+
)
114+
115+
if(WASMI_TARGET MATCHES "darwin")
116+
set(INSTALLED_LIB "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libwasmi.dylib")
117+
install(
118+
FILES "${INSTALLED_LIB}"
119+
DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
120+
COMPONENT Runtime
121+
)
122+
if(NOT CMAKE_INSTALL_NAME_TOOL)
123+
message(WARNING "CMAKE_INSTALL_NAME_TOOL is not set. LC_ID_DYLIB for libwasmi.dylib will not be set.")
124+
else()
125+
set(install_name_tool_cmd
126+
"${CMAKE_INSTALL_NAME_TOOL}"
127+
"-id"
128+
"@rpath/libwasmi.dylib"
129+
"${INSTALLED_LIB}"
130+
)
131+
install(CODE "execute_process(COMMAND ${install_name_tool_cmd})")
132+
endif()
133+
endif()
134+
135+
# Documentation Generation via Doxygen:
136+
set(DOXYGEN_CONF_IN ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.conf.in)
137+
set(DOXYGEN_CONF_OUT ${CMAKE_BINARY_DIR}/doxygen.conf)
138+
configure_file(${DOXYGEN_CONF_IN} ${DOXYGEN_CONF_OUT})
139+
add_custom_target(doc
140+
COMMAND doxygen ${DOXYGEN_CONF_OUT}
141+
DEPENDS ${WASMI_GENERATED_CONF_H} ${DOXYGEN_CONF_OUT}
142+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
143+
)
144+
145+
# C-Header Formatting via clang-format:
146+
find_program(CLANG_FORMAT clang-format REQUIRED)
147+
file(GLOB_RECURSE HEADER_FILES
148+
${CMAKE_CURRENT_SOURCE_DIR}/include/wasmi.h
149+
${CMAKE_CURRENT_SOURCE_DIR}/include/wasmi/*.h
150+
${CMAKE_CURRENT_SOURCE_DIR}/include/wasmi/*.hh
151+
)
152+
add_custom_target(check-format
153+
COMMAND ${CLANG_FORMAT} -style=llvm -Werror --dry-run ${HEADER_FILES}
154+
COMMENT "clang-format: Check formatting for Wasmi C-API header files"
155+
)
156+
add_custom_target(format
157+
COMMAND ${CLANG_FORMAT} -style=llvm -i ${HEADER_FILES}
158+
COMMENT "clang-format: Apply formatting rules for Wasmi C-API header files"
159+
)

crates/c_api/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "wasmi_c_api_impl"
3+
version.workspace = true
4+
rust-version.workspace = true
5+
documentation = "https://docs.rs/wasmi_c_api_impl"
6+
description = "C bindings for the Wasmi WebAssembly interpreter"
7+
authors.workspace = true
8+
repository.workspace = true
9+
edition.workspace = true
10+
readme.workspace = true
11+
license.workspace = true
12+
keywords.workspace = true
13+
categories.workspace = true
14+
exclude.workspace = true
15+
links = "wasmi_c_api"
16+
17+
[dependencies]
18+
wasmi = { workspace = true }
19+
wasmi_c_api_macros = { workspace = true }
20+
21+
[lib]
22+
name = "wasmi_c_api"
23+
test = false
24+
doctest = false
25+
26+
[features]
27+
default = ["std"]
28+
std = []

crates/c_api/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Wasmi C-API
2+
3+
## Usage in a C Project
4+
5+
If you have a C project with which you want to use Wasmi, you can interface with Wasmi's C API:
6+
7+
### Prerequisites
8+
9+
- [CMake](https://cmake.org/)
10+
- [A Rust Toolchain](https://www.rust-lang.org/tools/install)
11+
12+
From the root of the Wasmi repository, run the following commands:
13+
14+
```shell
15+
cmake -S crates/c_api -B target/c_api --install-prefix "$(pwd)/artifacts" &&
16+
cmake --build target/c_api --target install
17+
```
18+
19+
These commands will produce the following files:
20+
21+
- `artifacts/lib/libwasmi.{a,lib}`:
22+
The static Wasmi library.
23+
- `artifacts/lib/libwasmi.{so,dylib,dll}`:
24+
The dynamic (or shared) Wasmi library.
25+
- `artifacts/include/**.h`:
26+
The header files for interfacing with Wasmi from C or C++.
27+
28+
## Usage in a Rust Project
29+
30+
If you have a Rust crate that uses a C or C++ library that uses Wasmi, you can link to the Wasmi C API as follows:
31+
32+
1. Add a dependency on the `wasmi_c_api_impl` crate to your `Cargo.toml`. Note that package name differs from the library name.
33+
34+
```toml
35+
[dependencies]
36+
wasmi_c_api = { version = "0.35.0", package = "wasmi_c_api_impl" }
37+
```
38+
39+
2. In your `build.rs` file, when compiling your C/C++ source code, add the C `wasmi_c_api` headers to the include path:
40+
41+
```rust
42+
fn main() {
43+
let mut cfg = cc::Build::new();
44+
// Add the Wasmi and standard Wasm C-API headers to the include path.
45+
cfg
46+
.include(std::env::var("DEP_WASMI_C_API_INCLUDE").unwrap());
47+
.include(std::env::var("DEP_WASMI_C_API_WASM_INCLUDE").unwrap());
48+
// Compile your C code.
49+
cfg
50+
.file("src/your_c_code.c")
51+
.compile("your_library");
52+
}
53+
```

crates/c_api/artifact/Cargo.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "wasmi_c_api"
3+
version.workspace = true
4+
rust-version.workspace = true
5+
documentation = "https://docs.rs/wasmi_c_api_impl"
6+
description = "C bindings for the Wasmi WebAssembly interpreter"
7+
authors.workspace = true
8+
repository.workspace = true
9+
edition.workspace = true
10+
readme.workspace = true
11+
license.workspace = true
12+
keywords.workspace = true
13+
categories.workspace = true
14+
exclude.workspace = true
15+
publish = false
16+
17+
[lib]
18+
name = "wasmi"
19+
crate-type = ["staticlib", "cdylib"]
20+
doc = false
21+
test = false
22+
doctest = false
23+
path = "lib.rs"
24+
25+
[dependencies]
26+
wasmi_c_api_impl = { workspace = true }
27+
28+
[features]
29+
default = ["std"]
30+
std = ["wasmi_c_api_impl/std"]

crates/c_api/artifact/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub use wasmi_c_api::*;

crates/c_api/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
3+
println!("cargo:include={dir}/include");
4+
}

0 commit comments

Comments
 (0)