Chasing dependency rabbits /(・ × ・)\
in the Debian universe.
Whether it’s for troubleshooting, lightening your dependency load or tracking down what you need to make an AppImage sometimes you just need to chase those dependency rabbits.
# Check the dependencies of a package (whether installed or not).
apt depends bash
# Output:
bash
PreDepends: libc6 (>= 2.15)
PreDepends: libtinfo6 (>= 6)
Depends: base-files (>= 2.1.12)
Depends: debianutils (>= 2.15)
Conflicts: bash-completion (<< 20060301-0)
Recommends: bash-completion (>= 20060301-0)
Suggests: bash-doc
Replaces: bash-completion (<< 20060301-0)
Replaces: bash-doc (<= 2.05-1)
There’s currently 9 different dependency categories called “Package Interrelationship Fields” and a few more for compiling.
This is my TLDR from the Debian Docs
Dependency Type |
|
Means |
Pre-Depends |
|
I must have this and if it’s not there install it before Depends |
Depends |
|
I must have this |
Recommends |
|
I really should have this but can do without it |
Suggests |
|
This enhances me (soft Recommends) |
Breaks |
|
I’ll break this if you install me |
Conflicts |
|
Unless i’m removing or replacing this, you can’t install me (see: Replaces) |
Provides |
|
I’ll provide this other package virtually |
Replaces |
|
I’ll remove or replace this |
Enhances |
|
I’ll enhance this (reverse of Suggests) |
So what is apt depends <package>
saying about the dependencies of bash
?
- It must have
libc6
, libtinfo6
, base-files
and debianutils
. If any of these are missing the first two should be installed before the last two.
-
bash-doc
would be an enhancement
- It really should have
bash-completion
>= version 20060301-0 installed too.
- It’ll remove
bash-completion
and bash-doc
if their versions are too old.
It’s never just one rabbit. /(・ × ・))))))))\
apt depends
gives the immediate dependencies, not the dependency tree (all dependencies of all dependencies). Getting the sum total of packages required for a package to work requires using apt depends
recursively on every required package till there’s no more requirements.
There’s a package apt-rdepends
that claims to do just that.
sudo apt install apt-rdepends
apt-rdepends bash
# Output:
bash
Depends: base-files (>= 2.1.12)
Depends: debianutils (>= 2.15)
PreDepends: libc6 (>= 2.15)
PreDepends: libtinfo6 (>= 6)
base-files
PreDepends: awk
awk
debianutils
PreDepends: libc6 (>= 2.15)
libc6
Depends: libgcc1
libgcc1
Depends: gcc-8-base (= 8.3.0-6)
Depends: libc6 (>= 2.14)
gcc-8-base
libtinfo6
Depends: libc6 (>= 2.16)
It can also output in dot
readable format (part of graphviz
) that can render all kinds of relationship graphs.
sudo apt install graphviz
apt-rdepends --dotty bash | dot -Tpng > bash-dependencies.png
# Open bash-dependencies.png
Box: Package
Circle: Meta package
Yellow arrow: PreDepends
White arrow: Depends
(colors were inverted in krita
)
Chasing meta rabbits /(・ × ・))))))) × ・)>)))))))))))))))))\
The problem with apt-rdepends
is it stops at the meta package level without recursing into it’s dependencies (see: awk
above) so a lot of package rabbits are missing.
Thankfully apt-cache has CLI options that’ll recurse into all PreDepends and Depends packages similar to apt-rdepends
but including the meta packages.
# 8 dependencies
apt-rdepends bash | grep "^[^ ]"
# __vs__
# 25 dependencies
apt-cache depends --recurse --important bash | grep "^[^ <]"
Worth mentioning… while this doesn’t appear to affect my grep
above, apt-cache seems to have a bug where this random pipe can show up:
apt-cache depends --recurse --important bash
...
readline-common
|Depends: dpkg
Depends: install-info
...
There’s also no option for dot
graph output which is a lifestlye to which we’ve become acustom.
Visualizing the full dependency rabbit hole
Time to write some BASH…
This script converts the output of apt-cache depends --recurse --important <package>
into a dot
graph with an output the same as apt-rdepends --dotty
but including meta package dependencies:
sudo apt install graphviz
Path="$HOME/.local/bin/deptodot"
mkdir -p "${Path%/*}"; touch "$Path"; chmod u+x "$Path"; nano "$Path";
# Paste the following:
#!/usr/bin/env bash
Err(){
printf '%s\n' "$2" 1>&2
[ $1 -gt 0 ] && exit $1
}
PackageName=$1
[ -z "$1" ] && Err 1 'No package provided'
[ -z "$( apt-cache search --names-only "^$1$" )" ] && Err 1 "Package doesn't exist"
printf '%s\n' 'digraph packages {'
printf '%s\n' 'concentrate=true;'
while IFS= read -r Line; do
set -- $Line
# If the line doesn't start with a space and doesn't have a :
# it's a package, otherwise it's a package dependency
if [ "${Line:0:1}" != ' ' ] && [ "${Line##*:}" != '$Line' ]; then
CurrentPackage=$Line
CurrentSubPackage=
# If it starts with a < it's a meta package
if [ "${Line:0:1}" = '<' ]; then
printf '%s\n' "\"$CurrentPackage\" [shape=ellipse];"
else
printf '%s\n' "\"$CurrentPackage\" [shape=box];"
fi
else
# If the dependency starts with a < it's a meta package and will
# uniquely act as the parent to dependencies on following lines.
[ "${2:0:1}" = '<' ] && CurrentSubPackage=$2
if [ "${Line##*PreDepends:}" != "$Line" ]; then
# Pre-dependency of the current package
printf '%s\n' "\"$CurrentPackage\" -> \"$2\"[color=blue];"
elif [ "${Line##*Depends:}" != "$Line" ]; then
# Dependency of the current package
printf '%s\n' "\"$CurrentPackage\" -> \"$2\";"
else
# Sub-dependency of the current meta dependency of the current package
printf '%s\n' "\"$CurrentSubPackage\" -> \"$1\";"
fi
fi
done <<< $( apt-cache depends --recurse --important "$PackageName" )
# If you don't have $HOME/.local/bin in your path, run
# this and consider adding it to your ~/.bashrc
PATH="$HOME/.local/bin:$PATH"
deptodot 'bash' | dot -Tpng > bash-dependencies.png
# Open bash-dependencies.png
Full BASH dependency tree (click for no blur)
Takeaway
# List immediate dependencies
apt depends <Package>
# List full dependency tree
apt-cache depends --recurse --important <Package> # CLI
deptodot <Package> | dot -Tpng > image.png # Graphically using graphviz (script above)
# List full dependency tree except meta package dependencies
apt-rdepends <Package> # CLI
apt-rdepends --dotty <Package> | dot -Tpng > image.png # Graphically using graphviz