Skip to content

Commit 65ff041

Browse files
committed
Generate extern wrappers for inlined functions
If bindgen finds an inlined function and the `--generate-extern-functions` options is enabled, then: - It will generate two new source and header files with external functions that wrap the inlined functions. - Rerun `Bindings::generate` using the new header file to include these wrappers in the generated bindings. The following additional options were added: - `--extern-function-suffix=<suffix>`: Adds <suffix> to the name of each external wrapper function (`__extern` is used by default). - `--extern-functions-file-name=<name>`: Uses <name> as the file name for the header and source files (`extern` is used by default). - `--extern-function-directory=<dir>`: Creates the source and header files inside <dir> (`/tmp/bindgen` is used by default). The C code serialization is experimental and only supports a very limited set of C functions. Fixes #1090.
1 parent ed3aa90 commit 65ff041

File tree

9 files changed

+487
-9
lines changed

9 files changed

+487
-9
lines changed

bindgen-cli/options.rs

+31
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,21 @@ where
574574
.value_name("override")
575575
.multiple_occurrences(true)
576576
.number_of_values(1),
577+
Arg::new("generate-extern-functions")
578+
.long("generate-extern-functions")
579+
.help("Generate extern wrappers for inlined functions"),
580+
Arg::new("extern-functions-file-name")
581+
.long("extern-functions-file-name")
582+
.help("Sets the name of the header and source code files that would be created if any extern wrapper functions must be generated due to the presence of inlined functions.")
583+
.takes_value(true),
584+
Arg::new("extern-functions-directory")
585+
.long("extern-functions-directory")
586+
.help("Sets the directory path where any extra files must be created due to the presence of inlined functions.")
587+
.takes_value(true),
588+
Arg::new("extern-function-suffix")
589+
.long("extern-function-suffix")
590+
.help("Sets the suffix added to the extern wrapper functions generated for inlined functions.")
591+
.takes_value(true),
577592
Arg::new("V")
578593
.long("version")
579594
.help("Prints the version, and exits"),
@@ -1106,5 +1121,21 @@ where
11061121
}
11071122
}
11081123

1124+
if matches.is_present("generate-extern-functions") {
1125+
builder = builder.generate_extern_functions(true);
1126+
}
1127+
1128+
if let Some(file_name) = matches.value_of("extern-functions-file-name") {
1129+
builder = builder.extern_functions_file_name(file_name);
1130+
}
1131+
1132+
if let Some(directory) = matches.value_of("extern-functions-directory") {
1133+
builder = builder.extern_functions_directory(directory);
1134+
}
1135+
1136+
if let Some(suffix) = matches.value_of("extern-function-suffix") {
1137+
builder = builder.extern_function_suffix(suffix);
1138+
}
1139+
11091140
Ok((builder, output, verbose))
11101141
}

bindgen-tests/tests/expectations/tests/generate-extern-functions.rs

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-flags: --generate-extern-functions
2+
3+
static inline int foo() {
4+
return 0;
5+
}
6+
static int bar() {
7+
return 1;
8+
}
9+
inline int baz() {
10+
return 2;
11+
}

