summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/bin/bindgen.rs20
-rw-r--r--src/ir/context.rs6
-rw-r--r--src/ir/item.rs54
-rwxr-xr-xsrc/lib.rs65
-rw-r--r--src/uses.rs102
5 files changed, 231 insertions, 16 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs
index 2a7995e7..17385d85 100755
--- a/src/bin/bindgen.rs
+++ b/src/bin/bindgen.rs
@@ -84,6 +84,10 @@ Options:
matching <regex>. Same behavior on emptyness
than the type whitelisting.
+ --dummy-uses=<path> For testing purposes, generate a C/C++ file
+ containing dummy uses of all types defined in
+ the input header.
+
<clang-args> Options other than stated above are passed
directly through to clang.
";
@@ -182,6 +186,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
"--use-msvc-mangling" => {
options.msvc_mangling = true;
}
+ "--dummy-uses" => {
+ let dummy_path = iter.next()
+ .expect("--dummy-uses expects a file path");
+ options.dummy_uses = Some(dummy_path);
+ }
other if source_file.is_none() => {
source_file = Some(other.into());
}
@@ -193,6 +202,12 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
if let Some(source_file) = source_file.take() {
options.clang_args.push(source_file);
+ options.input_header = options.clang_args.last().cloned();
+ } else {
+ options.input_header = options.clang_args
+ .iter()
+ .find(|arg| arg.ends_with(".h") || arg.ends_with(".hpp"))
+ .cloned();
}
let out = if let Some(ref path_name) = dest_file {
@@ -244,9 +259,12 @@ pub fn main() {
let (options, out) = parse_args_or_exit(bind_args);
- let bindings = Bindings::generate(options, None)
+ let mut bindings = Bindings::generate(options, None)
.expect("Unable to generate bindings");
+ bindings.write_dummy_uses()
+ .expect("Unable to write dummy uses to file.");
+
bindings.write(out)
.expect("Unable to write bindings to file.");
}
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 6c56eefe..beccc514 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -384,8 +384,10 @@ impl<'ctx> BindgenContext<'ctx> {
// because we remove it before the end of this function.
self.gen_ctx = Some(unsafe { mem::transmute(&ctx) });
- self.resolve_typerefs();
- self.process_replacements();
+ if !self.collected_typerefs() {
+ self.resolve_typerefs();
+ self.process_replacements();
+ }
let ret = cb(self);
self.gen_ctx = None;
diff --git a/src/ir/item.rs b/src/ir/item.rs
index 4e893e35..690f4222 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -38,13 +38,44 @@ pub trait ItemCanonicalName {
/// }
/// ```
///
-/// For bar, the canonical path is `foo::BAR`, while the canonical name is just
-/// `BAR`.
+/// For bar, the canonical path is `vec!["foo", "BAR"]`, while the canonical
+/// name is just `"BAR"`.
pub trait ItemCanonicalPath {
/// Get the canonical path for this item.
fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String>;
}
+/// A trait for iterating over an item and its parents and up its ancestor chain
+/// up to (but not including) the implicit root module.
+pub trait ItemAncestors {
+ /// Get an iterable over this item's ancestors.
+ fn ancestors<'a, 'b>(&self, ctx: &'a BindgenContext<'b>) -> ItemAncestorsIter<'a, 'b>;
+}
+
+/// An iterator over an item and its ancestors.
+pub struct ItemAncestorsIter<'a, 'b>
+ where 'b: 'a,
+{
+ item: ItemId,
+ ctx: &'a BindgenContext<'b>,
+}
+
+impl<'a, 'b> Iterator for ItemAncestorsIter<'a, 'b>
+ where 'b: 'a,
+{
+ type Item = ItemId;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let item = self.ctx.resolve_item(self.item);
+ if item.parent_id() == self.item {
+ None
+ } else {
+ self.item = item.parent_id();
+ Some(item.id())
+ }
+ }
+}
+
/// A single identifier for an item.
///
/// TODO: Build stronger abstractions on top of this, like TypeId(ItemId)?
@@ -76,6 +107,25 @@ impl ItemCanonicalPath for ItemId {
}
}
+impl ItemAncestors for ItemId {
+ fn ancestors<'a, 'b>(&self,
+ ctx: &'a BindgenContext<'b>)
+ -> ItemAncestorsIter<'a, 'b> {
+ ItemAncestorsIter {
+ item: *self,
+ ctx: ctx,
+ }
+ }
+}
+
+impl ItemAncestors for Item {
+ fn ancestors<'a, 'b>(&self,
+ ctx: &'a BindgenContext<'b>)
+ -> ItemAncestorsIter<'a, 'b> {
+ self.id().ancestors(ctx)
+ }
+}
+
impl TypeCollector for ItemId {
type Extra = ();
diff --git a/src/lib.rs b/src/lib.rs
index 1592c275..1e840246 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -60,6 +60,7 @@ mod clang;
mod ir;
mod parse;
mod regex_set;
+mod uses;
#[cfg(rustfmt)]
mod codegen;
@@ -68,16 +69,17 @@ doc_mod!(clang, clang_docs);
doc_mod!(ir, ir_docs);
doc_mod!(parse, parse_docs);
doc_mod!(regex_set, regex_set_docs);
+doc_mod!(uses, uses_docs);
mod codegen {
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
}
-
use ir::context::BindgenContext;
use ir::item::{Item, ItemId};
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;
+
use std::borrow::Borrow;
use std::collections::HashSet;
use std::fs::OpenOptions;
@@ -118,10 +120,19 @@ pub fn builder() -> Builder {
impl Builder {
/// Set the input C/C++ header.
- pub fn header<T: Into<String>>(self, header: T) -> Builder {
+ pub fn header<T: Into<String>>(mut self, header: T) -> Builder {
+ let header = header.into();
+ self.options.input_header = Some(header.clone());
self.clang_arg(header)
}
+ /// Generate a C/C++ file that includes the header and has dummy uses of
+ /// every type defined in the header.
+ pub fn dummy_uses<T: Into<String>>(mut self, dummy_uses: T) -> Builder {
+ self.options.dummy_uses = Some(dummy_uses.into());
+ self
+ }
+
/// Hide the given type from the generated bindings.
pub fn hide_type<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.hidden_types.insert(arg.into());
@@ -200,7 +211,7 @@ impl Builder {
}
/// Generate the Rust bindings using the options built up thus far.
- pub fn generate(self) -> Result<Bindings, ()> {
+ pub fn generate<'ctx>(self) -> Result<Bindings<'ctx>, ()> {
Bindings::generate(self.options, None)
}
}
@@ -274,6 +285,13 @@ pub struct BindgenOptions {
/// The set of arguments to pass straight through to Clang.
pub clang_args: Vec<String>,
+
+ /// The input header file.
+ pub input_header: Option<String>,
+
+ /// Generate a dummy C/C++ file that includes the header and has dummy uses
+ /// of all types defined therein. See the `uses` module for more.
+ pub dummy_uses: Option<String>,
}
impl Default for BindgenOptions {
@@ -296,6 +314,8 @@ impl Default for BindgenOptions {
msvc_mangling: false,
raw_lines: vec![],
clang_args: vec![],
+ input_header: None,
+ dummy_uses: None,
}
}
}
@@ -314,20 +334,20 @@ pub enum LinkType {
}
/// Generated Rust bindings.
-#[derive(Debug, Clone)]
-pub struct Bindings {
+#[derive(Debug)]
+pub struct Bindings<'ctx> {
+ context: BindgenContext<'ctx>,
module: ast::Mod,
- raw_lines: Vec<String>,
}
-impl Bindings {
+impl<'ctx> Bindings<'ctx> {
/// Generate bindings for the given options.
///
/// Deprecated - use a `Builder` instead
#[deprecated]
pub fn generate(options: BindgenOptions,
span: Option<Span>)
- -> Result<Bindings, ()> {
+ -> Result<Bindings<'ctx>, ()> {
let span = span.unwrap_or(DUMMY_SP);
let mut context = BindgenContext::new(options);
@@ -339,8 +359,8 @@ impl Bindings {
};
Ok(Bindings {
+ context: context,
module: module,
- raw_lines: context.options().raw_lines.clone(),
})
}
@@ -376,11 +396,11 @@ impl Bindings {
try!(writer.write("/* automatically generated by rust-bindgen */\n\n"
.as_bytes()));
- for line in self.raw_lines.iter() {
+ for line in self.context.options().raw_lines.iter() {
try!(writer.write(line.as_bytes()));
try!(writer.write("\n".as_bytes()));
}
- if !self.raw_lines.is_empty() {
+ if !self.context.options().raw_lines.is_empty() {
try!(writer.write("\n".as_bytes()));
}
@@ -390,6 +410,29 @@ impl Bindings {
try!(eof(&mut ps.s));
ps.s.out.flush()
}
+
+ /// Generate and write dummy uses of all the types we parsed, if we've been
+ /// requested to do so in the options.
+ ///
+ /// See the `uses` module for more information.
+ pub fn write_dummy_uses(&mut self) -> io::Result<()> {
+ let file =
+ if let Some(ref dummy_path) = self.context.options().dummy_uses {
+ Some(try!(OpenOptions::new()
+ .write(true)
+ .truncate(true)
+ .create(true)
+ .open(dummy_path)))
+ } else {
+ None
+ };
+
+ if let Some(file) = file {
+ try!(uses::generate_dummy_uses(&mut self.context, file));
+ }
+
+ Ok(())
+ }
}
/// Determines whether the given cursor is in any of the files matched by the
diff --git a/src/uses.rs b/src/uses.rs
new file mode 100644
index 00000000..47f72da6
--- /dev/null
+++ b/src/uses.rs
@@ -0,0 +1,102 @@
+//! Take in our IR and output a C/C++ file with dummy uses of each IR type.
+//!
+//! Say that we had this C++ header, `header.hpp`:
+//!
+//! ```c++
+//! class Point {
+//! int x;
+//! int y;
+//! }
+//!
+//! enum Bar {
+//! THIS,
+//! THAT,
+//! OTHER
+//! }
+//! ```
+//!
+//! If we generated dummy uses for this header, we would get a `.cpp` file like
+//! this:
+//!
+//! ```c++
+//! #include "header.hpp"
+//!
+//! void dummy(Point*) {}
+//! void dummy(Bar*) {}
+//! ```
+//!
+//! This is useful because we can compile this `.cpp` file into an object file,
+//! and then compare its debugging information to the debugging information
+//! generated for our Rust bindings. These two sets of debugging information had
+//! better agree on the C/C++ types' physical layout, or else our bindings are
+//! incorrect!
+//!
+//! "But you still haven't explained why we have to generate the dummy uses" you
+//! complain. Well if the types are never used, then they are elided when the
+//! C/C++ compiler generates debugging information.
+
+use ir::context::BindgenContext;
+use ir::item::{Item, ItemAncestors, ItemCanonicalName};
+use std::io;
+
+// Like `canonical_path`, except we always take namespaces into account, ignore
+// the generated names of anonymous items, and return a `String`.
+//
+// TODO: Would it be easier to try and demangle the USR?
+fn namespaced_name(ctx: &BindgenContext, item: &Item) -> String {
+ let mut names: Vec<_> = item.ancestors(ctx)
+ .map(|id| ctx.resolve_item(id).canonical_name(ctx))
+ .filter(|name| !name.starts_with("_bindgen_"))
+ .collect();
+ names.reverse();
+ names.join("::")
+}
+
+/// Generate the dummy uses for all the items in the given context, and write
+/// the dummy uses to `dest`.
+pub fn generate_dummy_uses<W>(ctx: &mut BindgenContext,
+ mut dest: W)
+ -> io::Result<()>
+ where W: io::Write,
+{
+ ctx.gen(|ctx| {
+ let input_header = ctx.options()
+ .input_header
+ .as_ref()
+ .expect("Should not generate dummy uses without an input header");
+
+ try!(writeln!(dest, "/* automatically generated by rust-bindgen */"));
+ try!(writeln!(dest, ""));
+ try!(writeln!(dest, "#include \"{}\"", input_header));
+ try!(writeln!(dest, ""));
+
+ let type_items = ctx.whitelisted_items()
+ .map(|id| ctx.resolve_item(id))
+ .filter(|item| {
+ // We only want type items.
+ if let Some(ty) = item.kind().as_type() {
+ // However, we don't want anonymous types, as we can't
+ // generate dummy uses for them.
+ ty.name().is_some() &&
+ // Nor do we want builtin types or named template type
+ // arguments. Again, we can't generate dummy uses for
+ // these.
+ !ty.is_builtin_or_named() &&
+ // And finally, we won't be creating any dummy
+ // specializations, so ignore template declarations and
+ // partial specializations.
+ item.applicable_template_args(ctx).is_empty()
+ } else {
+ false
+ }
+ })
+ .map(|item| namespaced_name(ctx, item))
+ .enumerate();
+
+ for (idx, name) in type_items {
+ try!(writeln!(dest, "void dummy{}({}*) {{ }}", idx, name));
+ }
+
+ Ok(())
+ })
+}