Nix Notes: Overriding Haskell Packages
Setup
In one of the projects, I use a pinned version of nixpkgs based on miso
.
There are two advantages to that, one is that I know that miso
builds with that version,
and the second one is that I can take advantage of miso
’s cachix
cache and not compile everything from scratch.
In the most basic form, it looks like this.
File nixpkgs.nix
:
let
bootstrap = import <nixpkgs> {};
misoTarball = bootstrap.fetchFromGitHub {
owner = "dmjio";
repo = "miso";
rev = "2c193a3253216d70f0ac182fbe9c801de00363ae";
sha256 = "1ywksdzcfd339x1hxp5pvkgbv9mdy1y0971k8v161hg33na2p8wz";
};
miso = import "${misoTarball}" { } ;
inherit (miso) pkgs;
in miso.pkgs
I can then use it in another file to compile my local Haskell package named automation-server
.
File default.nix
in the same directory as the cabal file:
let
pkgs = import ./nixpkgs.nix;
ghc865 = pkgs.haskell.packages.ghc865;
myPackage = ghc865.callCabal2nix "automation-server" ./. { };
in myPackage
nix-build
compiles that package and returns the path to compiled binaries.
Easy.
Problem
I wanted to use the haxl
library,
version at least 2.1.0.0
because of GenHaxl
having an incompatible number of arguments with the previous version,
so I modified the cabal file with a haxl >= 2.1.0.0
version bound.
I was a bit surprised when it failed with:
Setup: Encountered missing dependencies:
haxl >=2.1.0.0
If I removed the version bound it was able to depend on haxl
, but some older version and my code didn’t compile.
Partial solution
I eventually
found out
that you can override
the version of the package in pkgs
.
You do it like this:
let
pkgs = import ./nixpkgs.nix;
ghc865 = pkgs.haskell.packages.ghc865.extend (self: super: {
haxl = self.callHackage "haxl" "2.1.2.0" {};
});
myPackage = ghc865.callCabal2nix "automation-server" ./. { };
in myPackage
That unfortunately didn’t work right away, I was met with this error:
building '/nix/store/qbbd8m86s90vvfjpw91wki0ihnp9hcdf-all-cabal-hashes-component-haxl-2.1.2.0.drv'...
tar: */haxl/2.1.2.0/haxl.json: Not found in archive
tar: */haxl/2.1.2.0/haxl.cabal: Not found in archive
tar: Exiting with failure status due to previous errors
Full solution
It turns out that for reproducibility
not only Haskell packages are pinned in nixpkgs, but also there’s a repository
with their hashes called all-cabal-hashes
that is also pinned.
So I grabbed the latest git commit hash from all-cabal-hashes
,
8c7bdd9ad4bc3671b4214e32766873d443af2622
in my case, and modified the nix file as follows:
let
pkgs = import ./nixpkgs.nix;
ghc865 = (pkgs.haskell.packages.ghc865.override {
all-cabal-hashes = pkgs.fetchurl {
url = "https://github.com/commercialhaskell/all-cabal-hashes/archive/8c7bdd9ad4bc3671b4214e32766873d443af2622.tar.gz";
sha256 = "0q9qdpvn3c64rwnafcqkzzyi4z72mvvwmvn06d89fnzfpqjxvwx2";
};
}).extend (self: super: {
haxl = self.callHackage "haxl" "2.1.2.0" {};
}
);
myPackage = ghc865.callCabal2nix "automation-server" ./. { };
in myPackage
That solved my problem.
Wrapping up
You might wonder where I got the sha256
hash for all-cabal-hashes
or miso
.
I don’t know the proper way to do it, but you can get by putting in a wrong hash
and letting Nix tell you what the hash should have been.
Just don’t use a hash that was used before, because then Nix blindly uses an
existing resource with that hash and doesn’t even try to download yours and reverify.
While researching this I didn’t find anything resembling documentation for the Naskell infrastructure in Nix, only random forum posts and github issues.
In the future, I plan to look into the sources a bit more. For a while, I didn’t even know where to look for them, but now that I know where they are and I can parse the Nix language that will be my source of truth. Code never lies.
Additional resources
- Nix Pills is something I would recommend to read first, it’s not Haskell specific
- Nix and Haskell in production is something that people recommend, but I need to read it again because I read it before reading Nix Pills