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:
-
A nixpkgs overlay to replace GnuPG globally
-
Replace the version of GnuPG that shows up in the system
PATH
via the NixOS module -
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.