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 3 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
36 changes: 36 additions & 0 deletions bindgen-cli/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,21 @@ 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 extern wrappers for inlined functions
#[arg(long, requires = "experimental")]
generate_extern_functions: bool,
pvdrz marked this conversation as resolved.
Show resolved Hide resolved
/// 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.
#[arg(long, requires = "experimental", value_name = "FILENAME")]
extern_functions_file_name: Option<String>,
#[arg(long, requires = "experimental", value_name = "DIRECTORY")]
/// Sets the directory path where any extra files must be created due to the presence of inlined functions.
extern_functions_directory: Option<String>,
/// Sets the suffix added to the extern wrapper functions generated for inlined functions.
#[arg(long, requires = "experimental", value_name = "SUFFIX")]
extern_function_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 +488,11 @@ where
with_derive_custom_struct,
with_derive_custom_enum,
with_derive_custom_union,
generate_extern_functions,
extern_functions_file_name,
extern_functions_directory,
extern_function_suffix,
experimental: _,
version,
clang_args,
} = command;
Expand Down Expand Up @@ -978,5 +998,21 @@ where
}
}

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

if let Some(file_name) = extern_functions_file_name {
builder = builder.extern_functions_file_name(file_name);
}

if let Some(directory) = extern_functions_directory {
builder = builder.extern_functions_directory(directory);
}

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

Ok((builder, output, verbose))
}
124 changes: 96 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,71 @@ fn main() {
"including stub via include dir must produce correct dep path",
);
}

fn setup_extern_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("generate-extern-functions.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))
.generate_extern_functions(true)
.extern_functions_directory(out_path.display().to_string())
.generate()
.expect("Unable to generate bindings");

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

let obj_path = out_path.join("extern.o");
let lib_path = out_path.join("libextern.a");

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

if !std::process::Command::new("ar")
.arg("rcs")
.arg(lib_path)
.arg(obj_path)
.output()
.expect("`ar` command error")
.status
.success()
{
panic!("Could not emit library file");
}

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

fn main() {
setup_macro_test();
setup_extern_test();
}
16 changes: 16 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,15 @@ fn test_custom_derive() {
assert!(meter < lightyear);
assert!(meter > micron);
}

#[test]
fn test_extern_bindings() {
// 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);
}
}
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

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

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, `--generated-extern-functions`.
2 changes: 2 additions & 0 deletions bindgen-tests/tests/expectations/tests/generated/extern.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int foo__extern() { return foo(); }
int bar__extern() { return bar(); }
2 changes: 2 additions & 0 deletions bindgen-tests/tests/expectations/tests/generated/extern.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int foo__extern();
int bar__extern();
11 changes: 11 additions & 0 deletions bindgen-tests/tests/headers/generate-extern-functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// bindgen-flags: --experimental --generate-extern-functions

static inline int foo() {
return 11;
}
static int bar() {
return 1;
}
inline int baz() {
return 2;
}
49 changes: 49 additions & 0 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,3 +713,52 @@ fn commandline_multiple_headers() {
.header("tests/headers/16-byte-alignment.h");
build_flags_output_helper(&bindings);
}

#[test]
fn test_extern_generated_headers() {
// This test is for testing diffs of the generated C source and header files
// TODO: If another such feature is added, convert this test into a more generic
// test that looks at `tests/headers/generated` directory.
let expect_path = PathBuf::from("tests/expectations/tests/generated");
println!("In path is ::: {}", expect_path.to_str().unwrap());

let generated_path = PathBuf::from(env::var("OUT_DIR").unwrap());
println!("Out path is ::: {}", generated_path.to_str().unwrap());

let _bindings = Builder::default()
.header("tests/headers/generate-extern-functions.h")
.generate_extern_functions(true)
.extern_functions_directory(generated_path.display().to_string())
.generate()
.expect("Failed to generate bindings");

let expected_c = fs::read_to_string(expect_path.join("extern.c"))
.expect("Could not read generated extern.c");
let expected_h = fs::read_to_string(expect_path.join("extern.h"))
.expect("Could not read generated extern.h");

let actual_c = fs::read_to_string(generated_path.join("extern.c"))
.expect("Could not read actual extern.c");
let actual_h = fs::read_to_string(generated_path.join("extern.h"))
.expect("Could not read actual extern.h");

if expected_c != actual_c {
error_diff_mismatch(
&actual_c,
&expected_c,
None,
Path::new(expect_path.join("extern.c").to_str().unwrap()),
)
.unwrap();
}

if expected_h != actual_h {
error_diff_mismatch(
&actual_h,
&expected_h,
None,
Path::new(expect_path.join("extern.h").to_str().unwrap()),
)
.unwrap();
}
}
1 change: 1 addition & 0 deletions bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ runtime = ["clang-sys/runtime"]
# Dynamically discover a `rustfmt` binary using the `which` crate
which-rustfmt = ["which"]
cli = []
experimental = []

# These features only exist for CI testing -- don't use them if you're not hacking
# on bindgen!
Expand Down
Loading