суббота, 23 мая 2015 г.

A dense summary of Peter Simons Haskell-on-Nix video

Listing & installing Hackage packages

  - listing compilers:
nix-env -qaP -A haskell.compiler
  - listing packages (essentially Hackage names + NixOS-curated version):
nix-env -qaP -A haskellPackages
nix-env -qaP -A haskell.packages.ghc7101
  - installing packages
nix-env -iA haskellPackages.foo
nix-env -iA haskell.packages.ghc7101.foo

Environmenting

  Enter a shell with *ghc* = =ghc-7.10.1=:
nix-shell -p haskell.compiler.ghc7101
  The following makes cabal just work:
nix-shell -p haskell.compiler.ghc7101 --command "cabal configure"
cabal build
  Build two environments with different versions of GHC, until GC strikes:
nix-env -p ~/ghc-7.6.3 -iA haskell.compiler.ghc763
nix-env -p ~/ghc-7.8.4 -iA haskell.compiler.ghc784
  Same, persistently, with regard to GC:
PROFILE_DIR=/nix/var/nix/profiles/per-user/$USER
nix-env -p $PROFILE_DIR/ghc-7.6.3 -iA ...

Persistent, name-bound user-profile-wide compiler+package environment

  Putting this in =~/.nixpkgs/config.nix=:
  {
    packageOverrides = super: let self = super.pkgs; in
    {
      myHaskellEnv =
        self.haskell.packages.ghc7101.ghcWithPackages
          (haskellPackages: with haskellPackages; [
            arrows async cabal-install case-insensitive
            cgi criterion hspec HStringTemplate
          ]);
    };
  }
  ..and then, this in =~/.bashrc=:
  if [ -e ~/.nix-profile/bin/ghc ]; then
          eval $(grep export ~/.nix-profile/bin/ghc)
  fi
  ..wraps GHC and allows for:
nix-env -iA myHaskellEnv

Per-project environments

Manual generation, no cabal integration

    =~/src/foo/shell.nix=:
    { compiler ? "ghc7101" }:
    with (import <nixpkgs> {}).pkgs;
    let
      ghc = haskell.packages.${compiler}.ghcWithPackages
              (pkgs: with pkgs; [ aeson lens monad-par ]);
    in
      stdenv.mkDerivation {
        name = "my-haskell-env-0";
        buildInputs = [ ghc ];
        shellHook = "eval $(grep export ${ghc}/bin/ghc)";
      }

cd ~/src/foo/
nix-shell --argstr compiler ghc784

Automated through cabal2nix, has cabal integration

cabal2nix --shell . >shell.nix
nix-shell --command "cabal configure"
cabal build
    ..where cabal2nix emits something like this:
    with (import <nixpkgs> {}).pkgs;
    let pkg = haskellPackages.callPackage
                ({ mkDerivation, base, stdenv, transformers }:
      mkDerivation {
            pname = "mtl";
            version = "2.2.1";
            src = ./.;
            buildDepends = [ base transformers ];
            homepage = "http://github.com/ekmett/mtl";
            license = stdenv.lib.licenses.bsd3;
      }) {};
    in
      pkg.env

Inject locally-derived packages into the global namespace

cd ~/src/foo
cabal2nix . > default.nix

    Then, in =~/.nixpkgs/config.nix=:
    {
      packageOverrides = super: let self = super.pkgs; in
      {
        foo = self.haskellPackages.callPackage ../src/foo {};
      };
    }
nix-env -iA foo
    Or, alternatively, to make it available to other packages:
    {
      packageOverrides = super: let self = super.pkgs; in
      {
        haskellPackages = super.haskellPackages.override {
          overrides = self: super: {
            foo = self.callPackage ../src/foo {};
            bar = self.callPackage ../src/bar {};
          };
        };
      };
    }

воскресенье, 26 апреля 2015 г.

Overriding hackage-packages in HaskellNG on NixOS

On #nixos, Benno Fünfstück (aka bennofs) has produced a very exhaustive explanation of how to override a hackage package in the new Haskell-NG infrastructure. (Here's the original gist).

