dt.iki.fi

Pacman and Optional Dependencies

Archlinux packages list optional dependencies: "An array of packages that are not needed for the software to function, but provide additional features." But there's no special treatment built in for these packages, which can lead to problems (or at least needlessly left behind packages piling up).

Scenario 1-up-

The end user has to install optional dependencies manually, and choose between marking them as explicit (--asexplicit) or as a dependency (--asdeps), there's no third way.

The default for manually installed packages is as explicit; the casual user might forget about this and lose the overview over various explicitely installed packages that are not needed anymore!

Scenario 2-up-

You installed package Y, and package X got pulled in automatically as a (non-optional) dependency. Now you uninstall package Y, and get a list of orphans to uninstall. This can be automated thusly:

alias orphrem='sudo pacman -R $(pacman -Qdtq)'

However, package X is not on that list because it's an optional dependency for some other installed package. But you do not desire this. One could change above alias:

alias orphrem='sudo pacman -R $(pacman -Qdttq)'

The second 't' would also list optional dependencies as orphans - and the alias would uninstall them, all of them. That isn't desirable either.


And so unneeded installed packages tend to pile up.

What to do?-up-

Make it a habit to install optional dependencies as dependencies:

optinst() {
echo "Install $@ as dependencies (w. pacman):"
[[ "$@" == "" ]] && echo "Must provide at least one argument." && return 1
sudo pacman -S --asdeps "$@"
}

Then I also wrote a function that helps me get an overview of which installed dependencies come up as true orphans, and which as optional, and a way to quickly change an optional dependency's status to explicitely installed, or remove it.

red() { tput setaf 9; echo "$@"; tput sgr0; }

orph() {
local i j skip list
local orphans=( $(pacman -Qdtq) )
red "True Orphans:"
if [[ "${orphans[@]}" != "" ]]; then
    for i in "${orphans[@]}"; do echo $i; done
    return
else
    echo None
fi

# now repeat for ALL orphans including optional dependencies
local allorphans=( $(pacman -Qdttq) )
local optsonly=()
# and filter out optional dependencies
for i in "${allorphans[@]}"; do
    skip=
    for j in "${orphans[@]}"; do
        [[ $i == $j ]] && { skip=1; break; }
    done
    [[ -n $skip ]] || optsonly+=("$i")
done

# display result in a select list, with additional info & options when selected
if [[ "${optsonly[@]}" != "" ]]; then
    red "Optional Dependencies:"
    select i in "${optsonly[@]}"; do
        [[ "$i" == "" ]] && return
        red -n $i
        expac -l ' ' ' (%d) is optional for:' $i
        list=( $(pacsift --local --exact --optdep $i) )
        for j in ${list[@]}; do
            red -n " - ${j##*/}:"
            expac -l '\n' %O $j | grep "$i: "| cut -d: -f2
        done
        read -p "Mark as explicitly installed (e) or remove (r) no action? " j
        case $j in
            e) sudo pacman -D --asexplicit $i
            ;;
            r) sudo pacman -R $i
               return
            ;;
            *) [[ "$j" != "" ]] && echo -n "Didn't understand. "; echo "Back to main list."
            ;;
        esac
    done
fi
}

You will require both expac and pacutils for this.

Now I can modify the orphrem alias slightly:

alias orphrem='sudo pacman -R $(pacman -Qdtq); orph'

Example-up-

I had previously installed germinal which pulled in these dependencies:

extra/appstream-glib  /  ->  0.7.15-1
extra/gcab            /  ->  1.2-1
aur/germinal          /  ->  25-1
extra/intltool        /  ->  0.51.0-4
extra/libstemmer      /  ->  0+337-3
community/tmux        /  ->  2.9_a-4
extra/vte-common      /  ->  0.56.3-1
extra/vte3            /  ->  0.56.3-1

I have now uninstalled it. This leaves me with some orphans:

$ orphrem
checking dependencies...

Package (2)     Old Version  Net Change

appstream-glib  0.7.15-1      -2.95 MiB
intltool        0.51.0-4      -0.15 MiB

Total Removed Size:  3.10 MiB

:: Do you want to remove these packages? [Y/n] 
:: Processing package changes...
(1/2) removing intltool                                       [#################################] 100%
(2/2) removing appstream-glib                                 [#################################] 100%
:: Running post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...

True Orphans:
gcab
libstemmer

$ orphrem
checking dependencies...

Package (2)  Old Version  Net Change

gcab         1.2-1         -0.36 MiB
libstemmer   0+337-3       -0.36 MiB

Total Removed Size:  0.72 MiB

:: Do you want to remove these packages? [Y/n] 
:: Processing package changes...
(1/2) removing libstemmer                                     [#################################] 100%
(2/2) removing gcab                                           [#################################] 100%
:: Running post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...

True Orphans:
None
Optional Dependencies:
1) enca           9) libtiger         17) python-html5lib      25) rrdtool
2) ghostscript       10) libusb-compat    18) python-lxml      26) timidity++
3) gst-plugins-bad   11) libvncserver     19) python-pysocks       27) tmux
4) gtk2-perl         12) ocl-icd          20) python-setproctitle  28) unixodbc
5) java8-openjfx     13) perl-file-mimeinfo   21) python2-mutagen      29) vte3
6) lib32-openal      14) perl-net-dbus    22) qt5-multimedia
7) lib32-v4l-utils   15) perl-x11-protocol    23) qt5-script
8) libfbclient       16) python-dbus      24) qt5-xmlpatterns
Choice: 

As you can see, the packages vte3 and tmux, that were only just installed as dependencies for germinal, do not show up as orphans. They show up in a long list of optional dependencies, and I have an option to uninstall them now.