"couldn't read cabal file Cabal.cabal"

Installing Haskell on Ubuntu seems to be a challenge. But maybe I can help!

The root cause of the problem, as is often the case in Haskell, has to do with library versions. Ubuntu 13.04, the most recent version of Ubuntu at the time of writing, uses a version of ghc which isn't compatible with any version of the Haskell Platform. On the older Ubuntu 11.10, an equally-old version of the Haskell Platform is available, but its version of cabal-install cannot install anything! It always gives this error:

couldn't read cabal file Cabal/1.18.0/Cabal.cabal

Let me show you how this error can be fixed.

Packages from the future

Our first hint that the problem has to to with version numbers is that "cabal --version" tells me that it is linked with Cabal 1.10.1, yet it complains about Cabal 1.18.0! How does Cabal even know about this newer version of itself?

The answer is that after its installation, Cabal has asked me to run "cabal update", and foolishly, that's what I did.

"cabal update" connects to the internet and fetches one .cabal file for each version of all the packages which have ever been uploaded to hackage. The problem is that the most recent versions of those packages use the most recent Cabal format for their .cabal files, a format which didn't exist in version 1.10.1; so of course, our old Cabal 1.10.1 cannot read them.

Death to visitors from the future

The solution is clear: we must get rid of those newer files. But which ones are too new?

.cabalfiles are supposed to specify which version of Cabal they need, but I have opted for a more pragmatic solution: automatically deleting each file which Cabal complains about until it stops complaining. Cabal only complains about one file at a time, so the process is a bit slow, but it works!

Cabal stores its file list inside a tar file, so our first step is to extract those files into the filesystem, where we can manipulate them more easily.

mkdir cabal-packages
cd cabal-packages
tar xvf ~/.cabal/packages/hackage.haskell.org/00-index.tar

There is also a tar.gz, but it's not used.

Next, we need a way to update Cabal to reflect our changes. Here is the simple script "update_tar.sh" which I have created for this purpose.

rm -f ~/.cabal/packages/hackage.haskell.org/00-index.tar
tar cvf ~/.cabal/packages/hackage.haskell.org/00-index.tar *

The script "remove_future_packages.sh" is only slightly more complicated. It runs an arbitrary Cabal command, watches it fail, extracts the name of the next problematic file, deletes it, and tries again. And again.

set -e
while true; do
  ./update_tar.sh > /dev/null
  # capture errors of the form
  # cabal: Couldn't read cabal file "hashable/"
  ERR="$(cabal install cabal-dev 2>&1 || true)"
  if [ "$(echo "$ERR" | grep "^cabal: Couldn't read cabal file")" ]; then
    FILE="$(echo "$ERR" | grep "^cabal: Couldn't read cabal file" | cut -d'"' -f2)"
    DIR="$(dirname "$FILE")"
    echo "removing $DIR"
    if [ -d "$DIR" ]; then
      rm -rf "$DIR"
    echo "done!"

Lies, damn lies, and cabal version bounds

After the script was done removing all the problematic files, I could finally run Cabal... only to see it fail halfway through a build, with a different error. The specifics will vary depending on the package you are trying to install; in my case, I was installing cabal-dev. After all the newer versions were removed by my script, the most recent version which Cabal 1.10.1 could still read was cabal-dev-0.9.2. Its build failed while looking for catchIOError, a relatively new function which was introduced in base-4.4. My version of base was older than that, but Cabal didn't think that was a problem, because the .cabal file for cabal-dev-0.9.2 states that it will work with base-4.0 and above. Lies! It doesn't!

Fortunately, the solution was simple: I removed cabal-dev/0.9.2 and re-ran update_tar.sh to tell Cabal about my change. And then, finally, cabal-dev-0.9.1 was installed without a fuss!

This adventure with problematic version numbers has encouraged me to test my Haskell packages with many versions of their dependencies, to make sure that my .cabal files don't lie. And the tool which will help me do this is, let's see... oh, wait, it's cabal-dev! How ironic.