NIX: Small/Random, but Useful patterns

Nesting

Say you have a few packages that you would like to build but you want an easy way of doing so. Here’s a handy little pattern that could help out.

# suppose this is your default.nix
# although I recommend separating the shell.
{
    pkgs ? import <nixpkgs>{}
}:rec{
  output = with pkgs;[hello cowsay];
  shell = pkgs.mkShell {
      name = "Shell";
      buildInputs = output;
      };
}

So now to build everything in output all you need to do is run:

nix-build default.nix -A output

which will subsequently generate all the derivations inside the list. And if you just wanted to run a shell with them you could do:

nix-shell default.nix -A shell

and you would be in a shell with all your needed binaries included.

Nesting

So this is probably where I would say this pattern comes in handy. Let’s say the above default.nix is actually inside a nested folder which we will call ./nest.

And we actually want to call the derivation inside our root folder ./.. The trick is to call them in the same way, from the root default.nix.

{
    pkg ? import ./nest {}
}
:let
  out = pkg.output;
in
  out

Zlib package Haskell

Working on my dissertation I wanted to have a nix-shell where I could test in a fast manner my web-interface for the FMCt. I was initially doing this:

{ release  ? import ./release.nix
, sources  ? import ./nix/sources.nix
, pkgs     ? import sources.nixpkgs{}
, FMCt-web ? import ./default.nix
}:
pkgs.mkShell {
  nativeBuildInputs = with pkgs;[
    cabal-install
    ghcid # ghcide
    gnumake               # makefile
    haskellPackages.aeson
    haskellPackages.blaze-markup
    haskellPackages.hlint # linting
    haskellPackages.scotty
    haskellPackages.zlib
    haskellPackages.ghc
    rlwrap                # to be able to easily re-enter last input when in the repl
  ];
}

The issue I was having was getting the following error:

<command line>: can't load .so/.DLL for: libz.so (libz.so: cannot open shared object file: No such file or directory)

Which was very annoying, as I thouth I was provisioning both the environment and the package for haskellPackages.zlib. Where I was wrong was in the following bit: making the haskellPackages available to the environment is only useful if we need the executables. If we need the libraries the correct way to do it is the following:

{ release  ? import ./release.nix
, sources  ? import ./nix/sources.nix
, pkgs     ? import sources.nixpkgs{}
, FMCt-web ? import ./default.nix
}:
let 
  # add ghc packages that should be available to the ghc here
  ghc = (pkgs.haskellPackages.ghcWithPackages (
    hpkgs: with hpkgs;[
      zlib
      scotty
      aeson
      blaze-markup
      aeson
    ]
  ));
in
pkgs.mkShell {
  nativeBuildInputs = with pkgs;[
    cabal-install
    ghc
    haskellPackages.hlint # linting
    # ... 
  ];
}

This was very helpful for me to see, that actually it is not the environment that needs to be provisioned, but rather ghc itself. Hence why ghcWithPackages solved the issue.