Paul Joseph Davis

Erlang Dependency Graph

Overview

Got distracted and wrote a small parser for dialyzer to print the dependencies for a set of Erlang beam files. Just used CouchDB sources as that's what I had handy.

Script

#! /usr/bin/env python

import os
import re
import subprocess as sp
import sys

edge_re = re.compile(r"(couch[^:]+):([^/]+)/\d+")

def dialyze(file):
    command = ' '.join([
        "dialyzer",
        "--build_plt",
        "-pa", "src/ibrowse",
        "-pa", "src/mochiweb",
        "-pa", "/usr/local/lib/erlang/lib",
        "-c", file
    ])
    pipe = sp.Popen(
        command, shell=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE
    )
    (stdout, stderr) = pipe.communicate(input="")
    for line in stdout.split("\n"):
        match = edge_re.match(line.strip())
        if not match:
            continue
        yield (match.group(1), match.group(2))

def start_graph():
    print "digraph G {"

def add_edges(file, edges):
    src = os.path.split(file)[1][:-len(".beam")]
    edges[src] = set()
    for (mod, fun) in dialyze(file):
        edges[src].add(mod)

def end_graph():
    print "}"

def main():
    if len(sys.argv) != 2:
        print "usage: %s code_dir" % sys.argv[0]
        exit(-1)

    start_graph()
    edges = {}
    for root, dnames, fnames in os.walk(sys.argv[1]):
        for fname in fnames:
            if not fname.endswith(".beam"):
                continue
            add_edges(os.path.join(root, fname), edges)
    keys = edges.keys()
    keys.sort(key=lambda x: len(edges[x]), reverse=True)
    for k in keys:
        for m in edges[k]:
            print "  %s -> %s;" % (k, m)
    end_graph()

if __name__ == '__main__':
    main()

Result

CouchDB Dependency Graph