From 5fffea969a50a6af6432c652e39f76a56b9a0233 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Tue, 3 May 2022 19:11:56 +0100 Subject: [PATCH] Provide a way for non-wasm Rust code to find the WASI libc --- Cargo.lock | 5 ++++ compiler/build/Cargo.toml | 3 ++- compiler/build/src/link.rs | 10 ++++---- wasi-libc-sys/.gitignore | 1 + wasi-libc-sys/build.rs | 51 +++++++++++++++++++++----------------- wasi-libc-sys/src/lib.rs | 12 +++++++-- 6 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 wasi-libc-sys/.gitignore diff --git a/Cargo.lock b/Cargo.lock index 00722d3728..7764e98cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3428,6 +3428,7 @@ dependencies = [ "serde_json", "target-lexicon", "tempfile", + "wasi_libc_sys", ] [[package]] @@ -4893,6 +4894,10 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi_libc_sys" +version = "0.1.0" + [[package]] name = "wasm-bindgen" version = "0.2.79" diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index 218f783af5..cf2f32d230 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -31,6 +31,7 @@ libloading = "0.7.1" tempfile = "3.2.0" inkwell = { path = "../../vendor/inkwell", optional = true } target-lexicon = "0.12.3" +wasi_libc_sys = { path = "../../wasi-libc-sys", optional = true } [target.'cfg(target_os = "macos")'.dependencies] serde_json = "1.0.69" @@ -40,7 +41,7 @@ target-arm = [] target-aarch64 = ["roc_gen_dev/target-aarch64"] target-x86 = [] target-x86_64 = ["roc_gen_dev/target-x86_64"] -target-wasm32 = ["roc_gen_wasm"] +target-wasm32 = ["roc_gen_wasm", "wasi_libc_sys"] # This is a separate feature because when we generate docs on Netlify, # it doesn't have LLVM installed. (Also, it doesn't need to do code gen.) diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index bcee333e6e..02d5ee7208 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -72,12 +72,12 @@ fn find_zig_str_path() -> PathBuf { } fn find_wasi_libc_path() -> PathBuf { + use wasi_libc_sys::WASI_LIBC_PATH; + // Environment variable defined in wasi-libc-sys/build.rs - if let Ok(wasi_libc_path) = std::env::var("WASI_LIBC_SYS_PATH") { - let wasi_libc_pathbuf = PathBuf::from(&wasi_libc_path); - if std::path::Path::exists(&wasi_libc_pathbuf) { - return wasi_libc_pathbuf; - } + let wasi_libc_pathbuf = PathBuf::from(WASI_LIBC_PATH); + if std::path::Path::exists(&wasi_libc_pathbuf) { + return wasi_libc_pathbuf; } panic!("cannot find `wasi-libc.a`") diff --git a/wasi-libc-sys/.gitignore b/wasi-libc-sys/.gitignore new file mode 100644 index 0000000000..9802e8fdbf --- /dev/null +++ b/wasi-libc-sys/.gitignore @@ -0,0 +1 @@ +src/generated.rs diff --git a/wasi-libc-sys/build.rs b/wasi-libc-sys/build.rs index b4d00f815d..0b318afa43 100644 --- a/wasi-libc-sys/build.rs +++ b/wasi-libc-sys/build.rs @@ -1,8 +1,8 @@ use std::env; +use std::ffi::OsStr; use std::fs; - -// Environment variable that can be used from other build scripts -const WASI_LIBC_SYS_PATH: &str = "WASI_LIBC_SYS_PATH"; +use std::path::Path; +use std::process::Command; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -11,31 +11,36 @@ fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let zig_cache_dir = format!("{}/zig-cache", out_dir); let out_file = format!("{}/wasi-libc.a", out_dir); - println!("cargo:rustc-env={}={}", WASI_LIBC_SYS_PATH, &out_file); - - // Compile a dummy C program with Zig, putting libc into our own private cache directory - let args = [ - "build-exe", - "-target", - "wasm32-wasi", - "-lc", - "-O", - "ReleaseSmall", - "--global-cache-dir", - &zig_cache_dir, - "src/dummy.c", - &format!("-femit-bin={}/dummy.wasm", out_dir), - ]; + // Compile a dummy C program with Zig, with our own private cache directory let zig = zig_executable(); + run_command( + Path::new("."), + &zig, + [ + "build-exe", + "-target", + "wasm32-wasi", + "-lc", + "-O", + "ReleaseSmall", + "--global-cache-dir", + &zig_cache_dir, + "src/dummy.c", + &format!("-femit-bin={}/dummy.wasm", out_dir), + ], + ); - // println!("{} {}", zig, args.join(" ")); + // Find the libc.a file that Zig wrote (as a side-effect of compiling the dummy program) + let find_cmd_output = run_command(Path::new("."), "find", [&zig_cache_dir, "-name", "libc.a"]); + let zig_libc_path = find_cmd_output.trim(); // get rid of a newline - run_command(Path::new("."), &zig, args); - let zig_libc_path = run_command(Path::new("."), "find", [&zig_cache_dir, "-name", "libc.a"]); + // Copy libc to where Cargo expects it + fs::copy(&zig_libc_path, &out_file).unwrap(); - // Copy libc out of Zig's cache, to where Cargo expects it - fs::copy(&zig_libc_path, &out_file); + // Generate some Rust code to indicate where the file is + let generated_rust = format!("pub const WASI_LIBC_PATH: &str = \"{}\";\n", out_file); + fs::write("src/generated.rs", generated_rust).unwrap(); } fn zig_executable() -> String { diff --git a/wasi-libc-sys/src/lib.rs b/wasi-libc-sys/src/lib.rs index 5fcf78879c..566b052ce1 100644 --- a/wasi-libc-sys/src/lib.rs +++ b/wasi-libc-sys/src/lib.rs @@ -1,7 +1,8 @@ -use core::ffi::c_void; - // Rust's libc crate doesn't support Wasm, so we provide an implementation from Zig // We define Rust signatures here as we need them, rather than trying to cover all of libc +#[cfg(target_family = "wasm")] +use core::ffi::c_void; +#[cfg(target_family = "wasm")] extern "C" { pub fn malloc(size: usize) -> *mut c_void; pub fn free(p: *mut c_void); @@ -9,3 +10,10 @@ extern "C" { pub fn memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void; pub fn memset(dst: *mut c_void, ch: i32, n: usize) -> *mut c_void; } + +// If a non-Wasm target is using this crate, we assume it is a build script that wants to emit Wasm +// Tell it where to find the Wasm .a file +#[cfg(not(target_family = "wasm"))] +mod generated; +#[cfg(not(target_family = "wasm"))] +pub use generated::WASI_LIBC_PATH;