diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs new file mode 100644 index 000000000..9d9f548d1 --- /dev/null +++ b/gen/build/src/cargo.rs @@ -0,0 +1,153 @@ +use crate::gen::{CfgEvaluator, CfgResult}; +use once_cell::sync::OnceCell; +use std::borrow::Borrow; +use std::cmp::Ordering; +use std::collections::{BTreeMap as Map, BTreeSet as Set}; +use std::env; + +static ENV: OnceCell = OnceCell::new(); + +struct CargoEnv { + features: Set, + cfgs: Map, +} + +pub(super) struct CargoEnvCfgEvaluator; + +impl CfgEvaluator for CargoEnvCfgEvaluator { + fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult { + let env = ENV.get_or_init(CargoEnv::load); + if name == "feature" { + if let Some(query_value) = query_value { + CfgResult::from(env.features.contains(Lookup::new(query_value))) + } else { + let msg = "expected `feature = \"...\"`".to_owned(); + CfgResult::Undetermined { msg } + } + } else { + match env.cfgs.get(Lookup::new(name)) { + Some(cargo_value) => { + if let Some(query_value) = query_value { + CfgResult::from(cargo_value.split(',').any(|value| value == query_value)) + } else { + CfgResult::True + } + } + None => CfgResult::False, + } + } + } +} + +impl CargoEnv { + fn load() -> Self { + const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_"; + const CARGO_CFG_PREFIX: &str = "CARGO_CFG_"; + + let mut features = Set::new(); + let mut cfgs = Map::new(); + for (k, v) in env::vars_os() { + let k = match k.to_str() { + Some(k) => k, + None => continue, + }; + let v = match v.into_string() { + Ok(v) => v, + Err(_) => continue, + }; + if let Some(feature_name) = k.strip_prefix(CARGO_FEATURE_PREFIX) { + let feature_name = Name(feature_name.to_owned()); + features.insert(feature_name); + } else if let Some(cfg_name) = k.strip_prefix(CARGO_CFG_PREFIX) { + let cfg_name = Name(cfg_name.to_owned()); + cfgs.insert(cfg_name, v); + } + } + CargoEnv { features, cfgs } + } +} + +struct Name(String); + +impl Ord for Name { + fn cmp(&self, rhs: &Self) -> Ordering { + Lookup::new(&self.0).cmp(Lookup::new(&rhs.0)) + } +} + +impl PartialOrd for Name { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +impl Eq for Name {} + +impl PartialEq for Name { + fn eq(&self, rhs: &Self) -> bool { + Lookup::new(&self.0).eq(Lookup::new(&rhs.0)) + } +} + +#[repr(transparent)] +struct Lookup(str); + +impl Lookup { + fn new(name: &str) -> &Self { + unsafe { &*(name as *const str as *const Self) } + } +} + +impl Borrow for Name { + fn borrow(&self) -> &Lookup { + Lookup::new(&self.0) + } +} + +impl Ord for Lookup { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0 + .bytes() + .map(CaseAgnosticByte) + .cmp(rhs.0.bytes().map(CaseAgnosticByte)) + } +} + +impl PartialOrd for Lookup { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +impl Eq for Lookup {} + +impl PartialEq for Lookup { + fn eq(&self, rhs: &Self) -> bool { + self.0 + .bytes() + .map(CaseAgnosticByte) + .eq(rhs.0.bytes().map(CaseAgnosticByte)) + } +} + +struct CaseAgnosticByte(u8); + +impl Ord for CaseAgnosticByte { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase()) + } +} + +impl PartialOrd for CaseAgnosticByte { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +impl Eq for CaseAgnosticByte {} + +impl PartialEq for CaseAgnosticByte { + fn eq(&self, rhs: &Self) -> bool { + self.cmp(rhs) == Ordering::Equal + } +} diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index b8a463c3a..bdb43a159 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -78,6 +78,7 @@ clippy::wrong_self_convention )] +mod cargo; mod cfg; mod deps; mod error; @@ -89,6 +90,7 @@ mod syntax; mod target; mod vec; +use crate::cargo::CargoEnvCfgEvaluator; use crate::deps::{Crate, HeaderDir}; use crate::error::{Error, Result}; use crate::gen::error::report; @@ -393,6 +395,7 @@ fn make_include_dir(prj: &Project) -> Result { fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt { allow_dot_includes: false, + cfg_evaluator: Box::new(CargoEnvCfgEvaluator), ..Opt::default() }; let generated = gen::generate_from_path(rust_source_file, &opt);