`setxkbmap` fails to find layout

I am trying to add a new keyboard layout. I have the following snippet.

  system.replaceRuntimeDependencies =
    let
      xkeyboardconfig = pkgs.xorg.xkeyboardconfig_custom {
        layouts =
          let
            default-layout = {
              description = "";
              symbolsFile = null;
              languages = [ "eng" ];
              compatFile = null;
              geometryFile = null;
              keycodesFile = null;
              typesFile = null;
            };
          in
            {
              bsk = default-layout // {
                description = "BSK";
                symbolsFile = ./layouts/bsk;
              };
            };
      };

      xorgserver = pkgs.xorg.xorgserver.overrideAttrs (old: {
        configureFlags = old.configureFlags ++ [
          "--with-xkb-bin-directory=${xkbcomp}/bin"
          "--with-xkb-path=${xkeyboardconfig}/share/X11/xkb"
        ];
      });

      setxkbmap = pkgs.xorg.setxkbmap.overrideAttrs (old: {
        postInstall =
          ''
          mkdir -p $out/share
          ln -sfn ${xkeyboardconfig}/etc/X11 $out/share/X11
        '';
      });

      xkbcomp = pkgs.xorg.xkbcomp.overrideAttrs (old: {
        configureFlags = [
          "--with-xkb-config-root=${xkeyboardconfig}/share/X11/xkb"
        ];
      });
    in
      [
        {
          original = pkgs.xorg.xkeyboardconfig;
          replacement = xkeyboardconfig;
        }
        {
          original = pkgs.xorg.xorgserver;
          replacement = xorgserver;
        }
        {
          original = pkgs.xorg.setxkbmap;
          replacement = setxkbmap;
        }
        {
          original = pkgs.xorg.xkbcomp;
          replacement = xkbcomp;
        }
      ];

The problem is that when I then run setxkbmap bsk, it returns Error loading new keyboard description.

Digging into configuration, I have located the origin of the failure. There are actually two setxkbmap derivations. One, whose share/X11/xkb/symbols/ contains a file named bsk, one which does not. They are both built when I run nixos-rebuild test, and they are both kept when I run nix-collect-garbage.

When I run nix show-derivation nixpkgs.xorg.setxkbmap, the out path points to the derivation which does not contain the bsk file.

How do I fix this? How can I make setxkbmap find bsk?


  services.xserver = {
    enable = true;
    layout = "my";
    extraLayouts = {
      my = {
        description = "Custom layout file";
        languages = [ "eng" "rus" ];
        symbolsFile = ./xkb/symbols/my;
      };
    };

weird, i posted a comment in russian, anyway this is a way to add additional xkb layouts/symbols
or replace existing ones

As written in Nixos builds a ton of packages instead of fetching them in the cache - #5 by jtojnar, you should not need to replace anything else other than xkeyboardconfig.

You could try checking nix-store --query --tree /run/current-system/sw to see what pulls it in.

That is to be expected as, unlike overlays, system.replaceRuntimeDependencies does not change nixpkgs packages but only replaces them in the final built system path.

Does running the other setxkbmap, which does contain your new file, work?

This is not ok, because it recompiles the X server and every that depends on it on each rebuild. See the issue it causes.


Ah, ok I didn’t see that :stuck_out_tongue:

If I understood right, I don’t need to run nix-store --query --tree /run/current-system/sw since the behavior of system.replaceRuntimeDependencies already explains why I do have two derivations.


So, I patched my snippet, and looked for the actual runtime replacement of the derivation, and found it. Actually, the derivation path of the patched derivation is in the final derivation. No problem there, it runs fine.

I didn’t know you could do that, but it actually makes sense. I looked in the patched derivation tree, looking for the binary, and I understood that the final derivation configuration directory was linked to the patched derivation (which, when you think of, makes sense: if it didn’t, how the patched derivation would impact the system?).

But, if it is the case, then there should be a bsk file in /nix/store/final-setxkbmap/share/X11/xkb/symbols/. And, turns out, there is! I think when I looked for it the first time I went in the directory of the original derivation.

Whatever, if the file is in the right place, then setxkbmap should work, right? Lol.
After some debugging, I realized setxkbmap crashes even before trying to read the bsk file. Strange. So, it doesn’t even look for it. Why is that? Debug, debug, debug. Ah, so it reads X11/xkb/rules/evdev, which is supposed to contain an entry for my layout. But it doesn’t. According to this tutorial (and some others), evdev is built from X11/xkb/rules/xorg.xml or X11/xkb/rules/evdev.xml, neither of witch contains bsk. They should contain an entry that looks like

<layout>
  <configItem>
    <name> BSK </name>
    <shortDescription> ... </shortDescription>
    <description> ... </description>
  </configItem>
</layout>

Isn’t NixOS supposed to take care of that?


Actually, I think that this is supposed to be the role of this snippet. However, it only changes base.xml, not xorg.xml or evdev.xml. Is it a bug? Did I miss something?

EDIT: I don’t really understand why, but bsk is present in all three base.xml, evdev.xml and xorg.xml in the patched derivation, but not in the final one. Also, for a strange reason, base.xml and evdev.xml are always identical, but are not linked each other (even though in the snippet only base.xml is changed, evdev.xml also ends up being changed…). Also, I have been confused by the hash that have been changed during the build. bsk is not present in the path of the final derivation.
EDIT 2: Also, even though bsk is present in evdev.xml in the patched derivation, it is not present in evdev, which is the file setxkbmap reads, so even if I call the patched binary, it doesn’t work…

Oh, you are right :woman_facepalming: should have thought of that myself.

Yeah, I think you are right. You can try to edit it to see if doing the same for evdev.xml fixes it. (Pass -I nixpkgs=path/to/nixpkgs/checkout to nixos-rebuild to build from a locally cloned directory.)

From further exploration (I edited my post) I understood I have been confused by the renaming of hash after the rebuild, and I have confused the final derivation with the patched derivation :confused: (the hash of the new final derivation starts with the same four letters of the last patched derivation). So, even though I was thinking that I was navigating in the final derivation, I was not.

So, the problem for now is that the final derivation does not include the patches.

Also, for a strange reason, going in the patched derivation and running setxkbmap bsk does not work… but one problem at the time.

Just to be sure, since running the patched setxkbmap bsk didn’t seem to be working, I tried the standard way (with system.xserver.extraLayouts), rebuilt, and strange things happened. When I just did that, it didn’t work. If I reboot, then run nix-collect-garbage (reboot because some derivations were apparently linked by roots in /proc/…), it still doesn’t work. But if I reboot after nix-collect-garbage -d, then it works… Even though old derivations are not supposed to interfere.