use std::path::{Path, PathBuf};
use std::{env, fs};
const EMSCRIPTEN_ARCH: &str = "vendored/emscripten/system/lib/libc_musl_arch_emscripten";
const MUSL_ARCH_SHIM: &str = "src/wasm_ffi/c/musl_arch";
fn parse_dir<T: AsRef<Path>>(path: T, sources: &mut Vec<PathBuf>, ext: &str) {
let Ok(dirs) = fs::read_dir(path) else {
return;
};
for dir in dirs {
let dir = dir.unwrap();
let path = dir.path();
if path.is_file()
&& let Some(extension) = path.extension()
&& extension == ext
{
sources.push(path);
}
}
}
fn copy_headers(src_dir: &str, dest_dir: &Path) {
let Ok(entries) = fs::read_dir(src_dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "h")
&& let Some(name) = path.file_name()
{
fs::copy(&path, dest_dir.join(name)).ok();
}
}
}
pub fn includes() -> [PathBuf; 3] {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let generated_include = out_dir.join("include");
[
generated_include,
"vendored/musl/include".into(),
"vendored/musl/src/include".into(),
]
}
fn musl_includes() -> Vec<PathBuf> {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
vec![
out_dir.join("include"),
MUSL_ARCH_SHIM.into(),
"vendored/musl/arch/generic".into(),
"vendored/musl/src/include".into(),
"vendored/musl/src/internal".into(),
"vendored/musl/include".into(),
EMSCRIPTEN_ARCH.into(),
"src/wasm_ffi/c".into(),
]
}
const MUSL_WHOLESALE: &[&str] = &["string", "ctype", "math", "multibyte"];
const MUSL_FILES: &[&str] = &[
"stdlib/atof.c",
"stdlib/atoi.c",
"stdlib/strtod.c",
"stdlib/strtol.c",
"stdlib/wcstod.c",
"stdlib/wcstol.c",
"exit/atexit.c", "misc/basename.c",
"misc/dirname.c",
"locale/strtod_l.c",
"fenv/fenv.c",
"fenv/fesetround.c",
"stdio/sscanf.c",
"stdio/vsscanf.c",
"stdio/vfscanf.c",
"stdio/__uflow.c",
"stdio/__toread.c",
"internal/intscan.c",
"internal/floatscan.c",
"internal/shgetc.c",
];
pub fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let bits_dir = out_dir.join("include").join("bits");
fs::create_dir_all(&bits_dir).expect("failed to create bits dir");
copy_headers("vendored/musl/arch/generic/bits", &bits_dir);
let emsc_bits = format!("{EMSCRIPTEN_ARCH}/bits");
if let Ok(entries) = fs::read_dir(&emsc_bits) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap().to_str().unwrap_or_default();
if name == "errno.h" {
continue;
}
if path.extension().is_some_and(|ext| ext == "h") {
fs::copy(&path, bits_dir.join(name)).ok();
}
}
}
let mut sources: Vec<PathBuf> = vec![];
for dir in MUSL_WHOLESALE {
parse_dir(format!("vendored/musl/src/{dir}"), &mut sources, "c");
}
for f in MUSL_FILES {
sources.push(format!("vendored/musl/src/{f}").into());
}
let mut build = cc::Build::new();
build
.includes(musl_includes())
.flag("-D_XOPEN_SOURCE=700")
.flag("-fno-builtin")
.flag("-DNDEBUG")
.flag_if_supported("-Wno-macro-redefined")
.flag_if_supported("-w")
.std("c17")
.file("src/wasm_ffi/c/errno.c")
.file("src/wasm_ffi/c/nanoprintf.c")
.file("src/wasm_ffi/c/stdio_shim.c")
.file("src/wasm_ffi/c/wasm_libc_shim.c")
.files(sources)
.compile("wasm32-libc");
}