Partially wroking ASCII dependency graph.

This commit is contained in:
Todd Gamblin 2014-12-29 00:03:35 -08:00
parent 860f834aad
commit 6ffcdc1166
2 changed files with 187 additions and 1 deletions

View File

@ -44,7 +44,15 @@ def spec(parser, args):
print "Normalized" print "Normalized"
print "------------------------------" print "------------------------------"
spec.normalize() spec.normalize()
print spec.tree(color=True, indent=2) print spec.tree(color=True, indent=2, cover='paths')
print
print spec.topological_sort(reverse=True)
print
print "Graph"
print "------------------------------"
print spec.graph()
return
print "Concretized" print "Concretized"
print "------------------------------" print "------------------------------"

View File

@ -93,6 +93,7 @@
import sys import sys
import itertools import itertools
import hashlib import hashlib
from heapq import *
from StringIO import StringIO from StringIO import StringIO
from operator import attrgetter from operator import attrgetter
@ -712,6 +713,15 @@ def flat_dependencies(self, **kwargs):
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message) raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
def index(self):
"""Return DependencyMap that points to all the dependencies in this
spec."""
dm = DependencyMap()
for spec in self.traverse():
dm[spec.name] = spec
return dm
def flatten(self): def flatten(self):
"""Pull all dependencies up to the root (this spec). """Pull all dependencies up to the root (this spec).
Merge constraints for dependencies with the same name, and if they Merge constraints for dependencies with the same name, and if they
@ -1316,6 +1326,174 @@ def tree(self, **kwargs):
return out return out
def graph(self, **kwargs):
N = kwargs.get('node', 'o') # Node character
out = kwargs.get('out', sys.stdout)
indent = kwargs.get('indent', 0)
indent *= ' '
topo_order = self.topological_sort(reverse=True)
clone = self.copy()
nodes = clone.index()
def ordered_deps(node):
deps = node.dependencies
return sorted((d for d in deps), reverse=True)
frontier = []
debug = True
debug = False
def back_edge(end, start):
assert(end < start)
if (start - end) > 1:
if debug:
out.write(" " * 80)
out.write(indent)
out.write("| " * (end + 1))
out.write("|_" * (start - end - 2))
out.write("|/ ")
out.write("/ " * (len(frontier) - start))
out.write("\n")
if debug:
out.write(" " * 80)
out.write(indent)
out.write("| " * end)
out.write("|/")
out.write("| " * (start - end - 1))
out.write(" /" * (len(frontier) - start))
out.write("\n")
def connect_deps(i, deps):
if len(deps) == 1 and deps in frontier:
j = frontier.index(deps)
if j < i:
back_edge(j, i)
else:
if i < j:
frontier.pop(j)
frontier.insert(i, deps)
back_edge(i, j+1)
elif deps:
frontier.insert(i, deps)
def add_deps_to_frontier(node, i):
deps = ordered_deps(node)
connect_deps(i, deps)
for d in deps:
del nodes[d].dependents[node.name]
name = topo_order.pop()
add_deps_to_frontier(nodes[name], 0)
if debug:
out.write("%-80s" % frontier)
out.write(indent)
out.write('%s %s\n' % (N, name))
while topo_order:
if debug:
out.write("%-80s" % frontier)
# Find last i, len(frontier[i]) > 1
i = len(frontier) - 1
for f in frontier[-1::-1]:
if len(f) > 1: break
i -= 1
# Expand frontier until there are enough columns for all children.
if i >= 0:
out.write(indent)
out.write("| " * i)
out.write("|\ ")
out.write("\ " * (len(frontier) - i - 1))
out.write("\n")
name = frontier[i].pop(0)
deps = [name]
connect_deps(i, deps)
else:
name = topo_order.pop()
node = nodes[name]
# Find the next node in topo order and remove it from
# the frontier. Since specs are single-rooted DAGs,
# the node is always there. If the graph had multiple
# roots, we'd need to handle that case case of a new root.
i, elt = next(f for f in enumerate(frontier) if name in f[1])
frontier.pop(i)
out.write("| " * i)
out.write("%s " % N)
out.write("| " * (len(frontier) - i))
out.write(" %s\n" % name)
if node.dependencies:
add_deps_to_frontier(node, i)
elif frontier:
if debug:
out.write(" " * 80)
out.write("| " * i)
out.write(" /" * (len(frontier) - i))
out.write("\n")
out.write("\n")
out.write("%s\n" % frontier)
# Reverse the lines in the output
#return '\n'.join(reversed(out.getvalue().split('\n')))
return "" #out.getvalue()
def topological_sort(self, **kwargs):
"""Return a list of dependency specs sorted topologically.
This spec is not modified in the process."""
reverse = kwargs.get('reverse', False)
if not reverse:
parents = lambda s: s.dependents
children = lambda s: s.dependencies
else:
parents = lambda s: s.dependencies
children = lambda s: s.dependents
spec = self.copy()
nodes = spec.index()
topo_order = []
remaining = [name for name in nodes.keys() if not parents(nodes[name])]
heapify(remaining)
while remaining:
name = heappop(remaining)
topo_order.append(name)
node = nodes[name]
for dep in children(node).values():
del parents(dep)[node.name]
if not parents(dep):
heappush(remaining, dep.name)
if any(parents(s) for s in spec.traverse()):
raise ValueError("Spec has cycles!")
else:
return topo_order
def __repr__(self): def __repr__(self):
return str(self) return str(self)