Using multiple versions of GHC in parallel with GNU stow

Do any of the following apply to you?

  • You sometimes hack on GHC.
  • You sometimes want to try out an unreleased GHC version.
  • You maintain a library and want to make sure it builds with several different versions of GHC.
  • You want to try out a newly released version of GHC but without blowing away your existing (working!) GHC install.

As you can see, these are roughly in order from most to least “hard core”, and I expect that the last two are fairly common, even if the first two are not. The common thread, of course, is having multiple versions of GHC installed simultaneously.

The solution

A solution that I’ve found which works quite well (on Linux, and I expect it would work on OSX as well) and deserves to be better known is GNU stow. Here’s how it works: instead of installing things directly in /usr/local (or $HOME/local, or whatever), you instead install in sandboxed locations like /usr/local/stow/ghc-7.6.1. You then tell stow that you want to “install” ghc-7.6.1, and it creates all the necessary symlinks to mirror the directory structure under the ghc-7.6.1 directory into /usr/local. But of course it tracks everything so that you can easily uninstall all these symlinks as well. (And yes, it’s smart about symlinking an entire directory when possible, instead of all the individual files, and later “splitting up” such big-scale links when necessary.)

The pros of this sort of scheme should be clear:

  1. Once you’ve “installed” a particular version you just use it as normal—no need to pass the right directories as flags to tools like cabal or ghc-pkg.
  2. Switching to a different version of GHC is relatively quick (see below).
  3. Installing new versions is pain-free; it’s just as easy to have twenty different versions as it is to have two.

Of course, there are a few downsides as well:

  1. Trusting a Perl script to munge your /usr/local. (Though for what it’s worth, I have been using it heavily for about a year now—I currently have six different versions of GHC installed—and have never once encountered a single problem.)
  2. You can’t use several versions of GHC at once—there is always exactly one “current” version. So if you want to, e.g., do test builds of a library under several versions of GHC in parallel, you need a different solution.

A step-by-step guide

So here’s how this works in more detail. First, of course, you have to install stow itself; I’ll assume you can get it from your OS package manager or know how to build it from source. What you get is simply an executable called stow.

Now, when installing GHC (whether a binary distribution or from source), give the configure step an extra prefix argument, for example:

./configure --prefix=/usr/local/stow/ghc-7.6.1

Just take the directory where you would usually install GHC, and add stow and then an additional subdirectory for the particular version you are installing. This directory doesn’t have to already exist; the installer will create it if it doesn’t.

(Note that if you already have GHC installed, you’ll have to uninstall it first and then reinstall it with stowstow won’t work unless it can manage all your GHC installs.)

After the installation process completes, you have an unusable ghc because, of course, .../stow/ghc-7.6.1 isn’t on any relevant paths. All you have to do to finish installing it is

cd /usr/local/stow && stow ghc-7.6.1

This instructs stow to make symlinks into ghc-7.6.1 from /usr/local. After this completes, that’s it! You can use your new GHC as usual.

To uninstall, follow the same procedure but give stow the -D option:

cd /usr/local/stow && stow -D ghc-7.6.1

To switch from one version of GHC to another just uninstall the current version and then install the new one. I actually have a script which automates this process: it looks in the stow directory to see which versions are availble, prompts me to choose one from a list, then uninstalls the current version (obtained with ghc --numeric-version) and installs the newly selected version. On my laptop this process takes just a couple seconds.

That’s it! Happy stowing!

About Brent

Assistant Professor of Computer Science at Hendrix College. Functional programmer, mathematician, teacher, pianist, follower of Jesus.
This entry was posted in haskell and tagged , , , . Bookmark the permalink.

10 Responses to Using multiple versions of GHC in parallel with GNU stow

  1. Why not xstow?
    (actually I don’t have much experience with either)

    • Brent says:

      I actually hadn’t heard of xstow. But after taking a look it seems that the reasons to use xstow would be (1) you need to use stow on a system that doesn’t have Perl installed; (2) you want some of the fancy features that xstow adds; but neither applies to me, so I see no particular reason to prefer one over the other.

  2. illissius says:

    I simply download the GHC binary tarballs, install them into separate directories (/opt/ghc7.2, /opt/ghc7.4, …), and then set PATH and LD_LIBRARY_PATH to point at the one I want to use. With a simple helper script so my fingers don’t get sore: (though I expect WordPress will mangle it)

    # $1 -> base dir, $2 -> program, $3+ -> args

    I also have simple wrappers in /usr/local/bin for ghc7.2, runghc7.2, ghci7.2, ghc7.4, … and so on just to make life easier. The advantage of this approach is that it’s “stateless”, the only thing that matters is your environment variables, you don’t need to muck around with symlinks, and there’s no obstacle to using different versions of GHC “simultaneously” (I suppose one-and-then-the-other is the closest you can get).

    I have one annoyance: cabal-install has its own version of Cabal that it was built/installed with, which in my case is pretty old, and each version of GHC also has its own version of Cabal. In many cases my cabal-install’s version of Cabal is too old and I have to tell it to use GHC’s version. But the only way to do that is to specify the version number, which is different for each GHC, in ~/.cabal/config. So editing it every time can get to be irritating. I haven’t found a way to do it with an environment variable or anything similar. If you happen to know a solution, I would much appreciate it. :)

    • Brent says:

      Well, I guess there’s more than one way to skin a cat! Glad you found something that works well for you. As for cabal-install, I’ve never had that problem so I don’t know. Why not install a newer version?

  3. Johan Tibell says:

    > You maintain a library and want to make sure it builds with several different versions of GHC.

    This use case is supported by Cabal itself, which lets you specify which GHC to use using the -w flag.

  4. Badi' Abdul-Wahid says:


    Thanks for this post, it is always interesting to see how others approach common problems.

    I wanted to point out the modules project [1]. My work often requires use of different versions (often built with incompatible configurations) of the same software. Switching between them is as simple as “$ module load ghc” (or gromacs/4.5.5 or 4.0.3 as my case may be).

    Similar to what illissius above does, the module file allows environmental variables to be set or modified.

    There are many ways to solve the problem of support multiple versions. For my purposes though, modules has been the simplest to setup and maintain. I eventually installed it on my laptop, allowing me to use macports, homebrew, or my own custom installations, without having them interfere with each other.


  5. C. Godfoot says:

    Sounds a lot like a very limited Gobolinux ( install. With the advantage of it being an active project.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.