When targeting WebAssembly, C/C++ code can be compiled as a library, and then get statically linked to a Rust project.
Install the Zig toolchain in order to compile C and C++ code to WebAssembly.
zig cc is available for many platforms including Windows, and makes it easy to switch back and forth between native and wasm targets. WebAssembly is a Tier-1 target, and it was successfully used to port libraries such as ffmpeg, zlib, openssl, boringssl and libsodium.
Compile individual files with zig cc -target wasm32-freestanding or zig cc -target wasm32-wasi instead of cc :
zig cc -target wasm32-wasi -Os -c example.cHere's an example C file:
int add(int a, int b)
{
return a + b;
}The wasm32-freestanding target works everywhere out of the box, including web browsers, whereas wasm32-wasi requires an additional JavaScript helper, or a runtime that supports WASI-Core.
Runtime-specific optimizations can be enabled with the -mcpu command-line flag, for example -mcpu=generic+simd128. Optimizations apply both to application/libraries being compiled and to the C library.
Link the object files into a static library, by using the zig ar command instead of the ar command:
zig ar r libexample.a *.oCongratulations! The WebAssembly library has been created.
An alternative to steps 2 and 3 is to build the project using the Zig build system, by creating a build.zig file, and then compile with zig build -Dtarget=wasm32-wasi -Doptimize=ReleaseSmall. Examples: libhydrogen, libaegis.
Create a new Rust project, as well as the following files in its root directory:
rust-toolchainwith the following content:
[toolchain]
targets = ["wasm32-wasi"]build.rswith the following content:
use std::env;
pub fn main() {
let src_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
println!("cargo:rustc-link-lib=static=example"); // name of the wasm static library to link
println!("cargo:rustc-link-search=native={}/wasm-libs", src_dir); // directory of the wasm static library
}Create a wasm-libs directory, and copy the libexample.a file into that directory, so that the Rust project can find it.
Use C functions using the Rust FFI interface:
use core::ffi::*;
extern "C" {
pub fn add(a: c_int, b: c_int) -> c_int;
}
fn main() {
println!("{}", unsafe { add(1, 2) });
}Compile the Rust code:
cargo build --target=wasm32-wasiOr install the cargo-wasix Cargo subcommand.