14 Mar 2024

Hackage revisions in Nix

Today I got very confused when using callHackageDirect to add the openapi3 package gave me errors like this

> Using Parsec parser
> Configuring openapi3-3.2.3...
> CallStack (from HasCallStack):
>   withMetadata, called at libraries/Cabal/Cabal/src/Distribution/Simple/Ut...
> Error: Setup: Encountered missing or private dependencies:
> base >=4.11.1.0 && <4.18,
> base-compat-batteries >=0.11.1 && <0.13,
> template-haskell >=2.13.0.0 && <2.20

When looking at its entry on Hackage those weren't the version ranges for the dependencies. Also, running ghc-pkg list told me that I already had all required packages at versions matching what Hackage said. So, what's actually happening here?

It took me a while before remembering about revisions but once I did it was clear that callHackageDirect always fetches the initial revision of a package (i.e. it fetches the original tar-ball uploaded by the author). After realising this it makes perfect sense – it's the only revision that's guaranteed to be there and won't change. However, it would be very useful to be able to pick a revision that actually builds.

I'm not the first one to find this, of course. It's been noted and written about on the discource several years ago. What I didn't find though was a way to influence what revision that's picked. It took a bit of rummaging around in the nixpkgs code but finally I found two variables that's used in the Hackage derivation to control this

Setting them is done using the overrideCabal function. This is a piece of my setup for a modified set of Haskell packages:

hl = nixpkgs.haskell.lib.compose;

hsPkgs = nixpkgs.haskell.packages.ghc963.override {
  overrides = newpkgs: oldpkgs: {
    openapi3 = hl.overrideCabal (drv: {
      revision = "4";
      editedCabalFile =
        "sha256-a5C58iYrL7eAEHCzinICiJpbNTGwiOFFAYik28et7fI=";
    }) (oldpkgs.callHackageDirect {
      pkg = "openapi3";
      ver = "3.2.3";
      sha256 = "sha256-0F16o3oqOB5ri6KBdPFEFHB4dv1z+Pw6E5f1rwkqwi8=";
    } { });

It's not very ergonomic, and I think an extended version of callHackageDirect would make sense.

Tags: haskell nix
Comment here.