#!/scratchbox/tools/bin/python

# Copyright 2005  Movial
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# lib/graph.py -- Generates dependency diagrams and writes them to .dot
# files.  Needs the pydot and pyparsing libraries (not provided by
# Scratchbox; they need to be built by hand).  See http://dkbza.org/pydot.html.

import sys
import pydot
import parse, util

# Python 2.3 doesn't have the built-in set keyword.
import sets
set = sets.Set

##
## Dependencies

def draw_binaries(options, params):
	"""Generates a diagram with Essential binary packages, binary
	   packages listed in PARAMS and their dependencies in separate
	   clusters.  If "--no-deps" is in OPTIONS, the dependencies are
	   not included.  If "--no-sbox" is in OPTIONS, the packages in
	   Scratchbox's toolchain are removed from the list.  The diagram
	   is written into the "binaries.dot" file."""

	graph = pydot.Dot(type="digraph")

	essential = pydot.Cluster("essential", style="filled", color="red")
	dependencies = pydot.Cluster("dependencies", style="filled", color="white")
	graph.add_subgraph(essential)
	graph.add_subgraph(dependencies)

	binaries = util.get_binaries(options, params)
	for binary in binaries:
		if binary.essential:
			node = pydot.Node(binary.package, style="filled", color="white")
			essential.add_node(node)
		else:
			node = pydot.Node(binary.package, style="filled", color="yellow")
			dependencies.add_node(node)

		for name in binary.all_depends():
			if "--no-sbox" in options or not util.toolchain_contains(name):
				dep = util.lookup_binary(name)

				if binary.essential >= dep.essential:
					edge = pydot.Edge(binary.package, dep.package)
					graph.add_edge(edge)

	success = graph.write("binaries.dot")
	if not success:
		raise Exception, "Failed to write graph"

##
## Main

actions = {
	"binaries": (draw_binaries, "[--no-deps] [--no-sbox] <packages>")
}

def usage():
	print >>sys.stderr, "Usage:", sys.argv[0], "<action> [<options>] [<parameters>]"
	print >>sys.stderr

	print >>sys.stderr, "Actions:"
	for action in actions:
		func, sig = actions[action]
		print >>sys.stderr, "    %s\t%s" % (action, sig)
	print >>sys.stderr

	sys.exit(1)

def main(args):
	if len(args) < 1:
		usage()

	action = args[0]
	if action == "-h" or action.endswith("help"):
		usage()

	if action not in actions:
		usage()

	func, sig = actions.get(action)

	options = []
	params = []

	for arg in args[1:]:
		if arg[0] == "-":
			options.append(arg)
		else:
			params.append(arg)

	try:
		func(options, params)

	except KeyboardInterrupt:
		sys.exit(1)

if __name__ == "__main__":
	main(sys.argv[1:])

