37

As far as I understood, Nix is alternative for cabal sandbox. I finally managed to install Nix, but I still don't understand how it can replace a sandbox.

I understand you don't need cabal using Nix and the wrapped version of GHC; however if you want to publish a package you'll need at some point to package it using cabal. Therefore, you need to be able to write and test your cabal configuration within NIX. How do you do that?

Ideally, I would like an environment similar to cabal sandbox but "contained" within NIX, is that possible? In fact, what I really would like is the equivalent of nested sandboxes — as I usually work on projects made of multiple packages.

Update about my current workflow

At the moment I work on 2 or 3 independent projects (P1, P2, P3) which are each composed of 2 or 3 cabal modules/packages, let's say for P1: L11, L12 (libraries) and E11 (executables). E11 depends on L12 which depends on L11. I mainly split the executables from the library because they are private and kept on a private git repo.

In theory, each project could have this own sandbox (shared between its submodules). I tried that (having a common sandbox for L11 L12 and E11), but it's quickly annoying because, if you modify L11, you can't rebuild it because E11 depends on it, so I have to uninstall E11 first to recompile L11. It might be no exactly the case, but I encounter the similar problem. This would be fine if I were occasionally modifying L11, but in practice, I changed it more that E11.

As the shared sandbox doesn't work, so I went back to the one sandbox for every package solution. It's working but is less than ideal. The main problem is if I modify L11, I need to compile it twice (once in L11, and then again in E11). Also, each time I'm starting a new sandbox, as everybody knows, I need to wait a while to get everything package downloaded and recompiled.

So by using Nix, I'm hopping to be able to set up separate cabal "environments" per project, which solves all the issue aboves.

Hope this is clearer.

stites
  • 4,903
  • 5
  • 32
  • 43
mb14
  • 22,276
  • 7
  • 60
  • 102
  • 1
    This might help: https://ocharles.org.uk/blog/posts/2014-02-04-how-i-develop-with-nixos.html – ErikR Jan 15 '15 at 17:48
  • 2
    I've read it many times, but I haven't be able to extract the needed information. – mb14 Jan 15 '15 at 18:02

1 Answers1

54

I do all my development using Nix and cabal these days, and I can happily say that they work in harmony very well. My current workflow is very new, in that it relies on features in nixpkgs that have only just reached the master branch. As such, the first thing you'll need to do is clone nixpkgs from Github:

cd ~
git clone git://github.com/nixos/nixpkgs

