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