Working with wrappers and overrideAttrs

In my NixOS configuration, I enable services.urxvtd. In 24.05, I ran into the bug described in tmux v3.4 outputs garbage in urxvt [SOLVED] / Applications & Desktop Environments / Arch Linux Forums. There’s a patch that fixes the bug, and I’d like to apply the patch to urxvt on my system.

Unfortunately I don’t didn’t know how to get the patch to where it needs to go. [While writing this post, I figured out something that works. I still have questions…]. The best I’ve got is was this:

services.urxvtd = { 
  enable = true;
  package = pkgs.rxvt-unicode-unwrapped.overrideAttrs (orig: {
    patches = orig.patches ++ [ ../patches/urxvt-garbage.patch ];
  });
};

With that, I’ve successfully applied the patch. But I’m using the wrong package now! I want the wrapped version, which properly includes some Perl search paths or whatever.

But I don’t know how to modify the underlying unwrapped package while still using the wrapped package for services.urxvtd.package.

Some things I’ve tried:

Apply overrideAttrs to pkgs.rxvt-unicode

I naively assumed that the wrapper still evaluates to something that has an overrideAttrs, but I guess it doesn’t.

Replace urxvtd-unicode-unwrapped in a nixpkgs overlay

Doesn’t work, don’t know why. pkgs.rxvt-unicode-unwrapped should be using pkgs.rxvt-unicode as a callPackage input, right? Everything built fine, but the patch didn’t end up in my system.

Use overrideDerivation (1)

I.e.

package = pkgs.rxvt-unicode.overrideDerivation (drv: {
  rxvt-unicode-unwrapped = pkgs.rxvt-unicode-unwrapped.overrideAttrs (...);
});

This managed to build, but the patch didn’t end up in my system, so it didn’t work.

Use overrideDerivation (2)

Ok, can I add patches directly to the rxvt-unicode derivation?

package = pkgs.rxvt-unicode.overrideDerivation (orig:{
    patches = orig.patches ++ [
      ../patches/urxvt-garbage.patch
    ];
  });

No, this again builds fine but the patch isn’t in my system.

Use override [THIS IS THE ONE THAT WORKS]

package = pkgs.rxvt-unicode.override ({
  rxvt-unicode-unwrapped = pkgs.rxvt-unicode-unwrapped.overrideAttrs (orig: {
    patches = orig.patches ++ [
      ../patches/urxvt-garbage.patch
    ];
  });
});

Hey, this works!

Unfortunately the chapter on overrides wasn’t enough for me to figure this out directly. I guess my journey went like:

  1. From previous experience, I had learned that you can add patches via overrideAttrs
  2. Reading nixpkgs sources, I learned that mkOverridable adds an override attribute that lets you change the inputs to a function
  3. Reading nixpkgs sources, I learned that rxvt-unicode-unwrapped was an overridable function that takes an rxvt-unicode-unwrapped argument
  4. I can compose override with overrideAttrs to build the package I need

I feel like there could be improvements to the process: I suffered this bug for six months because I didn’t have time to do this little journey before now. But I don’t have any immediate suggestions. Maybe this post will help somebody in the future.

The problem is that there isn’t a single method to overriding any given derivation. In the real world, derivations need to do a lot of funky stuff or need to be split due to various (good) reasons.

The pattern you have stumbled into where the actual derivation is wrapped within another derivation is quite common, so it could be documented but it’s still not something that can be applied generically.

I think you’ve slightly misunderstood here. rxvt-unicode is a derivation which takes rxvt-unicode-unwrapped as an input which in turn is the “actual” derivation that builds the code.

To apply a patch to the code, you must override the rxvt-unicode-unwrapped derivation via overrideAttrs. This overridden derivation must then be passed to rxvt-unicode which takes it as an input. To override an input, you use override.

It’s critical to understand the distinction here: overriding derivation function inputs vs. overriding mkDerivation arguments.

3 Likes

And finally, overrideDerivation is not something I’ve ever used or found necessary. I’m surprised we even document it in the manual.

2 Likes

Thanks for the additional insights!

To be fair, there’s a warning there that one should almost always prefer overrideAttrs. But the warning is not as effective as it could be. Three problems: the explanation for the preference is (1) on overrideAttrs, not overrideDerivation, (2) hidden in a ‘note’ rather than inline in the text, (3) hard to understand. I’ve read it 3-4 times now and still don’t get it.

I guess that’s one thing we could try to fix.

with __structuredAttributes and the finalAttrs pattern, It’s easier to modify a derivation via overrideAttrs which has the benefit of being user to explore and use in the REPL. Maybe it’s possible to do the same with the override pattern but 1. I dont know how 2. I doubt it would be as ergonomic

… and this is precisely the problem that infuse solves.

Here’s the solution to OP’s problem, written as an infusion:

services.urxvtd = {
  enable = true;
  package = infuse pkgs.rxvt-unicode {
    __input.rxvt-unicode-unwrapped.
    __output.patches.
    __append = [
      ../patches/urxvt-garbage.patch
    ];
  };
};

Much nicer, right? And it says what it does; even if you don’t understand infusions you can see what it’s doing: reading from bottom to top, it appends to the patches attribute of the output of the rxvt-unicode-unwrapped input to rxvt-unicode – which is what you wanted. The fetchGit expression to give you infuse is in the comment at the top of this file.

Not in-tree as part of nixpkgs, that’s true. But infuse is the one and only tool needed for doing overrides and overlays. It subsumes all the other ones.

Also, you never need to call infuse twice, like OP has to do in “the one that works”. I can see why that’s totally confusing: why are we overriding twice in order to add one patch? Infusions compose using simple list syntax: instead of writing

infuse pkgs infusion2 (infuse pkgs infusion1)

you can instead write the following, which does exactly the same thing:

infuse pkgs [ infusion1 infusion2 ]
1 Like

Could you elaborate on how you’d override a derivation input using a function that only concerns itself with mkDerivation arguments?