extern crate toml;
use std::{path::{Path, PathBuf}, 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<File>,
line: u32,
}
impl<'a> PlatformConfParser<'a>
{
fn read_conf(path: &'a Path) -> Result<(Vec<String>, Vec<String>), 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<RetType, String>
{
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"
{
return Ok(RetType::None);
}
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<String>, Vec<String>), String>
{
let mut features: Vec<String> = Vec::new();
let mut envi: Vec<String> = 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;
if n == 0
{
break;
}
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));
}
}
fn main()
{
#[cfg(target_pointer_width = "64")]
println!("cargo:rustc-cfg=feature=\"__LP64\"");
pub const PAGE_SHIFT: u32 = 12;
pub const PAGE_SIZE: u32 = 1 << PAGE_SHIFT;
println!("cargo:rustc-cfg=feature=\"PG_{}\"", PAGE_SIZE);
if PAGE_SIZE <= 8192
{
println!("cargo:rustc-cfg=feature=\"PG_LESSEQ8192\"");
}
#[cfg(
any(
all(feature = "KLD_MODULE", not(feature = "KLD_TIED")),
feature = "WITNESS",
feature = "INVARIANTS",
feature = "LOCK_PROFILING",
feature = "KTR"
)
)]
println!("cargo:rustc-cfg=feature=\"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::<Value>().unwrap();
let cargo_features: HashSet<String>;
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=feature=\"KERNEL\"");
for f in features
{
println!("cargo:rustc-cfg=feature=\"{}\"", f);
}
for e in envi
{
println!("cargo:rustc-env={}", e);
}
}
else
{
let mut features: Vec<String> = 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(" "));
}
}