Packaging ngless with nix & brew
I spent a little bit of time packaging ngless in these last few days. I currently have packages for nix and homebrew. I used homebrew because I wanted to get a package for Mac OS, but there is also an easy-to-install Linux port, so this could be an option for Linux users too.
Because ngless is Haskell based, I was afraid this was going to be complicated. Before stack appeared, getting all your Haskell dependencies right for a Haskell project was a non-trivial affair (The google query cabal hell returns >500k results).
Contrary to expectations, it was surprisingly easy to package ngless with both nix and brew.
Nix
The result looks like this:
{ nixpkgs ? import {}, compiler ? "ghc801" }: nixpkgs.pkgs.haskell.packages.${compiler}.callPackage ./ngless.nix { }
plus the file ngless.nix, which was 95% autogenerated with cabal2nix.
Now, with nix, you can install ngless with a single command:
nix-env -i -f https://github.com/luispedro/ngless-nix/archive/master.tar.gz
At the moment, you need to be on the unstable channel for this to work (because some of the dependencies were only added recently).
The only non-trivial bit was tweaking the dependencies so that ngless can be compiled with GHC 8. I am still using the previous version (7.10) for development and could have just used it as well for this distribution (in fact you can still use it by specifying --arg compiler '"ghc7103"'
on the command line).
However, (1) eventually GHC 8 will take over so we might as well see the issues now and (2) ngless compiled with GHC 8 runs 10~20% faster (in part because of a change in GHC introduced to make ngless faster¹).
Homebrew
Since I already use nix on my laptop and have experience navigating its quirks, I was confident that I could get nix to work, but I had never used brew.
Still, about 30 minutes after starting to google for brew, I had a formula that mostly worked. One or two bug fixes later and it just works. I was able to install ngless on both a Linux and a Mac machine using it. Below I show the whole thing so you can see how small the resulting file is.
On the one hand, it was much simpler than nix; dependencies are implicit and there were no weird error messages from the nix language or any need to follow a long trail of references to figure out where something is defined. On the other hand, though, I did run into a few issues which would not have existed with nix related to versions and caching.
After brew downloads the file corresponding to a specific version, it will not redownload even if you change its URL/SHA256. It just assumes that the previous file is correct and you have to manually delete it from the cache. Not a big deal, but nix would not let you make this mistake.
This seems to be the nix tradeoff: a bit rough on the user experience over the short term but easier to understand and fewer errors over the long term. If the efforts to improve nix's usability bear fruit, it'll be superior to alternatives. Right now, brew is still nicer for casual users.
Here is the complete brew formula:
require "language/haskell" class Ngless < Formula include Language::Haskell::Cabal desc "Domain Specific Language for NGS Processing" homepage "http://ngless.readthedocs.io/" url "https://github.com/luispedro/ngless/archive/34e7e79be62f76b1a4d61d94a59a1e42d9111308.zip" sha256 "1ac04e535390b0a6189bcfc1c486b54443dc46b7f6b74bf5221b8e4c98c463bc" version "0.0.0" head "https://github.com/luispedro/ngless.git" depends_on "ghc" => :build depends_on "cabal-install" => :build depends_on "bwa" => :install depends_on "samtools" => :install def install system "m4 NGLess.cabal.m4 > NGLess.cabal" install_cabal_package end end ¹ This change really did fix a performance bug in GHC, which should help all multi-threaded programmes, but it was ngless that triggered it.