commercialhaskell / stack

The Haskell Tool Stack
http://haskellstack.org
BSD 3-Clause "New" or "Revised" License
3.98k stars 845 forks source link

Optionally use haskell packages from nix. #2378

Open Pitometsu opened 8 years ago

Pitometsu commented 8 years ago

It's a feature request.
Is it possible to use stack as a thin frontend to nix, and install even haskell dependencies from here?

It may have format like

nix: true
  sys: true

or

nix: true
resolver: nix-ghc-7.10.3

Also, in this case nix should understand extra-dependencies from git.

sjakobi commented 8 years ago

I'm not with familiar with nix things. Pinging @YPares.

domenkozar commented 7 years ago

This would have to use packages from https://raw.githubusercontent.com/NixOS/nixpkgs/master/pkgs/development/haskell-modules/hackage-packages.nix and be able to map name+version into nix names.

ElvishJerricco commented 7 years ago

You can hack Stack to do this, but it's ugly, and it doesn't seem to work very well with Intero (though it seems to work fine for all things directly in Stack).

# stack.yaml
nix:
  enable: true
  shell-file: shell.nix
  packages: []
resolver: ghc-8.0.1
packages:
- '.'

First, we tell Stack to use a ghc-xxx resolver, because these come with no packages, and we can expect Nix to provide all the packages we need.


# default.nix
{ pkgs ? import <nixpkgs> {}
, haskellPackages ? pkgs.haskellPackages }:

let haskellOverrides = self: super:
    { # equivalent to extra-deps
      # somePackage = self.callHackage "somePackage" "0.version.bump.0" {};
    };
    hp = haskellPackages.override { overrides = haskellOverrides; };

    # cabal2nixResult should really be in nixpkgs somewhere, but I redefine it here
    # Edit: For clarity, I'll explain.
    # cabal2nixResult basically converts a `.cabal` file to a usable Nix expression at build time.
    cabal2nixResult = src: pkgs.runCommand "cabal2nixResult" {
      buildCommand = ''
        cabal2nix file://"${src}" >"$out"
      '';
      buildInputs = [ pkgs.cabal2nix ];

      # Support unicode characters in cabal files
      ${if !pkgs.stdenv.isDarwin then "LOCALE_ARCHIVE" else null} = "${pkgs.glibcLocales}/lib/locale/locale-archive";
      ${if !pkgs.stdenv.isDarwin then "LC_ALL" else null} = "en_US.UTF-8";
    } "";

in hp.callPackage (cabal2nixResult ./.) {}

Here, we use Nix to build based on the .cabal file, and setup a section for extra deps.


# shell.nix
{ pkgs ? import <nixpkgs> {}
, haskellPackages ? pkgs.haskellPackages }:
# ^ Note, we ignored the `ghc` argument stack provides

let overrideCabal = pkg: pkgs.haskell.lib.overrideCabal pkg ({ buildDepends ? [], ... }: {
      buildDepends = buildDepends ++ [ pkgs.cabal-install ];
    });

in (overrideCabal (import ./default.nix { inherit pkgs haskellPackages; })).env

Finally, this creates the shell that Stack needs to do its thing. Ordinarily, Stack gives shell.nix a GHC that it's obligated to put on the path for Stack to use. But we've opted to instead use our own GHC. The GHC we use will include all the dependencies for our package. Once Stack enters the shell, it will use that GHC to compile everything.


It mostly works with Intero. For the most part, it works just fine. But it seems like there are edge cases that cause Intero to hang or fail. Template Haskell seems to be a major culprit of this. Haven't tried to track this down at all. But I think that if Stack had more official support for this goal, we might see some better behavior.


My suggestion would be to have some field under nix in stack.yaml that basically says "Nix is going to do all the Haskell dependencies work, so there's no need for a resolver or extra-deps or anything." Then Stack just enters the nix shell and figures out how to use the environment that Nix setup. May require a little specialized setup on the Nix side, but that would be ok.

ElvishJerricco commented 7 years ago

For reference: Here's a repo where I lay out a skeleton for the above approach.

unhammer commented 1 year ago

@ElvishJerricco how do you use that skeleton, do you run stack inside a nix-shell? Do you still have to set up individual .nix files for each repo?

(I'd love it if I could stack build --nix and it would just use cachix for haskell libs instead of recompiling the entire kmettiverse, but without having to learn the dotnix language or clinix usage.)