Skip to content

Commit 4b006da

Browse files
authored
Add option to deduplicate extern blocks (#2258)
1 parent 04c0cd0 commit 4b006da

File tree

4 files changed

+120
-3
lines changed

4 files changed

+120
-3
lines changed

src/lib.rs

+70-3
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,10 @@ impl Builder {
615615
output_vector.push("--sort-semantically".into());
616616
}
617617

618+
if self.options.merge_extern_blocks {
619+
output_vector.push("--merge-extern-blocks".into());
620+
}
621+
618622
// Add clang arguments
619623

620624
output_vector.push("--".into());
@@ -1542,14 +1546,20 @@ impl Builder {
15421546
self
15431547
}
15441548

1545-
/// If true, enables the sorting of the output in a predefined manner
1549+
/// If true, enables the sorting of the output in a predefined manner.
15461550
///
15471551
/// TODO: Perhaps move the sorting order out into a config
15481552
pub fn sort_semantically(mut self, doit: bool) -> Self {
15491553
self.options.sort_semantically = doit;
15501554
self
15511555
}
15521556

1557+
/// If true, merges extern blocks.
1558+
pub fn merge_extern_blocks(mut self, doit: bool) -> Self {
1559+
self.options.merge_extern_blocks = doit;
1560+
self
1561+
}
1562+
15531563
/// Generate the Rust bindings using the options built up thus far.
15541564
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
15551565
// Add any extra arguments from the environment to the clang command line.
@@ -2095,8 +2105,11 @@ struct BindgenOptions {
20952105
/// Emit vtable functions.
20962106
vtable_generation: bool,
20972107

2098-
/// Sort the code generation
2108+
/// Sort the code generation.
20992109
sort_semantically: bool,
2110+
2111+
/// Deduplicate `extern` blocks.
2112+
merge_extern_blocks: bool,
21002113
}
21012114

21022115
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2107,7 +2120,7 @@ impl ::std::panic::UnwindSafe for BindgenOptions {}
21072120
impl BindgenOptions {
21082121
/// Whether any of the enabled options requires `syn`.
21092122
fn require_syn(&self) -> bool {
2110-
self.sort_semantically
2123+
self.sort_semantically || self.merge_extern_blocks
21112124
}
21122125

21132126
fn build(&mut self) {
@@ -2258,6 +2271,7 @@ impl Default for BindgenOptions {
22582271
force_explicit_padding: false,
22592272
vtable_generation: false,
22602273
sort_semantically: false,
2274+
merge_extern_blocks: false,
22612275
}
22622276
}
22632277
}
@@ -2563,6 +2577,59 @@ impl Bindings {
25632577
.unwrap()
25642578
.1;
25652579

2580+
if options.merge_extern_blocks {
2581+
// Here we will store all the items after deduplication.
2582+
let mut items = Vec::new();
2583+
2584+
// Keep all the extern blocks in a different `Vec` for faster search.
2585+
let mut foreign_mods = Vec::<syn::ItemForeignMod>::new();
2586+
for item in syn_parsed_items {
2587+
match item {
2588+
syn::Item::ForeignMod(syn::ItemForeignMod {
2589+
attrs,
2590+
abi,
2591+
brace_token,
2592+
items: foreign_items,
2593+
}) => {
2594+
let mut exists = false;
2595+
for foreign_mod in &mut foreign_mods {
2596+
// Check if there is a extern block with the same ABI and
2597+
// attributes.
2598+
if foreign_mod.attrs == attrs &&
2599+
foreign_mod.abi == abi
2600+
{
2601+
// Merge the items of the two blocks.
2602+
foreign_mod
2603+
.items
2604+
.extend_from_slice(&foreign_items);
2605+
exists = true;
2606+
break;
2607+
}
2608+
}
2609+
// If no existing extern block had the same ABI and attributes, store
2610+
// it.
2611+
if !exists {
2612+
foreign_mods.push(syn::ItemForeignMod {
2613+
attrs,
2614+
abi,
2615+
brace_token,
2616+
items: foreign_items,
2617+
});
2618+
}
2619+
}
2620+
// If the item is not an extern block, we don't have to do anything.
2621+
_ => items.push(item),
2622+
}
2623+
}
2624+
2625+
// Move all the extern blocks alongiside the rest of the items.
2626+
for foreign_mod in foreign_mods {
2627+
items.push(syn::Item::ForeignMod(foreign_mod));
2628+
}
2629+
2630+
syn_parsed_items = items;
2631+
}
2632+
25662633
if options.sort_semantically {
25672634
syn_parsed_items.sort_by_key(|item| match item {
25682635
syn::Item::Type(_) => 0,

src/options.rs

+7
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ where
560560
Arg::new("sort-semantically")
561561
.long("sort-semantically")
562562
.help("Enables sorting of code generation in a predefined manner."),
563+
Arg::new("merge-extern-blocks")
564+
.long("merge-extern-blocks")
565+
.help("Deduplicates extern blocks."),
563566
Arg::new("V")
564567
.long("version")
565568
.help("Prints the version, and exits"),
@@ -1076,5 +1079,9 @@ where
10761079
builder = builder.sort_semantically(true);
10771080
}
10781081

1082+
if matches.is_present("merge-extern-blocks") {
1083+
builder = builder.merge_extern_blocks(true);
1084+
}
1085+
10791086
Ok((builder, output, verbose))
10801087
}

tests/expectations/tests/merge-extern-blocks.rs

+37
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/headers/merge-extern-blocks.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// bindgen-flags: --merge-extern-blocks -- --target=x86_64-unknown-linux
2+
int foo();
3+
typedef struct Point {
4+
int x;
5+
} Point;
6+
int bar();

0 commit comments

Comments
 (0)