Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 59 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ quickcheck = "1.0"
quote = { version = "1", default-features = false }
regex = { version = "1.5.3", default-features = false }
rustc-hash = "2.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
shlex = "1"
similar = "2.2.1"
syn = "2.0"
Expand Down
2 changes: 2 additions & 0 deletions bindgen-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ owo-colors.workspace = true
prettyplease = { workspace = true, features = ["verbatim"] }
proc-macro2.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
shlex.workspace = true
similar = { workspace = true, features = ["inline"] }
syn.workspace = true
Expand Down
22 changes: 22 additions & 0 deletions bindgen-tests/tests/expectations/tests/emit-symbol-list-bar.rs

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

20 changes: 20 additions & 0 deletions bindgen-tests/tests/expectations/tests/emit-symbol-list-foo.rs

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"types": [
"foo_int_t",
"int",
"foo_struct",
"int",
"float",
"_bindgen_ty_id_9",
"int",
"void",
"int"
],
"functions": [
"foo_function"
],
"vars": [
"foo_global_var"
]
}
23 changes: 23 additions & 0 deletions bindgen-tests/tests/expectations/tests/emit-symbol-list.rs

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

11 changes: 11 additions & 0 deletions bindgen-tests/tests/expectations/tests/emit-symbol-list.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type my_int_t
type int
type my_struct
type int
type float
type _bindgen_ty_id_9
type int
type void
function my_function
type int
var my_global_var
15 changes: 15 additions & 0 deletions bindgen-tests/tests/headers/emit-symbol-list-bar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Dependent library header (like "bar" in the issue)
// bar depends on foo

#include "emit-symbol-list-foo.h"

typedef foo_int_t bar_int_t;

struct bar_struct {
struct foo_struct foo;
int z;
};

void bar_function(struct foo_struct* arg, struct bar_struct* arg2);

extern int bar_global_var;
12 changes: 12 additions & 0 deletions bindgen-tests/tests/headers/emit-symbol-list-foo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Base library header (like "foo" in the issue)

typedef int foo_int_t;

struct foo_struct {
int x;
float y;
};

void foo_function(int arg);

extern int foo_global_var;
12 changes: 12 additions & 0 deletions bindgen-tests/tests/headers/emit-symbol-list.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Test header for emit-symbol-list feature

typedef int my_int_t;

struct my_struct {
int x;
float y;
};

void my_function(int arg);

extern int my_global_var;
126 changes: 126 additions & 0 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,129 @@ fn test_wrap_static_fns() {
.unwrap();
}
}

/// Test the emit-symbol-list feature end-to-end, matching the use case from issue #3326:
/// - foo-sys generates bindings and emits a symbol list
/// - bar-sys (which depends on foo) uses that symbol list to blocklist foo's symbols
#[test]
fn test_emit_symbol_list() {
use bindgen::Builder;

let foo_header = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/headers/emit-symbol-list-foo.h"
);
let bar_header = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/headers/emit-symbol-list-bar.h"
);
let expected_foo_rust = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/expectations/tests/emit-symbol-list-foo.rs"
);
let expected_foo_symbols = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/expectations/tests/emit-symbol-list-foo.symbols"
);
let expected_bar_rust = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/expectations/tests/emit-symbol-list-bar.rs"
);

let observed_symbols = tempfile::NamedTempFile::new().unwrap();

// Step 1: Generate bindings for foo and emit symbol list
let foo_bindings = Builder::default()
.header(foo_header)
.clang_arg("--target=x86_64-unknown-linux")
.emit_symbol_list(observed_symbols.path())
.generate()
.expect("Failed to generate foo bindings");

// Check foo's generated Rust bindings
let observed_foo_rust = format_code(foo_bindings.to_string()).unwrap();
let expected_foo_rust_content =
fs::read_to_string(expected_foo_rust).unwrap();
let expected_foo_rust_formatted =
format_code(&expected_foo_rust_content).unwrap();

if observed_foo_rust != expected_foo_rust_formatted {
error_diff_mismatch(
&observed_foo_rust,
&expected_foo_rust_formatted,
Some(Path::new(foo_header)),
Path::new(expected_foo_rust),
)
.unwrap();
}

// Check foo's symbol list output
let observed_symbols_content =
fs::read_to_string(observed_symbols.path()).unwrap();
let expected_symbols_content =
fs::read_to_string(expected_foo_symbols).unwrap();

if observed_symbols_content != expected_symbols_content {
error_diff_mismatch(
&observed_symbols_content,
&expected_symbols_content,
Some(Path::new(foo_header)),
Path::new(expected_foo_symbols),
)
.unwrap();
}

// Step 2: Generate bindings for bar, using foo's symbol list as blocklist
// Include foo's bindings so bar can reference the blocklisted types.
//
// Note: In real usage, you'd typically `use foo_sys::*;` from a separate
// crate/module rather than `include` --- we use `include` here because
// expectation files are linted independently.
let mut bar_builder = Builder::default()
.header(bar_header)
.clang_arg("--target=x86_64-unknown-linux")
.clang_arg(concat!("-I", env!("CARGO_MANIFEST_DIR"), "/tests/headers"))
.raw_line("include!(\"emit-symbol-list-foo.rs\");");

// Parse the JSON symbol list and add blocklist entries
#[derive(serde::Deserialize)]
struct SymbolList {
types: Vec<String>,
functions: Vec<String>,
vars: Vec<String>,
}

let symbols: SymbolList =
serde_json::from_str(&observed_symbols_content).unwrap();

for name in &symbols.types {
bar_builder = bar_builder.blocklist_type(format!("^{name}$"));
}
for name in &symbols.functions {
bar_builder = bar_builder.blocklist_function(format!("^{name}$"));
}
for name in &symbols.vars {
bar_builder = bar_builder.blocklist_var(format!("^{name}$"));
}

let bar_bindings = bar_builder
.generate()
.expect("Failed to generate bar bindings");

// Check bar's generated Rust bindings (should NOT contain foo's symbols)
let observed_bar_rust = format_code(bar_bindings.to_string()).unwrap();
let expected_bar_rust_content =
fs::read_to_string(expected_bar_rust).unwrap();
let expected_bar_rust_formatted =
format_code(&expected_bar_rust_content).unwrap();

if observed_bar_rust != expected_bar_rust_formatted {
error_diff_mismatch(
&observed_bar_rust,
&expected_bar_rust_formatted,
Some(Path::new(bar_header)),
Path::new(expected_bar_rust),
)
.unwrap();
}
}
2 changes: 2 additions & 0 deletions bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ proc-macro2.workspace = true
quote.workspace = true
regex = { workspace = true, features = ["std", "unicode-perl"] }
rustc-hash.workspace = true
serde.workspace = true
serde_json.workspace = true
shlex.workspace = true
syn = { workspace = true, features = ["full", "extra-traits", "visit-mut"] }

Expand Down
Loading
Loading