How to install xmonad and xmobar via stack

October 4, 2017

Arch Linux recently changed their Haskell packages (no more static linking), which broke a bunch of stuff. Specifically, upgrading xmonad-0.13-8xmonad-0.13-9 produces errors when recompiling xmonad, and only downgrading seems to fix the issue; and using cabal-install (cabal install xmonad) with Arch’s ghc likewise produces errors, failing to install xmonad. For those of you coming from Google, the errors are of the format Could not find module .... See here and here, for example.

In addition, Arch’s xmobar (with xmonad-0.13-8) has been crashing sporadically (segmentation faults), for some reason. See here, for instance.

In this post, I’ll explain how I got xmonad (and xmonad-contrib) and xmobar installed and working – and no xmobar crashes, so far – via stack. I’ll try to keep it as short and simple as possible. No in-depth explanations; just a straightforward, step-by-step rundown of what I did. The usual disclaimers (YMMV, etc.) apply.

I’ll assume you already have an ~/.xmonad directory with an xmonad.hs config file. If you don’t use xmobar, this post can still be useful; just ignore the lines/steps that refer to xmobar.

Step 1: Get stack

There are couple ways to get stack. I installed stack-static from the AUR, because it doesn’t come with any Haskell dependencies.

If you don’t care about tracking stack with your package manager, then, as the stack how-to-install explains, just run

curl -sSL https://get.haskellstack.org/ | sh

or

wget -qO- https://get.haskellstack.org/ | sh

Step 2: Install GHC with stack

To build and install Haskell packages, we need GHC. Simply run

stack setup

to install GHC into ~/.stack. Useful for the kind of sandboxing projects that we’re doing with xmonad.

NB: You can run stack ghc to do things with GHC, stack ghci to fire up interactive GHC, and so on.

Step 3: Get xmonad, xmonad-contrib, and xmobar

We’ll be turning our ~/.xmonad directory into a stack project, so first, head over there.

cd ~/.xmonad

For the remainder of this post, I’ll assume you’re inside ~/.xmonad.

Next, download the xmonad, xmonad-contrib, and xmobar Git repositories, which contain the .cabal and .yaml files that stack will look for in the next step. I like to add -git to their directory names, just as a reminder.

# From inside ~/.xmonad.
git clone "https://github.com/xmonad/xmonad" xmonad-git
git clone "https://github.com/xmonad/xmonad-contrib" xmonad-contrib-git
git clone "https://github.com/jaor/xmobar" xmobar-git

Your ~/.xmonad directory should now contain xmonad-git, xmonad-contrib-git, and xmobar-git, each of which contains a .cabal file and a .yaml file.

Step 4: Initialize stack

This step is easy: just run

# From inside ~/.xmonad.
stack init

Stack will find the .cabal and .yaml files and auto-create the file stack.yaml for you. It’ll look like this:

# ~/.xmonad/stack.yaml
resolver: lts-9.6
packages:
- xmobar-git
- xmonad-git
- xmonad-contrib-git
extra-deps: []
flags: {}
extra-package-dbs: []

At this point, you can modify stack.yaml to add flags, etc. The only change I made was to add the flag all_extensions to xmobar, by changing

flags: {}

to

flags:
  xmobar:
    all_extensions: true

This flag provides all the xmobar bells & whistles, like support for xft, mpd, battery, wifi, etc.

NB: If you add the with_iwlib flag (or all_extensions), you’ll need to also install the iwlib C library and headers. In Arch Linux, just install wireless_tools; in Debian-based systems, libiw-dev. Or, in your stack.yaml, change

extra_deps: []

to

extra_deps:
    - iwlib-0.1.0

(The specific version you’ll need will change over time. If stack install from step 5 produces an error, just see what stack recommends.)

Step 5: Build and install everything

Next, run

# From inside ~/.xmonad.
stack install

to build and install xmonad, xmonad-contrib, and xmobar (and all their dependencies). You’ll now have two new binaries, xmonad and xmobar, installed into ~/.local/bin.

NB: You’ll want to add ~/.local/bin to your PATH, if it isn’t already. (If you use a login manager, see Step 9 below.)

Step 6: Write a build file

Since we’re doing everything via stack, rather than ghc directly, xmonad --recompile won’t quite work yet. As of xmonad 0.13, we can write a custom build script, named build and located inside ~/.xmonad, which will use stack ghc to recompile xmonad. (Borrowed from pbrisbin.)

# ~/.xmonad/build
#!/bin/sh
exec stack ghc -- \
  --make xmonad.hs \
  -i \
  -ilib \
  -fforce-recomp \
  -main-is main \
  -v0 \
  -o "$1"

Make sure it’s executable:

chmod a+x build

Step 7: Recompile and restart xmonad

You should now be able to recompile and restart xmonad (and xmobar) with

xmonad --recompile && xmonad --restart

NB: I had to restart my computer in order for xmobar to start up properly – probably because xmonad couldn’t find the xmobar binary.

Step 8: Updating

Whenever you update your xmonad, xmonad-contrib, or xmobar repositories, just cd ~/.xmonad and run

stack install

to rebuild and reinstall everything.

NB: If you add a new flag or extra dependencies (in stack.yaml), you may need to run stack clean first.

(Step 9: Loose ends with login managers)

If you use a login manager, such as LightDM, then you may need to take some additional steps. I don’t use a login manager, nor do I know much about them, but I’ll use LightDM as the working example since I’ve read a little about it.

First off, LightDM uses *.desktop files located /usr/share/xsessions to know which desktop environments (or window managers) you have available to choose from. So, you’ll probably need to create xmonad.desktop. The xmonad package from the official Arch repos installs the following file, so you can just copy it verbatim and place it into /usr/share/xsessions:

[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Xmonad
Comment=Lightweight X11 tiled window manager written in Haskell
Exec=xmonad
Icon=xmonad
Terminal=false
StartupNotify=false
Categories=Application;

Second, running xmonad --recompile may not work yet. If it doesn’t, make sure that you’ve added ~/.local/bin to your PATH by adding it to one of your shell profile files, such as ~/.profile or (if you only use one shell, e.g. Bash) ~/.bash_profile, and not to your shell’s configuration file (e.g. ~/.bashrc). The reason is that LightDM (and by extension xmonad) is invoked from a login shell, which sources profile files like ~/.profile, but not (necessarily) shell config files like ~/.bashrc. See this SE thread and this Quora answer to learn more about the difference.

If that still doesn’t work, then it’s possible that your login manager doesn’t even source ~/.profile (see the end of the SE thread linked above). In that case, a possible fix is to just manually symlink ~/.local/bin/xmonad to /usr/bin (since the latter is definitely in your PATH; thanks to Ashesh in the comments below for this fix):

ln -s ~/.local/bin/xmonad /usr/bin