I immediately felt that it's actually more of a blog post than a snippet, and so here it is:
{}: # nix-env expects a function

let 
  # Get nixpkgs (in configuration.nix, use pkgs for this, but this file is standalone
  # to test it easier so we have to manually import nixpkgs)
  pkgs = import  {};

  # First, get the haskell packages from nixpkgs. In configuration.nix, you
  # can use pkgs.haskellngPackages for this of course.
  haskellngPackages = pkgs.haskellngPackages;
  # (this could also be written as inherit (pkgs) haskellngPackages; )


  # The ghc-mod expression is defined in nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix as:
  #    "ghc-mod" = callPackage
  #    ({ mkDerivation, async, base, Cabal, ......}:
  #     mkDerivation {
  #       pname = "ghc-mod";
  #       version = "5.2.1.2";
  #       sha256 = "11wnrdb6blw169w6kd49ax9h1r9qkka5329lmdhimvki8amv8riv";
  #       isLibrary = true;
  #       isExecutable = true;
  #       buildDepends = [ async base Cabal ..... ];
  #       testDepends = [ base Cabal ...... ];
  #       buildTools = [ emacs makeWrapper ];
  #       configureFlags = "--datasubdir=ghc-mod-5.2.1.2";
  #       postInstall = ''
  #         cd $out/share/ghc-mod-5.2.1.2
  #         make
  #         rm Makefile
  #         cd ..
  #         ensureDir "$out/share/emacs"
  #         mv ghc-mod-5.2.1.2 emacs/site-lisp
  #       '';
  #       homepage = "http://www.mew.org/~kazu/proj/ghc-mod/";
  #       description = "Happy Haskell Programming";
  #       license = stdenv.lib.licenses.bsd3;
  #     }) { inherit (pkgs) emacs;  inherit (pkgs) makeWrapper;};
  #  (..... marks parts that were abbreviated)
  #
  # Note that the package is build by a call to the `mkDerivation` function. We would like to pass an additional
  # argument to mkDerivation such that the expression instead looks like this:
  #     mkDerivation {
  #       pname = "ghc-mod";
  #       version = "5.2.1.2";
  #       src = pkgs.fetchgit {
  #         url = https://github.com/kazu-yamamoto/ghc-mod;
  #         rev = "247e4e0e7616fe1fecc68fdcf80d6249ac4cee4f";
  #         sha256 = "2a23271d0e6907351a246f095040ba18c3ab6bf1cba08a14338d701defa55474";
  #         # sha256 and rev can be determined using 'nix-prefetch-git https://github.com/kazu-yamamoto/ghc-mod'
  #       };
  #       .... # rest like above
  #     })
  #
  # This is what the haskell-ng.lib.overrideCabal function allows us to do.
  # `overrideCabal` expects a function that transforms the old argument set passed
  # to `mkDerivation` to a new argument set that will be passed to `mkDerivation`.
  #
  # So, we can define a new ghc-mod package that overrides the old haskellngPackages.ghc-mod package:
  ghc-mod-git = pkgs.haskell-ng.lib.overrideCabal haskellngPackages.ghc-mod (oldAttrs: {
    src = pkgs.fetchgit {
      url = https://github.com/kazu-yamamoto/ghc-mod;
      rev = "247e4e0e7616fe1fecc68fdcf80d6249ac4cee4f";
      sha256 = "2a23271d0e6907351a246f095040ba18c3ab6bf1cba08a14338d701defa55474";
     };
    # the new ghc mod also requires some new dependencies. Add them to buildDepends:
    buildDepends = oldAttrs.buildDepends ++ [ cabal-helper-new haskellngPackages.cereal ];
  });

  cabal-helper-new = pkgs.haskell-ng.lib.overrideCabal haskellngPackages.cabal-helper (oldAttrs: {
    version = "0.3.2.0";
    sha256 = "06igjmr0n8418wid1pr74cgvlsmwni7ar72g9bddivlbxax1pfli";
  });

  # The problem with this approach is: all packages that depend on ghc-mod will need to be
  # changed if they should use the new ghc-mod!
  #
  # Also, if haskellngPackages.ghc-mod also depends on cabal-helper, then ghc-mod-git will now
  # have two versions of cabal-helper in it's build environment!
  #
  # If we want reverse dependencies of ghc-mod to see the new ghc-mod too, we need to override
  # the haskellngPackage set. 
  
  # To do this, we need an override function. This function takes two arguments:
  #   self: this is the final package set, after all customizations have been applied
  #         (note that this is recursive: it is like 'fix $ \self -> ...' in Haskell)
  #   super: this is the "previous" package set, where previous means before our 
  #           customizations have been applied.
  overrideFunction = self: super: {
    # Here we just override ghc-mod like above, with one small difference: the package we override is
    # `super.ghc-mod`, not `haskellngPackages.ghc-mod`.
    ghc-mod = pkgs.haskell-ng.lib.overrideCabal super.ghc-mod (oldAttrs: {
      src = pkgs.fetchgit {
        url = https://github.com/kazu-yamamoto/ghc-mod;
        rev = "247e4e0e7616fe1fecc68fdcf80d6249ac4cee4f";
        sha256 = "2a23271d0e6907351a246f095040ba18c3ab6bf1cba08a14338d701defa55474";
      };

      # the new ghc mod also requires cereal and cabal-helper now. Add it to buildDepends:
      # note: we use self.cereal and self.cabal-helper here, so if the package set if 
      # overriden *again* in the future, this package will use the overriden cereal and cabal-helper.
      buildDepends = oldAttrs.buildDepends ++ [ self.cabal-helper self.cereal ];
    });

    # ghc-mod from git also requires a newer version of cabal-helper than nixos-unstable contains.
    cabal-helper = pkgs.haskell-ng.lib.overrideCabal super.cabal-helper (oldAttrs: {
      version = "0.3.2.0";
      sha256 = "06igjmr0n8418wid1pr74cgvlsmwni7ar72g9bddivlbxax1pfli";
    });
  };

  # Now we only need to apply our override function to the haskell package set.
  #
  # 
  # We can do this using `.override`.
  # `.override` allows to change the arguments given to callPackage, and the
  # haskellngPackage set is just defined as:
  #     haskellngPackages = callPackage ../development/haskell-modules {
  #       ghc = compiler.ghc784;
  #       packageSetConfig = callPackage ../development/haskell-modules/configuration-ghc-7.8.x.nix { };
  #     }
  # (it's an alias for packages.ghc784 defined in nixpkgs/pkgs/top-level/haskell-ng.nix)
  #
  # With .override, we pass an additional argument when calling ../development/haskell-modules 
  # (which is just nixpkgs/pkgs/development/haskell-modules/default.nix, the file that hooks everything together) called
  # overrides. This means that customizedPackages is essentially:
  #     haskellngPackages = callPackage ../development/haskell-modules {
  #       ghc = compiler.ghc784;
  #       packageSetConfig = callPackage ../development/haskell-modules/configuration-ghc-7.8.x.nix { };
  #       overrides = overrideFunction
  # `haskell-modules/default.nix` will then apply our custom overrides when building the package set.
  customizedPackages = haskellngPackages.override {
    overrides = overrideFunction;
  };

# Now we just take ghc-mod from the customized package set:
# By applying the overrides to all of haskellngPackages, all packages that
# depend on ghc-mod will also see the new ghc-mod.
in customizedPackages.ghc-mod 
#in ghc-mod-git # ghc-mod-git also works, as explained above.

# As said above, 

# You can test this using:
#
# nix-build /path/to/this/file.nix
# 
# Or install it to your user environment with:
#
# nix-env -i -f /path/to/this/file.nix
#
# You can even see that ghc-mod-git and customizedPackages.ghc-mod are exactly identical:
# just change customizedPackages.ghc-mod to ghc-mod-git above, and rebuild. Nix will not
# rebuild ghc-mod, but instead just use the already build ghc-mod, since the packages are identical.