Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate extern wrappers for inlined functions #2335

Merged
merged 37 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
afbc4d8
Generate extern wrappers for inlined functions
pvdrz Nov 4, 2022
ad0f161
add(static inlined): tests
amanjeev Jan 19, 2023
101cde0
add the `experimental` feature
pvdrz Jan 24, 2023
61b2b44
Rename options
pvdrz Jan 26, 2023
65a80e0
Fixup: Rename options
pvdrz Jan 26, 2023
eb823b8
merge filename and directory arguments
pvdrz Jan 26, 2023
8b0bdcd
avoid second run
pvdrz Jan 26, 2023
39ac21b
move all c serialization to codegen
pvdrz Jan 26, 2023
fa47c2b
some nits
pvdrz Jan 27, 2023
e33237b
update docs and remove headers
pvdrz Jan 27, 2023
d65527f
keep code serialization in the happy path
pvdrz Jan 27, 2023
c80ed2d
rename `non-extern` to `static`
pvdrz Jan 27, 2023
9ecbde4
Remove headers
pvdrz Jan 31, 2023
34278f4
update integration tests
pvdrz Jan 31, 2023
eb2a3d3
run rustfmt
pvdrz Jan 31, 2023
595ce78
add experimental feature to dependency
pvdrz Jan 31, 2023
cb93924
use static kind
pvdrz Jan 31, 2023
e36e088
force function names
pvdrz Jan 31, 2023
ceebca1
refactor c serialization
pvdrz Jan 31, 2023
f7df058
add types for serialized functions and support pointer types
pvdrz Feb 1, 2023
c55aa5b
buffer all the code before writing
pvdrz Feb 1, 2023
c12645f
stop bindgen if there's a serialization error
pvdrz Feb 2, 2023
3eb7f69
add missing space
pvdrz Feb 2, 2023
b682c6c
track location while reporting errors
pvdrz Feb 2, 2023
6721b96
fix test
pvdrz Feb 2, 2023
ab6ea25
add support for Comp types
pvdrz Feb 2, 2023
bc7f6bf
run rustfmt
pvdrz Feb 2, 2023
e823b32
support `char`
pvdrz Feb 2, 2023
0aafbee
add `Extra` associated type
pvdrz Feb 2, 2023
58f740d
add proper support for functions
pvdrz Feb 2, 2023
affcb2f
fix tests and remove dbg
pvdrz Feb 2, 2023
4819ea4
run rustfmt
pvdrz Feb 2, 2023
7b8fe72
handle type aliases
pvdrz Feb 2, 2023
7698c71
handle constness
pvdrz Feb 2, 2023
9326863
use void for empty args
pvdrz Feb 2, 2023
5a9e5ca
pass amanfmt :trollface:
pvdrz Feb 6, 2023
572d1a4
some nits
pvdrz Feb 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindgen-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ path = "main.rs"
name = "bindgen"

[dependencies]
bindgen = { path = "../bindgen", version = "=0.63.0", features = ["cli"] }
bindgen = { path = "../bindgen", version = "=0.63.0", features = ["cli", "experimental"] }
shlex = "1"
clap = { version = "4", features = ["derive"] }
env_logger = { version = "0.9.0", optional = true }
Expand Down
30 changes: 30 additions & 0 deletions bindgen-cli/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,20 @@ struct BindgenCommand {
/// Derive custom traits on a `union`. The <CUSTOM> value must be of the shape <REGEX>=<DERIVE> where <DERIVE> is a coma-separated list of derive macros.
#[arg(long, value_name = "CUSTOM")]
with_derive_custom_union: Vec<String>,
/// Generate wrappers for `static` and `static inline` functions.
#[arg(long, requires = "experimental")]
wrap_static_fns: bool,
/// Sets the path for the source file that must be created due to the presence of `static` and
/// `static inline` functions.
#[arg(long, requires = "experimental", value_name = "PATH")]
wrap_static_fns_path: Option<PathBuf>,
/// Sets the suffix added to the extern wrapper functions generated for `static` and `static
/// inline` functions.
#[arg(long, requires = "experimental", value_name = "SUFFIX")]
wrap_static_fns_suffix: Option<String>,
/// Enables experimental features.
#[arg(long)]
experimental: bool,
/// Prints the version, and exits
#[arg(short = 'V', long)]
version: bool,
Expand Down Expand Up @@ -473,6 +487,10 @@ where
with_derive_custom_struct,
with_derive_custom_enum,
with_derive_custom_union,
wrap_static_fns,
wrap_static_fns_path,
wrap_static_fns_suffix,
experimental: _,
version,
clang_args,
} = command;
Expand Down Expand Up @@ -978,5 +996,17 @@ where
}
}

