Edit (2015-08-10): the tools have been updated to fix a few bugs as well as incompatibilities with the latest GHC; they now also work on Cabal sandboxes.

Problems

A. There is no way to uninstall packages in Cabal

Currently, cabal-install doesn’t know how to uninstall packages. The best you can do is to unregister the package with the GHC package-database manager, eliminating the package metadata from the database without removing the actual files.

There is a package called cabal-uninstall (and probably a few others) that remedies this to some extent, but it doesn’t work if the package has already been unregistered.

Additionally, while ghc-pkg does warn you of dependency breakage, it doesn’t provide an easy way to remove a package and all its dependents transitively.

B. Removing broken packages is a tedious chore

If for some reason your packages get broken, there’s no easy way to clean up the mess.

On my system, this usually happens because Arch Linux upgrades some of the globally-installed packages, breaking swaths of user-installed packages in the process (or it could be due to a GHC upgrade).

To fix this you’d have to unregister all of the broken packages. And if you want to reclaim the space used by the dead packages, you’d have to dive into ~/.cabal and remove them manually.

Tools

To help fix the issues outlined above, here are some scripts to assist the process:

  • cabal-gc: a “garbage collector” that searches for any lingering packages whose files still remain but are no longer registered. (Note: it does not remove any executables in the bin directory.)

  • ghc-pkg-check: a thin wrapper around ghc-pkg check that suppresses the haddock warnings that no-one seems to care about.

  • ghc-pkg-unregister: a thin wrapper around ghc-pkg unregister that allows multiple packages to be unregistered in one call.

While it’s possible to fully automate this process, it’s best to do them one at a time to make sure nothing bad happens.

Tutorial

First off, if you want to manage packages in a sandbox, be sure to run this command prior to everything else below:

cabal [--sandbox-config-file=./cabal.sandbox.config] exec -- "$SHELL"

(The --sandbox-config-file=… flag is optional.)

Now,

  • if your goal is to uninstall a specific package as well as its dependents, then follow all the steps below;
  • if your goal is to only remove broken packages, then jump straight to step 2.

1. Remove the target packages

To remove packages, the first thing to do would be to run:

ghc-pkg-unregister my-package-0.5.0 some-package-1.0.0

If there are any dependent packages, you will receive an error telling you that those will be broken:

ghc-pkg: unregistering some-package-1.0.0 would break the following packages: other-package-2.0.0 another-package-3.0.0 (use --force to override)

If you’re alright with this, add the --force flag to remove it for good. We’ll eliminate the broken packages shortly.

ghc-pkg-unregister --force some-package-1.0.0

unregistering some-package-1.0.0 would break the following packages: other-package-2.0.0 another-package-3.0.0 (ignoring)

Despite the conditional mood in the message, the package will indeed be unregistered.

2. Remove the dependent packages

Now, we need to get rid of the broken packages. Let’s see which packages are broken:

ghc-pkg-check

There are problems in package other-package-2.0.0: dependency "some-package-1.0.0-…" doesn't exist There are problems in package another-package-3.0.0: dependency "some-package-1.0.0-…" doesn't exist The following packages are broken, either because they have a problem listed above, or because they depend on a broken package. other-package-2.0.0 another-package-3.0.0

To remove all of these broken packages, run:

ghc-pkg-check --simple-output | xargs ghc-pkg-unregister --force

To make sure all of the broken packages are taken care of, run ghc-pkg-check again. If there are more broken packages, you’ll need to keep repeating this step until all of them have been eliminated.

3. Delete the associated files

This step is optional, although it can be useful to reclaim some disk space.

Since we will be deleting files, it’s best to perform a dry run first:

cabal-gc

can be removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/anoth_d9K7jTHCBftCdWJFHesDel can be removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/other_8j0AdSQVkKosS3ev8r3pBu can be removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/mypac_XYf894wKraySpNs9RUwwMV can be removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/somep_x3XTaLFeSmcMHNpv7Ng8xw can be removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/another-package-3.0.0 can be removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/other-package-2.0.0 can be removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/my-package-0.5.0 can be removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/some-package-1.0.0

If everything looks okay, go ahead and delete them for real:

cabal-gc -r

removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/anoth_d9K7jTHCBftCdWJFHesDel removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/other_8j0AdSQVkKosS3ev8r3pBu removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/mypac_XYf894wKraySpNs9RUwwMV removed: /home/user/.cabal/lib/x86_64-linux-ghc-7.10.1/somep_x3XTaLFeSmcMHNpv7Ng8xw removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/another-package-3.0.0 removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/other-package-2.0.0 removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/my-package-0.5.0 removed: /home/user/.cabal/share/doc/x86_64-linux-ghc-7.10.1/some-package-1.0.0