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::borrow::Cow; |
6 | use std::cell::RefCell; |
7 | use std::collections::HashMap; |
8 | use std::fs; |
9 | use std::fs::File; |
10 | use std::io::{Read, Write}; |
11 | use std::path; |
12 | use std::rc::Rc; |
13 | |
14 | use crate::bindgen::config::{Config, Language}; |
15 | use crate::bindgen::ir::{ |
16 | Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef, |
17 | }; |
18 | use crate::bindgen::writer::{Source, SourceWriter}; |
19 | |
20 | /// A bindings header that can be written. |
21 | pub struct Bindings { |
22 | pub config: Config, |
23 | /// The map from path to struct, used to lookup whether a given type is a |
24 | /// transparent struct. This is needed to generate code for constants. |
25 | struct_map: ItemMap<Struct>, |
26 | typedef_map: ItemMap<Typedef>, |
27 | struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>, |
28 | globals: Vec<Static>, |
29 | constants: Vec<Constant>, |
30 | items: Vec<ItemContainer>, |
31 | functions: Vec<Function>, |
32 | source_files: Vec<path::PathBuf>, |
33 | /// Bindings are generated by a recursive call to cbindgen |
34 | /// and shouldn't do anything when written anywhere. |
35 | noop: bool, |
36 | } |
37 | |
38 | #[derive (PartialEq, Eq)] |
39 | enum NamespaceOperation { |
40 | Open, |
41 | Close, |
42 | } |
43 | |
44 | impl Bindings { |
45 | #[allow (clippy::too_many_arguments)] |
46 | pub(crate) fn new( |
47 | config: Config, |
48 | struct_map: ItemMap<Struct>, |
49 | typedef_map: ItemMap<Typedef>, |
50 | constants: Vec<Constant>, |
51 | globals: Vec<Static>, |
52 | items: Vec<ItemContainer>, |
53 | functions: Vec<Function>, |
54 | source_files: Vec<path::PathBuf>, |
55 | noop: bool, |
56 | ) -> Bindings { |
57 | Bindings { |
58 | config, |
59 | struct_map, |
60 | typedef_map, |
61 | struct_fileds_memo: Default::default(), |
62 | globals, |
63 | constants, |
64 | items, |
65 | functions, |
66 | source_files, |
67 | noop, |
68 | } |
69 | } |
70 | |
71 | // FIXME(emilio): What to do when the configuration doesn't match? |
72 | pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool { |
73 | let mut any = false; |
74 | self.struct_map.for_items(path, |s| any |= s.is_transparent); |
75 | any |
76 | } |
77 | |
78 | /// Peels through typedefs to allow resolving structs. |
79 | fn resolved_struct_path<'a>(&self, path: &'a BindgenPath) -> Cow<'a, BindgenPath> { |
80 | use crate::bindgen::ir::Type; |
81 | |
82 | let mut resolved_path = Cow::Borrowed(path); |
83 | loop { |
84 | let mut found = None; |
85 | self.typedef_map.for_items(&resolved_path, |item| { |
86 | if let Type::Path(ref p) = item.aliased { |
87 | found = Some(p.path().clone()); |
88 | } |
89 | }); |
90 | resolved_path = match found { |
91 | Some(p) => Cow::Owned(p), |
92 | None => break, |
93 | } |
94 | } |
95 | resolved_path |
96 | } |
97 | |
98 | pub fn struct_exists(&self, path: &BindgenPath) -> bool { |
99 | let mut any = false; |
100 | self.struct_map |
101 | .for_items(&self.resolved_struct_path(path), |_| any = true); |
102 | any |
103 | } |
104 | |
105 | pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> { |
106 | let mut memos = self.struct_fileds_memo.borrow_mut(); |
107 | if let Some(memo) = memos.get(path) { |
108 | return memo.clone(); |
109 | } |
110 | |
111 | let resolved_path = self.resolved_struct_path(path); |
112 | |
113 | let mut fields = Vec::<String>::new(); |
114 | self.struct_map.for_items(&resolved_path, |st| { |
115 | let mut pos: usize = 0; |
116 | for field in &st.fields { |
117 | if let Some(found_pos) = fields.iter().position(|v| *v == field.name) { |
118 | pos = found_pos + 1; |
119 | } else { |
120 | fields.insert(pos, field.name.clone()); |
121 | pos += 1; |
122 | } |
123 | } |
124 | }); |
125 | |
126 | let fields = Rc::new(fields); |
127 | memos.insert(path.clone(), fields.clone()); |
128 | if let Cow::Owned(p) = resolved_path { |
129 | memos.insert(p, fields.clone()); |
130 | } |
131 | fields |
132 | } |
133 | |
134 | pub fn generate_depfile<P: AsRef<path::Path>>(&self, header_path: P, depfile_path: P) { |
135 | if let Some(dir) = depfile_path.as_ref().parent() { |
136 | if !dir.exists() { |
137 | std::fs::create_dir_all(dir).unwrap() |
138 | } |
139 | } |
140 | let canon_header_path = header_path.as_ref().canonicalize().unwrap(); |
141 | let mut canon_source_files: Vec<_> = self |
142 | .source_files |
143 | .iter() |
144 | .chain(self.config.config_path.as_ref()) |
145 | .map(|p| p.canonicalize().unwrap()) |
146 | .collect(); |
147 | // Sorting makes testing easier by ensuring the output is ordered. |
148 | canon_source_files.sort_unstable(); |
149 | |
150 | // When writing the depfile we must escape whitespace in paths to avoid it being interpreted |
151 | // as a seperator. |
152 | // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode |
153 | // compliant slice, without knowing the encoding, so we lossy convert such cases, |
154 | // to avoid panics. |
155 | let mut depfile = File::create(depfile_path).unwrap(); |
156 | write!( |
157 | &mut depfile, |
158 | " {}:" , |
159 | canon_header_path.to_string_lossy().replace(' ' , " \\ " ) |
160 | ) |
161 | .expect("Writing header name to depfile failed" ); |
162 | canon_source_files.into_iter().for_each(|source_file| { |
163 | // Add line-continue and line-break and then indent with 4 spaces. |
164 | // This makes the output more human-readable. |
165 | depfile.write_all(b" \\\n " ).unwrap(); |
166 | let escaped_path = source_file.to_string_lossy().replace(' ' , " \\ " ); |
167 | depfile.write_all(escaped_path.as_bytes()).unwrap(); |
168 | }); |
169 | |
170 | writeln!(&mut depfile).unwrap(); |
171 | |
172 | depfile.flush().unwrap(); |
173 | } |
174 | |
175 | pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool { |
176 | if self.noop { |
177 | return false; |
178 | } |
179 | |
180 | // Don't compare files if we've never written this file before |
181 | if !path.as_ref().is_file() { |
182 | if let Some(parent) = path::Path::new(path.as_ref()).parent() { |
183 | fs::create_dir_all(parent).unwrap(); |
184 | } |
185 | self.write(File::create(path).unwrap()); |
186 | return true; |
187 | } |
188 | |
189 | let mut new_file_contents = Vec::new(); |
190 | self.write(&mut new_file_contents); |
191 | |
192 | let mut old_file_contents = Vec::new(); |
193 | { |
194 | let mut old_file = File::open(&path).unwrap(); |
195 | old_file.read_to_end(&mut old_file_contents).unwrap(); |
196 | } |
197 | |
198 | if old_file_contents != new_file_contents { |
199 | let mut new_file = File::create(&path).unwrap(); |
200 | new_file.write_all(&new_file_contents).unwrap(); |
201 | true |
202 | } else { |
203 | false |
204 | } |
205 | } |
206 | |
207 | pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) { |
208 | if self.noop { |
209 | return; |
210 | } |
211 | |
212 | if let Some(ref f) = self.config.header { |
213 | out.new_line_if_not_start(); |
214 | write!(out, " {}" , f); |
215 | out.new_line(); |
216 | } |
217 | if let Some(f) = self.config.include_guard() { |
218 | out.new_line_if_not_start(); |
219 | write!(out, "#ifndef {}" , f); |
220 | out.new_line(); |
221 | write!(out, "#define {}" , f); |
222 | out.new_line(); |
223 | } |
224 | if self.config.pragma_once && self.config.language != Language::Cython { |
225 | out.new_line_if_not_start(); |
226 | write!(out, "#pragma once" ); |
227 | out.new_line(); |
228 | } |
229 | if self.config.include_version { |
230 | out.new_line_if_not_start(); |
231 | write!( |
232 | out, |
233 | "/* Generated with cbindgen: {} */" , |
234 | crate::bindgen::config::VERSION |
235 | ); |
236 | out.new_line(); |
237 | } |
238 | if let Some(ref f) = self.config.autogen_warning { |
239 | out.new_line_if_not_start(); |
240 | write!(out, " {}" , f); |
241 | out.new_line(); |
242 | } |
243 | |
244 | if self.config.no_includes |
245 | && self.config.sys_includes().is_empty() |
246 | && self.config.includes().is_empty() |
247 | && (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython) |
248 | && self.config.after_includes.is_none() |
249 | { |
250 | return; |
251 | } |
252 | |
253 | out.new_line_if_not_start(); |
254 | |
255 | if !self.config.no_includes { |
256 | match self.config.language { |
257 | Language::C => { |
258 | out.write("#include <stdarg.h>" ); |
259 | out.new_line(); |
260 | out.write("#include <stdbool.h>" ); |
261 | out.new_line(); |
262 | if self.config.usize_is_size_t { |
263 | out.write("#include <stddef.h>" ); |
264 | out.new_line(); |
265 | } |
266 | out.write("#include <stdint.h>" ); |
267 | out.new_line(); |
268 | out.write("#include <stdlib.h>" ); |
269 | out.new_line(); |
270 | } |
271 | Language::Cxx => { |
272 | out.write("#include <cstdarg>" ); |
273 | out.new_line(); |
274 | if self.config.usize_is_size_t { |
275 | out.write("#include <cstddef>" ); |
276 | out.new_line(); |
277 | } |
278 | out.write("#include <cstdint>" ); |
279 | out.new_line(); |
280 | out.write("#include <cstdlib>" ); |
281 | out.new_line(); |
282 | out.write("#include <ostream>" ); |
283 | out.new_line(); |
284 | out.write("#include <new>" ); |
285 | out.new_line(); |
286 | if self.config.enumeration.cast_assert_name.is_none() |
287 | && (self.config.enumeration.derive_mut_casts |
288 | || self.config.enumeration.derive_const_casts) |
289 | { |
290 | out.write("#include <cassert>" ); |
291 | out.new_line(); |
292 | } |
293 | } |
294 | Language::Cython => { |
295 | out.write( |
296 | "from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t" , |
297 | ); |
298 | out.new_line(); |
299 | out.write( |
300 | "from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t" , |
301 | ); |
302 | out.new_line(); |
303 | out.write("cdef extern from *" ); |
304 | out.open_brace(); |
305 | out.write("ctypedef bint bool" ); |
306 | out.new_line(); |
307 | out.write("ctypedef struct va_list" ); |
308 | out.new_line(); |
309 | out.close_brace(false); |
310 | } |
311 | } |
312 | } |
313 | |
314 | for include in self.config.sys_includes() { |
315 | write!(out, "#include < {}>" , include); |
316 | out.new_line(); |
317 | } |
318 | |
319 | for include in self.config.includes() { |
320 | write!(out, "#include \"{}\"" , include); |
321 | out.new_line(); |
322 | } |
323 | |
324 | if self.config.language == Language::Cython { |
325 | for (module, names) in &self.config.cython.cimports { |
326 | write!(out, "from {} cimport {}" , module, names.join(", " )); |
327 | out.new_line(); |
328 | } |
329 | } |
330 | |
331 | if let Some(ref line) = self.config.after_includes { |
332 | write!(out, " {}" , line); |
333 | out.new_line(); |
334 | } |
335 | } |
336 | |
337 | pub fn write<F: Write>(&self, file: F) { |
338 | if self.noop { |
339 | return; |
340 | } |
341 | |
342 | let mut out = SourceWriter::new(file, self); |
343 | |
344 | self.write_headers(&mut out); |
345 | |
346 | self.open_namespaces(&mut out); |
347 | |
348 | for constant in &self.constants { |
349 | if constant.uses_only_primitive_types() { |
350 | out.new_line_if_not_start(); |
351 | constant.write(&self.config, &mut out, None); |
352 | out.new_line(); |
353 | } |
354 | } |
355 | |
356 | for item in &self.items { |
357 | if item |
358 | .deref() |
359 | .annotations() |
360 | .bool("no-export" ) |
361 | .unwrap_or(false) |
362 | { |
363 | continue; |
364 | } |
365 | |
366 | out.new_line_if_not_start(); |
367 | match *item { |
368 | ItemContainer::Constant(..) => unreachable!(), |
369 | ItemContainer::Static(..) => unreachable!(), |
370 | ItemContainer::Enum(ref x) => x.write(&self.config, &mut out), |
371 | ItemContainer::Struct(ref x) => x.write(&self.config, &mut out), |
372 | ItemContainer::Union(ref x) => x.write(&self.config, &mut out), |
373 | ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out), |
374 | ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out), |
375 | } |
376 | out.new_line(); |
377 | } |
378 | |
379 | for constant in &self.constants { |
380 | if !constant.uses_only_primitive_types() { |
381 | out.new_line_if_not_start(); |
382 | constant.write(&self.config, &mut out, None); |
383 | out.new_line(); |
384 | } |
385 | } |
386 | |
387 | if !self.functions.is_empty() || !self.globals.is_empty() { |
388 | if self.config.cpp_compatible_c() { |
389 | out.new_line_if_not_start(); |
390 | out.write("#ifdef __cplusplus" ); |
391 | } |
392 | |
393 | if self.config.language == Language::Cxx { |
394 | if let Some(ref using_namespaces) = self.config.using_namespaces { |
395 | for namespace in using_namespaces { |
396 | out.new_line(); |
397 | write!(out, "using namespace {};" , namespace); |
398 | } |
399 | out.new_line(); |
400 | } |
401 | } |
402 | |
403 | if self.config.language == Language::Cxx || self.config.cpp_compatible_c() { |
404 | out.new_line(); |
405 | out.write("extern \"C \" {" ); |
406 | out.new_line(); |
407 | } |
408 | |
409 | if self.config.cpp_compatible_c() { |
410 | out.write("#endif // __cplusplus" ); |
411 | out.new_line(); |
412 | } |
413 | |
414 | for global in &self.globals { |
415 | out.new_line_if_not_start(); |
416 | global.write(&self.config, &mut out); |
417 | out.new_line(); |
418 | } |
419 | |
420 | for function in &self.functions { |
421 | out.new_line_if_not_start(); |
422 | function.write(&self.config, &mut out); |
423 | out.new_line(); |
424 | } |
425 | |
426 | if self.config.cpp_compatible_c() { |
427 | out.new_line(); |
428 | out.write("#ifdef __cplusplus" ); |
429 | } |
430 | |
431 | if self.config.language == Language::Cxx || self.config.cpp_compatible_c() { |
432 | out.new_line(); |
433 | out.write("} // extern \"C \"" ); |
434 | out.new_line(); |
435 | } |
436 | |
437 | if self.config.cpp_compatible_c() { |
438 | out.write("#endif // __cplusplus" ); |
439 | out.new_line(); |
440 | } |
441 | } |
442 | |
443 | if self.config.language == Language::Cython |
444 | && self.globals.is_empty() |
445 | && self.constants.is_empty() |
446 | && self.items.is_empty() |
447 | && self.functions.is_empty() |
448 | { |
449 | out.write("pass" ); |
450 | } |
451 | |
452 | self.close_namespaces(&mut out); |
453 | |
454 | if let Some(f) = self.config.include_guard() { |
455 | out.new_line_if_not_start(); |
456 | if self.config.language == Language::C { |
457 | write!(out, "#endif /* {} */" , f); |
458 | } else { |
459 | write!(out, "#endif // {}" , f); |
460 | } |
461 | out.new_line(); |
462 | } |
463 | if let Some(ref f) = self.config.trailer { |
464 | out.new_line_if_not_start(); |
465 | write!(out, " {}" , f); |
466 | if !f.ends_with(' \n' ) { |
467 | out.new_line(); |
468 | } |
469 | } |
470 | } |
471 | |
472 | fn all_namespaces(&self) -> Vec<&str> { |
473 | if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { |
474 | return vec![]; |
475 | } |
476 | let mut ret = vec![]; |
477 | if let Some(ref namespace) = self.config.namespace { |
478 | ret.push(&**namespace); |
479 | } |
480 | if let Some(ref namespaces) = self.config.namespaces { |
481 | for namespace in namespaces { |
482 | ret.push(&**namespace); |
483 | } |
484 | } |
485 | ret |
486 | } |
487 | |
488 | fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) { |
489 | if self.config.language == Language::Cython { |
490 | if op == NamespaceOperation::Open { |
491 | out.new_line(); |
492 | let header = self.config.cython.header.as_deref().unwrap_or("*" ); |
493 | write!(out, "cdef extern from {}" , header); |
494 | out.open_brace(); |
495 | } else { |
496 | out.close_brace(false); |
497 | } |
498 | return; |
499 | } |
500 | |
501 | let mut namespaces = self.all_namespaces(); |
502 | if namespaces.is_empty() { |
503 | return; |
504 | } |
505 | |
506 | if op == NamespaceOperation::Close { |
507 | namespaces.reverse(); |
508 | } |
509 | |
510 | if self.config.cpp_compatible_c() { |
511 | out.new_line_if_not_start(); |
512 | out.write("#ifdef __cplusplus" ); |
513 | } |
514 | |
515 | for namespace in namespaces { |
516 | out.new_line(); |
517 | match op { |
518 | NamespaceOperation::Open => write!(out, "namespace {} {{" , namespace), |
519 | NamespaceOperation::Close => write!(out, " }} // namespace {}" , namespace), |
520 | } |
521 | } |
522 | |
523 | out.new_line(); |
524 | if self.config.cpp_compatible_c() { |
525 | out.write("#endif // __cplusplus" ); |
526 | out.new_line(); |
527 | } |
528 | } |
529 | |
530 | pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) { |
531 | self.open_close_namespaces(NamespaceOperation::Open, out); |
532 | } |
533 | |
534 | pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) { |
535 | self.open_close_namespaces(NamespaceOperation::Close, out); |
536 | } |
537 | } |
538 | |