Debian – How to find installed deb packages needlessly marked as manually installed

aptaptitudedebianpackage-management

Problem statement

apt and aptitude both remember which packages were installed automatically as dependencies of some other package or manually for some reason.
For various reasons the list of manually packages may become needlessly large and can start to accumulate over time. These packages will be uninstalled only when specifically requested.

One common example are library packages which may be installed as hotfix for some reason, but later they could be marked as automatically installed once some software depending on them is installed. However, if that doesn't happen, they will remain installed even once the packages depending on them are uninstalled. Tools like deborphan may find these left-over packages in some cases, but they have to guess a lot and it's not a systemic solution.

Description of a possible solution

That's why I'm looking for a way to find such packages that can be marked as automatically installed without them resulting to be uninstalled. This would mean finding packages that are currently marked as manually installed, but are dependencies (or recommended dependencies) of some other installed package. These packages could then be marked as automatically installed and will be removed once they are not needed anymore by other software.

I think I might be able to make a tool like that with the Python bindings for libapt, but I would hate to reinvent the wheel if something like that already exists.

Best Answer

Search for packages that are manually installed, and that are a mandatory or recommended dependency of an installed package. Aptitude can do that.

aptitude search -q -F %p '?installed !?automatic (?reverse-depends(?installed .*) | ?reverse-recommends(?installed .*))'

I don't think there's a way to indicate what dependency was found for each package. If you want that information, Python would be the way to go. This very quick-and-dirty script seems to do it (mostly, I think it may be incorrect in some non-straightforward cases involving disjunctions, version dependencies, pre-depends, …).

#!/usr/bin/env python2
import apt
packages = apt.Cache()
covered = {}
# Inverse dependency computation: for each installed package, record which
# packages require it (as Depends: or Recommends:).
for p in packages:
    if p.installed:
        for l in p.installed.dependencies + p.installed.recommends:
            for d in l:
                if packages.has_key(d.name) and packages[d.name].installed:
                    if not covered.has_key(d.name):
                        covered[d.name] = []
                    covered[d.name] += [p.name]
# Print out the manually installed packages that are required by another
# installed package, as well as the requiring package(s).
for p in sorted(covered.keys()):
    if not packages[p].is_auto_installed:
        print(' '.join([p] + covered[p]))