1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | |
5 | use std::fmt; |
6 | use std::io::Write; |
7 | |
8 | use crate::bindgen::cargo::cargo_metadata::Dependency; |
9 | use crate::bindgen::config::{Config, Language}; |
10 | use crate::bindgen::writer::SourceWriter; |
11 | |
12 | #[derive (PartialEq, Eq)] |
13 | enum DefineKey<'a> { |
14 | Boolean(&'a str), |
15 | Named(&'a str, &'a str), |
16 | } |
17 | |
18 | impl<'a> DefineKey<'a> { |
19 | fn load(key: &str) -> DefineKey { |
20 | // TODO: dirty parser |
21 | if !key.contains('=' ) { |
22 | return DefineKey::Boolean(key); |
23 | } |
24 | |
25 | let mut splits = key.trim().split('=' ); |
26 | |
27 | let name = match splits.next() { |
28 | Some(n) => n.trim(), |
29 | None => return DefineKey::Boolean(key), |
30 | }; |
31 | |
32 | let value = match splits.next() { |
33 | Some(v) => v.trim(), |
34 | None => return DefineKey::Boolean(key), |
35 | }; |
36 | |
37 | if splits.next().is_some() { |
38 | return DefineKey::Boolean(key); |
39 | } |
40 | |
41 | DefineKey::Named(name, value) |
42 | } |
43 | } |
44 | |
45 | #[derive (Debug, Clone)] |
46 | pub enum Cfg { |
47 | Boolean(String), |
48 | Named(String, String), |
49 | Any(Vec<Cfg>), |
50 | All(Vec<Cfg>), |
51 | Not(Box<Cfg>), |
52 | } |
53 | |
54 | impl fmt::Display for Cfg { |
55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
56 | match self { |
57 | Cfg::Boolean(key) => write!(f, " {}" , key), |
58 | Cfg::Named(key, value) => write!(f, " {} = {:?}" , key, value), |
59 | Cfg::Any(cfgs) => { |
60 | write!(f, "any(" )?; |
61 | for (index, cfg) in cfgs.iter().enumerate() { |
62 | if index > 0 { |
63 | write!(f, ", " )?; |
64 | } |
65 | write!(f, " {}" , cfg)?; |
66 | } |
67 | write!(f, ")" ) |
68 | } |
69 | Cfg::All(cfgs) => { |
70 | write!(f, "all(" )?; |
71 | for (index, cfg) in cfgs.iter().enumerate() { |
72 | if index > 0 { |
73 | write!(f, ", " )?; |
74 | } |
75 | write!(f, " {}" , cfg)?; |
76 | } |
77 | write!(f, ")" ) |
78 | } |
79 | Cfg::Not(cfg) => write!(f, "not( {})" , cfg), |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl Cfg { |
85 | pub fn join(cfgs: &[Cfg]) -> Option<Cfg> { |
86 | if cfgs.is_empty() { |
87 | None |
88 | } else { |
89 | Some(Cfg::All(cfgs.to_owned())) |
90 | } |
91 | } |
92 | |
93 | pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> { |
94 | match (parent, child) { |
95 | (None, None) => None, |
96 | (None, Some(child)) => Some(child), |
97 | (Some(parent), None) => Some(parent.clone()), |
98 | (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])), |
99 | } |
100 | } |
101 | |
102 | pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> { |
103 | let mut configs = Vec::new(); |
104 | |
105 | for attr in attrs { |
106 | if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() { |
107 | if !path.is_ident("cfg" ) || nested.len() != 1 { |
108 | continue; |
109 | } |
110 | |
111 | if let Some(config) = Cfg::load_single(nested.first().unwrap()) { |
112 | configs.push(config); |
113 | } |
114 | } |
115 | } |
116 | |
117 | match configs.len() { |
118 | 0 => None, |
119 | 1 => Some(configs.pop().unwrap()), |
120 | _ => Some(Cfg::All(configs)), |
121 | } |
122 | } |
123 | |
124 | pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> { |
125 | let target = dependency.target.as_ref()?; |
126 | match syn::parse_str::<syn::Meta>(target) { |
127 | Ok(target) => { |
128 | // Parsing succeeded using the #[cfg] syntax |
129 | if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target { |
130 | if !path.is_ident("cfg" ) || nested.len() != 1 { |
131 | return None; |
132 | } |
133 | Cfg::load_single(nested.first().unwrap()) |
134 | } else { |
135 | None |
136 | } |
137 | } |
138 | Err(_) => { |
139 | // Parsing failed using #[cfg], this may be a literal target |
140 | // name |
141 | Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new( |
142 | target, |
143 | proc_macro2::Span::call_site(), |
144 | )))) |
145 | } |
146 | } |
147 | } |
148 | |
149 | fn load_single(item: &syn::NestedMeta) -> Option<Cfg> { |
150 | Some(match *item { |
151 | syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => { |
152 | Cfg::Boolean(format!(" {}" , path.segments.first().unwrap().ident)) |
153 | } |
154 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { |
155 | ref path, |
156 | lit: syn::Lit::Str(ref value), |
157 | .. |
158 | })) => Cfg::Named( |
159 | format!(" {}" , path.segments.first().unwrap().ident), |
160 | value.value(), |
161 | ), |
162 | syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { |
163 | ref path, |
164 | ref nested, |
165 | .. |
166 | })) => { |
167 | if path.is_ident("any" ) { |
168 | Cfg::Any(Cfg::load_list(nested.iter())?) |
169 | } else if path.is_ident("all" ) { |
170 | Cfg::All(Cfg::load_list(nested.iter())?) |
171 | } else if path.is_ident("not" ) { |
172 | if nested.len() != 1 { |
173 | return None; |
174 | } |
175 | |
176 | Cfg::Not(Box::new(Cfg::load_single(&nested[0])?)) |
177 | } else { |
178 | return None; |
179 | } |
180 | } |
181 | _ => return None, |
182 | }) |
183 | } |
184 | |
185 | fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> { |
186 | let mut configs = Vec::new(); |
187 | |
188 | for attr in attrs { |
189 | configs.push(Cfg::load_single(attr)?); |
190 | } |
191 | |
192 | if configs.is_empty() { |
193 | None |
194 | } else { |
195 | Some(configs) |
196 | } |
197 | } |
198 | } |
199 | |
200 | pub trait ToCondition: Sized { |
201 | fn to_condition(&self, config: &Config) -> Option<Condition>; |
202 | } |
203 | |
204 | impl ToCondition for Option<Cfg> { |
205 | fn to_condition(&self, config: &Config) -> Option<Condition> { |
206 | self.as_ref()?.to_condition(config) |
207 | } |
208 | } |
209 | |
210 | impl ToCondition for Cfg { |
211 | fn to_condition(&self, config: &Config) -> Option<Condition> { |
212 | match *self { |
213 | Cfg::Boolean(ref cfg_name) => { |
214 | let define = config |
215 | .defines |
216 | .iter() |
217 | .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key)); |
218 | if let Some((_, define)) = define { |
219 | Some(Condition::Define(define.to_owned())) |
220 | } else { |
221 | warn!( |
222 | "Missing `[defines]` entry for ` {}` in cbindgen config." , |
223 | self, |
224 | ); |
225 | None |
226 | } |
227 | } |
228 | Cfg::Named(ref cfg_name, ref cfg_value) => { |
229 | let define = config.defines.iter().find(|(key, ..)| { |
230 | DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key) |
231 | }); |
232 | if let Some((_, define)) = define { |
233 | Some(Condition::Define(define.to_owned())) |
234 | } else { |
235 | warn!( |
236 | "Missing `[defines]` entry for ` {}` in cbindgen config." , |
237 | self, |
238 | ); |
239 | None |
240 | } |
241 | } |
242 | Cfg::Any(ref children) => { |
243 | let conditions: Vec<_> = children |
244 | .iter() |
245 | .filter_map(|x| x.to_condition(config)) |
246 | .collect(); |
247 | match conditions.len() { |
248 | 0 => None, |
249 | 1 => conditions.into_iter().next(), |
250 | _ => Some(Condition::Any(conditions)), |
251 | } |
252 | } |
253 | Cfg::All(ref children) => { |
254 | let cfgs: Vec<_> = children |
255 | .iter() |
256 | .filter_map(|x| x.to_condition(config)) |
257 | .collect(); |
258 | match cfgs.len() { |
259 | 0 => None, |
260 | 1 => cfgs.into_iter().next(), |
261 | _ => Some(Condition::All(cfgs)), |
262 | } |
263 | } |
264 | Cfg::Not(ref child) => child |
265 | .to_condition(config) |
266 | .map(|cfg| Condition::Not(Box::new(cfg))), |
267 | } |
268 | } |
269 | } |
270 | |
271 | #[derive (Debug, Clone)] |
272 | pub enum Condition { |
273 | Define(String), |
274 | Any(Vec<Condition>), |
275 | All(Vec<Condition>), |
276 | Not(Box<Condition>), |
277 | } |
278 | |
279 | impl Condition { |
280 | fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { |
281 | match *self { |
282 | Condition::Define(ref define) => { |
283 | if config.language == Language::Cython { |
284 | write!(out, " {}" , define); |
285 | } else { |
286 | out.write("defined(" ); |
287 | write!(out, " {}" , define); |
288 | out.write(")" ); |
289 | } |
290 | } |
291 | Condition::Any(ref conditions) => { |
292 | out.write("(" ); |
293 | for (i, condition) in conditions.iter().enumerate() { |
294 | if i != 0 { |
295 | out.write(if config.language == Language::Cython { |
296 | " or " |
297 | } else { |
298 | " || " |
299 | }); |
300 | } |
301 | condition.write(config, out); |
302 | } |
303 | out.write(")" ); |
304 | } |
305 | Condition::All(ref conditions) => { |
306 | out.write("(" ); |
307 | for (i, condition) in conditions.iter().enumerate() { |
308 | if i != 0 { |
309 | out.write(if config.language == Language::Cython { |
310 | " and " |
311 | } else { |
312 | " && " |
313 | }); |
314 | } |
315 | condition.write(config, out); |
316 | } |
317 | out.write(")" ); |
318 | } |
319 | Condition::Not(ref condition) => { |
320 | out.write(if config.language == Language::Cython { |
321 | "not " |
322 | } else { |
323 | "!" |
324 | }); |
325 | condition.write(config, out); |
326 | } |
327 | } |
328 | } |
329 | } |
330 | |
331 | pub trait ConditionWrite { |
332 | fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>); |
333 | fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>); |
334 | } |
335 | |
336 | impl ConditionWrite for Option<Condition> { |
337 | fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { |
338 | if let Some(ref cfg) = *self { |
339 | if config.language == Language::Cython { |
340 | out.write("IF " ); |
341 | cfg.write(config, out); |
342 | out.open_brace(); |
343 | } else { |
344 | out.push_set_spaces(0); |
345 | out.write("#if " ); |
346 | cfg.write(config, out); |
347 | out.pop_set_spaces(); |
348 | out.new_line(); |
349 | } |
350 | } |
351 | } |
352 | |
353 | fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { |
354 | if self.is_some() { |
355 | if config.language == Language::Cython { |
356 | out.close_brace(false); |
357 | } else { |
358 | out.new_line(); |
359 | out.push_set_spaces(0); |
360 | out.write("#endif" ); |
361 | out.pop_set_spaces(); |
362 | } |
363 | } |
364 | } |
365 | } |
366 | |