if wrap_static_fns {
builder = builder.wrap_static_fns(true);
}

if let Some(path) = wrap_static_fns_path {
builder = builder.wrap_static_fns_path(path);
}

if let Some(suffix) = wrap_static_fns_suffix {
builder = builder.wrap_static_fns_suffix(suffix);
}

Ok((builder, output, verbose))
}
2 changes: 1 addition & 1 deletion bindgen-integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ publish = false
build = "build.rs"

[build-dependencies]
bindgen = { path = "../bindgen" }
bindgen = { path = "../bindgen", features = ["experimental"] }
cc = "1.0"

[features]
Expand Down
128 changes: 100 additions & 28 deletions bindgen-integration/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate cc;
use bindgen::callbacks::{
DeriveInfo, IntKind, MacroParsingBehavior, ParseCallbacks,
};
use bindgen::{Builder, EnumVariation};
use bindgen::{Builder, CargoCallbacks, EnumVariation};
use std::collections::HashSet;
use std::env;
use std::path::PathBuf;
Expand All @@ -28,21 +28,14 @@ impl ParseCallbacks for MacroCallback {
MacroParsingBehavior::Default
}

fn item_name(&self, original_item_name: &str) -> Option<String> {
if original_item_name.starts_with("my_prefixed_") {
Some(
original_item_name
.trim_start_matches("my_prefixed_")
.to_string(),
)
} else if original_item_name.starts_with("MY_PREFIXED_") {
Some(
original_item_name
.trim_start_matches("MY_PREFIXED_")
.to_string(),
)
} else {
None
fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
match name {
"TESTMACRO_CUSTOMINTKIND_PATH" => Some(IntKind::Custom {
name: "crate::MacroInteger",
is_signed: true,
}),

_ => None,
}
}

Expand All @@ -67,17 +60,6 @@ impl ParseCallbacks for MacroCallback {
}
}

fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
match name {
"TESTMACRO_CUSTOMINTKIND_PATH" => Some(IntKind::Custom {
name: "crate::MacroInteger",
is_signed: true,
}),

_ => None,
}
}

