www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Class Hierarchy Graphs

reply "w0rp" <devw0rp gmail.com> writes:
Hello everyone. I was talking in the IRC channel today and I 
mentioned something small I wrote a couple of years ago which 
generates a Graphviz DOT file based on ModuleInfo, so you can see 
a visual class hierarchy with interfaces included. There was some 
interest in this in the channel at the time.

The code doesn't do much more than that at the moment, it could 
offer a few more options including outputing the hierarchy in 
other formats, and the graph it generates could perhaps look 
better. I'm just wondering if anyone here is interested in having 
a tool like this.

If enough people are interested and have a few suggestions for 
some features they like which aren't crazy massive feature lists, 
I'd be willing to expand it a bit, add some documentation, and 
offer it as a DUB package.
Mar 26 2015
next sibling parent "w0rp" <devw0rp gmail.com> writes:
On Thursday, 26 March 2015 at 17:41:58 UTC, w0rp wrote:
 Hello everyone. I was talking in the IRC channel today and I 
 mentioned something small I wrote a couple of years ago which 
 generates a Graphviz DOT file based on ModuleInfo, so you can 
 see a visual class hierarchy with interfaces included. There 
 was some interest in this in the channel at the time.

 The code doesn't do much more than that at the moment, it could 
 offer a few more options including outputing the hierarchy in 
 other formats, and the graph it generates could perhaps look 
 better. I'm just wondering if anyone here is interested in 
 having a tool like this.

 If enough people are interested and have a few suggestions for 
 some features they like which aren't crazy massive feature 
 lists, I'd be willing to expand it a bit, add some 
 documentation, and offer it as a DUB package.
I forgot to link the GitHub repository. Here it is. https://github.com/w0rp/dhier
Mar 26 2015
prev sibling next sibling parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
On Thursday, 26 March 2015 at 17:41:58 UTC, w0rp wrote:
 Hello everyone. I was talking in the IRC channel today and I 
 mentioned something small I wrote a couple of years ago which 
 generates a Graphviz DOT file based on ModuleInfo, so you can 
 see a visual class hierarchy with interfaces included. There 
 was some interest in this in the channel at the time.

 The code doesn't do much more than that at the moment, it could 
 offer a few more options including outputing the hierarchy in 
 other formats, and the graph it generates could perhaps look 
 better. I'm just wondering if anyone here is interested in 
 having a tool like this.

 If enough people are interested and have a few suggestions for 
 some features they like which aren't crazy massive feature 
 lists, I'd be willing to expand it a bit, add some 
 documentation, and offer it as a DUB package.
+1 How much of a stretch would it be to extend the dependency graph to compositional types? struct Foo { mixin Bar!(a,b,c); } alias Qux = Baz!Foo; and so on?
Mar 26 2015
parent "w0rp" <devw0rp gmail.com> writes:
On Thursday, 26 March 2015 at 17:43:46 UTC, Vlad Levenfeld wrote:
 +1

 How much of a stretch would it be to extend the dependency 
 graph to compositional types?

 struct Foo {
   mixin Bar!(a,b,c);
 }

 alias Qux = Baz!Foo;

 and so on?
I'm not sure about that. I'd have to think about it a bit. Templates are probably trickier. Someone else has also suggested module dependency graphs. I could maybe offer something for that too.
Mar 26 2015
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/26/15 10:41 AM, w0rp wrote:
 Hello everyone. I was talking in the IRC channel today and I mentioned
 something small I wrote a couple of years ago which generates a Graphviz
 DOT file based on ModuleInfo, so you can see a visual class hierarchy
 with interfaces included. There was some interest in this in the channel
 at the time.

 The code doesn't do much more than that at the moment, it could offer a
 few more options including outputing the hierarchy in other formats, and
 the graph it generates could perhaps look better. I'm just wondering if
 anyone here is interested in having a tool like this.

 If enough people are interested and have a few suggestions for some
 features they like which aren't crazy massive feature lists, I'd be
 willing to expand it a bit, add some documentation, and offer it as a
 DUB package.
That would definitely be of interest. Also, if some of that can be reused to show inter-module dependencies that would be even better. Thanks! -- Andrei
Mar 26 2015
prev sibling parent reply "Dragos Carp" <dragoscarp gmail.com> writes:
On Thursday, 26 March 2015 at 17:41:58 UTC, w0rp wrote:
 If enough people are interested and have a few suggestions for 
 some features they like which aren't crazy massive feature 
 lists, I'd be willing to expand it a bit, add some 
 documentation, and offer it as a DUB package.
Very nice! PlantUML [1] output would be even nicer! [1] http://plantuml.sourceforge.net/classes.html
Mar 27 2015
parent reply "w0rp" <devw0rp gmail.com> writes:
On Friday, 27 March 2015 at 08:26:51 UTC, Dragos Carp wrote:
 On Thursday, 26 March 2015 at 17:41:58 UTC, w0rp wrote:
 If enough people are interested and have a few suggestions for 
 some features they like which aren't crazy massive feature 
 lists, I'd be willing to expand it a bit, add some 
 documentation, and offer it as a DUB package.
