| 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::borrow::Cow; |
| 6 | use std::cell::RefCell; |
| 7 | use std::collections::HashMap; |
| 8 | use std::fs; |
| 9 | use std::fs::File; |
| 10 | use std::io::{Read, Write}; |
| 11 | use std::path; |
| 12 | use std::rc::Rc; |
| 13 | |
| 14 | use crate::bindgen::config::{Config, Language}; |
| 15 | use crate::bindgen::ir::{ |
| 16 | Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef, |
| 17 | }; |
| 18 | use crate::bindgen::writer::{Source, SourceWriter}; |
| 19 | |
| 20 | /// A bindings header that can be written. |
| 21 | pub 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)] |
| 39 | enum NamespaceOperation { |
| 40 | Open, |
| 41 | Close, |
| 42 | } |
| 43 | |
| 44 | impl 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 | } |
| 538 | |