diff --git a/src/lib.rs b/src/lib.rs index fb02fe16f5..a4dfd7b1ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -592,6 +592,10 @@ impl Builder { output_vector.push("--sort-semantically".into()); } + if self.options.merge_extern_blocks { + output_vector.push("--merge-extern-blocks".into()); + } + // Add clang arguments output_vector.push("--".into()); @@ -1481,7 +1485,7 @@ impl Builder { self } - /// If true, enables the sorting of the output in a predefined manner + /// If true, enables the sorting of the output in a predefined manner. /// /// TODO: Perhaps move the sorting order out into a config pub fn sort_semantically(mut self, doit: bool) -> Self { @@ -1489,6 +1493,12 @@ impl Builder { self } + /// If true, merges extern blocks. + pub fn merge_extern_blocks(mut self, doit: bool) -> Self { + self.options.merge_extern_blocks = doit; + self + } + /// Generate the Rust bindings using the options built up thus far. pub fn generate(mut self) -> Result { // Add any extra arguments from the environment to the clang command line. @@ -2019,8 +2029,11 @@ struct BindgenOptions { /// Emit vtable functions. vtable_generation: bool, - /// Sort the code generation + /// Sort the code generation. sort_semantically: bool, + + /// Deduplicate `extern` blocks. + merge_extern_blocks: bool, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -2031,7 +2044,7 @@ impl ::std::panic::UnwindSafe for BindgenOptions {} impl BindgenOptions { /// Whether any of the enabled options requires `syn`. fn require_syn(&self) -> bool { - self.sort_semantically + self.sort_semantically || self.merge_extern_blocks } fn build(&mut self) { @@ -2175,6 +2188,7 @@ impl Default for BindgenOptions { force_explicit_padding: false, vtable_generation: false, sort_semantically: false, + merge_extern_blocks: false, } } } @@ -2480,6 +2494,59 @@ impl Bindings { .unwrap() .1; + if options.merge_extern_blocks { + // Here we will store all the items after deduplication. + let mut items = Vec::new(); + + // Keep all the extern blocks in a different `Vec` for faster search. + let mut foreign_mods = Vec::::new(); + for item in syn_parsed_items { + match item { + syn::Item::ForeignMod(syn::ItemForeignMod { + attrs, + abi, + brace_token, + items: foreign_items, + }) => { + let mut exists = false; + for foreign_mod in &mut foreign_mods { + // Check if there is a extern block with the same ABI and + // attributes. + if foreign_mod.attrs == attrs && + foreign_mod.abi == abi + { + // Merge the items of the two blocks. + foreign_mod + .items + .extend_from_slice(&foreign_items); + exists = true; + break; + } + } + // If no existing extern block had the same ABI and attributes, store + // it. + if !exists { + foreign_mods.push(syn::ItemForeignMod { + attrs, + abi, + brace_token, + items: foreign_items, + }); + } + } + // If the item is not an extern block, we don't have to do anything. + _ => items.push(item), + } + } + + // Move all the extern blocks alongiside the rest of the items. + for foreign_mod in foreign_mods { + items.push(syn::Item::ForeignMod(foreign_mod)); + } + + syn_parsed_items = items; + } + if options.sort_semantically { syn_parsed_items.sort_by_key(|item| match item { syn::Item::Type(_) => 0, diff --git a/src/options.rs b/src/options.rs index 83da21f42f..8ae7cbe864 100644 --- a/src/options.rs +++ b/src/options.rs @@ -518,6 +518,9 @@ where Arg::new("sort-semantically") .long("sort-semantically") .help("Enables sorting of code generation in a predefined manner."), + Arg::new("merge-extern-blocks") + .long("merge-extern-blocks") + .help("Deduplicates extern blocks."), Arg::new("V") .long("version") .help("Prints the version, and exits"), @@ -1007,5 +1010,9 @@ where builder = builder.sort_semantically(true); } + if matches.is_present("merge-extern-blocks") { + builder = builder.merge_extern_blocks(true); + } + Ok((builder, output, verbose)) } diff --git a/tests/expectations/tests/merge-extern-blocks.rs b/tests/expectations/tests/merge-extern-blocks.rs new file mode 100644 index 0000000000..66ceeff07f --- /dev/null +++ b/tests/expectations/tests/merge-extern-blocks.rs @@ -0,0 +1,37 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Point { + pub x: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_Point() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(Point)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(Point)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize }, + 0usize, + concat!("Offset of field: ", stringify!(Point), "::", stringify!(x)) + ); +} +extern "C" { + pub fn foo() -> ::std::os::raw::c_int; + pub fn bar() -> ::std::os::raw::c_int; +} diff --git a/tests/headers/merge-extern-blocks.h b/tests/headers/merge-extern-blocks.h new file mode 100644 index 0000000000..1d46b7d4a3 --- /dev/null +++ b/tests/headers/merge-extern-blocks.h @@ -0,0 +1,6 @@ +// bindgen-flags: --merge-extern-blocks -- --target=x86_64-unknown-linux +int foo(); +typedef struct Point { + int x; +} Point; +int bar();