Installing a patched version of a package in home-manager?

I’m trying to add a patch to my user environment (specifically home-manager) version of a package.

Looking at these other threads, I thought I’d mostly pieced together the required syntax,

Other threads

How do I patch agda for use in a nix shell? - #3 by hyphenrf

How to patch in an overlay - #4 by danbst

but the following block of Nix code gives me this error when I call home-manager switch:

error: A definition for option home.packages."[definition 1-entry 6]" is not of type package. Definition values:

  • In /nix/store/z1cv5msqxs1j3yarbrdzsy74l4pz4hj1-source/home.nix: <function>
{ config, pkgs, ... }:

{
  home.username = "lain";
  home.homeDirectory = "/home/lain";
  home.stateVersion = "23.11";
  programs.home-manager.enable = true;
  programs.bash.enable = true;

  home.packages = with pkgs; [
    firefox
    nano
    gitFull git-lfs

    # Problems start below this line
    llama-cpp.overrideAttrs (old : {
      patches = (old.patches or []) ++ [
        "~/Documents/projects/llama-sandbox/fix-llamacpp-githubissue6723.patch"
      ];
    })

  ];
}

What am I doing wrong? What’s the proper way to add a custom patch to a package in home-manager?

In case it matters: I’m on NixOS 24.05, but using home-manager as a package so that I can fiddle with the user environment without root access. (home-manager itself is installed as a system-wide package, but other than that it’s all user config.)

Nix treats llama-cpp.overrideAttrs and (<function that applies an override>) as two separate list items, and therefore your list contains a function and a function, both of which aren’t packages. There’s no way for nix to know that you meant to apply the second function as an argument, because you’re in the middle of a list, where each entry should be a value.

So, simply wrap that function call in brackets:

  home.packages = with pkgs; [
    firefox
    nano
    gitFull git-lfs

    (llama-cpp.overrideAttrs (old : {
      patches = (old.patches or []) ++ [
        "~/Documents/projects/llama-sandbox/fix-llamacpp-githubissue6723.patch"
      ];
    }))
  ];

or, arguably more readably, break the package definition out of the list:

{ config, pkgs, ... }: let
  llama-cpp = llama-cpp.overrideAttrs (old : {
    patches = (old.patches or []) ++ [
      "~/Documents/projects/llama-sandbox/fix-llamacpp-githubissue6723.patch"
    ];
  });
in {
  home.username = "lain";
  home.homeDirectory = "/home/lain";
  home.stateVersion = "23.11";
  programs.home-manager.enable = true;
  programs.bash.enable = true;

  home.packages = with pkgs; [
    firefox
    nano
    gitFull
    git-lfs
    llama-cpp
  ];
}

You may want to use fetchPatch to fetch that patch, by the way, then you don’t have to refer to a local file, making your config non-reproducible. It’ll also make sure to properly standardize it as required by the patchphase.

2 Likes

I see; adding the extra set of parentheses called the function and fixed that type error.

However, now I’m ending up with a “No such file or directory” error when the patch surely does exist. (It didn’t seem to be a permissions issue as the file and its immediate parent had o+r set, and every parent of the file had o+x set), but I guess that — as you mention — that would be a reproducibility issue even if it had worked.

Is there any way to declare the patch contents inline (and offline)? The documentation doesn’t seem to contain type information for that:

https://nixos.org/manual/nixpkgs/stable/#var-stdenv-patches

I would use a path literal instead of a string:

patches = (old.patches or []) ++ [
  ~/Documents/projects/llama-sandbox/fix-llamacpp-githubissue6723.patch
];

This will make nix add the path to the nix store, and then properly substitute it. I think if you use a string literal it won’t be added to the nix store, and during the build phase it will then not be accessible due to the build sandbox. If you disabled the sandbox it’d likely work, but that’d be a side-effectful build. In fact you mention a bunch of those side-effects, nix is supposed to prevent the need for that kind of debugging :stuck_out_tongue:

I’d still really recommend using fetchpatch if that patch is accessible somewhere online. If not, you should at least put it in your config repo and use a relative path.

1 Like

Wow, I didn’t realize that paths and strings are different datatypes. Thank you for the clarification.

(Also, it doesn’t seem to support the implicit-user-home; I had to substitute in the full path.)

I don’t believe there are any copies of it per se floating around online yet; I extracted it from the body of this comment.

In this case, would “your config repo” refer to ~/.config/home-manager?

Erm, I have to admit I’ve forgotten what the default paths were.

Wherever the module you shared is, probably; mostly the point is to have all the files you need for your config under one directory, with no unrelated files in-between, making sure you use relative paths to refer to all files. Then you can just copy that directory and know it will evaluate on another computer, or from another directory, without having to then go into your sources and check where some random patch file is.