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::{BufWriter, 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, Type, Typedef,
17};
18use crate::bindgen::language_backend::{
19    CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend,
20};
21use crate::bindgen::writer::SourceWriter;
22
23/// A bindings header that can be written.
24pub struct Bindings {
25    pub config: Config,
26    /// The map from path to struct, used to lookup whether a given type is a
27    /// transparent struct. This is needed to generate code for constants.
28    struct_map: ItemMap<Struct>,
29    typedef_map: ItemMap<Typedef>,
30    struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
31    pub globals: Vec<Static>,
32    pub constants: Vec<Constant>,
33    pub items: Vec<ItemContainer>,
34    pub functions: Vec<Function>,
35    source_files: Vec<path::PathBuf>,
36    /// Bindings are generated by a recursive call to cbindgen
37    /// and shouldn't do anything when written anywhere.
38    noop: bool,
39    pub package_version: String,
40}
41
42impl Bindings {
43    #[allow(clippy::too_many_arguments)]
44    pub(crate) fn new(
45        config: Config,
46        struct_map: ItemMap<Struct>,
47        typedef_map: ItemMap<Typedef>,
48        constants: Vec<Constant>,
49        globals: Vec<Static>,
50        items: Vec<ItemContainer>,
51        functions: Vec<Function>,
52        source_files: Vec<path::PathBuf>,
53        noop: bool,
54        package_version: String,
55    ) -> Bindings {
56        Bindings {
57            config,
58            struct_map,
59            typedef_map,
60            struct_fileds_memo: Default::default(),
61            globals,
62            constants,
63            items,
64            functions,
65            source_files,
66            noop,
67            package_version,
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        let mut resolved_path = Cow::Borrowed(path);
81        loop {
82            let mut found = None;
83            self.typedef_map.for_items(&resolved_path, |item| {
84                if let Type::Path(ref p) = item.aliased {
85                    found = Some(p.path().clone());
86                }
87            });
88            resolved_path = match found {
89                Some(p) => Cow::Owned(p),
90                None => break,
91            }
92        }
93        resolved_path
94    }
95
96    pub fn struct_exists(&self, path: &BindgenPath) -> bool {
97        let mut any = false;
98        self.struct_map
99            .for_items(&self.resolved_struct_path(path), |_| any = true);
100        any
101    }
102
103    pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> {
104        let mut memos = self.struct_fileds_memo.borrow_mut();
105        if let Some(memo) = memos.get(path) {
106            return memo.clone();
107        }
108
109        let resolved_path = self.resolved_struct_path(path);
110
111        let mut fields = Vec::<String>::new();
112        self.struct_map.for_items(&resolved_path, |st| {
113            let mut pos: usize = 0;
114            for field in &st.fields {
115                if let Some(found_pos) = fields.iter().position(|v| *v == field.name) {
116                    pos = found_pos + 1;
117                } else {
118                    fields.insert(pos, field.name.clone());
119                    pos += 1;
120                }
121            }
122        });
123
124        let fields = Rc::new(fields);
125        memos.insert(path.clone(), fields.clone());
126        if let Cow::Owned(p) = resolved_path {
127            memos.insert(p, fields.clone());
128        }
129        fields
130    }
131
132    /// Lists the exported symbols that can be dynamically linked, i.e. globals and functions.
133    pub fn dynamic_symbols_names(&self) -> impl Iterator<Item = &str> {
134        use crate::bindgen::ir::Item;
135
136        let function_names = self.functions.iter().map(|f| f.path().name());
137        let global_names = self.globals.iter().map(|g| g.export_name());
138        function_names.chain(global_names)
139    }
140
141    pub fn generate_symfile<P: AsRef<path::Path>>(&self, symfile_path: P) {
142        if let Some(dir) = symfile_path.as_ref().parent() {
143            std::fs::create_dir_all(dir).unwrap();
144        }
145        let mut writer = BufWriter::new(File::create(symfile_path).unwrap());
146        writeln!(&mut writer, "{{").expect("writing symbol file header failed");
147        for symbol in self.dynamic_symbols_names() {
148            writeln!(&mut writer, "{};", symbol).expect("writing symbol failed");
149        }
150        write!(&mut writer, "}};").expect("writing symbol file footer failed");
151    }
152
153    pub fn generate_depfile<P: AsRef<path::Path>>(&self, header_path: P, depfile_path: P) {
154        if let Some(dir) = depfile_path.as_ref().parent() {
155            if !dir.exists() {
156                std::fs::create_dir_all(dir).unwrap()
157            }
158        }
159        let canon_header_path = header_path.as_ref().canonicalize().unwrap();
160        let mut canon_source_files: Vec<_> = self
161            .source_files
162            .iter()
163            .chain(self.config.config_path.as_ref())
164            .map(|p| p.canonicalize().unwrap())
165            .collect();
166        // Sorting makes testing easier by ensuring the output is ordered.
167        canon_source_files.sort_unstable();
168
169        // When writing the depfile we must escape whitespace in paths to avoid it being interpreted
170        // as a seperator.
171        // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode
172        // compliant slice, without knowing the encoding, so we lossy convert such cases,
173        // to avoid panics.
174        let mut depfile = File::create(depfile_path).unwrap();
175        write!(
176            &mut depfile,
177            "{}:",
178            canon_header_path.to_string_lossy().replace(' ', "\\ ")
179        )
180        .expect("Writing header name to depfile failed");
181        canon_source_files.into_iter().for_each(|source_file| {
182            // Add line-continue and line-break and then indent with 4 spaces.
183            // This makes the output more human-readable.
184            depfile.write_all(b" \\\n    ").unwrap();
185            let escaped_path = source_file.to_string_lossy().replace(' ', "\\ ");
186            depfile.write_all(escaped_path.as_bytes()).unwrap();
187        });
188
189        writeln!(&mut depfile).unwrap();
190
191        depfile.flush().unwrap();
192    }
193
194    pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
195        if self.noop {
196            return false;
197        }
198
199        // Don't compare files if we've never written this file before
200        if !path.as_ref().is_file() {
201            if let Some(parent) = path::Path::new(path.as_ref()).parent() {
202                fs::create_dir_all(parent).unwrap();
203            }
204            self.write(File::create(path).unwrap());
205            return true;
206        }
207
208        let mut new_file_contents = Vec::new();
209        self.write(&mut new_file_contents);
210
211        let mut old_file_contents = Vec::new();
212        {
213            let mut old_file = File::open(&path).unwrap();
214            old_file.read_to_end(&mut old_file_contents).unwrap();
215        }
216
217        if old_file_contents != new_file_contents {
218            let mut new_file = File::create(&path).unwrap();
219            new_file.write_all(&new_file_contents).unwrap();
220            true
221        } else {
222            false
223        }
224    }
225
226    pub fn write<F: Write>(&self, file: F) {
227        match self.config.language {
228            Language::Cxx | Language::C => {
229                self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config))
230            }
231            Language::Cython => {
232                self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config))
233            }
234        }
235    }
236
237    fn write_with_backend<F: Write, LB: LanguageBackend>(
238        &self,
239        file: F,
240        language_backend: &mut LB,
241    ) {
242        if self.noop {
243            return;
244        }
245
246        let mut out = SourceWriter::new(file, self);
247
248        language_backend.write_bindings(&mut out, self);
249    }
250}