cxx_build/
cargo.rs

1use crate::gen::{CfgEvaluator, CfgResult};
2use std::borrow::Borrow;
3use std::cmp::Ordering;
4use std::collections::{BTreeMap as Map, BTreeSet as Set};
5use std::env;
6use std::ptr;
7use std::sync::OnceLock;
8
9static ENV: OnceLock<CargoEnv> = OnceLock::new();
10
11struct CargoEnv {
12    features: Set<Name>,
13    cfgs: Map<Name, String>,
14}
15
16pub(super) struct CargoEnvCfgEvaluator;
17
18impl CfgEvaluator for CargoEnvCfgEvaluator {
19    fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult {
20        let env = ENV.get_or_init(CargoEnv::load);
21        if name == "feature" {
22            return if let Some(query_value) = query_value {
23                CfgResult::from(env.features.contains(Lookup::new(query_value)))
24            } else {
25                let msg = "expected `feature = \"...\"`".to_owned();
26                CfgResult::Undetermined { msg }
27            };
28        }
29        if name == "test" && query_value.is_none() {
30            let msg = "cfg(test) is not supported because Cargo runs your build script only once across the lib and test build of the same crate".to_owned();
31            return CfgResult::Undetermined { msg };
32        }
33        if let Some(cargo_value) = env.cfgs.get(Lookup::new(name)) {
34            return if let Some(query_value) = query_value {
35                CfgResult::from(cargo_value.split(',').any(|value| value == query_value))
36            } else {
37                CfgResult::True
38            };
39        }
40        if name == "debug_assertions" && query_value.is_none() {
41            return CfgResult::from(truecfg!(debug_assertions));
42        }
43        CfgResult::False
44    }
45}
46
47impl CargoEnv {
48    fn load() -> Self {
49        const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
50        const CARGO_CFG_PREFIX: &str = "CARGO_CFG_";
51
52        let mut features = Set::new();
53        let mut cfgs = Map::new();
54        for (k, v) in env::vars_os() {
55            let Some(k) = k.to_str() else {
56                continue;
57            };
58            let Ok(v) = v.into_string() else {
59                continue;
60            };
61            if let Some(feature_name) = k.strip_prefix(CARGO_FEATURE_PREFIX) {
62                let feature_name = Name(feature_name.to_owned());
63                features.insert(feature_name);
64            } else if let Some(cfg_name) = k.strip_prefix(CARGO_CFG_PREFIX) {
65                let cfg_name = Name(cfg_name.to_owned());
66                cfgs.insert(cfg_name, v);
67            }
68        }
69        CargoEnv { features, cfgs }
70    }
71}
72
73struct Name(String);
74
75impl Ord for Name {
76    fn cmp(&self, rhs: &Self) -> Ordering {
77        Lookup::new(&self.0).cmp(Lookup::new(&rhs.0))
78    }
79}
80
81impl PartialOrd for Name {
82    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
83        Some(self.cmp(rhs))
84    }
85}
86
87impl Eq for Name {}
88
89impl PartialEq for Name {
90    fn eq(&self, rhs: &Self) -> bool {
91        Lookup::new(&self.0).eq(Lookup::new(&rhs.0))
92    }
93}
94
95#[repr(transparent)]
96struct Lookup(str);
97
98impl Lookup {
99    fn new(name: &str) -> &Self {
100        unsafe { &*(ptr::from_ref::<str>(name) as *const Self) }
101    }
102}
103
104impl Borrow<Lookup> for Name {
105    fn borrow(&self) -> &Lookup {
106        Lookup::new(&self.0)
107    }
108}
109
110impl Ord for Lookup {
111    fn cmp(&self, rhs: &Self) -> Ordering {
112        self.0
113            .bytes()
114            .map(CaseAgnosticByte)
115            .cmp(rhs.0.bytes().map(CaseAgnosticByte))
116    }
117}
118
119impl PartialOrd for Lookup {
120    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
121        Some(self.cmp(rhs))
122    }
123}
124
125impl Eq for Lookup {}
126
127impl PartialEq for Lookup {
128    fn eq(&self, rhs: &Self) -> bool {
129        self.0
130            .bytes()
131            .map(CaseAgnosticByte)
132            .eq(rhs.0.bytes().map(CaseAgnosticByte))
133    }
134}
135
136struct CaseAgnosticByte(u8);
137
138impl Ord for CaseAgnosticByte {
139    fn cmp(&self, rhs: &Self) -> Ordering {
140        self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase())
141    }
142}
143
144impl PartialOrd for CaseAgnosticByte {
145    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
146        Some(self.cmp(rhs))
147    }
148}
149
150impl Eq for CaseAgnosticByte {}
151
152impl PartialEq for CaseAgnosticByte {
153    fn eq(&self, rhs: &Self) -> bool {
154        self.cmp(rhs) == Ordering::Equal
155    }
156}