Skip to content

Graphviz implementation #508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 16, 2017
Merged
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
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, &());

Expand Down
31 changes: 30 additions & 1 deletion src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions src/ir/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions src/ir/item_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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."),
Expand Down Expand Up @@ -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();
}
Expand Down