Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a cfg evaluator for the cxx-build crate #1000

Merged
merged 1 commit into from Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
153 changes: 153 additions & 0 deletions 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<CargoEnv> = OnceCell::new();

struct CargoEnv {
features: Set<Name>,
cfgs: Map<Name, String>,
}

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<Ordering> {
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<Lookup> 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<Ordering> {
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<Ordering> {
Some(self.cmp(rhs))
}
}

impl Eq for CaseAgnosticByte {}

impl PartialEq for CaseAgnosticByte {
fn eq(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Equal
}
}
3 changes: 3 additions & 0 deletions gen/build/src/lib.rs
Expand Up @@ -78,6 +78,7 @@
clippy::wrong_self_convention
)]

mod cargo;
mod cfg;
mod deps;
mod error;
Expand All @@ -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;
Expand Down Expand Up @@ -393,6 +395,7 @@ fn make_include_dir(prj: &Project) -> Result<PathBuf> {
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);
Expand Down