XMonad and Taffybar on NixOS using Stack

2018-02-16

I recently installed NixOS on one of my laptops. I setup XMonad and Taffybar, using stack to compile them. This is somewhat unconventional on NixOS, so I wanted to write a blog post detailing how I did it.

These steps are aimed for people using NixOS, but they should work for any Linux distro with slight modification.

XMonad on NixOS the normal way

The normal way to use XMonad on NixOS is to enable it in /etc/nixos/configuration.nix:

The downside of this is that you must configure which GHC version and XMonad version you want to use in the configuration.nix file1. This isn't a huge problem, but it makes the solution non-portable to other Linux distros. The method I explain below should be portable to other distros as-is.

XMonad on NixOS the stack way

Before building XMonad, stack needs to be installed on the system. In general, you should install the latest version of stack.2

One easy way to do this is as follows:

  1. Use your package manager to install stack.
  2. Have stack upgrade itself to the latest version.
  3. Uninstall the older version with your package manager.

On NixOS

The previous three steps looks like the following when done on NixOS:

  1. Install stack with nix-env:

    You can check that stack is actually installed with the following command:

  2. Have stack upgrade itself to the latest version.

    stack will download the latest version of itself and install it to ~/.local/bin/.

    Make sure ~/.local/bin/ is on your PATH. This can be done in your current bash session like the following:

    The above statement should also be added to your ~/.bashrc file so that stack will be on your PATH for every bash session.

    You can check to make sure the latest version of stack is being used:

  3. Use nix-env to uninstall the stack executable installed in step 1.

If you ever want to upgrade to a newer version of stack, you can just run stack upgrade again.

NixOS-specific stack settings

There are a few NixOS-specific settings needed for stack. These should be added to the ~/.stack/config.yaml file. This is not needed for other distros:

You must specify the system packages that should be available when running stack build or stack exec. The packages specified above are needed for XMonad and Taffybar.

Changes to /etc/nixos/configuration.nix

Before setting up XMonad, some options must be enabled in /etc/nixos/configuration.nix.

Here is a /etc/nixos/configuration.nix file with the relevant options:

After modifying this file, you should run sudo nixos-rebuild switch to make sure the changes take effect.

On other distros, this corresponds to the following steps:

  • Install dmenu.
  • Install X11 packages.3
  • Install upower and enable the upower daemon.

Compiling XMonad and Taffybar

Now that we have the latest version of stack and all required system packages, it is time to compile and install XMonad and Taffybar.

xmonad.hs

XMonad is configured with a file ~/.xmonad/xmonad.hs. This is just a normal Haskell file. Writing the xmonad.hs is out of scope for this article, but you should be able to find many examples by googling. My xmonad.hs is here.

When XMonad is launched, it calls GHC to compile this file and produce a new XMonad binary with all the options you have specified. It then re-execs this new binary.

This works well when GHC and XMonad are installed system-wide. However, when you are using stack, this obviously isn't going to work. XMonad won't be able to find GHC.

Recent versions4 of XMonad provide a way around this: a build.sh file.

build.sh

Instead of trying to call GHC directly, recent versions of XMonad will instead execute a ~/.xmonad/build.sh file, if it exists. You are free to write any sort of build steps you want in this build.sh file.

We will be using build.sh to do the following things:

  1. Install the gtk2hs-buildtools Haskell package. This is used by Taffybar.
  2. Install the taffybar Haskell package.
  3. Install the xmonad and xmonad-contrib Haskell packages.
  4. Build the ~/.xmonad/xmonad.hs file.

My build.sh file can be found here.5

In the beginning of the build.sh file, I source a file ~/.xmonad/xmonad_build_vars.sh. This file looks like the following:

These settings will come in handy later, when actually running XMonad and Taffybar.

Now ~/.xmonad/build.sh can be run.

This will produce a ~/.xmonad/xmonad-x86_64-linux executable. This executable is XMonad compiled with your settings from xmonad.hs. There will also be xmonad and taffybar executables in ~/.xmonad/local-bin/.

Running XMonad and Taffybar

There are many different ways of running XMonad. Two popular ways are to use either startx or xdm.

startx is run from the command line. When run, it executes the file ~/.xinitrc. xdm is a graphical login manager. If you enter the correct username and password, it executes the ~/.xsession file for you.

In general, the contents of these files should be the same. The first part of the script will fork off some background processes. The second part of the script will exec your window manager (which is XMonad in this case).

~/.xsession

NixOS does not currently have support for startx, so the remainder6 of this article will be explaining how to use xdm and ~/.xsession.

If you added the services.xserver.enable = true setting to your /etc/nixos/configuration.nix file, then xdm should start up when you boot your computer.

The following is an example ~/.xsession file. Make sure to make it executable:

Some things to keep in mind:

  • I've seen systems where /bin/sh will always be used to run ~/.xsession and ~/.xinitrc. Some versions of sh do not understand bash's source alias for .. Make sure you don't use any bash-isms in this file.
  • Taffybar is similar to XMonad in that it recompiles a ~/.config/taffybar/taffybar.hs file when it is executed. This means that Taffybar must be run with stack exec, so it gets access to all the correct Haskell libaries. You can see my taffybar.hs file here.

You can see my ~/.xsession, which contains some examples of setting environment variables and running background processes.

xdm

When you type your username and password into xdm, it will run your ~/.xsession file. This should run XMonad. If you are using my xmonad.hs, you should see Taffybar at the top of your screen. You should also be able to use dmenu to run any other program with the key Super + p.

Conclusion

This setup is a little complicated, but it works well across distros, whether you are using startx or xdm.

Footnotes


  1. This could be done using the xmonad.haskellPackages configuration option.

  2. Older versions of stack will not work with newer versions of lts.

  3. If you are unsure of which packages are needed, consult your distro's documentation.

    If you can't find anything in your distro's documentation, one easy trick is to install XMonad (or another window manager) through your system's package manager. This should pull in all X11-related dependencies. Then you can just uninstall the XMonad package installed through the package manager.

  4. This functionality was available starting with xmonad-0.13.

  5. My build.sh file is somewhat complicated. Since build.sh is run every time XMonad is run, I want build.sh to be able to run as quickly as possible. In order to run only the minimal number of commands necessary, I am using a Makefile-like timestamp-based build process. For instance, if the build.sh file is older than the taffybar binary, then I don't try to reinstall Taffybar. However, if the build.sh file is newer than the taffybar binary, then I run stack install taffybar to reinstall Taffybar.

    In practice, this is very flexible and works reasonably well. However, if I was starting from scratch, I might elect to use a proper build system, like make or shake.

    Another possibility would be to create a Haskell package for your XMonad configuration, complete with a stack.yaml file. Running stack build in this directory would create the XMonad executable for your config.

  6. If you want to use the startx approach on a different distro, you will need to install a package that provides the startx binary. This package is generally called something like xinit or x11-xinit.

    After installing this package, create the ~/.xinitrc file and run startx. XMonad should start up.

tags: nixos, haskell