Skip to content

Commit 9b7ccf5

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 c515919 commit 9b7ccf5

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

40074007
// We can't currently do anything with Internal functions so just
40084008
// avoid generating anything for them.
4009-
match self.linkage() {
4010-
Linkage::Internal => return None,
4011-
Linkage::External => {}
4009+
if matches!(self.linkage(), Linkage::Internal) {
4010+
return None;
40124011
}
40134012

40144013
// Pure virtual methods have no actual symbol, so we can't generate
@@ -4118,6 +4117,7 @@ impl CodeGenerator for Function {
41184117
write!(&mut canonical_name, "{}", times_seen).unwrap();
41194118
}
41204119

4120+
let mut has_link_name_attr = false;
41214121
let link_name = mangled_name.unwrap_or(name);
41224122
if !is_dynamic_function &&
41234123
!utils::names_will_be_identical_after_mangling(
@@ -4127,6 +4127,7 @@ impl CodeGenerator for Function {
41274127
)
41284128
{
41294129
attributes.push(attributes::link_name(link_name));
4130+
has_link_name_attr = true;
41304131
}
41314132

41324133
// Unfortunately this can't piggyback on the `attributes` list because
@@ -4137,6 +4138,20 @@ impl CodeGenerator for Function {
41374138
quote! { #[link(wasm_import_module = #name)] }
41384139
});
41394140

4141+
if ctx.options().generate_extern_functions.is_second_run() {
4142+
if let Some(name) = canonical_name.strip_suffix(
4143+
ctx.options()
4144+
.extern_function_suffix
4145+
.as_deref()
4146+
.unwrap_or(crate::DEFAULT_EXTERN_FUNCTION_SUFFIX),
4147+
) {
4148+
if !has_link_name_attr {
4149+
attributes.push(attributes::link_name(&canonical_name));
4150+
}
4151+
canonical_name = name.to_owned();
4152+
}
4153+
}
4154+
41404155
let ident = ctx.rust_ident(canonical_name);
41414156
let tokens = quote! {
41424157
#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};
@@ -462,6 +463,9 @@ pub struct BindgenContext {
462463

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

467471
/// A traversal of allowlisted items.
@@ -577,6 +581,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
577581
has_type_param_in_array: None,
578582
has_float: None,
579583
warnings: Vec::new(),
584+
c_items: Vec::new(),
580585
}
581586
}
582587

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)