diff --git a/stm32-bindings-gen/src/lib.rs b/stm32-bindings-gen/src/lib.rs index 12426f7..5f7fe07 100644 --- a/stm32-bindings-gen/src/lib.rs +++ b/stm32-bindings-gen/src/lib.rs @@ -1,10 +1,25 @@ +use bindgen::callbacks::{ItemInfo, ItemKind, ParseCallbacks}; use std::io::Write; use std::{fs, path::PathBuf}; use tempfile::NamedTempFile; +#[derive(Debug)] +struct UppercaseCallbacks; + +impl ParseCallbacks for UppercaseCallbacks { + fn item_name(&self, item: ItemInfo<'_>) -> Option { + if matches!(item.kind, ItemKind::Var) { + Some(item.name.to_ascii_uppercase()) + } else { + None + } + } +} + pub struct Options { pub out_dir: PathBuf, pub sources_dir: PathBuf, + pub target_triple: String, } pub struct Gen { @@ -34,11 +49,25 @@ impl Gen { // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. - let bindings = bindgen::Builder::default() - .clang_arg(format!( - "-I{}/Middlewares/ST/STM32_WPAN/mac_802_15_4/core/inc", - self.opts.sources_dir.to_str().unwrap() - )) + let target_flag = format!("--target={}", self.opts.target_triple); + let include_arg = format!( + "-I{}/Middlewares/ST/STM32_WPAN/mac_802_15_4/core/inc", + self.opts.sources_dir.to_str().unwrap() + ); + let mut builder = bindgen::Builder::default() + .parse_callbacks(Box::new(UppercaseCallbacks)) + // Force Clang to use the same layout as the selected target. + .clang_arg(&target_flag) + .clang_arg(&include_arg); + if self + .opts + .target_triple + .to_ascii_lowercase() + .starts_with("thumb") + { + builder = builder.clang_arg("-mthumb"); + } + let bindings = builder // The input header we would like to generate // bindings for. .header("stm32-bindings-gen/inc/wpan-wba.h") @@ -53,12 +82,30 @@ impl Gen { .write_to_file(&out_path) .expect("Couldn't write bindings!"); - let file_contents = fs::read_to_string(&out_path).unwrap(); - let file_contents = file_contents + let mut file_contents = fs::read_to_string(&out_path).unwrap(); + file_contents = file_contents .replace("::std::mem::", "::core::mem::") .replace("::std::os::raw::", "::core::ffi::") .replace("::std::option::", "::core::option::"); + file_contents = file_contents + .lines() + .map(|line| { + if let Some(rest) = line.strip_prefix("pub const ") { + if let Some((name, tail)) = rest.split_once(':') { + let upper = name.trim().to_ascii_uppercase(); + return format!("pub const {}:{}", upper, tail); + } + } + line.to_owned() + }) + .collect::>() + .join("\n"); + + if !file_contents.ends_with('\n') { + file_contents.push('\n'); + } + fs::write(&out_path, file_contents).unwrap(); // copy misc files diff --git a/stm32-bindings-gen/src/main.rs b/stm32-bindings-gen/src/main.rs index 140cdf2..d019910 100644 --- a/stm32-bindings-gen/src/main.rs +++ b/stm32-bindings-gen/src/main.rs @@ -1,16 +1,70 @@ -use std::path::PathBuf; +use std::{env, path::PathBuf, process}; use stm32_bindings_gen::{Gen, Options}; fn main() { let out_dir = PathBuf::from("build/stm32-bindings"); let sources_dir = PathBuf::from("sources"); - - // let args: Vec = args().collect(); + let target_triple = resolve_target_triple(); let opts = Options { out_dir, sources_dir, + target_triple, }; Gen::new(opts).run_gen(); } + +fn resolve_target_triple() -> String { + let mut args = env::args().skip(1); + let mut positional: Option = None; + + while let Some(arg) = args.next() { + match arg.as_str() { + "--help" | "-h" => { + eprintln!("Usage: stm32-bindings-gen [--target ] [triple]"); + process::exit(0); + } + "--target" => { + let value = args.next().unwrap_or_else(|| { + eprintln!("Expected a value after --target"); + process::exit(1); + }); + let trimmed = value.trim(); + if trimmed.is_empty() { + eprintln!("Target triple cannot be empty."); + process::exit(1); + } + return trimmed.to_string(); + } + _ => { + if let Some(value) = arg.strip_prefix("--target=") { + let trimmed = value.trim(); + if trimmed.is_empty() { + eprintln!("Target triple cannot be empty."); + process::exit(1); + } + return trimmed.to_string(); + } + if positional.is_none() { + let trimmed = arg.trim(); + if !trimmed.is_empty() { + positional = Some(trimmed.to_string()); + } + } + } + } + } + + positional + .or_else(|| env::var("BINDGEN_TARGET").ok()) + .and_then(|s| { + let trimmed = s.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } + }) + .unwrap_or_else(|| "thumbv8m.main-none-eabihf".to_string()) +}