Skip to main content

cxx_build/
cargo.rs

1use crate::bridge::{CfgEvaluator, CfgResult};
2use std::borrow::Borrow;
3use std::cmp::Ordering;
4use std::collections::BTreeMap as Map;
5use std::env;
6use std::ptr;
7use std::sync::OnceLock;
8
9static ENV: OnceLock<CargoEnv> = OnceLock::new();
10
11struct CargoEnv {
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            if query_value.is_none() {
22                let msg = "expected `feature = \"...\"`".to_owned();
23                return CfgResult::Undetermined { msg };
24            } else if query_value == Some("") {
25                return CfgResult::False;
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        // Unneeded since Rust 1.93 (https://github.com/rust-lang/cargo/pull/16160)
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        let mut cfgs = Map::new();
50        for (k, v) in env::vars_os() {
51            let Some(k) = k.to_str() else {
52                continue;
53            };
54            let Ok(v) = v.into_string() else {
55                continue;
56            };
57            if let Some(cfg_name) = k.strip_prefix("CARGO_CFG_") {
58                let cfg_name = Name(cfg_name.to_owned());
59                cfgs.insert(cfg_name, v);
60            }
61        }
62        CargoEnv { cfgs }
63    }
64}
65
66struct Name(String);
67
68impl Ord for Name {
69    fn cmp(&self, rhs: &Self) -> Ordering {
70        Lookup::new(&self.0).cmp(Lookup::new(&rhs.0))
71    }
72}
73
74impl PartialOrd for Name {
75    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
76        Some(self.cmp(rhs))
77    }
78}
79
80impl Eq for Name {}
81
82impl PartialEq for Name {
83    fn eq(&self, rhs: &Self) -> bool {
84        Lookup::new(&self.0).eq(Lookup::new(&rhs.0))
85    }
86}
87
88#[repr(transparent)]
89struct Lookup(str);
90
91impl Lookup {
92    fn new(name: &str) -> &Self {
93        unsafe { &*(ptr::from_ref::<str>(name) as *const Self) }
94    }
95}
96
97impl Borrow<Lookup> for Name {
98    fn borrow(&self) -> &Lookup {
99        Lookup::new(&self.0)
100    }
101}
102
103impl Ord for Lookup {
104    fn cmp(&self, rhs: &Self) -> Ordering {
105        self.0
106            .bytes()
107            .map(CaseAgnosticByte)
108            .cmp(rhs.0.bytes().map(CaseAgnosticByte))
109    }
110}
111
112impl PartialOrd for Lookup {
113    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
114        Some(self.cmp(rhs))
115    }
116}
117
118impl Eq for Lookup {}
119
120impl PartialEq for Lookup {
121    fn eq(&self, rhs: &Self) -> bool {
122        self.0
123            .bytes()
124            .map(CaseAgnosticByte)
125            .eq(rhs.0.bytes().map(CaseAgnosticByte))
126    }
127}
128
129struct CaseAgnosticByte(u8);
130
131impl Ord for CaseAgnosticByte {
132    fn cmp(&self, rhs: &Self) -> Ordering {
133        self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase())
134    }
135}
136
137impl PartialOrd for CaseAgnosticByte {
138    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
139        Some(self.cmp(rhs))
140    }
141}
142
143impl Eq for CaseAgnosticByte {}
144
145impl PartialEq for CaseAgnosticByte {
146    fn eq(&self, rhs: &Self) -> bool {
147        self.cmp(rhs) == Ordering::Equal
148    }
149}