Files
haskell.nix/docs/user-guide.md
T
2020-05-04 14:06:00 +08:00

11 KiB

User Guide

Haskell.nix can be use for two different use cases:

Use 1 : to build a specific package from hackage or a stackage lts or nightly set.

To build a package, say lens, from a stackage snapshot, say lts-13.28, you could run

nix build '(with import <nixpkgs> (import ./. {}).nixpkgsArgs; haskell-nix.snapshots."lts-13.28").lens.components.library'

which would build the lens library component from the lens package as fixed by the lts-13.28 stackage snapshot.

To build any package from hackage, say lens, in version, say 4.17.1, you could run

nix build '(with import <nixpkgs> (import ./. {}).nixpkgsArgs; (haskell-nix.hackage-package { name = "lens"; version = "4.17.1"; })).components.library'

which would build the lens library component from the lens-4.17.1 package from hackage. The dependencies would be solved against the most recent hackage-index-state that comes via the hackage.nix pin with your haskell.nix checkout. A specific one can be specified as well:

nix build '(with import <nixpkgs> (import ./. {}).nixpkgsArgs; (haskell-nix.hackage-package { name = "lens"; version = "4.17.1"; index-state = "2019-07-14T00:00:00Z"; })).components.library'

which would use the hackage index as of 2019-07-14T00:00:00Z to produce a build plan for the lens-4.17.1 package.

Use 2 : to build a stack or cabal project.

So you want to use Haskell.nix with your stack or cabal project. The general approach will be to pick the right tool from nix-tools and produce a pkgs.nix expression. Getting a copy of the nix-tools (and potentially the Haskell.nix source), will then equip us to produce derivations that we can nix build.

Installing nix-tools

To build the latest nix-tools and store the result at ./nt, run:

nix build '(with import <nixpkgs> (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs; haskell-nix.nix-tools)' --out-link nt

If you would like to then install nix-tools into your profile, run:

nix-env -i ./nt

Getting the Haskell.nix source (optional)

The Haskell.nix and nix-tools source will be useful if you would like to contribute improvements, or read the source code to fully understand something that the documentation doesn't cover.

git clone https://github.com/input-output-hk/nix-tools
git clone https://github.com/input-output-hk/haskell.nix
cd haskell.nix
nix build -f . haskell-nix.nix-tools --out-link nt

Setting up the Cachix binary cache

CI pushes to cachix so you can benefit from the cache if you pin a combination of haskell.nix and nixpkgs built by CI. You'll need to configure the nix-tools cachix as a substituter for nix and add the public key found at the url to trusted-public-keys.

Using Haskell.nix with your project

The easiest way to get a hold of Haskell.nix is with fetchTarball.

