forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1380 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1380 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Generate Rust bindings for C and C++ libraries.
 | |
| //!
 | |
| //! Provide a C/C++ header file, receive Rust FFI code to call into C/C++
 | |
| //! functions and use types defined in the header.
 | |
| //!
 | |
| //! See the [`Builder`](./struct.Builder.html) struct for usage.
 | |
| //!
 | |
| //! See the [Users Guide](https://rust-lang.github.io/rust-bindgen/) for
 | |
| //! additional documentation.
 | |
| #![deny(missing_docs)]
 | |
| #![deny(unused_extern_crates)]
 | |
| #![deny(clippy::disallowed_methods)]
 | |
| // To avoid rather annoying warnings when matching with CXCursor_xxx as a
 | |
| // constant.
 | |
| #![allow(non_upper_case_globals)]
 | |
| // `quote!` nests quite deeply.
 | |
| #![recursion_limit = "128"]
 | |
| 
 | |
| #[macro_use]
 | |
| extern crate bitflags;
 | |
| #[macro_use]
 | |
| extern crate lazy_static;
 | |
| #[macro_use]
 | |
| extern crate quote;
 | |
| 
 | |
| #[cfg(feature = "logging")]
 | |
| #[macro_use]
 | |
| extern crate log;
 | |
| 
 | |
| #[cfg(not(feature = "logging"))]
 | |
| #[macro_use]
 | |
| mod log_stubs;
 | |
| 
 | |
| #[macro_use]
 | |
| mod extra_assertions;
 | |
| 
 | |
| mod codegen;
 | |
| mod deps;
 | |
| mod options;
 | |
| mod time;
 | |
| 
 | |
| pub mod callbacks;
 | |
| 
 | |
| mod clang;
 | |
| #[cfg(feature = "experimental")]
 | |
| mod diagnostics;
 | |
| mod features;
 | |
| mod ir;
 | |
| mod parse;
 | |
| mod regex_set;
 | |
| 
 | |
| pub use codegen::{
 | |
|     AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
 | |
| };
 | |
| #[cfg(feature = "__cli")]
 | |
| pub use features::RUST_TARGET_STRINGS;
 | |
| pub use features::{RustTarget, LATEST_STABLE_RUST};
 | |
| pub use ir::annotations::FieldVisibilityKind;
 | |
| pub use ir::function::Abi;
 | |
| pub use regex_set::RegexSet;
 | |
| 
 | |
| use codegen::CodegenError;
 | |
| use features::RustFeatures;
 | |
| use ir::comment;
 | |
| use ir::context::{BindgenContext, ItemId};
 | |
| use ir::item::Item;
 | |
| use options::BindgenOptions;
 | |
| use parse::ParseError;
 | |
| 
 | |
| use std::borrow::Cow;
 | |
| use std::collections::hash_map::Entry;
 | |
| use std::env;
 | |
| use std::ffi::OsStr;
 | |
| use std::fs::{File, OpenOptions};
 | |
| use std::io::{self, Write};
 | |
| use std::path::{Path, PathBuf};
 | |
| use std::process::{Command, Stdio};
 | |
| use std::rc::Rc;
 | |
| use std::str::FromStr;
 | |
| 
 | |
| // Some convenient typedefs for a fast hash map and hash set.
 | |
| type HashMap<K, V> = rustc_hash::FxHashMap<K, V>;
 | |
| type HashSet<K> = rustc_hash::FxHashSet<K>;
 | |
| 
 | |
| /// Default prefix for the anon fields.
 | |
| pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
 | |
| 
 | |
| const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
 | |
| 
 | |
| fn file_is_cpp(name_file: &str) -> bool {
 | |
|     name_file.ends_with(".hpp") ||
 | |
|         name_file.ends_with(".hxx") ||
 | |
|         name_file.ends_with(".hh") ||
 | |
|         name_file.ends_with(".h++")
 | |
| }
 | |
| 
 | |