bindgen/codegen/mod.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -4045,9 +4045,8 @@ impl CodeGenerator for Function {
40454045

40464046
// We can't currently do anything with Internal functions so just
40474047
// avoid generating anything for them.
4048-
match self.linkage() {
4049-
Linkage::Internal => return None,
4050-
Linkage::External => {}
4048+
if matches!(self.linkage(), Linkage::Internal) {
4049+
return None;
40514050
}
40524051

40534052
// Pure virtual methods have no actual symbol, so we can't generate
@@ -4157,6 +4156,7 @@ impl CodeGenerator for Function {
41574156
write!(&mut canonical_name, "{}", times_seen).unwrap();
41584157
}
41594158

4159+
let mut has_link_name_attr = false;
41604160
let link_name = mangled_name.unwrap_or(name);
41614161
if !is_dynamic_function &&
41624162
!utils::names_will_be_identical_after_mangling(
@@ -4166,6 +4166,7 @@ impl CodeGenerator for Function {
41664166
)
41674167
{
41684168
attributes.push(attributes::link_name(link_name));
4169+
has_link_name_attr = true;
41694170
}
41704171

41714172
// Unfortunately this can't piggyback on the `attributes` list because
@@ -4176,6 +4177,20 @@ impl CodeGenerator for Function {
41764177
quote! { #[link(wasm_import_module = #name)] }
41774178
});
41784179

4180+
if ctx.options().generate_extern_functions.is_second_run() {
4181+
if let Some(name) = canonical_name.strip_suffix(
4182+
ctx.options()
4183+
.extern_function_suffix
4184+
.as_deref()
4185+
.unwrap_or(crate::DEFAULT_EXTERN_FUNCTION_SUFFIX),
4186+
) {
4187+
if !has_link_name_attr {
4188+
attributes.push(attributes::link_name(&canonical_name));
4189+
}
4190+
canonical_name = name.to_owned();
4191+
}
4192+
}
4193+
41794194
let ident = ctx.rust_ident(canonical_name);
41804195
let tokens = quote! {
41814196
#wasm_link_attribute

bindgen/ir/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use super::int::IntKind;
1616
use super::item::{IsOpaque, Item, ItemAncestors, ItemSet};
1717
use super::item_kind::ItemKind;
1818
use super::module::{Module, ModuleKind};
19+
use super::serialize::CItem;
1920
use super::template::{TemplateInstantiation, TemplateParameters};
2021
use super::traversal::{self, Edge, ItemTraversal};
2122
use super::ty::{FloatKind, Type, TypeKind};
@@ -461,6 +462,9 @@ pub struct BindgenContext {
461462

462463
/// The set of warnings raised during binding generation.
463464
warnings: Vec<String>,
465+
466+
/// C items that need to be serialized to an extra header file.
467+
pub(crate) c_items: Vec<CItem>,
464468
}
465469

466470
/// A traversal of allowlisted items.
@@ -576,6 +580,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
576580
has_type_param_in_array: None,
577581
has_float: None,
578582
warnings: Vec::new(),
583+
c_items: Vec::new(),
579584
}
580585
}
581586

bindgen/ir/function.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::item::Item;
77
use super::traversal::{EdgeKind, Trace, Tracer};
88
use super::ty::TypeKind;
99
use crate::clang::{self, Attribute};
10+
use crate::ir::serialize::CItem;
1011
use crate::parse::{
1112
ClangItemParser, ClangSubItemParser, ParseError, ParseResult,
1213
};
@@ -680,9 +681,12 @@ impl ClangSubItemParser for Function {
680681
.definition()
681682
.map_or(false, |x| x.is_inlined_function())
682683
{
683-
if !context.options().generate_inline_functions {
684+
if !(context.options().generate_inline_functions ||
685+
context.options().generate_extern_functions.is_true())
686+
{
684687
return Err(ParseError::Continue);
685688
}
689+
686690
if cursor.is_deleted_function() {
687691
return Err(ParseError::Continue);
688692
}
@@ -727,6 +731,16 @@ impl ClangSubItemParser for Function {
727731

728732
let function =
729733
Self::new(name, mangled_name, sig, comment, kind, linkage);
734+
735+
if matches!(linkage, Linkage::Internal) &&
736+
context.options().generate_extern_functions.is_true()
737+
{
738+
match CItem::from_function(&function, context) {
739+
Ok(c_item) => context.c_items.push(c_item),
740+
Err(err) => warn!("Serialization failed: {:?}", err),
741+
}
742+
}
743+
730744
Ok(ParseResult::New(function, Some(cursor)))
731745
}
732746
}

bindgen/ir/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod item_kind;
1818
pub mod layout;
1919
pub mod module;
2020
pub mod objc;
21+
pub(crate) mod serialize;
2122
pub mod template;
2223
pub mod traversal;
2324
pub mod ty;

0 commit comments

Comments
 (0)