nmattia / snack

Nix-based incremental build tool for Haskell projects
334 stars 25 forks source link

Use of the documented `snack.nix` fails: error: value is a function while a set was expected, at (string):11:17 #179

Closed sir4ur0n closed 4 years ago

sir4ur0n commented 4 years ago

Hi,

I just discovered snack yesterday, I am extremely interested in using it (incremental build + Nix :heart:). While toying around with it, it fails when I start using a snack.nix as documented.

$ stack new foobar
$ cd foobar
$ niv init
$ niv add nmattia/snack
# shell.nix
with { pkgs = import ./nix {}; };
pkgs.mkShell {
  buildInputs = [ pkgs.niv pkgs.nix pkgs.snack ];
}

# nix/default.nix
{ sources ? import ./sources.nix }:
with
  { overlay = _: _:
      {
        niv = (import sources.niv {}).niv; # this is how you import niv into the overlay as well
        snack = (import sources.snack).snack-exe;
      };
  };
import sources.nixpkgs
  { overlays = [ overlay ] ; config = {}; }

Everything seems to run fine (besides the warning, but I assume it's unrelated?):

$ snack run
warning: unknown setting 'sandbox-fallback'
someFunc

Now add the snack.nix file:

# snack.nix
rec {
  ghc-version = "ghc802";
  ghcWithPackages = pkgs.haskellPackages.ghcWithPackages;
  pkgs = import ./nix;
}

And suddenly everything goes south:

$ snack run
warning: unknown setting 'sandbox-fallback'
error: value is a function while a set was expected, at (string):11:17

Is this a documentation issue, or is there a bug?

Thank you!

sir4ur0n commented 4 years ago

Ok I have investigated (with my rather poor knowledge of Nix) and looked at the code, I think I figured out how to make it work.

An actual syntax that works is:

rec {
  ghc-version = "ghc882";
  ghcWithPackages = pkgs.haskellPackages.ghcWithPackages;
  pkgs = (import ./nix) {}; # notice the brackets here to actually call the function
}

After further investigation:

Is this the intended behavior? Would you mind clarifying the behavior regarding the pkgs field please?

Thank you! 😄

nmattia commented 4 years ago

You're right, the pkgs field should evaluate to a package set. It really boils down to what import ./nix evaluates to. For instance, this would work:

# ./nix/default.nix
import <nixpkgs> {}

# snack.nix
{ pkgs = import ./nix; }

but the following wouldn't work:

# ./nix/default.nix
{}: import <nixpkgs> {}

# snack.nix
{ pkgs = import ./nix; }

I realize the latter (i.e. ./nix/default.nix taking an attribute set) is much more common. I'll update the documentation to reflect that.

nmattia commented 4 years ago

There you go! Let me know if that works for you.

Re. clarifying the behavior of pkgs:

rec {
...

  # Finally you can provide your own set of Nix packages, which should evaluate
  # to an attribute set:
  pkgs = import ./nix {};
}

The important part:

... which should evaluate to an attribute set.

I realize this may not be straightforward when you start with Nix!

sir4ur0n commented 4 years ago

I realize the latter (i.e. ./nix/default.nix taking an attribute set) is much more common. I'll update the documentation to reflect that.

In particular, this is the default when using niv which is the first configuration mode presented by the documentation :smile: Indeed thank you, the documentation is better now (it works OOTB).

However the documentation is still unclear/confusing in my opinion:

Maybe provide several examples? Dumb proposal (if it looks good to you I don't mind opening the PR):


Using other versions of GHC and nixpkgs

The snack executable comes with a bundled version of nixpkgs and uses the GHC executable provided by haskell.packages.ghc822.ghcWithPackages. You may override those default by providing a snack.nix:

# By default ./snack.nix is used if detected
$ snack build
# But you can also explicitly pass the snack file
$ snack --snack-nix my-snack.nix build

Customize the GHC version

# snack.nix
rec {
  # If you only wish to change the version of GHC being used, set
  # `ghc-version`. The following versions are currently available:
  #  * ghc822
  #  * ghc822Binary
  #  * ghc844
  #  * ghc863Binary
  #  * ghc864
  #  * ghc865
  #  * ghcHEAD
  # NOTE: not all versions have been tested with snack.
  ghc-version = "ghc802";

  # You can reuse the project packages, or customize here too
  pkgs = import ./nix {};
}

Customize the ghcWithPackages

# snack.nix
rec {
  # Alternatively you can provide you own `ghcWithPackages`, which should have
  # the same structure as that provided by
  # `pkgs.haskell.packages.<version>.ghcWithPackages:
  ghcWithPackages = pkgs.haskell.packages.ghc822.ghcWithPackages;

  # You can reuse the project packages, or customize here too
  pkgs = import ./nix {};
}

Note, I also tried using the GHC version integer-simple as well as ghcjsXXX but compilation fails, so I removed those.

nmattia commented 4 years ago

Hey Julien! Sorry for the late reply.

Doc says "Finally you can provide your own set of Nix packages" which lets the reader believe it's optional to pass the nipkgs field while it is mandatory

The nixpkgs field should definitely be optional. If it's not, it's a bug!

if it looks good to you I don't mind opening the PR

It looks great, thanks!