title: Cross-Building Haskell Programs using LXC
author: John Lenz
tags: haskell, lxc
date: September 19, 2012
One of the benefits of Haskell is that compiled Haskell programs can be run on any computer, even
computers without any Haskell packages installed. For this to work the version of libc and various
helper libraries like zlib must match between the build machine and the target machine, so for
deployment between similar distribution versions one can just compile right on the develop machine
and deploy. Unfortunately, if you are like me and want to deploy code to a server running 32-bit
Debian stable and your development machine is 64-bit Ubuntu 12.04, you need a way of compiling
32-bit Haskell programs on the Ubuntu machine.
After some work, I was able to get [virthualenv](http://hackage.haskell.org/package/virthualenv) on
my development machine to cross-compile using a downloaded i386 binary ghc (this used the recent
multiarch support in Debian and Ubuntu). This worked and produced 32-bit Haskell programs and
simple programs worked fine, unfortunately the version of zlib did not match so running more
complicated programs on Debian would exit with an error. (The changes to get virthualenv + i386 ghc
working were mostly to tell ghc to pass "-m32" to gcc.) Because of the library version issue, I
decided to investigate instead using containers to run a copy of Debian on my Ubuntu development
machine. The default container technology in Ubuntu 12.04 is [LXC](http://lxc.sourceforge.net/), so
that is what I used.
# An LXC container with the latest GHC
I suggest you first read [this documentation](https://help.ubuntu.com/12.04/serverguide/lxc.html) on
LXC in Ubuntu. A word of warning, LXC had several significant changes in Ubuntu 12.04 so if you do
a google search for LXC and Ubuntu you get a lot of outdated information. I was tripped up by this
initially, instead I suggest you stick to the Ubuntu document linked above, it is way easier than a
lot of tutorials I found in a google search.
To install LXC, just apt-get install lxc. I didn't need to do any configuration beyond that. Next,
we need to edit the debian template to specify the arch. The ubuntu templates support specifying
the arch on the command line, but unfortunately the debian templates in 12.04 don't. (This might
change in future versions, so if you are reading this much after September 2012 you should check out
the options.) In any case, edit /usr/lib/lxc/templates/lxc-debian and go to around line 191. After
the code checking architecture, I just added a line 'arch="i386"' to force i386 arch.
Next, create the container.
# sudo lxc-create -t debian -n deb386
Next, login with root/root, change the root password, create a user, install sudo, and add the user to sudo.
# adduser myuser
# apt-get install sudo vim-tiny
# usermod -G sudo myuser
From now on, I switch to logging in to the container using ssh as the user I just created. I use
ssh-copy-id so I don't need a password to log in. Once logged in as a user, install the packages
needed by GHC and download the [binary package](http://www.haskell.org/ghc/download_ghc_7_4_2) for
GHC. (Readers from the future: you might consider a later version of GHC, probably the same one
used by the latest haskell platform.)
# sudo apt-get install wget bzip2 libgmp3c2 gcc make libgmp3-dev zlib1g-dev
# wget <url for ghc>
Extract ghc, configure and install. Note I install ghc in a path in opt so in the future I can
upgrade to a new version of GHC easily.
# cd ghc-7.4.2
# ./configure --prefix=/opt/ghc.7.4.2
# sudo make install
# vi ~/.bashrc, add /opt/ghc.7.4.2/bin to the path
Next, download the latest [cabal-install](http://hackage.haskell.org/package/cabal-install) source
package (the link near the bottom for cabal-install-ver.tar.gz). I copy and paste the link to wget
inside the terminal. Extract cabal, and run bootstrap.sh to install cabal locally for your user.
Note, an alternative is to install the [Haskell Platform](http://www.haskell.org/platform/) from
source, but this requires several more debian packages like opengl installed, and since we are just
going to be using virthualenv anyway, the haskell platform isn't required.
# cd cabal-install-0.14.0
# sh bootstrap.sh
# vi ~/.bashrc, add ~/.cabal/bin to path
Next, update the cabal repo and install virthaulenv. Note, virthaulenv was recently renamed as
hsenv but hsenv has not released yet. Readers from the future should use hsenv instead once it is
# cabal update
# cabal install virthualenv
Now that cabal and virthaulenv are installed, you have a full-featured build machine for Haskell.
Right now I build manually by cloning my repo inside the container and running the appropriate
virthaulenv and cabal build commands, but I am considering writing a simple build script which pulls
# An example
The main use for my container is for [gitit](http://gitit.net/) and
[yesod](http://www.yesodweb.com/) to deploy servers to my debian stable server. But these are
slightly more complicated so I will leave talking about them to a future post. But as a short
example, I show how pandoc can be compiled. These are generic virthualenv commands, see the
virthualenv README for more information.
# mkdir pandoc && cd pandoc
# source .virthualenv/bin/activate
# cabal install pandoc
Now the binary at ~/.cabal/bin/pandoc can be copied to debian stable and run.
The main issue with packages compiled this way is cabal's data directory, which programs access using
Whichever directory is used to compile for example gitit must be present on the debian server. In a
future post I will explain in more detail how to make gitit deployable. Note this issue will even
partially hurt pandoc compiled as above, since pandoc also uses getDataFileName for some templates.