import <nixpkgs> (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs

Using your cabal.project file

If your project has a cabal.project you can add a default.nix like this:

{ pkgs ? import <nixpkgs> (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs
, haskellCompiler ? "ghc865"
}:
  pkgs.haskell-nix.cabalProject {
    src = pkgs.haskell-nix.haskellLib.cleanGit { src = ./.; };
    ghc = pkgs.buildPackages.pkgs.haskell-nix.compiler.${haskellCompiler};
    # pkg-def-extras = [
    #   # Additional packages ontop of all those listed in `cabal.project`
    # ];
    # modules = [{
    #   # Specific package overrides would go here for example:
    #   packages.cbors.package.ghcOptions = "-Werror";
    #   packages.cbors.patches = [ ./one.patch ];
    #   packages.cbors.flags.optimize-gmp = false;
    #   # It may be better to set flags in `cabal.project` instead
    #   # (`plan-to-nix` will include them as defaults).
    # }];
  }

Note: The cleanGit call will exclude any files not in the index file in the local .git directory. This is extremely useful as it avoids unwanted builds (for instance when the contents of dist-newstyle are modified by cabal). It is important that src is the root directory of the repo (so cleanGit can find the .git directory). If the project is not at the root of the repo, then also pass subDir to cleanGit with the location of the project relative to src as a string (more information on using cleanGit). For example:

  src = pkgs.haskell-nix.haskellLib.cleanGit
    { src = ./.; subDir = "subdir/another-subdir"; };

If you want to use a custom filtering function, don't use the built-in nix filterSource function. Due to some technicalities about how filtering and the nix store work, it will cause all components in your project to be rebuilt any time any part of the project source changes. Use cleanSourceWith instead:

  src = pkgs.haskell-nix.haskellLib.cleanSourceWith
    { src = ./.; filter = myFilterFunction; };

Note that cleanSourceWith will also take the subDir argument.

You can build a component from your project with nix-build (in this case the hello executable in a helloworld package):

nix-build -A helloworld.components.exes.hello

For interactive development you can run:

nix-shell -A shellFor

The resulting shell will include all the dependencies of the local packages listed in your cabal.project file (but it will not build those local packages).

Inside the shell use cabal new-build and cabal new-repl to work on the cabal project as you normally would. They will use the packages provided by nix whenever possible.

It is a good idea to add write-ghc-environment-files: never to your cabal.project file to prevent unwanted .ghc.environment.* files (they will prevent subsequent nix-shell invocations from working properly).

Adding an index-state to your cabal.project can pin the plan used so that the same packages will be built each time (until you change the index-state or one of the constraints in your local .cabal files).

Using your stack.yaml file

If your project has a stack.yaml you can add a default.nix like this:

{ pkgs ? import <nixpkgs> (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs
}:
  pkgs.haskell-nix.stackProject {
    src = pkgs.haskell-nix.haskellLib.cleanGit { src = ./.; };
    # pkg-def-extras = [
    #   # Additional packages ontop of all those listed in `stack.yaml`
    # ];
    # modules = [
    #   # Specific package overrides would go here for example:
    #   packages.cbors.package.ghcOptions = "-Werror";
    #   packages.cbors.patches = [ ./one.patch ];
    #   packages.cbors.flags.optimize-gmp = false;
    #   # It may be better to set flags in `stack.yaml` instead
    #   # (`stack-to-nix` will include them as defaults).
    # ];
  }

You can build a component from your project with nix-build (in this case the hello executable in a helloworld package):

nix-build -A helloworld.components.exes.hello

Pinning the Haskell.nix version

For simplicity's sake we will use fetchTarball for the examples in this documentation. This will always get the latest version, and is similar to an auto-updating Nix channel.

However, in your own project, you may wish to pin Haskell.nix (as you would pin Nixpkgs). This will make your builds reproducable, more predictable, and faster (because the fixed version is cached).

One way of doing this is to use nix-prefetch-git to get a JSON file with a revision and SHA-256 hash of Haskell.nix.

$ nix-prefetch-git --quiet https://github.com/input-output-hk/haskell.nix | tee haskell-nix-src.json
{
  "url": "https://github.com/input-output-hk/haskell.nix",
  "rev": "f1a94a4c82a2ab999a67c3b84269da78d89f0075",
  "date": "2019-06-05T01:06:12+00:00",
  "sha256": "0ggxsppjlb6q6a83y12cwgrdnqnw1s128rpibgzs5p1966bdfqla",
  "fetchSubmodules": false
}

(The tee command is just to show you the result.) Use the following expression to import that version:

{ nixpkgs ? <nixpkgs> }:

let
  spec = builtins.fromJSON (builtins.readFile ./haskell-nix-src.json);
  haskell-nix-src = (import nixpkgs {}).fetchgit {
    name = "haskell-lib";
    inherit (spec) url rev sha256 fetchSubmodules;
  };
in
  import nixpkgs (import haskell-nix-src)

There are other possible schemes for pinning. See haskell.nix/lib/fetch-external.nix, the niv tool, or the Nix Flakes proposal.

Overriding the Hackage version

Sometimes you might want to use a chosen version of Haskell.nix with a recent update of Hackage or Stackage. This can be done with JSON pins:

nix-prefetch-git https://github.com/input-output-hk/hackage.nix | tee hackage-src.json
nix-prefetch-git https://github.com/input-output-hk/stackage.nix | tee stackage-src.json

The resulting JSON files will correspond to the latest revision of hackage.nix and stackage.nix. See Architecture for more information about how these Git repositories correspond to the actual Hackage and Stackage.

{ pkgs ? import <nixpkgs> (haskellNixArgs // { overlays = haskellNixArgs.overlays ++ [
  (self: super: {
    haskell-nix = super.haskell-nix // {
      hackageSourceJSON  = ./hackage-src.json;
      stackageSourceJSON = ./stackage-src.json;
    };
  })]; })
, haskellNixArgs ? (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs
}:
  pkgs

Using nix repl

It's sometimes useful to load Haskell.nix in the REPL to explore attrsets and try examples.

# example.nix
{ nixpkgs ? <nixpkgs> }:
rec {
  haskell = import nixpkgs (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs;
  pkgNames = haskell.pkgs.lib.attrNames haskell.haskell-nix.snapshots."lts-13.18";
}

Load the example file:

$ nix repl '<nixpkgs>' example.nix
Welcome to Nix version 2.3pre6631_e58a7144. Type :? for help.

Loading '<nixpkgs>'...
Added 10403 variables.

Loading 'example2.nix'...
Added 2 variables.

nix-repl> lib.take 5 pkgNames
[ "ALUT" "ANum" "Allure" "Boolean" "BoundedChan" ]

nix-repl> 

Now that you have nix-tools and are able to import Haskell.nix, you can continue to the next chapter.