One thing that has long baffled me is why derivations that apparently do the same thing look so different.
For example, I’m curious about the difference between these two derivations. The first derivation is the one that I use to build a simple Haskell program.
let
pkgs = import <nixpkgs> { };
in
pkgs.haskellPackages.developPackage {
root = ./.;
}
The second derivation is the one I use to install the same program in my NixOS configuration. It’s in an overlay.*
I assume that pkgs.haskellPackages.developPackage returns a derivation. Is there documentation for pkgs.haskellPackages.developPackage? I searched both the nix and nixpkgs manuals for it, but no luck. I even searched both the nix and nixpkgs repos for the string “developPackage” to see if I could find the source code.
Including a executableHaskellDepends attribute in mkDerivation seems to be the magic word to tell nix to build a standard Haskell executable or library. Is executableHaskellDepends processed by mkDerivation, or plain old derivation? The documentation for mkDerivation doesn’t mention it.
I understand that one reason the two derivations are different is that the first one gets the source from the local directory, and the second fetches it from github. And of course the second needs some extra packaging information such as version number and license. But I am suprised that the Haskell part of the two derivations is so different. Is pkgs.haskellPackages.developPackage basically a convenient wrapper around mkDerivation?
There you can also find the docs I’m not sure they’re actually exposed to the outside world, a lot of these deeper ecosystem functions are only documented in internal comments.
Just like stdenv, haskellPackages provides a mkDerivation function. haskellPackages also provides a callPackage function that works similarly to the top-level callPackage. If you dig through the above files, you’ll find that they are defined here:
Similarly to stdenv.mkDerivation, haskellPackages.mkDerivation takes an attribute set for building a Haskell package, and returns a derivation. It takes arguments like isLibrary, executableHaskellDepends, pname, etc. Internally, it uses the builder pkgs/development/haskell-modules/generic-builder.nix. (This file is quite complex, but if you read through it, you can figure out exactly what happens with the values passed in as executableHaskellDepends. This generic Haskell builder is effectively a wrapper around stdenv.mkDerivation for specifically building Haskell packages.)
This is similar to the top-level callPackage, but it knows about all the Haskell packages in the Nixpkgs Haskell package set. For example, you showed a Haskell derivation that started out like this:
If you pass this function (or file) to haskellPackages.callPackage, it knows how to automatically find the arguments for Haskell packages like aeson, lens, etc. It also knows to fall back to looking for packages in the top-level Nixpkgs for things that aren’t Haskell libraries. So things like fetchgit, lib, etc will be pulled from the top-level Nixpkgs.
It is admittedly somewhat confusing that haskellPackages.mkDerivation and stdenv.mkDerivation are named the same, but (completely) different functions (as well as callPackage).
The big problem with the cabal2nix CLI tool is that you have to manually run it, redirect output to a file on disk, then calls haskellPackages.callPackage on that output. haskellPackages.callCabal2nix wraps up this whole process for you. For example:
$ nix repl '<nixpkgs>'
nix-repl> haskellPackages.callCabal2nix "jot" (builtins.fetchGit "https://github.com/mhwombat/jot.git") {}
«derivation /nix/store/1wgfvjy7k3y66378214d9k3cwrp7z340-jot-1.4.drv»
nix-repl> :b haskellPackages.callCabal2nix "jot" (builtins.fetchGit "https://github.com/mhwombat/jot.git") {}
This derivation produced the following outputs:
doc -> /nix/store/hfnfmv4sv0q5jhaq5yjrkysa4kiij54y-jot-1.4-doc
out -> /nix/store/ckq3xw2qg696pm4iaclifzj7bjq0l3p2-jot-1.4
This is a little easier than manually running cabal2nix, and passing the output to haskellPackages.callPackage.
haskellPackages.callCabal2nix internally uses IFD, with all its advantages and disadvantages.
This is a function for easily doing Haskell development on a local package. With the jot example, this would be used for instance if you have the jot repo checked out locally, and you want to jump into an environment with cabal available, as well as all the Haskell dependencies that are needed. There is a good example of haskellPackages.developPackage at the top of your first post.
haskellPackages.developPackage is mostly a direct wrapper around haskellPackages.callCabal2nix, but it has a little extra logic for giving you a development environment if you call it from nix-shell.
Example of building:
$ nix repl '<nixpkgs>'
nix-repl> haskellPackages.developPackage { root = builtins.fetchGit "https://github.com/mhwombat/jot.git"; }
«derivation /nix/store/0gx99ynmfkpn60bmr3jgm37vqn3v9ags-jot-1.4.drv»
nix-repl> :b haskellPackages.developPackage { root = builtins.fetchGit "https://github.com/mhwombat/jot.git"; }
This derivation produced the following outputs:
doc -> /nix/store/wlrijn4irrpasam4kxk20rn5b8dnrpgi-jot-1.4-doc
out -> /nix/store/7fgbai1xswflaw61wl9ccj1zf6k7vyi2-jot-1.4
This is for creating a development environment that contains multiple Haskell packages. This is useful when you’re working on a “big” Haskell project, where you have a bunch of individual Haskell packages.
In general, you’d define each of your individual packages with callCabal2nix, and then pass them all to shellFor. See the documentation of haskellPackages.shellFor for an example of this.
When doing development on Haskell packages locally, we generally recommend that users define an overlay for their local packages. The derivations for each of the local packages are generally defined with haskellPackages.callCabal2nix. Then haskellPackages.shellFor is used to define a shell environment.
Sort of. There is a little documentation in the source code. But that doesn’t really give a high-level overview.
There is an open PR that is bringing the Haskell-related documentation back into Nixpkgs. In theory the developPackage documentation should be added to this. This is definitely the type of thing we’d love to get help with if you’re interested in helping out!
I explained this above a little bit, but exectuableHaskellDepends is an argument to the generic Haskell builder in Nixpkgs. The generic Haskell builder is exposed through haskellPackages.mkDerivation. It is somewhat confusing, but haskellPackages.mkDerivation is completely(?) different from stdenv.mkDerivation.
haskellPackages.developPackage is a direct wrapper around haskellPackages.callCabal2nix. But haskellPackages.callCabal2nix is more-or-less a convenient wrapper around cabal2nix, haskellPackages.callPackage, and haskellPackages.mkDerivation.
When you use ./. in your config it makes a derivation that is named based on the name of the folder the nix file is in. This can lead to surprising results when CI might give the checkout folder a weird name.
This issue is common and insidious enough to have earned a place in the nix anti patterns:
Thank you so much everyone for the explanations. I’m learning so much from this one thread, and it will take me a few days to fully digest all this excellent information.
@cdepillabout, I’m reading that PR and would love to contribute to the documentation effort. For now I will continue to write short tutorials on things as I figure them out. Then I will discuss with the team if any of the things I’ve written would be useful to incorporate into the docs, and if so, where the information should go.
I found a simple solution to the question “How do I install a (cabal-based) haskell executable in my NixOS environment?” Not exactly your original question, but Google sent me here, so sharing… Starting from a similar nix-build environment: