In a previous post I showed how to work around a deadlock between GnuPG and Emacs. As a brief recap, GnuPG 2.4.1 introduced a change in its output which breaks a protocol that Emacs relied on, so I pinned GnuPG to version 2.4.0 on my system to avoid the bug.

Damien Cassou reached out to me and expressed a preferred way of dealing with this issue: patch the current version of GnuPG so it doesn’t contain the bug. This means we get to keep any bug fixes and security updates in the most recent version without having to suffer with the deadlock bug.

I’m going to demonstrate how to apply a patch to GnuPG in nixpkgs, but before that let’s review the Nix code for pinning GnuPG to version 2.4.0.

Pinning the GnuPG Package

In the last post we looked at three different options for pinning GnuPG:

  1. A nixpkgs overlay to replace GnuPG globally

  2. Replace the version of GnuPG that shows up in the system PATH via the NixOS module

  3. Replace the version of GnuPG that shows up in the user environment via the Home Manager module

My preference was for the last option, using Home Manager:

{ pkgs }:

{
  programs.gpg.package = pkgs.gnupg.overrideAttrs (orig: {
    version = "2.4.0";
    src = pkgs.fetchurl {
      url = "mirror://gnupg/gnupg/gnupg-2.4.0.tar.bz2";
      hash = "sha256-HXkVjdAdmSQx3S4/rLif2slxJ/iXhOosthDGAPsMFIM=";
    };
  });
}

However, I didn’t really explain what was going on in that Nix code. I’d like to give that a try now.

Overriding Nix Packages

One way to think about a package written in Nix is as a set of attributes. (Of course, this isn’t true, but it’s a useful analogy and allows us to continue without having to talk about lazy evaluation and fixpoints.) These attribute sets are immutable so we can’t change them. But what we can do is build new packages from existing ones.

This is done with the overrideAttrs function, which allows us to create a new package by replacing attributes in an existing package. The argument to overrideAttrs is a function that is given the existing attributes and should return replacement attributes.

In the Nix code above that pins GnuPG to version 2.4.0, I replaced the version and src attributes. The result is a new package based on the existing GnuPG 2.4.1 package, except that the upstream source code is now coming from the GnuPG 2.4.0 tarball.

This won’t always work, but it does in this case because GnuPG 2.4.0 and 2.4.1 are so similar. If I tried to update the GnuPG package by replacing its source code with that of the Linux kernel things wouldn’t turn out so nicely.

The most interesting thing about overrideAttrs is that we can replace any package attribute we want. I’ve used this to great effect over the years: adding shell commands to the end of install scripts, changing compiler flags, installing files that were missed by make install, to name a few.

And of course, we can use this to apply patches to the source code before building.

Patching the Package Instead

Nix packages have an optional patches attribute which is a list of patch files to apply to the source code before building. And thanks to overrideAttrs we can add patches to that list:

final: prev:

{
  gnupg_plus_960877b = prev.gnupg.overrideAttrs (orig: {
    patches = (orig.patches or [ ]) ++ [
      (prev.fetchurl {
        url = "https://github.com/gpg/gnupg/commit/960877b10f42ba664af4fb29130a3ba48141e64a.diff";
        sha256 = "0pa7rvy9i9w16njxdg6ly5nw3zwy0shv0v23l1mmi0b7jy7ldpvf";
      })
    ];
  });
}

This example is an overlay and is straight out of my Nix configuration. It creates a new package (gnupg_plus_960877b) from the exiting gnupg package by replacing the patches attribute with one that includes the fix for the deadlock bug.

I ensure that I’m including patches from the package I’m modifying by using the original patches list (orig.patches), or if that’s missing, an empty list. Then I append ("++") a list with one element which uses fetchurl to get the patch I want.

The only thing left to do is tell Home Manager to use this new package:

{ pkgs }:

{
  programs.gpg.package = pkgs.gnupg_plus_960877b;
}

Conclusion

Nix and nixpkgs make it very easy to alter an existing package thanks to the overrideAttrs function. Using nixpkgs overlays we can even replace a package globally throughout the package set.

Sometimes we may want to alter a package’s src attribute to downgrade (or upgrade) it to another version. In this case we took Damien Cassou’s advice and applied a patch to the exiting GnuPG package.


Update (February 23, 2024): GnuPG 2.4.4 has been released and it contains the patch mentioned in this post. In other words, GnuPG 2.4.4 and Emacs work nicely together once again.