diff --git a/samples/philosophers/build.rs b/samples/philosophers/build.rs index 22233f15..0e4ea67c 100644 --- a/samples/philosophers/build.rs +++ b/samples/philosophers/build.rs @@ -5,5 +5,5 @@ // zephyr-build must be a build dependency. fn main() { - zephyr_build::export_bool_kconfig(); + zephyr_build::export_kconfig_bool_options(); } diff --git a/zephyr-build/Cargo.toml b/zephyr-build/Cargo.toml index a31eaa8e..22498955 100644 --- a/zephyr-build/Cargo.toml +++ b/zephyr-build/Cargo.toml @@ -14,4 +14,5 @@ Provides utilities for accessing Kconfig and devicetree information. # Whether these need to be vendored is an open question. They are not # used by the core Zephyr tree, but are needed by zephyr applications. [dependencies] +anyhow = "1.0.93" regex = "1.10.3" diff --git a/zephyr-build/src/lib.rs b/zephyr-build/src/lib.rs index bec71347..582ad68f 100644 --- a/zephyr-build/src/lib.rs +++ b/zephyr-build/src/lib.rs @@ -11,31 +11,43 @@ // This builds a program that is run on the compilation host before the code is compiled. It can // output configuration settings that affect the compilation. -use std::io::{BufRead, BufReader, Write}; +use std::collections::HashSet; use std::env; use std::fs::File; +use std::io::{BufRead, BufReader, Write}; use std::path::Path; use regex::Regex; +/// Extract boolean Kconfig entries. This must happen in any crate that wishes to access the +/// configuration settings. +pub fn extract_kconfig_bool_options(path: &str) -> anyhow::Result> { + let config_y = Regex::new(r"^(CONFIG_.*)=y$").expect("hardcoded regex is always valid"); + + let input = File::open(path)?; + let flags: HashSet = BufReader::new(input) + .lines() + // If the line is an Err(_), just ignore it. + .map(|x| x.unwrap_or_default()) + .filter(|line| config_y.is_match(line)) + .collect(); + + Ok(flags) +} + /// Export boolean Kconfig entries. This must happen in any crate that wishes to access the /// configuration settings. -pub fn export_bool_kconfig() { +pub fn export_kconfig_bool_options() { let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); // Ensure the build script is rerun when the dotconfig changes. println!("cargo:rerun-if-env-changed=DOTCONFIG"); println!("cargo-rerun-if-changed={}", dotconfig); - let config_y = Regex::new(r"^(CONFIG_.*)=y$").unwrap(); - - let file = File::open(&dotconfig).expect("Unable to open dotconfig"); - for line in BufReader::new(file).lines() { - let line = line.expect("reading line from dotconfig"); - if let Some(caps) = config_y.captures(&line) { - println!("cargo:rustc-cfg={}", &caps[1]); - } - } + extract_kconfig_bool_options(&dotconfig) + .expect("failed to extract flags from .config") + .iter() + .for_each(|flag| println!("cargo:rustc-cfg={flag}")); } /// Capture bool, numeric and string kconfig values in a 'kconfig' module. @@ -60,16 +72,18 @@ pub fn build_kconfig_mod() { let line = line.expect("reading line from dotconfig"); if let Some(caps) = config_hex.captures(&line) { writeln!(&mut f, "#[allow(dead_code)]").unwrap(); - writeln!(&mut f, "pub const {}: usize = {};", - &caps[1], &caps[2]).unwrap(); + writeln!(&mut f, "pub const {}: usize = {};", &caps[1], &caps[2]).unwrap(); } else if let Some(caps) = config_int.captures(&line) { writeln!(&mut f, "#[allow(dead_code)]").unwrap(); - writeln!(&mut f, "pub const {}: isize = {};", - &caps[1], &caps[2]).unwrap(); + writeln!(&mut f, "pub const {}: isize = {};", &caps[1], &caps[2]).unwrap(); } else if let Some(caps) = config_str.captures(&line) { writeln!(&mut f, "#[allow(dead_code)]").unwrap(); - writeln!(&mut f, "pub const {}: &'static str = {};", - &caps[1], &caps[2]).unwrap(); + writeln!( + &mut f, + "pub const {}: &'static str = {};", + &caps[1], &caps[2] + ) + .unwrap(); } } } diff --git a/zephyr-sys/Cargo.toml b/zephyr-sys/Cargo.toml index 385dcd5f..81f4a8f7 100644 --- a/zephyr-sys/Cargo.toml +++ b/zephyr-sys/Cargo.toml @@ -16,3 +16,4 @@ Zephyr low-level API bindings. anyhow = "1.0" bindgen = { version = "0.69.4", features = ["experimental"] } # zephyr-build = { version = "3.7.0", path = "../zephyr-build" } +zephyr-build = { version = "3.7.0", path = "../zephyr-build" } diff --git a/zephyr-sys/build.rs b/zephyr-sys/build.rs index 5d43b42d..89745bfc 100644 --- a/zephyr-sys/build.rs +++ b/zephyr-sys/build.rs @@ -11,14 +11,12 @@ // This builds a program that is run on the compilation host before the code is compiled. It can // output configuration settings that affect the compilation. -use anyhow::Result; - -use bindgen::Builder; - use std::env; use std::path::{Path, PathBuf}; -fn main() -> Result<()> { +use bindgen::Builder; + +fn main() -> anyhow::Result<()> { // Determine which version of Clang we linked with. let version = bindgen::clang_version(); println!("Clang version: {:?}", version); @@ -50,36 +48,60 @@ fn main() -> Result<()> { // println!("includes: {:?}", env::var("INCLUDE_DIRS")); // println!("defines: {:?}", env::var("INCLUDE_DEFINES")); - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let wrapper_path = PathBuf::from(env::var("WRAPPER_FILE").unwrap()); + let out_path = PathBuf::from(env::var("OUT_DIR").expect("missing output directory")); + let wrapper_path = PathBuf::from(env::var("WRAPPER_FILE").expect("missing wrapper file")); // Bindgen everything. let bindings = Builder::default() - .header(Path::new("wrapper.h").canonicalize().unwrap().to_str().unwrap()) - .use_core() - .clang_arg(&target_arg); + .clang_arg("-DRUST_BINDGEN") + .clang_arg(format!("-I{}/lib/libc/minimal/include", zephyr_base)) + .clang_arg(&target_arg) + .header( + Path::new("wrapper.h") + .canonicalize() + .unwrap() + .to_str() + .unwrap(), + ) + .use_core(); + let bindings = define_args(bindings, "-I", "INCLUDE_DIRS"); let bindings = define_args(bindings, "-D", "INCLUDE_DEFINES"); + let bindings = bindings .wrap_static_fns(true) - .wrap_static_fns_path(wrapper_path) - // seems to come from somewhere mysterious in Zephyr. For us, just pull in the - // one from the minimal libc. - .clang_arg("-DRUST_BINDGEN") - .clang_arg(format!("-I{}/lib/libc/minimal/include", zephyr_base)) - .derive_copy(false) - .allowlist_function("k_.*") - .allowlist_function("gpio_.*") - .allowlist_function("sys_.*") - .allowlist_function("z_log.*") - .allowlist_function("bt_.*") + .wrap_static_fns_path(wrapper_path); + + let bindings = bindings.derive_copy(false); + + let bindings = bindings + // Deprecated + .blocklist_function("sys_clock_timeout_end_calc") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); + + let dotconfig = env::var("DOTCONFIG").expect("missing DOTCONFIG path"); + let options = zephyr_build::extract_kconfig_bool_options(&dotconfig) + .expect("failed to extract kconfig boolean options"); + + let bindings = bindings + // Kernel .allowlist_item("E.*") .allowlist_item("K_.*") + .allowlist_item("LOG_.*") + .allowlist_item("Z_.*") .allowlist_item("ZR_.*") - .allowlist_item("LOG_LEVEL_.*") - // Deprecated - .blocklist_function("sys_clock_timeout_end_calc") - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .allowlist_function("k_.*") + .allowlist_function("z_log.*") + // Bluetooth + .allowlist_item_if("CONFIG_BT_.*", || options.contains("CONFIG_BT")) + .allowlist_function_if("bt_.*", || options.contains("CONFIG_BT")) + // GPIO + .allowlist_item_if("CONFIG_GPIO_.*", || options.contains("CONFIG_GPIO")) + .allowlist_function_if("gpio_.*", || options.contains("CONFIG_GPIO")) + // UART + .allowlist_item_if("CONFIG_UART_.*", || options.contains("CONFIG_SERIAL")) + .allowlist_function_if("uart_.*", || options.contains("CONFIG_SERIAL")) + // Generate .generate() .expect("Unable to generate bindings"); @@ -90,8 +112,44 @@ fn main() -> Result<()> { Ok(()) } +trait BuilderExt { + type B; + + fn allowlist_function_if

