Skip to content

Commit ac49717

Browse files
author
bors-servo
authored
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
2 parents a10457f + da54412 commit ac49717

File tree

7 files changed

+103
-1
lines changed

7 files changed

+103
-1
lines changed

CONTRIBUTING.md

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ out to us in a GitHub issue, or stop by
1515
- [Overview](#overview)
1616
- [Running All Tests](#running-all-tests)
1717
- [Authoring New Tests](#authoring-new-tests)
18+
- [Generating Graphviz Dot File](#generating-graphviz-dot-file)
1819
- [Automatic code formatting](#automatic-code-formatting)
1920
- [Debug Logging](#debug-logging)
2021
- [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:
112113
$ cargo test -p tests_expectations
113114
```
114115

116+
## Generating Graphviz Dot Files
117+
118+
We have a special thing which will help you debug your codegen context if something
119+
will go wrong. It will generate a [`graphviz`](http://graphviz.org/pdf/dotguide.pdf)
120+
dot file and then you can create a PNG from it with `graphviz` tool in your OS.
121+
122+
Here is an example how it could be done:
123+
124+
```
125+
$ cargo run -- example.hpp --emit-ir-graphviz output.dot
126+
```
127+
128+
It will generate your graphviz dot file and then you will need tog
129+
create a PNG from it with `graphviz`.
130+
131+
Something like this:
132+
133+
```
134+
$ dot -Tpng output.dot -o output.png
135+
```
136+
115137
## Automatic code formatting
116138

117139
We use [`rustfmt`](https://github.com/rust-lang-nursery/rustfmt) to enforce a

src/codegen/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2499,6 +2499,13 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
24992499
}
25002500
}
25012501

2502+
if let Some(path) = context.options().emit_ir_graphviz.as_ref() {
2503+
match context.emit_ir_graphviz(path.clone()) {
2504+
Ok(()) => info!("Your dot file was generated successfully into: {}", path),
2505+
Err(e) => error!("{}", e),
2506+
}
2507+
}
2508+
25022509
context.resolve_item(context.root_module())
25032510
.codegen(context, &mut result, &whitelisted_items, &());
25042511

src/ir/context.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::int::IntKind;
55
use super::item::{Item, ItemCanonicalPath, ItemSet};
66
use super::item_kind::ItemKind;
77
use super::module::{Module, ModuleKind};
8-
use super::traversal::{self, Edge, ItemTraversal};
8+
use super::traversal::{self, Edge, ItemTraversal, Trace};
99
use super::ty::{FloatKind, TemplateDeclaration, Type, TypeKind};
1010
use BindgenOptions;
1111
use cexpr;
@@ -18,6 +18,8 @@ use std::cell::Cell;
1818
use std::collections::{HashMap, hash_map};
1919
use std::collections::btree_map::{self, BTreeMap};
2020
use std::fmt;
21+
use std::fs::File;
22+
use std::io::{self, Write};
2123
use std::iter::IntoIterator;
2224
use syntax::ast::Ident;
2325
use syntax::codemap::{DUMMY_SP, Span};
@@ -1109,6 +1111,33 @@ impl<'ctx> BindgenContext<'ctx> {
11091111
&self.options
11101112
}
11111113

1114+
/// Output graphviz dot file.
1115+
pub fn emit_ir_graphviz(&self, path: String) -> io::Result<()> {
1116+
let file = try!(File::create(path));
1117+
let mut dot_file = io::BufWriter::new(file);
1118+
writeln!(&mut dot_file, "digraph {{")?;
1119+
1120+
let mut err: Option<io::Result<_>> = None;
1121+
1122+
for (id, item) in self.items() {
1123+
writeln!(&mut dot_file, "{} {};", id.0, item.dot_attributes(self))?;
1124+
1125+
item.trace(self, &mut |sub_id: ItemId, _edge_kind| {
1126+
match writeln!(&mut dot_file, "{} -> {};", id.0, sub_id.as_usize()) {
1127+
Ok(_) => {},
1128+
Err(e) => err = Some(Err(e)),
1129+
}
1130+
}, &());
1131+
1132+
if err.is_some() {
1133+
return err.unwrap();
1134+
}
1135+
}
1136+
1137+
writeln!(&mut dot_file, "}}")?;
1138+
Ok(())
1139+
}
1140+
11121141
/// Tokenizes a namespace cursor in order to get the name and kind of the
11131142
/// namespace,
11141143
fn tokenize_namespace(&self,

src/ir/item.rs

+14
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,20 @@ impl Item {
372372
self.id
373373
}
374374

375+
/// Get this `Item`'s dot attributes.
376+
pub fn dot_attributes(&self, ctx: &BindgenContext) -> String {
377+
format!("[fontname=\"courier\", label=< \
378+
<table border=\"0\"> \
379+
<tr><td>ItemId({})</td></tr> \
380+
<tr><td>name</td><td>{}</td></tr> \
381+
<tr><td>kind</td><td>{}</td></tr> \
382+
</table> \
383+
>]",
384+
self.id.as_usize(),
385+
self.name(ctx).get(),
386+
self.kind.kind_name())
387+
}
388+
375389
/// Get this `Item`'s parent's identifier.
376390
///
377391
/// For the root module, the parent's ID is its own ID.

src/ir/item_kind.rs

+10
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ impl ItemKind {
3232
}
3333
}
3434

35+
/// Transform our `ItemKind` into a string.
36+
pub fn kind_name(&self) -> &'static str {
37+
match *self {
38+
ItemKind::Module(..) => "Module",
39+
ItemKind::Type(..) => "Type",
40+
ItemKind::Function(..) => "Function",
41+
ItemKind::Var(..) => "Var"
42+
}
43+
}
44+
3545
/// Is this a module?
3646
pub fn is_module(&self) -> bool {
3747
self.as_module().is_some()

src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ impl Builder {
175175
self
176176
}
177177

178+
/// Set the output graphviz file.
179+
pub fn emit_ir_graphviz<T: Into<String>>(mut self, path: T) -> Builder {
180+
let path = path.into();
181+
self.options.emit_ir_graphviz = Some(path);
182+
self
183+
}
184+
178185
/// Whether the generated bindings should contain documentation comments or
179186
/// not.
180187
///
@@ -491,6 +498,9 @@ pub struct BindgenOptions {
491498
/// True if we should dump our internal IR for debugging purposes.
492499
pub emit_ir: bool,
493500

501+
/// Output graphviz dot file.
502+
pub emit_ir_graphviz: Option<String>,
503+
494504
/// True if we should emulate C++ namespaces with Rust modules in the
495505
/// generated bindings.
496506
pub enable_cxx_namespaces: bool,
@@ -595,6 +605,7 @@ impl Default for BindgenOptions {
595605
links: vec![],
596606
emit_ast: false,
597607
emit_ir: false,
608+
emit_ir_graphviz: None,
598609
derive_debug: true,
599610
derive_default: false,
600611
enable_cxx_namespaces: false,

src/options.rs

+9
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ pub fn builder_from_flags<I>
8484
Arg::with_name("emit-ir")
8585
.long("emit-ir")
8686
.help("Output our internal IR for debugging purposes."),
87+
Arg::with_name("emit-ir-graphviz")
88+
.long("emit-ir-graphviz")
89+
.help("Dump graphviz dot file.")
90+
.value_name("path")
91+
.takes_value(true),
8792
Arg::with_name("enable-cxx-namespaces")
8893
.long("enable-cxx-namespaces")
8994
.help("Enable support for C++ namespaces."),
@@ -270,6 +275,10 @@ pub fn builder_from_flags<I>
270275
builder = builder.emit_ir();
271276
}
272277

278+
if let Some(path) = matches.value_of("emit-ir-graphviz") {
279+
builder = builder.emit_ir_graphviz(path);
280+
}
281+
273282
if matches.is_present("enable-cxx-namespaces") {
274283
builder = builder.enable_cxx_namespaces();
275284
}

0 commit comments

Comments
 (0)