diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 182 |
1 files changed, 139 insertions, 43 deletions
@@ -51,6 +51,7 @@ macro_rules! doc_mod { mod clang; mod codegen; +mod deps; mod features; mod ir; mod parse; @@ -88,7 +89,7 @@ type HashSet<K> = ::rustc_hash::FxHashSet<K>; pub(crate) use std::collections::hash_map::Entry; /// Default prefix for the anon fields. -pub const DEFAULT_ANON_FIELDS_PREFIX: &'static str = "__bindgen_anon_"; +pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_"; fn file_is_cpp(name_file: &str) -> bool { name_file.ends_with(".hpp") || @@ -307,6 +308,7 @@ impl Builder { (&self.options.blocklisted_types, "--blocklist-type"), (&self.options.blocklisted_functions, "--blocklist-function"), (&self.options.blocklisted_items, "--blocklist-item"), + (&self.options.blocklisted_files, "--blocklist-file"), (&self.options.opaque_types, "--opaque-type"), (&self.options.allowlisted_functions, "--allowlist-function"), (&self.options.allowlisted_types, "--allowlist-type"), @@ -316,6 +318,7 @@ impl Builder { (&self.options.no_debug_types, "--no-debug"), (&self.options.no_default_types, "--no-default"), (&self.options.no_hash_types, "--no-hash"), + (&self.options.must_use_types, "--must-use-type"), ]; for (set, flag) in regex_sets { @@ -557,6 +560,14 @@ impl Builder { output_vector.push("--translate-enum-integer-types".into()); } + if self.options.c_naming { + output_vector.push("--c-naming".into()); + } + + if self.options.force_explicit_padding { + output_vector.push("--explicit-padding".into()); + } + // Add clang arguments output_vector.push("--".into()); @@ -604,6 +615,19 @@ impl Builder { self } + /// Add a depfile output which will be written alongside the generated bindings. + pub fn depfile<H: Into<String>, D: Into<PathBuf>>( + mut self, + output_module: H, + depfile: D, + ) -> Builder { + self.options.depfile = Some(deps::DepfileSpec { + output_module: output_module.into(), + depfile_path: depfile.into(), + }); + self + } + /// Add `contents` as an input C/C++ header named `name`. /// /// The file `name` will be added to the clang arguments. @@ -798,6 +822,13 @@ impl Builder { self } + /// Hide any contents of the given file from the generated bindings, + /// regardless of whether it's a type, function, module etc. + pub fn blocklist_file<T: AsRef<str>>(mut self, arg: T) -> Builder { + self.options.blocklisted_files.insert(arg); + self + } + /// Treat the given type as opaque in the generated bindings. Regular /// expressions are supported. /// @@ -1401,11 +1432,22 @@ impl Builder { self } + /// If true, always emit explicit padding fields. + /// + /// If a struct needs to be serialized in its native format (padding bytes + /// and all), for example writing it to a file or sending it on the network, + /// then this should be enabled, as anything reading the padding bytes of + /// a struct may lead to Undefined Behavior. + pub fn explicit_padding(mut self, doit: bool) -> Self { + self.options.force_explicit_padding = doit; + self + } + /// Generate the Rust bindings using the options built up thus far. pub fn generate(mut self) -> Result<Bindings, ()> { // Add any extra arguments from the environment to the clang command line. if let Some(extra_clang_args) = - env::var("BINDGEN_EXTRA_CLANG_ARGS").ok() + get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") { // 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) { @@ -1417,11 +1459,13 @@ impl Builder { // Transform input headers to arguments on the clang command line. self.options.input_header = self.input_headers.pop(); - self.options - .clang_args - .extend(self.input_headers.drain(..).flat_map(|header| { - iter::once("-include".into()).chain(iter::once(header)) - })); + self.options.extra_input_headers = self.input_headers; + self.options.clang_args.extend( + self.options.extra_input_headers.iter().flat_map(|header| { + iter::once("-include".into()) + .chain(iter::once(header.to_string())) + }), + ); self.options.input_unsaved_files.extend( self.input_header_contents @@ -1553,6 +1597,13 @@ impl Builder { self } + /// Add `#[must_use]` for the given type. Regular + /// expressions are supported. + pub fn must_use_type<T: Into<String>>(mut self, arg: T) -> Builder { + self.options.must_use_types.insert(arg.into()); + self + } + /// Set whether `arr[size]` should be treated as `*mut T` or `*mut [T; size]` (same for mut) pub fn array_pointers_in_arguments(mut self, doit: bool) -> Self { self.options.array_pointers_in_arguments = doit; @@ -1600,6 +1651,15 @@ impl Builder { self.options.translate_enum_integer_types = doit; self } + + /// Generate types with C style naming. + /// + /// This will add prefixes to the generated type names. For example instead of a struct `A` we + /// will generate struct `struct_A`. Currently applies to structs, unions, and enums. + pub fn c_naming(mut self, doit: bool) -> Self { + self.options.c_naming = doit; + self + } } /// Configuration options for generated bindings. @@ -1617,6 +1677,10 @@ struct BindgenOptions { /// blocklisted and should not appear in the generated code. blocklisted_items: RegexSet, + /// The set of files whose contents should be blocklisted and should not + /// appear in the generated code. + blocklisted_files: RegexSet, + /// The set of types that should be treated as opaque structures in the /// generated code. opaque_types: RegexSet, @@ -1624,6 +1688,9 @@ struct BindgenOptions { /// The explicit rustfmt path. rustfmt_path: Option<PathBuf>, + /// The path to which we should write a Makefile-syntax depfile (if any). + depfile: Option<deps::DepfileSpec>, + /// The set of types that we should have bindings for in the generated /// code. /// @@ -1785,6 +1852,9 @@ struct BindgenOptions { /// The input header file. input_header: Option<String>, + /// Any additional input header files. + extra_input_headers: Vec<String>, + /// Unsaved files for input. input_unsaved_files: Vec<clang::UnsavedFile>, @@ -1878,6 +1948,9 @@ struct BindgenOptions { /// The set of types that we should not derive `Hash` for. no_hash_types: RegexSet, + /// The set of types that we should be annotated with `#[must_use]`. + must_use_types: RegexSet, + /// Decide if C arrays should be regular pointers in rust or array pointers array_pointers_in_arguments: bool, @@ -1899,6 +1972,12 @@ struct BindgenOptions { /// Always translate enum integer types to native Rust integer types. translate_enum_integer_types: bool, + + /// Generate types with C style naming. + c_naming: bool, + + /// Always output explicit padding fields + force_explicit_padding: bool, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1915,6 +1994,7 @@ impl BindgenOptions { &mut self.blocklisted_types, &mut self.blocklisted_functions, &mut self.blocklisted_items, + &mut self.blocklisted_files, &mut self.opaque_types, &mut self.bitfield_enums, &mut self.constified_enums, @@ -1930,6 +2010,7 @@ impl BindgenOptions { &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; for regex_set in &mut regex_sets { @@ -1961,8 +2042,10 @@ impl Default for BindgenOptions { blocklisted_types: Default::default(), blocklisted_functions: Default::default(), blocklisted_items: Default::default(), + blocklisted_files: Default::default(), opaque_types: Default::default(), rustfmt_path: Default::default(), + depfile: Default::default(), allowlisted_types: Default::default(), allowlisted_functions: Default::default(), allowlisted_vars: Default::default(), @@ -2008,6 +2091,7 @@ impl Default for BindgenOptions { module_lines: HashMap::default(), clang_args: vec![], input_header: None, + extra_input_headers: vec![], input_unsaved_files: vec![], parse_callbacks: None, codegen_config: CodegenConfig::all(), @@ -2032,12 +2116,15 @@ impl Default for BindgenOptions { no_debug_types: Default::default(), no_default_types: Default::default(), no_hash_types: Default::default(), + must_use_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, dynamic_library_name: None, dynamic_link_require_all: false, respect_cxx_access_specs: false, translate_enum_integer_types: false, + c_naming: false, + force_explicit_padding: false, } } } @@ -2075,7 +2162,7 @@ pub struct Bindings { module: proc_macro2::TokenStream, } -pub(crate) const HOST_TARGET: &'static str = +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 @@ -2083,7 +2170,8 @@ pub(crate) const HOST_TARGET: &'static str = fn rust_to_clang_target(rust_target: &str) -> String { if rust_target.starts_with("aarch64-apple-") { let mut clang_target = "arm64-apple-".to_owned(); - clang_target.push_str(&rust_target["aarch64-apple-".len()..]); + clang_target + .push_str(rust_target.strip_prefix("aarch64-apple-").unwrap()); return clang_target; } rust_target.to_owned() @@ -2205,10 +2293,7 @@ impl Bindings { // Whether we are working with C or C++ inputs. let is_cpp = args_are_cpp(&options.clang_args) || - options - .input_header - .as_ref() - .map_or(false, |i| file_is_cpp(&i)); + options.input_header.as_deref().map_or(false, file_is_cpp); let search_paths = if is_cpp { clang.cpp_search_paths @@ -2296,15 +2381,6 @@ impl Bindings { }) } - /// Convert these bindings into source text (with raw lines prepended). - pub fn to_string(&self) -> String { - let mut bytes = vec![]; - self.write(Box::new(&mut bytes) as Box<dyn Write>) - .expect("writing to a vec cannot fail"); - String::from_utf8(bytes) - .expect("we should only write bindings that are valid utf-8") - } - /// 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() @@ -2319,7 +2395,7 @@ impl Bindings { /// Write these bindings as source text to the given `Write`able. pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> { if !self.options.disable_header_comment { - let version = Some("0.58.1"); + let version = Some("0.59.2"); let header = format!( "/* automatically generated by rust-bindgen {} */\n\n", version.unwrap_or("(unknown version)") @@ -2354,7 +2430,7 @@ impl Bindings { } /// Gets the rustfmt path to rustfmt the generated bindings. - fn rustfmt_path<'a>(&'a self) -> io::Result<Cow<'a, PathBuf>> { + fn rustfmt_path(&self) -> io::Result<Cow<PathBuf>> { debug_assert!(self.options.rustfmt_bindings); if let Some(ref p) = self.options.rustfmt_path { return Ok(Cow::Borrowed(p)); @@ -2445,6 +2521,18 @@ impl Bindings { } } +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 { @@ -2493,7 +2581,7 @@ fn parse(context: &mut BindgenContext) -> Result<(), ()> { if context.options().emit_ast { fn dump_if_not_builtin(cur: &clang::Cursor) -> CXChildVisitResult { if !cur.is_builtin() { - clang::ast_dump(&cur, 0) + clang::ast_dump(cur, 0) } else { CXChildVisit_Continue } @@ -2530,26 +2618,19 @@ pub fn clang_version() -> ClangVersion { let raw_v: String = clang::extract_clang_version(); let split_v: Option<Vec<&str>> = raw_v .split_whitespace() - .filter(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit())) - .next() + .find(|t| t.chars().next().map_or(false, |v| v.is_ascii_digit())) .map(|v| v.split('.').collect()); - match split_v { - Some(v) => { - if v.len() >= 2 { - let maybe_major = v[0].parse::<u32>(); - let maybe_minor = v[1].parse::<u32>(); - match (maybe_major, maybe_minor) { - (Ok(major), Ok(minor)) => { - return ClangVersion { - parsed: Some((major, minor)), - full: raw_v.clone(), - } - } - _ => {} - } + 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(), + }; } } - None => {} }; ClangVersion { parsed: None, @@ -2557,10 +2638,25 @@ pub fn clang_version() -> ClangVersion { } } +/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found. +fn get_target_dependent_env_var(var: &str) -> Option<String> { + if let Ok(target) = env::var("TARGET") { + if let Ok(v) = env::var(&format!("{}_{}", var, target)) { + return Some(v); + } + if let Ok(v) = + env::var(&format!("{}_{}", var, target.replace("-", "_"))) + { + return Some(v); + } + } + env::var(var).ok() +} + /// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed /// line /// -/// When running in side a `build.rs` script, this can be used to make cargo invalidate the +/// 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; |