Building Haskell projects for Nix

Similar to the last post on building a C project, today I quickly cover how to build a Haskell project.

You might ask why? I got asked about that stuff by my friendly office colleague Samuel. By the way, without him I would have never installed/used Nixos in the first place. Good fella, go checkout his blog.

#packaging #coding #nix #haskell

The goal was to package oama. Oama helps you to renew OAuth2 tokens from the command line. To be honest, I haven't checked the details on how it works yet, I was only interested in packaging it for Nix.

I was reading a bit on how it can be used with my favorite msmtp client.

Then I read some of the Nix documentation on how to build and use Haskell packages. They describe how to quickly bootstrap development environments (i.e., a Nix shell) using the handy cabal2nix utility.

The first step for packaging was also to convert the cabal description of the package into a valid Nix expression which already proved useful for development purposes.

git clone https://github.com/pdobsan/oama.git oama.git
cd oama.git
cabal2nix . > oama.nix

Create default.nix:

# Retrieve nixpkgs impurely from NIX_PATH for now, you can pin it instead, of course.
{ pkgs ? import <nixpkgs> {} }:

# use the nixpkgs default haskell package set
pkgs.haskellPackages.callPackage ./oama.nix { }

If you have a look at oama.nix it contains a lot of references to Haskell packages (.e.g, haskellPackages.aeson). The callPackage takes care of the name resolution in this case. haskellPackages.mkDerivation is a wrapper around stdenv.mkDerivation (see last post building a C project), but works without the standard Haskell build tool cabal-install.

To build oama and quickly run it in a shell:

nix-shell -p cabal-install
nix-build default.nix
./result/bin/oama

I did not look further into shellFor at this point, because I was interested in creating a Nix package that I can install globally.

For this, I took the “stdenv template” from the last post and merged 😻 it with the oama.nix output from cabal2nix. The result:

let
  pkgs = import <nixpkgs> { };
  hpkgs = pkgs.haskellPackages;
in
  hpkgs.mkDerivation {
    pname = "oama";
    version = "0.10.1";
    src = pkgs.fetchgit {
      url = "https://github.com/pdobsan/oama.git";
      rev = "refs/tags/0.10.1";
      hash = "sha256-mQBlCrF9rvFOfSOxhMi6JUKDJocFmO4Hhc3Zy7AqiXk=";
    };

    isLibrary = true;
    isExecutable = true;
    libraryHaskellDepends = with hpkgs; [
      aeson base bytestring containers directory hsyslog http-conduit
      network-uri optparse-applicative pretty-simple process string-qq
      strings text time twain unix utf8-string warp yaml
    ];
    executableHaskellDepends = with hpkgs; [
      aeson base bytestring containers directory hsyslog http-conduit
      network-uri optparse-applicative pretty-simple process string-qq
      strings text time twain unix utf8-string warp yaml
    ];

    mainProgram = "oama";
    license = pkgs.lib.licenses.bsd3;
  }

Essentially, I only had to use nix-prefetch-git to get the shasum of the git revision and make sure I use the pkgs and hpkgs (reference to pkgs.haskellPackages) correctly in the right places.

Afterwards, again, building and installing oama globally was a peace of cake 🧁:

nix-build oama.nix
nix-env -i -f oama.nix

Now I only have to figure out what to do with this tool 🤔 but this is left for another post.

🛜 RSS | 🐘 Mastodon | 💬 IRC