#!/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