Haskell project structure in NixOS
Contents
This blog post was inspired by the work of Gabriel Gonzalez at haskell-nix github repository which has a lot of practical information on how to structure a Haskell project in NixOS. Here I will show my own view on a typical Haskell project structure in NixOS taking into account Gabriel’s work.
I would encourage everyone interested to fork haskell-nix and go through Gabriel’s tutorial. That helped me understand some of the issues I was not aware before.
Requirements
default.nix
must not change other than with$ cabal2nix . > default.nix
shell.nix
must be able to bring developer environment withHoogle
server that will serve documentation for all dependencies used in Cabal filerelease.nix
must declare at least two packages - one with dynamically linked dependencies, and another - with statically linked dependenciesrelease.nix
must produce output expected by Hydra build systemshell.nix
as well asrelease.nix
must use pinnednixpkgs
to ensure reproducibility of buildsshell.nix
as well asrelease.nix
must be open for choosing version ofghc
compiler
Generate default.nix
This is mostly straightforward:
|
|
Pinning nixpkgs
To ensure reproducible builds it is important to fixate on a specific version of
nixpkgs
. Let’s now generate nixpkgs.json
that will be used later in
shell.nix
and release.nix
by issuing the following command:
|
|
This should produce the following output:
|
|
Building shell.nix
It is possible to generate
shell.nix
with$ cabal2nix --shell . > shell.nix
, but it is not recommended as we are going to make sure we meet the requirement of running a Hoogle server locally with all the used dependencies.
|
|
Then in order to start your local Hoogle server:
|
|
Then navigate to 127.0.0.1:8080 in order to test if documentation server is running.
Building release.nix
release.nix
also needs to use pinned version of nixpkgs
. It also declares
two haskell packages - one package-name
and another package-name-static
-
dynamically and statically linked versions respectively. It also produces a set
of derivations as a result which is what Hydra
expects.
|
|
Make sure to rename
package-name
with your project name.
Then building the project locally ends up being as simple as:
|
|
And installing dynamically linked package with $ nix-env -i ./result
. Or
statically linked package with $ nix-env -i ./result-2
.
Make sure you understand that package-name-static
package is statically linked
against current version of glibc
used in the current system. This has some
consequences that are not necessarily obvious. For example it will not
necessarily work when packaged as alpine
docker image. Reason being that
alpine
images are built using musl
C-library instead of glibc
, and since
your package-name-static
has glibc
statically linked it won’t be possible to
always properly run it. The worst part about it is that it will only fail in
run-time, e.g. when resolving DNS for establishing a TCP connection. I am sure
there will be some other issues too.
Author Roman Kuznetsov
LastMod 2017-10-15
License Roman Kuznetsov