#!/usr/bin/env python
"""
Script that recursively follows the binary dependencies of an executable or
library file (with ldd) and outputs into Graphviz graph.
"""
__author__ = "Martin Blais <blais@furius.ca>"


# stdlib imports
import sys, re, commands
from os.path import basename
from subprocess import *
from collections import defaultdict
from itertools import imap


def main():
    import optparse
    parser = optparse.OptionParser(__doc__.strip())
    opts, args = parser.parse_args()
    if not args:
        parser.error("You must specify some files to process.")
        
    todo = args

    mre = re.compile('^(?:(?:\s*(\S*)\s+=>)?\s*(\S*)\s+\(0x[0-9a-fA-F]+\)|statically linked)\s*$')

    depends = defaultdict(set)
    while todo:
        fn = todo.pop()

        print >> sys.stderr, 'Processing: %s' % fn
        p = Popen(('/usr/bin/ldd', fn), stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()

        dset = depends[fn]
        for line in imap(str.strip, out.splitlines()):
            if not line:
                continue
            mo = mre.match(line)
            assert mo, repr(line)
            depfn = mo.group(2) or mo.group(1)
            if depfn is not None:
                dset.add(depfn)
                if depfn not in depends:
                    todo.append(depfn)

    print _pre
    for fn, dset in depends.iteritems():
        for dfn in dset:
            print '"%s" -> "%s";' % (fn, dfn)
    print _post
        

_pre = '''
digraph "source tree" {
    overlap=scale;
    size="8,10";
    ratio="fill";
    fontsize="16";
    fontname="Helvetica";
        clusterrank="local";
'''

_post = '''
}
'''

                
# Run main if loaded as a script
if __name__ == "__main__":
    main()

