cbindgen/bindgen/
bindings.rs

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}