fn func_macro(&self, name: &str, value: &[&[u8]]) {
match name {
"TESTMACRO_NONFUNCTIONAL" => {
Expand Down Expand Up @@ -122,6 +104,24 @@ impl ParseCallbacks for MacroCallback {
}
}

fn item_name(&self, original_item_name: &str) -> Option<String> {
if original_item_name.starts_with("my_prefixed_") {
Some(
original_item_name
.trim_start_matches("my_prefixed_")
.to_string(),
)
} else if original_item_name.starts_with("MY_PREFIXED_") {
Some(
original_item_name
.trim_start_matches("MY_PREFIXED_")
.to_string(),
)
} else {
None
}
}

// Test the "custom derives" capability by adding `PartialEq` to the `Test` struct.
fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec<String> {
if info.name == "Test" {
Expand Down Expand Up @@ -149,7 +149,7 @@ impl Drop for MacroCallback {
}
}

fn main() {
fn setup_macro_test() {
cc::Build::new()
.cpp(true)
.file("cpp/Test.cc")
Expand Down Expand Up @@ -204,3 +204,75 @@ fn main() {
"including stub via include dir must produce correct dep path",
);
}

fn setup_wrap_static_fns_test() {
// GH-1090: https://github.com/rust-lang/rust-bindgen/issues/1090
// set output directory under /target so it is easy to clean generated files
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let out_rust_file = out_path.join("extern.rs");

let input_header_dir = PathBuf::from("../bindgen-tests/tests/headers/")
.canonicalize()
.expect("Cannot canonicalize libdir path");
let input_header_file_path = input_header_dir.join("wrap-static-fns.h");
let input_header_file_path_str = input_header_file_path
.to_str()
.expect("Path could not be converted to a str");

// generate external bindings with the external .c and .h files
let bindings = Builder::default()
.header(input_header_file_path_str)
.parse_callbacks(Box::new(CargoCallbacks))
.wrap_static_fns(true)
.wrap_static_fns_path(
out_path.join("wrap_static_fns").display().to_string(),
)
.generate()
.expect("Unable to generate bindings");

println!("cargo:rustc-link-lib=static=wrap_static_fns"); // tell cargo to link libextern
println!("bindings generated: {}", bindings);

let obj_path = out_path.join("wrap_static_fns.o");
let lib_path = out_path.join("libwrap_static_fns.a");

// build the external files to check if they work
let clang_output = std::process::Command::new("clang")
.arg("-c")
.arg("-o")
.arg(&obj_path)
.arg(out_path.join("wrap_static_fns.c"))
.arg("-include")
.arg(input_header_file_path)
.output()
.expect("`clang` command error");
if !clang_output.status.success() {
panic!(
"Could not compile object file:\n{}",
String::from_utf8_lossy(&clang_output.stderr)
);
}

let ar_output = std::process::Command::new("ar")
.arg("rcs")
.arg(lib_path)
.arg(obj_path)
.output()
.expect("`ar` command error");

if !ar_output.status.success() {
panic!(
"Could not emit library file:\n{}",
String::from_utf8_lossy(&ar_output.stderr)
);
}

bindings
.write_to_file(out_rust_file)
.expect("Cound not write bindings to the Rust file");
}

fn main() {
setup_macro_test();
setup_wrap_static_fns_test();
}
36 changes: 36 additions & 0 deletions bindgen-integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ mod bindings {
include!(concat!(env!("OUT_DIR"), "/test.rs"));
}

mod extern_bindings {
include!(concat!(env!("OUT_DIR"), "/extern.rs"));
}

use std::ffi::CStr;
use std::mem;
use std::os::raw::c_int;
Expand Down Expand Up @@ -286,3 +290,35 @@ fn test_custom_derive() {
assert!(meter < lightyear);
assert!(meter > micron);
}

#[test]
fn test_wrap_static_fns() {
// GH-1090: https://github.com/rust-lang/rust-bindgen/issues/1090
unsafe {
let f = extern_bindings::foo();
assert_eq!(11, f);

let b = extern_bindings::bar();
assert_eq!(1, b);

let t = extern_bindings::takes_ptr(&mut 1);
assert_eq!(2, t);

extern "C" fn function(x: i32) -> i32 {
x + 1
}

let tp = extern_bindings::takes_fn_ptr(Some(function));
assert_eq!(2, tp);

let tf = extern_bindings::takes_fn(Some(function));
assert_eq!(3, tf);

let ta = extern_bindings::takes_alias(Some(function));
assert_eq!(4, ta);

let tq =
extern_bindings::takes_qualified(&(&5 as *const _) as *const _);
assert_eq!(5, tq);
}
}
2 changes: 1 addition & 1 deletion bindgen-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version = "0.1.0"
publish = false

[dev-dependencies]
bindgen = { path = "../bindgen", features = ["cli"] }
bindgen = { path = "../bindgen", features = ["cli", "experimental"] }
diff = "0.1"
shlex = "1"
clap = { version = "4", features = ["derive"] }
Expand Down
4 changes: 4 additions & 0 deletions bindgen-tests/tests/expectations/tests/generated/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated C, C++, Header files

This directory contains files for features where extra files are generated
as a part of the feature. For example, `--wrap-static-fns`.
14 changes: 14 additions & 0 deletions bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
int foo__extern(void) asm("foo__extern");
int foo__extern() { return foo(); }
int bar__extern(void) asm("bar__extern");
int bar__extern() { return bar(); }
int takes_ptr__extern(int *arg) asm("takes_ptr__extern");
int takes_ptr__extern(int *arg) { return takes_ptr(arg); }
int takes_fn_ptr__extern(int (*f) (int)) asm("takes_fn_ptr__extern");
int takes_fn_ptr__extern(int (*f) (int)) { return takes_fn_ptr(f); }
int takes_fn__extern(int (f) (int)) asm("takes_fn__extern");
int takes_fn__extern(int (f) (int)) { return takes_fn(f); }
int takes_alias__extern(func f) asm("takes_alias__extern");
int takes_alias__extern(func f) { return takes_alias(f); }
int takes_qualified__extern(const int *const *arg) asm("takes_qualified__extern");
int takes_qualified__extern(const int *const *arg) { return takes_qualified(arg); }
52 changes: 52 additions & 0 deletions bindgen-tests/tests/expectations/tests/wrap-static-fns.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions bindgen-tests/tests/headers/wrap-static-fns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// bindgen-flags: --experimental --wrap-static-fns

static inline int foo() {
return 11;
}
static int bar() {
return 1;
}
inline int baz() {
return 2;
}

static inline int takes_ptr(int* arg) {
return *arg + 1;
}

static inline int takes_fn_ptr(int (*f)(int)) {
return f(1);
}

static inline int takes_fn(int (f)(int)) {
return f(2);
}

typedef int (func)(int);

static inline int takes_alias(func f) {
return f(3);
}

static inline int takes_qualified(const int *const *arg) {
return **arg;
}
Loading