Nix is a purely functional package manager. Stack can be configured to integrate with Nix. Integration provides these benefits:
more reproducible builds. This is because fixed versions of any system libraries and commands required to build the project are automatically built using Nix and managed locally for each project. These system packages never conflict with any existing versions of these libraries on your system. That they are managed locally to the project means that you do not need to alter your system in any way to build any odd project pulled from the Internet; and
implicit sharing of system packages between projects. This means you do not have more copies on-disk than you need.
The Nix package manager is a pre-requisite for integration. On Linux (including Windows Subsystem for Linux) and macOS, it can be downloaded and installed from the Nix download page.
When integrated with Nix, Stack handles Haskell dependencies as it usually does and the Nix package manager handles the non-Haskell dependencies needed by the Haskell packages.
Stack downloads Haskell packages from Stackage and builds them locally. Stack uses Nix to download Nix packages. These provide the GHC compiler and external C libraries that you would normally install manually.
Nix's nix-shell
starts an interactive shell based on a Nix expression. Stack can automatically create a Nix build environment in the background using nix-shell
. There are two alternative options to create such a build environment:
shell.nix
file that gives you more control over the libraries and tools available inside the shell.A shell.nix
file requires writing code in Nix's custom language. Use this option only if you know Nix and have special requirements, such as using custom Nix packages that override the standard ones or using system libraries with special requirements.
Once Nix is installed, the Nix commands (nix-shell
etc) should be available. If they are not, it could be because the file $HOME/.nix-profile/etc/profile.d/nix.sh
is not sourced by your shell.
You should either:
source ~/.nix-profile/etc/profile.d/nix.sh
each time you open a terminal and need Nix; orsource ~/.nix-profile/etc/profile.d/nix.sh
to your ~/.bashrc
or ~/.bash_profile
file.A Nix path can be specified between angle brackets, e.g. <nixpkgs>
, and the directories listed in the NIX_PATH
environment variable will be searched for the given file or directory name. Stack makes use of path <nixpkgs>
. From Nix 2.4, NIX_PATH
is not set by nix.sh
. If NIX_PATH
is not set, Nix will fall back to (first) $HOME/.nix-defexpr/channels
in impure and unrestricted evaluation mode. However, Stack may use a pure Nix mode (see further below). That directory can be appended to NIX_PATH
with export NIX_PATH=${NIX_PATH:+$NIX_PATH:}$HOME/.nix-defexpr/channels
. For information about how Stack itself can configure NIX_PATH
, see further below.
On NixOS, Nix integration is enabled by default; on other operating systems it is disabled. To enable Nix integration, add the following section to your Stack configuration file (stack.yaml
or config.yaml
):
nix:
enable: true # false by default, except on NixOS
The equivalent command line flag (which will prevail) is --[no-]nix
. Passing any --nix-*
option on the command line will imply the --nix
option.
If Nix integration is not enabled, Stack will notify the user if a nix
executable is on the PATH. If that notification is unwanted, it can be muted by setting Stack's configuration option notify-if-nix-on-path
to false
.
With Nix integration enabled, stack build
and stack exec
will automatically launch themselves in a local build environment (using nix-shell
behind the scenes). It is not necessary to run stack setup
, unless you want to cache a GHC installation before running a build.
Known limitation on macOS: currently, stack --nix ghci
fails on macOS, due to a bug in GHCi when working with external shared libraries.
With Nix integration enabled in Stack's configuration file, every developer of your project needs to have Nix installed, but the developer also gets all external libraries automatically.
Julien Debon of Tweag has published a blog post on Smooth, non-invasive Haskell Stack and Nix shell integration (2 June 2022). The post explains how to set things up so that both Nix and non-Nix developers can work together on the same project. The tweag/haskell-stack-nix-example
GitHub repository provides an example of working Stack and Nix shell integration to accompany the post.
Nix 2.4 (released 1 November 2021) introduced a new and experimental format to package Nix-based projects, known as 'flakes'.
The example below adapts and extends the example accompanying the blog post above to use Nix flakes. The flake.nix
file is:
{
description = "my project description";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
hPkgs =
pkgs.haskell.packages."ghc8107"; # need to match Stackage LTS version
# from stack.yaml snapshot
myDevTools = [
hPkgs.ghc # GHC compiler in the desired version (will be available on PATH)
hPkgs.ghcid # Continuous terminal Haskell compile checker
hPkgs.ormolu # Haskell formatter
hPkgs.hlint # Haskell codestyle checker
hPkgs.hoogle # Lookup Haskell documentation
hPkgs.haskell-language-server # LSP server for editor
hPkgs.implicit-hie # auto generate LSP hie.yaml file from cabal
hPkgs.retrie # Haskell refactoring tool
# hPkgs.cabal-install
stack-wrapped
pkgs.zlib # External C library needed by some Haskell packages
];
# Wrap Stack to work with our Nix integration. We do not want to modify
# stack.yaml so non-Nix users do not notice anything.
# - no-nix: We do not want Stack's way of integrating Nix.
# --system-ghc # Use the existing GHC on PATH (will come from this Nix file)
# --no-install-ghc # Do not try to install GHC if no matching GHC found on PATH
stack-wrapped = pkgs.symlinkJoin {
name = "stack"; # will be available as the usual `stack` in terminal
paths = [ pkgs.stack ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/stack \
--add-flags "\
--no-nix \
--system-ghc \
--no-install-ghc \
"
'';
};
in {
devShells.default = pkgs.mkShell {
buildInputs = myDevTools;
# Make external Nix c libraries like zlib known to GHC, like
# pkgs.haskell.lib.buildStackProject does
# https://github.com/NixOS/nixpkgs/blob/d64780ea0e22b5f61cd6012a456869c702a72f20/pkgs/development/haskell-modules/generic-stack-builder.nix#L38
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath myDevTools;
};
});
}
Check-in this flake.nix
to your project's repository. Run the nix develop
command (it searches for flake.nix
by default) and you WIll find a new flake.lock
file. That file that pins the precise nixpkgs package set. Check-in that flake.lock
file as well, and every Nix developer of your project will use precisely the same package set.
Nix integration will instruct Stack to build inside a local build environment. That environment will also download and use a GHC Nix package matching the required version of the configured Stack snapshot.
Enabling Nix integration means that packages will always be built using the local GHC from Nix inside your shell, rather than your globally installed system GHC (if any).
Stack can use only GHC versions that are in the Nix package repository. The Nixpkgs master branch usually picks up new versions quickly, but it takes two or three days before those updates arrive in the unstable
channel. Release channels, like nixos-22.05
, receive those updates only occasionally -- say, every two or three months --, so you should not expect them to have the latest compiler available. Fresh NixOS installs use a release version by default.
To identify whether a given compiler is available, you can use the following Nix command:
nix-env -f "<nixpkgs>" -qaP -A haskell.compiler.ghc924
haskell.compiler.ghc924 ghc-9.2.4
If Nix does not know that version of GHC, you will see the following error message:
nix-env -f "<nixpkgs>" -qaP -A haskell.compiler.ghc999
error: attribute ‘ghc999’ in selection path ‘haskell.compiler.ghc999’ not found
You can list all known Haskell compilers in Nix with the following:
nix-instantiate --eval -E "with import <nixpkgs> {}; lib.attrNames haskell.compiler"
Alternatively, use nix repl
, a convenient tool to explore nixpkgs:
In the REPL, load nixpkgs and get the same information through autocomplete:
nix-repl> :l <nixpkgs>
nix-repl> haskell.compiler.ghc<Tab>
You can type and evaluate any Nix expression in the Nix REPL, such as the one we gave to nix-instantiate
earlier.
To let Nix manage external C libraries, add (for example) the following section to your Stack configuration file:
nix:
enable: true
packages: [zlib, glpk, pcre]
The equivalent command line option is --nix-packages "zlib glpk pcre"
.
The packages
key and the shell-file
key (see further below) are alternatives. Specifying both results in an error.
The example above will instruct Stack to build inside a local build environment that will have the Nix packages zlib, glpk and pcre installed, which provide the C libraries of the same names.
Note: currently, Stack only discovers dynamic and static libraries in the lib/
folder of any Nix package, and likewise header files in the include/
folder. If you are dealing with a package that does not follow this standard layout, you will have to deal with that using a custom shell.nix
file (see further below).
shell.nix
file¶
In Nix, a 'derivation' is a description of a build action and its result is a Nix store object. Nix's custom language can provide a fully customized derivation as an environment to use. To specify such a shell.nix
file, add the following section to your Stack configuration file:
nix:
enable: true
shell-file: shell.nix
The equivalent command line option (which will prevail) is --nix-shell-file shell.nix
.
The packages
and shell-file
keys are alternatives. Specifying both results in an error.
Defining a shell.nix
file allow you to override some Nix derivations, for instance to change some build options of the libraries you use, or to set additional environment variables. For further information, see the Nix manual.
The shell.nix
file that is the equivalent of the packages: [zlib, glpk, pcre]
example above is:
{ghc}:
with (import <nixpkgs> {});
haskell.lib.buildStackProject {
inherit ghc;
name = "myEnv";
buildInputs = [ zlib glpk pcre ];
}
The buildStackProject
utility function is documented in the Nixpkgs manual.
Stack expects the shell.nix
file to define a function of with one argument called ghc
(arguments are not positional), which you should give to function buildStackProject
. This argument is a GHC Nix package in the version as defined in the snapshot you set in Stack's project-level configuration file (stack.yaml
, by default).
By default, Stack will run the build in a pure Nix build environment (or shell). Building in a pure Nix shell means:
with limited exceptions, no environment variable will be forwarded from your user session to the Nix shell (variables like HTTP_PROXY
, PATH
, STACK_XDG
and STACK_ROOT
notably will not be available); and
the build should fail if you have not specified all the dependencies in the packages:
section of the Stack configuration file, even if these dependencies are installed elsewhere on your system. This behaviour enforces a complete description of the build environment to facilitate reproducibility.
To override this behaviour, add the following section to your Stack YAML configuration file:
nix:
enable: true
pure: false
The equivalent command line flag (which will prevail) is --[no-]-nix-pure
.
To run the build in a pure Nix shell but preserve specific environment variables use Nix's nix-shell
command's --keep
option. For example, to preserve the STACK_XDG
environment variable, add the following to your Stack YAML configuration file:
nix:
nix-shell-options:
- --keep
- STACK_XDG
The equivalent command line option is:
--nix-shell-options "--keep STACK_XDG"
Note: On macOS, shells are non-pure by default currently. This is due soon to be resolved locale issues. So on macOS you will need to be a bit more careful to check that you really have listed all dependencies.
Nix package sources¶Nix organizes its packages in snapshots of packages (each snapshot being a "package set") similar to how Stackage organizes Haskell packages. By default, nix-shell
will look for the "nixpkgs" package set located by your NIX_PATH
environment variable. This package set can be different depending on when you installed Nix and which nixpkgs channel you are using (similar to the LTS channel for stable packages and the nightly channel for bleeding edge packages in Stackage). This is bad for reproducibility so that nixpkgs should be pinned, i.e., set to the same package set for every developer of your project.
To set or override the Nix package set, add the following section to your Stack configuration file:
nix:
path: [nixpkgs=<path_to_my_own_nixpkgs_clone>]
The equivalent command line option is --nix-path <path_to_my_own_nixpkgs_clone>
.
By this means, you can ask Nix to use your own local checkout of the nixpkgs repository. You could in this way use a bleeding edge nixpkgs, cloned from the NixOS/nixpkgs
repository master
branch, or edit the Nix descriptions of some packages.
The Tweag example repository shows how you can pin a package set.
Non-project specific configuration¶Below is a summary of the non-project specific configuration options and their default values. The options can be set in Stack's project-level configuration file (stack.yaml
, by default) or its global configuration file (config.yaml
).
nix:
# false by default, except on NixOS. Is Nix integration enabled?
enable: true
# true by default. Should Nix run in a pure shell?
pure: true
# Empty by default. The list of packages you want to be available in the
# nix-shell at build time (with `stack build`) and run time (with
# `stack exec`).
packages: []
# Unset by default. You cannot set this option if `packages:`
# is already present and not empty.
shell-file: shell.nix
# A list of strings, empty by default. Additional options that will be passed
# verbatim to the `nix-shell` command.
nix-shell-options: []
# A list of strings, empty by default, such as
# `[nixpkgs=/my/local/nixpkgs/clone]` that will be used to override
# NIX_PATH.
path: []
# false by default. Whether to add your Nix dependencies as Nix garbage
# collection roots. This way, calling nix-collect-garbage will not remove
# those packages from the Nix store, saving you some time when running
# stack build again with Nix support activated.
#
# This creates a `nix-gc-symlinks` directory in the project `.stack-work`.
# To revert that, just delete this `nix-gc-symlinks` directory.
add-gc-roots: false
stack --nix-help
will list the equivalent command line flags and options.
NixOS is a Linux distribution based on Nix, that is composed using modules and packages defined in the Nixpkgs project.
When using Stack on NixOS, you must use Stack's Nix integration to install GHC. That is because external C libraries in NixOS are not installed in the usual distribution directories. GHC installed through Stack (without Nix) cannot find those libraries and, therefore, cannot build most projects. However, GHC provided through Nix can be modified to find the external C libraries provided through Nix.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4