diff --git a/Cargo.toml b/Cargo.toml index 787d1ebb2ee..3815f65d43f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ build-time = "0.1" # For esp_app_desc!() const_format = "0.2" # For esp_app_desc!() [build-dependencies] -embuild = { version = "0.31.2", features = ["glob", "kconfig"] } +embuild = { git = "https://github.com/gopakumarce/embuild.git", branch = "gopa-inline_fns", features = ["glob", "kconfig"] } anyhow = "1" regex = "1.5" -bindgen = "0.63" +bindgen = { version = "0.65.1", features = ["experimental"] } cargo_metadata = "0.15" serde = { version = "1.0", features = ["derive"] } strum = { version = "0.24", features = ["derive"] } diff --git a/build/build.rs b/build/build.rs index 4ccf64b3011..0d07d5aaaad 100644 --- a/build/build.rs +++ b/build/build.rs @@ -2,12 +2,17 @@ compile_error!("One of the features `pio` or `native` must be selected."); use std::iter::once; +use crate::native::cargo_driver::chip::Chip; use anyhow::*; use bindgen::callbacks::{IntKind, ParseCallbacks}; use common::*; use embuild::bindgen::BindgenExt; use embuild::utils::OsStrExt; use embuild::{bindgen as bindgen_utils, build, cargo, kconfig, path_buf}; +use std::fs::File; +use std::io::BufReader; +use std::io::{BufRead, Write}; +use std::str::FromStr; mod common; mod config; @@ -48,6 +53,132 @@ impl ParseCallbacks for BindgenCallbacks { } } +const STATIC_INLINE: &str = "static_inlines"; + +fn static_inlines_c() -> String { + format!("{}.c", STATIC_INLINE) +} + +fn static_inlines_o() -> String { + format!("{}.o", STATIC_INLINE) +} + +fn static_inlines_tmp() -> String { + format!("{}_tmp.c", STATIC_INLINE) +} + +fn static_inlines_a() -> String { + format!("lib{}.a", STATIC_INLINE) +} + +// TODO: The symbols from the components/esp_rom//ld are hard coded +// addresses resolved during link time, rust linker cant find those symbols +// and hence the inlines that depend on those dont work. Ignore them for now +const IGNORE_STATIC_INLINES: [&str; 3] = [ + "_xtos_interrupt_enable__extern", + "_xtos_interrupt_disable__extern", + "esp_cpu_intr_get_handler_arg__extern", +]; + +fn strip_quotes(args: &str) -> Vec { + let mut out = vec![]; + for arg in args.split_whitespace() { + let mut chars = arg.chars(); + let first = chars.next(); + chars.next_back(); + let trim = if first == Some('\"') { + chars.as_str() + } else { + arg + }; + out.push(trim.to_string()); + } + out +} + +fn ignore_api(api: &str) -> bool { + for ignore in IGNORE_STATIC_INLINES.iter() { + if api.contains(ignore) { + return true; + } + } + false +} + +fn process_static_inlines( + build_output_args: &str, + clang_args: Vec, + mcu: &str, + headers: Vec, +) -> anyhow::Result<()> { + let chip = Chip::from_str(mcu)?; + let gcc = format!("{}-gcc", chip.gcc_toolchain()); + let ar = format!("{}-gcc-ar", chip.gcc_toolchain()); + + let out_dir_path = cargo::out_dir(); + let file = File::open(out_dir_path.join(static_inlines_c())).unwrap(); + let mut tmp = File::create(out_dir_path.join(static_inlines_tmp())).unwrap(); + let lines = BufReader::new(file).lines(); + for line in lines { + let line = line.unwrap(); + if !ignore_api(&line) { + tmp.write_all(line.as_bytes())?; + writeln!(tmp, "")?; + } + } + tmp.flush()?; + + let mut gcc_cmd = std::process::Command::new(gcc); + let mut gcc_args = gcc_cmd + .arg("-mlongcalls") + .arg("-O") + .arg("-c") + .arg("-o") + .arg(out_dir_path.join(&static_inlines_o())) + .arg(out_dir_path.join(&static_inlines_tmp())); + for hdr in headers.iter() { + gcc_args = gcc_args.arg("-include").arg(hdr); + } + gcc_args = gcc_args.args(strip_quotes(build_output_args)); + gcc_args = gcc_args.args(clang_args); + + let gcc_output = gcc_args.output().unwrap(); + if !gcc_output.status.success() { + panic!( + "Could not compile object file:\n{}", + String::from_utf8_lossy(&gcc_output.stderr) + ); + } + + #[cfg(not(target_os = "windows"))] + let lib_output = std::process::Command::new(ar) + .arg("rcs") + .arg(out_dir_path.join(&static_inlines_a())) + .arg(out_dir_path.join(&static_inlines_o())) + .output() + .unwrap(); + #[cfg(target_os = "windows")] + let lib_output = std::process::Command::new("lib") + .arg(&out_dir_path.join(&static_inlines_o())) + .output() + .unwrap(); + + if !lib_output.status.success() { + panic!( + "Could not emit library file:\n{}", + String::from_utf8_lossy(&lib_output.stderr) + ); + } + + println!( + "cargo:rustc-link-search=native={}", + out_dir_path.to_string_lossy() + ); + println!("cargo:rustc-link-lib=static={}", STATIC_INLINE); + + Ok(()) +} + fn main() -> anyhow::Result<()> { let build_output = build_driver::build()?; @@ -97,9 +228,13 @@ fn main() -> anyhow::Result<()> { // Because we have multiple bindgen invocations and we can't clone a bindgen::Builder, // we have to set the options every time. let configure_bindgen = |bindgen: bindgen::Builder| { + let mut outdir = cargo::out_dir(); + outdir.push(STATIC_INLINE); Ok(bindgen .parse_callbacks(Box::new(BindgenCallbacks)) .use_core() + .wrap_static_fns(true) + .wrap_static_fns_path(outdir) .enable_function_attribute_detection() .clang_arg("-DESP_PLATFORM") .blocklist_function("strtold") @@ -145,7 +280,7 @@ fn main() -> anyhow::Result<()> { ); configure_bindgen(build_output.bindgen.clone().builder()?)? - .headers(headers)? + .headers(headers.clone())? .generate() .with_context(bindgen_err)? .write_to_file(&bindings_file) @@ -185,7 +320,7 @@ fn main() -> anyhow::Result<()> { .into_iter() .chain(EspIdfVersion::parse(bindings_file)?.cfg_args()) .chain(build_output.components.cfg_args()) - .chain(once(mcu)) + .chain(once(mcu.clone())) .collect(), }; cfg_args.propagate(); @@ -211,5 +346,8 @@ fn main() -> anyhow::Result<()> { link_args.propagate(); } + let clang_args: Vec = build_output.components.clang_args().collect(); + process_static_inlines(&build_output.cincl_args.args, clang_args, &mcu, headers)?; + Ok(()) }