| fn args_are_cpp(clang_args: &[Box<str>]) -> bool {
 | |
|     for w in clang_args.windows(2) {
 | |
|         if w[0].as_ref() == "-xc++" || w[1].as_ref() == "-xc++" {
 | |
|             return true;
 | |
|         }
 | |
|         if w[0].as_ref() == "-x" && w[1].as_ref() == "c++" {
 | |
|             return true;
 | |
|         }
 | |
|         if w[0].as_ref() == "-include" && file_is_cpp(w[1].as_ref()) {
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     false
 | |
| }
 | |
| 
 | |
| bitflags! {
 | |
|     /// A type used to indicate which kind of items we have to generate.
 | |
|     #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
 | |
|     pub struct CodegenConfig: u32 {
 | |
|         /// Whether to generate functions.
 | |
|         const FUNCTIONS = 1 << 0;
 | |
|         /// Whether to generate types.
 | |
|         const TYPES = 1 << 1;
 | |
|         /// Whether to generate constants.
 | |
|         const VARS = 1 << 2;
 | |
|         /// Whether to generate methods.
 | |
|         const METHODS = 1 << 3;
 | |
|         /// Whether to generate constructors
 | |
|         const CONSTRUCTORS = 1 << 4;
 | |
|         /// Whether to generate destructors.
 | |
|         const DESTRUCTORS = 1 << 5;
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl CodegenConfig {
 | |
|     /// Returns true if functions should be generated.
 | |
|     pub fn functions(self) -> bool {
 | |
|         self.contains(CodegenConfig::FUNCTIONS)
 | |
|     }
 | |
| 
 | |
|     /// Returns true if types should be generated.
 | |
|     pub fn types(self) -> bool {
 | |
|         self.contains(CodegenConfig::TYPES)
 | |
|     }
 | |
| 
 | |
|     /// Returns true if constants should be generated.
 | |
|     pub fn vars(self) -> bool {
 | |
|         self.contains(CodegenConfig::VARS)
 | |
|     }
 | |
| 
 | |
|     /// Returns true if methds should be generated.
 | |
|     pub fn methods(self) -> bool {
 | |
|         self.contains(CodegenConfig::METHODS)
 | |
|     }
 | |
| 
 | |
|     /// Returns true if constructors should be generated.
 | |
|     pub fn constructors(self) -> bool {
 | |
|         self.contains(CodegenConfig::CONSTRUCTORS)
 | |
|     }
 | |
| 
 | |
|     /// Returns true if destructors should be generated.
 | |
|     pub fn destructors(self) -> bool {
 | |
|         self.contains(CodegenConfig::DESTRUCTORS)
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Default for CodegenConfig {
 | |
|     fn default() -> Self {
 | |
|         CodegenConfig::all()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Formatting tools that can be used to format the bindings
 | |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | |
| #[non_exhaustive]
 | |
| pub enum Formatter {
 | |
|     /// Do not format the bindings.
 | |
|     None,
 | |
|     /// Use `rustfmt` to format the bindings.
 | |
|     Rustfmt,
 | |
|     #[cfg(feature = "prettyplease")]
 | |
|     /// Use `prettyplease` to format the bindings.
 | |
|     Prettyplease,
 | |
| }
 | |
| 
 | |
| impl Default for Formatter {
 | |
|     fn default() -> Self {
 | |
|         Self::Rustfmt
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl FromStr for Formatter {
 | |
|     type Err = String;
 | |
| 
 | |
|     fn from_str(s: &str) -> Result<Self, Self::Err> {
 | |
|         match s {
 | |
|             "none" => Ok(Self::None),
 | |
|             "rustfmt" => Ok(Self::Rustfmt),
 | |
|             #[cfg(feature = "prettyplease")]
 | |
|             "prettyplease" => Ok(Self::Prettyplease),
 | |
|             _ => Err(format!("`{}` is not a valid formatter", s)),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl std::fmt::Display for Formatter {
 | |
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|         let s = match self {
 | |
|             Self::None => "none",
 | |
|             Self::Rustfmt => "rustfmt",
 | |
|             #[cfg(feature = "prettyplease")]
 | |
|             Self::Prettyplease => "prettyplease",
 | |
|         };
 | |
| 
 | |
|         s.fmt(f)
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Configure and generate Rust bindings for a C/C++ header.
 | |
| ///
 | |
| /// This is the main entry point to the library.
 | |
| ///
 | |
| /// ```ignore
 | |
| /// use bindgen::builder;
 | |
| ///
 | |
| /// // Configure and generate bindings.
 | |
| /// let bindings = builder().header("path/to/input/header")
 | |
| ///     .allowlist_type("SomeCoolClass")
 | |
| ///     .allowlist_function("do_some_cool_thing")
 | |
| ///     .generate()?;
 | |
| ///
 | |
| /// // Write the generated bindings to an output file.
 | |
| /// bindings.write_to_file("path/to/output.rs")?;
 | |
| /// ```
 | |
| ///
 | |
| /// # Enums
 | |
| ///
 | |
| /// Bindgen can map C/C++ enums into Rust in different ways. The way bindgen maps enums depends on
 | |
| /// the pattern passed to several methods:
 | |
| ///
 | |
| /// 1. [`constified_enum_module()`](#method.constified_enum_module)
 | |
| /// 2. [`bitfield_enum()`](#method.bitfield_enum)
 | |
| /// 3. [`newtype_enum()`](#method.newtype_enum)
 | |
| /// 4. [`rustified_enum()`](#method.rustified_enum)
 | |
| ///
 | |
| /// For each C enum, bindgen tries to match the pattern in the following order:
 | |
| ///
 | |
| /// 1. Constified enum module
 | |
| /// 2. Bitfield enum
 | |
| /// 3. Newtype enum
 | |
| /// 4. Rustified enum
 | |
| ///
 | |
| /// If none of the above patterns match, then bindgen will generate a set of Rust constants.
 | |
| ///
 | |
| /// # Clang arguments
 | |
| ///
 | |
| /// Extra arguments can be passed to with clang:
 | |
| /// 1. [`clang_arg()`](#method.clang_arg): takes a single argument
 | |
| /// 2. [`clang_args()`](#method.clang_args): takes an iterator of arguments
 | |
| /// 3. `BINDGEN_EXTRA_CLANG_ARGS` environment variable: whitespace separate
 | |
| ///    environment variable of arguments
 | |
| ///
 | |
| /// Clang arguments specific to your crate should be added via the
 | |
| /// `clang_arg()`/`clang_args()` methods.
 | |
| ///
 | |
| /// End-users of the crate may need to set the `BINDGEN_EXTRA_CLANG_ARGS` environment variable to
 | |
| /// add additional arguments. For example, to build against a different sysroot a user could set
 | |
| /// `BINDGEN_EXTRA_CLANG_ARGS` to `--sysroot=/path/to/sysroot`.
 | |
| ///
 | |
| /// # Regular expression arguments
 | |
| ///
 | |
| /// Some [`Builder`] methods, such as `allowlist_*` and `blocklist_*`, allow regular
 | |
| /// expressions as arguments. These regular expressions will be enclosed in parentheses and
 | |
| /// anchored with `^` and `$`. So, if the argument passed is `<regex>`, the regular expression to be
 | |
| /// stored will be `^(<regex>)$`.
 | |
| ///
 | |
| /// As a consequence, regular expressions passed to `bindgen` will try to match the whole name of
 | |
| /// an item instead of a section of it, which means that to match any items with the prefix
 | |
| /// `prefix`, the `prefix.*` regular expression must be used.
 | |
| ///
 | |
| /// Certain methods, like [`Builder::allowlist_function`], use regular expressions over function
 | |
| /// names. To match C++ methods, prefix the name of the type where they belong, followed by an
 | |
| /// underscore. So, if the type `Foo` has a method `bar`, it can be matched with the `Foo_bar`
 | |
| /// regular expression.
 | |
| ///
 | |
| /// Additionally, Objective-C interfaces can be matched by prefixing the regular expression with
 | |
| /// `I`. For example, the `IFoo` regular expression matches the `Foo` interface, and the `IFoo_foo`
 | |
| /// regular expression matches the `foo` method of the `Foo` interface.
 | |
| ///
 | |
| /// Releases of `bindgen` with a version lesser or equal to `0.62.0` used to accept the wildcard
 | |
| /// pattern `*` as a valid regular expression. This behavior has been deprecated, and the `.*`
 | |
| /// regular expression must be used instead.
 | |
| #[derive(Debug, Default, Clone)]
 | |
| pub struct Builder {
 | |
|     options: BindgenOptions,
 | |
| }
 | |
| 
 | |
| /// Construct a new [`Builder`](./struct.Builder.html).
 | |
| pub fn builder() -> Builder {
 | |
|     Default::default()
 | |
| }
 | |
| 
 | |
| fn get_extra_clang_args(
 | |
|     parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
 | |
| ) -> Vec<String> {
 | |
|     // Add any extra arguments from the environment to the clang command line.
 | |
|     let extra_clang_args = match get_target_dependent_env_var(
 | |
|         parse_callbacks,
 | |
|         "BINDGEN_EXTRA_CLANG_ARGS",
 | |
|     ) {
 | |
|         None => return vec![],
 | |
|         Some(s) => s,
 | |
|     };
 | |
| 
 | |
|     // Try to parse it with shell quoting. If we fail, make it one single big argument.
 | |
|     if let Some(strings) = shlex::split(&extra_clang_args) {
 | |
|         return strings;
 | |
|     }
 | |
|     vec![extra_clang_args]
 | |
| }
 | |
| 
 | |
| impl Builder {
 | |
|     /// Generate the Rust bindings using the options built up thus far.
 | |
|     pub fn generate(mut self) -> Result<Bindings, BindgenError> {
 | |
|         // Add any extra arguments from the environment to the clang command line.
 | |
|         self.options.clang_args.extend(
 | |
|             get_extra_clang_args(&self.options.parse_callbacks)
 | |
|                 .into_iter()
 | |
|                 .map(String::into_boxed_str),
 | |
|         );
 | |
| 
 | |
|         for header in &self.options.input_headers {
 | |
|             self.options
 | |
|                 .for_each_callback(|cb| cb.header_file(header.as_ref()));
 | |
|         }
 | |
| 
 | |
|         // Transform input headers to arguments on the clang command line.
 | |
|         self.options.clang_args.extend(
 | |
|             self.options.input_headers
 | |
|                 [..self.options.input_headers.len().saturating_sub(1)]
 | |
|                 .iter()
 | |
|                 .flat_map(|header| ["-include".into(), header.clone()]),
 | |
|         );
 | |
| 
 | |
|         let input_unsaved_files =
 | |
|             std::mem::take(&mut self.options.input_header_contents)
 | |
|                 .into_iter()
 | |
|                 .map(|(name, contents)| {
 | |
|                     clang::UnsavedFile::new(name.as_ref(), contents.as_ref())
 | |
|                 })
 | |
|                 .collect::<Vec<_>>();
 | |
| 
 | |
|         Bindings::generate(self.options, input_unsaved_files)
 | |
|     }
 | |
| 
 | |
|     /// Preprocess and dump the input header files to disk.
 | |
|     ///
 | |
|     /// This is useful when debugging bindgen, using C-Reduce, or when filing
 | |
|     /// issues. The resulting file will be named something like `__bindgen.i` or
 | |
|     /// `__bindgen.ii`
 | |
|     pub fn dump_preprocessed_input(&self) -> io::Result<()> {
 | |
|         let clang =
 | |
|             clang_sys::support::Clang::find(None, &[]).ok_or_else(|| {
 | |
|                 io::Error::new(
 | |
|                     io::ErrorKind::Other,
 | |
|                     "Cannot find clang executable",
 | |
|                 )
 | |
|             })?;
 | |
| 
 | |
|         // The contents of a wrapper file that includes all the input header
 | |
|         // files.
 | |
|         let mut wrapper_contents = String::new();
 | |
| 
 | |
|         // Whether we are working with C or C++ inputs.
 | |
|         let mut is_cpp = args_are_cpp(&self.options.clang_args);
 | |
| 
 | |
|         // For each input header, add `#include "$header"`.
 | |
|         for header in &self.options.input_headers {
 | |
|             is_cpp |= file_is_cpp(header);
 | |
| 
 | |
|             wrapper_contents.push_str("#include \"");
 | |
|             wrapper_contents.push_str(header);
 | |
|             wrapper_contents.push_str("\"\n");
 | |
|         }
 | |
| 
 | |
|         // For each input header content, add a prefix line of `#line 0 "$name"`
 | |
|         // followed by the contents.
 | |
|         for (name, contents) in &self.options.input_header_contents {
 | |
|             is_cpp |= file_is_cpp(name);
 | |
| 
 | |
|             wrapper_contents.push_str("#line 0 \"");
 | |
|             wrapper_contents.push_str(name);
 | |
|             wrapper_contents.push_str("\"\n");
 | |
|             wrapper_contents.push_str(contents);
 | |
|         }
 | |
| 
 | |
|         let wrapper_path = PathBuf::from(if is_cpp {
 | |
|             "__bindgen.cpp"
 | |
|         } else {
 | |
|             "__bindgen.c"
 | |
|         });
 | |
| 
 | |
|         {
 | |
|             let mut wrapper_file = File::create(&wrapper_path)?;
 | |
|             wrapper_file.write_all(wrapper_contents.as_bytes())?;
 | |
|         }
 | |
| 
 | |
|         let mut cmd = Command::new(clang.path);
 | |
|         cmd.arg("-save-temps")
 | |
|             .arg("-E")
 | |
|             .arg("-C")
 | |
|             .arg("-c")
 | |
|             .arg(&wrapper_path)
 | |
|             .stdout(Stdio::piped());
 | |
| 
 | |
|         for a in &self.options.clang_args {
 | |
|             cmd.arg(a.as_ref());
 | |
|         }
 | |
| 
 | |
|         for a in get_extra_clang_args(&self.options.parse_callbacks) {
 | |
|             cmd.arg(a);
 | |
|         }
 | |
| 
 | |
|         let mut child = cmd.spawn()?;
 | |
| 
 | |
|         let mut preprocessed = child.stdout.take().unwrap();
 | |
|         let mut file = File::create(if is_cpp {
 | |
|             "__bindgen.ii"
 | |
|         } else {
 | |
|             "__bindgen.i"
 | |
|         })?;
 | |
|         io::copy(&mut preprocessed, &mut file)?;
 | |
| 
 | |
|         if child.wait()?.success() {
 | |
|             Ok(())
 | |
|         } else {
 | |
|             Err(io::Error::new(
 | |
|                 io::ErrorKind::Other,
 | |
|                 "clang exited with non-zero status",
 | |
|             ))
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl BindgenOptions {
 | |
|     fn build(&mut self) {
 | |
|         const REGEX_SETS_LEN: usize = 29;
 | |
| 
 | |
|         let regex_sets: [_; REGEX_SETS_LEN] = [
 | |
|             &mut self.blocklisted_types,
 | |
|             &mut self.blocklisted_functions,
 | |
|             &mut self.blocklisted_items,
 | |
|             &mut self.blocklisted_files,
 | |
|             &mut self.blocklisted_vars,
 | |
|             &mut self.opaque_types,
 | |
|             &mut self.allowlisted_vars,
 | |
|             &mut self.allowlisted_types,
 | |
|             &mut self.allowlisted_functions,
 | |
|             &mut self.allowlisted_files,
 | |
|             &mut self.allowlisted_items,
 | |
|             &mut self.bitfield_enums,
 | |
|             &mut self.constified_enums,
 | |
|             &mut self.constified_enum_modules,
 | |
|             &mut self.newtype_enums,
 | |
|             &mut self.newtype_global_enums,
 | |
|             &mut self.rustified_enums,
 | |
|             &mut self.rustified_non_exhaustive_enums,
 | |
|             &mut self.type_alias,
 | |
|             &mut self.new_type_alias,
 | |
|             &mut self.new_type_alias_deref,
 | |
|             &mut self.bindgen_wrapper_union,
 | |
|             &mut self.manually_drop_union,
 | |
|             &mut self.no_partialeq_types,
 | |
|             &mut self.no_copy_types,
 | |
|             &mut self.no_debug_types,
 | |
|             &mut self.no_default_types,
 | |
|             &mut self.no_hash_types,
 | |
|             &mut self.must_use_types,
 | |
|         ];
 | |
| 
 | |
|         let record_matches = self.record_matches;
 | |
|         #[cfg(feature = "experimental")]
 | |
|         {
 | |
|             let sets_len = REGEX_SETS_LEN + self.abi_overrides.len();
 | |
|             let names = if self.emit_diagnostics {
 | |
|                 <[&str; REGEX_SETS_LEN]>::into_iter([
 | |
|                     "--blocklist-type",
 | |
|                     "--blocklist-function",
 | |
|                     "--blocklist-item",
 | |
|                     "--blocklist-file",
 | |
|                     "--blocklist-var",
 | |
|                     "--opaque-type",
 | |
|                     "--allowlist-type",
 | |
|                     "--allowlist-function",
 | |
|                     "--allowlist-var",
 | |
|                     "--allowlist-file",
 | |
|                     "--allowlist-item",
 | |
|                     "--bitfield-enum",
 | |
|                     "--newtype-enum",
 | |
|                     "--newtype-global-enum",
 | |
|                     "--rustified-enum",
 | |
|                     "--rustified-enum-non-exhaustive",
 | |
|                     "--constified-enum-module",
 | |
|                     "--constified-enum",
 | |
|                     "--type-alias",
 | |
|                     "--new-type-alias",
 | |
|                     "--new-type-alias-deref",
 | |
|                     "--bindgen-wrapper-union",
 | |
|                     "--manually-drop-union",
 | |
|                     "--no-partialeq",
 | |
|                     "--no-copy",
 | |
|                     "--no-debug",
 | |
|                     "--no-default",
 | |
|                     "--no-hash",
 | |
|                     "--must-use",
 | |
|                 ])
 | |
|                 .chain((0..self.abi_overrides.len()).map(|_| "--override-abi"))
 | |
|                 .map(Some)
 | |
|                 .collect()
 | |
|             } else {
 | |
|                 vec![None; sets_len]
 | |
|             };
 | |
| 
 | |
|             for (regex_set, name) in
 | |
|                 self.abi_overrides.values_mut().chain(regex_sets).zip(names)
 | |
|             {
 | |
|                 regex_set.build_with_diagnostics(record_matches, name);
 | |
|             }
 | |
|         }
 | |
|         #[cfg(not(feature = "experimental"))]
 | |
|         for regex_set in self.abi_overrides.values_mut().chain(regex_sets) {
 | |
|             regex_set.build(record_matches);
 | |
|         }
 | |
| 
 | |
|         let rust_target = self.rust_target;
 | |
|         #[allow(deprecated)]
 | |
|         if rust_target <= RustTarget::Stable_1_30 {
 | |
|             deprecated_target_diagnostic(rust_target, self);
 | |
|         }
 | |
| 
 | |
|         // Disable `untagged_union` if the target does not support it.
 | |
|         if !self.rust_features.untagged_union {
 | |
|             self.untagged_union = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// Update rust target version
 | |
|     pub fn set_rust_target(&mut self, rust_target: RustTarget) {
 | |
|         self.rust_target = rust_target;
 | |
| 
 | |
|         // Keep rust_features synced with rust_target
 | |
|         self.rust_features = rust_target.into();
 | |
|     }
 | |
| 
 | |
|     /// Get features supported by target Rust version
 | |
|     pub fn rust_features(&self) -> RustFeatures {
 | |
|         self.rust_features
 | |
|     }
 | |
| 
 | |
|     fn last_callback<T>(
 | |
|         &self,
 | |
|         f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>,
 | |
|     ) -> Option<T> {
 | |
|         self.parse_callbacks
 | |
|             .iter()
 | |
|             .filter_map(|cb| f(cb.as_ref()))
 | |
|             .last()
 | |
|     }
 | |
| 
 | |
|     fn all_callbacks<T>(
 | |
|         &self,
 | |
|         f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>,
 | |
|     ) -> Vec<T> {
 | |
|         self.parse_callbacks
 | |
|             .iter()
 | |
|             .flat_map(|cb| f(cb.as_ref()))
 | |
|             .collect()
 | |
|     }
 | |
| 
 | |
|     fn for_each_callback(&self, f: impl Fn(&dyn callbacks::ParseCallbacks)) {
 | |
|         self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref()));
 | |
|     }
 | |
| 
 | |
|     fn process_comment(&self, comment: &str) -> String {
 | |
|         let comment = comment::preprocess(comment);
 | |
|         self.parse_callbacks
 | |
|             .last()
 | |
|             .and_then(|cb| cb.process_comment(&comment))
 | |
|             .unwrap_or(comment)
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn deprecated_target_diagnostic(target: RustTarget, _options: &BindgenOptions) {
 | |
|     warn!("The {} Rust target is deprecated. If you have a need to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", target);
 | |
| 
 | |
|     #[cfg(feature = "experimental")]
 | |
|     if _options.emit_diagnostics {
 | |
|         use crate::diagnostics::{Diagnostic, Level};
 | |
| 
 | |
|         let mut diagnostic = Diagnostic::default();
 | |
|         diagnostic.with_title(
 | |
|             format!("The {} Rust target is deprecated.", target),
 | |
|             Level::Warn,
 | |
|         );
 | |
|         diagnostic.add_annotation(
 | |
|             "This Rust target was passed to `--rust-target`",
 | |
|             Level::Info,
 | |
|         );
 | |
|         diagnostic.add_annotation("If you have a good reason to use this target please report it at https://github.com/rust-lang/rust-bindgen/issues", Level::Help);
 | |
|         diagnostic.display();
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "runtime")]
 | |
| fn ensure_libclang_is_loaded() {
 | |
|     if clang_sys::is_loaded() {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // XXX (issue #350): Ensure that our dynamically loaded `libclang`
 | |
|     // doesn't get dropped prematurely, nor is loaded multiple times
 | |
|     // across different threads.
 | |
| 
 | |
|     lazy_static! {
 | |
|         static ref LIBCLANG: std::sync::Arc<clang_sys::SharedLibrary> = {
 | |
|             clang_sys::load().expect("Unable to find libclang");
 | |
|             clang_sys::get_library().expect(
 | |
|                 "We just loaded libclang and it had better still be \
 | |
|                  here!",
 | |
|             )
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     clang_sys::set_library(Some(LIBCLANG.clone()));
 | |
| }
 | |
| 
 | |
| #[cfg(not(feature = "runtime"))]
 | |
| fn ensure_libclang_is_loaded() {}
 | |
| 
 | |
| /// Error type for rust-bindgen.
 | |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | |
| #[non_exhaustive]
 | |
| pub enum BindgenError {
 | |
|     /// The header was a folder.
 | |
|     FolderAsHeader(PathBuf),
 | |
|     /// Permissions to read the header is insufficient.
 | |
|     InsufficientPermissions(PathBuf),
 | |
|     /// The header does not exist.
 | |
|     NotExist(PathBuf),
 | |
|     /// Clang diagnosed an error.
 | |
|     ClangDiagnostic(String),
 | |
|     /// Code generation reported an error.
 | |
|     Codegen(CodegenError),
 | |
| }
 | |
| 
 | |
| impl std::fmt::Display for BindgenError {
 | |
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|         match self {
 | |
|             BindgenError::FolderAsHeader(h) => {
 | |
|                 write!(f, "'{}' is a folder", h.display())
 | |
|             }
 | |
|             BindgenError::InsufficientPermissions(h) => {
 | |
|                 write!(f, "insufficient permissions to read '{}'", h.display())
 | |
|             }
 | |
|             BindgenError::NotExist(h) => {
 | |
|                 write!(f, "header '{}' does not exist.", h.display())
 | |
|             }
 | |
|             BindgenError::ClangDiagnostic(message) => {
 | |
|                 write!(f, "clang diagnosed error: {}", message)
 | |
|             }
 | |
|             BindgenError::Codegen(err) => {
 | |
|                 write!(f, "codegen error: {}", err)
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl std::error::Error for BindgenError {}
 | |
| 
 | |
| /// Generated Rust bindings.
 | |
| #[derive(Debug)]
 | |
| pub struct Bindings {
 | |
|     options: BindgenOptions,
 | |
|     module: proc_macro2::TokenStream,
 | |
| }
 | |
| 
 | |
| pub(crate) const HOST_TARGET: &str =
 | |
|     include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
 | |
| 
 | |
| // Some architecture triplets are different between rust and libclang, see #1211
 | |
| // and duplicates.
 | |
| fn rust_to_clang_target(rust_target: &str) -> Box<str> {
 | |
|     if rust_target.starts_with("aarch64-apple-") {
 | |
|         let mut clang_target = "arm64-apple-".to_owned();
 | |
|         clang_target
 | |
|             .push_str(rust_target.strip_prefix("aarch64-apple-").unwrap());
 | |
|         return clang_target.into();
 | |
|     } else if rust_target.starts_with("riscv64gc-") {
 | |
|         let mut clang_target = "riscv64-".to_owned();
 | |
|         clang_target.push_str(rust_target.strip_prefix("riscv64gc-").unwrap());
 | |
|         return clang_target.into();
 | |
|     } else if rust_target.ends_with("-espidf") {
 | |
|         let mut clang_target =
 | |
|             rust_target.strip_suffix("-espidf").unwrap().to_owned();
 | |
|         clang_target.push_str("-elf");
 | |
|         if clang_target.starts_with("riscv32imc-") {
 | |
|             clang_target = "riscv32-".to_owned() +
 | |
|                 clang_target.strip_prefix("riscv32imc-").unwrap();
 | |
|         }
 | |
|         return clang_target.into();
 | |
|     } else if rust_target.starts_with("riscv32imc-") {
 | |
|         let mut clang_target = "riscv32-".to_owned();
 | |
|         clang_target.push_str(rust_target.strip_prefix("riscv32imc-").unwrap());
 | |
|         return clang_target.into();
 | |
|     } else if rust_target.starts_with("riscv32imac-") {
 | |
|         let mut clang_target = "riscv32-".to_owned();
 | |
|         clang_target
 | |
|             .push_str(rust_target.strip_prefix("riscv32imac-").unwrap());
 | |
|         return clang_target.into();
 | |
|     }
 | |
|     rust_target.into()
 | |
| }
 | |
| 
 | |
| /// Returns the effective target, and whether it was explicitly specified on the
 | |
| /// clang flags.
 | |
| fn find_effective_target(clang_args: &[Box<str>]) -> (Box<str>, bool) {
 | |
|     let mut args = clang_args.iter();
 | |
|     while let Some(opt) = args.next() {
 | |
|         if opt.starts_with("--target=") {
 | |
|             let mut split = opt.split('=');
 | |
|             split.next();
 | |
|             return (split.next().unwrap().into(), true);
 | |
|         }
 | |
| 
 | |
|         if opt.as_ref() == "-target" {
 | |
|             if let Some(target) = args.next() {
 | |
|                 return (target.clone(), true);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // If we're running from a build script, try to find the cargo target.
 | |
|     if let Ok(t) = env::var("TARGET") {
 | |
|         return (rust_to_clang_target(&t), false);
 | |
|     }
 | |
| 
 | |
|     (rust_to_clang_target(HOST_TARGET), false)
 | |
| }
 | |
| 
 | |
| impl Bindings {
 | |
|     /// Generate bindings for the given options.
 | |
|     pub(crate) fn generate(
 | |
|         mut options: BindgenOptions,
 | |
|         input_unsaved_files: Vec<clang::UnsavedFile>,
 | |
|     ) -> Result<Bindings, BindgenError> {
 | |
|         ensure_libclang_is_loaded();
 | |
| 
 | |
|         #[cfg(feature = "runtime")]
 | |
|         debug!(
 | |
|             "Generating bindings, libclang at {}",
 | |
|             clang_sys::get_library().unwrap().path().display()
 | |
|         );
 | |
|         #[cfg(not(feature = "runtime"))]
 | |
|         debug!("Generating bindings, libclang linked");
 | |
| 
 | |
|         options.build();
 | |
| 
 | |
|         let (effective_target, explicit_target) =
 | |
|             find_effective_target(&options.clang_args);
 | |
| 
 | |
|         let is_host_build =
 | |
|             rust_to_clang_target(HOST_TARGET) == effective_target;
 | |
| 
 | |
|         // NOTE: The is_host_build check wouldn't be sound normally in some
 | |
|         // cases if we were to call a binary (if you have a 32-bit clang and are
 | |
|         // building on a 64-bit system for example).  But since we rely on
 | |
|         // opening libclang.so, it has to be the same architecture and thus the
 | |
|         // check is fine.
 | |
|         if !explicit_target && !is_host_build {
 | |
|             options.clang_args.insert(
 | |
|                 0,
 | |
|                 format!("--target={}", effective_target).into_boxed_str(),
 | |
|             );
 | |
|         };
 | |
| 
 | |
|         fn detect_include_paths(options: &mut BindgenOptions) {
 | |
|             if !options.detect_include_paths {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // Filter out include paths and similar stuff, so we don't incorrectly
 | |
|             // promote them to `-isystem`.
 | |
|             let clang_args_for_clang_sys = {
 | |
|                 let mut last_was_include_prefix = false;
 | |
|                 options
 | |
|                     .clang_args
 | |
|                     .iter()
 | |
|                     .filter(|arg| {
 | |
|                         if last_was_include_prefix {
 | |
|                             last_was_include_prefix = false;
 | |
|                             return false;
 | |
|                         }
 | |
| 
 | |
|                         let arg = arg.as_ref();
 | |
| 
 | |
|                         // https://clang.llvm.org/docs/ClangCommandLineReference.html
 | |
|                         // -isystem and -isystem-after are harmless.
 | |
|                         if arg == "-I" || arg == "--include-directory" {
 | |
|                             last_was_include_prefix = true;
 | |
|                             return false;
 | |
|                         }
 | |
| 
 | |
|                         if arg.starts_with("-I") ||
 | |
|                             arg.starts_with("--include-directory=")
 | |
|                         {
 | |
|                             return false;
 | |
|                         }
 | |
| 
 | |
|                         true
 | |
|                     })
 | |
|                     .map(|arg| arg.clone().into())
 | |
|                     .collect::<Vec<_>>()
 | |
|             };
 | |
| 
 | |
|             debug!(
 | |
|                 "Trying to find clang with flags: {:?}",
 | |
|                 clang_args_for_clang_sys
 | |
|             );
 | |
| 
 | |
|             let clang = match clang_sys::support::Clang::find(
 | |
|                 None,
 | |
|                 &clang_args_for_clang_sys,
 | |
|             ) {
 | |
|                 None => return,
 | |
|                 Some(clang) => clang,
 | |
|             };
 | |
| 
 | |
|             debug!("Found clang: {:?}", clang);
 | |
| 
 | |
|             // Whether we are working with C or C++ inputs.
 | |
|             let is_cpp = args_are_cpp(&options.clang_args) ||
 | |
|                 options.input_headers.iter().any(|h| file_is_cpp(h));
 | |
| 
 | |
|             let search_paths = if is_cpp {
 | |
|                 clang.cpp_search_paths
 | |
|             } else {
 | |
|                 clang.c_search_paths
 | |
|             };
 | |
| 
 | |
|             if let Some(search_paths) = search_paths {
 | |
|                 for path in search_paths.into_iter() {
 | |
|                     if let Ok(path) = path.into_os_string().into_string() {
 | |
|                         options.clang_args.push("-isystem".into());
 | |
|                         options.clang_args.push(path.into_boxed_str());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         detect_include_paths(&mut options);
 | |
| 
 | |
|         #[cfg(unix)]
 | |
|         fn can_read(perms: &std::fs::Permissions) -> bool {
 | |
|             use std::os::unix::fs::PermissionsExt;
 | |
|             perms.mode() & 0o444 > 0
 | |
|         }
 | |
| 
 | |
|         #[cfg(not(unix))]
 | |
|         fn can_read(_: &std::fs::Permissions) -> bool {
 | |
|             true
 | |
|         }
 | |
| 
 | |
|         if let Some(h) = options.input_headers.last() {
 | |
|             let path = Path::new(h.as_ref());
 | |
|             if let Ok(md) = std::fs::metadata(path) {
 | |
|                 if md.is_dir() {
 | |
|                     return Err(BindgenError::FolderAsHeader(path.into()));
 | |
|                 }
 | |
|                 if !can_read(&md.permissions()) {
 | |
|                     return Err(BindgenError::InsufficientPermissions(
 | |
|                         path.into(),
 | |
|                     ));
 | |
|                 }
 | |
|                 options.clang_args.push(h.clone());
 | |
|             } else {
 | |
|                 return Err(BindgenError::NotExist(path.into()));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         for (idx, f) in input_unsaved_files.iter().enumerate() {
 | |
|             if idx != 0 || !options.input_headers.is_empty() {
 | |
|                 options.clang_args.push("-include".into());
 | |
|             }
 | |
|             options.clang_args.push(f.name.to_str().unwrap().into())
 | |
|         }
 | |
| 
 | |
|         debug!("Fixed-up options: {:?}", options);
 | |
| 
 | |
|         let time_phases = options.time_phases;
 | |
|         let mut context = BindgenContext::new(options, &input_unsaved_files);
 | |
| 
 | |
|         if is_host_build {
 | |
|             debug_assert_eq!(
 | |
|                 context.target_pointer_size(),
 | |
|                 std::mem::size_of::<*mut ()>(),
 | |
|                 "{:?} {:?}",
 | |
|                 effective_target,
 | |
|                 HOST_TARGET
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         {
 | |
|             let _t = time::Timer::new("parse").with_output(time_phases);
 | |
|             parse(&mut context)?;
 | |
|         }
 | |
| 
 | |
|         let (module, options) =
 | |
|             codegen::codegen(context).map_err(BindgenError::Codegen)?;
 | |
| 
 | |
|         Ok(Bindings { options, module })
 | |
|     }
 | |
| 
 | |
|     /// Write these bindings as source text to a file.
 | |
|     pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
 | |
|         let file = OpenOptions::new()
 | |
|             .write(true)
 | |
|             .truncate(true)
 | |
|             .create(true)
 | |
|             .open(path.as_ref())?;
 | |
|         self.write(Box::new(file))?;
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Write these bindings as source text to the given `Write`able.
 | |
|     pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> {
 | |
|         const NL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
 | |
| 
 | |
|         if !self.options.disable_header_comment {
 | |
|             let version =
 | |
|                 option_env!("CARGO_PKG_VERSION").unwrap_or("(unknown version)");
 | |
|             writeln!(
 | |
|                 writer,
 | |
|                 "/* automatically generated by rust-bindgen {version} */{NL}",
 | |
|             )?;
 | |
|         }
 | |
| 
 | |
|         for line in self.options.raw_lines.iter() {
 | |
|             writer.write_all(line.as_bytes())?;
 | |
|             writer.write_all(NL.as_bytes())?;
 | |
|         }
 | |
| 
 | |
|         if !self.options.raw_lines.is_empty() {
 | |
|             writer.write_all(NL.as_bytes())?;
 | |
|         }
 | |
| 
 | |
|         match self.format_tokens(&self.module) {
 | |
|             Ok(formatted_bindings) => {
 | |
|                 writer.write_all(formatted_bindings.as_bytes())?;
 | |
|             }
 | |
|             Err(err) => {
 | |
|                 eprintln!(
 | |
|                     "Failed to run rustfmt: {} (non-fatal, continuing)",
 | |
|                     err
 | |
|                 );
 | |
|                 writer.write_all(self.module.to_string().as_bytes())?;
 | |
|             }
 | |
|         }
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Gets the rustfmt path to rustfmt the generated bindings.
 | |
|     fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> {
 | |
|         debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt));
 | |
|         if let Some(ref p) = self.options.rustfmt_path {
 | |
|             return Ok(Cow::Borrowed(p));
 | |
|         }
 | |
|         if let Ok(rustfmt) = env::var("RUSTFMT") {
 | |
|             return Ok(Cow::Owned(rustfmt.into()));
 | |
|         }
 | |
|         #[cfg(feature = "which-rustfmt")]
 | |
|         match which::which("rustfmt") {
 | |
|             Ok(p) => Ok(Cow::Owned(p)),
 | |
|             Err(e) => {
 | |
|                 Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)))
 | |
|             }
 | |
|         }
 | |
|         #[cfg(not(feature = "which-rustfmt"))]
 | |
|         // No rustfmt binary was specified, so assume that the binary is called
 | |
|         // "rustfmt" and that it is in the user's PATH.
 | |
|         Ok(Cow::Owned("rustfmt".into()))
 | |
|     }
 | |
| 
 | |
|     /// Formats a token stream with the formatter set up in `BindgenOptions`.
 | |
|     fn format_tokens(
 | |
|         &self,
 | |
|         tokens: &proc_macro2::TokenStream,
 | |
|     ) -> io::Result<String> {
 | |
|         let _t = time::Timer::new("rustfmt_generated_string")
 | |
|             .with_output(self.options.time_phases);
 | |
| 
 | |
|         match self.options.formatter {
 | |
|             Formatter::None => return Ok(tokens.to_string()),
 | |
|             #[cfg(feature = "prettyplease")]
 | |
|             Formatter::Prettyplease => {
 | |
|                 return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens)));
 | |
|             }
 | |
|             Formatter::Rustfmt => (),
 | |
|         }
 | |
| 
 | |
|         let rustfmt = self.rustfmt_path()?;
 | |
|         let mut cmd = Command::new(&*rustfmt);
 | |
| 
 | |
|         cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
 | |
| 
 | |
|         if let Some(path) = self
 | |
|             .options
 | |
|             .rustfmt_configuration_file
 | |
|             .as_ref()
 | |
|             .and_then(|f| f.to_str())
 | |
|         {
 | |
|             cmd.args(["--config-path", path]);
 | |
|         }
 | |
| 
 | |
|         let mut child = cmd.spawn()?;
 | |
|         let mut child_stdin = child.stdin.take().unwrap();
 | |
|         let mut child_stdout = child.stdout.take().unwrap();
 | |
| 
 | |
|         let source = tokens.to_string();
 | |
| 
 | |
|         // Write to stdin in a new thread, so that we can read from stdout on this
 | |
|         // thread. This keeps the child from blocking on writing to its stdout which
 | |
|         // might block us from writing to its stdin.
 | |
|         let stdin_handle = ::std::thread::spawn(move || {
 | |
|             let _ = child_stdin.write_all(source.as_bytes());
 | |
|             source
 | |
|         });
 | |
| 
 | |
|         let mut output = vec![];
 | |
|         io::copy(&mut child_stdout, &mut output)?;
 | |
| 
 | |
|         let status = child.wait()?;
 | |
|         let source = stdin_handle.join().expect(
 | |
|             "The thread writing to rustfmt's stdin doesn't do \
 | |
|              anything that could panic",
 | |
|         );
 | |
| 
 | |
|         match String::from_utf8(output) {
 | |
|             Ok(bindings) => match status.code() {
 | |
|                 Some(0) => Ok(bindings),
 | |
|                 Some(2) => Err(io::Error::new(
 | |
|                     io::ErrorKind::Other,
 | |
|                     "Rustfmt parsing errors.".to_string(),
 | |
|                 )),
 | |
|                 Some(3) => {
 | |
|                     rustfmt_non_fatal_error_diagnostic(
 | |
|                         "Rustfmt could not format some lines",
 | |
|                         &self.options,
 | |
|                     );
 | |
|                     Ok(bindings)
 | |
|                 }
 | |
|                 _ => Err(io::Error::new(
 | |
|                     io::ErrorKind::Other,
 | |
|                     "Internal rustfmt error".to_string(),
 | |
|                 )),
 | |
|             },
 | |
|             _ => Ok(source),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn rustfmt_non_fatal_error_diagnostic(msg: &str, _options: &BindgenOptions) {
 | |
|     warn!("{}", msg);
 | |
| 
 | |
|     #[cfg(feature = "experimental")]
 | |
|     if _options.emit_diagnostics {
 | |
|         use crate::diagnostics::{Diagnostic, Level};
 | |
| 
 | |
|         Diagnostic::default()
 | |
|             .with_title(msg, Level::Warn)
 | |
|             .add_annotation(
 | |
|                 "The bindings will be generated but not formatted.",
 | |
|                 Level::Note,
 | |
|             )
 | |
|             .display();
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl std::fmt::Display for Bindings {
 | |
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|         let mut bytes = vec![];
 | |
|         self.write(Box::new(&mut bytes) as Box<dyn Write>)
 | |
|             .expect("writing to a vec cannot fail");
 | |
|         f.write_str(
 | |
|             std::str::from_utf8(&bytes)
 | |
|                 .expect("we should only write bindings that are valid utf-8"),
 | |
|         )
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Determines whether the given cursor is in any of the files matched by the
 | |
| /// options.
 | |
| fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
 | |
|     ctx.options().builtins || !cursor.is_builtin()
 | |
| }
 | |
| 
 | |
| /// Parse one `Item` from the Clang cursor.
 | |
| fn parse_one(
 | |
|     ctx: &mut BindgenContext,
 | |
|     cursor: clang::Cursor,
 | |
|     parent: Option<ItemId>,
 | |
| ) {
 | |
|     if !filter_builtins(ctx, &cursor) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     match Item::parse(cursor, parent, ctx) {
 | |
|         Ok(..) => {}
 | |
|         Err(ParseError::Continue) => {}
 | |
|         Err(ParseError::Recurse) => {
 | |
|             cursor
 | |
|                 .visit_sorted(ctx, |ctx, child| parse_one(ctx, child, parent));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Parse the Clang AST into our `Item` internal representation.
 | |
| fn parse(context: &mut BindgenContext) -> Result<(), BindgenError> {
 | |
|     use clang_sys::*;
 | |
| 
 | |
|     let mut error = None;
 | |
|     for d in context.translation_unit().diags().iter() {
 | |
|         let msg = d.format();
 | |
|         let is_err = d.severity() >= CXDiagnostic_Error;
 | |
|         if is_err {
 | |
|             let error = error.get_or_insert_with(String::new);
 | |
|             error.push_str(&msg);
 | |
|             error.push('\n');
 | |
|         } else {
 | |
|             eprintln!("clang diag: {}", msg);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if let Some(message) = error {
 | |
|         return Err(BindgenError::ClangDiagnostic(message));
 | |
|     }
 | |
| 
 | |
|     let cursor = context.translation_unit().cursor();
 | |
| 
 | |
|     if context.options().emit_ast {
 | |
|         fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult {
 | |
|             if !cur.is_builtin() {
 | |
|                 clang::ast_dump(cur, 0)
 | |
|             } else {
 | |
|                 CXChildVisit_Continue
 | |
|             }
 | |
|         }
 | |
|         cursor.visit(|cur| dump_if_not_builtin(&cur));
 | |
|     }
 | |
| 
 | |
|     let root = context.root_module();
 | |
|     context.with_module(root, |ctx| {
 | |
|         cursor.visit_sorted(ctx, |ctx, child| parse_one(ctx, child, None))
 | |
|     });
 | |
| 
 | |
|     assert!(
 | |
|         context.current_module() == context.root_module(),
 | |
|         "How did this happen?"
 | |
|     );
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| /// Extracted Clang version data
 | |
| #[derive(Debug)]
 | |
| pub struct ClangVersion {
 | |
|     /// Major and minor semver, if parsing was successful
 | |
|     pub parsed: Option<(u32, u32)>,
 | |
|     /// full version string
 | |
|     pub full: String,
 | |
| }
 | |
| 
 | |
| /// Get the major and the minor semver numbers of Clang's version
 | |
| pub fn clang_version() -> ClangVersion {
 | |
|     ensure_libclang_is_loaded();
 | |
| 
 | |
|     //Debian clang version 11.0.1-2
 | |
|     let raw_v: String = clang::extract_clang_version();
 | |
|     let split_v: Option<Vec<&str>> = raw_v
 | |
|         .split_whitespace()
 | |
|         .find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit()))
 | |
|         .map(|v| v.split('.').collect());
 | |
|     if let Some(v) = split_v {
 | |
|         if v.len() >= 2 {
 | |
|             let maybe_major = v[0].parse::<u32>();
 | |
|             let maybe_minor = v[1].parse::<u32>();
 | |
|             if let (Ok(major), Ok(minor)) = (maybe_major, maybe_minor) {
 | |
|                 return ClangVersion {
 | |
|                     parsed: Some((major, minor)),
 | |
|                     full: raw_v.clone(),
 | |
|                 };
 | |
|             }
 | |
|         }
 | |
|     };
 | |
|     ClangVersion {
 | |
|         parsed: None,
 | |
|         full: raw_v.clone(),
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn env_var<K: AsRef<str> + AsRef<OsStr>>(
 | |
|     parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
 | |
|     key: K,
 | |
| ) -> Result<String, std::env::VarError> {
 | |
|     for callback in parse_callbacks {
 | |
|         callback.read_env_var(key.as_ref());
 | |
|     }
 | |
|     std::env::var(key)
 | |
| }
 | |
| 
 | |
| /// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
 | |
| fn get_target_dependent_env_var(
 | |
|     parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
 | |
|     var: &str,
 | |
| ) -> Option<String> {
 | |
|     if let Ok(target) = env_var(parse_callbacks, "TARGET") {
 | |
|         if let Ok(v) = env_var(parse_callbacks, format!("{}_{}", var, target)) {
 | |
|             return Some(v);
 | |
|         }
 | |
|         if let Ok(v) = env_var(
 | |
|             parse_callbacks,
 | |
|             format!("{}_{}", var, target.replace('-', "_")),
 | |
|         ) {
 | |
|             return Some(v);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     env_var(parse_callbacks, var).ok()
 | |
| }
 | |
| 
 | |
| /// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
 | |
| /// line and on env variable usage by echoing a rerun-if-env-changed line
 | |
| ///
 | |
| /// When running inside a `build.rs` script, this can be used to make cargo invalidate the
 | |
| /// generated bindings whenever any of the files included from the header change:
 | |
| /// ```
 | |
| /// use bindgen::builder;
 | |
| /// let bindings = builder()
 | |
| ///     .header("path/to/input/header")
 | |
| ///     .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
 | |
| ///     .generate();
 | |
| /// ```
 | |
| #[derive(Debug)]
 | |
| pub struct CargoCallbacks {
 | |
|     rerun_on_header_files: bool,
 | |
| }
 | |
| 
 | |
| /// Create a new `CargoCallbacks` value with [`CargoCallbacks::rerun_on_header_files`] disabled.
 | |
| ///
 | |
| /// This constructor has been deprecated in favor of [`CargoCallbacks::new`] where
 | |
| /// [`CargoCallbacks::rerun_on_header_files`] is enabled by default.
 | |
| #[deprecated = "Use `CargoCallbacks::new()` instead. Please, check the documentation for further information."]
 | |
| pub const CargoCallbacks: CargoCallbacks = CargoCallbacks {
 | |
|     rerun_on_header_files: false,
 | |
| };
 | |
| 
 | |
| impl CargoCallbacks {
 | |
|     /// Create a new `CargoCallbacks` value.
 | |
|     pub fn new() -> Self {
 | |
|         Self {
 | |
|             rerun_on_header_files: true,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// Whether Cargo should re-run the build script if any of the input header files has changed.
 | |
|     ///
 | |
|     /// This option is enabled by default unless the deprecated [`const@CargoCallbacks`]
 | |
|     /// constructor is used.
 | |
|     pub fn rerun_on_header_files(mut self, doit: bool) -> Self {
 | |
|         self.rerun_on_header_files = doit;
 | |
|         self
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Default for CargoCallbacks {
 | |
|     fn default() -> Self {
 | |
|         Self::new()
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl callbacks::ParseCallbacks for CargoCallbacks {
 | |
|     fn header_file(&self, filename: &str) {
 | |
|         if self.rerun_on_header_files {
 | |
|             println!("cargo:rerun-if-changed={}", filename);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn include_file(&self, filename: &str) {
 | |
|         println!("cargo:rerun-if-changed={}", filename);
 | |
|     }
 | |
| 
 | |
|     fn read_env_var(&self, key: &str) {
 | |
|         println!("cargo:rerun-if-env-changed={}", key);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test command_line_flag function.
 | |
| #[test]
 | |
| fn commandline_flag_unit_test_function() {
 | |
|     //Test 1
 | |
|     let bindings = crate::builder();
 | |
|     let command_line_flags = bindings.command_line_flags();
 | |
| 
 | |
|     let test_cases = [
 | |
|         "--rust-target",
 | |
|         "--no-derive-default",
 | |
|         "--generate",
 | |
|         "functions,types,vars,methods,constructors,destructors",
 | |
|     ]
 | |
|     .iter()
 | |
|     .map(|&x| x.into())
 | |
|     .collect::<Vec<String>>();
 | |
| 
 | |
|     assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
 | |
| 
 | |
|     //Test 2
 | |
|     let bindings = crate::builder()
 | |
|         .header("input_header")
 | |
|         .allowlist_type("Distinct_Type")
 | |
|         .allowlist_function("safe_function");
 | |
| 
 | |
|     let command_line_flags = bindings.command_line_flags();
 | |
|     let test_cases = [
 | |
|         "--rust-target",
 | |
|         "input_header",
 | |
|         "--no-derive-default",
 | |
|         "--generate",
 | |
|         "functions,types,vars,methods,constructors,destructors",
 | |
|         "--allowlist-type",
 | |
|         "Distinct_Type",
 | |
|         "--allowlist-function",
 | |
|         "safe_function",
 | |
|     ]
 | |
|     .iter()
 | |
|     .map(|&x| x.into())
 | |
|     .collect::<Vec<String>>();
 | |
|     println!("{:?}", command_line_flags);
 | |
| 
 | |
|     assert!(test_cases.iter().all(|x| command_line_flags.contains(x)));
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_rust_to_clang_target() {
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("aarch64-apple-ios").as_ref(),
 | |
|         "arm64-apple-ios"
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_rust_to_clang_target_riscv() {
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("riscv64gc-unknown-linux-gnu").as_ref(),
 | |
|         "riscv64-unknown-linux-gnu"
 | |
|     );
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("riscv32imc-unknown-none-elf").as_ref(),
 | |
|         "riscv32-unknown-none-elf"
 | |
|     );
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("riscv32imac-unknown-none-elf").as_ref(),
 | |
|         "riscv32-unknown-none-elf"
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_rust_to_clang_target_espidf() {
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("riscv32imc-esp-espidf").as_ref(),
 | |
|         "riscv32-esp-elf"
 | |
|     );
 | |
|     assert_eq!(
 | |
|         rust_to_clang_target("xtensa-esp32-espidf").as_ref(),
 | |
|         "xtensa-esp32-elf"
 | |
|     );
 | |
| }
 | 
