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::fs;
6 use std::fs::File;
7 use std::io::{Read, Write};
8 use std::path;
9 
10 use bindgen::config::{Config, Language};
11 use bindgen::ir::{
12     Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct,
13 };
14 use bindgen::writer::{Source, SourceWriter};
15 
16 /// A bindings header that can be written.
17 pub struct Bindings {
18     pub config: Config,
19     /// The map from path to struct, used to lookup whether a given type is a
20     /// transparent struct. This is needed to generate code for constants.
21     struct_map: ItemMap<Struct>,
22     globals: Vec<Static>,
23     constants: Vec<Constant>,
24     items: Vec<ItemContainer>,
25     functions: Vec<Function>,
26 }
27 
28 impl Bindings {
new( config: Config, struct_map: ItemMap<Struct>, constants: Vec<Constant>, globals: Vec<Static>, items: Vec<ItemContainer>, functions: Vec<Function>, ) -> Bindings29     pub(crate) fn new(
30         config: Config,
31         struct_map: ItemMap<Struct>,
32         constants: Vec<Constant>,
33         globals: Vec<Static>,
34         items: Vec<ItemContainer>,
35         functions: Vec<Function>,
36     ) -> Bindings {
37         Bindings {
38             config,
39             struct_map,
40             globals,
41             constants,
42             items,
43             functions,
44         }
45     }
46 
47     // FIXME(emilio): What to do when the configuration doesn't match?
struct_is_transparent(&self, path: &BindgenPath) -> bool48     pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
49         let mut any = false;
50         self.struct_map.for_items(path, |s| any |= s.is_transparent);
51         any
52     }
53 
struct_exists(&self, path: &BindgenPath) -> bool54     pub fn struct_exists(&self, path: &BindgenPath) -> bool {
55         let mut any = false;
56         self.struct_map.for_items(path, |_| any = true);
57         any
58     }
59 
write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool60     pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
61         // Don't compare files if we've never written this file before
62         if !path.as_ref().is_file() {
63             if let Some(parent) = path::Path::new(path.as_ref()).parent() {
64                 fs::create_dir_all(parent).unwrap();
65             }
66             self.write(File::create(path).unwrap());
67             return true;
68         }
69 
70         let mut new_file_contents = Vec::new();
71         self.write(&mut new_file_contents);
72 
73         let mut old_file_contents = Vec::new();
74         {
75             let mut old_file = File::open(&path).unwrap();
76             old_file.read_to_end(&mut old_file_contents).unwrap();
77         }
78 
79         if old_file_contents != new_file_contents {
80             let mut new_file = File::create(&path).unwrap();
81             new_file.write_all(&new_file_contents).unwrap();
82             true
83         } else {
84             false
85         }
86     }
87 
write_headers<F: Write>(&self, out: &mut SourceWriter<F>)88     pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
89         if let Some(ref f) = self.config.header {
90             out.new_line_if_not_start();
91             write!(out, "{}", f);
92             out.new_line();
93         }
94         if let Some(ref f) = self.config.include_guard {
95             out.new_line_if_not_start();
96             write!(out, "#ifndef {}", f);
97             out.new_line();
98             write!(out, "#define {}", f);
99             out.new_line();
100         }
101         if self.config.include_version {
102             out.new_line_if_not_start();
103             write!(
104                 out,
105                 "/* Generated with cbindgen:{} */",
106                 ::bindgen::config::VERSION
107             );
108             out.new_line();
109         }
110         if let Some(ref f) = self.config.autogen_warning {
111             out.new_line_if_not_start();
112             write!(out, "{}", f);
113             out.new_line();
114         }
115 
116         if self.config.no_includes
117             && self.config.sys_includes.is_empty()
118             && self.config.includes.is_empty()
119         {
120             return;
121         }
122 
123         out.new_line_if_not_start();
124 
125         if !self.config.no_includes {
126             if self.config.language == Language::C {
127                 out.write("#include <stdarg.h>");
128                 out.new_line();
129                 out.write("#include <stdbool.h>");
130                 out.new_line();
131                 out.write("#include <stdint.h>");
132                 out.new_line();
133                 out.write("#include <stdlib.h>");
134                 out.new_line();
135             } else {
136                 out.write("#include <cstdarg>");
137                 out.new_line();
138                 out.write("#include <cstdint>");
139                 out.new_line();
140                 out.write("#include <cstdlib>");
141                 out.new_line();
142                 out.write("#include <new>");
143                 out.new_line();
144                 if self.config.enumeration.cast_assert_name.is_none()
145                     && (self.config.enumeration.derive_mut_casts
146                         || self.config.enumeration.derive_const_casts)
147                 {
148                     out.write("#include <cassert>");
149                     out.new_line();
150                 }
151             }
152         }
153 
154         for include in &self.config.sys_includes {
155             write!(out, "#include <{}>", include);
156             out.new_line();
157         }
158 
159         for include in &self.config.includes {
160             write!(out, "#include \"{}\"", include);
161             out.new_line();
162         }
163     }
164 
write<F: Write>(&self, file: F)165     pub fn write<F: Write>(&self, file: F) {
166         let mut out = SourceWriter::new(file, self);
167 
168         self.write_headers(&mut out);
169 
170         if self.config.language == Language::Cxx {
171             self.open_namespaces(&mut out);
172         }
173 
174         for constant in &self.constants {
175             if constant.ty.is_primitive_or_ptr_primitive() {
176                 out.new_line_if_not_start();
177                 constant.write(&self.config, &mut out, None);
178                 out.new_line();
179             }
180         }
181 
182         for item in &self.items {
183             if item
184                 .deref()
185                 .annotations()
186                 .bool("no-export")
187                 .unwrap_or(false)
188             {
189                 continue;
190             }
191 
192             out.new_line_if_not_start();
193             match *item {
194                 ItemContainer::Constant(..) => unreachable!(),
195                 ItemContainer::Static(..) => unreachable!(),
196                 ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
197                 ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
198                 ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
199                 ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
200                 ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
201             }
202             out.new_line();
203         }
204 
205         for constant in &self.constants {
206             if !constant.ty.is_primitive_or_ptr_primitive() {
207                 out.new_line_if_not_start();
208                 constant.write(&self.config, &mut out, None);
209                 out.new_line();
210             }
211         }
212 
213         if !self.functions.is_empty() || !self.globals.is_empty() {
214             if self.config.language == Language::C && self.config.cpp_compat {
215                 out.new_line_if_not_start();
216                 out.write("#ifdef __cplusplus");
217             }
218 
219             if self.config.language == Language::Cxx || self.config.cpp_compat {
220                 out.new_line();
221                 out.write("extern \"C\" {");
222                 out.new_line();
223             }
224 
225             if self.config.language == Language::C && self.config.cpp_compat {
226                 out.write("#endif // __cplusplus");
227                 out.new_line();
228             }
229 
230             for global in &self.globals {
231                 out.new_line_if_not_start();
232                 global.write(&self.config, &mut out);
233                 out.new_line();
234             }
235 
236             for function in &self.functions {
237                 out.new_line_if_not_start();
238                 function.write(&self.config, &mut out);
239                 out.new_line();
240             }
241 
242             if self.config.language == Language::C && self.config.cpp_compat {
243                 out.new_line();
244                 out.write("#ifdef __cplusplus");
245             }
246 
247             if self.config.language == Language::Cxx || self.config.cpp_compat {
248                 out.new_line();
249                 out.write("} // extern \"C\"");
250                 out.new_line();
251             }
252 
253             if self.config.language == Language::C && self.config.cpp_compat {
254                 out.write("#endif // __cplusplus");
255                 out.new_line();
256             }
257         }
258 
259         if self.config.language == Language::Cxx {
260             self.close_namespaces(&mut out);
261         }
262 
263         if let Some(ref f) = self.config.include_guard {
264             out.new_line_if_not_start();
265             if self.config.language == Language::C {
266                 write!(out, "#endif /* {} */", f);
267             } else {
268                 write!(out, "#endif // {}", f);
269             }
270             out.new_line();
271         }
272         if let Some(ref f) = self.config.trailer {
273             out.new_line_if_not_start();
274             write!(out, "{}", f);
275             out.new_line();
276         }
277     }
278 
open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>)279     pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
280         let mut wrote_namespace: bool = false;
281         if let Some(ref namespace) = self.config.namespace {
282             wrote_namespace = true;
283 
284             out.new_line();
285             write!(out, "namespace {} {{", namespace);
286         }
287         if let Some(ref namespaces) = self.config.namespaces {
288             wrote_namespace = true;
289             for namespace in namespaces {
290                 out.new_line();
291                 write!(out, "namespace {} {{", namespace);
292             }
293         }
294         if wrote_namespace {
295             out.new_line();
296         }
297     }
298 
close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>)299     pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
300         let mut wrote_namespace: bool = false;
301         if let Some(ref namespaces) = self.config.namespaces {
302             wrote_namespace = true;
303 
304             for namespace in namespaces.iter().rev() {
305                 out.new_line_if_not_start();
306                 write!(out, "}} // namespace {}", namespace);
307             }
308         }
309         if let Some(ref namespace) = self.config.namespace {
310             wrote_namespace = true;
311 
312             out.new_line_if_not_start();
313             write!(out, "}} // namespace {}", namespace);
314         }
315         if wrote_namespace {
316             out.new_line();
317         }
318     }
319 }
320