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
5use std::borrow::Cow;
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::fs;
9use std::fs::File;
10use std::io::{Read, Write};
11use std::path;
12use std::rc::Rc;
13
14use crate::bindgen::config::{Config, Language};
15use crate::bindgen::ir::{
16 Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef,
17};
18use crate::bindgen::writer::{Source, SourceWriter};
19
20/// A bindings header that can be written.
21pub 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)]
39enum NamespaceOperation {
40 Open,
41 Close,
42}
43
44impl 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