Skip to content

Commit cfdf15f

Browse files
committed
Rewrite the core of the binding generator.
TL;DR: The binding generator is a mess as of right now. At first it was funny (in a "this is challenging" sense) to improve on it, but this is not sustainable. The truth is that the current architecture of the binding generator is a huge pile of hacks, so these few days I've been working on rewriting it with a few goals. 1) Have the hacks as contained and identified as possible. They're sometimes needed because how clang exposes the AST, but ideally those hacks are well identified and don't interact randomly with each others. As an example, in the current bindgen when scanning the parameters of a function that references a struct clones all the struct information, then if the struct name changes (because we mangle it), everything breaks. 2) Support extending the bindgen output without having to deal with clang. The way I'm aiming to do this is separating completely the parsing stage from the code generation one, and providing a single id for each item the binding generator provides. 3) No more random mutation of the internal representation from anywhere. That means no more Rc<RefCell<T>>, no more random circular references, no more borrow_state... nothing. 4) No more deduplication of declarations before code generation. Current bindgen has a stage, called `tag_dup_decl`[1], that takes care of deduplicating declarations. That's completely buggy, and for C++ it's a complete mess, since we YOLO modify the world. I've managed to take rid of this using the clang canonical declaration, and the definition, to avoid scanning any type/item twice. 5) Code generation should not modify any internal data structure. It can lookup things, traverse whatever it needs, but not modifying randomly. 6) Each item should have a canonical name, and a single source of mangling logic, and that should be computed from the inmutable state, at code generation. I've put a few canonical_name stuff in the code generation phase, but it's still not complete, and should change if I implement namespaces. Improvements pending until this can land: 1) Add support for missing core stuff, mainly generating functions (note that we parse the signatures for types correctly though), bitfields, generating C++ methods. 2) Add support for the necessary features that were added to work around some C++ pitfalls, like opaque types, etc... 3) Add support for the sugar that Manish added recently. 4) Optionally (and I guess this can land without it, because basically nobody uses it since it's so buggy), bring back namespace support. These are not completely trivial, but I think I can do them quite easily with the current architecture. I'm putting the current state of affairs here as a request for comments... Any thoughts? Note that there are still a few smells I want to eventually re-redesign, like the ParseError::Recurse thing, but until that happens I'm way happier with this kind of architecture. I'm keeping the old `parser.rs` and `gen.rs` in tree just for reference while I code, but they will go away. [1]: https://github.com/Yamakaky/rust-bindgen/blob/master/src/gen.rs#L448
1 parent bbd6b2c commit cfdf15f

File tree

142 files changed

+8115
-6967
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+8115
-6967
lines changed

Cargo.toml

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
[package]
2-
authors = ["Jyun-Yan You <jyyou.tw@gmail.com>"]
2+
authors = [
3+
"Jyun-Yan You <jyyou.tw@gmail.com>",
4+
"Emilio Cobos Álvarez <ecoal95@gmail.com>",
5+
"The Servo project developers",
6+
]
37
build = "build.rs"
48
description = "A binding generator for Rust"
5-
homepage = "https://github.com/crabtw/rust-bindgen"
9+
homepage = "https://github.com/servo/rust-bindgen"
610
keywords = ["bindings", "ffi", "code-generation"]
711
license = "BSD-3-Clause"
812
name = "bindgen"
913
readme = "README.md"
10-
repository = "https://github.com/crabtw/rust-bindgen"
11-
version = "0.16.0"
14+
repository = "https://github.com/servo/rust-bindgen"
15+
version = "0.17.0"
1216

1317
[[bin]]
1418
doc = false
@@ -20,22 +24,24 @@ quasi_codegen = "0.15"
2024
[dependencies]
2125
clang-sys = "0.8.0"
2226
docopt = "0.6.82"
23-
libc = "0.2.*"
24-
log = "0.3.*"
27+
libc = "0.2"
28+
log = "0.3"
29+
env_logger = "0.3"
2530
rustc-serialize = "0.3.19"
26-
syntex_syntax = "0.38"
31+
syntex_syntax = "0.43"
32+
regex = "0.1"
2733

2834
[dependencies.aster]
2935
features = ["with-syntex"]
30-
version = "0.21.1"
36+
version = "0.26"
3137

3238
[dependencies.clippy]
3339
optional = true
3440
version = "*"
3541

3642
[dependencies.quasi]
3743
features = ["with-syntex"]
38-
version = "0.15"
44+
version = "0.19"
3945

4046
[features]
4147
llvm_stable = []

build.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ mod codegen {
55

66
pub fn main() {
77
let out_dir = env::var_os("OUT_DIR").unwrap();
8-
let src = Path::new("src/gen.rs");
9-
let dst = Path::new(&out_dir).join("gen.rs");
8+
let src = Path::new("src/codegen/mod.rs");
9+
let dst = Path::new(&out_dir).join("codegen.rs");
1010

1111
quasi_codegen::expand(&src, &dst).unwrap();
12-
println!("cargo:rerun-if-changed=src/gen.rs");
12+
println!("cargo:rerun-if-changed=src/codegen/mod.rs");
13+
println!("cargo:rerun-if-changed=src/codegen/helpers.rs");
1314
}
1415
}
1516

src/bin/bindgen.rs

+43-40
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,20 @@
22
#![crate_type = "bin"]
33

44
extern crate bindgen;
5+
extern crate env_logger;
56
#[macro_use]
67
extern crate docopt;
78
#[macro_use]
89
extern crate log;
910
extern crate clang_sys;
1011
extern crate rustc_serialize;
1112

