25

How do I setup a simple Haskell development environment on NixOS using the new haskell-ng infrastructure?

rzetterberg
  • 9,544
  • 3
  • 40
  • 54

1 Answers1

32

Here's one way of doing it on a per project basis. I'm developing a Yesod webbapp so I'm using my current setup as an example.

In my project folder I have two files: default.nix (specifies how to build my project) and shell.nix (specifies how to setup build inputs).

This way I can just run nix-shell and it drops me into an environment which has ghc installed with the provided packages and paths setup.

The setup

default.nix

{ stdenv, haskellPackages }:

let
  env = haskellPackages.ghcWithPackages (p: with p; [
    aeson
    bytestring
    cabal-install
    classy-prelude
    classy-prelude-conduit
    classy-prelude-yesod
    conduit
    containers
    data-default
    directory
    fast-logger
    file-embed
    hjsmin
    http-conduit
    monad-control
    monad-logger
    persistent
    persistent-postgresql
    persistent-template
    safe
    shakespeare
    template-haskell
    text
    time
    unordered-containers
    vector
    wai-extra
    wai-logger
    warp
    yaml
    yesod
    yesod-auth
    yesod-bin
    yesod-core
    yesod-form
    yesod-static
  ]);
in
  stdenv.mkDerivation {
    name        = "project-name";
    buildInputs = [env];
    shellHook   = ''
      export NIX_GHC="${env}/bin/ghc"
      export NIX_GHCPKG="${env}/bin/ghc-pkg"
      export NIX_GHC_DOCDIR="${env}/share/doc/ghc/html"
      export NIX_GHC_LIBDIR=$( $NIX_GHC --print-libdir )
    '';
  }

shell.nix

{ pkgs ? (import <nixpkgs> {}) }:

(import ./default.nix) {
    stdenv            = pkgs.stdenv;
    haskellPackages = pkgs.haskellPackages;
  }

More information

For more information you can read:

A Journey into the Haskell NG infrastructure: Part I

A Journey into the Haskell NG infrastructure: Part II

Those links explains how haskell-ng works and what will happen in the future.

If you want to dig into the source of the haskell-ng infrastructure I suggest:

The file where haskellPackages is defined

The haskell packages module

The haskell-module folder

Update: Building dependencies with profiling enabled

All packages in haskell-ng are defined in hackage-packages.nix. They are built using cabal.mkDerivation.

If you want to override how a package is built you need to override how mkDerivation is called on that package. In the Haskell Wiki you have information on how to do that.. Basically you define a new haskellPackages with the overrides you want for each package:

let haskellPackages' = haskellPackages.override {
       overrides = self: super: {
         fullstop = super.fullstop.override {
           mkDerivation = (attrs: self.mkDerivation (attrs // { doCheck = false; }));
         };
       };
     };

This can be quite tedious to do, therefor there are helper functions in the file lib.nix in haskell-modules that you can use.

The file haskell-ng imports lib.nix and exposes it. And haskell-ng is exposed as a top-level package so you can access the lib functions easily.

Say we have a package that want to enable profiling on the text library we can do something like this:

{ pkgs ? (import <nixpkgs> {}).pkgs }:

let
  lib = pkgs.haskell-ng.lib;
  env = pkgs.haskellPackages.ghcWithPackages (p: with p; [
    (lib.enableLibraryProfiling text)
  ]);
in
  pkgs.stdenv.mkDerivation {
    name        = "hello-world-wide-web";
    buildInputs = [env];
    shellHook   = ''
      export NIX_GHC="${env}/bin/ghc"
      export NIX_GHCPKG="${env}/bin/ghc-pkg"
      export NIX_GHC_DOCDIR="${env}/share/doc/ghc/html"
      export NIX_GHC_LIBDIR=$( $NIX_GHC --print-libdir )
    '';
  }

I found this information this weekend by going through the nix source for haskell-ng and testing it. I'm still quite new to NixOS and nix so there could be a better way of doing this.

Community
  • 1
  • 1
rzetterberg
  • 9,544
  • 3
  • 40
  • 54
  • Thanks for posting this! One thing I'd find helpful is how to manage profiling, which requires all dependencies to be built with profiling support as well. I found a way, but I don't fully understand it and it currently applies globally, so it would be good to see how to have only a single package's environment demand the profiling versions of its dependencies. – Levi Pearson Mar 13 '15 at 23:29
  • @LeviPearson No worries! Just thought it would be nice to put this information in a place that is easy to access. See my edit regarding overriding how packages are built and specifically adding profiling! – rzetterberg Mar 16 '15 at 08:47
  • I figured out how to call lib.enableLibraryProfiling, but it didn't seem to affect the recursive dependencies of the derivation I called it on, so my build just failed one step further on as its dependencies w/profiling enabled weren't found. Maybe I just missed a 'clean' step or something, though. – Levi Pearson Mar 16 '15 at 17:07
  • 1
    I'm new to NixOS, and with some help I finally got one of my haskell projects building under Haskell NG. But my default.nix looks like your shell.nix, and my shell.nix looks like your default.nix. Do you have the filenames swapped in your answer, or do the filenames not matter? – mhwombat May 17 '15 at 17:04
  • @LeviPearson Ah, I see! I think what you need to do is to recursively overwrite each package and their dependency via `buildDepends`. – rzetterberg May 21 '15 at 07:39
  • @mhwombat When calling `nix-shell` without arguments it first looks for `shell.nix`, if that file doesn't exist it looks for `default.nix`. That's why I added all imports in `shell.nix`, so I can just call `nix-shell` without args. – rzetterberg May 21 '15 at 07:41
  • Where would I add a (for example) postgres server here? So, if my app needs a running postgresql server - how to integrate this in the upper definitions? – musicmatze May 21 '15 at 18:50
  • 1
    @musicmatze If your package needs a running postgresql server, your `default.nix` should only define what libraries it needs to use postgresql. It would not define what services the system needs to run. – rzetterberg May 24 '15 at 11:19
  • 1
    @musicmatze Take nginx as an example, the [`default.nix`-file](https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/http/nginx/default.nix) only defines it's dependencies for building, installing and running. How nginx is deployed (whether it uses systemd to start it, if it needs a mutable directory to store data in, etc) is defined in a NixOS [`service.nix`-file](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-servers/nginx/default.nix). – rzetterberg May 24 '15 at 11:21