Skip to content
Open
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
54 changes: 48 additions & 6 deletions rust/ruby-rbs-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,46 @@ fn main() -> Result<(), Box<dyn Error>> {
let include = vendor_rbs.join("include");
let c_src = vendor_rbs.join("src");

build(&include, &c_src)?;
let target = env::var("TARGET").unwrap_or_default();
let is_wasm = target.contains("wasm32");

let bindings = generate_bindings(&include)?;
build(&include, &c_src, is_wasm)?;

let bindings = generate_bindings(&include, is_wasm)?;
write_bindings(&bindings)?;

Ok(())
}

fn build(include_dir: &Path, src_dir: &Path) -> Result<(), Box<dyn Error>> {
fn build(include_dir: &Path, src_dir: &Path, is_wasm: bool) -> Result<(), Box<dyn Error>> {
let mut build = cc::Build::new();

build.include(include_dir);

// Suppress unused parameter warnings from C code
build.flag_if_supported("-Wno-unused-parameter");

// Cross-compile the C parser to wasm with the WASI SDK's clang. Only the C
// compile targets wasm; bindgen (below) still runs against the host, since
// the resulting #[repr(C)] declarations are layout-portable.
if is_wasm {
let wasi_sdk = PathBuf::from(
env::var("WASI_SDK_PATH").expect("WASI_SDK_PATH must be set for wasm builds"),
);
build.compiler(wasi_sdk.join("bin").join("clang"));

let sysroot = wasi_sdk.join("share").join("wasi-sysroot");
build.flag(&format!("--sysroot={}", sysroot.display()));
build.include(sysroot.join("include"));

println!(
"cargo:rustc-link-search=native={}",
sysroot.join("lib/wasm32-wasi").display()
);
build.define("_WASI_EMULATED_MMAN", "1");
println!("cargo:rustc-link-lib=wasi-emulated-mman");
}

build.files(source_files(src_dir)?);
build.try_compile("rbs")?;

Expand Down Expand Up @@ -64,8 +88,11 @@ fn source_files<P: AsRef<Path>>(root_dir: P) -> Result<Vec<String>, Box<dyn Erro
Ok(files)
}

fn generate_bindings(include_path: &Path) -> Result<bindgen::Bindings, Box<dyn Error>> {
let bindings = bindgen::Builder::default()
fn generate_bindings(
include_path: &Path,
is_wasm: bool,
) -> Result<bindgen::Bindings, Box<dyn Error>> {
let mut builder = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}", include_path.display()))
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
Expand Down Expand Up @@ -170,7 +197,22 @@ fn generate_bindings(include_path: &Path) -> Result<bindgen::Bindings, Box<dyn E
// Constant pool functions
.allowlist_function("rbs_constant_pool_free")
.allowlist_function("rbs_constant_pool_id_to_constant")
.allowlist_function("rbs_constant_pool_init")
.allowlist_function("rbs_constant_pool_init");

if is_wasm {
// Generate the FFI declarations for the HOST target rather than the wasm
// target: host bindgen is reliable, whereas wasm-target bindgen is
// libclang-fragile (drops functions / emits opaque structs). The emitted
// #[repr(C)] structs are layout-portable, so they compile correctly for
// wasm32. Drop the layout assertions, which would otherwise hardcode the
// host sizes and fail when recompiled for wasm.
let host = env::var("HOST").expect("HOST is not set");
builder = builder
.clang_arg(format!("--target={host}"))
.layout_tests(false);
}

let bindings = builder
.generate()
.map_err(|_| "Unable to generate rbs bindings")?;

Expand Down
Loading