extern crate toml; use std::{path::{Path, PathBuf}, str::Chars, iter::Peekable, env, fs::{OpenOptions, File}, io::{BufReader, BufRead}, collections::HashSet}; use std::io::Read; use toml::Value; macro_rules! msg { ($($tokens: tt)*) => { println!("cargo:warning={}", format!($($tokens)*)) } } enum RetType { Feature(String), Environment(String), None } pub struct PlatformConfParser<'a> { path: &'a Path, conf_file: BufReader, line: u32, } impl<'a> PlatformConfParser<'a> { fn read_conf(path: &'a Path) -> Result<(Vec, Vec), String> { let conf_file = OpenOptions::new() .append(false) .create(false) .read(true) .write(false) .truncate(false) .open(path) .map_err(|e| e.to_string())?; let mut pcp = Self{ path: path, conf_file: BufReader::new(conf_file), line: 0 }; return pcp.parse_conf(); } fn read_string(buf: String) -> Result { let mut p = buf.chars().peekable(); let mut left: String = String::with_capacity(15); while let Some(c) = p.next() { if c.is_ascii_control() == true { break; } else if c.is_ascii_alphanumeric() == true { left.push(c); } } if left.as_str() == "makeoptions" || left.as_str() == "cpu" || left.as_str() == "ident" { // skip return Ok(RetType::None); } // skip control while let Some(c) = p.peek() { if c.is_ascii_control() == true { p.next(); } else if c.is_ascii_alphanumeric() == true || c.is_ascii_graphic() == true { break; } } let mut right: String = String::with_capacity(15); while let Some(c) = p.next() { if c.is_ascii_control() == true || c == '#' { break; } else if c.is_ascii_alphanumeric() == true || c.is_ascii_graphic() == true { right.push(c); } } if right.is_empty() == true { return Err(format!("empty right value, left: {}", left)); } if right.contains("=") == true { return Ok(RetType::Environment(right)); } else { return Ok(RetType::Feature(right)); } } fn parse_conf(&mut self) -> Result<(Vec, Vec), String> { let mut features: Vec = Vec::new(); let mut envi: Vec = Vec::new(); loop { let mut buf = String::new(); let n = self.conf_file .read_line(&mut buf) .map_err(|e| e.to_string())?; self.line += 1; // reached EOF if n == 0 { break; } // skip comments if buf.starts_with("#") == true { continue; } else if buf.starts_with("\n") == true { continue; } match Self::read_string(buf) { Ok(RetType::Feature(r)) => { features.push(r); }, Ok(RetType::Environment(r)) => { envi.push(r); }, Ok(RetType::None) => {}, Err(e) => { msg!("path: '{}', line: '{}', error: {}", self.path.display(), self.line, e); } } } return Ok((features, envi)); } } //#[path = "src/building.rs"] //mod building; /* mod building { include!("src/building.rs"); } */ fn main() { // don't forget to change same fields in src/machine/{arch}/param.rs // bullsh@t called "conditional compilation" in Rust s@cks a lot. pub const PAGE_SHIFT: u32 = 12; pub const PAGE_SIZE: u32 = 1 << PAGE_SHIFT; println!("cargo:rustc-cfg=PG_{}", PAGE_SIZE); #[cfg( any( all(feature = "KLD_MODULE", not(feature = "KLD_TIED")), feature = "WITNESS", feature = "INVARIANTS", feature = "LOCK_PROFILING", feature = "KTR" ) )] println!("cargo:rustc-cfg=LOCK_DEBUG"); let read_cfg = env::var_os("CARGO_FEATURE_BUILD_CONFIG_KERNEL"); if read_cfg.is_some() { let mut cargo_cur_dir = env::current_dir().unwrap(); cargo_cur_dir.push("Cargo.toml"); let mut carg_file = OpenOptions::new().read(true).open(cargo_cur_dir.as_path()).unwrap(); let mut cargo_file_txt = String::new(); carg_file.read_to_string(&mut cargo_file_txt).unwrap(); let t = cargo_file_txt.parse::().unwrap(); let cargo_features: HashSet; match t { Value::Table(t) => { let features = t.get("features").unwrap(); match features { Value::Table(arr) => { cargo_features = arr.keys().map(|s| s.clone()).collect(); }, _ => { panic!("expexted table!") } } }, _ => { panic!("exp tbl"); } } let cfg_path = env::var("KPI_PLATFORM_CFG_PATH") .map_or(PathBuf::from("/usr/src/sys/amd64/conf/GENERIC"), |d| PathBuf::from(d)); msg!("CONF: {}", cfg_path.display()); let (mut features, envi) = match PlatformConfParser::read_conf(cfg_path.as_path()) { Ok(r) => r, Err(e) => { panic!("Failed parsing: {}, error: {}", cfg_path.display(), e); } }; features.retain(|f| { return cargo_features.contains(f); } ); msg!("Building rust-kpi crate with the following features: \"{}\"", features.as_slice().join("\", \"")); msg!("Building rust-kpi crate with the following environment: {}", envi.as_slice().join(" ")); println!("cargo:rustc-cfg=KERNEL"); for f in features { println!("cargo:rustc-cfg={}", f); } for e in envi { println!("cargo:rustc-env={}", e); } } else { let mut features: Vec = Vec::with_capacity(env::vars().count()); for (name, _value) in env::vars() { if name.starts_with("CARGO_FEATURE_") { features.push(name.clone()); } } msg!("Building with manually provided crate flags: {}", features.join(" ")); } }