Very nice! PlantUML [1] output would be even nicer! [1] http://plantuml.sourceforge.net/classes.html
I'll give that a go. I was thinking before of offerring a DOT file with just the class and interface names, and another one with the class and interface names and some methods. I might support that and also PlantUML. I've been considering some kind of web-friendly format, but I'm not sure what to do for that yet. Maybe it's enough to have Graphviz output an SVG. Last night I simplified part of the code and re-arranged everything into a DUB package format, which wasn't too hard. I'll probably work on it over the weekend.
Mar 27 2015
parent reply "w0rp" <devw0rp gmail.com> writes:
I just added a module dependency graph generator by following a 
similar process to the library which I used for class 
hierarchies. I'd be willing to bet that Andrei wants to apply it 
to Phobos, and that is indeed a thing that can be done.

There's an example on GitHub, and the readme explains how to run 
it. The example outputs... this. (Try not to scream.)

https://i.imgur.com/Af4RKuc.png

So like you might expect for Phobos. Everything importing 
everything else, just about. I have a few comments on this.

The library just gives you the DOT file as output, not the image. 
Graphviz probably lets you pick different strategies for how a 
graph should be drawn. Drawing a graph is really complicated, so 
it's best to leave that to Graphviz. I believe you can specify 
drawing co-ordinates for vertices in a DOT file. In any case, how 
the graph is drawn in the end is a probably a user decision.

Like the previous class example, you can filter out modules from 
the output with a regular expression. You will probably have to 
do this to get sensible output, as you might not care which 
standard library modules you're depending on, or you might care, 
but not for a few modules in particular, etc.

The filters and the methods can be combined to only look at 
particular sets of modules at a time, so you can generate graphs 
for only a subset of Phobos, etc. This might help in making the 
imports look more like a binary tree.

Back on the class hierarchies. I hit a bit of a snag with that. I 
have been using ModuleInfo and ClassInfo to generate the 
diagrams, but ClassInfo is a runtime feature which has no 
knowledge of method or attribute names. So I might have to use 
compile time reflection all the way for that. So in short, I 
haven't thought of a good way to get method and attribute names 
into the output yet.

As an aside, ModuleInfo has been undocumented forever, as far as 
I can tell. Would it be possible to get some documentation for 
ModuleInfo up on the site? I've been figuring out how to use it 
just by reading object_.d. If it was documented, someone else 
would have probably written this stuff I'm writing by now 
already, because it's really simple code.
Mar 29 2015
next sibling parent "w0rp" <devw0rp gmail.com> writes:
In an attempt to get something, anything, easier to read, I wrote 
another function which produces a graph where the module nodes 
are ranked by their incoming edge counts. The only way I could 
figure out how to get Graphviz to do this was to write the 
numbers as edges too and so on. So it looks like this.

https://i.imgur.com/jPGdPRm.png

I don't know why Graphviz decides that the distance between the 
ranks and the proper graph must be so huge. There's probably a 
way to write the two as subgraphs, and then set the distance 
between them to be small. I used the othorgonal lines for this 
one. Maybe some node and edge colouring would make the lines 
easier to follow, but generally as more things import each other 
the graph will tend to look messier.
Mar 29 2015
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/29/15 4:24 AM, w0rp wrote:
 I just added a module dependency graph generator by following a similar
 process to the library which I used for class hierarchies. I'd be
 willing to bet that Andrei wants to apply it to Phobos, and that is
 indeed a thing that can be done.

 There's an example on GitHub, and the readme explains how to run it. The
 example outputs... this. (Try not to scream.)

 https://i.imgur.com/Af4RKuc.png

 So like you might expect for Phobos. Everything importing everything
 else, just about. I have a few comments on this.
Not screaming, it's as expected :o). This tool will be of good help for weeding some dependencies out. One other informative graph that should be much easier to generate is the direct imports graph (no transitivity). I should add that removing dependencies for Phobos should be approached with due consideration. It shouldn't be a goal in and of itself; users don't care for it. It does have positive effects such as easier debugging of the compiler that need to be appraised. [snip
 As an aside, ModuleInfo has been undocumented forever, as far as I can
 tell. Would it be possible to get some documentation for ModuleInfo up
 on the site? I've been figuring out how to use it just by reading
 object_.d. If it was documented, someone else would have probably
 written this stuff I'm writing by now already, because it's really
 simple code.
We should, and maybe we can take this opportunity to define it well. Once it's documented, it sets the stage for backward compatibility. Andrei
Mar 29 2015
parent reply "w0rp" <devw0rp gmail.com> writes:
On Sunday, 29 March 2015 at 15:40:56 UTC, Andrei Alexandrescu 
wrote:
 One other informative graph that should be much easier to 
 generate is the direct imports graph (no transitivity).
