port-alldeps package ...
lists all dependencies of Macports packages -- 1st 2nd 3rd level ...
by repeatedly calling "port deps".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | #!/usr/bin/env python
"""
port-alldeps package ...
lists all dependencies of Macports packages -- 1st 2nd 3rd level ...
by repeatedly calling "port deps".
Example:
port deps pandoc ->
pandoc has build dependencies on:
ghc
haddock
pandoc has library dependencies on:
gmp
port-alldeps pandoc ->
pandoc* -> ghc haddock* gmp
ghc -> readline gmp perl5.8
haddock* -> ghc hs-ghc-paths*
gmp ->
readline -> ncurses
perl5.8 ->
hs-ghc-paths* -> ghc
ncurses -> ncursesw
ncursesw ->
"*" marks packages that are not installed.
Thus port-alldeps | egrep '\* *->' lists what has to be built:
pandoc* -> ghc haddock* gmp
haddock* -> ghc hs-ghc-paths*
hs-ghc-paths* -> ghc
"port list installed" is run first, which may be slow.
To use a file instead,
port list installed > date.portsinstalled
export Macportsinstalled=date.portsinstalled
"""
# breadth-first, not topo sorted
# alldeps-dot | graphviz not so hot, see aewm.png
# "port deps" is slow, one exec per dep is slower:
# alldeps ImageMagick 14 sec, port -F 9 sec
# http://en.wikipedia.org/wiki/Macports ff
# alternate approach: cache the whole deps graph, 4k nodes
# google "package dependency graph" ...
# really need a short Howto edit portfiles to ignore some deps
#...............................................................................
import os
import subprocess
import sys
__date__ = "4aug 2009" # 27oct 2008
__author_email__ = "denis-bz-py at t-online dot de"
#...............................................................................
def execv( cmd_arg_list ):
""" execvp [cmd, args ...] -> [line ...]
"""
out, err = subprocess.Popen( cmd_arg_list, stdout=subprocess.PIPE, shell=False ) \
.communicate() # wait til process ends
lines = out.split( "\n" )
if lines: lines.pop() # "" after \n at the end
return lines
def macport( args ):
""" port args -> [lines]
(exec each call, slow)
"""
# <-> one running process ? try Pexpect ?
return execv( ["port"] + args.split() ) # in $PATH
installed = {}
Macportsinstalled = os.getenv( "Macportsinstalled", None )
def port_installed():
""" port list installed -> installed[ mod ... ] """
if Macportsinstalled:
lines = open( Macportsinstalled ) .readlines()
else:
print >>sys.stderr, "running \"port list installed\" ..."
lines = macport( "list installed" )
for line in lines:
mod, version = line.split()[0:2]
installed[mod] = version
print "# info: %d ports are installed" % len(installed)
#...............................................................................
def port_deps( mod ):
""" -> [deps] or [] """
deps = []
for line in macport( "deps " + mod ):
if not (line.endswith( ":" ) \
or line.endswith( " has no dependencies" )):
deps.append( line.strip() )
return deps
#...............................................................................
def deco( mod ):
return mod if mod in installed else (mod + "*")
def print_deps( mod, deps ):
print "%-10s ->" % deco(mod) ,
for d in deps:
print deco(d) ,
print ""
def alldeps( mod ):
""" all deps: just iterate port deps ... breadth-first """
bfs = [mod]
done = {}
while bfs:
mod = bfs.pop( 0 )
if mod in done: continue
deps = port_deps( mod )
print_deps( mod, deps )
done[mod] = 1
bfs.extend( [d for d in deps if d not in done] )
#...............................................................................
roots = ["pandoc"]
if len(sys.argv) > 1:
if sys.argv[1].startswith( "-" ): # -h --help
print __doc__ # at the top
exit( 1 )
roots = sys.argv[1:]
try:
import bz.util
print bz.util.From() # date pwd etc.
bz.util.scan_eq_args( globals() ) # Macportsinstalled= ...
except:
pass
port_installed() # port list installed -> installed[ mod ... ]
for root in roots:
alldeps( root )
print ""
# end port-alldeps.py
|
"port install apackage" can lead to an avalanche of installs
if "apackage" requires "anotherpackage", which requires "yetanotherpackage" ...
port-alldeps lists the tree of package dependencies,
for people who (unlike Calvin) like to look, then leap.
Tags: macports, package_dependency_graph
I find this module pretty confusing and would suggest a class to compartmentalise functionality a bit better. What is the bz module that you conditionally import? If it is only relevant to one method it would be preferrable to have it tied to that method only or at the top of the module.
Writing a function to mimic execv but actually use subprocess makes little sense to me. Although I am not sure if I would actually want to do everything with subprocesses. It should be enough to get the path information and then process the info from the relevant Portfile directly. Personally I'd find some additional information about the ports such as whether they have already been listed (Graphviz has lots of repeated dependencies) or the number of library, build or runtime dependencies.
Please drop the Java ( () ) isms and print >> was never a good idea. Actually I'd be tempted to log to stdout rather than print.
A shorter version that I wrote to do just the minimal work - print dependency tree:
Here is an example: