Auto merge of #508 - impowski:graphviz_dot, r=fitzgen
Graphviz implementation
This will solve #484 . Right now it's really basic and I will change some of things in future commits like docs and other things.
r? @fitzgen
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cbaaf3c..8c34710 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,6 +15,7 @@
- [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 @@
$ 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 92e3487..ad6736b 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -2499,6 +2499,13 @@
}
}
+ 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 d2fb2be..7383c09 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -5,7 +5,7 @@
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::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 @@
&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 5b78551..8f16a96 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -372,6 +372,20 @@
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 d9e4690..3ff0673 100644
--- a/src/ir/item_kind.rs
+++ b/src/ir/item_kind.rs
@@ -32,6 +32,16 @@
}
}
+ /// 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()
diff --git a/src/lib.rs b/src/lib.rs
index 7bf9806..42363eb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -175,6 +175,13 @@
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 @@
/// 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 @@
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 49bad84..e54ee01 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -84,6 +84,11 @@
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 @@
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();
}