#!/usr/bin/tclsh # # Run a recursive dependency listing against a given port, outputing # either a basic text display of the result, or dot language so a graph # can be created. # The dot functionality can be used by doing: # $ port-rdeps -d apache2 | dot -Tpng -o apache2.png # # Todo: # Does not differentiate, in output, between bin/lib/run type dependencies # Once "good enough", integrate into port # # http://trac.macports.org/ticket/11892 set MY_VERSION 1.0 array set portsSeen {} proc printUsage {} { puts "Usage: $::argv0 \[-dhrvV\] \[-t macports-tcl-path\] port-name \[variants...\]" puts " -d Output dot-format instead of plain ASCII" puts " -h This help" puts " -r Reduce ASCII output, showing a given port only once" puts " -t Give a different location for the base MacPorts Tcl" puts " file (defaults to /Library/Tcl)" puts " -v verbose output, includes dependency labels" puts " -V show version and MacPorts version being used" puts "" puts "port-name is the name of a port to show dependencies" puts "variants is the list of variants to enable/disable: +one -two..." puts "" puts "For dot output, dependency types are represented as follows:" puts " depends_run is dashed" puts " depends_build is dotted" puts " depends_lib is solid" } proc dependenciesForPort {portName variantInfo} { set dependencyList [list] set portSearchResult [mportlookup $portName] if {[llength $portSearchResult] < 1} { puts "Warning: port \"$portName\" not found" return [list] } array set portInfo [lindex $portSearchResult 1] set mport [mportopen $portInfo(porturl) {} $variantInfo] array unset portInfo array set portInfo [mportinfo $mport] mportclose $mport array set dependencyDictionary {depends_build build depends_lib lib depends_run run} foreach dependencyType [array names dependencyDictionary] { if {[info exists portInfo($dependencyType)] && [string length $portInfo($dependencyType)] > 0} { foreach dependency $portInfo($dependencyType) { set afterColon [expr {[string last ":" $dependency] + 1}] lappend dependencyList [list $dependencyDictionary($dependencyType) [string range $dependency 0 [expr [string first ":" $dependency] - 1]] [string range $dependency $afterColon end]] } } } return $dependencyList } proc print_dependencies {dependencyArray portName outputAscii reduceAscii verbose} { upvar $dependencyArray portDependencies global portsSeen if {$outputAscii} { set spaces [string repeat " " [expr {$outputAscii * 2}]] set outputAscii [expr {$outputAscii + 1}] } elseif {[info exists portsSeen($portName)]} { return } set portsSeen($portName) 1 foreach aList $portDependencies($portName) { set aType [lindex $aList 0] set aBy [lindex $aList 1] set aPort [lindex $aList 2] if {$outputAscii && $reduceAscii && [info exists portsSeen($aPort)]} { continue } set style "solid" if {$aType == "run"} { set style "dashed" } elseif {$aType == "build"} { set style "dotted" } set color "black" if {$aBy != "port"} { set color "#708090" } if {$outputAscii && !$verbose} { puts "${spaces}${aPort}" } elseif {$outputAscii && $verbose} { puts "${spaces}${aPort} (depends_${aType}, ${aBy})" } elseif {!$outputAscii && !$verbose} { puts [format {"%s" -> "%s" [ style=%s ] } $portName $aPort $style] } elseif {!$outputAscii && $verbose} { puts [format {"%s" -> "%s" [ label="%s",style="%s",color="%s" ]} $portName $aPort $aBy $style $color] } print_dependencies portDependencies $aPort $outputAscii $reduceAscii $verbose } } proc find_all_dependencies {portName variantInfo outputAscii reduceAscii verbose} { array set portDependencies {} set portList [list $portName] while {[llength $portList] > 0} { set aPort [lindex $portList 0] set portDependencies($aPort) [dependenciesForPort $aPort $variantInfo] set portList [lreplace $portList 0 0] foreach possiblyNewPort $portDependencies($aPort) { if {![info exists portDependencies([lindex $possiblyNewPort 2])]} { lappend portList [lindex $possiblyNewPort 2] } } } if {$outputAscii} { puts "Dependencies of $portName:" } else { puts "strict digraph \"$portName\" \{" } print_dependencies portDependencies $portName $outputAscii $reduceAscii $verbose if {!$outputAscii} { puts "\}" } } # Begin set asciiOutput 1 set reduceAsciiOutput 0 set macportsTclPath /Library/Tcl set verbose 0 set showVersion 0 while {[string index [lindex $::argv 0] 0] == "-" } { switch [string range [lindex $::argv 0] 1 end] { d { set asciiOutput 0 } h { printUsage exit 0 } r { set reduceAsciiOutput 1 } t { if {[llength $::argv] < 2} { puts "-t needs a path" printUsage exit 1 } set macportsTclPath [lindex $::argv 1] set ::argv [lrange $::argv 1 end] } v { set verbose 1 } V { set showVersion 1 } default { puts "Unknown option [lindex $::argv 0]" printUsage exit 1 } } set ::argv [lrange $::argv 1 end] } source ${macportsTclPath}/macports1.0/macports_fastload.tcl package require macports mportinit if {$showVersion} { puts "Version $MY_VERSION" puts "MacPorts version [macports::version]" exit 0 } if {[llength $::argv] == 0} { puts "Error: missing port-name" printUsage exit 1 } set portName [lindex $::argv 0] set ::argv [lrange $::argv 1 end] array set variantInfo {} foreach variantSetting $::argv { set flag [string index $variantSetting 0] set variantName [string range $variantSetting 1 end] set variantInfo($variantName) $flag } find_all_dependencies $portName [array get variantInfo] $asciiOutput $reduceAsciiOutput $verbose