12-
use bindgen::{Bindings, BindgenOptions, LinkType, Logger};
13+
use bindgen::{Bindings, BindgenOptions, LinkType};
1314
use std::io;
1415
use std::path;
1516
use std::env;
1617
use std::default::Default;
1718
use std::fs;
18-
use std::process::exit;
19-
20-
struct StdLogger;
21-
22-
impl Logger for StdLogger {
23-
fn error(&self, msg: &str) {
24-
println!("{}", msg);
25-
}
26-
27-
fn warn(&self, msg: &str) {
28-
println!("{}", msg);
29-
}
30-
}
3119

3220
const USAGE: &'static str = "
3321
Usage:
@@ -40,6 +28,9 @@ Usage:
4028
[--dtor-attr=<attr>...] \
4129
[--opaque-type=<type>...] \
4230
[--blacklist-type=<type>...] \
31+
[--whitelist-type=<type>...] \
32+
[--whitelist-function=<name>...] \
33+
[--whitelist-var=<name>...] \
4334
<input-header> \
4435
[-- <clang-args>...]
4536
@@ -95,15 +86,25 @@ Options:
9586
ulonglong
9687
slonglong
9788
98-
--raw-line=<raw> TODO
99-
--dtor-attr=<attr> TODO
100-
--no-class-constants TODO
101-
--no-unstable-rust TODO
102-
--no-namespaced-constants TODO
103-
--no-bitfield-methods TODO
104-
--ignore-methods TODO
105-
--opaque-type=<type> TODO
106-
--blacklist-type=<type> TODO
89+
--raw-line=<raw> Add a raw line at the beginning of the output.
90+
--dtor-attr=<attr> Attributes to add to structures with destructor.
91+
--no-class-constants Avoid generating class constants.
92+
--no-unstable-rust Avoid generating unstable rust.
93+
--no-namespaced-constants Avoid generating constants right under namespaces.
94+
--no-bitfield-methods Avoid generating methods for bitfield access.
95+
--ignore-methods Avoid generating all kind of methods.
96+
--opaque-type=<type> Mark a type as opaque.
97+
--blacklist-type=<type> Mark a type as hidden.
98+
--whitelist-type=<type> Whitelist the type. If this set or any other
99+
of the whitelisting sets is not empty, then
100+
all the non-whitelisted types (or dependant)
101+
won't be generated.
102+
--whitelist-function=<regex> Whitelist all the free-standing functions
103+
matching <regex>. Same behavior on emptyness
104+
than the type whitelisting.
105+
--whitelist-var=<regex> Whitelist all the free-standing variables
106+
matching <regex>. Same behavior on emptyness
107+
than the type whitelisting.
107108
108109
<clang-args> Options other than stated above are passed
109110
directly through to clang.
@@ -134,6 +135,9 @@ struct Args {
134135
flag_ignore_methods: bool,
135136
flag_opaque_type: Vec<String>,
136137
flag_blacklist_type: Vec<String>,
138+
flag_whitelist_type: Vec<String>,
139+
flag_whitelist_function: Vec<String>,
140+
flag_whitelist_var: Vec<String>,
137141
arg_clang_args: Vec<String>,
138142
}
139143

@@ -182,7 +186,10 @@ impl Into<ParseResult<(BindgenOptions, Box<io::Write>)>> for Args {
182186
options.gen_bitfield_methods = !self.flag_no_bitfield_methods;
183187
options.ignore_methods = self.flag_ignore_methods;
184188
options.opaque_types.extend(self.flag_opaque_type.drain(..));
185-
options.blacklist_type.extend(self.flag_blacklist_type.drain(..));
189+
options.hidden_types.extend(self.flag_blacklist_type.drain(..));
190+
options.whitelisted_types.extend(self.flag_whitelist_type.drain(..));
191+
options.whitelisted_functions.extend(self.flag_whitelist_function.drain(..));
192+
options.whitelisted_vars.extend(self.flag_whitelist_var.drain(..));
186193
options.clang_args.extend(self.arg_clang_args.drain(..));
187194
options.clang_args.push(self.arg_input_header);
188195

@@ -191,6 +198,13 @@ impl Into<ParseResult<(BindgenOptions, Box<io::Write>)>> for Args {
191198
}
192199

193200
pub fn main() {
201+
log::set_logger(|max_log_level| {
202+
use env_logger::Logger;
203+
let env_logger = Logger::new();
204+
max_log_level.set(env_logger.filter());
205+
Box::new(env_logger)
206+
}).expect("Failed to set logger.");
207+
194208
let mut bind_args: Vec<_> = env::args().collect();
195209

196210
if let Some(clang) = clang_sys::support::Clang::find(None) {
@@ -217,24 +231,13 @@ pub fn main() {
217231
.and_then(|d| d.argv(bind_args.iter()).decode())
218232
.unwrap_or_else(|e| e.exit());
219233

220-
let logger = StdLogger;
221234
let result: ParseResult<_> = args.into();
222235
let (options, out) = result.unwrap_or_else(|msg| {
223-
logger.error(&msg);
224-
exit(-1);
236+
panic!("Failed to generate_bindings: {:?}", msg);
225237
});
226238

227-
match Bindings::generate(&options, Some(&logger as &Logger), None) {
228-
Ok(bindings) => match bindings.write(out) {
229-
Ok(()) => (),
230-
Err(e) => {
231-
logger.error(&format!("Unable to write bindings to file. {}", e));
232-
exit(-1);
233-
}
234-
},
235-
Err(()) => {
236-
logger.error("Failed to generate bindings".into());
237-
exit(-1);
238-
}
239-
}
239+
let bindings = Bindings::generate(options, None)
240+
.expect("Unable to generate bindings");
241+
bindings.write(out)
242+
.expect("Unable to write bindings to file.");
240243
}

0 commit comments

Comments
 (0)