Skip to content

Instantly share code, notes, and snippets.

@bennofs
Created April 26, 2015 12:45
Show Gist options
  • Save bennofs/bd243e58661af62047a5 to your computer and use it in GitHub Desktop.
Save bennofs/bd243e58661af62047a5 to your computer and use it in GitHub Desktop.
Overriding with haskell-ng
{}: # 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 <nixpkgs> {};
# 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.
@lseppala
Copy link

lseppala commented Jun 5, 2015

This was an extremely helpful and thorough explanation. The past three hours of trying to figure this out have finally come to a close. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment