Skip to content

Commit 0736ef4

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 95fd17b commit 0736ef4

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
@@ -565,6 +565,21 @@ where
565565
Arg::new("wrap-unsafe-ops")
566566
.long("wrap-unsafe-ops")
567567
.help("Wrap unsafe operations in unsafe blocks."),
568+
Arg::new("generate-extern-functions")
569+
.long("generate-extern-functions")
570+
.help("Generate extern wrappers for inlined functions"),
571+
Arg::new("extern-functions-file-name")
572+
.long("extern-functions-file-name")
573+
.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.")
574+
.takes_value(true),
575+
Arg::new("extern-functions-directory")
576+
.long("extern-functions-directory")
577+
.help("Sets the directory path where any extra files must be created due to the presence of inlined functions.")
578+
.takes_value(true),
579+
Arg::new("extern-function-suffix")
580+
.long("extern-function-suffix")
581+
.help("Sets the suffix added to the extern wrapper functions generated for inlined functions.")
582+
.takes_value(true),
568583
Arg::new("V")
569584
.long("version")
570585
.help("Prints the version, and exits"),
@@ -1092,5 +1107,21 @@ where
10921107
builder = builder.wrap_unsafe_ops(true);
10931108
}
10941109

1110+
if matches.is_present("generate-extern-functions") {
1111+
builder = builder.generate_extern_functions(true);
1112+
}
1113+
1114+
if let Some(file_name) = matches.value_of("extern-functions-file-name") {
1115+
builder = builder.extern_functions_file_name(file_name);
1116+
}
1117+
1118+
if let Some(directory) = matches.value_of("extern-functions-directory") {
1119+
builder = builder.extern_functions_directory(directory);
1120+
}
1121+
1122+
if let Some(suffix) = matches.value_of("extern-function-suffix") {
1123+
builder = builder.extern_function_suffix(suffix);
1124+
}
1125+
10951126
Ok((builder, output, verbose))
10961127
}

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
@@ -3995,9 +3995,8 @@ impl CodeGenerator for Function {
39953995

39963996
// We can't currently do anything with Internal functions so just
39973997
// avoid generating anything for them.
3998-
match self.linkage() {
3999-
Linkage::Internal => return None,
4000-
Linkage::External => {}
3998+
if matches!(self.linkage(), Linkage::Internal) {
3999+
return None;
40014000
}
40024001

40034002
// Pure virtual methods have no actual symbol, so we can't generate
@@ -4107,6 +4106,7 @@ impl CodeGenerator for Function {
41074106
write!(&mut canonical_name, "{}", times_seen).unwrap();
41084107
}
41094108

4109+
let mut has_link_name_attr = false;
41104110
let link_name = mangled_name.unwrap_or(name);
41114111
if !is_dynamic_function &&
41124112
!utils::names_will_be_identical_after_mangling(
@@ -4116,6 +4116,7 @@ impl CodeGenerator for Function {
41164116
)
41174117
{
41184118
attributes.push(attributes::link_name(link_name));
4119+
has_link_name_attr = true;
41194120
}
41204121

41214122
// Unfortunately this can't piggyback on the `attributes` list because
@@ -4126,6 +4127,20 @@ impl CodeGenerator for Function {
41264127
quote! { #[link(wasm_import_module = #name)] }
41274128
});
41284129

4130+
if ctx.options().generate_extern_functions.is_second_run() {
4131+
if let Some(name) = canonical_name.strip_suffix(
4132+
ctx.options()
4133+
.extern_function_suffix
4134+
.as_deref()
4135+
.unwrap_or(crate::DEFAULT_EXTERN_FUNCTION_SUFFIX),
4136+
) {
4137+
if !has_link_name_attr {
4138+
attributes.push(attributes::link_name(&canonical_name));
4139+
}
4140+
canonical_name = name.to_owned();
4141+
}
4142+
}
4143+
41294144
let ident = ctx.rust_ident(canonical_name);
41304145
let tokens = quote! {
41314146
#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::{ClangSubItemParser, ParseError, ParseResult};
1112
use clang_sys::{self, CXCallingConv};
1213
use proc_macro2;
@@ -678,9 +679,12 @@ impl ClangSubItemParser for Function {
678679
.definition()
679680
.map_or(false, |x| x.is_inlined_function())
680681
{
681-
if !context.options().generate_inline_functions {
682+
if !(context.options().generate_inline_functions ||
683+
context.options().generate_extern_functions.is_true())
684+
{
682685
return Err(ParseError::Continue);
683686
}
687+
684688
if cursor.is_deleted_function() {
685689
return Err(ParseError::Continue);
686690
}
@@ -725,6 +729,16 @@ impl ClangSubItemParser for Function {
725729

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

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)