Patching GHC for your project

I’m sharing here how you can apply a patch to GHC from within your project, so that next time I forget it, I can find it.

Of course you could fork nixpkgs and update patches = in the ghc derivation like this one but sometimes you want to patch only the GHC used in your project, not for all of nixpkgs (e.g. you don’t care about your patch applying to pandoc and thus rebuilding half of nixpkgs).

How to:

Use this (e.g. in your overlay or other nix configuration):

  haskellPackages = with pkgs.haskell.lib; pkgs.haskell.packages.ghc865.override (oldHaskellPackages: {

    # Override the ghc here:
    overrides = lib.composeExtensions (oldHaskellPackages.overrides or (_: _: {})) (self: super: {
      patches = (old.patches or []) ++ [
        (pkgs.fetchpatch {
          url = "https://gitlab.haskell.org/nh2/ghc/-/commit/766ae0129dadd677d5ab500c2af8d5d747761ac4.patch";
          name = "ghc-Add-RTS-disable-delayed-os-memory-return.patch";
          sha256 = "0p46g1315kgg8v4gv29cni9rm9b2nbjcpg0qaf5sr6yarwaq7k47";
        })
      ];
    });

    overrides = self: super: {
      # Haskell package overrides go in here
      cryptonite = overrideCabal super.cryptonite (old: {
        src = ...;
      });
    });
  });

The line

lib.composeExtensions (oldHaskellPackages.overrides or (_: _: {}))

is long and ugly, but it is currently the correct boilerplate way to ensure to not accidentally remove the effects of previous overlays, as is explained in https://github.com/NixOS/nixpkgs/issues/26561#issuecomment-397350884.

7 Likes

One thing that I haven’t figured out yet is how to use a patched GHC project for a single Haskell package only.

E.g. in the example above, I’m adding a flag to the RTS which needs a patched GHC only for linking the final executable (e.g. pandoc).

Is it possible to override the GHC e.g. only for that one link?

CC @peti

Hmm, I’m no longer sure this actually works correctly.

Doing nix-store -q --tree on the output, I now see that both the the original GHC and the patched GHC are added to the inputDrvs of the package:

+---/nix/store/1hndilaprvpk2x3hyiidhgvpj77nqa8b-myhaskellpackage-0.1.0.0.drv
    +---/nix/store/iszb9bc4wbl1xax6a1ppyy73aahdqi3n-stdenv-linux.drv [...]
    +---/nix/store/mnd8c37nk0q1b70wrv1i3w1sg69sw3ja-ghc-8.6.5.drv [...]
    ...
    +---/nix/store/s6k04mlqmyz5vrf91hk8bg066ndyyv7x-ghc-8.6.5.drv [...]

That also means both those GHCs are on PATH, and creates a problem for me in that in cabal inside nix-shell a different ghc is used than by nix-build. As a result, building my exe in nix-shell results in my patched GHC flag being available, and the executable built with nix-build errors with the flag being unknown.

1 Like

I’ve filed this as https://github.com/NixOS/nixpkgs/issues/101580

Do you have a working example now? I’ve tried to build a custom GHC from source here: eff/default.nix at 5cc61455fdcf3763c519be683b13e42ea131a24b · turion/eff · GitHub

But I can’t get it to work.

My central thing is an overlay like this:

let
  src = ...;
  overlay = self: super: let
    ghc = (super.haskell.compiler.ghcHEAD.override { version = "8.11.20200115"; }).overrideAttrs (old: {
      inherit src;
    });
  in {
    haskell = super.haskell // {
      packages = super.haskell.packages // {
        ghcHEAD = super.haskell.packages.ghcHEAD.override {
          inherit ghc;
        };
      };
      compilers = super.haskell.compilers // {
        ghcHEAD = ghc;
      };
    };
  };
in ...

Today I just ran into the challenge of adding a patch to GHC. The snippet in the OP was really helpful in pointing me to the right direction, but it didn’t work immediately.

Here’s a modified snippet that worked for me, in the form of a full overlay:

EDIT: DISREGARD THIS POST! SEE NEXT POST!

It still suffers from the problem mentioned above, namely that there are two versions of GHC in every haskell package’s dependencies. Below is a fixed version

Previous contents of this post
self: super:
let
  # Besides an override of ghc, we also have overrides for Haskell libraries. haskell-overlay.nix looks
  # roughly like this:
  # self: super: { <Haskell library overrides here> }
  haskellOverlay = import ./haskell-overlay.nix;
in
{
  haskellPackages = super.haskell.packages.ghc8104.override (
    oldHaskellPackages: {
      # Note: overrides is incompatible with the "extends" attribute, so we add all of our overlays
      # in the call to composeManyExtensions.
      overrides = super.lib.composeManyExtensions [
        # Existing overrides from possible previous overlays
        (oldHaskellPackages.overrides or (_: _: {}))
        # The actual GHC override
        (
          haskellSelf: haskellSuper: {
            ghc = haskellSuper.ghc.overrideAttrs (
              prev: {
                patches = (prev.patches or []) ++ [
                  ./some-patch.patch
                ];
              }
            );
          }
        )

        # The custom overlay for Haskell libraries
        haskellOverlay
      ];
    }
  );
}

The nixpkgs revision the above is tested on is f930ea227cecaed1f1bdb047fef54fe4f0721c8c.

Note: this only changes haskellPackages. This means that you will see the patch in haskellPackages.ghc.patches, but not in haskell.packages.ghc8104.ghc.patches, nor in haskell.compiler.ghc8104.patches. In practice this means you should use haskellPackages to build your application, rather than haskell.packages.<compiler>

This specific snippet applies to ghc 8.10.4 specifically, but I reckon changing the version number will make it work for any other available version. No guarantees though.

2 Likes

Looks like the above snippet still suffers from the problem mentioned by @nh2. Here’s a fixed version

self: super:
let
  haskellOverlay = import ./haskell-overlay.nix;
in
{
  # Method of overriding copied from
  # https://github.com/NixOS/nixpkgs/issues/101580#issuecomment-716086458
  haskell = super.haskell // {
    packages = super.haskell.packages // {
      ghc8104 =
        (
          super.haskell.packages.ghc8104.override {
            ghc = super.haskell.compiler.ghc8104.overrideAttrs (
              prev: {
                patches = (prev.patches or []) ++ [
                  ./some-patch.patch
                ];
              }
            );
          }
        ).extend haskellOverlay;
    };
  };
}

Unlike my previous post, this will work for both haskellPackages and `haskell.packages.ghc8104.

1 Like