Updated brew graph

Changed 'graph' command to show only installed by default. Prettified
resulting graph.

By default, the `brew graph` command would output a dependency graph for every
formula it knew about. Now it only outputs a dependency graph the for formulas
installed. If you want to see the graph for all formulas, use `brew graph
--all`.

Additionally, the old version of the graph command would filter out any
formulas without depdency connections. The updated version now only does this
if calculating dependencies for all formulas via the `--all` flag.

Finally, the resulting graph has been redesigned to be simpler to read. All
formulas which have no other formulas depending on them (i.e., root nodes) are
aligned to the left. They are also outlined in a light grey box, which is
labelled "Safe to Remove". As implied, all of the formulas in this box can be
safely removed without breaking other installed formulas; all formulas outside
this box have at least one installed formula depending on them. This new graph
style is surpressed if the `--all` flag is used.

Closes Homebrew/homebrew#18282.

Signed-off-by: Adam Vandenberg <flangy@gmail.com>
This commit is contained in:
Matt Torok 2013-03-06 01:04:05 -08:00 committed by Adam Vandenberg
parent a60eed48bc
commit 14e3c77f60

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
$ brew install graphviz $ brew install graphviz
$ brew graph | dot -Tsvg -ohomebrew.svg $ brew graph | dot -Tsvg -ohomebrew.html
$ open homebrew.svg $ open homebrew.html
""" """
from __future__ import with_statement from __future__ import with_statement
@ -274,13 +274,26 @@ class Graph(NodeContainer, EdgeContainer, ClusterContainer):
def main(): def main():
cmd = ["brew", "deps"] cmd = ["brew", "deps"]
cmd.extend(sys.argv[1:] or ["--all"]) if sys.argv[1:]:
if '--all' in sys.argv[1:]:
show = 'all'
cmd.extend(['--all'])
else:
show = 'one'
hideOrphaned = False
cmd.extend(sys.argv[1:])
else:
show = 'installed'
cmd.extend(['--installed'])
code, output = run(cmd) code, output = run(cmd)
output = output.strip() output = output.strip()
depgraph = list() depgraph = list()
for f in output.split("\n"): for f in output.split("\n"):
stuff = f.split(":",2) stuff = f.split(":",2)
if len(stuff) < 2:
continue
name = stuff[0] name = stuff[0]
deps = stuff[1].strip() deps = stuff[1].strip()
if not deps: if not deps:
@ -289,20 +302,39 @@ def main():
deps = deps.split(" ") deps = deps.split(" ")
depgraph.append((name, deps)) depgraph.append((name, deps))
hb = Graph("Homebrew Dependencies", attrib={'labelloc':'b', 'rankdir':'LR', 'ranksep':'5'}) hb = Graph("Homebrew Dependencies", attrib={'labelloc':'t', 'rankdir':'LR', 'ranksep':'5'})
# Independent formulas (those that are not dependended on by any other formula) get placed in
# their own subgraph so we can align them together on the left.
if show == 'installed':
sub = hb.cluster("independent", "Safe to Remove", attrib={'rank': 'min', 'style': 'filled', 'fillcolor': '#F0F0F0', 'color': 'invis'})
else:
sub = hb
used = set() seen = set()
for f in depgraph: def addNode(graph, name):
for d in f[1]: if name not in seen:
used.add(f[0]) graph.node(name, name, attrib={'shape': 'box'})
used.add(d) seen.add(name)
return True
return False
independent = set()
for f in depgraph: for f in depgraph:
if f[0] not in used: # Filter out orphan formulas when showing all, to cut down on noise
if show == 'all' and len(f[1]) == 0:
continue continue
n = hb.node(f[0], f[0])
independent.add(f[0])
for d in f[1]: for d in f[1]:
hb.link(d, f[0]) independent.discard(d)
hb.link(f[0], d)
# Children we can add right away because we don't care where they go
addNode(hb, d)
# For all installed formulas, place them in the 'indep' subgraph iff they
# are not depended on by other formulas, i.e. are root nodes.
for d in independent:
addNode(sub, d)
hb.dot() hb.dot()