diff options
-rw-r--r-- | CONTRIBUTING.md | 22 | ||||
-rw-r--r-- | src/codegen/mod.rs | 7 | ||||
-rw-r--r-- | src/ir/context.rs | 31 | ||||
-rw-r--r-- | src/ir/item.rs | 14 | ||||
-rw-r--r-- | src/ir/item_kind.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 11 | ||||
-rw-r--r-- | src/options.rs | 9 |
7 files changed, 103 insertions, 1 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cbaaf3c4..8c347107 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ out to us in a GitHub issue, or stop by - [Overview](#overview) - [Running All Tests](#running-all-tests) - [Authoring New Tests](#authoring-new-tests) +- [Generating Graphviz Dot File](#generating-graphviz-dot-file) - [Automatic code formatting](#automatic-code-formatting) - [Debug Logging](#debug-logging) - [Using `creduce` to Minimize Test Cases](#using-creduce-to-minimize-test-cases) @@ -112,6 +113,27 @@ Then verify the new Rust bindings compile and pass some basic tests: $ cargo test -p tests_expectations ``` +## Generating Graphviz Dot Files + +We have a special thing which will help you debug your codegen context if something +will go wrong. It will generate a [`graphviz`](http://graphviz.org/pdf/dotguide.pdf) +dot file and then you can create a PNG from it with `graphviz` tool in your OS. + +Here is an example how it could be done: + +``` +$ cargo run -- example.hpp --emit-ir-graphviz output.dot +``` + +It will generate your graphviz dot file and then you will need tog +create a PNG from it with `graphviz`. + +Something like this: + +``` +$ dot -Tpng output.dot -o output.png +``` + ## Automatic code formatting We use [`rustfmt`](https://github.com/rust-lang-nursery/rustfmt) to enforce a diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 92e34874..ad6736b0 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2499,6 +2499,13 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> { } } + if let Some(path) = context.options().emit_ir_graphviz.as_ref() { + match context.emit_ir_graphviz(path.clone()) { + Ok(()) => info!("Your dot file was generated successfully into: {}", path), + Err(e) => error!("{}", e), + } + } + context.resolve_item(context.root_module()) .codegen(context, &mut result, &whitelisted_items, &()); diff --git a/src/ir/context.rs b/src/ir/context.rs index d2fb2bef..7383c09a 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -5,7 +5,7 @@ use super::int::IntKind; use super::item::{Item, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; use super::module::{Module, ModuleKind}; -use super::traversal::{self, Edge, ItemTraversal}; +use super::traversal::{self, Edge, ItemTraversal, Trace}; use super::ty::{FloatKind, TemplateDeclaration, Type, TypeKind}; use BindgenOptions; use cexpr; @@ -18,6 +18,8 @@ use std::cell::Cell; use std::collections::{HashMap, hash_map}; use std::collections::btree_map::{self, BTreeMap}; use std::fmt; +use std::fs::File; +use std::io::{self, Write}; use std::iter::IntoIterator; use syntax::ast::Ident; use syntax::codemap::{DUMMY_SP, Span}; @@ -1109,6 +1111,33 @@ impl<'ctx> BindgenContext<'ctx> { &self.options } + /// Output graphviz dot file. + pub fn emit_ir_graphviz(&self, path: String) -> io::Result<()> { + let file = try!(File::create(path)); + let mut dot_file = io::BufWriter::new(file); + writeln!(&mut dot_file, "digraph {{")?; + + let mut err: Option<io::Result<_>> = None; + + for (id, item) in self.items() { + writeln!(&mut dot_file, "{} {};", id.0, item.dot_attributes(self))?; + + item.trace(self, &mut |sub_id: ItemId, _edge_kind| { + match writeln!(&mut dot_file, "{} -> {};", id.0, sub_id.as_usize()) { + Ok(_) => {}, + Err(e) => err = Some(Err(e)), + } + }, &()); + + if err.is_some() { + return err.unwrap(); + } + } + + writeln!(&mut dot_file, "}}")?; + Ok(()) + } + /// Tokenizes a namespace cursor in order to get the name and kind of the /// namespace, fn tokenize_namespace(&self, diff --git a/src/ir/item.rs b/src/ir/item.rs index 5b785513..8f16a96f 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -372,6 +372,20 @@ impl Item { self.id } + /// Get this `Item`'s dot attributes. + pub fn dot_attributes(&self, ctx: &BindgenContext) -> String { + format!("[fontname=\"courier\", label=< \ + <table border=\"0\"> \ + <tr><td>ItemId({})</td></tr> \ + <tr><td>name</td><td>{}</td></tr> \ + <tr><td>kind</td><td>{}</td></tr> \ + </table> \ + >]", + self.id.as_usize(), + self.name(ctx).get(), + self.kind.kind_name()) + } + /// Get this `Item`'s parent's identifier. /// /// For the root module, the parent's ID is its own ID. diff --git a/src/ir/item_kind.rs b/src/ir/item_kind.rs index d9e4690c..3ff06731 100644 --- a/src/ir/item_kind.rs +++ b/src/ir/item_kind.rs @@ -32,6 +32,16 @@ impl ItemKind { } } + /// Transform our `ItemKind` into a string. + pub fn kind_name(&self) -> &'static str { + match *self { + ItemKind::Module(..) => "Module", + ItemKind::Type(..) => "Type", + ItemKind::Function(..) => "Function", + ItemKind::Var(..) => "Var" + } + } + /// Is this a module? pub fn is_module(&self) -> bool { self.as_module().is_some() @@ -175,6 +175,13 @@ impl Builder { self } + /// Set the output graphviz file. + pub fn emit_ir_graphviz<T: Into<String>>(mut self, path: T) -> Builder { + let path = path.into(); + self.options.emit_ir_graphviz = Some(path); + self + } + /// Whether the generated bindings should contain documentation comments or /// not. /// @@ -491,6 +498,9 @@ pub struct BindgenOptions { /// True if we should dump our internal IR for debugging purposes. pub emit_ir: bool, + /// Output graphviz dot file. + pub emit_ir_graphviz: Option<String>, + /// True if we should emulate C++ namespaces with Rust modules in the /// generated bindings. pub enable_cxx_namespaces: bool, @@ -595,6 +605,7 @@ impl Default for BindgenOptions { links: vec![], emit_ast: false, emit_ir: false, + emit_ir_graphviz: None, derive_debug: true, derive_default: false, enable_cxx_namespaces: false, diff --git a/src/options.rs b/src/options.rs index 49bad841..e54ee012 100644 --- a/src/options.rs +++ b/src/options.rs @@ -84,6 +84,11 @@ pub fn builder_from_flags<I> Arg::with_name("emit-ir") .long("emit-ir") .help("Output our internal IR for debugging purposes."), + Arg::with_name("emit-ir-graphviz") + .long("emit-ir-graphviz") + .help("Dump graphviz dot file.") + .value_name("path") + .takes_value(true), Arg::with_name("enable-cxx-namespaces") .long("enable-cxx-namespaces") .help("Enable support for C++ namespaces."), @@ -270,6 +275,10 @@ pub fn builder_from_flags<I> builder = builder.emit_ir(); } + if let Some(path) = matches.value_of("emit-ir-graphviz") { + builder = builder.emit_ir_graphviz(path); + } + if matches.is_present("enable-cxx-namespaces") { builder = builder.enable_cxx_namespaces(); } |