I had a go at it, and I have something for that now. This graph should show what std.regex imports. I'm not sure if anything is missing from the module info. I imagine there's a template which might need insantiating first in order to add in import line, and I'm not sure if the ModuleInfo will include all the scoped imports or not. I assume it does. https://i.imgur.com/r1TZVKO.png I added an extra method named 'addModuleWithDirectDepdencies' for adding a module to the set of modules and just its direct dependencies, not the transitive dependencies. Then I added an extra set to the structure for tracking which modules are supposed to be leaf nodes in the graph, and made the DOT functions check for that. I had to add the leaf checking, as the dependencies could be importing each other, and the structure was only storing nodes, not edges. I should maybe make the structures store just a directed graph, but whatever.
Mar 29 2015
next sibling parent "w0rp" <devw0rp gmail.com> writes:
On Sunday, 29 March 2015 at 16:18:40 UTC, w0rp wrote:
 I had to add the leaf checking, as the dependencies could be 
 importing each other, and the structure was only storing nodes, 
 not edges. I should maybe make the structures store just a 
 directed graph, but whatever.
Thinking about it a little more, I should probably change the API for classes and modules so the functions which write the DOT files take just a directed graph, and provide functions for building the directed graphs. Then you can play with the directed graphs as much as you want. I have a directed graph type already in my container library which would be suitable.
Mar 29 2015
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/29/15 9:18 AM, w0rp wrote:
 On Sunday, 29 March 2015 at 15:40:56 UTC, Andrei Alexandrescu wrote:
 One other informative graph that should be much easier to generate is
 the direct imports graph (no transitivity).
I had a go at it, and I have something for that now. This graph should show what std.regex imports. I'm not sure if anything is missing from the module info. I imagine there's a template which might need insantiating first in order to add in import line, and I'm not sure if the ModuleInfo will include all the scoped imports or not. I assume it does. https://i.imgur.com/r1TZVKO.png I added an extra method named 'addModuleWithDirectDepdencies' for adding a module to the set of modules and just its direct dependencies, not the transitive dependencies. Then I added an extra set to the structure for tracking which modules are supposed to be leaf nodes in the graph, and made the DOT functions check for that. I had to add the leaf checking, as the dependencies could be importing each other, and the structure was only storing nodes, not edges. I should maybe make the structures store just a directed graph, but whatever.
Awesome. Two more ideas: * imports in unittest mode vs. not (will show how good we are at making imports local) * direct imports but followed transitively, e.g. the graph for std.regex' imports would show the imports of the modules imported etc. Andrei
Mar 29 2015
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Sun, Mar 29, 2015 at 03:44:02PM -0700, Andrei Alexandrescu via Digitalmars-d
wrote:
[...]
 * direct imports but followed transitively, e.g. the graph for
 std.regex' imports would show the imports of the modules imported etc.
[...] That would be interesting to see! It would be a great help in reducing unnecessary dependencies between Phobos modules. T -- They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
Mar 29 2015
parent "w0rp" <devw0rp gmail.com> writes:
I have just updated my reposistory so instead of using a 
ClassHierarchyInfo type for class diagrams and a 
ModuleDependencyInfo type for module diagrams, now digraph types 
are used, coming from my container library. I had to update my 
container library a little to get it to work, as I had some 
problems with const and immutable types, but I needed to fix 
those issues anyway. So now class diagrams use 
Digraph!(const(ClassInfo)) and module diagrams use 
Digraph!(const(ModuleInfo*)).

I have made some implementation changes for my container library, 
but the API for the graphs should be the same, and it is 
documented here.

https://w0rp.com/project/dstruct/dstruct/graph/

So now you can play around with the graphs directly if you want, 
so you can say manually that module foo imports bar, when the 
helper functions can't figure it out. I'm not exactly sure what 
kind of graph you were after Andrei, as I have one thing for 
direct imports and another for everything transitively, but you 
can probably make whatever it is happen in a few ways. The actual 
method for it is pretty short, so I'll paste the whole thing 
here. (Adding an edge twice does effectively nothing, as the 
edges aren't weighted here.)

void addModuleWithDependencies(Dependencies strategy = 
Dependencies.all)
(ref ModuleGraph graph, const(ModuleInfo*) mod) {
     graph.addVertex(mod);

     // Add the imported modules.
     foreach(importedModule; mod.importedModules) {
         static if (strategy == Dependencies.all) {
             if (!graph.hasEdge(mod, importedModule)) {
                 graph.addEdge(mod, importedModule);

                 // Add recursive dependencies.
                 graph.addModuleWithDependencies(importedModule);
             }
         } else {
             graph.addEdge(mod, importedModule);
         }
     }
}

ModuleGraph is just an alias for the Digraph type mentioned 
before. I think my only pain point is that there's no way, at 
least that I know of, to just get ModuleInfo for a given module. 
I have to use a foreach over all modules until I find the module 
I'm after. It works, though.
Apr 06 2015