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
137            .functions
138            .iter()
139            .filter(|f| f.annotations.should_export())
140            .map(|f| f.path().name());
141        let global_names = self
142            .globals
143            .iter()
144            .filter(|s| s.annotations.should_export())
145            .map(|g| g.export_name());
146        function_names.chain(global_names)
147    }
148
149    pub fn generate_symfile<P: AsRef<path::Path>>(&self, symfile_path: P) {
150        if let Some(dir) = symfile_path.as_ref().parent() {
151            std::fs::create_dir_all(dir).unwrap();
152        }
153        let mut writer = BufWriter::new(File::create(symfile_path).unwrap());
154        writeln!(&mut writer, "{{").expect("writing symbol file header failed");
155        for symbol in self.dynamic_symbols_names() {
156            writeln!(&mut writer, "{symbol};").expect("writing symbol failed");
157        }
158        write!(&mut writer, "}};").expect("writing symbol file footer failed");
159    }
160
161    pub fn generate_depfile<P: AsRef<path::Path>>(&self, header_path: P, depfile_path: P) {
162        if let Some(dir) = depfile_path.as_ref().parent() {
163            if !dir.exists() {
164                std::fs::create_dir_all(dir).unwrap()
165            }
166        }
167        let canon_header_path = header_path.as_ref().canonicalize().unwrap();
168        let mut canon_source_files: Vec<_> = self
169            .source_files
170            .iter()
171            .chain(self.config.config_path.as_ref())
172            .map(|p| p.canonicalize().unwrap())
173            .collect();
174        // Sorting makes testing easier by ensuring the output is ordered.
175        canon_source_files.sort_unstable();
176
177        // When writing the depfile we must escape whitespace in paths to avoid it being interpreted
178        // as a seperator.
179        // It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode
180        // compliant slice, without knowing the encoding, so we lossy convert such cases,
181        // to avoid panics.
182        let mut depfile = File::create(depfile_path).unwrap();
183        write!(
184            &mut depfile,
185            "{}:",
186            canon_header_path.to_string_lossy().replace(' ', "\\ ")
187        )
188        .expect("Writing header name to depfile failed");
189        canon_source_files.into_iter().for_each(|source_file| {
190            // Add line-continue and line-break and then indent with 4 spaces.
191            // This makes the output more human-readable.
192            depfile.write_all(b" \\\n    ").unwrap();
193            let escaped_path = source_file.to_string_lossy().replace(' ', "\\ ");
194            depfile.write_all(escaped_path.as_bytes()).unwrap();
195        });
196
197        writeln!(&mut depfile).unwrap();
198
199        depfile.flush().unwrap();
200    }
201
202    pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
203        if self.noop {
204            return false;
205        }
206
207        // Don't compare files if we've never written this file before
208        if !path.as_ref().is_file() {
209            if let Some(parent) = path::Path::new(path.as_ref()).parent() {
210                fs::create_dir_all(parent).unwrap();
211            }
212            self.write(File::create(path).unwrap());
213            return true;
214        }
215
216        let mut new_file_contents = Vec::new();
217        self.write(&mut new_file_contents);
218
219        let mut old_file_contents = Vec::new();
220        {
221            let mut old_file = File::open(&path).unwrap();
222            old_file.read_to_end(&mut old_file_contents).unwrap();
223        }
224
225        if old_file_contents != new_file_contents {
226            let mut new_file = File::create(&path).unwrap();
227            new_file.write_all(&new_file_contents).unwrap();
228            true
229        } else {
230            false
231        }
232    }
233
234    pub fn write<F: Write>(&self, file: F) {
235        match self.config.language {
236            Language::Cxx | Language::C => {
237                self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config))
238            }
239            Language::Cython => {
240                self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config))
241            }
242        }
243    }
244
245    fn write_with_backend<F: Write, LB: LanguageBackend>(
246        &self,
247        file: F,
248        language_backend: &mut LB,
249    ) {
250        if self.noop {
251            return;
252        }
253
254        let mut out = SourceWriter::new(file, self);
255
256        language_backend.write_bindings(&mut out, self);
257    }
258}