cbindgen/bindgen/
bindings.rs1use 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
23pub struct Bindings {
25 pub config: Config,
26 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 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 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 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 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 canon_source_files.sort_unstable();
168
169 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 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 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}