Thus, I eventually got my act together and wrote this script. I've tested it rather exhaustively, but I don't claim 100% functionality. One problem seems to be that if you have installed a variant of a package, even by default the port deps command doesn't give you all of the dependencies -- see this bug report as an example of what I'm talking about.
Most of the code should be readily understandable, as I've tried to use descriptive variable names and have commented the code throughout. But the central discovery process is a nasty comglomeration of (clever ;-) ) hacks. In programming terms, the dependency structure is a network. You can question the status of each node and get the children (dependencies) of each port. However, knowing that a port needs to be upgraded, there's no readily avaliable method for finding the ports that depend upon a given port (the parent nodes), except by going through all of the ports you have installed and questioning each and every one of them. This is where the PitA enters the picture!
The script handles all of this work itself; read on for more detail on some of the routines, in case you're curious as to how it exactly works.
[robg adds: I haven't tested this one; make sure you make the script executable with chmod a+x scriptname before running it.]
What needs explaining are the following pieces of code:
# build the total list of ports that have dependencies
(( index=0 ))
for listet in ${portList[@]}
do
portDepends=( $( port deps ${listet} | sed "/${listet}/d" |tr -d 't' ) )
if [ ${#portDepends[@]} -ne 0 ]
then
portDependencies[${index}]=$( echo "${listet}: ${portDepends[@]}" )
(( index++ ))
fi
done
port deps ${listet} will give you the ports that a port depends on, but with some extraneous information that's unwelcome in this connection, like this:
[18:48:46@~]$ port deps nessus-plugins
nessus-plugins has build dependencies on:
nessus-core
nessus-plugins has library dependencies on:
nessus-libraries
[18:52:10@~]$
So the sed "/${listet}/d" bit in the above code removes the descriptive lines, and tr -d 't' removes the Tab at the beginning of each line of dependencies. Lastly, every kind of dependency is made into a list: portDepends=( $( ... commands ... ) ). Then it's checked whether the port has any dependecies at all -- with the if [ ${#portDepends[@]} -ne 0 ] line -- and if so, it's added to the list
Next, there's this bit of code:
portDeactivate=( \
$( { for (( index=0 ; index < ${#portDependencies[@]} ; index++ ))
do
echo "${portDependencies[${index}]}"
done } \
| grep ${deactivate} | sed -E "/^${deactivate}:/d" | cut -f 1 -d ':' ) )
This piece of code loops through all of the ports with dependencies, and extracts the ports that have dependencies on the port we have to deactivate or upgrade. The reason for using a numerical index instead of just doing for listed in ${portDependencies[@]} is that ${listed} then isn't a text-string, but a list in itself, which will cause problems further on, because each item in ${listed} then gets treated by itself, instead of as a total.
The for loop is made into a sub-program by enclosing it in curly brackets ({ for... }, and then the output from the sub-program is piped into the subsequent commands. These commands then extract the name of the port that has dependencies on a port, and removes the port itself from the list. The whole mess is then once again made into a list.

