Monday, October 23, 2023

[Yukon Beta Blog] Graphing Unit Dependencies

"A picture is worth a thousand words lines of code"

Obligatory disclaimer:

"This blog post is based on a pre-release version of the RAD Studio software and it has been written with specific permission by Embarcadero. No feature is committed until the product GA release."

The Delphi 12 compiler can generate a graph of unit dependencies for a project. Other projects exist that do graphing, but it's nice to have it available out of the box with the compiler doing the heavy lifting and generating the output automatically.

The generated graph file (projectname.gv) uses the DOT graph description language which was created along with the open source GraphViz graph visualisation software originally started by AT&T.

GraphViz has an extensive command line interface for manipulating DOT files and creating visual diagrams with support for different layout engines and output formats. And since the file format is open source and plain text, there are lots of third-party tools available, including some online editors/viewers.


How Is It Used?

I created a simple VCL project named Project1 with two units. For good measure, these units refer to each other, creating a circular reference. If I add the --graphviz parameter when compiling, a separate file is created - Project1.gv. This can be done in the IDE under Additional options to pass to the compiler, using the command line compiler with this parameter or with MSBuild using /property.

An empty Delphi application includes references to a bunch of system units, resulting in a fairly large and complicated diagram. You can always export the graph as SVG and zoom and pan around, but it does seem a little excessive.

Update: I put the full Project1.gv output on pastebin in case someone wants to use the GraphViz tools or one of the online viewers to generate the graph for themselves or save to SVG. This image uses the DOT layout engine. Maybe also try the Circo or FDP layouts.


That's a Complicated Graph

It really is. Especially for such a tiny project. You can use --graphviz-exclude to ignore system.*, vcl.* (or fmx.*) and winapi.* units to generate something more manageable:

Add this to the command line compile or to Additionl Options in project settings:

--graphviz --graphviz-exclude=system.*;vcl.*;winapi.*

I prefer MSBuild, where you can use the /property (or /p) parameter to do the same thing:

msbuild Project1.dproj /p:DCC_AdditionalSwitches="--graphviz --graphviz-exclude=system.*;vcl.*;winapi.*"

Which gives us a more readable graph:

digraph Project1 {
	Project1 -> { Unit1 Unit2 }
	Unit1 -> Unit2
	Unit2 -> Unit1 [arrowhead=open,style=dashed]
}


Now it's much easier to spot that circular reference I mentioned before. Units used in the interface section have a solid line and units used in the implementation section have a dashed line.

Aside from getting a visual representation of a project's layout and dependencies, identifying circular references was a big driving factor for this feature. They significantly affect compiler performance and if there are a lot of them in a project, I consider that to be a code smell.


What Next?

This feature is described as "experimental", which means Embarcadero could use your feedback. Do you like it? How would you change it? Please be specific when you describe your use case and file a feature request in quality portal. That's still the best way to get their attention.

I haven't used GraphViz before, but now I understand that there are a tonne of third-party tools and I'm curious to see what's available. Or better yet, what I might be able to write myself. As I wrote earlier, the .gv file is plain text and well documented.

Even if you exclude system units, non-trivial projects could still generate large, complicated graphs. It might be useful to parse the .gv file and create diagrams for subsets of the project that you're interested in.

It's nice that circular references are visible in generated graphs, but since the raw data is available, it would probably be more productive to parse that and report excessive circular references automatically, maybe as part of the regular build script.

I really like this feature. At the very least, it's a good start.


2 comments:

Hallvards New Blog said...

Hi Bruce,

Very nice - as you say there are third party tools that do similar stuff, but having it in the box and having the compiler generate it is a major plus.

Could you generate and post a link to the SVG version of the VCL unit dependency graph - the PNG version is mostly unreadable here..

/Hallvard

Bruce McGee said...

Hi Hallvard,

Thanks.

The point of the image was to demonstrate how involved these graphs can get, so it wasn't intended to be very readable.

But you make a good point. I've updated the post to include the full Project1.gv file that you can use to generate and export the graphs yourself.