(self, pattern: &str, pred: P) -> Self::B + where + P: FnOnce() -> bool; + + fn allowlist_item_if

(self, pattern: &str, pred: P) -> Self::B + where + P: FnOnce() -> bool; +} + +impl BuilderExt for Builder { + type B = Builder; + + fn allowlist_function_if

(self, pattern: &str, pred: P) -> Self::B + where + P: FnOnce() -> bool, + { + if pred() { + return self.allowlist_function(pattern); + } + self + } + + fn allowlist_item_if

(self, pattern: &str, pred: P) -> Self::B + where + P: FnOnce() -> bool, + { + if pred() { + return self.allowlist_item(pattern); + } + self + } +} + fn define_args(bindings: Builder, prefix: &str, var_name: &str) -> Builder { - let text = env::var(var_name).unwrap(); + let text = env::var(var_name).expect("missing environment variable"); let mut bindings = bindings; for entry in text.split(" ") { if entry.is_empty() { diff --git a/zephyr-sys/wrapper.h b/zephyr-sys/wrapper.h index 8c3ca158..fb04c5d2 100644 --- a/zephyr-sys/wrapper.h +++ b/zephyr-sys/wrapper.h @@ -32,9 +32,12 @@ extern int errno; #include #include + #include -#include +#include + #include +#include /* * bindgen will output #defined constant that resolve to simple numbers. There are some symbols diff --git a/zephyr/build.rs b/zephyr/build.rs index f4345e95..34ee38b5 100644 --- a/zephyr/build.rs +++ b/zephyr/build.rs @@ -12,6 +12,6 @@ // output configuration settings that affect the compilation. fn main() { - zephyr_build::export_bool_kconfig(); + zephyr_build::export_kconfig_bool_options(); zephyr_build::build_kconfig_mod(); }