Last year, I purchased the Raspberry Pi 3B+ for some hobby projects. Shortly after I started to learn Haskell, I was wondering if I could build and run Haskell software on the ARMv7 platform. So I made some attempts with my Raspberry but quickly realized that despite being able to run compiled binaries, the 1GB or RAM is just not enough to build them using the GHC. So I gave it up for a while.

Then the Raspberry Pi 4B came out, with option to have up to 4GB of RAM. So I purchased the 4GB model and tried again. This time, with greatly increased available RAM size, I was able to make things working. You can find the entire process described in steps bellow.

1 Before we begin

First, we should talk about platforms supported by GHC. You should not face any trouble installing and running GHC on your Windows/Linux/macOS machine, because these are Tier 1 platforms, meaning that they are top priority for GHC team and should have full support. On the other hand, ARM architecture is Tier 2, meaning that GHC build might not be available for our platform in desired version, which was actually the biggest issue I faced. We’ll talk about this later.

Also worth mentioning that for my experimenting, I’m running official Raspbian distribution, meaning that I’m using 32bit architecture. If anyone will try this with some 64bit Linux distro, I’d gladly provide link here.

2 Installation steps

2.1 Increase SWAP size

Although 4GB of RAM ought to be enough for anyone™, I decided to increase the default value of 100MB to 1GB, just in case.

$ sudo dphys-swapfile swapoff  # disable swap
$ sudo vim /etc/dphys-swapfile # and set 'CONF_SWAPSIZE' to 1024
$ sudo dphys-swapfile setup    # refresh with new settings
$ sudo dphys-swapfile swapon   # re-enable swap

2.2 Install Stack

This step will download current version of Stack for our architecture.

$ curl -sSL | sh

It’s also good to add path to dir ~/.local/bin to $PATH, since this is the directory where binaries installed by stack install are placed. You can do that by adding following line to .bashrc:

export PATH="$PATH:~/.local/bin"

2.3 Setup Stack project

Here comes the tricky part. As I mentioned above, because ARM is GHC Tier 2 platform, there might not be GHC version matching the Stackage resolver you use in your project. For example, my project used version lts-14.6 that matches with GHC 8.6.5. However, when you try to build the project using stack setup, you might face following error:

$ stack setup
No setup information found for ghc-8.6.5 on your platform.
This probably means a GHC bindist has not yet been added for OS key 'linux-armv7', 'linux-armv7-ncurses6', 'linux-armv7-tinfo6'.
Supported versions: ghc-7.10.2, ghc-7.10.3, ghc-8.0.1, ghc-8.0.2, ghc-8.2.1, ghc-8.2.2, ghc-8.6.3

I really wasn’t interested to custom build proper GHC version from sources, but you can force GHC version by adding below shown argument to any stack command call, so I decided to force version 8.6.3 instead of 8.6.5, which is unavailable. Fortunately, I haven’t faced any compatibility issues.

$ stack setup --compiler ghc-8.6.3

Alternative would be to use older version of Stack resolver, but that might not be an option, cause the last one for GHC version 8.6.3 is lts-13.11 and some of the packages might be present in too old version.

2.4 Solving troubles with GHC

After running the stack setup from previous step, you might face some errors related to missing LLVM dependencies. In my case, GHC version 8.6.3 requires following dependency to be installed:

$ sudo aptitude install llvm-6.0-dev

Also you’ll need to add the following path to $PATH variable (for example by adding to .bashrc):

export PATH="$PATH:/usr/lib/llvm-6.0/bin/"

Next problem you might face is this error when trying to build project using stack build:

/tmp/ghc19719_0/ghc_8.s: Assembler messages:

/home/pi/Repositories/haskell-tools/mat35//tmp/ghc19719_0/ghc_8.s:41:0: error:
     Error: selected processor does not support `movw r3,:lower16:stg_bh_upd_frame_info' in ARM mode
41 |         movw    r3, :lower16:stg_bh_upd_frame_info
   | ^

I was able to fix this by simple wrapper, that wraps the original GHC binary and adds the -opta-march=armv7-a argument to it:

$ cd ~/.stack/programs/arm-linux/ghc-8.6.3/bin
$ mv ghc-8.6.3 ghc-8.6.3-bin
$ touch ghc-8.6.3
$ chmod +x ghc-8.6.3

Then add following content to the newly created file:

~/.stack/programs/arm-linux/ghc-8.6.3/bin/ghc-8.6.3-bin -opta-march=armv7-a $@

3 Enjoy

And that’s pretty much all. Now you should be able to build and run any Haskell library or program, just prepare yourself that compilation times are still pretty much longer than on your laptop/PC.