Skip to main content

Pipes in apt-cache Output

· 6 min read
Ubuntu logo

Whilst pondering a thought-experiment related to installing Ubuntu packages (more on that in another post), I was looking to understand the output of the apt-cache depends command. However, while doing a quick Google search, I found that the top two results were wrong - one subtly (though sort-of right), the other blatently wrong. So, I did a little digging to figure it out for myself.

Before we get to the right answer, let's start with those top two results. The first search result says:

As you guessed the | indicates "or". The control line normally shows foo | bar, but apt-cache depends appears to reformat it by showing each alternative on its own line and prefixing the first with the |. In other words, the pipe flags the preferred option and the next line is the alternative.

(emphasis mine)

This is actually pretty close, but not quite true, as we'll soon see.

The second search result says:

As for the |(pipe flag), between two packages in the list it denotes an "or" relationship between packages following each other. For example in the second example:

Depends: update-inetd
|Depends: openbsd-inetd

update-inetd and openbsd-inetd are alternatives to each other yet the first one(update-inetd) is preferred.

This is actually relating the wrong entries. The | before openbsd-inetd indicates an or relationship with the next line, which is not even shown in their quote. The previous line (update-inetd) is not at all relevant to the | indicator.

Ok, so the first result was close, so let's see why it's not quite correct. To see it's shortcomings, we need to consider a package dependency that has more than two alternatives. The first search result above actually includes one such example in the dependencies for a solr-common package, but as that package is not readily available on my current Ubuntu, I'll use the xorg package as an example instead.

So, here's an excerpt of the apt-cache depends xorg outout:

xorg
Depends: xserver-xorg
...
Depends: xorg-docs-core
|Depends: gnome-terminal
|Depends: xterm
xterm:i386
Depends: <x-terminal-emulator>
...

Where this example is interesting, and the existing explanations fall short, is the presence of multple consecutive lines begining with a pipe (|) - lines 5 and 6. In this case we can see what's happening by looking at Depends line in the apt show xorg output:

$ apt show xorg | grep '^Depends:'
Depends: xserver-xorg (>= 1:7.7+23ubuntu2), libgl1, libgl1-mesa-dri, libglu1-mesa, xfonts-base (>= 1:1.0.0-1), x11-apps, x11-session-utils, x11-utils, x11-xkb-utils, x11-xserver-utils, xauth, xinit, xfonts-utils, xkb-data, xorg-docs-core, gnome-terminal | xterm | x-terminal-emulator, xinput

Ok, that long line is bit hard to read, so let's split the line on commas (,) for better readbility:

$ apt show xorg | grep '^Depends:' | tr ',' '\n'
Depends: xserver-xorg (>= 1:7.7+23ubuntu2)
libgl1
libgl1-mesa-dri
libglu1-mesa
xfonts-base (>= 1:1.0.0-1)
x11-apps
x11-session-utils
x11-utils
x11-xkb-utils
x11-xserver-utils
xauth
xinit
xfonts-utils
xkb-data
xorg-docs-core
gnome-terminal | xterm | x-terminal-emulator
xinput

Now we can see that one of the dependencies is a set of three alternatives (on the highlighted line 17 above).

Narrowing the output of both commands (apt show xorg and apt-cache depends xorg) to just that dependency, we see:

apt show xorg (excerpt)
gnome-terminal | xterm | x-terminal-emulator

versus:

apt-cache depends xorg (excerpt)
 |Depends: gnome-terminal
|Depends: xterm
xterm:i386
Depends: <x-terminal-emulator>

To generalise, the package dependency consisting of a list of alternatives A | B | ... | D | E becomes:

|A
|B
|...
|D
E

So we can say that, for the apt-cache depends output, the pipe (|) indicator tells us that the alternative is or'd with whatever lines follow it, up to, and including, the first entry without a pipe (|).

Now, how does that make the first linked article subtly wrong? There's two statement in the quote that are not quite right. The first one:

but apt-cache depends appears to reformat it by showing each alternative on its own line and prefixing the first with the |

It's subtle, but it doesn't only prefix the first. It prefixes all but the last. Of course, when there's only two alternatives, then the statements are affectively the same. But even in the example that the quote was discussing, there's more than two alternatives shown, so even in that case, the original statement was incorrect.

And the second statement (well, clause really):

the pipe flags the preferred option

Again, it's subtle, but not quite true. If there are n alternatives, then there will be n-1 alternatives with pipe indicators, but not all (if any) are "preferred". Though generally (not strictly) speaking, the earlier alternatives are sort-of "more preferred" than later ones, this is not strictly true. Indeed the Debian documentation1 never mentions any concept of "preferred" packages in dependency alternatives.

But since this first-one (or earlier ones) being preferred concept has some basis in fact, let's see how these alternatives are handled. There's some official documentation here2, but here's a brief summary of the parts I think are relevant to this "preference" idea.

  1. If one or more of the alternatives is already installed, then regardless of it's position in the alernatives ordering, the dependency is considered met, and no other alternatives are considered.
  2. For each alternative, in the order given, if the alternative can be satisfied, then it is considered.
  3. If the result of #2 being applied to all packages requested to be installed results in a conflict, then the next alternative is considered instead.

An important part of step 2, is not just checking alternative package version ranges, but also architecture. I think this is worth calling out, because a primary use case for dependency alternatives is handling scenarios where different packages are required for different architectures. From the Debian documentation1:

For example:

Depends: foo [i386], bar [amd64]

becomes Depends: foo when the package is built on the i386 architecture, Depends: bar when the package is built on the amd64 architecture, and omitted entirely in binary packages built on all other architectures.

So in this (common) scenario, it would be wrong to claim either alternative as "preferred" per se, and certainly not based on its position.

Well, that's probably enough rambling about pipes in apt-cache output. Hopefully you found it interesting (I did).

Footnotes

  1. Declaring relationships between packages 2

  2. Immediate dependency resolution - Resolving package dependencies