Skip to content

Commit 4b5fec4

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 6322b2d commit 4b5fec4

File tree

9 files changed

+488
-9
lines changed

9 files changed

+488
-9
lines changed

bindgen-cli/options.rs

+32
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,22 @@ where
616616
.long("wrap-unsafe-ops")
617617
.help("Wrap unsafe operations in unsafe blocks.")
618618
.action(ArgAction::SetTrue),
619+
Arg::new("generate-extern-functions")
620+
.long("generate-extern-functions")
621+
.help("Generate extern wrappers for inlined functions")
622+
.action(ArgAction::SetTrue),
623+
Arg::new("extern-functions-file-name")
624+
.long("extern-functions-file-name")
625+
.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.")
626+
.action(ArgAction::Set),
627+
Arg::new("extern-functions-directory")
628+
.long("extern-functions-directory")
629+
.help("Sets the directory path where any extra files must be created due to the presence of inlined functions.")
630+
.action(ArgAction::Set),
631+
Arg::new("extern-function-suffix")
632+
.long("extern-function-suffix")
633+
.help("Sets the suffix added to the extern wrapper functions generated for inlined functions.")
634+
.action(ArgAction::Set),
619635
Arg::new("V")
620636
.long("version")
621637
.help("Prints the version, and exits")
@@ -1162,5 +1178,21 @@ where
11621178
builder = builder.wrap_unsafe_ops(true);
11631179
}
11641180

1181+
if matches.get_flag("generate-extern-functions") {
1182+
builder = builder.generate_extern_functions(true);
1183+
}
1184+
1185+
if let Some(file_name) = matches.get_one::<String>("extern-functions-file-name") {
1186+
builder = builder.extern_functions_file_name(file_name);
1187+
}
1188+
1189+
if let Some(directory) = matches.get_one::<String>("extern-functions-directory") {
1190+
builder = builder.extern_functions_directory(directory);
1191+
}
1192+
1193+
if let Some(suffix) = matches.get_one::<String>("extern-function-suffix") {
1194+
builder = builder.extern_function_suffix(suffix);
1195+
}
1196+
11651197
Ok((builder, output, verbose))
11661198
}

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

39923992
// We can't currently do anything with Internal functions so just
39933993
// avoid generating anything for them.
3994-
match self.linkage() {
3995-
Linkage::Internal => return None,
3996-
Linkage::External => {}
3994+
if matches!(self.linkage(), Linkage::Internal) {
3995+
return None;
39973996
}
39983997

39993998
// Pure virtual methods have no actual symbol, so we can't generate
@@ -4103,6 +4102,7 @@ impl CodeGenerator for Function {
41034102
write!(&mut canonical_name, "{}", times_seen).unwrap();
41044103
}
41054104

4105+
let mut has_link_name_attr = false;
41064106
let link_name = mangled_name.unwrap_or(name);
41074107
if !is_dynamic_function &&
41084108
!utils::names_will_be_identical_after_mangling(
@@ -4112,6 +4112,7 @@ impl CodeGenerator for Function {
41124112
)
41134113
{
41144114
attributes.push(attributes::link_name(link_name));
4115+
has_link_name_attr = true;
41154116
}
41164117

41174118
// Unfortunately this can't piggyback on the `attributes` list because
@@ -4122,6 +4123,20 @@ impl CodeGenerator for Function {
41224123
quote! { #[link(wasm_import_module = #name)] }
41234124
});
41244125

4126+
if ctx.options().generate_extern_functions.is_second_run() {
4127+
if let Some(name) = canonical_name.strip_suffix(
4128+
ctx.options()
4129+
.extern_function_suffix
4130+
.as_deref()
4131+
.unwrap_or(crate::DEFAULT_EXTERN_FUNCTION_SUFFIX),
4132+
) {
4133+
if !has_link_name_attr {
4134+
attributes.push(attributes::link_name(&canonical_name));
4135+
}
4136+
canonical_name = name.to_owned();
4137+
}
4138+
}
4139+
41254140
let ident = ctx.rust_ident(canonical_name);
41264141
let tokens = quote! {
41274142
#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};
@@ -477,6 +478,9 @@ pub struct BindgenContext {
477478

478479
/// The set of warnings raised during binding generation.
479480
warnings: Vec<String>,
481+
482+
/// C items that need to be serialized to an extra header file.
483+
pub(crate) c_items: Vec<CItem>,
480484
}
481485

482486
/// A traversal of allowlisted items.
@@ -593,6 +597,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
593597
has_type_param_in_array: None,
594598
has_float: None,
595599
warnings: Vec::new(),
600+
c_items: Vec::new(),
596601
}
597602
}
598603

bindgen/ir/function.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::traversal::{EdgeKind, Trace, Tracer};
88
use super::ty::TypeKind;
99
use crate::callbacks::{ItemInfo, ItemKind};
1010
use crate::clang::{self, Attribute};
11+
use crate::ir::serialize::CItem;
1112
use crate::parse::{ClangSubItemParser, ParseError, ParseResult};
1213
use clang_sys::{self, CXCallingConv};
1314
use proc_macro2;
@@ -679,9 +680,12 @@ impl ClangSubItemParser for Function {
679680
.definition()
680681
.map_or(false, |x| x.is_inlined_function())
681682
{
682-
if !context.options().generate_inline_functions {
683+
if !(context.options().generate_inline_functions ||
684+
context.options().generate_extern_functions.is_true())
685+
{
683686
return Err(ParseError::Continue);
684687
}
688+
685689
if cursor.is_deleted_function() {
686690
return Err(ParseError::Continue);
687691
}
@@ -728,6 +732,16 @@ impl ClangSubItemParser for Function {
728732

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

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)