Recursive dependencies, recursive uninstall, variants

Shreevatsa R shreevatsa.public at
Fri Apr 25 10:59:12 PDT 2008

Hi MacPorts developers,

I was planning to write some non-trivial code before posting to this
list, but I thought I would first check if I was doing anything

I found that the "port deps" command only lists the explicit
dependencies of a port, and not everything that the port actually
depends on ("recursive dependencies"). This meant that often
installing something trivial looking would start pulling in something
enormous that I didn't want to install, and I would have to kill it,
or wait hours. Contrast this to something like Debian's apt (used by
Fink too), which when you try to install something, gives a message
     # apt-get install nautilus
     Reading Package Lists... Done
     Building Dependency Tree... Done
     The following extra packages will be installed:
       bonobo libmedusa0 libnautilus0
     The following NEW packages will be installed:
       bonobo libmedusa0 libnautilus0 nautilus
     0 packages upgraded, 4 newly installed, 0 to remove and 1  not upgraded.
     Need to get 8329kB of archives. After unpacking 17.2MB will be used.
     Do you want to continue? [Y/n]

So deciding that at least knowing all dependencies of a port
beforehand would help, I tried writing a command for recursive
dependencies. The proc action_deps does a lot of stuff, and it's not
easy getting just the list of dependencies with no extra output, so I
wrote my own "bare" proc that does the actual dependencies, etc., and
rdeps that recursively calls it:

proc add_to_set {set_var item} {
    upvar $set_var var
    if {[lsearch -exact $var $item] == -1}  { lappend var $item }

proc actdeps {portname depstype} {
    array unset portinfo
    array set portinfo [lindex [mportsearch $portname no exact] 1]

    #set deps [map {x {lindex [split $x :] end}} $portinfo($depstype)]
    set deps []
    if {[info exists portinfo($depstype)]} {
        foreach i $portinfo($depstype) {
            lappend deps [lindex [split $i :] end]
    return $deps

proc rdeps {portname {depth 0}} {
    global shownports
    if [info exists shownports] {    } else { set shownports [] }
    add_to_set shownports $portname

    set depstypes {depends_build depends_lib depends_run}
    set depstypes_descr {"build" "library" "runtime"}

    foreach depstype $depstypes depsdecr $depstypes_descr {
        foreach port [actdeps $portname $depstype] {
            if {[lsearch $shownports $port] == -1} {
                puts "[string repeat \t $depth]$port"
                rdeps $port [expr {$depth + 1}]

proc action_rdeps {action portlist opts} {
    foreachport $portlist {
        rdeps $portname

And "rdeps       action_rdeps" in action_array. Yeah, it uses a global
variable to avoid printing the same ports again, but it works:

~$ port rdeps subversion

This is not great code, but it does what I want. (It is also the only
Tcl I have ever written, so please tell me what I'm doing stupidly.)
I was planning to do the same for variants (because I install
something and later discover I should have installed one of its
dependencies with a different variant), and for uninstall (because it
is a real pain to uninstall ports).
More specifically, I was planning to
(1) make a general (simple) function for getting a specific port's
"tree of dependencies" (and/or tree of dependents) and then something
to act on it. Uninstall would then be trivial.
(2) Also, putting something to return the list of dependencies of a
port in the MacPorts API itself.
(3) Also, looking at action_deps, it seems to share a lot in common
with other functions (searching for ports, etc.), so that part can be
cleaned up...



More information about the macports-dev mailing list