How can I force MacPorts to upgrade all upgradeable ports that it can?

Suppose I have outdated Ports A, B, C, D, E, F and G. Suppose for simplicity that all of them are "requested".

  • A has no dependencies.
  • B directly depends on C and D and E.
  • C directly depends on E and F.
  • D has no dependencies.
  • E has no dependencies.
  • F has no dependencies.
  • G has no dependencies.

To the best of my understanding, when MacPorts runs (sudo port upgrade outdated), it installs the ports in a dependency tree sorted by alphabetical order. So, it would install the ports in this order: A, D, E, F, C, B.

Suppose Port A is broken. MacPorts quits installing.

I see this as an inconvenience, because the other outdated ports could very well be working fine. The fact that A won't install has no effect on any of the other ports. Now, because a single port won't install, all the rest of my ports are out-of-date until this port is fixed.

My Question:

How can I tell MacPorts to continue installing all installable ports that it can install without error?

This is probably the same as this one in spirit: MacPorts: Continue installing other updates after error. I don't buy the answers on that page, however:

  • The MacPorts dev (@ClemensLang) says that there's a reason MacPorts doesn't continue. (The links he includes support this, but it gets too technical for me. Sorry!) In my case above, installing Port G after Port A fails wouldn't seem to be problematic. (...or is it?) Is there a way to do it safely?
  • The -p flag is evidently problematic. I would have thought that would be the answer to this solution, but multiple devs caution users not to use it unless they really understand what it does. I'd rather play it safe.
  • I could try sudo port upgrade outdated and not A. This works if only one independent port fails, but what if I have two or three ports (out of dozens) that fail? Could I do something like, sudo port upgrade outdated and not A and not C and not E (etc.)?

Suppose there are too many ports that fail to exclude them individually. Suppose also that there are too many ports to upgrade them individually.


This is what I use... it might not be pretty but it works:

sudo bash -c 'for port in `port outdated  2>/dev/null |  tail -n +2 | awk '\''{print $1}'\''` ; do echo -n "$port:" ;  port -N upgrade $port 2>> /tmp/upgrade-ports.err >> /tmp/upgrade-ports.out && echo "✓" || echo "✕" ; done'

Let me brake it down for you:

  • first, you don't want sudo in a loop, otherwise you need to keep putting your password. To avoid sudo I got the whole loop and quoted it (Alt+' in zsh is very helpful to properly escape the existing quotes) and then ran it with sudo bash -c. Alternatively you can put it in a file and run it as a script with sudo script

  • that leaves us with this loop:

for port in `port outdated  2>/dev/null | tail -n +2 | awk '{print $1}'` ; do echo -n "$port:" ;  port -N upgrade $port 2>> /tmp/upgrade-ports.err >> /tmp/upgrade-ports.out && echo "✓" || echo "✕" ; done```
  • I build the list of ports with: port outdated 2>/dev/null | tail -n +2 | awk '{print $1}' (tail removes the line "The following installed ports are outdated:")
  • for every port I print the port name and a semicolon (without newline )
  • then I try to upgrade it non interactively, and save the output and errors
  • if the upgrade was successful I print a "✓" and a newline, otherwise a "✕" and a newline
  • note that this will not show the dependencies being installed, but you can tail the log to see what is happening*
  • this is what the output looks like:
adwaita-icon-theme:✓
apr:✕
aquaterm:✓

HTH

*Edited to clarify. This means that in your example the output would look like this:

A:✕
B:✓
C:✓
D:✓
E:✓
F:✓
G:✓

But B will take a LONG time, and C, D, E and F will be very fast because they have already been upgraded during B.