Building a static GraphViz for macOS

Note: This is a very old post from 2020 I had sitting in my drafts. I figured I would publish it in case it was useful for someone else. Notably, this predates Apple Silicon, so yo would want to build another version that’s ARM and use lipo to make a universal binary. This is left as an exercise to the reader.

I recently had the need for building GraphViz without any external dependencies other than what comes with macOS, as part of an application calling into it. While on Windows, I used their provided binaries, I was on my own for macOS since I wasn’t willing to ship half of Homebrew with my application. Instead, I took a simpler way.

It turns out that out of the box, you can have an effective GraphViz without any dependencies other than things like expat and zlib that ship with the system. It’s just a normal autotools project after all – I don’t even have brew and friends installed. However, GraphViz has a somewhat involved plugin system involving ltdl and prefixes with configuration files. We can simplify this even further with some additional configuration options. We probably also want to specify the minimum version of macOS we support (in my case, Sierra) to the linker and compiler, and enable the specific plugins (in my case, enable Quartz, disable Visio) we want.

# Export these if you need a specific SDK
LDFLAGS="-Wl,-macosx_version_min,10.12" CPPFLAGS="-mmacosx-version-min=10.12"
# Some configure flags (you can enable Visio if you want, it's on by default)
./configure --with-quartz=yes --enable-static --disable-shared --with-visio=no --disable-ltdl

Unfortunately, out of the box we don’t have any PDF distillers the build system likes, so it freaks out when you make and it hits a PDF target. Since I don’t care about documentation for this build and I didn’t see any option to disable it, so I did even better: just replace false to true in the generated makefiles. (This isn’t even the worst part of it.)

for i in $(find . -name Makefile); do echo $i; sed -i bak "s/false /true /g" $i; done

Once that’s done, the static binary in cmd/dot/dot_static can be used for what you need, and placed anywhere, and renamed/hardlinked to provide the different graph types. However, there’s one problem – this dot only supports the basic plugins like plain and PostScript output. I wanted PDF, and we can do this through Quartz. The Quartz plugin isn’t going to load however, because we lack external plugin support – we just have its objects. There is one more hack we can do, and it is extremely cursed. (If there’s a better way to do this, please tell me.)

This mailing list post was useful for my understanding how to hack on GraphViz plugin loading. What we do with this is hack at the dot_builtins.c file, add the Quartz plugin to it. We then need to modify the build system to do this. The reasonable answer would be using your local copy of autoconf, automake, and the rest of the autotools family to add what you need (that is, a Quartz plugin dependency as it does the other hard plugin dependencies it has, and the framework required). However, I didn’t have autotools installed, so the next best option? Modifying the built Makefiles to include them, of course!

I added -framework ApplicationServices to LDFLAGS (otherwise when we include the static archive for the Quartz plugin, it explodes because it can’t find the CoreGraphics symbols), and wherever I saw a hard plugin dependency (to build it in statically), I copied the line and modified it for Quartz (i.e ones mentioning neato_layout). Once this was done, and it rebuilt, the dot_static binary had a lot more outputs like PDF available through Quartz, and still didn’t need anything more than what came with macOS.

Leave a Reply

Your email address will not be published. Required fields are marked *