(In the future this won't be necessary, but right now it is).

Single Project Usage

Now that we have a nixpkgs clone, we can start using the haskellng package set. haskellng is a rewrite of how we package things in Nix, and is of interest to us for being more predictable (package names match Hackage package names) and more configurable. First, we'll install the cabal2nix tool, which can automate some things for us, and we'll also install cabal-install to provide the cabal executable:

nix-env -f ~/nixpkgs -i -A haskellngPackages.cabal2nix -A haskellngPackages.cabal-install

From this point, it's all pretty much clear sailing.

If you're starting a new project, you can just call cabal init in a new directory, as you would normally. When you're ready to build, you can turn this .cabal file into a development environment:

cabal init
# answer the questions
cabal2nix --shell my-project.cabal > shell.nix

This gives you a shell.nix file, which can be used with nix-shell. You don't need to use this very often though - the only time you'll usually use it is with cabal configure:

nix-shell -I ~ --command 'cabal configure'

cabal configure caches absolute paths to everything, so now when you want to build you just use cabal build as normal:

cabal build

Whenever your .cabal file changes you'll need to regenerate shell.nix - just run the command above, and then cabal configure afterwards.

Multiple Project Usage

The approach scales nicely to multiple projects, but it requires a little bit more manual work to "glue" everything together. To demonstrate how this works, lets consider my socket-io library. This library depends on engine-io, and I usually develop both at the same time.

The first step to Nix-ifying this project is to generate default.nix expressions along side each individual .cabal file:

cabal2nix engine-io/engine-io.cabal > engine-io/default.nix
cabal2nix socket-io/socket-io.cabal > socket-io/default.nix

These default.nix expressions are functions, so we can't do much right now. To call the functions, we write our own shell.nix file that explains how to combine everything. For engine-io/shell.nix, we don't have to do anything particularly clever:

with (import <nixpkgs> {}).pkgs;
(haskellngPackages.callPackage ./. {}).env

For socket-io, we need to depend on engine-io:

with (import <nixpkgs> {}).pkgs;
let modifiedHaskellPackages = haskellngPackages.override {
      overrides = self: super: {
        engine-io = self.callPackage ../engine-io {};
        socket-io = self.callPackage ./. {};
      };
    };
in modifiedHaskellPackages.socket-io.env

Now we have shell.nix in each environment, so we can use cabal configure as before.

The key observation here is that whenever engine-io changes, we need to reconfigure socket-io to detect these changes. This is as simple as running

cd socket-io; nix-shell -I ~ --command 'cabal configure'

Nix will notice that ../engine-io has changed, and rebuild it before running cabal configure.

Alexey
  • 3,843
  • 6
  • 30
  • 44
ocharles
  • 6,172
  • 2
  • 35
  • 46
  • 1
    Thank you! This looks much simpler than what I'm doing at the moment. Can you expand a bit on what haskellng is doing ("how we package things in nix")? Is the workflow for cross-project dependencies similar to what you described in https://ocharles.org.uk/blog/posts/2014-02-04-how-i-develop-with-nixos.html, or has that changed? – Kris Nuttycombe Jan 15 '15 at 21:12
  • `nix-env -f ~/nixpkgs -i -A haskellngPackages.cabal2nix -A haskellngPackages.cabal` returns: `error: attribute ‘cabal’ in selection path ‘haskellngPackages.cabal’ not found` for me. Any suggestions? – Kris Nuttycombe Jan 15 '15 at 21:39
  • 1
    In my experience, working from HEAD nixpkgs is painful in a compile-the-world sense. Maybe suggest checking out a recent channel version (am I getting the terminology right)? – Itai Zukerman Jan 15 '15 at 22:07
  • 2
    @KrisNuttycombe I think Ollie meant `haskellngPackages.cabal-install`. – Alp Mestanogullari Jan 15 '15 at 22:44
  • 1
    cabal2nix-2.0 seems to have some problems building... "Setup: At least the following dependencies are missing: QuickCheck -any, deepseq-generics -any, hspec -any" and yet all three of those packages appear to install fine from haskellngPackages... @ocharles any suggestions? – Kris Nuttycombe Jan 16 '15 at 03:07
  • @ocharles That's pretty much what I'm looking for. Seems much simpler that your original blog post. I'll give it a try. – mb14 Jan 16 '15 at 09:07
  • @ocharles I updated my question to explain my workflow. – mb14 Jan 16 '15 at 09:34
  • Thanks! I've updated my answer. @KrisNuttycombe, I think this is what you're looking for too. Re problems installing `cabal2nix` - best line there is to discuss this on the Nixpkgs Github issue tracker. – ocharles Jan 16 '15 at 11:43
  • @KrisNuttycombe Got the same error yesterday, but after a `git pull` today I can install the most recent `cabal2nix` just fine. – Alp Mestanogullari Jan 16 '15 at 13:20
  • 3
    This command (`cabal2nix --shell my-project.cabal > shell.nix`) doesn't work for me. Instead I had to use `cabal2nix --shell ./. > shell.nix` and edit the `src` attribute to point to `./src`. – Matthew Pickering Jan 16 '15 at 20:29
  • @ocharles I tried it with toy projects and it seems to be indeed exactly what I'm looking for. However things were not as straight forward but I understand it's a work in progress, and the issues are out of topic on SO. – mb14 Jan 16 '15 at 23:09
  • @ocharles I noticed that cabal-install is not part of the environment create by shell.nix. Do you have it installed globally? Is it just the one provided by nix-env -i cabal-install? I sort of liked having it as part of the shell environment and not having to have ghc or anything else installed globally – purefn Jan 18 '15 at 02:37
  • @purefn it is installed globally, as mentioned above. – ocharles Jan 18 '15 at 19:36
  • _"in the future"_ — so are we now in the future yet or not? :) – Erik Kaplun Dec 23 '15 at 12:15
  • Also, as of 23 Dec '15, how does Cabal+Nix compare to Stack and/or Stack+Nix? – Erik Kaplun Dec 23 '15 at 13:11
  • 4
    @ErikAllik We're "in the future" in that haskellng is now just the standard way of packaging haskell stuff in nixpkgs (took me a while to figure that out when I recently jumped to nixos; there's so many tutorials telling you to use haskellng). I tried stack+nix, since I was using stack without nix and it has some integration, but have so far concluded I prefer cabal+nix; nix itself provides the sandboxing, lts package sets, and ability to depend on non-haskell projects, and I'm finding it easier to just work with nix-shell than to mix in stack exec as well. – Ben Jan 28 '16 at 00:08
  • 2
    @ErikAllik Relevant links about Stack+Nix: http://www.tweag.io/blog/stack-nix-portable-reproducible-builds and https://github.com/commercialhaskell/stack/blob/master/doc/nix_integration.md – Wizek Oct 02 '16 at 13:27