Override desktopItem from package

If this works is greatly dependent on how the package expression is written. Let’s take xournal for example, which uses the most common method of referring to the desktopItem using recursive attribute set.

This is actually the same issue that occurs with any overriding of attribute from recursive attribute sets as discussed in https://discourse.nixos.org/t/avoid-rec-expresions-in-nixpkgs/8293/4: Nix is a functional language and values in Nix are immutable – when you create a recursive attribute set, the references to its items refer to the original attribute set. Even when you create a new set replacing the value that is referenced:

nix-repl> ex = rec { a = "foo"; b = "${a}bar"; }

nix-repl> ex
{ a = "foo"; b = "foobar"; }

nix-repl> ex // { a = "baz"; }
{ a = "baz"; b = "foobar"; }

Since overrideAttrs internally does just this for the attribute set passed to stdenv.mkDerivation, the postInstall in the new attrset will keep referring to the old desktopItem.

One way to fix that would be just replacing the old references alongside attribute path in the postInstall

(mypackage.overrideAttrs (oldAttrs: rec {
  desktopItem = oldAttrs.desktopItem // { # ← this parts still does not actually work
    exec = "/my_exec";
    terminal = "true";
  };
  postInstall = builtins.replaceStrings [ "${oldAttrs.desktopItem}" ] [ "${desktopItem}" ] oldAttrs.postInstall;
}));

There is however another issue. oldAttrs.desktopItem is not just an attribute set that when changed a new desktop item is created. It is a derivation created by makeDesktopItem function. And just creating a new attribute with changed exec attribute will not propagate the change into a new derivation.

Unfortunately, looking at makeDesktopItem source it does not seem to be easily overridable. As far as I can tell, there is only overrideAttrs function from the derivation created by runCommandLocal (or rather stdenv.mkDerivation called by the trivial builder). This means you will have to resort to the following monstrosity:

(mypackage.overrideAttrs (oldAttrs: rec {
  desktopItem = oldAttrs.desktopItem.overrideAttrs (desktopAttrs: {
    buildCommand =
      let
        oldExec = builtins.match ".*(\nExec=[^\n]+\n).*" desktopAttrs.buildCommand;
        oldTerminal = builtins.match ".*(\nTerminal=[^\n]+\n).*" desktopAttrs.buildCommand;
        matches = oldExec ++ oldTerminal;
        replacements = [ "\nExec=/my_exec\n" "\nTerminal=true\n" ];
      in
        assert oldExec != null && oldTerminal != null;
        builtins.replaceStrings matches replacements desktopAttrs.buildCommand;
  });
  postInstall = builtins.replaceStrings [ "${oldAttrs.desktopItem}" ] [ "${desktopItem}" ] oldAttrs.postInstall;
}))

Note that there is a bug in builtins.replaceStrings that keeps the original desktop file in the string, which will cause it to be built as a dependency of your overridden mypackage, even though it is necessary. But that is pretty small concern so I did not bother getting around it here.

4 Likes