Search code examples
c++rustwebassemblyffirust-wasm

Compiling Rust that calls C++ to WASM


I've found this How do I use a C library in a Rust library compiled to WebAssembly?, but this relies on wasm-merge, which has been discontinued. My problem is the following, I have some C++ code that I would like to call from Rust in order to have the option to compile the resulting package either to native code for use in mobile apps or to Webassembly for use in Node.js. At the moment, I have the following setup:

libTest.cpp

extern "C"{
    int test_function(int i){
        return i;
    }
}

lib.rs

use wasm_bindgen::prelude::*;

#[link(name = "Test")]
extern "C"{
    pub fn test_function(i: i32) -> i32 ;
}

#[wasm_bindgen]
pub fn test_function_js(i : i32) -> i32{
    let res = unsafe{test_function(i)};
    res
}    

build.rs

fn main() {
    cc::Build::new()
        .cpp(true)
        .file("libTest.cpp")
        .compile("libTest.a");
}

This compiles and works when compiling to native code using a simple cargo build, but does not work for building to wasm, for which I'm doing cargo build --target wasm32-unknown-unknown. There I get the two errors

  = note: rust-lld: error: /[path to my project]/target/wasm32-unknown-unknown/debug/build/rustCpp-cc5e129d4ee03598/out/libTest.a: archive has no index; run ranlib to add one
          rust-lld: error: unable to find library -lstdc++

Is this the right way to go about this and if yes, how do I resolve the above error? If not, how do I best go about calling C++ from Rust and compiling it to wasm?


Solution

  • (This is not really a full answer, but too long for a comment.)

    I can compile your example with

    cc::Build::new()
      .archiver("llvm-ar") // Takes care of "archive has no index" - emar might be an alternative
      .cpp_link_stdlib(None) // Takes care of "unable to find library -lstdc++"
      … // rest of your flags
    

    but I'm not sure whether the resulting binary is useful to you. Especially, it contains WASI imports when compiled in debug mode, and you'll probably get linker errors if you start using any interesting functions (e.g. sin).

    You could in theory give the C++ compiler a full stdlib to work with through .flag("--sysroot=/usr/share/wasi-sysroot/") (if you have wasi-sdk or wasi-libc++ installed), but

    • I'm unsure how to best account for differences of where this folder is usually installed (maybe like this)
    • I think you have to also pass this flag at link time, but I don't know how (it seems to work without, though)
    • that would target wasi, and may not be useful for whatever bindgen-based environment you have in mind.