in lib/ramble/spack/graph.py [0:0]
def write(self, spec, color=None, out=None):
"""Write out an ascii graph of the provided spec.
Arguments:
spec -- spec to graph. This only handles one spec at a time.
Optional arguments:
out -- file object to write out to (default is sys.stdout)
color -- whether to write in color. Default is to autodetect
based on output file.
"""
if out is None:
out = sys.stdout
if color is None:
color = out.isatty()
self._out = llnl.util.tty.color.ColorStream(out, color=color)
# We'll traverse the spec in topological order as we graph it.
nodes_in_topological_order = topological_sort(spec, deptype=self.deptype)
# Work on a copy to be nondestructive
spec = spec.copy()
# Colors associated with each node in the DAG.
# Edges are colored by the node they point to.
self._name_to_color = {
spec.dag_hash(): self.colors[i % len(self.colors)]
for i, spec in enumerate(nodes_in_topological_order)
}
# Frontier tracks open edges of the graph as it's written out.
self._frontier = [[spec.dag_hash()]]
while self._frontier:
# Find an unexpanded part of frontier
i = find(self._frontier, lambda f: len(f) > 1)
if i >= 0:
# Expand frontier until there are enough columns for all children.
# Figure out how many back connections there are and
# sort them so we do them in order
back = []
for d in self._frontier[i]:
b = find(self._frontier[:i], lambda f: f == [d])
if b != -1:
back.append((b, d))
# Do all back connections in sorted order so we can
# pipeline them and save space.
if back:
back.sort()
prev_ends = []
collapse_l1 = False
for j, (b, d) in enumerate(back):
self._frontier[i].remove(d)
if i - b > 1:
collapse_l1 = any(not e for e in self._frontier)
self._back_edge_line(
prev_ends, b, i, collapse_l1, 'left-1')
del prev_ends[:]
prev_ends.append(b)
# Check whether we did ALL the deps as back edges,
# in which case we're done.
pop = not self._frontier[i]
collapse_l2 = pop
if collapse_l1:
collapse_l2 = False
if pop:
self._frontier.pop(i)
self._back_edge_line(
prev_ends, -1, -1, collapse_l2, 'left-2')
elif len(self._frontier[i]) > 1:
# Expand forward after doing all back connections
if (i + 1 < len(self._frontier) and
len(self._frontier[i + 1]) == 1 and
self._frontier[i + 1][0] in self._frontier[i]):
# We need to connect to the element to the right.
# Keep lines straight by connecting directly and
# avoiding unnecessary expand/contract.
name = self._frontier[i + 1][0]
self._frontier[i].remove(name)
self._merge_right_line(i)
else:
# Just allow the expansion here.
dep_hash = self._frontier[i].pop(0)
deps = [dep_hash]
self._frontier.insert(i, deps)
self._expand_right_line(i)
self._frontier.pop(i)
self._connect_deps(i, deps, "post-expand")
# Handle any remaining back edges to the right
j = i + 1
while j < len(self._frontier):
deps = self._frontier.pop(j)
if not self._connect_deps(j, deps, "back-from-right"):
j += 1
else:
# Nothing to expand; add dependencies for a node.
node = nodes_in_topological_order.pop()
# Find the named node in the frontier and draw it.
i = find(self._frontier, lambda f: node.dag_hash() in f)
self._node_line(i, node)
# Replace node with its dependencies
self._frontier.pop(i)
edges = sorted(
node.edges_to_dependencies(deptype=self.deptype), reverse=True
)
if edges:
deps = [e.spec.dag_hash() for e in edges]
self._connect_deps(i, deps, "new-deps") # anywhere.
elif self._frontier:
self._collapse_line(i)