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.