use std::{ env, fs::{self, DirEntry}, path::{Path, PathBuf}, process::Command, }; const RENAMES: &[(&str, &str)] = &[ ("CUsizeResult", "size_t"), ("CIsizeResult", "ssize_t"), ("CEofResult", "int"), ("CIntCountResult", "int"), ("CIntZeroResult", "int"), ("CFdResult", "int"), ("COffsetResult", "off_t"), ("CPidResult", "pid_t"), ]; fn include_dir(d: &DirEntry) -> bool { d.metadata().map(|m| m.is_dir()).unwrap_or(false) && d.path() .iter() .nth(2) .map_or(false, |c| c.to_str().map_or(false, |x| !x.starts_with("_"))) } fn generate_header(config_path: impl AsRef, header_output: impl AsRef) { let config_path = config_path.as_ref(); let header_output = header_output.as_ref(); let relative_path = config_path .strip_prefix("src/headers") .ok() .and_then(|p| p.parent()) .and_then(|p| p.to_str()) .unwrap() .replace("_", "/"); // TODO use outer workspace's target directory? let header_path = header_output.join(&relative_path).with_extension("h"); let mod_path = config_path.with_file_name("mod.rs"); let mut config = cbindgen::Config::from_file(config_path).unwrap(); config .export .rename .extend(RENAMES.into_iter().map(|&(x, y)| (x.into(), y.into()))); cbindgen::Builder::new() .with_config(config) .with_src(mod_path) .generate() .unwrap() .write_to_file(header_path); } fn compile_crt0(arch: &str, output_dir: impl AsRef) { let output_dir = output_dir.as_ref(); let mut command = Command::new("clang"); command .arg(format!("--target={}-unknown-none", arch)) .arg("-nostdlib") .arg("-nostdinc") .arg("-c") .arg("-o") .arg(output_dir.join("crt0.o")) .arg(format!("crt/{}/crt0.S", arch)); if !command.status().unwrap().success() { panic!("Couldn't compile crt0.S"); } } fn main() { let target = env::var("TARGET").expect("$TARGET is not set"); let profile = env::var("PROFILE").expect("$PROFILE is not set"); let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("$CARGO_CFG_TARGET_ARCH is not set"); let output_dir = PathBuf::from(format!("target/{target}/{profile}")); let header_output = output_dir.join("include"); compile_crt0(&arch, output_dir); fs::read_dir("src/headers") .unwrap() .into_iter() .filter_map(Result::ok) .filter(|d| include_dir(d)) .map(|d| d.path().as_path().join("cbindgen.toml")) .filter(|p| p.exists()) .for_each(|p| { println!("cargo:rerun-if-changed={:?}", p.parent().unwrap()); println!("cargo:rerun-if-changed={:?}", p); println!("cargo:rerun-if-changed={:?}", p.with_file_name("mod.rs")); generate_header(&p, &header_output); }); }